Merge branch 'master' of https://github.com/highfidelity/hifi into event-installer-interface-options

This commit is contained in:
howard-stearns 2017-06-19 14:22:12 -07:00
commit 91656a5fe5
252 changed files with 3429 additions and 6019 deletions
assignment-client/src
cmake
domain-server/resources
interface
libraries

View file

@ -23,7 +23,6 @@
#include <EntityEditPacketSender.h>
#include <EntityTree.h>
#include <EntityTreeHeadlessViewer.h>
#include <ScriptEngine.h>
#include <ThreadedAssignment.h>
@ -31,6 +30,7 @@
#include "AudioGate.h"
#include "MixedAudioStream.h"
#include "entities/EntityTreeHeadlessViewer.h"
#include "avatars/ScriptableAvatar.h"
class Agent : public ThreadedAssignment {

View file

@ -186,6 +186,10 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
listenPort = argumentVariantMap.value(ASSIGNMENT_CLIENT_LISTEN_PORT_OPTION).toUInt();
}
if (parser.isSet(portOption)) {
listenPort = parser.value(portOption).toUInt();
}
if (parser.isSet(numChildsOption)) {
if (minForks && minForks > numForks) {
qCritical() << "--min can't be more than -n";

View file

@ -132,7 +132,7 @@ void AvatarMixer::start() {
auto start = usecTimestampNow();
nodeList->nestedEach([&](NodeList::const_iterator cbegin, NodeList::const_iterator cend) {
std::for_each(cbegin, cend, [&](const SharedNodePointer& node) {
manageDisplayName(node);
manageIdentityData(node);
++_sumListeners;
});
}, &lockWait, &nodeTransform, &functor);
@ -183,8 +183,9 @@ void AvatarMixer::start() {
// NOTE: nodeData->getAvatar() might be side effected, must be called when access to node/nodeData
// is guaranteed to not be accessed by other thread
void AvatarMixer::manageDisplayName(const SharedNodePointer& node) {
void AvatarMixer::manageIdentityData(const SharedNodePointer& node) {
AvatarMixerClientData* nodeData = reinterpret_cast<AvatarMixerClientData*>(node->getLinkedData());
bool sendIdentity = false;
if (nodeData && nodeData->getAvatarSessionDisplayNameMustChange()) {
AvatarData& avatar = nodeData->getAvatar();
const QString& existingBaseDisplayName = nodeData->getBaseDisplayName();
@ -210,9 +211,39 @@ void AvatarMixer::manageDisplayName(const SharedNodePointer& node) {
soFar.second++; // refcount
nodeData->flagIdentityChange();
nodeData->setAvatarSessionDisplayNameMustChange(false);
sendIdentityPacket(nodeData, node); // Tell node whose name changed about its new session display name.
sendIdentity = true;
qCDebug(avatars) << "Giving session display name" << sessionDisplayName << "to node with ID" << node->getUUID();
}
if (nodeData && nodeData->getAvatarSkeletonModelUrlMustChange()) { // never true for an empty _avatarWhitelist
nodeData->setAvatarSkeletonModelUrlMustChange(false);
AvatarData& avatar = nodeData->getAvatar();
static const QUrl emptyURL("");
QUrl url = avatar.cannonicalSkeletonModelURL(emptyURL);
if (!isAvatarInWhitelist(url)) {
qCDebug(avatars) << "Forbidden avatar" << nodeData->getNodeID() << avatar.getSkeletonModelURL() << "replaced with" << (_replacementAvatar.isEmpty() ? "default" : _replacementAvatar);
avatar.setSkeletonModelURL(_replacementAvatar);
sendIdentity = true;
}
}
if (sendIdentity) {
sendIdentityPacket(nodeData, node); // Tell node whose name changed about its new session display name or avatar.
}
}
bool AvatarMixer::isAvatarInWhitelist(const QUrl& url) {
// The avatar is in the whitelist if:
// 1. The avatar's URL's host matches one of the hosts of the URLs in the whitelist AND
// 2. The avatar's URL's path starts with the path of that same URL in the whitelist
for (const auto& whiteListedPrefix : _avatarWhitelist) {
auto whiteListURL = QUrl::fromUserInput(whiteListedPrefix);
// check if this script URL matches the whitelist domain and, optionally, is beneath the path
if (url.host().compare(whiteListURL.host(), Qt::CaseInsensitive) == 0 &&
url.path().startsWith(whiteListURL.path(), Qt::CaseInsensitive)) {
return true;
}
}
return false;
}
void AvatarMixer::throttle(std::chrono::microseconds duration, int frame) {
@ -402,13 +433,17 @@ void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer<ReceivedMessage> mes
AvatarData::parseAvatarIdentityPacket(message->getMessage(), identity);
bool identityChanged = false;
bool displayNameChanged = false;
avatar.processAvatarIdentity(identity, identityChanged, displayNameChanged);
bool skeletonModelUrlChanged = false;
avatar.processAvatarIdentity(identity, identityChanged, displayNameChanged, skeletonModelUrlChanged);
if (identityChanged) {
QMutexLocker nodeDataLocker(&nodeData->getMutex());
nodeData->flagIdentityChange();
if (displayNameChanged) {
nodeData->setAvatarSessionDisplayNameMustChange(true);
}
if (skeletonModelUrlChanged && !_avatarWhitelist.isEmpty()) {
nodeData->setAvatarSkeletonModelUrlMustChange(true);
}
}
}
}
@ -764,4 +799,19 @@ void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) {
qCDebug(avatars) << "This domain requires a minimum avatar scale of" << _domainMinimumScale
<< "and a maximum avatar scale of" << _domainMaximumScale;
const QString AVATAR_WHITELIST_DEFAULT{ "" };
static const QString AVATAR_WHITELIST_OPTION = "avatar_whitelist";
_avatarWhitelist = domainSettings[AVATARS_SETTINGS_KEY].toObject()[AVATAR_WHITELIST_OPTION].toString(AVATAR_WHITELIST_DEFAULT).split(',', QString::KeepEmptyParts);
static const QString REPLACEMENT_AVATAR_OPTION = "replacement_avatar";
_replacementAvatar = domainSettings[AVATARS_SETTINGS_KEY].toObject()[REPLACEMENT_AVATAR_OPTION].toString(REPLACEMENT_AVATAR_DEFAULT);
if ((_avatarWhitelist.count() == 1) && _avatarWhitelist[0].isEmpty()) {
_avatarWhitelist.clear(); // KeepEmptyParts above will parse "," as ["", ""] (which is ok), but "" as [""] (which is not ok).
}
if (_avatarWhitelist.isEmpty()) {
qCDebug(avatars) << "All avatars are allowed.";
} else {
qCDebug(avatars) << "Avatars other than" << _avatarWhitelist << "will be replaced by" << (_replacementAvatar.isEmpty() ? "default" : _replacementAvatar);
}
}

View file

@ -59,7 +59,12 @@ private:
void parseDomainServerSettings(const QJsonObject& domainSettings);
void sendIdentityPacket(AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode);
void manageDisplayName(const SharedNodePointer& node);
void manageIdentityData(const SharedNodePointer& node);
bool isAvatarInWhitelist(const QUrl& url);
const QString REPLACEMENT_AVATAR_DEFAULT{ "" };
QStringList _avatarWhitelist { };
QString _replacementAvatar { REPLACEMENT_AVATAR_DEFAULT };
p_high_resolution_clock::time_point _lastFrameTimestamp;

View file

@ -65,6 +65,8 @@ public:
void flagIdentityChange() { _identityChangeTimestamp = usecTimestampNow(); }
bool getAvatarSessionDisplayNameMustChange() const { return _avatarSessionDisplayNameMustChange; }
void setAvatarSessionDisplayNameMustChange(bool set = true) { _avatarSessionDisplayNameMustChange = set; }
bool getAvatarSkeletonModelUrlMustChange() const { return _avatarSkeletonModelUrlMustChange; }
void setAvatarSkeletonModelUrlMustChange(bool set = true) { _avatarSkeletonModelUrlMustChange = set; }
void resetNumAvatarsSentLastFrame() { _numAvatarsSentLastFrame = 0; }
void incrementNumAvatarsSentLastFrame() { ++_numAvatarsSentLastFrame; }
@ -146,6 +148,7 @@ private:
uint64_t _identityChangeTimestamp;
bool _avatarSessionDisplayNameMustChange{ true };
bool _avatarSkeletonModelUrlMustChange{ false };
int _numAvatarsSentLastFrame = 0;
int _numFramesSinceAdjustment = 0;

View file

@ -16,9 +16,9 @@
#include <SharedUtil.h>
#include <Octree.h>
#include <OctreePacketData.h>
#include <OctreeHeadlessViewer.h>
#include <ViewFrustum.h>
#include "../octree/OctreeHeadlessViewer.h"
#include "EntityTree.h"
class EntitySimulation;

View file

@ -9,17 +9,14 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <NodeList.h>
#include "OctreeLogging.h"
#include "OctreeHeadlessViewer.h"
OctreeHeadlessViewer::OctreeHeadlessViewer() : OctreeRenderer() {
_viewFrustum.setProjection(glm::perspective(glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES), DEFAULT_ASPECT_RATIO, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP));
}
#include <NodeList.h>
#include <OctreeLogging.h>
void OctreeHeadlessViewer::init() {
OctreeRenderer::init();
OctreeHeadlessViewer::OctreeHeadlessViewer() {
_viewFrustum.setProjection(glm::perspective(glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES), DEFAULT_ASPECT_RATIO, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP));
}
void OctreeHeadlessViewer::queryOctree() {

View file

@ -12,28 +12,17 @@
#ifndef hifi_OctreeHeadlessViewer_h
#define hifi_OctreeHeadlessViewer_h
#include <udt/PacketHeaders.h>
#include <SharedUtil.h>
#include <ViewFrustum.h>
#include <OctreeProcessor.h>
#include <JurisdictionListener.h>
#include <OctreeQuery.h>
#include "JurisdictionListener.h"
#include "Octree.h"
#include "OctreeConstants.h"
#include "OctreeQuery.h"
#include "OctreeRenderer.h"
#include "OctreeSceneStats.h"
#include "Octree.h"
// Generic client side Octree renderer class.
class OctreeHeadlessViewer : public OctreeRenderer {
class OctreeHeadlessViewer : public OctreeProcessor {
Q_OBJECT
public:
OctreeHeadlessViewer();
virtual ~OctreeHeadlessViewer() {};
virtual void renderElement(OctreeElementPointer element, RenderArgs* args) override { /* swallow these */ }
virtual void init() override ;
virtual void render(RenderArgs* renderArgs) override { /* swallow these */ }
void setJurisdictionListener(JurisdictionListener* jurisdictionListener) { _jurisdictionListener = jurisdictionListener; }
@ -71,6 +60,7 @@ private:
JurisdictionListener* _jurisdictionListener = nullptr;
OctreeQuery _octreeQuery;
ViewFrustum _viewFrustum;
float _voxelSizeScale { DEFAULT_OCTREE_SIZE_SCALE };
int _boundaryLevelAdjust { 0 };
int _maxPacketsPerSecond { DEFAULT_MAX_OCTREE_PPS };

View file

@ -47,7 +47,7 @@ void OctreeInboundPacketProcessor::resetStats() {
_singleSenderStats.clear();
}
unsigned long OctreeInboundPacketProcessor::getMaxWait() const {
uint32_t OctreeInboundPacketProcessor::getMaxWait() const {
// calculate time until next sendNackPackets()
quint64 nextNackTime = _lastNackTime + TOO_LONG_SINCE_LAST_NACK;
quint64 now = usecTimestampNow();

View file

@ -80,7 +80,7 @@ protected:
virtual void processPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) override;
virtual unsigned long getMaxWait() const override;
virtual uint32_t getMaxWait() const override;
virtual void preProcess() override;
virtual void midProcess() override;

View file

@ -19,10 +19,10 @@
#include <QtCore/QUuid>
#include <EntityEditPacketSender.h>
#include <EntityTreeHeadlessViewer.h>
#include <plugins/CodecPlugin.h>
#include <ScriptEngine.h>
#include <ThreadedAssignment.h>
#include "../entities/EntityTreeHeadlessViewer.h"
class EntityScriptServer : public ThreadedAssignment {
Q_OBJECT

View file

@ -12,12 +12,19 @@ elseif ($ENV{QT_CMAKE_PREFIX_PATH})
set(QT_CMAKE_PREFIX_PATH $ENV{QT_CMAKE_PREFIX_PATH})
endif ()
set(QUAZIP_CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DCMAKE_PREFIX_PATH=${QT_CMAKE_PREFIX_PATH} -DCMAKE_INSTALL_NAME_DIR:PATH=<INSTALL_DIR>/lib -DZLIB_ROOT=${ZLIB_ROOT} -DCMAKE_POSITION_INDEPENDENT_CODE=ON)
if (APPLE)
else ()
set(QUAZIP_CMAKE_ARGS ${QUAZIP_CMAKE_ARGS} -DCMAKE_CXX_STANDARD=11)
endif ()
ExternalProject_Add(
${EXTERNAL_NAME}
URL https://s3-us-west-1.amazonaws.com/hifi-production/dependencies/quazip-0.7.2.zip
URL_MD5 2955176048a31262c09259ca8d309d19
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DCMAKE_PREFIX_PATH=${QT_CMAKE_PREFIX_PATH} -DCMAKE_INSTALL_NAME_DIR:PATH=<INSTALL_DIR>/lib -DZLIB_ROOT=${ZLIB_ROOT} -DCMAKE_POSITION_INDEPENDENT_CODE=ON
CMAKE_ARGS ${QUAZIP_CMAKE_ARGS}
LOG_DOWNLOAD 1
LOG_CONFIGURE 1
LOG_BUILD 1

View file

@ -7,16 +7,18 @@
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
macro(LINK_HIFI_LIBRARIES)
function(LINK_HIFI_LIBRARIES)
file(RELATIVE_PATH RELATIVE_LIBRARY_DIR_PATH ${CMAKE_CURRENT_SOURCE_DIR} "${HIFI_LIBRARY_DIR}")
set(LIBRARIES_TO_LINK ${ARGN})
foreach(HIFI_LIBRARY ${LIBRARIES_TO_LINK})
foreach(HIFI_LIBRARY ${LIBRARIES_TO_LINK})
if (NOT TARGET ${HIFI_LIBRARY})
add_subdirectory("${RELATIVE_LIBRARY_DIR_PATH}/${HIFI_LIBRARY}" "${RELATIVE_LIBRARY_DIR_PATH}/${HIFI_LIBRARY}")
endif ()
endforeach()
foreach(HIFI_LIBRARY ${LIBRARIES_TO_LINK})
include_directories("${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src")
include_directories("${CMAKE_BINARY_DIR}/libraries/${HIFI_LIBRARY}/shaders")
@ -29,4 +31,4 @@ macro(LINK_HIFI_LIBRARIES)
setup_memory_debugger()
endmacro(LINK_HIFI_LIBRARIES)
endfunction()

View file

@ -147,6 +147,7 @@
{
"name": "security",
"label": "Security",
"restart": false,
"settings": [
{
"name": "http_username",
@ -866,6 +867,22 @@
"help": "Limits the scale of avatars in your domain. Cannot be greater than 1000.",
"placeholder": 3.0,
"default": 3.0
},
{
"name": "avatar_whitelist",
"label": "Avatars Allowed from:",
"help": "Comma separated list of URLs (with optional paths) that avatar .fst files are allowed from. If someone attempts to use an avatar with a different domain, it will be rejected and the replacement avatar will be used. If left blank, any domain is allowed.",
"placeholder": "",
"default": "",
"advanced": true
},
{
"name": "replacement_avatar",
"label": "Replacement Avatar for disallowed avatars",
"help": "A URL for an avatar .fst to be used when someone tries to use an avatar that is not allowed. If left blank, the generic default avatar is used.",
"placeholder": "",
"default": "",
"advanced": true
}
]
},

View file

@ -26,7 +26,7 @@
</ul>
<button id="advanced-toggle-button" class="btn btn-info advanced-toggle">Show advanced</button>
<button class="btn btn-success save-button">Save and restart</button>
<button class="btn btn-success save-button">Save</button>
</div>
</div>
@ -77,7 +77,7 @@
</div>
<div class="col-xs-12 hidden-sm hidden-md hidden-lg">
<button class="btn btn-success save-button" id="small-save-button">Save and restart</button>
<button class="btn btn-success save-button" id="small-save-button">Save</button>
</div>
</div>

View file

@ -223,6 +223,14 @@ $(document).ready(function(){
// set focus to the first input in the new row
$target.closest('table').find('tr.inputs input:first').focus();
}
var tableRows = sibling.parent();
var tableBody = tableRows.parent();
// if theres no more siblings, we should jump to a new row
if (sibling.next().length == 0 && tableRows.nextAll().length == 1) {
tableBody.find("." + Settings.ADD_ROW_BUTTON_CLASS).click();
}
}
} else if ($target.is('input')) {
@ -1320,6 +1328,18 @@ function makeTableCategoryInput(setting, numVisibleColumns) {
return html;
}
function getDescriptionForKey(key) {
for (var i in Settings.data.descriptions) {
if (Settings.data.descriptions[i].name === key) {
return Settings.data.descriptions[i];
}
}
}
var SAVE_BUTTON_LABEL_SAVE = "Save";
var SAVE_BUTTON_LABEL_RESTART = "Save and restart";
var reasonsForRestart = [];
function badgeSidebarForDifferences(changedElement) {
// figure out which group this input is in
var panelParentID = changedElement.closest('.panel').attr('id');
@ -1342,13 +1362,24 @@ function badgeSidebarForDifferences(changedElement) {
}
var badgeValue = 0
var description = getDescriptionForKey(panelParentID);
// badge for any settings we have that are not the same or are not present in initialValues
for (var setting in panelJSON) {
if ((!_.has(initialPanelJSON, setting) && panelJSON[setting] !== "") ||
(!_.isEqual(panelJSON[setting], initialPanelJSON[setting])
&& (panelJSON[setting] !== "" || _.has(initialPanelJSON, setting)))) {
badgeValue += 1
badgeValue += 1;
// add a reason to restart
if (description && description.restart != false) {
reasonsForRestart.push(setting);
}
} else {
// remove a reason to restart
if (description && description.restart != false) {
reasonsForRestart = $.grep(reasonsForRestart, function(v) { return v != setting; });
}
}
}
@ -1357,6 +1388,7 @@ function badgeSidebarForDifferences(changedElement) {
badgeValue = ""
}
$(".save-button").html(reasonsForRestart.length > 0 ? SAVE_BUTTON_LABEL_RESTART : SAVE_BUTTON_LABEL_SAVE);
$("a[href='#" + panelParentID + "'] .badge").html(badgeValue);
}

View file

@ -18,6 +18,7 @@ Original.CheckBox {
id: checkBox
property int colorScheme: hifi.colorSchemes.light
property string color: hifi.colors.lightGray
readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light
property bool isRedCheck: false
property int boxSize: 14
@ -89,7 +90,7 @@ Original.CheckBox {
label: Label {
text: control.text
colorScheme: checkBox.colorScheme
color: control.color
x: 2
wrapMode: Text.Wrap
enabled: checkBox.enabled

View file

@ -353,6 +353,14 @@ FocusScope {
showDesktop();
}
function ensureTitleBarVisible(targetWindow) {
// Reposition window to ensure that title bar is vertically inside window.
if (targetWindow.frame && targetWindow.frame.decoration) {
var topMargin = -targetWindow.frame.decoration.anchors.topMargin; // Frame's topMargin is a negative value.
targetWindow.y = Math.max(targetWindow.y, topMargin);
}
}
function centerOnVisible(item) {
var targetWindow = d.getDesktopWindow(item);
if (!targetWindow) {
@ -375,11 +383,12 @@ FocusScope {
targetWindow.x = newX;
targetWindow.y = newY;
ensureTitleBarVisible(targetWindow);
// If we've noticed that our recommended desktop rect has changed, record that change here.
if (recommendedRect != newRecommendedRect) {
recommendedRect = newRecommendedRect;
}
}
function repositionOnVisible(item) {
@ -394,7 +403,6 @@ FocusScope {
return;
}
var oldRecommendedRect = recommendedRect;
var oldRecommendedDimmensions = { x: oldRecommendedRect.width, y: oldRecommendedRect.height };
var newRecommendedRect = Controller.getRecommendedOverlayRect();
@ -426,7 +434,6 @@ FocusScope {
newPosition.y = -1
}
if (newPosition.x === -1 && newPosition.y === -1) {
var originRelativeX = (targetWindow.x - oldRecommendedRect.x);
var originRelativeY = (targetWindow.y - oldRecommendedRect.y);
@ -444,6 +451,8 @@ FocusScope {
}
targetWindow.x = newPosition.x;
targetWindow.y = newPosition.y;
ensureTitleBarVisible(targetWindow);
}
Component { id: messageDialogBuilder; MessageDialog { } }

View file

@ -1,266 +0,0 @@
//
// Audio.qml
// qml/hifi
//
// Audio setup
//
// Created by Vlad Stelmahovsky on 03/22/2017
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtGraphicalEffects 1.0
import "../styles-uit"
import "../controls-uit" as HifiControls
import "components"
Rectangle {
id: audio;
//put info text here
property alias infoText: infoArea.text
color: "#404040";
HifiConstants { id: hifi; }
objectName: "AudioWindow"
property string title: "Audio Options"
signal sendToScript(var message);
Component {
id: separator
LinearGradient {
start: Qt.point(0, 0)
end: Qt.point(0, 4)
gradient: Gradient {
GradientStop { position: 0.0; color: "#303030" }
GradientStop { position: 0.33; color: "#252525" } // Equivalent of darkGray0 over baseGray background.
GradientStop { position: 0.5; color: "#303030" }
GradientStop { position: 0.6; color: "#454a49" }
GradientStop { position: 1.0; color: "#454a49" }
}
cached: true
}
}
Column {
anchors { left: parent.left; right: parent.right }
spacing: 8
RalewayRegular {
anchors { left: parent.left; right: parent.right; leftMargin: 30 }
height: 45
size: 20
color: "white"
text: audio.title
}
Loader {
width: parent.width
height: 5
sourceComponent: separator
}
//connections required to syncronize with Menu
Connections {
target: AudioDevice
onMuteToggled: {
audioMute.checkbox.checked = AudioDevice.getMuted()
}
}
Connections {
target: AvatarInputs !== undefined ? AvatarInputs : null
onShowAudioToolsChanged: {
audioTools.checkbox.checked = showAudioTools
}
}
AudioCheckbox {
id: audioMute
width: parent.width
anchors { left: parent.left; right: parent.right; leftMargin: 30 }
checkbox.checked: AudioDevice.muted
text.text: qsTr("Mute microphone")
onCheckBoxClicked: {
AudioDevice.muted = checked
}
}
AudioCheckbox {
id: audioTools
width: parent.width
anchors { left: parent.left; right: parent.right; leftMargin: 30 }
checkbox.checked: AvatarInputs !== undefined ? AvatarInputs.showAudioTools : false
text.text: qsTr("Show audio level meter")
onCheckBoxClicked: {
if (AvatarInputs !== undefined) {
AvatarInputs.showAudioTools = checked
}
}
}
Loader {
width: parent.width
height: 5
sourceComponent: separator
}
Row {
anchors { left: parent.left; right: parent.right; leftMargin: 30 }
height: 40
spacing: 8
HiFiGlyphs {
text: hifi.glyphs.mic
color: hifi.colors.primaryHighlight
anchors.verticalCenter: parent.verticalCenter
size: 32
}
RalewayRegular {
anchors.verticalCenter: parent.verticalCenter
size: 16
color: "#AFAFAF"
text: qsTr("CHOOSE INPUT DEVICE")
}
}
ListView {
id: inputAudioListView
anchors { left: parent.left; right: parent.right; leftMargin: 70 }
height: 125
spacing: 0
clip: true
snapMode: ListView.SnapToItem
model: AudioDevice
delegate: Item {
width: parent.width
visible: devicemode === 0
height: visible ? 36 : 0
AudioCheckbox {
id: cbin
anchors.verticalCenter: parent.verticalCenter
Binding {
target: cbin.checkbox
property: 'checked'
value: devicechecked
}
width: parent.width
cbchecked: devicechecked
text.text: devicename
onCheckBoxClicked: {
if (checked) {
if (devicename.length > 0) {
console.log("Audio.qml about to call AudioDevice.setInputDeviceAsync().devicename:" + devicename);
AudioDevice.setInputDeviceAsync(devicename);
} else {
console.log("Audio.qml attempted to set input device to empty device name.");
}
}
}
}
}
}
Loader {
width: parent.width
height: 5
sourceComponent: separator
}
Row {
anchors { left: parent.left; right: parent.right; leftMargin: 30 }
height: 40
spacing: 8
HiFiGlyphs {
text: hifi.glyphs.unmuted
color: hifi.colors.primaryHighlight
anchors.verticalCenter: parent.verticalCenter
size: 32
}
RalewayRegular {
anchors.verticalCenter: parent.verticalCenter
size: 16
color: "#AFAFAF"
text: qsTr("CHOOSE OUTPUT DEVICE")
}
}
ListView {
id: outputAudioListView
anchors { left: parent.left; right: parent.right; leftMargin: 70 }
height: 250
spacing: 0
clip: true
snapMode: ListView.SnapToItem
model: AudioDevice
delegate: Item {
width: parent.width
visible: devicemode === 1
height: visible ? 36 : 0
AudioCheckbox {
id: cbout
width: parent.width
anchors.verticalCenter: parent.verticalCenter
Binding {
target: cbout.checkbox
property: 'checked'
value: devicechecked
}
text.text: devicename
onCheckBoxClicked: {
if (checked) {
if (devicename.length > 0) {
console.log("Audio.qml about to call AudioDevice.setOutputDeviceAsync().devicename:" + devicename);
AudioDevice.setOutputDeviceAsync(devicename);
} else {
console.log("Audio.qml attempted to set output device to empty device name.");
}
}
}
}
}
}
Loader {
id: lastSeparator
width: parent.width
height: 6
sourceComponent: separator
}
Row {
anchors { left: parent.left; right: parent.right; leftMargin: 30 }
height: 40
spacing: 8
HiFiGlyphs {
id: infoSign
text: hifi.glyphs.info
color: "#AFAFAF"
anchors.verticalCenter: parent.verticalCenter
size: 60
}
RalewayRegular {
id: infoArea
width: parent.width - infoSign.implicitWidth - parent.spacing - 10
wrapMode: Text.WordWrap
anchors.verticalCenter: parent.verticalCenter
size: 12
color: hifi.colors.baseGrayHighlight
}
}
}
}

View file

@ -590,14 +590,11 @@ Item {
console.log("This avatar is no longer present. goToUserInDomain() failed.");
return;
}
var vector = Vec3.subtract(avatar.position, MyAvatar.position);
var distance = Vec3.length(vector);
var target = Vec3.multiply(Vec3.normalize(vector), distance - 2.0);
// FIXME: We would like the avatar to recompute the avatar's "maybe fly" test at the new position, so that if high enough up,
// the avatar goes into fly mode rather than falling. However, that is not exposed to Javascript right now.
// FIXME: it would be nice if this used the same teleport steps and smoothing as in the teleport.js script.
// Note, however, that this script allows teleporting to a person in the air, while teleport.js is going to a grounded target.
MyAvatar.orientation = Quat.lookAtSimple(MyAvatar.position, avatar.position);
MyAvatar.position = Vec3.sum(MyAvatar.position, target);
MyAvatar.position = Vec3.sum(avatar.position, Vec3.multiplyQbyV(avatar.orientation, {x: 0, y: 0, z: -2}));
MyAvatar.orientation = Quat.multiply(avatar.orientation, {y: 1});
}
}

View file

@ -128,7 +128,7 @@ Rectangle {
pal.sendToScript({method: 'refreshNearby', params: params});
}
Item {
Rectangle {
id: palTabContainer;
// Anchors
anchors {
@ -137,6 +137,7 @@ Rectangle {
left: parent.left;
right: parent.right;
}
color: "white";
Rectangle {
id: tabSelectorContainer;
// Anchors

View file

@ -0,0 +1,162 @@
//
// Audio.qml
// qml/hifi/audio
//
// Audio setup
//
// Created by Vlad Stelmahovsky on 03/22/2017
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.3
import "../../styles-uit"
import "../../controls-uit" as HifiControls
import "../../windows"
import "./" as Audio
Rectangle {
id: root;
HifiConstants { id: hifi; }
property var eventBridge;
property string title: "Audio Settings - " + Audio.context;
signal sendToScript(var message);
color: hifi.colors.baseGray;
// only show the title if loaded through a "loader"
function showTitle() {
return root.parent.objectName == "loader";
}
Column {
y: 16; // padding does not work
spacing: 16;
width: parent.width;
RalewayRegular {
x: 16; // padding does not work
size: 16;
color: "white";
text: root.title;
visible: root.showTitle();
}
Separator { visible: root.showTitle() }
Grid {
columns: 2;
x: 16; // padding does not work
spacing: 16;
Audio.CheckBox {
text: qsTr("Mute microphone");
checked: Audio.muted;
onClicked: {
Audio.muted = checked;
checked = Qt.binding(function() { return Audio.muted; }); // restore binding
}
}
Audio.CheckBox {
text: qsTr("Enable noise reduction");
checked: Audio.noiseReduction;
onClicked: {
Audio.noiseReduction = checked;
checked = Qt.binding(function() { return Audio.noiseReduction; }); // restore binding
}
}
Audio.CheckBox {
text: qsTr("Show audio level meter");
checked: AvatarInputs.showAudioTools;
onClicked: {
AvatarInputs.showAudioTools = checked;
checked = Qt.binding(function() { return AvatarInputs.showAudioTools; }); // restore binding
}
}
}
Separator {}
RowLayout {
HiFiGlyphs {
text: hifi.glyphs.mic;
color: hifi.colors.primaryHighlight;
anchors.verticalCenter: parent.verticalCenter;
size: 28;
}
RalewayRegular {
anchors.verticalCenter: parent.verticalCenter;
size: 16;
color: hifi.colors.lightGrayText;
text: qsTr("CHOOSE INPUT DEVICE");
}
}
ListView {
anchors { left: parent.left; right: parent.right; leftMargin: 70 }
height: 125;
spacing: 0;
snapMode: ListView.SnapToItem;
clip: true;
model: Audio.devices.input;
delegate: Item {
width: parent.width;
height: 36;
Audio.CheckBox {
text: display;
checked: selected;
onClicked: {
selected = checked;
checked = Qt.binding(function() { return selected; }); // restore binding
}
}
}
}
Separator {}
RowLayout {
HiFiGlyphs {
text: hifi.glyphs.unmuted;
color: hifi.colors.primaryHighlight;
anchors.verticalCenter: parent.verticalCenter;
size: 36;
}
RalewayRegular {
anchors.verticalCenter: parent.verticalCenter;
size: 16;
color: hifi.colors.lightGrayText;
text: qsTr("CHOOSE OUTPUT DEVICE");
}
}
ListView {
anchors { left: parent.left; right: parent.right; leftMargin: 70 }
height: 125;
spacing: 0;
snapMode: ListView.SnapToItem;
clip: true;
model: Audio.devices.output;
delegate: Item {
width: parent.width;
height: 36;
Audio.CheckBox {
text: display;
checked: selected;
onClicked: {
selected = checked;
checked = Qt.binding(function() { return selected; }); // restore binding
}
}
}
}
}
}

View file

@ -0,0 +1,18 @@
//
// CheckBox.qml
// qml/hifi/audio
//
// Created by Zach Pomerantz on 6/12/2017
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import "../../controls-uit" as HifiControls
HifiControls.CheckBox {
color: "white"
}

View file

@ -1,30 +0,0 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
import "../../styles-uit"
import "../../controls-uit" as HifiControls
Row {
id: row
spacing: 16
property alias checkbox: cb
property alias cbchecked: cb.checked
property alias text: txt
signal checkBoxClicked(bool checked)
HifiControls.CheckBox {
id: cb
boxSize: 20
colorScheme: hifi.colorSchemes.dark
anchors.verticalCenter: parent.verticalCenter
onClicked: checkBoxClicked(cb.checked)
}
RalewayBold {
id: txt
wrapMode: Text.WordWrap
width: parent.width - cb.boxSize - 30
anchors.verticalCenter: parent.verticalCenter
size: 16
color: "white"
}
}

View file

@ -0,0 +1,27 @@
//
// Audio.qml
//
// Created by Zach Pomerantz on 6/12/2017
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import "../../windows"
import "../audio"
ScrollingWindow {
id: root;
resizable: true;
destroyOnHidden: true;
width: 400;
height: 577;
minSize: Qt.vector2d(400, 500);
Audio { id: audio; width: root.width }
objectName: "AudioDialog";
title: audio.title;
}

View file

@ -5,9 +5,9 @@ import "../../dialogs"
PreferencesDialog {
id: root
objectName: "AudioPreferencesDialog"
objectName: "AudioBuffersDialog"
title: "Audio Settings"
showCategories: ["Audio"]
showCategories: ["Audio Buffers"]
property var settings: Settings {
category: root.objectName
property alias x: root.x
@ -16,4 +16,3 @@ PreferencesDialog {
property alias height: root.height
}
}

View file

@ -6,16 +6,10 @@ Item {
id: tablet
objectName: "tablet"
property double micLevel: 0.8
property bool micEnabled: true
property int rowIndex: 0
property int columnIndex: 0
property int count: (flowMain.children.length - 1)
// called by C++ code to keep mic state updated
function setMicEnabled(newMicEnabled) {
tablet.micEnabled = newMicEnabled;
}
// called by C++ code to keep audio bar updated
function setMicLevel(newMicLevel) {
tablet.micLevel = newMicLevel;
@ -121,8 +115,8 @@ Item {
}
Item {
visible: (!tablet.micEnabled && !toggleMuteMouseArea.containsMouse)
|| (tablet.micEnabled && toggleMuteMouseArea.containsMouse)
visible: (Audio.muted && !toggleMuteMouseArea.containsMouse)
|| (!Audio.muted && toggleMuteMouseArea.containsMouse)
Image {
id: muteIcon
@ -201,7 +195,7 @@ Item {
preventStealing: true
propagateComposedEvents: false
scrollGestureEnabled: false
onClicked: tabletRoot.toggleMicEnabled()
onClicked: { Audio.muted = !Audio.muted }
}
RalewaySemiBold {
@ -271,7 +265,7 @@ Item {
PropertyChanges {
target: muteIcon
visible: micEnabled
visible: !Audio.muted
}
PropertyChanges {

View file

@ -1,7 +1,7 @@
//
// TabletAudioPreferences.qml
// TabletAudioBuffers.qml
//
// Created by Davd Rowe on 7 Mar 2017.
// Created by Zach Pomerantz on 6/5/2017.
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
@ -17,7 +17,9 @@ StackView {
id: profileRoot
initialItem: root
objectName: "stack"
property string title: "Audio Settings"
property string title: "Audio Buffers"
property alias gotoPreviousApp: root.gotoPreviousApp;
property var eventBridge;
signal sendToScript(var message);
@ -31,7 +33,7 @@ StackView {
TabletPreferencesDialog {
id: root
objectName: "TabletAudioPreferences"
showCategories: ["Audio"]
objectName: "TabletAudioBuffersDialog"
showCategories: ["Audio Buffers"]
}
}

View file

@ -147,10 +147,6 @@ Item {
}
}
function toggleMicEnabled() {
ApplicationInterface.toggleMuteAudio();
}
function setUsername(newUsername) {
username = newUsername;
}

View file

@ -71,10 +71,6 @@ Windows.ScrollingWindow {
}
}
function toggleMicEnabled() {
ApplicationInterface.toggleMuteAudio();
}
function setUsername(newUsername) {
username = newUsername;
}

View file

@ -0,0 +1,41 @@
//
// Separator.qml
//
// Created by Zach Fox on 2017-06-06
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import "../styles-uit"
Item {
// Size
height: 2;
width: parent.width;
Rectangle {
// Size
width: parent.width;
height: 1;
// Anchors
anchors.left: parent.left;
anchors.bottom: parent.bottom;
anchors.bottomMargin: height;
// Style
color: hifi.colors.baseGrayShadow;
}
Rectangle {
// Size
width: parent.width;
height: 1;
// Anchors
anchors.left: parent.left;
anchors.bottom: parent.bottom;
// Style
color: hifi.colors.baseGrayHighlight;
}
}

View file

@ -150,11 +150,11 @@
#include "InterfaceLogging.h"
#include "LODManager.h"
#include "ModelPackager.h"
#include "scripting/Audio.h"
#include "networking/CloseEventSender.h"
#include "scripting/TestScriptingInterface.h"
#include "scripting/AccountScriptingInterface.h"
#include "scripting/AssetMappingsScriptingInterface.h"
#include "scripting/AudioDeviceScriptingInterface.h"
#include "scripting/ClipboardScriptingInterface.h"
#include "scripting/DesktopScriptingInterface.h"
#include "scripting/GlobalServicesScriptingInterface.h"
@ -251,6 +251,8 @@ static const QString MARKETPLACE_CDN_HOSTNAME = "mpassets.highfidelity.com";
static const int INTERVAL_TO_CHECK_HMD_WORN_STATUS = 500; // milliseconds
static const QString DESKTOP_DISPLAY_PLUGIN_NAME = "Desktop";
static const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system";
const QHash<QString, Application::AcceptURLMethod> Application::_acceptedExtensions {
{ SVO_EXTENSION, &Application::importSVOFromURL },
{ SVO_JSON_EXTENSION, &Application::importSVOFromURL },
@ -604,7 +606,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
_undoStackScriptingInterface(&_undoStack),
_entitySimulation(new PhysicalEntitySimulation()),
_physicsEngine(new PhysicsEngine(Vectors::ZERO)),
_entityClipboardRenderer(false, this, this),
_entityClipboard(new EntityTree()),
_lastQueriedTime(usecTimestampNow()),
_previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION),
@ -737,9 +738,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
recorder->recordFrame(AUDIO_FRAME_TYPE, audio);
}
});
audioIO->startThread();
auto audioScriptingInterface = DependencyManager::set<AudioScriptingInterface>();
connect(audioIO.data(), &AudioClient::muteToggled, this, &Application::audioMuteToggled);
auto audioScriptingInterface = DependencyManager::set<AudioScriptingInterface, scripting::Audio>();
connect(audioIO.data(), &AudioClient::mutedByMixer, audioScriptingInterface.data(), &AudioScriptingInterface::mutedByMixer);
connect(audioIO.data(), &AudioClient::receivedFirstPacket, audioScriptingInterface.data(), &AudioScriptingInterface::receivedFirstPacket);
connect(audioIO.data(), &AudioClient::disconnected, audioScriptingInterface.data(), &AudioScriptingInterface::disconnected);
@ -755,8 +756,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
audioScriptingInterface->environmentMuted();
}
});
audioIO->startThread();
connect(this, &Application::activeDisplayPluginChanged,
reinterpret_cast<scripting::Audio*>(audioScriptingInterface.data()), &scripting::Audio::onContextChanged);
ResourceManager::init();
// Make sure we don't time out during slow operations at startup
@ -979,7 +980,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// Make sure we don't time out during slow operations at startup
updateHeartbeat();
connect(this, SIGNAL(aboutToQuit()), this, SLOT(aboutToQuit()));
connect(this, SIGNAL(aboutToQuit()), this, SLOT(onAboutToQuit()));
// hook up bandwidth estimator
QSharedPointer<BandwidthRecorder> bandwidthRecorder = DependencyManager::get<BandwidthRecorder>();
@ -1473,7 +1474,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
});
sendStatsTimer->start();
// Periodically check for count of nearby avatars
static int lastCountOfNearbyAvatars = -1;
QTimer* checkNearbyAvatarsTimer = new QTimer(this);
@ -1631,12 +1631,12 @@ QString Application::getUserAgent() {
return userAgent;
}
void Application::toggleTabletUI() const {
void Application::toggleTabletUI(bool shouldOpen) const {
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
auto hmd = DependencyManager::get<HMDScriptingInterface>();
TabletProxy* tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
TabletProxy* tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet(SYSTEM_TABLET));
bool messageOpen = tablet->isMessageDialogOpen();
if (!messageOpen || (messageOpen && !hmd->getShouldShowTablet())) {
if ((!messageOpen || (messageOpen && !hmd->getShouldShowTablet())) && !(shouldOpen && hmd->getShouldShowTablet())) {
auto HMD = DependencyManager::get<HMDScriptingInterface>();
HMD->toggleShouldShowTablet();
}
@ -1668,7 +1668,7 @@ void Application::updateHeartbeat() const {
static_cast<DeadlockWatchdogThread*>(_deadlockWatchdogThread)->updateHeartbeat();
}
void Application::aboutToQuit() {
void Application::onAboutToQuit() {
emit beforeAboutToQuit();
foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) {
@ -1747,6 +1747,10 @@ void Application::cleanupBeforeQuit() {
// Cleanup all overlays after the scripts, as scripts might add more
_overlays.cleanupAllOverlays();
// The cleanup process enqueues the transactions but does not process them. Calling this here will force the actual
// removal of the items.
// See https://highfidelity.fogbugz.com/f/cases/5328
_main3DScene->processTransactionQueue();
// first stop all timers directly or by invokeMethod
// depending on what thread they run in
@ -2009,7 +2013,6 @@ void Application::initializeUi() {
surfaceContext->setContextProperty("Stats", Stats::getInstance());
surfaceContext->setContextProperty("Settings", SettingsScriptingInterface::getInstance());
surfaceContext->setContextProperty("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data());
surfaceContext->setContextProperty("AudioDevice", AudioDeviceScriptingInterface::getInstance());
surfaceContext->setContextProperty("AvatarBookmarks", DependencyManager::get<AvatarBookmarks>().data());
surfaceContext->setContextProperty("LocationBookmarks", DependencyManager::get<LocationBookmarks>().data());
@ -2338,12 +2341,6 @@ void Application::runTests() {
runUnitTests();
}
void Application::audioMuteToggled() const {
QAction* muteAction = Menu::getInstance()->getActionForOption(MenuOption::MuteAudio);
Q_CHECK_PTR(muteAction);
muteAction->setChecked(DependencyManager::get<AudioClient>()->isMuted());
}
void Application::faceTrackerMuteToggled() {
QAction* muteAction = Menu::getInstance()->getActionForOption(MenuOption::MuteFaceTracking);
@ -2413,7 +2410,7 @@ void Application::showHelp() {
queryString.addQueryItem("handControllerName", handControllerName);
queryString.addQueryItem("defaultTab", defaultTab);
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
TabletProxy* tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
TabletProxy* tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet(SYSTEM_TABLET));
tablet->gotoWebScreen(INFO_HELP_PATH + "?" + queryString.toString());
//InfoView::show(INFO_HELP_PATH, false, queryString.toString());
}
@ -2869,7 +2866,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
if (isShifted && isMeta) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->togglePinned();
//offscreenUi->getRootContext()->engine()->clearComponentCache();
//offscreenUi->getSurfaceContext()->engine()->clearComponentCache();
//OffscreenUi::information("Debugging", "Component cache cleared");
// placeholder for dialogs being converted to QML.
}
@ -2910,6 +2907,12 @@ void Application::keyPressEvent(QKeyEvent* event) {
Menu::getInstance()->triggerOption(MenuOption::DefaultSkybox);
break;
case Qt::Key_M:
if (isMeta) {
DependencyManager::get<AudioClient>()->toggleMute();
}
break;
case Qt::Key_N:
if (!isOption && !isShifted && isMeta) {
DependencyManager::get<NodeList>()->toggleIgnoreRadius();
@ -4009,11 +4012,6 @@ void Application::init() {
DependencyManager::get<NodeList>()->sendDomainServerCheckIn();
getEntities()->init();
{
QMutexLocker viewLocker(&_viewMutex);
getEntities()->setViewFrustum(_viewFrustum);
}
getEntities()->setEntityLoadingPriorityFunction([this](const EntityItem& item) {
auto dims = item.getDimensions();
auto maxSize = glm::compMax(dims);
@ -4043,13 +4041,6 @@ void Application::init() {
// of events related clicking, hovering over, and entering entities
getEntities()->connectSignalsToSlots(entityScriptingInterface.data());
_entityClipboardRenderer.init();
{
QMutexLocker viewLocker(&_viewMutex);
_entityClipboardRenderer.setViewFrustum(_viewFrustum);
}
_entityClipboardRenderer.setTree(_entityClipboard);
// Make sure any new sounds are loaded as soon as know about them.
connect(tree.get(), &EntityTree::newCollisionSoundURL, this, [this](QUrl newURL, EntityItemID id) {
EntityTreePointer tree = getEntities()->getTree();
@ -4494,10 +4485,11 @@ void Application::update(float deltaTime) {
} else {
const quint64 MUTE_MICROPHONE_AFTER_USECS = 5000000; //5 secs
Menu* menu = Menu::getInstance();
if (menu->isOptionChecked(MenuOption::AutoMuteAudio) && !menu->isOptionChecked(MenuOption::MuteAudio)) {
auto audioClient = DependencyManager::get<AudioClient>();
if (menu->isOptionChecked(MenuOption::AutoMuteAudio) && !audioClient->isMuted()) {
if (_lastFaceTrackerUpdate > 0
&& ((usecTimestampNow() - _lastFaceTrackerUpdate) > MUTE_MICROPHONE_AFTER_USECS)) {
menu->triggerOption(MenuOption::MuteAudio);
audioClient->toggleMute();
_lastFaceTrackerUpdate = 0;
}
} else {
@ -5041,9 +5033,6 @@ QRect Application::getDesirableApplicationGeometry() const {
return applicationGeometry;
}
// FIXME, preprocessor guard this check to occur only in DEBUG builds
static QThread * activeRenderingThread = nullptr;
PickRay Application::computePickRay(float x, float y) const {
vec2 pickPoint { x, y };
PickRay result;
@ -5114,7 +5103,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
auto myAvatar = getMyAvatar();
myAvatar->preDisplaySide(renderArgs);
activeRenderingThread = QThread::currentThread();
PROFILE_RANGE(render, __FUNCTION__);
PerformanceTimer perfTimer("display");
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide()");
@ -5187,8 +5175,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
// Before the deferred pass, let's try to use the render engine
_renderEngine->run();
}
activeRenderingThread = nullptr;
}
void Application::resetSensors(bool andReload) {
@ -5318,6 +5304,11 @@ void Application::nodeActivated(SharedNodePointer node) {
if (node->getType() == NodeType::AvatarMixer) {
// new avatar mixer, send off our identity packet on next update loop
// Reset skeletonModelUrl if the last server modified our choice.
static const QUrl empty{};
if (getMyAvatar()->getFullAvatarURLFromPreferences() != getMyAvatar()->cannonicalSkeletonModelURL(empty)) {
getMyAvatar()->resetFullAvatarURL();
}
getMyAvatar()->markIdentityDataChanged();
getMyAvatar()->resetLastSent();
}
@ -5538,7 +5529,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
scriptEngine->registerGlobalObject("Stats", Stats::getInstance());
scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance());
scriptEngine->registerGlobalObject("Snapshot", DependencyManager::get<Snapshot>().data());
scriptEngine->registerGlobalObject("AudioDevice", AudioDeviceScriptingInterface::getInstance());
scriptEngine->registerGlobalObject("AudioStats", DependencyManager::get<AudioClient>()->getStats().data());
scriptEngine->registerGlobalObject("AudioScope", DependencyManager::get<AudioScope>().data());
scriptEngine->registerGlobalObject("AvatarBookmarks", DependencyManager::get<AvatarBookmarks>().data());
@ -5817,38 +5807,24 @@ bool Application::displayAvatarAttachmentConfirmationDialog(const QString& name)
}
}
void Application::toggleRunningScriptsWidget() const {
auto scriptEngines = DependencyManager::get<ScriptEngines>();
bool scriptsRunning = !scriptEngines->getRunningScripts().isEmpty();
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
auto tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
void Application::showDialog(const QUrl& widgetUrl, const QUrl& tabletUrl, const QString& name) const {
auto tablet = DependencyManager::get<TabletScriptingInterface>()->getTablet(SYSTEM_TABLET);
auto hmd = DependencyManager::get<HMDScriptingInterface>();
bool onTablet = false;
if (tablet->getToolbarMode() || false == scriptsRunning) {
static const QUrl url("hifi/dialogs/RunningScripts.qml");
DependencyManager::get<OffscreenUi>()->show(url, "RunningScripts");
} else {
auto hmd = DependencyManager::get<HMDScriptingInterface>();
if (!hmd->getShouldShowTablet() && !isHMDMode()) {
static const QUrl url("hifi/dialogs/RunningScripts.qml");
DependencyManager::get<OffscreenUi>()->show(url, "RunningScripts");
} else {
static const QUrl url("../../hifi/dialogs/TabletRunningScripts.qml");
tablet->pushOntoStack(url);
if (!tablet->getToolbarMode()) {
onTablet = tablet->pushOntoStack(tabletUrl);
if (onTablet) {
toggleTabletUI(true);
}
}
//DependencyManager::get<OffscreenUi>()->show(url, "RunningScripts");
//if (_runningScriptsWidget->isVisible()) {
// if (_runningScriptsWidget->hasFocus()) {
// _runningScriptsWidget->hide();
// } else {
// _runningScriptsWidget->raise();
// setActiveWindow(_runningScriptsWidget);
// _runningScriptsWidget->setFocus();
// }
//} else {
// _runningScriptsWidget->show();
// _runningScriptsWidget->setFocus();
//}
if (!onTablet) {
DependencyManager::get<OffscreenUi>()->show(widgetUrl, name);
}
if (tablet->getToolbarMode()) {
DependencyManager::get<OffscreenUi>()->show(widgetUrl, name);
}
}
void Application::showScriptLogs() {
@ -5870,7 +5846,7 @@ void Application::showAssetServerWidget(QString filePath) {
}
};
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
auto tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
auto tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet(SYSTEM_TABLET));
auto hmd = DependencyManager::get<HMDScriptingInterface>();
if (tablet->getToolbarMode()) {
DependencyManager::get<OffscreenUi>()->show(url, "AssetServer", startUpload);
@ -5905,21 +5881,6 @@ void Application::addAssetToWorldFromURL(QString url) {
request->send();
}
void Application::showDialog(const QString& desktopURL, const QString& tabletURL, const QString& name) const {
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
auto tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
auto hmd = DependencyManager::get<HMDScriptingInterface>();
if (tablet->getToolbarMode()) {
DependencyManager::get<OffscreenUi>()->show(desktopURL, name);
} else {
tablet->pushOntoStack(tabletURL);
if (!hmd->getShouldShowTablet() && !isHMDMode()) {
hmd->openTablet();
}
}
}
void Application::addAssetToWorldFromURLRequestFinished() {
auto request = qobject_cast<ResourceRequest*>(sender());
auto url = request->getUrl().toString();
@ -6403,7 +6364,7 @@ void Application::loadScriptURLDialog() const {
void Application::loadLODToolsDialog() {
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
auto tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
auto tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet(SYSTEM_TABLET));
if (tablet->getToolbarMode() || (!tablet->getTabletRoot() && !isHMDMode())) {
auto dialogsManager = DependencyManager::get<DialogsManager>();
dialogsManager->lodTools();
@ -6415,7 +6376,7 @@ void Application::loadLODToolsDialog() {
void Application::loadEntityStatisticsDialog() {
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
auto tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
auto tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet(SYSTEM_TABLET));
if (tablet->getToolbarMode() || (!tablet->getTabletRoot() && !isHMDMode())) {
auto dialogsManager = DependencyManager::get<DialogsManager>();
dialogsManager->octreeStatsDetails();
@ -6426,7 +6387,7 @@ void Application::loadEntityStatisticsDialog() {
void Application::loadDomainConnectionDialog() {
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
auto tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
auto tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet(SYSTEM_TABLET));
if (tablet->getToolbarMode() || (!tablet->getTabletRoot() && !isHMDMode())) {
auto dialogsManager = DependencyManager::get<DialogsManager>();
dialogsManager->showDomainConnectionDialog();
@ -7083,11 +7044,6 @@ void Application::updateSystemTabletMode() {
}
}
void Application::toggleMuteAudio() {
auto menu = Menu::getInstance();
menu->setIsOptionChecked(MenuOption::MuteAudio, !menu->isOptionChecked(MenuOption::MuteAudio));
}
OverlayID Application::getTabletScreenID() const {
auto HMD = DependencyManager::get<HMDScriptingInterface>();
return HMD->getCurrentTabletScreenID();

View file

@ -181,7 +181,6 @@ public:
QUndoStack* getUndoStack() { return &_undoStack; }
MainWindow* getWindow() const { return _window; }
EntityTreePointer getEntityClipboard() const { return _entityClipboard; }
EntityTreeRenderer* getEntityClipboardRenderer() { return &_entityClipboardRenderer; }
EntityEditPacketSender* getEntityEditPacketSender() { return &_entityEditSender; }
ivec2 getMouse() const;
@ -318,11 +317,10 @@ public slots:
Q_INVOKABLE void loadScriptURLDialog() const;
void toggleLogDialog();
void toggleEntityScriptServerLogDialog();
void toggleRunningScriptsWidget() const;
Q_INVOKABLE void showAssetServerWidget(QString filePath = "");
Q_INVOKABLE void loadAddAvatarBookmarkDialog() const;
void showDialog(const QString& desktopURL, const QString& tabletURL, const QString& name) const;
void showDialog(const QUrl& widgetUrl, const QUrl& tabletUrl, const QString& name) const;
// FIXME: Move addAssetToWorld* methods to own class?
void addAssetToWorldFromURL(QString url);
@ -391,7 +389,6 @@ public slots:
void addAssetToWorldMessageClose();
Q_INVOKABLE void toggleMuteAudio();
void loadLODToolsDialog();
void loadEntityStatisticsDialog();
void loadDomainConnectionDialog();
@ -401,11 +398,10 @@ private slots:
void showDesktop();
void clearDomainOctreeDetails();
void clearDomainAvatars();
void aboutToQuit();
void onAboutToQuit();
void resettingDomain();
void audioMuteToggled() const;
void faceTrackerMuteToggled();
void activeChanged(Qt::ApplicationState state);
@ -503,7 +499,7 @@ private:
static void dragEnterEvent(QDragEnterEvent* event);
void maybeToggleMenuVisible(QMouseEvent* event) const;
void toggleTabletUI() const;
void toggleTabletUI(bool shouldOpen = false) const;
MainWindow* _window;
QElapsedTimer& _sessionRunTimer;
@ -535,7 +531,6 @@ private:
PhysicalEntitySimulationPointer _entitySimulation;
PhysicsEnginePointer _physicsEngine;
EntityTreeRenderer _entityClipboardRenderer;
EntityTreePointer _entityClipboard;
mutable QMutex _viewMutex { QMutex::Recursive };

View file

@ -17,6 +17,7 @@
#include <OctreeConstants.h>
#include <PIDController.h>
#include <SimpleMovingAverage.h>
#include <render/Args.h>
const float DEFAULT_DESKTOP_LOD_DOWN_FPS = 20.0;
const float DEFAULT_HMD_LOD_DOWN_FPS = 20.0;
@ -45,7 +46,6 @@ const float ADJUST_LOD_MAX_SIZE_SCALE = DEFAULT_OCTREE_SIZE_SCALE;
// This controls how low the auto-adjust LOD will go. We want a minimum vision of ~20:500 or 0.04 of default
const float ADJUST_LOD_MIN_SIZE_SCALE = DEFAULT_OCTREE_SIZE_SCALE * 0.04f;
class RenderArgs;
class AABox;
class LODManager : public QObject, public Dependency {

View file

@ -94,8 +94,13 @@ Menu::Menu() {
addActionToQMenuAndActionHash(editMenu, redoAction);
// Edit > Running Scripts
addActionToQMenuAndActionHash(editMenu, MenuOption::RunningScripts, Qt::CTRL | Qt::Key_J,
qApp, SLOT(toggleRunningScriptsWidget()));
auto action = addActionToQMenuAndActionHash(editMenu, MenuOption::RunningScripts, Qt::CTRL | Qt::Key_J);
connect(action, &QAction::triggered, [] {
static const QUrl widgetUrl("hifi/dialogs/RunningScripts.qml");
static const QUrl tabletUrl("../../hifi/dialogs/TabletRunningScripts.qml");
static const QString name("RunningScripts");
qApp->showDialog(widgetUrl, tabletUrl, name);
});
// Edit > Open and Run Script from File... [advanced]
addActionToQMenuAndActionHash(editMenu, MenuOption::LoadScript, Qt::CTRL | Qt::Key_O,
@ -143,28 +148,13 @@ Menu::Menu() {
addActionToQMenuAndActionHash(editMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches()),
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
// Audio menu ----------------------------------
MenuWrapper* audioMenu = addMenu("Audio");
auto audioIO = DependencyManager::get<AudioClient>();
// Audio > Mute
addCheckableActionToQMenuAndActionHash(audioMenu, MenuOption::MuteAudio, Qt::CTRL | Qt::Key_M, false,
audioIO.data(), SLOT(toggleMute()));
// Audio > Show Level Meter
addCheckableActionToQMenuAndActionHash(audioMenu, MenuOption::AudioTools, 0, false);
addCheckableActionToQMenuAndActionHash(audioMenu, MenuOption::AudioNoiseReduction, 0, true,
audioIO.data(), SLOT(toggleAudioNoiseReduction()));
// Avatar menu ----------------------------------
MenuWrapper* avatarMenu = addMenu("Avatar");
auto avatarManager = DependencyManager::get<AvatarManager>();
auto avatar = avatarManager->getMyAvatar();
// Avatar > Attachments...
auto action = addActionToQMenuAndActionHash(avatarMenu, MenuOption::Attachments);
action = addActionToQMenuAndActionHash(avatarMenu, MenuOption::Attachments);
connect(action, &QAction::triggered, [] {
qApp->showDialog(QString("hifi/dialogs/AttachmentsDialog.qml"),
QString("../../hifi/tablet/TabletAttachmentsDialog.qml"), "AttachmentsDialog");
@ -297,6 +287,14 @@ Menu::Menu() {
QString("../../hifi/tablet/TabletGeneralPreferences.qml"), "GeneralPreferencesDialog");
});
action = addActionToQMenuAndActionHash(settingsMenu, "Audio...");
connect(action, &QAction::triggered, [] {
static const QUrl widgetUrl("hifi/dialogs/Audio.qml");
static const QUrl tabletUrl("../../hifi/audio/Audio.qml");
static const QString name("AudioDialog");
qApp->showDialog(widgetUrl, tabletUrl, name);
});
// Settings > Avatar...
action = addActionToQMenuAndActionHash(settingsMenu, "Avatar...");
connect(action, &QAction::triggered, [] {
@ -622,10 +620,11 @@ Menu::Menu() {
action = addActionToQMenuAndActionHash(audioDebugMenu, "Buffers...");
connect(action, &QAction::triggered, [] {
qApp->showDialog(QString("hifi/dialogs/AudioPreferencesDialog.qml"),
QString("../../hifi/tablet/TabletAudioPreferences.qml"), "AudioPreferencesDialog");
qApp->showDialog(QString("hifi/dialogs/AudioBuffers.qml"),
QString("../../hifi/tablet/TabletAudioBuffers.qml"), "AudioBuffersDialog");
});
auto audioIO = DependencyManager::get<AudioClient>();
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoServerAudio, 0, false,
audioIO.data(), SLOT(toggleServerEcho()));
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoLocalAudio, 0, false,

View file

@ -36,7 +36,6 @@ namespace MenuOption {
const QString AssetMigration = "ATP Asset Migration";
const QString AssetServer = "Asset Browser";
const QString Attachments = "Attachments...";
const QString AudioNoiseReduction = "Noise Reduction";
const QString AudioScope = "Show Scope";
const QString AudioScopeFiftyFrames = "Fifty";
const QString AudioScopeFiveFrames = "Five";
@ -44,7 +43,6 @@ namespace MenuOption {
const QString AudioScopePause = "Pause Scope";
const QString AudioScopeTwentyFrames = "Twenty";
const QString AudioStatsShowInjectedStreams = "Audio Stats Show Injected Streams";
const QString AudioTools = "Show Level Meter";
const QString AutoMuteAudio = "Auto Mute Microphone";
const QString AvatarReceiveStats = "Show Receive Stats";
const QString AvatarBookmarks = "Avatar Bookmarks";
@ -124,7 +122,6 @@ namespace MenuOption {
const QString LogExtraTimings = "Log Extra Timing Details";
const QString LowVelocityFilter = "Low Velocity Filter";
const QString MeshVisible = "Draw Mesh";
const QString MuteAudio = "Mute Microphone";
const QString MuteEnvironment = "Mute Environment";
const QString MuteFaceTracking = "Mute Face Tracking";
const QString NamesAboveHeads = "Names Above Heads";

View file

@ -19,7 +19,6 @@
#include <DependencyManager.h>
#include <gpu/Batch.h>
#include <RenderArgs.h>
class AudioScope : public QObject, public Dependency {

View file

@ -1961,6 +1961,32 @@ void MyAvatar::updateOrientation(float deltaTime) {
totalBodyYaw += (speedFactor * deltaAngle * (180.0f / PI));
}
// Use head/HMD roll to turn while walking or flying.
if (qApp->isHMDMode() && _hmdRollControlEnabled) {
// Turn with head roll.
const float MIN_CONTROL_SPEED = 0.01f;
float speed = glm::length(getVelocity());
if (speed >= MIN_CONTROL_SPEED) {
// Feather turn when stopping moving.
float speedFactor;
if (getDriveKey(TRANSLATE_Z) != 0.0f || _lastDrivenSpeed == 0.0f) {
_lastDrivenSpeed = speed;
speedFactor = 1.0f;
} else {
speedFactor = glm::min(speed / _lastDrivenSpeed, 1.0f);
}
float direction = glm::dot(getVelocity(), getRotation() * Vectors::UNIT_NEG_Z) > 0.0f ? 1.0f : -1.0f;
float rollAngle = glm::degrees(asinf(glm::dot(IDENTITY_UP, _hmdSensorOrientation * IDENTITY_RIGHT)));
float rollSign = rollAngle < 0.0f ? -1.0f : 1.0f;
rollAngle = fabsf(rollAngle);
rollAngle = rollAngle > _hmdRollControlDeadZone ? rollSign * (rollAngle - _hmdRollControlDeadZone) : 0.0f;
totalBodyYaw += speedFactor * direction * rollAngle * deltaTime * _hmdRollControlRate;
}
}
// update body orientation by movement inputs
glm::quat initialOrientation = getOrientationOutbound();
setOrientation(getOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, totalBodyYaw, 0.0f))));

View file

@ -132,6 +132,10 @@ class MyAvatar : public Avatar {
Q_PROPERTY(bool characterControllerEnabled READ getCharacterControllerEnabled WRITE setCharacterControllerEnabled)
Q_PROPERTY(bool useAdvancedMovementControls READ useAdvancedMovementControls WRITE setUseAdvancedMovementControls)
Q_PROPERTY(bool hmdRollControlEnabled READ getHMDRollControlEnabled WRITE setHMDRollControlEnabled)
Q_PROPERTY(float hmdRollControlDeadZone READ getHMDRollControlDeadZone WRITE setHMDRollControlDeadZone)
Q_PROPERTY(float hmdRollControlRate READ getHMDRollControlRate WRITE setHMDRollControlRate)
public:
enum DriveKeys {
TRANSLATE_X = 0,
@ -337,6 +341,13 @@ public:
void setUseAdvancedMovementControls(bool useAdvancedMovementControls)
{ _useAdvancedMovementControls.set(useAdvancedMovementControls); }
void setHMDRollControlEnabled(bool value) { _hmdRollControlEnabled = value; }
bool getHMDRollControlEnabled() const { return _hmdRollControlEnabled; }
void setHMDRollControlDeadZone(float value) { _hmdRollControlDeadZone = value; }
float getHMDRollControlDeadZone() const { return _hmdRollControlDeadZone; }
void setHMDRollControlRate(float value) { _hmdRollControlRate = value; }
float getHMDRollControlRate() const { return _hmdRollControlRate; }
// get/set avatar data
void saveData();
void loadData();
@ -687,6 +698,13 @@ private:
bool _useSnapTurn { true };
bool _clearOverlayWhenMoving { true };
const float ROLL_CONTROL_DEAD_ZONE_DEFAULT = 8.0f; // deg
const float ROLL_CONTROL_RATE_DEFAULT = 2.5f; // deg/sec/deg
bool _hmdRollControlEnabled { true };
float _hmdRollControlDeadZone { ROLL_CONTROL_DEAD_ZONE_DEFAULT };
float _hmdRollControlRate { ROLL_CONTROL_RATE_DEFAULT };
float _lastDrivenSpeed { 0.0f };
// working copies -- see AvatarData for thread-safe _sensorToWorldMatrixCache, used for outward facing access
glm::mat4 _sensorToWorldMatrix { glm::mat4() };

View file

@ -0,0 +1,109 @@
//
// Audio.cpp
// interface/src/scripting
//
// Created by Zach Pomerantz on 28/5/2017.
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "Audio.h"
#include "Application.h"
#include "AudioClient.h"
#include "ui/AvatarInputs.h"
using namespace scripting;
QString Audio::AUDIO { "Audio" };
QString Audio::DESKTOP { "Desktop" };
QString Audio::HMD { "VR" };
Setting::Handle<bool> enableNoiseReductionSetting { QStringList { Audio::AUDIO, "NoiseReduction" }, true };
Audio::Audio() : _devices(_contextIsHMD) {
auto client = DependencyManager::get<AudioClient>();
connect(client.data(), &AudioClient::muteToggled, this, &Audio::onMutedChanged);
connect(this, &Audio::contextChanged, &_devices, &AudioDevices::onContextChanged);
connect(&_devices._inputs, &AudioDeviceList::deviceChanged, this, &Audio::onInputChanged);
enableNoiseReduction(enableNoiseReductionSetting.get());
}
void Audio::setMuted(bool isMuted) {
if (_isMuted != isMuted) {
auto client = DependencyManager::get<AudioClient>().data();
QMetaObject::invokeMethod(client, "toggleMute", Qt::BlockingQueuedConnection);
_isMuted = isMuted;
emit mutedChanged(_isMuted);
}
}
void Audio::onMutedChanged() {
auto client = DependencyManager::get<AudioClient>().data();
bool isMuted;
QMetaObject::invokeMethod(client, "isMuted", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, isMuted));
if (_isMuted != isMuted) {
_isMuted = isMuted;
emit mutedChanged(_isMuted);
}
}
void Audio::enableNoiseReduction(bool enable) {
if (_enableNoiseReduction != enable) {
auto client = DependencyManager::get<AudioClient>().data();
QMetaObject::invokeMethod(client, "setNoiseReduction", Qt::BlockingQueuedConnection, Q_ARG(bool, enable));
enableNoiseReductionSetting.set(enable);
_enableNoiseReduction = enable;
emit noiseReductionChanged(enable);
}
}
void Audio::setInputVolume(float volume) {
// getInputVolume will not reflect changes synchronously, so clamp beforehand
volume = glm::clamp(volume, 0.0f, 1.0f);
if (_inputVolume != volume) {
auto client = DependencyManager::get<AudioClient>().data();
QMetaObject::invokeMethod(client, "setInputVolume", Qt::BlockingQueuedConnection, Q_ARG(float, volume));
_inputVolume = volume;
emit inputVolumeChanged(_inputVolume);
}
}
// different audio input devices may have different volumes
void Audio::onInputChanged() {
auto client = DependencyManager::get<AudioClient>().data();
float volume;
QMetaObject::invokeMethod(client, "getInputVolume", Qt::BlockingQueuedConnection, Q_RETURN_ARG(float, volume));
if (_inputVolume != volume) {
_inputVolume = volume;
emit inputVolumeChanged(_inputVolume);
}
}
QString Audio::getContext() const {
return _contextIsHMD ? Audio::HMD : Audio::DESKTOP;
}
void Audio::onContextChanged() {
bool isHMD = qApp->isHMDMode();
if (_contextIsHMD != isHMD) {
_contextIsHMD = isHMD;
emit contextChanged(getContext());
}
}
void Audio::setReverb(bool enable) {
DependencyManager::get<AudioClient>()->setReverb(enable);
}
void Audio::setReverbOptions(const AudioEffectOptions* options) {
DependencyManager::get<AudioClient>()->setReverbOptions(options);
}

View file

@ -0,0 +1,81 @@
//
// Audio.h
// interface/src/scripting
//
// Created by Zach Pomerantz on 28/5/2017.
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_scripting_Audio_h
#define hifi_scripting_Audio_h
#include "AudioScriptingInterface.h"
#include "AudioDevices.h"
#include "AudioEffectOptions.h"
#include "SettingHandle.h"
namespace scripting {
class Audio : public AudioScriptingInterface {
Q_OBJECT
SINGLETON_DEPENDENCY
Q_PROPERTY(bool muted READ isMuted WRITE setMuted NOTIFY mutedChanged)
Q_PROPERTY(bool noiseReduction READ noiseReductionEnabled WRITE enableNoiseReduction NOTIFY noiseReductionChanged)
Q_PROPERTY(float inputVolume READ getInputVolume WRITE setInputVolume NOTIFY inputVolumeChanged)
Q_PROPERTY(QString context READ getContext NOTIFY contextChanged)
Q_PROPERTY(AudioDevices* devices READ getDevices NOTIFY nop)
public:
static QString AUDIO;
static QString HMD;
static QString DESKTOP;
virtual ~Audio() {}
bool isMuted() const { return _isMuted; }
bool noiseReductionEnabled() const { return _enableNoiseReduction; }
float getInputVolume() const { return _inputVolume; }
QString getContext() const;
void setMuted(bool muted);
void enableNoiseReduction(bool enable);
void showMicMeter(bool show);
void setInputVolume(float volume);
Q_INVOKABLE void setReverb(bool enable);
Q_INVOKABLE void setReverbOptions(const AudioEffectOptions* options);
signals:
void nop();
void mutedChanged(bool isMuted);
void noiseReductionChanged(bool isEnabled);
void inputVolumeChanged(float volume);
void contextChanged(const QString& context);
public slots:
void onMutedChanged();
void onContextChanged();
void onInputChanged();
protected:
// Audio must live on a separate thread from AudioClient to avoid deadlocks
Audio();
private:
float _inputVolume { 1.0f };
bool _isMuted { false };
bool _enableNoiseReduction;
bool _contextIsHMD { false };
AudioDevices* getDevices() { return &_devices; }
AudioDevices _devices;
};
};
#endif // hifi_scripting_Audio_h

View file

@ -1,301 +0,0 @@
//
// AudioDeviceScriptingInterface.cpp
// interface/src/scripting
//
// Created by Brad Hefta-Gaub on 3/23/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <AudioClient.h>
#include <AudioClientLogging.h>
#include "AudioDeviceScriptingInterface.h"
#include "SettingsScriptingInterface.h"
AudioDeviceScriptingInterface* AudioDeviceScriptingInterface::getInstance() {
static AudioDeviceScriptingInterface sharedInstance;
return &sharedInstance;
}
QStringList AudioDeviceScriptingInterface::inputAudioDevices() const {
return _inputAudioDevices;
}
QStringList AudioDeviceScriptingInterface::outputAudioDevices() const {
return _outputAudioDevices;
}
bool AudioDeviceScriptingInterface::muted()
{
return getMuted();
}
AudioDeviceScriptingInterface::AudioDeviceScriptingInterface(): QAbstractListModel(nullptr) {
connect(DependencyManager::get<AudioClient>().data(), &AudioClient::muteToggled,
this, &AudioDeviceScriptingInterface::muteToggled);
connect(DependencyManager::get<AudioClient>().data(), &AudioClient::deviceChanged,
this, &AudioDeviceScriptingInterface::onDeviceChanged, Qt::QueuedConnection);
connect(DependencyManager::get<AudioClient>().data(), &AudioClient::currentInputDeviceChanged,
this, &AudioDeviceScriptingInterface::onCurrentInputDeviceChanged, Qt::QueuedConnection);
connect(DependencyManager::get<AudioClient>().data(), &AudioClient::currentOutputDeviceChanged,
this, &AudioDeviceScriptingInterface::onCurrentOutputDeviceChanged, Qt::QueuedConnection);
//fill up model
onDeviceChanged();
//set up previously saved device
SettingsScriptingInterface* settings = SettingsScriptingInterface::getInstance();
const QString inDevice = settings->getValue("audio_input_device", _currentInputDevice).toString();
if (inDevice != _currentInputDevice) {
qCDebug(audioclient) << __FUNCTION__ << "about to call setInputDeviceAsync() device: [" << inDevice << "] _currentInputDevice:" << _currentInputDevice;
setInputDeviceAsync(inDevice);
}
// If the audio_output_device setting is not available, use the _currentOutputDevice
auto outDevice = settings->getValue("audio_output_device", _currentOutputDevice).toString();
if (outDevice != _currentOutputDevice) {
qCDebug(audioclient) << __FUNCTION__ << "about to call setOutputDeviceAsync() outDevice: [" << outDevice << "] _currentOutputDevice:" << _currentOutputDevice;
setOutputDeviceAsync(outDevice);
}
}
bool AudioDeviceScriptingInterface::setInputDevice(const QString& deviceName) {
qCDebug(audioclient) << __FUNCTION__ << "deviceName:" << deviceName;
bool result;
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "switchInputToAudioDevice",
Qt::BlockingQueuedConnection,
Q_RETURN_ARG(bool, result),
Q_ARG(const QString&, deviceName));
return result;
}
bool AudioDeviceScriptingInterface::setOutputDevice(const QString& deviceName) {
qCDebug(audioclient) << __FUNCTION__ << "deviceName:" << deviceName;
bool result;
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "switchOutputToAudioDevice",
Qt::BlockingQueuedConnection,
Q_RETURN_ARG(bool, result),
Q_ARG(const QString&, deviceName));
return result;
}
bool AudioDeviceScriptingInterface::setDeviceFromMenu(const QString& deviceMenuName) {
QAudio::Mode mode;
if (deviceMenuName.indexOf("for Output") != -1) {
mode = QAudio::AudioOutput;
} else if (deviceMenuName.indexOf("for Input") != -1) {
mode = QAudio::AudioInput;
} else {
return false;
}
for (ScriptingAudioDeviceInfo di: _devices) {
if (mode == di.mode && deviceMenuName.contains(di.name)) {
if (mode == QAudio::AudioOutput) {
qCDebug(audioclient) << __FUNCTION__ << "about to call setOutputDeviceAsync() device: [" << di.name << "]";
setOutputDeviceAsync(di.name);
} else {
qCDebug(audioclient) << __FUNCTION__ << "about to call setInputDeviceAsync() device: [" << di.name << "]";
setInputDeviceAsync(di.name);
}
return true;
}
}
return false;
}
void AudioDeviceScriptingInterface::setInputDeviceAsync(const QString& deviceName) {
qCDebug(audioclient) << __FUNCTION__ << "deviceName:" << deviceName;
if (deviceName.isEmpty()) {
qCDebug(audioclient) << __FUNCTION__ << "attempt to set empty deviceName:" << deviceName << "... ignoring!";
return;
}
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "switchInputToAudioDevice",
Qt::QueuedConnection,
Q_ARG(const QString&, deviceName));
}
void AudioDeviceScriptingInterface::setOutputDeviceAsync(const QString& deviceName) {
qCDebug(audioclient) << __FUNCTION__ << "deviceName:" << deviceName;
if (deviceName.isEmpty()) {
qCDebug(audioclient) << __FUNCTION__ << "attempt to set empty deviceName:" << deviceName << "... ignoring!";
return;
}
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "switchOutputToAudioDevice",
Qt::QueuedConnection,
Q_ARG(const QString&, deviceName));
}
QString AudioDeviceScriptingInterface::getInputDevice() {
return DependencyManager::get<AudioClient>()->getDeviceName(QAudio::AudioInput);
}
QString AudioDeviceScriptingInterface::getOutputDevice() {
return DependencyManager::get<AudioClient>()->getDeviceName(QAudio::AudioOutput);
}
QString AudioDeviceScriptingInterface::getDefaultInputDevice() {
return DependencyManager::get<AudioClient>()->getDefaultDeviceName(QAudio::AudioInput);
}
QString AudioDeviceScriptingInterface::getDefaultOutputDevice() {
return DependencyManager::get<AudioClient>()->getDefaultDeviceName(QAudio::AudioOutput);
}
QVector<QString> AudioDeviceScriptingInterface::getInputDevices() {
return DependencyManager::get<AudioClient>()->getDeviceNames(QAudio::AudioInput);
}
QVector<QString> AudioDeviceScriptingInterface::getOutputDevices() {
return DependencyManager::get<AudioClient>()->getDeviceNames(QAudio::AudioOutput);
}
float AudioDeviceScriptingInterface::getInputVolume() {
return DependencyManager::get<AudioClient>()->getInputVolume();
}
void AudioDeviceScriptingInterface::setInputVolume(float volume) {
DependencyManager::get<AudioClient>()->setInputVolume(volume);
}
void AudioDeviceScriptingInterface::setReverb(bool reverb) {
DependencyManager::get<AudioClient>()->setReverb(reverb);
}
void AudioDeviceScriptingInterface::setReverbOptions(const AudioEffectOptions* options) {
DependencyManager::get<AudioClient>()->setReverbOptions(options);
}
void AudioDeviceScriptingInterface::toggleMute() {
DependencyManager::get<AudioClient>()->toggleMute();
}
void AudioDeviceScriptingInterface::setMuted(bool muted)
{
bool lMuted = getMuted();
if (lMuted == muted)
return;
toggleMute();
lMuted = getMuted();
emit mutedChanged(lMuted);
}
bool AudioDeviceScriptingInterface::getMuted() {
return DependencyManager::get<AudioClient>()->isMuted();
}
QVariant AudioDeviceScriptingInterface::data(const QModelIndex& index, int role) const {
//sanity
if (!index.isValid() || index.row() >= _devices.size())
return QVariant();
if (role == Qt::DisplayRole || role == DisplayNameRole) {
return _devices.at(index.row()).name;
} else if (role == SelectedRole) {
return _devices.at(index.row()).selected;
} else if (role == AudioModeRole) {
return (int)_devices.at(index.row()).mode;
}
return QVariant();
}
int AudioDeviceScriptingInterface::rowCount(const QModelIndex& parent) const {
Q_UNUSED(parent)
return _devices.size();
}
QHash<int, QByteArray> AudioDeviceScriptingInterface::roleNames() const {
QHash<int, QByteArray> roles;
roles.insert(DisplayNameRole, "devicename");
roles.insert(SelectedRole, "devicechecked");
roles.insert(AudioModeRole, "devicemode");
return roles;
}
void AudioDeviceScriptingInterface::onDeviceChanged()
{
beginResetModel();
_outputAudioDevices.clear();
_devices.clear();
_currentOutputDevice = getOutputDevice();
for (QString name: getOutputDevices()) {
ScriptingAudioDeviceInfo di;
di.name = name;
di.selected = (name == _currentOutputDevice);
di.mode = QAudio::AudioOutput;
_devices.append(di);
_outputAudioDevices.append(name);
}
emit outputAudioDevicesChanged(_outputAudioDevices);
_inputAudioDevices.clear();
_currentInputDevice = getInputDevice();
for (QString name: getInputDevices()) {
ScriptingAudioDeviceInfo di;
di.name = name;
di.selected = (name == _currentInputDevice);
di.mode = QAudio::AudioInput;
_devices.append(di);
_inputAudioDevices.append(name);
}
emit inputAudioDevicesChanged(_inputAudioDevices);
endResetModel();
emit deviceChanged();
}
void AudioDeviceScriptingInterface::onCurrentInputDeviceChanged(const QString& name)
{
currentDeviceUpdate(name, QAudio::AudioInput);
//we got a signal that device changed. Save it now
SettingsScriptingInterface* settings = SettingsScriptingInterface::getInstance();
settings->setValue("audio_input_device", name);
emit currentInputDeviceChanged(name);
}
void AudioDeviceScriptingInterface::onCurrentOutputDeviceChanged(const QString& name)
{
currentDeviceUpdate(name, QAudio::AudioOutput);
// FIXME - this is kinda janky to set the setting on the result of a signal
//we got a signal that device changed. Save it now
SettingsScriptingInterface* settings = SettingsScriptingInterface::getInstance();
qCDebug(audioclient) << __FUNCTION__ << "about to call settings->setValue('audio_output_device', name); name:" << name;
settings->setValue("audio_output_device", name);
emit currentOutputDeviceChanged(name);
}
void AudioDeviceScriptingInterface::currentDeviceUpdate(const QString& name, QAudio::Mode mode)
{
QVector<int> role;
role.append(SelectedRole);
for (int i = 0; i < _devices.size(); i++) {
ScriptingAudioDeviceInfo di = _devices.at(i);
if (di.mode != mode) {
continue;
}
if (di.selected && di.name != name ) {
di.selected = false;
_devices[i] = di;
emit dataChanged(index(i, 0), index(i, 0), role);
}
if (di.name == name) {
di.selected = true;
_devices[i] = di;
emit dataChanged(index(i, 0), index(i, 0), role);
}
}
}

View file

@ -1,107 +0,0 @@
//
// AudioDeviceScriptingInterface.h
// interface/src/scripting
//
// Created by Brad Hefta-Gaub on 3/22/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_AudioDeviceScriptingInterface_h
#define hifi_AudioDeviceScriptingInterface_h
#include <QObject>
#include <QString>
#include <QVector>
#include <QAbstractListModel>
#include <QAudio>
class AudioEffectOptions;
struct ScriptingAudioDeviceInfo {
QString name;
bool selected;
QAudio::Mode mode;
};
class AudioDeviceScriptingInterface : public QAbstractListModel {
Q_OBJECT
Q_PROPERTY(QStringList inputAudioDevices READ inputAudioDevices NOTIFY inputAudioDevicesChanged)
Q_PROPERTY(QStringList outputAudioDevices READ outputAudioDevices NOTIFY outputAudioDevicesChanged)
Q_PROPERTY(bool muted READ muted WRITE setMuted NOTIFY mutedChanged)
public:
static AudioDeviceScriptingInterface* getInstance();
QStringList inputAudioDevices() const;
QStringList outputAudioDevices() const;
bool muted();
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
QHash<int, QByteArray> roleNames() const override;
enum Roles {
DisplayNameRole = Qt::UserRole,
SelectedRole,
AudioModeRole
};
private slots:
void onDeviceChanged();
void onCurrentInputDeviceChanged(const QString& name);
void onCurrentOutputDeviceChanged(const QString& name);
void currentDeviceUpdate(const QString& name, QAudio::Mode mode);
public slots:
bool setInputDevice(const QString& deviceName);
bool setOutputDevice(const QString& deviceName);
bool setDeviceFromMenu(const QString& deviceMenuName);
QString getInputDevice();
QString getOutputDevice();
QString getDefaultInputDevice();
QString getDefaultOutputDevice();
QVector<QString> getInputDevices();
QVector<QString> getOutputDevices();
float getInputVolume();
void setInputVolume(float volume);
void setReverb(bool reverb);
void setReverbOptions(const AudioEffectOptions* options);
bool getMuted();
void toggleMute();
void setMuted(bool muted);
void setInputDeviceAsync(const QString& deviceName);
void setOutputDeviceAsync(const QString& deviceName);
private:
AudioDeviceScriptingInterface();
signals:
void muteToggled();
void deviceChanged();
void currentInputDeviceChanged(const QString& name);
void currentOutputDeviceChanged(const QString& name);
void mutedChanged(bool muted);
void inputAudioDevicesChanged(QStringList inputAudioDevices);
void outputAudioDevicesChanged(QStringList outputAudioDevices);
private:
QVector<ScriptingAudioDeviceInfo> _devices;
QStringList _inputAudioDevices;
QStringList _outputAudioDevices;
QString _currentInputDevice;
QString _currentOutputDevice;
};
#endif // hifi_AudioDeviceScriptingInterface_h

View file

@ -0,0 +1,242 @@
//
// AudioDevices.cpp
// interface/src/scripting
//
// Created by Zach Pomerantz on 28/5/2017.
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "AudioDevices.h"
#include "Application.h"
#include "AudioClient.h"
#include "Audio.h"
using namespace scripting;
Setting::Handle<QString> inputDeviceDesktop { QStringList { Audio::AUDIO, Audio::DESKTOP, "INPUT" }};
Setting::Handle<QString> outputDeviceDesktop { QStringList { Audio::AUDIO, Audio::DESKTOP, "OUTPUT" }};
Setting::Handle<QString> inputDeviceHMD { QStringList { Audio::AUDIO, Audio::HMD, "INPUT" }};
Setting::Handle<QString> outputDeviceHMD { QStringList { Audio::AUDIO, Audio::HMD, "OUTPUT" }};
QHash<int, QByteArray> AudioDeviceList::_roles {
{ Qt::DisplayRole, "display" },
{ Qt::CheckStateRole, "selected" }
};
Qt::ItemFlags AudioDeviceList::_flags { Qt::ItemIsSelectable | Qt::ItemIsEnabled };
QVariant AudioDeviceList::data(const QModelIndex& index, int role) const {
if (!index.isValid() || index.row() >= _devices.size()) {
return QVariant();
}
if (role == Qt::DisplayRole) {
return _devices.at(index.row()).display;
} else if (role == Qt::CheckStateRole) {
return _devices.at(index.row()).selected;
} else {
return QVariant();
}
}
bool AudioDeviceList::setData(const QModelIndex& index, const QVariant& value, int role) {
if (!index.isValid() || index.row() >= _devices.size()) {
return false;
}
bool success = false;
if (role == Qt::CheckStateRole) {
auto selected = value.toBool();
auto& device = _devices[index.row()];
// only allow switching to a new device, not deactivating an in-use device
if (selected
// skip if already selected
&& selected != device.selected) {
auto client = DependencyManager::get<AudioClient>();
QMetaObject::invokeMethod(client.data(), "switchAudioDevice", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(bool, success),
Q_ARG(QAudio::Mode, _mode),
Q_ARG(const QAudioDeviceInfo&, device.info));
if (success) {
device.selected = true;
emit deviceSelected(device.info);
emit deviceChanged(device.info);
}
}
}
emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0));
return success;
}
void AudioDeviceList::resetDevice(bool contextIsHMD, const QString& device) {
bool success { false };
// try to set the last selected device
if (!device.isNull()) {
auto i = 0;
for (; i < rowCount(); ++i) {
if (device == _devices[i].info.deviceName()) {
break;
}
}
if (i < rowCount()) {
success = setData(createIndex(i, 0), true, Qt::CheckStateRole);
}
// the selection failed - reset it
if (!success) {
emit deviceSelected(QAudioDeviceInfo());
}
}
// try to set to the default device for this mode
if (!success) {
auto client = DependencyManager::get<AudioClient>().data();
if (contextIsHMD) {
QString deviceName;
if (_mode == QAudio::AudioInput) {
deviceName = qApp->getActiveDisplayPlugin()->getPreferredAudioInDevice();
} else { // if (_mode == QAudio::AudioOutput)
deviceName = qApp->getActiveDisplayPlugin()->getPreferredAudioOutDevice();
}
if (!deviceName.isNull()) {
QMetaObject::invokeMethod(client, "switchAudioDevice", Q_ARG(QAudio::Mode, _mode), Q_ARG(QString, deviceName));
}
} else {
// use the system default
QMetaObject::invokeMethod(client, "switchAudioDevice", Q_ARG(QAudio::Mode, _mode));
}
}
}
void AudioDeviceList::onDeviceChanged(const QAudioDeviceInfo& device) {
_selectedDevice = device;
QModelIndex index;
for (auto i = 0; i < _devices.size(); ++i) {
AudioDevice& device = _devices[i];
if (device.selected && device.info != _selectedDevice) {
device.selected = false;
} else if (device.info == _selectedDevice) {
device.selected = true;
index = createIndex(i, 0);
}
}
emit deviceChanged(_selectedDevice);
emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0));
}
void AudioDeviceList::onDevicesChanged(const QList<QAudioDeviceInfo>& devices) {
beginResetModel();
_devices.clear();
foreach(const QAudioDeviceInfo& deviceInfo, devices) {
AudioDevice device;
device.info = deviceInfo;
device.display = device.info.deviceName()
.replace("High Definition", "HD")
.remove("Device")
.replace(" )", ")");
device.selected = (device.info == _selectedDevice);
_devices.push_back(device);
}
endResetModel();
}
AudioDevices::AudioDevices(bool& contextIsHMD) : _contextIsHMD(contextIsHMD) {
auto client = DependencyManager::get<AudioClient>();
connect(client.data(), &AudioClient::deviceChanged, this, &AudioDevices::onDeviceChanged, Qt::QueuedConnection);
connect(client.data(), &AudioClient::devicesChanged, this, &AudioDevices::onDevicesChanged, Qt::QueuedConnection);
// connections are made after client is initialized, so we must also fetch the devices
_inputs.onDeviceChanged(client->getActiveAudioDevice(QAudio::AudioInput));
_outputs.onDeviceChanged(client->getActiveAudioDevice(QAudio::AudioOutput));
_inputs.onDevicesChanged(client->getAudioDevices(QAudio::AudioInput));
_outputs.onDevicesChanged(client->getAudioDevices(QAudio::AudioOutput));
connect(&_inputs, &AudioDeviceList::deviceSelected, this, &AudioDevices::onInputDeviceSelected);
connect(&_outputs, &AudioDeviceList::deviceSelected, this, &AudioDevices::onOutputDeviceSelected);
}
void AudioDevices::onContextChanged(const QString& context) {
QString input;
QString output;
if (_contextIsHMD) {
input = inputDeviceHMD.get();
output = outputDeviceHMD.get();
} else {
input = inputDeviceDesktop.get();
output = outputDeviceDesktop.get();
}
_inputs.resetDevice(_contextIsHMD, input);
_outputs.resetDevice(_contextIsHMD, output);
}
void AudioDevices::onInputDeviceSelected(const QAudioDeviceInfo& device) {
QString deviceName;
if (!device.isNull()) {
deviceName = device.deviceName();
}
if (_contextIsHMD) {
inputDeviceHMD.set(deviceName);
} else {
inputDeviceDesktop.set(deviceName);
}
}
void AudioDevices::onOutputDeviceSelected(const QAudioDeviceInfo& device) {
QString deviceName;
if (!device.isNull()) {
deviceName = device.deviceName();
}
if (_contextIsHMD) {
outputDeviceHMD.set(deviceName);
} else {
outputDeviceDesktop.set(deviceName);
}
}
void AudioDevices::onDeviceChanged(QAudio::Mode mode, const QAudioDeviceInfo& device) {
if (mode == QAudio::AudioInput) {
_inputs.onDeviceChanged(device);
} else { // if (mode == QAudio::AudioOutput)
_outputs.onDeviceChanged(device);
}
}
void AudioDevices::onDevicesChanged(QAudio::Mode mode, const QList<QAudioDeviceInfo>& devices) {
static bool initialized { false };
auto initialize = [&]{
if (initialized) {
onContextChanged(QString());
} else {
initialized = true;
}
};
if (mode == QAudio::AudioInput) {
_inputs.onDevicesChanged(devices);
static std::once_flag inputFlag;
std::call_once(inputFlag, initialize);
} else { // if (mode == QAudio::AudioOutput)
_outputs.onDevicesChanged(devices);
static std::once_flag outputFlag;
std::call_once(outputFlag, initialize);
}
}

View file

@ -0,0 +1,98 @@
//
// AudioDevices.h
// interface/src/scripting
//
// Created by Zach Pomerantz on 28/5/2017.
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_scripting_AudioDevices_h
#define hifi_scripting_AudioDevices_h
#include <QObject>
#include <QAbstractListModel>
#include <QAudioDeviceInfo>
namespace scripting {
class AudioDevice {
public:
QAudioDeviceInfo info;
QString display;
bool selected { false };
};
class AudioDeviceList : public QAbstractListModel {
Q_OBJECT
public:
AudioDeviceList(QAudio::Mode mode) : _mode(mode) {}
int rowCount(const QModelIndex& parent = QModelIndex()) const override { Q_UNUSED(parent); return _devices.size(); }
QHash<int, QByteArray> roleNames() const override { return _roles; }
Qt::ItemFlags flags(const QModelIndex& index) const override { return _flags; }
// get/set devices through a QML ListView
QVariant data(const QModelIndex& index, int role) const override;
bool setData(const QModelIndex& index, const QVariant &value, int role) override;
// reset device to the last selected device in this context, or the default
void resetDevice(bool contextIsHMD, const QString& device);
signals:
void deviceSelected(const QAudioDeviceInfo& device);
void deviceChanged(const QAudioDeviceInfo& device);
private slots:
void onDeviceChanged(const QAudioDeviceInfo& device);
void onDevicesChanged(const QList<QAudioDeviceInfo>& devices);
private:
friend class AudioDevices;
static QHash<int, QByteArray> _roles;
static Qt::ItemFlags _flags;
QAudio::Mode _mode;
QAudioDeviceInfo _selectedDevice;
QList<AudioDevice> _devices;
};
class Audio;
class AudioDevices : public QObject {
Q_OBJECT
Q_PROPERTY(AudioDeviceList* input READ getInputList NOTIFY nop)
Q_PROPERTY(AudioDeviceList* output READ getOutputList NOTIFY nop)
public:
AudioDevices(bool& contextIsHMD);
signals:
void nop();
private slots:
void onContextChanged(const QString& context);
void onInputDeviceSelected(const QAudioDeviceInfo& device);
void onOutputDeviceSelected(const QAudioDeviceInfo& device);
void onDeviceChanged(QAudio::Mode mode, const QAudioDeviceInfo& device);
void onDevicesChanged(QAudio::Mode mode, const QList<QAudioDeviceInfo>& devices);
private:
friend class Audio;
AudioDeviceList* getInputList() { return &_inputs; }
AudioDeviceList* getOutputList() { return &_outputs; }
AudioDeviceList _inputs { QAudio::AudioInput };
AudioDeviceList _outputs { QAudio::AudioOutput };
bool& _contextIsHMD;
};
};
#endif // hifi_scripting_AudioDevices_h

View file

@ -152,9 +152,13 @@ void MenuScriptingInterface::closeInfoView(const QString& path) {
}
bool MenuScriptingInterface::isInfoViewVisible(const QString& path) {
if (QThread::currentThread() == qApp->thread()) {
return Menu::getInstance()->isInfoViewVisible(path);
}
bool result;
QMetaObject::invokeMethod(Menu::getInstance(), "isInfoViewVisible", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(bool, result), Q_ARG(const QString&, path));
Q_RETURN_ARG(bool, result), Q_ARG(const QString&, path));
return result;
}

View file

@ -18,9 +18,10 @@
HIFI_QML_DEF(AvatarInputs)
static AvatarInputs* INSTANCE{ nullptr };
Setting::Handle<bool> showAudioToolsSetting { QStringList { "AvatarInputs", "showAudioTools" }, false };
AvatarInputs* AvatarInputs::getInstance() {
if (!INSTANCE) {
AvatarInputs::registerType();
@ -32,6 +33,7 @@ AvatarInputs* AvatarInputs::getInstance() {
AvatarInputs::AvatarInputs(QQuickItem* parent) : QQuickItem(parent) {
INSTANCE = this;
_showAudioTools = showAudioToolsSetting.get();
}
#define AI_UPDATE(name, src) \
@ -43,16 +45,6 @@ AvatarInputs::AvatarInputs(QQuickItem* parent) : QQuickItem(parent) {
} \
}
#define AI_UPDATE_WRITABLE(name, src) \
{ \
auto val = src; \
if (_##name != val) { \
_##name = val; \
qDebug() << "AvatarInputs" << val; \
emit name##Changed(val); \
} \
}
#define AI_UPDATE_FLOAT(name, src, epsilon) \
{ \
float val = src; \
@ -94,8 +86,6 @@ void AvatarInputs::update() {
AI_UPDATE(cameraMuted, Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking));
AI_UPDATE(isHMD, qApp->isHMDMode());
AI_UPDATE_WRITABLE(showAudioTools, Menu::getInstance()->isOptionChecked(MenuOption::AudioTools));
auto audioIO = DependencyManager::get<AudioClient>();
const float audioLevel = loudnessToAudioLevel(DependencyManager::get<AudioClient>()->getLastInputLoudness());
@ -122,8 +112,9 @@ void AvatarInputs::setShowAudioTools(bool showAudioTools) {
if (_showAudioTools == showAudioTools)
return;
Menu::getInstance()->setIsOptionChecked(MenuOption::AudioTools, showAudioTools);
update();
_showAudioTools = showAudioTools;
showAudioToolsSetting.set(_showAudioTools);
emit showAudioToolsChanged(_showAudioTools);
}
void AvatarInputs::toggleCameraMute() {

View file

@ -49,7 +49,7 @@ signals:
void audioClippingChanged();
void audioLevelChanged();
void isHMDChanged();
void showAudioToolsChanged(bool showAudioTools);
void showAudioToolsChanged(bool show);
protected:
Q_INVOKABLE void resetSensors();

View file

@ -227,17 +227,17 @@ void setupPreferences() {
preferences->addPreference(preference);
}
static const QString AUDIO("Audio");
static const QString AUDIO_BUFFERS("Audio Buffers");
{
auto getter = []()->bool { return !DependencyManager::get<AudioClient>()->getReceivedAudioStream().dynamicJitterBufferEnabled(); };
auto setter = [](bool value) { DependencyManager::get<AudioClient>()->getReceivedAudioStream().setDynamicJitterBufferEnabled(!value); };
auto preference = new CheckPreference(AUDIO, "Disable dynamic jitter buffer", getter, setter);
auto preference = new CheckPreference(AUDIO_BUFFERS, "Disable dynamic jitter buffer", getter, setter);
preferences->addPreference(preference);
}
{
auto getter = []()->float { return DependencyManager::get<AudioClient>()->getReceivedAudioStream().getStaticJitterBufferFrames(); };
auto setter = [](float value) { DependencyManager::get<AudioClient>()->getReceivedAudioStream().setStaticJitterBufferFrames(value); };
auto preference = new SpinnerPreference(AUDIO, "Static jitter buffer frames", getter, setter);
auto preference = new SpinnerPreference(AUDIO_BUFFERS, "Static jitter buffer frames", getter, setter);
preference->setMin(0);
preference->setMax(2000);
preference->setStep(1);
@ -246,13 +246,13 @@ void setupPreferences() {
{
auto getter = []()->bool { return !DependencyManager::get<AudioClient>()->getOutputStarveDetectionEnabled(); };
auto setter = [](bool value) { DependencyManager::get<AudioClient>()->setOutputStarveDetectionEnabled(!value); };
auto preference = new CheckPreference(AUDIO, "Disable output starve detection", getter, setter);
auto preference = new CheckPreference(AUDIO_BUFFERS, "Disable output starve detection", getter, setter);
preferences->addPreference(preference);
}
{
auto getter = []()->float { return DependencyManager::get<AudioClient>()->getOutputBufferSize(); };
auto setter = [](float value) { DependencyManager::get<AudioClient>()->setOutputBufferSize(value); };
auto preference = new SpinnerPreference(AUDIO, "Output buffer initial frames", getter, setter);
auto preference = new SpinnerPreference(AUDIO_BUFFERS, "Output buffer initial frames", getter, setter);
preference->setMin(AudioClient::MIN_BUFFER_FRAMES);
preference->setMax(AudioClient::MAX_BUFFER_FRAMES);
preference->setStep(1);
@ -262,13 +262,13 @@ void setupPreferences() {
{
auto getter = []()->bool { return DependencyManager::get<AudioClient>()->isSimulatingJitter(); };
auto setter = [](bool value) { return DependencyManager::get<AudioClient>()->setIsSimulatingJitter(value); };
auto preference = new CheckPreference(AUDIO, "Packet jitter simulator", getter, setter);
auto preference = new CheckPreference(AUDIO_BUFFERS, "Packet jitter simulator", getter, setter);
preferences->addPreference(preference);
}
{
auto getter = []()->float { return DependencyManager::get<AudioClient>()->getGateThreshold(); };
auto setter = [](float value) { return DependencyManager::get<AudioClient>()->setGateThreshold(value); };
auto preference = new SpinnerPreference(AUDIO, "Packet throttle threshold", getter, setter);
auto preference = new SpinnerPreference(AUDIO_BUFFERS, "Packet throttle threshold", getter, setter);
preference->setMin(1);
preference->setMax(200);
preference->setStep(1);

View file

@ -16,6 +16,7 @@
#include <glm/gtx/quaternion.hpp>
#include <glm/gtx/vector_angle.hpp>
#include <render/Args.h>
#include <avatar/AvatarManager.h>
#include <Application.h>
#include <AudioClient.h>
@ -33,6 +34,7 @@
#include "SequenceNumberStats.h"
#include "StatTracker.h"
HIFI_QML_DEF(Stats)
using namespace std;
@ -454,7 +456,7 @@ void Stats::updateStats(bool force) {
}
}
void Stats::setRenderDetails(const RenderDetails& details) {
void Stats::setRenderDetails(const render::RenderDetails& details) {
STAT_UPDATE(triangles, details._trianglesRendered);
STAT_UPDATE(materialSwitches, details._materialSwitches);
if (_expanded) {

View file

@ -9,10 +9,11 @@
#ifndef hifi_Stats_h
#define hifi_Stats_h
#include <QtGui/QVector3D>
#include <OffscreenQmlElement.h>
#include <RenderArgs.h>
#include <QVector3D>
#include <AudioIOStats.h>
#include <render/Args.h>
#define STATS_PROPERTY(type, name, initialValue) \
Q_PROPERTY(type name READ name NOTIFY name##Changed) \
@ -138,7 +139,7 @@ public:
Stats(QQuickItem* parent = nullptr);
bool includeTimingRecord(const QString& name);
void setRenderDetails(const RenderDetails& details);
void setRenderDetails(const render::RenderDetails& details);
const QString& monospaceFont() {
return _monospaceFont;
}

View file

@ -112,7 +112,7 @@ void Image3DOverlay::render(RenderArgs* args) {
_geometryId
);
batch->setResourceTexture(0, args->_whiteTexture); // restore default white color after me
batch->setResourceTexture(0, nullptr); // restore default white color after me
}
const render::ShapeKey Image3DOverlay::getShapeKey() {

View file

@ -1,50 +0,0 @@
//
// LocalModelsOverlay.cpp
// interface/src/ui/overlays
//
// Created by Ryan Huffman on 07/08/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "LocalModelsOverlay.h"
#include <EntityTreeRenderer.h>
#include <gpu/Batch.h>
QString const LocalModelsOverlay::TYPE = "localmodels";
LocalModelsOverlay::LocalModelsOverlay(EntityTreeRenderer* entityTreeRenderer) :
Volume3DOverlay(),
_entityTreeRenderer(entityTreeRenderer) {
}
LocalModelsOverlay::LocalModelsOverlay(const LocalModelsOverlay* localModelsOverlay) :
Volume3DOverlay(localModelsOverlay),
_entityTreeRenderer(localModelsOverlay->_entityTreeRenderer)
{
}
void LocalModelsOverlay::update(float deltatime) {
_entityTreeRenderer->update();
}
void LocalModelsOverlay::render(RenderArgs* args) {
if (_visible) {
auto batch = args ->_batch;
Transform transform = Transform();
transform.setTranslation(args->getViewFrustum().getPosition() + getPosition());
batch->setViewTransform(transform);
_entityTreeRenderer->render(args);
transform.setTranslation(args->getViewFrustum().getPosition());
batch->setViewTransform(transform);
}
}
LocalModelsOverlay* LocalModelsOverlay::createClone() const {
return new LocalModelsOverlay(this);
}

View file

@ -1,37 +0,0 @@
//
// LocalModelsOverlay.h
// interface/src/ui/overlays
//
// Created by Ryan Huffman on 07/08/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_LocalModelsOverlay_h
#define hifi_LocalModelsOverlay_h
#include "Volume3DOverlay.h"
class EntityTreeRenderer;
class LocalModelsOverlay : public Volume3DOverlay {
Q_OBJECT
public:
static QString const TYPE;
virtual QString getType() const override { return TYPE; }
LocalModelsOverlay(EntityTreeRenderer* entityTreeRenderer);
LocalModelsOverlay(const LocalModelsOverlay* localModelsOverlay);
virtual void update(float deltatime) override;
virtual void render(RenderArgs* args) override;
virtual LocalModelsOverlay* createClone() const override;
private:
EntityTreeRenderer* _entityTreeRenderer;
};
#endif // hifi_LocalModelsOverlay_h

View file

@ -26,7 +26,6 @@
#include "Shape3DOverlay.h"
#include "ImageOverlay.h"
#include "Line3DOverlay.h"
#include "LocalModelsOverlay.h"
#include "ModelOverlay.h"
#include "Rectangle3DOverlay.h"
#include "Sphere3DOverlay.h"
@ -171,8 +170,6 @@ OverlayID Overlays::addOverlay(const QString& type, const QVariant& properties)
thisOverlay = std::make_shared<Line3DOverlay>();
} else if (type == Grid3DOverlay::TYPE) {
thisOverlay = std::make_shared<Grid3DOverlay>();
} else if (type == LocalModelsOverlay::TYPE) {
thisOverlay = std::make_shared<LocalModelsOverlay>(qApp->getEntityClipboardRenderer());
} else if (type == ModelOverlay::TYPE) {
thisOverlay = std::make_shared<ModelOverlay>();
} else if (type == Web3DOverlay::TYPE) {

View file

@ -21,7 +21,6 @@
#include "Cube3DOverlay.h"
#include "ImageOverlay.h"
#include "Line3DOverlay.h"
#include "LocalModelsOverlay.h"
#include "ModelOverlay.h"
#include "Overlays.h"
#include "Rectangle3DOverlay.h"

View file

@ -47,7 +47,6 @@
#include "LODManager.h"
#include "ui/OctreeStatsProvider.h"
#include "ui/DomainConnectionModel.h"
#include "scripting/AudioDeviceScriptingInterface.h"
#include "ui/AvatarInputs.h"
#include "avatar/AvatarManager.h"
#include "scripting/GlobalServicesScriptingInterface.h"
@ -185,6 +184,7 @@ void Web3DOverlay::loadSourceURL() {
_webSurface->getSurfaceContext()->setContextProperty("offscreenFlags", flags);
_webSurface->getSurfaceContext()->setContextProperty("AddressManager", DependencyManager::get<AddressManager>().data());
_webSurface->getSurfaceContext()->setContextProperty("Account", AccountScriptingInterface::getInstance());
_webSurface->getSurfaceContext()->setContextProperty("Audio", DependencyManager::get<AudioScriptingInterface>().data());
_webSurface->getSurfaceContext()->setContextProperty("AudioStats", DependencyManager::get<AudioClient>()->getStats().data());
_webSurface->getSurfaceContext()->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
_webSurface->getSurfaceContext()->setContextProperty("fileDialogHelper", new FileDialogHelper());
@ -195,7 +195,6 @@ void Web3DOverlay::loadSourceURL() {
_webSurface->getSurfaceContext()->setContextProperty("LODManager", DependencyManager::get<LODManager>().data());
_webSurface->getSurfaceContext()->setContextProperty("OctreeStats", DependencyManager::get<OctreeStatsProvider>().data());
_webSurface->getSurfaceContext()->setContextProperty("DCModel", DependencyManager::get<DomainConnectionModel>().data());
_webSurface->getSurfaceContext()->setContextProperty("AudioDevice", AudioDeviceScriptingInterface::getInstance());
_webSurface->getSurfaceContext()->setContextProperty("AvatarInputs", AvatarInputs::getInstance());
_webSurface->getSurfaceContext()->setContextProperty("GlobalServices", GlobalServicesScriptingInterface::getInstance());
_webSurface->getSurfaceContext()->setContextProperty("AvatarList", DependencyManager::get<AvatarManager>().data());
@ -319,7 +318,7 @@ void Web3DOverlay::render(RenderArgs* args) {
geometryCache->bindOpaqueWebBrowserProgram(batch, _isAA);
}
geometryCache->renderQuad(batch, halfSize * -1.0f, halfSize, vec2(0), vec2(1), color, _geometryId);
batch.setResourceTexture(0, args->_whiteTexture); // restore default white color after me
batch.setResourceTexture(0, nullptr); // restore default white color after me
}
const render::ShapeKey Web3DOverlay::getShapeKey() {
@ -441,17 +440,27 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) {
touchEvent->setTouchPoints(touchPoints);
touchEvent->setTouchPointStates(touchPointState);
// Send mouse events to the Web surface so that HTML dialog elements work with mouse press and hover.
// FIXME: Scroll bar dragging is a bit unstable in the tablet (content can jump up and down at times).
// This may be improved in Qt 5.8. Release notes: "Cleaned up touch and mouse event delivery".
//
// In Qt 5.9 mouse events must be sent before touch events to make sure some QtQuick components will
// receive mouse events
#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
if (!(this->_pressed && event.getType() == PointerEvent::Move)) {
QMouseEvent* mouseEvent = new QMouseEvent(mouseType, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier);
QCoreApplication::postEvent(_webSurface->getWindow(), mouseEvent);
}
#endif
QCoreApplication::postEvent(_webSurface->getWindow(), touchEvent);
if (this->_pressed && event.getType() == PointerEvent::Move) {
#if QT_VERSION < QT_VERSION_CHECK(5, 9, 0)
if (this->_pressed && event.getType() == PointerEvent::Move) {
return;
}
// Send mouse events to the Web surface so that HTML dialog elements work with mouse press and hover.
// FIXME: Scroll bar dragging is a bit unstable in the tablet (content can jump up and down at times).
// This may be improved in Qt 5.8. Release notes: "Cleaned up touch and mouse event delivery".
QMouseEvent* mouseEvent = new QMouseEvent(mouseType, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier);
QCoreApplication::postEvent(_webSurface->getWindow(), mouseEvent);
#endif
}
void Web3DOverlay::handlePointerEventAsMouse(const PointerEvent& event) {

View file

@ -1,5 +1,7 @@
set(TARGET_NAME animation)
setup_hifi_library(Network Script)
link_hifi_libraries(shared model fbx)
include_hifi_library_headers(networking)
include_hifi_library_headers(gpu)
target_nsight()

View file

@ -1164,9 +1164,32 @@ void Rig::updateFromHandAndFeetParameters(const HandAndFeetParameters& params, f
bool bodySensorTrackingEnabled = params.isLeftFootEnabled || params.isRightFootEnabled;
const float RELAX_DURATION = 0.6f;
const float CONTROL_DURATION = 0.4f;
const bool TO_CONTROLLED = true;
const bool FROM_CONTROLLED = false;
const bool LEFT_HAND = true;
const bool RIGHT_HAND = false;
if (params.isLeftEnabled) {
if (!_isLeftHandControlled) {
_leftHandControlTimeRemaining = CONTROL_DURATION;
_isLeftHandControlled = true;
}
glm::vec3 handPosition = params.leftPosition;
glm::quat handRotation = params.leftOrientation;
if (_leftHandControlTimeRemaining > 0.0f) {
// Move hand from non-controlled position to controlled position.
_leftHandControlTimeRemaining = std::max(_leftHandControlTimeRemaining - dt, 0.0f);
AnimPose handPose(Vectors::ONE, handRotation, handPosition);
if (transitionHandPose(_leftHandControlTimeRemaining, CONTROL_DURATION, handPose, LEFT_HAND, TO_CONTROLLED,
handPose)) {
handPosition = handPose.trans();
handRotation = handPose.rot();
}
}
if (!bodySensorTrackingEnabled) {
// prevent the hand IK targets from intersecting the body capsule
glm::vec3 displacement;
@ -1176,27 +1199,22 @@ void Rig::updateFromHandAndFeetParameters(const HandAndFeetParameters& params, f
}
_animVars.set("leftHandPosition", handPosition);
_animVars.set("leftHandRotation", params.leftOrientation);
_animVars.set("leftHandRotation", handRotation);
_animVars.set("leftHandType", (int)IKTarget::Type::RotationAndPosition);
_isLeftHandControlled = true;
_lastLeftHandControlledPose = AnimPose(glm::vec3(1.0f), params.leftOrientation, handPosition);
_lastLeftHandControlledPose = AnimPose(Vectors::ONE, handRotation, handPosition);
} else {
if (_isLeftHandControlled) {
_leftHandRelaxDuration = RELAX_DURATION;
_leftHandRelaxTimeRemaining = RELAX_DURATION;
_isLeftHandControlled = false;
}
if (_leftHandRelaxDuration > 0) {
if (_leftHandRelaxTimeRemaining > 0.0f) {
// Move hand from controlled position to non-controlled position.
_leftHandRelaxDuration = std::max(_leftHandRelaxDuration - dt, 0.0f);
auto ikNode = getAnimInverseKinematicsNode();
if (ikNode) {
float alpha = 1.0f - _leftHandRelaxDuration / RELAX_DURATION;
const AnimPose geometryToRigTransform(_geometryToRigTransform);
AnimPose uncontrolledHandPose = geometryToRigTransform * ikNode->getUncontrolledLeftHandPose();
AnimPose handPose;
::blend(1, &_lastLeftHandControlledPose, &uncontrolledHandPose, alpha, &handPose);
_leftHandRelaxTimeRemaining = std::max(_leftHandRelaxTimeRemaining - dt, 0.0f);
AnimPose handPose;
if (transitionHandPose(_leftHandRelaxTimeRemaining, RELAX_DURATION, _lastLeftHandControlledPose, LEFT_HAND,
FROM_CONTROLLED, handPose)) {
_animVars.set("leftHandPosition", handPose.trans());
_animVars.set("leftHandRotation", handPose.rot());
_animVars.set("leftHandType", (int)IKTarget::Type::RotationAndPosition);
@ -1209,7 +1227,25 @@ void Rig::updateFromHandAndFeetParameters(const HandAndFeetParameters& params, f
}
if (params.isRightEnabled) {
if (!_isRightHandControlled) {
_rightHandControlTimeRemaining = CONTROL_DURATION;
_isRightHandControlled = true;
}
glm::vec3 handPosition = params.rightPosition;
glm::quat handRotation = params.rightOrientation;
if (_rightHandControlTimeRemaining > 0.0f) {
// Move hand from non-controlled position to controlled position.
_rightHandControlTimeRemaining = std::max(_rightHandControlTimeRemaining - dt, 0.0f);
AnimPose handPose(Vectors::ONE, handRotation, handPosition);
if (transitionHandPose(_rightHandControlTimeRemaining, CONTROL_DURATION, handPose, RIGHT_HAND, TO_CONTROLLED,
handPose)) {
handPosition = handPose.trans();
handRotation = handPose.rot();
}
}
if (!bodySensorTrackingEnabled) {
// prevent the hand IK targets from intersecting the body capsule
glm::vec3 displacement;
@ -1219,27 +1255,22 @@ void Rig::updateFromHandAndFeetParameters(const HandAndFeetParameters& params, f
}
_animVars.set("rightHandPosition", handPosition);
_animVars.set("rightHandRotation", params.rightOrientation);
_animVars.set("rightHandRotation", handRotation);
_animVars.set("rightHandType", (int)IKTarget::Type::RotationAndPosition);
_isRightHandControlled = true;
_lastRightHandControlledPose = AnimPose(glm::vec3(1.0f), params.rightOrientation, handPosition);
_lastRightHandControlledPose = AnimPose(Vectors::ONE, handRotation, handPosition);
} else {
if (_isRightHandControlled) {
_rightHandRelaxDuration = RELAX_DURATION;
_rightHandRelaxTimeRemaining = RELAX_DURATION;
_isRightHandControlled = false;
}
if (_rightHandRelaxDuration > 0) {
if (_rightHandRelaxTimeRemaining > 0.0f) {
// Move hand from controlled position to non-controlled position.
_rightHandRelaxDuration = std::max(_rightHandRelaxDuration - dt, 0.0f);
auto ikNode = getAnimInverseKinematicsNode();
if (ikNode) {
float alpha = 1.0f - _rightHandRelaxDuration / RELAX_DURATION;
const AnimPose geometryToRigTransform(_geometryToRigTransform);
AnimPose uncontrolledHandPose = geometryToRigTransform * ikNode->getUncontrolledRightHandPose();
AnimPose handPose;
::blend(1, &_lastRightHandControlledPose, &uncontrolledHandPose, alpha, &handPose);
_rightHandRelaxTimeRemaining = std::max(_rightHandRelaxTimeRemaining - dt, 0.0f);
AnimPose handPose;
if (transitionHandPose(_rightHandRelaxTimeRemaining, RELAX_DURATION, _lastRightHandControlledPose, RIGHT_HAND,
FROM_CONTROLLED, handPose)) {
_animVars.set("rightHandPosition", handPose.trans());
_animVars.set("rightHandRotation", handPose.rot());
_animVars.set("rightHandType", (int)IKTarget::Type::RotationAndPosition);
@ -1545,3 +1576,25 @@ void Rig::computeAvatarBoundingCapsule(
glm::vec3 rigCenter = (geometryToRig * (0.5f * (totalExtents.maximum + totalExtents.minimum)));
localOffsetOut = rigCenter - (geometryToRig * rootPosition);
}
bool Rig::transitionHandPose(float deltaTime, float totalDuration, AnimPose& controlledHandPose, bool isLeftHand,
bool isToControlled, AnimPose& returnHandPose) {
auto ikNode = getAnimInverseKinematicsNode();
if (ikNode) {
float alpha = 1.0f - deltaTime / totalDuration;
const AnimPose geometryToRigTransform(_geometryToRigTransform);
AnimPose uncontrolledHandPose;
if (isLeftHand) {
uncontrolledHandPose = geometryToRigTransform * ikNode->getUncontrolledLeftHandPose();
} else {
uncontrolledHandPose = geometryToRigTransform * ikNode->getUncontrolledRightHandPose();
}
if (isToControlled) {
::blend(1, &uncontrolledHandPose, &controlledHandPose, alpha, &returnHandPose);
} else {
::blend(1, &controlledHandPose, &uncontrolledHandPose, alpha, &returnHandPose);
}
return true;
}
return false;
}

View file

@ -352,10 +352,15 @@ private:
int _nextStateHandlerId { 0 };
QMutex _stateMutex;
bool transitionHandPose(float deltaTime, float totalDuration, AnimPose& controlledHandPose, bool isLeftHand,
bool isToControlled, AnimPose& returnHandPose);
bool _isLeftHandControlled { false };
bool _isRightHandControlled { false };
float _leftHandRelaxDuration { 0.0f };
float _rightHandRelaxDuration { 0.0f };
float _leftHandControlTimeRemaining { 0.0f };
float _rightHandControlTimeRemaining { 0.0f };
float _leftHandRelaxTimeRemaining { 0.0f };
float _rightHandRelaxTimeRemaining { 0.0f };
AnimPose _lastLeftHandControlledPose;
AnimPose _lastRightHandControlledPose;
};

View file

@ -1,6 +1,8 @@
set(TARGET_NAME audio-client)
setup_hifi_library(Network Multimedia)
link_hifi_libraries(audio plugins)
include_hifi_library_headers(shared)
include_hifi_library_headers(networking)
# append audio includes to our list of includes to bubble
target_include_directories(${TARGET_NAME} PUBLIC "${HIFI_LIBRARY_DIR}/audio/src")

View file

@ -79,6 +79,49 @@ using Mutex = std::mutex;
using Lock = std::unique_lock<Mutex>;
static Mutex _deviceMutex;
// thread-safe
QList<QAudioDeviceInfo> getAvailableDevices(QAudio::Mode mode) {
// NOTE: availableDevices() clobbers the Qt internal device list
Lock lock(_deviceMutex);
return QAudioDeviceInfo::availableDevices(mode);
}
// now called from a background thread, to keep blocking operations off the audio thread
void AudioClient::checkDevices() {
auto inputDevices = getAvailableDevices(QAudio::AudioInput);
auto outputDevices = getAvailableDevices(QAudio::AudioOutput);
if (inputDevices != _inputDevices) {
_inputDevices.swap(inputDevices);
emit devicesChanged(QAudio::AudioInput, _inputDevices);
}
if (outputDevices != _outputDevices) {
_outputDevices.swap(outputDevices);
emit devicesChanged(QAudio::AudioOutput, _outputDevices);
}
}
QAudioDeviceInfo AudioClient::getActiveAudioDevice(QAudio::Mode mode) const {
Lock lock(_deviceMutex);
if (mode == QAudio::AudioInput) {
return _inputDeviceInfo;
} else { // if (mode == QAudio::AudioOutput)
return _outputDeviceInfo;
}
}
QList<QAudioDeviceInfo> AudioClient::getAudioDevices(QAudio::Mode mode) const {
Lock lock(_deviceMutex);
if (mode == QAudio::AudioInput) {
return _inputDevices;
} else { // if (mode == QAudio::AudioOutput)
return _outputDevices;
}
}
static void channelUpmix(int16_t* source, int16_t* dest, int numSamples, int numExtraChannels) {
for (int i = 0; i < numSamples/2; i++) {
@ -173,8 +216,9 @@ AudioClient::AudioClient() :
connect(&_receivedAudioStream, &InboundAudioStream::mismatchedAudioCodec, this, &AudioClient::handleMismatchAudioFormat);
_inputDevices = getDeviceNames(QAudio::AudioInput);
_outputDevices = getDeviceNames(QAudio::AudioOutput);
// initialize wasapi; if getAvailableDevices is called from the CheckDevicesThread before this, it will crash
getAvailableDevices(QAudio::AudioInput);
getAvailableDevices(QAudio::AudioOutput);
// start a thread to detect any device changes
@ -184,6 +228,8 @@ AudioClient::AudioClient() :
checkDevices();
});
});
const unsigned long DEVICE_CHECK_INTERVAL_MSECS = 2 * 1000;
_checkDevicesTimer->start(DEVICE_CHECK_INTERVAL_MSECS);
configureReverb();
@ -239,13 +285,6 @@ void AudioClient::audioMixerKilled() {
emit disconnected();
}
// thread-safe
QList<QAudioDeviceInfo> getAvailableDevices(QAudio::Mode mode) {
// NOTE: availableDevices() clobbers the Qt internal device list
Lock lock(_deviceMutex);
return QAudioDeviceInfo::availableDevices(mode);
}
QAudioDeviceInfo getNamedAudioDeviceForMode(QAudio::Mode mode, const QString& deviceName) {
QAudioDeviceInfo result;
foreach(QAudioDeviceInfo audioDevice, getAvailableDevices(mode)) {
@ -259,7 +298,7 @@ QAudioDeviceInfo getNamedAudioDeviceForMode(QAudio::Mode mode, const QString& de
}
#ifdef Q_OS_WIN
QString friendlyNameForAudioDevice(IMMDevice* pEndpoint) {
QString getWinDeviceName(IMMDevice* pEndpoint) {
QString deviceName;
IPropertyStore* pPropertyStore;
pEndpoint->OpenPropertyStore(STGM_READ, &pPropertyStore);
@ -280,7 +319,7 @@ QString friendlyNameForAudioDevice(IMMDevice* pEndpoint) {
return deviceName;
}
QString AudioClient::friendlyNameForAudioDevice(wchar_t* guid) {
QString AudioClient::getWinDeviceName(wchar_t* guid) {
QString deviceName;
HRESULT hr = S_OK;
CoInitialize(nullptr);
@ -292,7 +331,7 @@ QString AudioClient::friendlyNameForAudioDevice(wchar_t* guid) {
printf("Audio Error: device not found\n");
deviceName = QString("NONE");
} else {
deviceName = ::friendlyNameForAudioDevice(pEndpoint);
deviceName = ::getWinDeviceName(pEndpoint);
}
pMMDeviceEnumerator->Release();
pMMDeviceEnumerator = nullptr;
@ -375,7 +414,7 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) {
printf("Audio Error: device not found\n");
deviceName = QString("NONE");
} else {
deviceName = friendlyNameForAudioDevice(pEndpoint);
deviceName = getWinDeviceName(pEndpoint);
}
pMMDeviceEnumerator->Release();
pMMDeviceEnumerator = NULL;
@ -393,6 +432,11 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) {
return (mode == QAudio::AudioInput) ? QAudioDeviceInfo::defaultInputDevice() : QAudioDeviceInfo::defaultOutputDevice();
}
bool AudioClient::getNamedAudioDeviceForModeExists(QAudio::Mode mode, const QString& deviceName) {
return (getNamedAudioDeviceForMode(mode, deviceName).deviceName() == deviceName);
}
// attempt to use the native sample rate and channel count
bool nativeFormatForAudioDevice(const QAudioDeviceInfo& audioDevice,
QAudioFormat& audioFormat) {
@ -740,29 +784,22 @@ void AudioClient::selectAudioFormat(const QString& selectedCodecName) {
}
bool AudioClient::switchAudioDevice(QAudio::Mode mode, const QAudioDeviceInfo& deviceInfo) {
auto device = deviceInfo;
QString AudioClient::getDefaultDeviceName(QAudio::Mode mode) {
QAudioDeviceInfo deviceInfo = defaultAudioDeviceForMode(mode);
return deviceInfo.deviceName();
}
QVector<QString> AudioClient::getDeviceNames(QAudio::Mode mode) {
QVector<QString> deviceNames;
const QList<QAudioDeviceInfo> &availableDevice = getAvailableDevices(mode);
foreach(const QAudioDeviceInfo &audioDevice, availableDevice) {
deviceNames << audioDevice.deviceName().trimmed();
if (device.isNull()) {
device = defaultAudioDeviceForMode(mode);
}
if (mode == QAudio::AudioInput) {
return switchInputToAudioDevice(device);
} else { // if (mode == QAudio::AudioOutput)
return switchOutputToAudioDevice(device);
}
return deviceNames;
}
bool AudioClient::switchInputToAudioDevice(const QString& inputDeviceName) {
qCDebug(audioclient) << "switchInputToAudioDevice [" << inputDeviceName << "] [" << getNamedAudioDeviceForMode(QAudio::AudioInput, inputDeviceName).deviceName() << "]";
return switchInputToAudioDevice(getNamedAudioDeviceForMode(QAudio::AudioInput, inputDeviceName));
}
bool AudioClient::switchOutputToAudioDevice(const QString& outputDeviceName) {
qCDebug(audioclient) << "switchOutputToAudioDevice [" << outputDeviceName << "] [" << getNamedAudioDeviceForMode(QAudio::AudioOutput, outputDeviceName).deviceName() << "]";
return switchOutputToAudioDevice(getNamedAudioDeviceForMode(QAudio::AudioOutput, outputDeviceName));
bool AudioClient::switchAudioDevice(QAudio::Mode mode, const QString& deviceName) {
return switchAudioDevice(mode, getNamedAudioDeviceForMode(mode, deviceName));
}
void AudioClient::configureReverb() {
@ -1306,8 +1343,7 @@ void AudioClient::setIsStereoInput(bool isStereoInput) {
}
// change in channel count for desired input format, restart the input device
qCDebug(audioclient) << __FUNCTION__ << "about to call switchInputToAudioDevice:" << _inputAudioDeviceName;
switchInputToAudioDevice(_inputAudioDeviceName);
switchInputToAudioDevice(_inputDeviceInfo);
}
}
@ -1346,6 +1382,9 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn
qCDebug(audioclient) << __FUNCTION__ << "inputDeviceInfo: [" << inputDeviceInfo.deviceName() << "]";
bool supportedFormat = false;
// NOTE: device start() uses the Qt internal device list
Lock lock(_deviceMutex);
// cleanup any previously initialized device
if (_audioInput) {
// The call to stop() causes _inputDevice to be destructed.
@ -1358,7 +1397,7 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn
_audioInput = NULL;
_numInputCallbackBytes = 0;
_inputAudioDeviceName = "";
_inputDeviceInfo = QAudioDeviceInfo();
}
if (_inputToNetworkResampler) {
@ -1373,8 +1412,8 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn
if (!inputDeviceInfo.isNull()) {
qCDebug(audioclient) << "The audio input device " << inputDeviceInfo.deviceName() << "is available.";
_inputAudioDeviceName = inputDeviceInfo.deviceName().trimmed();
emit currentInputDeviceChanged(_inputAudioDeviceName);
_inputDeviceInfo = inputDeviceInfo;
emit deviceChanged(QAudio::AudioInput, inputDeviceInfo);
if (adjustedFormatForAudioDevice(inputDeviceInfo, _desiredInputFormat, _inputFormat)) {
qCDebug(audioclient) << "The format to be used for audio input is" << _inputFormat;
@ -1409,10 +1448,7 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn
int numFrameSamples = calculateNumberOfFrameSamples(_numInputCallbackBytes);
_inputRingBuffer.resizeForFrameSize(numFrameSamples);
// NOTE: device start() uses the Qt internal device list
Lock lock(_deviceMutex);
_inputDevice = _audioInput->start();
lock.unlock();
if (_inputDevice) {
connect(_inputDevice, SIGNAL(readyRead()), this, SLOT(handleMicAudioInput()));
@ -1462,6 +1498,9 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice
bool supportedFormat = false;
// NOTE: device start() uses the Qt internal device list
Lock lock(_deviceMutex);
Lock localAudioLock(_localAudioMutex);
_localSamplesAvailable.exchange(0, std::memory_order_release);
@ -1487,6 +1526,8 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice
delete[] _localOutputMixBuffer;
_localOutputMixBuffer = NULL;
_outputDeviceInfo = QAudioDeviceInfo();
}
if (_networkToOutputResampler) {
@ -1500,8 +1541,8 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice
if (!outputDeviceInfo.isNull()) {
qCDebug(audioclient) << "The audio output device " << outputDeviceInfo.deviceName() << "is available.";
_outputAudioDeviceName = outputDeviceInfo.deviceName().trimmed();
emit currentOutputDeviceChanged(_outputAudioDeviceName);
_outputDeviceInfo = outputDeviceInfo;
emit deviceChanged(QAudio::AudioOutput, outputDeviceInfo);
if (adjustedFormatForAudioDevice(outputDeviceInfo, _desiredOutputFormat, _outputFormat)) {
qCDebug(audioclient) << "The format to be used for audio output is" << _outputFormat;
@ -1576,10 +1617,7 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice
_audioOutputIODevice.start();
// NOTE: device start() uses the Qt internal device list
Lock lock(_deviceMutex);
_audioOutput->start(&_audioOutputIODevice);
lock.unlock();
// setup a loopback audio output device
_loopbackAudioOutput = new QAudioOutput(outputDeviceInfo, _outputFormat, this);
@ -1779,19 +1817,6 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) {
return bytesWritten;
}
// now called from a background thread, to keep blocking operations off the audio thread
void AudioClient::checkDevices() {
QVector<QString> inputDevices = getDeviceNames(QAudio::AudioInput);
QVector<QString> outputDevices = getDeviceNames(QAudio::AudioOutput);
if (inputDevices != _inputDevices || outputDevices != _outputDevices) {
_inputDevices = inputDevices;
_outputDevices = outputDevices;
emit deviceChanged();
}
}
void AudioClient::loadSettings() {
_receivedAudioStream.setDynamicJitterBufferEnabled(dynamicJitterBufferEnabled.get());
_receivedAudioStream.setStaticJitterBufferFrames(staticJitterBufferFrames.get());

View file

@ -123,8 +123,6 @@ public:
float getTimeSinceLastClip() const { return _timeSinceLastClip; }
float getAudioAverageInputLoudness() const { return _lastInputLoudness; }
bool isMuted() { return _muted; }
const AudioIOStats& getStats() const { return _stats; }
int getOutputBufferSize() { return _outputBufferSizeFrames.get(); }
@ -147,10 +145,15 @@ public:
bool outputLocalInjector(AudioInjector* injector) override;
QAudioDeviceInfo getActiveAudioDevice(QAudio::Mode mode) const;
QList<QAudioDeviceInfo> getAudioDevices(QAudio::Mode mode) const;
static const float CALLBACK_ACCELERATOR_RATIO;
bool getNamedAudioDeviceForModeExists(QAudio::Mode mode, const QString& deviceName);
#ifdef Q_OS_WIN
static QString friendlyNameForAudioDevice(wchar_t* guid);
static QString getWinDeviceName(wchar_t* guid);
#endif
public slots:
@ -170,11 +173,14 @@ public slots:
void handleRecordedAudioInput(const QByteArray& audio);
void reset();
void audioMixerKilled();
void toggleMute();
bool isMuted() { return _muted; }
virtual void setIsStereoInput(bool stereo) override;
void toggleAudioNoiseReduction() { _isNoiseGateEnabled = !_isNoiseGateEnabled; }
void setNoiseReduction(bool isNoiseGateEnabled) { _isNoiseGateEnabled = isNoiseGateEnabled; }
void toggleLocalEcho() { _shouldEchoLocally = !_shouldEchoLocally; }
void toggleServerEcho() { _shouldEchoToServer = !_shouldEchoToServer; }
@ -186,12 +192,9 @@ public slots:
bool shouldLoopbackInjectors() override { return _shouldEchoToServer; }
bool switchInputToAudioDevice(const QString& inputDeviceName);
bool switchOutputToAudioDevice(const QString& outputDeviceName);
QString getDeviceName(QAudio::Mode mode) const { return (mode == QAudio::AudioInput) ?
_inputAudioDeviceName : _outputAudioDeviceName; }
QString getDefaultDeviceName(QAudio::Mode mode);
QVector<QString> getDeviceNames(QAudio::Mode mode);
// calling with a null QAudioDevice will use the system default
bool switchAudioDevice(QAudio::Mode mode, const QAudioDeviceInfo& deviceInfo = QAudioDeviceInfo());
bool switchAudioDevice(QAudio::Mode mode, const QString& deviceName);
float getInputVolume() const { return (_audioInput) ? (float)_audioInput->volume() : 0.0f; }
void setInputVolume(float volume) { if (_audioInput) _audioInput->setVolume(volume); }
@ -213,7 +216,9 @@ signals:
void noiseGateClosed();
void changeDevice(const QAudioDeviceInfo& outputDeviceInfo);
void deviceChanged();
void deviceChanged(QAudio::Mode mode, const QAudioDeviceInfo& device);
void devicesChanged(QAudio::Mode mode, const QList<QAudioDeviceInfo>& devices);
void receivedFirstPacket();
void disconnected();
@ -222,9 +227,6 @@ signals:
void muteEnvironmentRequested(glm::vec3 position, float radius);
void currentOutputDeviceChanged(const QString& name);
void currentInputDeviceChanged(const QString& name);
protected:
AudioClient();
~AudioClient();
@ -291,9 +293,6 @@ private:
MixedProcessedAudioStream _receivedAudioStream;
bool _isStereoInput;
QString _inputAudioDeviceName;
QString _outputAudioDeviceName;
quint64 _outputStarveDetectionStartTimeMsec;
int _outputStarveDetectionCount;
@ -369,8 +368,11 @@ private:
glm::vec3 avatarBoundingBoxCorner;
glm::vec3 avatarBoundingBoxScale;
QVector<QString> _inputDevices;
QVector<QString> _outputDevices;
QAudioDeviceInfo _inputDeviceInfo;
QAudioDeviceInfo _outputDeviceInfo;
QList<QAudioDeviceInfo> _inputDevices;
QList<QAudioDeviceInfo> _outputDevices;
bool _hasReceivedFirstPacket { false };

View file

@ -2,5 +2,17 @@ set(TARGET_NAME avatars-renderer)
AUTOSCRIBE_SHADER_LIB(gpu model render render-utils)
setup_hifi_library(Widgets Network Script)
link_hifi_libraries(shared gpu model animation model-networking script-engine render image render-utils)
include_hifi_library_headers(avatars)
include_hifi_library_headers(networking)
include_hifi_library_headers(fbx)
include_hifi_library_headers(recording)
include_hifi_library_headers(trackers)
include_hifi_library_headers(ktx)
include_hifi_library_headers(procedural)
include_hifi_library_headers(physics)
include_hifi_library_headers(entities-renderer)
include_hifi_library_headers(audio)
include_hifi_library_headers(entities)
include_hifi_library_headers(octree)
target_bullet()

View file

@ -1504,7 +1504,7 @@ QUrl AvatarData::cannonicalSkeletonModelURL(const QUrl& emptyURL) const {
return _skeletonModelURL.scheme() == "file" ? emptyURL : _skeletonModelURL;
}
void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged) {
void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged, bool& skeletonModelUrlChanged) {
if (identity.sequenceId < _identitySequenceId) {
qCDebug(avatars) << "Ignoring older identity packet for avatar" << getSessionUUID()
@ -1517,6 +1517,7 @@ void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityC
if (_firstSkeletonCheck || (identity.skeletonModelURL != cannonicalSkeletonModelURL(emptyURL))) {
setSkeletonModelURL(identity.skeletonModelURL);
identityChanged = true;
skeletonModelUrlChanged = true;
if (_firstSkeletonCheck) {
displayNameChanged = true;
}

View file

@ -368,6 +368,7 @@ public:
virtual ~AvatarData();
static const QUrl& defaultFullAvatarModelUrl();
QUrl cannonicalSkeletonModelURL(const QUrl& empty) const;
virtual bool isMyAvatar() const { return false; }
@ -536,9 +537,8 @@ public:
static void parseAvatarIdentityPacket(const QByteArray& data, Identity& identityOut);
// identityChanged returns true if identity has changed, false otherwise.
// displayNameChanged returns true if displayName has changed, false otherwise.
void processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged);
// identityChanged returns true if identity has changed, false otherwise. Similarly for displayNameChanged and skeletonModelUrlChange.
void processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged, bool& skeletonModelUrlChanged);
QByteArray identityByteArray() const;
@ -697,7 +697,6 @@ protected:
QVector<AttachmentData> _attachmentData;
QString _displayName;
QString _sessionDisplayName { };
QUrl cannonicalSkeletonModelURL(const QUrl& empty) const;
QHash<QString, int> _jointIndices; ///< 1-based, since zero is returned for missing keys
QStringList _jointNames; ///< in order of depth-first traversal

View file

@ -148,8 +148,9 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer<ReceivedMessage>
auto avatar = newOrExistingAvatar(identity.uuid, sendingNode);
bool identityChanged = false;
bool displayNameChanged = false;
bool skeletonModelUrlChanged = false;
// In this case, the "sendingNode" is the Avatar Mixer.
avatar->processAvatarIdentity(identity, identityChanged, displayNameChanged);
avatar->processAvatarIdentity(identity, identityChanged, displayNameChanged, skeletonModelUrlChanged);
}
}

View file

@ -5,6 +5,7 @@ setup_hifi_library(Script Qml)
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
link_hifi_libraries(shared)
include_hifi_library_headers(networking)
GroupSources("src/controllers")

View file

@ -2,6 +2,14 @@ set(TARGET_NAME display-plugins)
AUTOSCRIBE_SHADER_LIB(gpu display-plugins)
setup_hifi_library(OpenGL)
link_hifi_libraries(shared plugins ui-plugins gl gpu-gl ui render-utils)
include_hifi_library_headers(gpu)
include_hifi_library_headers(model-networking)
include_hifi_library_headers(networking)
include_hifi_library_headers(model)
include_hifi_library_headers(fbx)
include_hifi_library_headers(image)
include_hifi_library_headers(ktx)
include_hifi_library_headers(render)
target_opengl()

View file

@ -10,7 +10,6 @@
#include <QtCore/QProcessEnvironment>
#include <ViewFrustum.h>
#include <controllers/Pose.h>
#include <gpu/Frame.h>
const QString DebugHmdDisplayPlugin::NAME("HMD Simulator");

View file

@ -2,6 +2,16 @@ set(TARGET_NAME entities-renderer)
AUTOSCRIBE_SHADER_LIB(gpu model procedural render render-utils)
setup_hifi_library(Widgets Network Script)
link_hifi_libraries(shared gpu procedural model model-networking script-engine render render-utils image)
include_hifi_library_headers(networking)
include_hifi_library_headers(gl)
include_hifi_library_headers(ktx)
include_hifi_library_headers(octree)
include_hifi_library_headers(audio)
include_hifi_library_headers(physics)
include_hifi_library_headers(animation)
include_hifi_library_headers(fbx)
include_hifi_library_headers(entities)
include_hifi_library_headers(avatars)
target_bullet()

View file

@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "EntityTreeRenderer.h"
#include <glm/gtx/quaternion.hpp>
#include <QEventLoop>
@ -24,7 +26,6 @@
#include <SceneScriptingInterface.h>
#include <ScriptEngine.h>
#include "EntityTreeRenderer.h"
#include "RenderableEntityItem.h"
@ -130,7 +131,12 @@ void EntityTreeRenderer::clear() {
if (scene) {
render::Transaction transaction;
foreach(auto entity, _entitiesInScene) {
entity->removeFromScene(entity, scene, transaction);
auto renderable = entity->getRenderableInterface();
if (!renderable) {
qCWarning(entitiesrenderer) << "EntityTreeRenderer::deletingEntity(), trying to remove non-renderable entity";
continue;
}
renderable->removeFromScene(entity, scene, transaction);
}
scene->enqueueTransaction(transaction);
} else {
@ -141,7 +147,7 @@ void EntityTreeRenderer::clear() {
// reset the zone to the default (while we load the next scene)
_layeredZones.clear();
OctreeRenderer::clear();
OctreeProcessor::clear();
}
void EntityTreeRenderer::reloadEntityScripts() {
@ -155,7 +161,7 @@ void EntityTreeRenderer::reloadEntityScripts() {
}
void EntityTreeRenderer::init() {
OctreeRenderer::init();
OctreeProcessor::init();
EntityTreePointer entityTree = std::static_pointer_cast<EntityTree>(_tree);
entityTree->setFBXService(this);
@ -181,7 +187,7 @@ void EntityTreeRenderer::shutdown() {
}
void EntityTreeRenderer::setTree(OctreePointer newTree) {
OctreeRenderer::setTree(newTree);
OctreeProcessor::setTree(newTree);
std::static_pointer_cast<EntityTree>(_tree)->setFBXService(this);
}
@ -791,24 +797,33 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) {
}
void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) {
if (!_entitiesInScene.contains(entityID)) {
return;
}
if (_tree && !_shuttingDown && _entitiesScriptEngine) {
_entitiesScriptEngine->unloadEntityScript(entityID, true);
}
auto scene = _viewState->getMain3DScene();
if (!scene) {
qCWarning(entitiesrenderer) << "EntityTreeRenderer::deletingEntity(), Unexpected null scene, possibly during application shutdown";
return;
}
auto entity = _entitiesInScene.take(entityID);
auto renderable = entity->getRenderableInterface();
if (!renderable) {
qCWarning(entitiesrenderer) << "EntityTreeRenderer::deletingEntity(), trying to remove non-renderable entity";
return;
}
forceRecheckEntities(); // reset our state to force checking our inside/outsideness of entities
// here's where we remove the entity payload from the scene
if (_entitiesInScene.contains(entityID)) {
auto entity = _entitiesInScene.take(entityID);
render::Transaction transaction;
auto scene = _viewState->getMain3DScene();
if (scene) {
entity->removeFromScene(entity, scene, transaction);
scene->enqueueTransaction(transaction);
} else {
qCWarning(entitiesrenderer) << "EntityTreeRenderer::deletingEntity(), Unexpected null scene, possibly during application shutdown";
}
}
render::Transaction transaction;
renderable->removeFromScene(entity, scene, transaction);
scene->enqueueTransaction(transaction);
}
void EntityTreeRenderer::addingEntity(const EntityItemID& entityID) {
@ -820,18 +835,25 @@ void EntityTreeRenderer::addingEntity(const EntityItemID& entityID) {
}
}
void EntityTreeRenderer::addEntityToScene(EntityItemPointer entity) {
void EntityTreeRenderer::addEntityToScene(const EntityItemPointer& entity) {
// here's where we add the entity payload to the scene
render::Transaction transaction;
auto scene = _viewState->getMain3DScene();
if (scene) {
if (entity->addToScene(entity, scene, transaction)) {
_entitiesInScene.insert(entity->getEntityItemID(), entity);
}
scene->enqueueTransaction(transaction);
} else {
if (!scene) {
qCWarning(entitiesrenderer) << "EntityTreeRenderer::addEntityToScene(), Unexpected null scene, possibly during application shutdown";
return;
}
auto renderable = entity->getRenderableInterface();
if (!renderable) {
qCWarning(entitiesrenderer) << "EntityTreeRenderer::addEntityToScene(), Unexpected non-renderable entity";
return;
}
render::Transaction transaction;
if (renderable->addToScene(entity, scene, transaction)) {
_entitiesInScene.insert(entity->getEntityItemID(), entity);
}
scene->enqueueTransaction(transaction);
}
@ -1050,3 +1072,4 @@ bool EntityTreeRenderer::LayeredZones::contains(const LayeredZones& other) {
}
return result;
}

View file

@ -20,9 +20,10 @@
#include <EntityTree.h>
#include <QMouseEvent>
#include <PointerEvent.h>
#include <OctreeRenderer.h>
#include <ScriptCache.h>
#include <TextureCache.h>
#include <OctreeProcessor.h>
#include <render/Forward.h>
class AbstractScriptingServicesInterface;
class AbstractViewStateInterface;
@ -38,7 +39,7 @@ using ModelWeakPointer = std::weak_ptr<Model>;
using CalculateEntityLoadingPriority = std::function<float(const EntityItem& item)>;
// Generic client side Octree renderer class.
class EntityTreeRenderer : public OctreeRenderer, public EntityItemFBXService, public Dependency {
class EntityTreeRenderer : public OctreeProcessor, public EntityItemFBXService, public Dependency {
Q_OBJECT
public:
EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState,
@ -144,7 +145,7 @@ protected:
private:
void resetEntitiesScriptEngine();
void addEntityToScene(EntityItemPointer entity);
void addEntityToScene(const EntityItemPointer& entity);
bool findBestZoneAndMaybeContainingEntities(QVector<EntityItemID>* entitiesContainingAvatar = nullptr);
bool applyLayeredZones();

View file

@ -40,7 +40,7 @@ namespace render {
template <> void payloadRender(const RenderableEntityItemProxy::Pointer& payload, RenderArgs* args) {
if (args) {
if (payload && payload->_entity && payload->_entity->getVisible()) {
payload->_entity->render(args);
payload->_entity->getRenderableInterface()->render(args);
}
}
}

View file

@ -34,13 +34,23 @@ enum class RenderItemStatusIcon {
void makeEntityItemStatusGetters(EntityItemPointer entity, render::Item::Status::Getters& statusGetters);
// Renderable entity item interface
class RenderableEntityInterface {
public:
virtual void render(RenderArgs* args) {};
virtual bool addToScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) = 0;
virtual void removeFromScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) = 0;
virtual RenderableEntityInterface* getRenderableInterface() { return nullptr; }
};
class RenderableEntityItemProxy {
public:
RenderableEntityItemProxy(EntityItemPointer entity, render::ItemID metaID) : _entity(entity), _metaID(metaID) { }
RenderableEntityItemProxy(const EntityItemPointer& entity, render::ItemID metaID)
: _entity(entity), _metaID(metaID) {}
typedef render::Payload<RenderableEntityItemProxy> Payload;
typedef Payload::DataPointer Pointer;
EntityItemPointer _entity;
const EntityItemPointer _entity;
render::ItemID _metaID;
};
@ -51,10 +61,11 @@ namespace render {
template <> uint32_t metaFetchMetaSubItems(const RenderableEntityItemProxy::Pointer& payload, ItemIDs& subItems);
}
// Mixin class for implementing basic single item rendering
class SimpleRenderableEntityItem {
class SimplerRenderableEntitySupport : public RenderableEntityInterface {
public:
bool addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) {
bool addToScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override {
_myItem = scene->allocateID();
auto renderData = std::make_shared<RenderableEntityItemProxy>(self, _myItem);
@ -69,7 +80,7 @@ public:
return true;
}
void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) {
void removeFromScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override {
transaction.removeItem(_myItem);
render::Item::clearID(_myItem);
}
@ -91,7 +102,6 @@ public:
qCWarning(entitiesrenderer) << "SimpleRenderableEntityItem::notifyChanged(), Unexpected null scene, possibly during application shutdown";
}
}
private:
render::ItemID _myItem { render::Item::INVALID_ITEM_ID };
};
@ -99,20 +109,18 @@ private:
#define SIMPLE_RENDERABLE() \
public: \
virtual bool addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override { return _renderHelper.addToScene(self, scene, transaction); } \
virtual void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override { _renderHelper.removeFromScene(self, scene, transaction); } \
virtual void locationChanged(bool tellPhysics = true) override { EntityItem::locationChanged(tellPhysics); _renderHelper.notifyChanged(); } \
virtual void dimensionsChanged() override { EntityItem::dimensionsChanged(); _renderHelper.notifyChanged(); } \
virtual void locationChanged(bool tellPhysics = true) override { EntityItem::locationChanged(tellPhysics); notifyChanged(); } \
virtual void dimensionsChanged() override { EntityItem::dimensionsChanged(); notifyChanged(); } \
virtual RenderableEntityInterface* getRenderableInterface() override { return this; } \
void checkFading() { \
bool transparent = isTransparent(); \
if (transparent != _prevIsTransparent) { \
_renderHelper.notifyChanged(); \
notifyChanged(); \
_isFading = false; \
_prevIsTransparent = transparent; \
} \
} \
private: \
SimpleRenderableEntityItem _renderHelper; \
bool _prevIsTransparent { isTransparent() };

View file

@ -27,7 +27,7 @@ RenderableLightEntityItem::RenderableLightEntityItem(const EntityItemID& entityI
{
}
bool RenderableLightEntityItem::addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) {
bool RenderableLightEntityItem::addToScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) {
_myItem = scene->allocateID();
auto renderItem = std::make_shared<LightPayload>();
@ -51,7 +51,7 @@ void RenderableLightEntityItem::somethingChangedNotification() {
LightEntityItem::somethingChangedNotification();
}
void RenderableLightEntityItem::removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) {
void RenderableLightEntityItem::removeFromScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) {
transaction.removeItem(_myItem);
render::Item::clearID(_myItem);
}

View file

@ -17,11 +17,13 @@
#include "RenderableEntityItem.h"
class RenderableLightEntityItem : public LightEntityItem {
class RenderableLightEntityItem : public LightEntityItem, public RenderableEntityInterface {
public:
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
RenderableLightEntityItem(const EntityItemID& entityItemID);
RenderableEntityInterface* getRenderableInterface() override { return this; }
virtual bool supportsDetailedRayIntersection() const override { return true; }
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
bool& keepSearching, OctreeElementPointer& element, float& distance,
@ -30,10 +32,10 @@ public:
void updateLightFromEntity(render::Transaction& transaction);
virtual bool addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override;
virtual bool addToScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override;
virtual void somethingChangedNotification() override;
virtual void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override;
virtual void removeFromScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override;
virtual void locationChanged(bool tellPhysics = true) override;

View file

@ -16,7 +16,7 @@
#include "RenderableEntityItem.h"
#include <GeometryCache.h>
class RenderableLineEntityItem : public LineEntityItem {
class RenderableLineEntityItem : public LineEntityItem, public SimplerRenderableEntitySupport {
public:
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
RenderableLineEntityItem(const EntityItemID& entityItemID) :

View file

@ -213,7 +213,7 @@ namespace render {
if (args) {
if (payload && payload->entity) {
PROFILE_RANGE(render_detail, "MetaModelRender");
payload->entity->render(args);
payload->entity->getRenderableInterface()->render(args);
}
}
}
@ -228,7 +228,7 @@ namespace render {
}
}
bool RenderableModelEntityItem::addToScene(EntityItemPointer self, const render::ScenePointer& scene,
bool RenderableModelEntityItem::addToScene(const EntityItemPointer& self, const render::ScenePointer& scene,
render::Transaction& transaction) {
_myMetaItem = scene->allocateID();
@ -249,7 +249,7 @@ bool RenderableModelEntityItem::addToScene(EntityItemPointer self, const render:
return true;
}
void RenderableModelEntityItem::removeFromScene(EntityItemPointer self, const render::ScenePointer& scene,
void RenderableModelEntityItem::removeFromScene(const EntityItemPointer& self, const render::ScenePointer& scene,
render::Transaction& transaction) {
transaction.removeItem(_myMetaItem);
render::Item::clearID(_myMetaItem);
@ -390,7 +390,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
if (!_model || _needsModelReload) {
// TODO: this getModel() appears to be about 3% of model render time. We should optimize
PerformanceTimer perfTimer("getModel");
auto renderer = qSharedPointerCast<EntityTreeRenderer>(args->_renderer);
auto renderer = qSharedPointerCast<EntityTreeRenderer>(args->_renderData);
getModel(renderer);
// Remap textures immediately after loading to avoid flicker
@ -605,7 +605,7 @@ bool RenderableModelEntityItem::findDetailedRayIntersection(const glm::vec3& ori
QString extraInfo;
return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance,
face, surfaceNormal, extraInfo, precisionPicking, precisionPicking);
face, surfaceNormal, extraInfo, precisionPicking, false);
}
void RenderableModelEntityItem::getCollisionGeometryResource() {

View file

@ -20,7 +20,7 @@
class Model;
class EntityTreeRenderer;
class RenderableModelEntityItem : public ModelEntityItem {
class RenderableModelEntityItem : public ModelEntityItem, RenderableEntityInterface {
public:
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
@ -28,6 +28,8 @@ public:
virtual ~RenderableModelEntityItem();
RenderableEntityInterface* getRenderableInterface() override { return this; }
virtual void setDimensions(const glm::vec3& value) override;
virtual void setModelURL(const QString& url) override;
@ -40,8 +42,8 @@ public:
void doInitialModelSimulation();
virtual bool addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override;
virtual void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override;
virtual bool addToScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override;
virtual void removeFromScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override;
void updateModelBounds();

View file

@ -161,7 +161,7 @@ RenderableParticleEffectEntityItem::RenderableParticleEffectEntityItem(const Ent
}
}
bool RenderableParticleEffectEntityItem::addToScene(EntityItemPointer self,
bool RenderableParticleEffectEntityItem::addToScene(const EntityItemPointer& self,
const render::ScenePointer& scene,
render::Transaction& transaction) {
_scene = scene;
@ -176,7 +176,7 @@ bool RenderableParticleEffectEntityItem::addToScene(EntityItemPointer self,
return true;
}
void RenderableParticleEffectEntityItem::removeFromScene(EntityItemPointer self,
void RenderableParticleEffectEntityItem::removeFromScene(const EntityItemPointer& self,
const render::ScenePointer& scene,
render::Transaction& transaction) {
transaction.removeItem(_renderItemId);

View file

@ -15,18 +15,20 @@
#include <TextureCache.h>
#include "RenderableEntityItem.h"
class RenderableParticleEffectEntityItem : public ParticleEffectEntityItem {
class RenderableParticleEffectEntityItem : public ParticleEffectEntityItem, public RenderableEntityInterface {
friend class ParticlePayloadData;
public:
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
RenderableParticleEffectEntityItem(const EntityItemID& entityItemID);
RenderableEntityInterface* getRenderableInterface() override { return this; }
virtual void update(const quint64& now) override;
void updateRenderItem();
virtual bool addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override;
virtual void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override;
virtual bool addToScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override;
virtual void removeFromScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override;
protected:
virtual void locationChanged(bool tellPhysics = true) override { EntityItem::locationChanged(tellPhysics); notifyBoundChanged(); }

View file

@ -206,7 +206,7 @@ void RenderablePolyLineEntityItem::render(RenderArgs* args) {
if (_texture->isLoaded()) {
batch.setResourceTexture(PAINTSTROKE_TEXTURE_SLOT, _texture->getGPUTexture());
} else {
batch.setResourceTexture(PAINTSTROKE_TEXTURE_SLOT, args->_whiteTexture);
batch.setResourceTexture(PAINTSTROKE_TEXTURE_SLOT, nullptr);
}
batch.setInputFormat(_format);

View file

@ -22,7 +22,7 @@
#include <QReadWriteLock>
class RenderablePolyLineEntityItem : public PolyLineEntityItem {
class RenderablePolyLineEntityItem : public PolyLineEntityItem, public SimplerRenderableEntitySupport {
public:
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
static void createPipeline();

View file

@ -820,7 +820,7 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) {
batch.drawIndexed(gpu::TRIANGLES, (gpu::uint32)mesh->getNumIndices(), 0);
}
bool RenderablePolyVoxEntityItem::addToScene(EntityItemPointer self,
bool RenderablePolyVoxEntityItem::addToScene(const EntityItemPointer& self,
const render::ScenePointer& scene,
render::Transaction& transaction) {
_myItem = scene->allocateID();
@ -838,7 +838,7 @@ bool RenderablePolyVoxEntityItem::addToScene(EntityItemPointer self,
return true;
}
void RenderablePolyVoxEntityItem::removeFromScene(EntityItemPointer self,
void RenderablePolyVoxEntityItem::removeFromScene(const EntityItemPointer& self,
const render::ScenePointer& scene,
render::Transaction& transaction) {
transaction.removeItem(_myItem);
@ -865,7 +865,7 @@ namespace render {
template <> void payloadRender(const PolyVoxPayload::Pointer& payload, RenderArgs* args) {
if (args && payload && payload->_owner) {
payload->_owner->render(args);
payload->_owner->getRenderableInterface()->render(args);
}
}
}

View file

@ -41,13 +41,15 @@ namespace render {
}
class RenderablePolyVoxEntityItem : public PolyVoxEntityItem {
class RenderablePolyVoxEntityItem : public PolyVoxEntityItem, public RenderableEntityInterface {
public:
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
RenderablePolyVoxEntityItem(const EntityItemID& entityItemID);
virtual ~RenderablePolyVoxEntityItem();
RenderableEntityInterface* getRenderableInterface() override { return this; }
void initializePolyVox();
virtual void somethingChangedNotification() override {
@ -105,10 +107,10 @@ public:
virtual void setYTextureURL(const QString& yTextureURL) override;
virtual void setZTextureURL(const QString& zTextureURL) override;
virtual bool addToScene(EntityItemPointer self,
virtual bool addToScene(const EntityItemPointer& self,
const render::ScenePointer& scene,
render::Transaction& transaction) override;
virtual void removeFromScene(EntityItemPointer self,
virtual void removeFromScene(const EntityItemPointer& self,
const render::ScenePointer& scene,
render::Transaction& transaction) override;

View file

@ -14,7 +14,7 @@
#include "RenderableEntityItem.h"
class RenderableShapeEntityItem : public ShapeEntityItem {
class RenderableShapeEntityItem : public ShapeEntityItem, private SimplerRenderableEntitySupport {
using Pointer = std::shared_ptr<RenderableShapeEntityItem>;
static Pointer baseFactory(const EntityItemID& entityID, const EntityItemProperties& properties);
public:

View file

@ -19,7 +19,7 @@
const int FIXED_FONT_POINT_SIZE = 40;
class RenderableTextEntityItem : public TextEntityItem {
class RenderableTextEntityItem : public TextEntityItem, public SimplerRenderableEntitySupport {
public:
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
RenderableTextEntityItem(const EntityItemID& entityItemID) : TextEntityItem(entityItemID) { }

View file

@ -198,7 +198,7 @@ void RenderableWebEntityItem::render(RenderArgs* args) {
#endif
if (!_webSurface) {
auto renderer = qSharedPointerCast<EntityTreeRenderer>(args->_renderer);
auto renderer = qSharedPointerCast<EntityTreeRenderer>(args->_renderData);
if (!buildWebSurface(renderer)) {
return;
}

View file

@ -27,7 +27,7 @@ class EntityTreeRenderer;
class RenderableWebEntityItem;
class RenderableWebEntityItem : public WebEntityItem {
class RenderableWebEntityItem : public WebEntityItem, SimplerRenderableEntitySupport {
public:
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
RenderableWebEntityItem(const EntityItemID& entityItemID);

View file

@ -257,7 +257,7 @@ bool RenderableZoneEntityItem::contains(const glm::vec3& point) const {
return false;
}
bool RenderableZoneEntityItem::addToScene(EntityItemPointer self, const render::ScenePointer& scene,
bool RenderableZoneEntityItem::addToScene(const EntityItemPointer& self, const render::ScenePointer& scene,
render::Transaction& transaction) {
_myMetaItem = scene->allocateID();
@ -277,7 +277,7 @@ bool RenderableZoneEntityItem::addToScene(EntityItemPointer self, const render::
return true;
}
void RenderableZoneEntityItem::removeFromScene(EntityItemPointer self, const render::ScenePointer& scene,
void RenderableZoneEntityItem::removeFromScene(const EntityItemPointer& self, const render::ScenePointer& scene,
render::Transaction& transaction) {
transaction.removeItem(_myMetaItem);
render::Item::clearID(_myMetaItem);

View file

@ -14,13 +14,14 @@
#include <Model.h>
#include <ZoneEntityItem.h>
#include "RenderableEntityItem.h"
class NetworkGeometry;
class KeyLightPayload;
class RenderableZoneEntityItemMeta;
class RenderableZoneEntityItem : public ZoneEntityItem {
class RenderableZoneEntityItem : public ZoneEntityItem, public RenderableEntityInterface {
public:
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
@ -30,6 +31,8 @@ public:
_needsInitialSimulation(true)
{ }
RenderableEntityInterface* getRenderableInterface() override { return this; }
virtual bool setProperties(const EntityItemProperties& properties) override;
virtual void somethingChangedNotification() override;
@ -41,8 +44,8 @@ public:
virtual void render(RenderArgs* args) override;
virtual bool contains(const glm::vec3& point) const override;
virtual bool addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override;
virtual void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override;
virtual bool addToScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override;
virtual void removeFromScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override;
render::ItemID getRenderItemID() const { return _myMetaItem; }

View file

@ -1,6 +1,8 @@
set(TARGET_NAME entities)
setup_hifi_library(Network Script)
link_hifi_libraries(avatars shared audio octree model model-networking fbx networking animation)
include_hifi_library_headers(networking)
include_hifi_library_headers(gpu)
target_bullet()

View file

@ -30,7 +30,7 @@ AddEntityOperator::AddEntityOperator(EntityTreePointer tree, EntityItemPointer n
_newEntityBox = queryCube.clamp((float)(-HALF_TREE_SCALE), (float)HALF_TREE_SCALE);
}
bool AddEntityOperator::preRecursion(OctreeElementPointer element) {
bool AddEntityOperator::preRecursion(const OctreeElementPointer& element) {
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
// In Pre-recursion, we're generally deciding whether or not we want to recurse this
@ -60,7 +60,7 @@ bool AddEntityOperator::preRecursion(OctreeElementPointer element) {
return keepSearching; // if we haven't yet found it, keep looking
}
bool AddEntityOperator::postRecursion(OctreeElementPointer element) {
bool AddEntityOperator::postRecursion(const OctreeElementPointer& element) {
// Post-recursion is the unwinding process. For this operation, while we
// unwind we want to mark the path as being dirty if we changed it below.
// We might have two paths, one for the old entity and one for the new entity.
@ -74,7 +74,7 @@ bool AddEntityOperator::postRecursion(OctreeElementPointer element) {
return keepSearching; // if we haven't yet found it, keep looking
}
OctreeElementPointer AddEntityOperator::possiblyCreateChildAt(OctreeElementPointer element, int childIndex) {
OctreeElementPointer AddEntityOperator::possiblyCreateChildAt(const OctreeElementPointer& element, int childIndex) {
// If we're getting called, it's because there was no child element at this index while recursing.
// We only care if this happens while still searching for the new entity location.
// Check to see if

View file

@ -16,9 +16,9 @@ class AddEntityOperator : public RecurseOctreeOperator {
public:
AddEntityOperator(EntityTreePointer tree, EntityItemPointer newEntity);
virtual bool preRecursion(OctreeElementPointer element) override;
virtual bool postRecursion(OctreeElementPointer element) override;
virtual OctreeElementPointer possiblyCreateChildAt(OctreeElementPointer element, int childIndex) override;
virtual bool preRecursion(const OctreeElementPointer& element) override;
virtual bool postRecursion(const OctreeElementPointer& element) override;
virtual OctreeElementPointer possiblyCreateChildAt(const OctreeElementPointer& element, int childIndex) override;
private:
EntityTreePointer _tree;
EntityItemPointer _newEntity;

View file

@ -55,7 +55,7 @@ void DeleteEntityOperator::addEntityIDToDeleteList(const EntityItemID& searchEnt
// does this entity tree element contain the old entity
bool DeleteEntityOperator::subTreeContainsSomeEntitiesToDelete(OctreeElementPointer element) {
bool DeleteEntityOperator::subTreeContainsSomeEntitiesToDelete(const OctreeElementPointer& element) {
bool containsEntity = false;
// If we don't have an old entity, then we don't contain the entity, otherwise
@ -72,7 +72,7 @@ bool DeleteEntityOperator::subTreeContainsSomeEntitiesToDelete(OctreeElementPoin
return containsEntity;
}
bool DeleteEntityOperator::preRecursion(OctreeElementPointer element) {
bool DeleteEntityOperator::preRecursion(const OctreeElementPointer& element) {
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
// In Pre-recursion, we're generally deciding whether or not we want to recurse this
@ -108,7 +108,7 @@ bool DeleteEntityOperator::preRecursion(OctreeElementPointer element) {
return keepSearching; // if we haven't yet found it, keep looking
}
bool DeleteEntityOperator::postRecursion(OctreeElementPointer element) {
bool DeleteEntityOperator::postRecursion(const OctreeElementPointer& element) {
// Post-recursion is the unwinding process. For this operation, while we
// unwind we want to mark the path as being dirty if we changed it below.
// We might have two paths, one for the old entity and one for the new entity.

View file

@ -36,8 +36,8 @@ public:
~DeleteEntityOperator();
void addEntityIDToDeleteList(const EntityItemID& searchEntityID);
virtual bool preRecursion(OctreeElementPointer element) override;
virtual bool postRecursion(OctreeElementPointer element) override;
virtual bool preRecursion(const OctreeElementPointer& element) override;
virtual bool postRecursion(const OctreeElementPointer& element) override;
const RemovedEntities& getEntities() const { return _entitiesToDelete; }
private:
@ -46,7 +46,7 @@ private:
quint64 _changeTime;
int _foundCount;
int _lookingCount;
bool subTreeContainsSomeEntitiesToDelete(OctreeElementPointer element);
bool subTreeContainsSomeEntitiesToDelete(const OctreeElementPointer& element);
};
#endif // hifi_DeleteEntityOperator_h

View file

@ -51,10 +51,6 @@ typedef std::shared_ptr<EntityTreeElement> EntityTreeElementPointer;
using EntityTreeElementExtraEncodeDataPointer = std::shared_ptr<EntityTreeElementExtraEncodeData>;
namespace render {
class Scene;
class Transaction;
}
#define DONT_ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() = 0;
#define ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() override { };
@ -65,6 +61,8 @@ namespace render {
class MeshProxyList;
class RenderableEntityInterface;
/// EntityItem class this is the base class for all entity types. It handles the basic properties and functionality available
/// to all other entity types. In particular: postion, size, rotation, age, lifetime, velocity, gravity. You can not instantiate
@ -83,6 +81,8 @@ public:
EntityItem(const EntityItemID& entityItemID);
virtual ~EntityItem();
virtual RenderableEntityInterface* getRenderableInterface() { return nullptr; }
inline EntityItemPointer getThisPointer() const {
return std::static_pointer_cast<EntityItem>(std::const_pointer_cast<SpatiallyNestable>(shared_from_this()));
}
@ -150,13 +150,6 @@ public:
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
bool& somethingChanged)
{ somethingChanged = false; return 0; }
virtual bool addToScene(EntityItemPointer self, const render::ScenePointer& scene,
render::Transaction& transaction) { return false; } // by default entity items don't add to scene
virtual void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene,
render::Transaction& transaction) { } // by default entity items don't add to scene
virtual void render(RenderArgs* args) { } // by default entity items don't know how to render
static int expectedBytes();
static void adjustEditPacketForClockSkew(QByteArray& buffer, qint64 clockSkew);

View file

@ -554,7 +554,7 @@ public:
};
bool EntityTree::findNearPointOperation(OctreeElementPointer element, void* extraData) {
bool EntityTree::findNearPointOperation(const OctreeElementPointer& element, void* extraData) {
FindNearPointArgs* args = static_cast<FindNearPointArgs*>(extraData);
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
@ -589,7 +589,7 @@ bool EntityTree::findNearPointOperation(OctreeElementPointer element, void* extr
return false;
}
bool findRayIntersectionOp(OctreeElementPointer element, void* extraData) {
bool findRayIntersectionOp(const OctreeElementPointer& element, void* extraData) {
RayArgs* args = static_cast<RayArgs*>(extraData);
bool keepSearching = true;
EntityTreeElementPointer entityTreeElementPointer = std::dynamic_pointer_cast<EntityTreeElement>(element);
@ -625,7 +625,7 @@ bool EntityTree::findRayIntersection(const glm::vec3& origin, const glm::vec3& d
}
EntityItemPointer EntityTree::findClosestEntity(glm::vec3 position, float targetRadius) {
EntityItemPointer EntityTree::findClosestEntity(const glm::vec3& position, float targetRadius) {
FindNearPointArgs args = { position, targetRadius, false, NULL, FLT_MAX };
withReadLock([&] {
// NOTE: This should use recursion, since this is a spatial operation
@ -642,7 +642,7 @@ public:
};
bool EntityTree::findInSphereOperation(OctreeElementPointer element, void* extraData) {
bool EntityTree::findInSphereOperation(const OctreeElementPointer& element, void* extraData) {
FindAllNearPointArgs* args = static_cast<FindAllNearPointArgs*>(extraData);
glm::vec3 penetration;
bool sphereIntersection = element->getAACube().findSpherePenetration(args->position, args->targetRadius, penetration);
@ -678,7 +678,7 @@ public:
QVector<EntityItemPointer> _foundEntities;
};
bool EntityTree::findInCubeOperation(OctreeElementPointer element, void* extraData) {
bool EntityTree::findInCubeOperation(const OctreeElementPointer& element, void* extraData) {
FindEntitiesInCubeArgs* args = static_cast<FindEntitiesInCubeArgs*>(extraData);
if (element->getAACube().touches(args->_cube)) {
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
@ -707,7 +707,7 @@ public:
QVector<EntityItemPointer> _foundEntities;
};
bool EntityTree::findInBoxOperation(OctreeElementPointer element, void* extraData) {
bool EntityTree::findInBoxOperation(const OctreeElementPointer& element, void* extraData) {
FindEntitiesInBoxArgs* args = static_cast<FindEntitiesInBoxArgs*>(extraData);
if (element->getAACube().touches(args->_box)) {
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
@ -732,7 +732,7 @@ public:
QVector<EntityItemPointer> entities;
};
bool EntityTree::findInFrustumOperation(OctreeElementPointer element, void* extraData) {
bool EntityTree::findInFrustumOperation(const OctreeElementPointer& element, void* extraData) {
FindInFrustumArgs* args = static_cast<FindInFrustumArgs*>(extraData);
if (element->isInView(args->frustum)) {
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
@ -1527,15 +1527,15 @@ void EntityTree::debugDumpMap() {
class ContentsDimensionOperator : public RecurseOctreeOperator {
public:
virtual bool preRecursion(OctreeElementPointer element) override;
virtual bool postRecursion(OctreeElementPointer element) override { return true; }
virtual bool preRecursion(const OctreeElementPointer& element) override;
virtual bool postRecursion(const OctreeElementPointer& element) override { return true; }
glm::vec3 getDimensions() const { return _contentExtents.size(); }
float getLargestDimension() const { return _contentExtents.largestDimension(); }
private:
Extents _contentExtents;
};
bool ContentsDimensionOperator::preRecursion(OctreeElementPointer element) {
bool ContentsDimensionOperator::preRecursion(const OctreeElementPointer& element) {
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
entityTreeElement->expandExtentsToContents(_contentExtents);
return true;
@ -1555,11 +1555,11 @@ float EntityTree::getContentsLargestDimension() {
class DebugOperator : public RecurseOctreeOperator {
public:
virtual bool preRecursion(OctreeElementPointer element) override;
virtual bool postRecursion(OctreeElementPointer element) override { return true; }
virtual bool preRecursion(const OctreeElementPointer& element) override;
virtual bool postRecursion(const OctreeElementPointer& element) override { return true; }
};
bool DebugOperator::preRecursion(OctreeElementPointer element) {
bool DebugOperator::preRecursion(const OctreeElementPointer& element) {
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
qCDebug(entities) << "EntityTreeElement [" << entityTreeElement.get() << "]";
entityTreeElement->debugDump();
@ -1573,11 +1573,11 @@ void EntityTree::dumpTree() {
class PruneOperator : public RecurseOctreeOperator {
public:
virtual bool preRecursion(OctreeElementPointer element) override { return true; }
virtual bool postRecursion(OctreeElementPointer element) override;
virtual bool preRecursion(const OctreeElementPointer& element) override { return true; }
virtual bool postRecursion(const OctreeElementPointer& element) override;
};
bool PruneOperator::postRecursion(OctreeElementPointer element) {
bool PruneOperator::postRecursion(const OctreeElementPointer& element) {
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
entityTreeElement->pruneChildren();
return true;
@ -1639,6 +1639,7 @@ QVector<EntityItemID> EntityTree::sendEntities(EntityEditPacketSender* packetSen
// If this is called repeatedly (e.g., multiple pastes with the same data), the new elements will clash unless we
// use new identifiers. We need to keep a map so that we can map parent identifiers correctly.
QHash<EntityItemID, EntityItemID> map;
args.map = &map;
withReadLock([&] {
recurseTreeWithOperation(sendEntitiesOperation, &args);
@ -1692,7 +1693,7 @@ QVector<EntityItemID> EntityTree::sendEntities(EntityEditPacketSender* packetSen
return map.values().toVector();
}
bool EntityTree::sendEntitiesOperation(OctreeElementPointer element, void* extraData) {
bool EntityTree::sendEntitiesOperation(const OctreeElementPointer& element, void* extraData) {
SendEntitiesOperationArgs* args = static_cast<SendEntitiesOperationArgs*>(extraData);
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);

Some files were not shown because too many files have changed in this diff Show more