mirror of
https://github.com/overte-org/overte.git
synced 2025-04-08 05:52:38 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into minimum-edit-entity-filter
This commit is contained in:
commit
d9182cd38d
36 changed files with 760 additions and 169 deletions
|
@ -12,6 +12,8 @@
|
|||
|
||||
#include "AssetServer.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QCryptographicHash>
|
||||
#include <QtCore/QDateTime>
|
||||
|
@ -21,6 +23,7 @@
|
|||
#include <QtCore/QJsonDocument>
|
||||
#include <QtCore/QString>
|
||||
|
||||
#include <SharedUtil.h>
|
||||
#include <ServerPathUtils.h>
|
||||
|
||||
#include "NetworkLogging.h"
|
||||
|
@ -28,8 +31,44 @@
|
|||
#include "SendAssetTask.h"
|
||||
#include "UploadAssetTask.h"
|
||||
|
||||
static const uint8_t MIN_CORES_FOR_MULTICORE = 4;
|
||||
static const uint8_t CPU_AFFINITY_COUNT_HIGH = 2;
|
||||
static const uint8_t CPU_AFFINITY_COUNT_LOW = 1;
|
||||
static const int INTERFACE_RUNNING_CHECK_FREQUENCY_MS = 1000;
|
||||
|
||||
const QString ASSET_SERVER_LOGGING_TARGET_NAME = "asset-server";
|
||||
|
||||
bool interfaceRunning() {
|
||||
bool result = false;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
QSharedMemory sharedMemory { getInterfaceSharedMemoryName() };
|
||||
result = sharedMemory.attach(QSharedMemory::ReadOnly);
|
||||
if (result) {
|
||||
sharedMemory.detach();
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
void updateConsumedCores() {
|
||||
static bool wasInterfaceRunning = false;
|
||||
bool isInterfaceRunning = interfaceRunning();
|
||||
// If state is unchanged, return early
|
||||
if (isInterfaceRunning == wasInterfaceRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
wasInterfaceRunning = isInterfaceRunning;
|
||||
auto coreCount = std::thread::hardware_concurrency();
|
||||
if (isInterfaceRunning) {
|
||||
coreCount = coreCount > MIN_CORES_FOR_MULTICORE ? CPU_AFFINITY_COUNT_HIGH : CPU_AFFINITY_COUNT_LOW;
|
||||
}
|
||||
qDebug() << "Setting max consumed cores to " << coreCount;
|
||||
setMaxCores(coreCount);
|
||||
}
|
||||
|
||||
|
||||
AssetServer::AssetServer(ReceivedMessage& message) :
|
||||
ThreadedAssignment(message),
|
||||
_taskPool(this)
|
||||
|
@ -45,6 +84,20 @@ AssetServer::AssetServer(ReceivedMessage& message) :
|
|||
packetReceiver.registerListener(PacketType::AssetGetInfo, this, "handleAssetGetInfo");
|
||||
packetReceiver.registerListener(PacketType::AssetUpload, this, "handleAssetUpload");
|
||||
packetReceiver.registerListener(PacketType::AssetMappingOperation, this, "handleAssetMappingOperation");
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
updateConsumedCores();
|
||||
QTimer* timer = new QTimer(this);
|
||||
auto timerConnection = connect(timer, &QTimer::timeout, [] {
|
||||
updateConsumedCores();
|
||||
});
|
||||
connect(qApp, &QCoreApplication::aboutToQuit, [this, timerConnection] {
|
||||
disconnect(timerConnection);
|
||||
});
|
||||
timer->setInterval(INTERFACE_RUNNING_CHECK_FREQUENCY_MS);
|
||||
timer->setTimerType(Qt::CoarseTimer);
|
||||
timer->start();
|
||||
#endif
|
||||
}
|
||||
|
||||
void AssetServer::run() {
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <StDev.h>
|
||||
#include <UUID.h>
|
||||
|
||||
#include "AudioHelpers.h"
|
||||
#include "AudioRingBuffer.h"
|
||||
#include "AudioMixerClientData.h"
|
||||
#include "AvatarAudioStream.h"
|
||||
|
@ -68,7 +69,8 @@ AudioMixer::AudioMixer(ReceivedMessage& message) :
|
|||
packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket");
|
||||
packetReceiver.registerListener(PacketType::NodeMuteRequest, this, "handleNodeMuteRequestPacket");
|
||||
packetReceiver.registerListener(PacketType::RadiusIgnoreRequest, this, "handleRadiusIgnoreRequestPacket");
|
||||
packetReceiver.registerListener(PacketType::RequestsDomainListData, this, "handleRequestsDomainListDataPacket");
|
||||
packetReceiver.registerListener(PacketType::RequestsDomainListData, this, "handleRequestsDomainListDataPacket");
|
||||
packetReceiver.registerListener(PacketType::PerAvatarGainSet, this, "handlePerAvatarGainSetDataPacket");
|
||||
|
||||
connect(nodeList.data(), &NodeList::nodeKilled, this, &AudioMixer::handleNodeKilled);
|
||||
}
|
||||
|
@ -186,7 +188,8 @@ void AudioMixer::handleNodeKilled(SharedNodePointer killedNode) {
|
|||
nodeList->eachNode([&killedNode](const SharedNodePointer& node) {
|
||||
auto clientData = dynamic_cast<AudioMixerClientData*>(node->getLinkedData());
|
||||
if (clientData) {
|
||||
clientData->removeHRTFsForNode(killedNode->getUUID());
|
||||
QUuid killedUUID = killedNode->getUUID();
|
||||
clientData->removeHRTFsForNode(killedUUID);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -240,6 +243,20 @@ void AudioMixer::handleNodeIgnoreRequestPacket(QSharedPointer<ReceivedMessage> p
|
|||
sendingNode->parseIgnoreRequestMessage(packet);
|
||||
}
|
||||
|
||||
void AudioMixer::handlePerAvatarGainSetDataPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode) {
|
||||
auto clientData = dynamic_cast<AudioMixerClientData*>(sendingNode->getLinkedData());
|
||||
if (clientData) {
|
||||
QUuid listeningNodeUUID = sendingNode->getUUID();
|
||||
// parse the UUID from the packet
|
||||
QUuid audioSourceUUID = QUuid::fromRfc4122(packet->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
||||
uint8_t packedGain;
|
||||
packet->readPrimitive(&packedGain);
|
||||
float gain = unpackFloatGainFromByte(packedGain);
|
||||
clientData->hrtfForStream(audioSourceUUID, QUuid()).setGainAdjustment(gain);
|
||||
qDebug() << "Setting gain adjustment for hrtf[" << listeningNodeUUID << "][" << audioSourceUUID << "] to " << gain;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioMixer::handleRadiusIgnoreRequestPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode) {
|
||||
sendingNode->parseIgnoreRadiusRequestMessage(packet);
|
||||
}
|
||||
|
|
|
@ -66,6 +66,7 @@ private slots:
|
|||
void handleRadiusIgnoreRequestPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
|
||||
void handleKillAvatarPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
|
||||
void handleNodeMuteRequestPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
|
||||
void handlePerAvatarGainSetDataPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
|
||||
|
||||
void start();
|
||||
void removeHRTFsForFinishedInjector(const QUuid& streamID);
|
||||
|
|
|
@ -262,8 +262,12 @@ void AvatarMixer::broadcastAvatarData() {
|
|||
// setup a PacketList for the avatarPackets
|
||||
auto avatarPacketList = NLPacketList::create(PacketType::BulkAvatarData);
|
||||
|
||||
if (avatar.getSessionDisplayName().isEmpty() && // We haven't set it yet...
|
||||
nodeData->getReceivedIdentity()) { // ... but we have processed identity (with possible displayName).
|
||||
if (nodeData->getAvatarSessionDisplayNameMustChange()) {
|
||||
const QString& existingBaseDisplayName = nodeData->getBaseDisplayName();
|
||||
if (--_sessionDisplayNames[existingBaseDisplayName].second <= 0) {
|
||||
_sessionDisplayNames.remove(existingBaseDisplayName);
|
||||
}
|
||||
|
||||
QString baseName = avatar.getDisplayName().trimmed();
|
||||
const QRegularExpression curses{ "fuck|shit|damn|cock|cunt" }; // POC. We may eventually want something much more elaborate (subscription?).
|
||||
baseName = baseName.replace(curses, "*"); // Replace rather than remove, so that people have a clue that the person's a jerk.
|
||||
|
@ -276,11 +280,14 @@ void AvatarMixer::broadcastAvatarData() {
|
|||
QPair<int, int>& soFar = _sessionDisplayNames[baseName]; // Inserts and answers 0, 0 if not already present, which is what we want.
|
||||
int& highWater = soFar.first;
|
||||
nodeData->setBaseDisplayName(baseName);
|
||||
avatar.setSessionDisplayName((highWater > 0) ? baseName + "_" + QString::number(highWater) : baseName);
|
||||
QString sessionDisplayName = (highWater > 0) ? baseName + "_" + QString::number(highWater) : baseName;
|
||||
avatar.setSessionDisplayName(sessionDisplayName);
|
||||
highWater++;
|
||||
soFar.second++; // refcount
|
||||
nodeData->flagIdentityChange();
|
||||
sendIdentityPacket(nodeData, node); // Tell new node about its sessionUUID. Others will find out below.
|
||||
nodeData->setAvatarSessionDisplayNameMustChange(false);
|
||||
sendIdentityPacket(nodeData, node); // Tell node whose name changed about its new session display name. Others will find out below.
|
||||
qDebug() << "Giving session display name" << sessionDisplayName << "to node with ID" << node->getUUID();
|
||||
}
|
||||
|
||||
// this is an AGENT we have received head data from
|
||||
|
@ -584,7 +591,7 @@ void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer<ReceivedMessage> mes
|
|||
if (avatar.processAvatarIdentity(identity)) {
|
||||
QMutexLocker nodeDataLocker(&nodeData->getMutex());
|
||||
nodeData->flagIdentityChange();
|
||||
nodeData->setReceivedIdentity();
|
||||
nodeData->setAvatarSessionDisplayNameMustChange(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,8 +53,8 @@ public:
|
|||
|
||||
HRCTime getIdentityChangeTimestamp() const { return _identityChangeTimestamp; }
|
||||
void flagIdentityChange() { _identityChangeTimestamp = p_high_resolution_clock::now(); }
|
||||
bool getReceivedIdentity() const { return _gotIdentity; }
|
||||
void setReceivedIdentity() { _gotIdentity = true; }
|
||||
bool getAvatarSessionDisplayNameMustChange() const { return _avatarSessionDisplayNameMustChange; }
|
||||
void setAvatarSessionDisplayNameMustChange(bool set = true) { _avatarSessionDisplayNameMustChange = set; }
|
||||
|
||||
void setFullRateDistance(float fullRateDistance) { _fullRateDistance = fullRateDistance; }
|
||||
float getFullRateDistance() const { return _fullRateDistance; }
|
||||
|
@ -112,7 +112,7 @@ private:
|
|||
std::unordered_set<QUuid> _hasReceivedFirstPacketsFrom;
|
||||
|
||||
HRCTime _identityChangeTimestamp;
|
||||
bool _gotIdentity { false };
|
||||
bool _avatarSessionDisplayNameMustChange{ false };
|
||||
|
||||
float _fullRateDistance = FLT_MAX;
|
||||
float _maxAvatarDistance = FLT_MAX;
|
||||
|
|
32
cmake/externals/LibOVRPlatform/CMakeLists.txt
vendored
Normal file
32
cmake/externals/LibOVRPlatform/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
include(ExternalProject)
|
||||
include(SelectLibraryConfigurations)
|
||||
|
||||
set(EXTERNAL_NAME LibOVRPlatform)
|
||||
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
|
||||
if (WIN32)
|
||||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/OVRPlatformSDK_v1.10.0.zip
|
||||
URL_MD5 e6c8264af16d904e6506acd5172fa0a9
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
LOG_DOWNLOAD 1
|
||||
)
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
|
||||
|
||||
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${SOURCE_DIR}/Windows/LibOVRPlatform64_1.lib CACHE TYPE INTERNAL)
|
||||
else()
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${SOURCE_DIR}/Windows/LibOVRPlatform32_1.lib CACHE TYPE INTERNAL)
|
||||
endif()
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/Include CACHE TYPE INTERNAL)
|
||||
endif ()
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
44
cmake/modules/FindLibOVRPlatform.cmake
Normal file
44
cmake/modules/FindLibOVRPlatform.cmake
Normal file
|
@ -0,0 +1,44 @@
|
|||
#
|
||||
# FindLibOVRPlatform.cmake
|
||||
#
|
||||
# Try to find the LibOVRPlatform library to use the Oculus Platform SDK
|
||||
#
|
||||
# You must provide a LIBOVRPLATFORM_ROOT_DIR which contains Windows and Include directories
|
||||
#
|
||||
# Once done this will define
|
||||
#
|
||||
# LIBOVRPLATFORM_FOUND - system found Oculus Platform SDK
|
||||
# LIBOVRPLATFORM_INCLUDE_DIRS - the Oculus Platform include directory
|
||||
# LIBOVRPLATFORM_LIBRARIES - Link this to use Oculus Platform
|
||||
#
|
||||
# Created on December 16, 2016 by Stephen Birarda
|
||||
# Copyright 2016 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
|
||||
#
|
||||
|
||||
|
||||
if (WIN32)
|
||||
# setup hints for LIBOVRPLATFORM search
|
||||
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
|
||||
hifi_library_search_hints("LibOVRPlatform")
|
||||
|
||||
find_path(LIBOVRPLATFORM_INCLUDE_DIRS OVR_Platform.h PATH_SUFFIXES Include HINTS ${LIBOVRPLATFORM_SEARCH_DIRS})
|
||||
|
||||
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
set(_LIB_NAME LibOVRPlatform64_1.lib)
|
||||
else()
|
||||
set(_LIB_NAME LibOVRPlatform32_1.lib)
|
||||
endif()
|
||||
|
||||
find_library(LIBOVRPLATFORM_LIBRARY_RELEASE NAMES ${_LIB_NAME} PATH_SUFFIXES Windows HINTS ${LIBOVRPLATFORM_SEARCH_DIRS})
|
||||
|
||||
include(SelectLibraryConfigurations)
|
||||
select_library_configurations(LIBOVRPLATFORM)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(LIBOVRPLATFORM DEFAULT_MSG LIBOVRPLATFORM_INCLUDE_DIRS LIBOVRPLATFORM_LIBRARIES)
|
||||
|
||||
mark_as_advanced(LIBOVRPLATFORM_INCLUDE_DIRS LIBOVRPLATFORM_LIBRARIES LIBOVRPLATFORM_SEARCH_DIRS)
|
||||
endif ()
|
|
@ -40,7 +40,7 @@
|
|||
{
|
||||
"name": "local_port",
|
||||
"label": "Local UDP Port",
|
||||
"help": "This is the local port your domain-server binds to for UDP connections.<br/>Depending on your router, this may need to be changed to run multiple full automatic networking domain-servers in the same network.",
|
||||
"help": "This is the local port your domain-server binds to for UDP connections.<br/>Depending on your router, this may need to be changed to unique values for each domain-server in order to run multiple full automatic networking domain-servers in the same network. You can use the value 0 to have the domain-server select a random port, which will help in preventing port collisions.",
|
||||
"default": "40102",
|
||||
"type": "int",
|
||||
"advanced": true
|
||||
|
|
|
@ -780,12 +780,12 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointer<Re
|
|||
|
||||
// This function processes the "Get Username from ID" request.
|
||||
void DomainServerSettingsManager::processUsernameFromIDRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
|
||||
// Before we do any processing on this packet, make sure it comes from a node that is allowed to kick (is an admin)
|
||||
if (sendingNode->getCanKick()) {
|
||||
// From the packet, pull the UUID we're identifying
|
||||
QUuid nodeUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
if (!nodeUUID.isNull()) {
|
||||
// From the packet, pull the UUID we're identifying
|
||||
QUuid nodeUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
||||
if (!nodeUUID.isNull()) {
|
||||
// Before we do any processing on this packet, make sure it comes from a node that is allowed to kick (is an admin)
|
||||
// OR from a node whose UUID matches the one in the packet
|
||||
if (sendingNode->getCanKick() || nodeUUID == sendingNode->getUUID()) {
|
||||
// First, make sure we actually have a node with this UUID
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
auto matchingNode = limitedNodeList->nodeWithUUID(nodeUUID);
|
||||
|
@ -813,12 +813,12 @@ void DomainServerSettingsManager::processUsernameFromIDRequestPacket(QSharedPoin
|
|||
qWarning() << "Node username request received for unknown node. Refusing to process.";
|
||||
}
|
||||
} else {
|
||||
qWarning() << "Node username request received for invalid node ID. Refusing to process.";
|
||||
qWarning() << "Refusing to process a username request packet from node" << uuidStringWithoutCurlyBraces(sendingNode->getUUID())
|
||||
<< ". Either node doesn't have kick permissions or is requesting a username not from their UUID.";
|
||||
}
|
||||
|
||||
} else {
|
||||
qWarning() << "Refusing to process a username request packet from node" << uuidStringWithoutCurlyBraces(sendingNode->getUUID())
|
||||
<< "that does not have kick permissions.";
|
||||
qWarning() << "Node username request received for invalid node ID. Refusing to process.";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Binary file not shown.
|
@ -10,35 +10,35 @@
|
|||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
import QtGraphicalEffects 1.0
|
||||
import "../styles-uit"
|
||||
|
||||
Row {
|
||||
Item {
|
||||
id: thisNameCard
|
||||
// Spacing
|
||||
spacing: 10
|
||||
// Anchors
|
||||
anchors.top: parent.top
|
||||
anchors {
|
||||
topMargin: (parent.height - contentHeight)/2
|
||||
bottomMargin: (parent.height - contentHeight)/2
|
||||
verticalCenter: parent.verticalCenter
|
||||
leftMargin: 10
|
||||
rightMargin: 10
|
||||
}
|
||||
|
||||
// Properties
|
||||
property int contentHeight: 50
|
||||
property string uuid: ""
|
||||
property string displayName: ""
|
||||
property string userName: ""
|
||||
property int displayTextHeight: 18
|
||||
property real displayNameTextPixelSize: 18
|
||||
property int usernameTextHeight: 12
|
||||
property real audioLevel: 0.0
|
||||
property bool isMyCard: false
|
||||
property bool selected: false
|
||||
|
||||
/* User image commented out for now - will probably be re-introduced later.
|
||||
Column {
|
||||
id: avatarImage
|
||||
// Size
|
||||
height: contentHeight
|
||||
height: parent.height
|
||||
width: height
|
||||
Image {
|
||||
id: userImage
|
||||
|
@ -49,22 +49,118 @@ Row {
|
|||
}
|
||||
}
|
||||
*/
|
||||
Column {
|
||||
Item {
|
||||
id: textContainer
|
||||
// Size
|
||||
width: parent.width - /*avatarImage.width - */parent.anchors.leftMargin - parent.anchors.rightMargin - parent.spacing
|
||||
height: contentHeight
|
||||
width: parent.width - /*avatarImage.width - parent.spacing - */parent.anchors.leftMargin - parent.anchors.rightMargin
|
||||
height: childrenRect.height
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
// DisplayName Text
|
||||
// DisplayName field for my card
|
||||
Rectangle {
|
||||
id: myDisplayName
|
||||
visible: isMyCard
|
||||
// Size
|
||||
width: parent.width + 70
|
||||
height: 35
|
||||
// Anchors
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: -10
|
||||
// Style
|
||||
color: hifi.colors.textFieldLightBackground
|
||||
border.color: hifi.colors.blueHighlight
|
||||
border.width: 0
|
||||
TextInput {
|
||||
id: myDisplayNameText
|
||||
// Properties
|
||||
text: thisNameCard.displayName
|
||||
maximumLength: 256
|
||||
clip: true
|
||||
// Size
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
// Anchors
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 10
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: editGlyph.width + editGlyph.anchors.rightMargin
|
||||
// Style
|
||||
color: hifi.colors.darkGray
|
||||
FontLoader { id: firaSansSemiBold; source: "../../fonts/FiraSans-SemiBold.ttf"; }
|
||||
font.family: firaSansSemiBold.name
|
||||
font.pixelSize: displayNameTextPixelSize
|
||||
selectionColor: hifi.colors.blueHighlight
|
||||
selectedTextColor: "black"
|
||||
// Text Positioning
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
horizontalAlignment: TextInput.AlignLeft
|
||||
// Signals
|
||||
onEditingFinished: {
|
||||
pal.sendToScript({method: 'displayNameUpdate', params: text})
|
||||
cursorPosition = 0
|
||||
focus = false
|
||||
myDisplayName.border.width = 0
|
||||
color = hifi.colors.darkGray
|
||||
}
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
myDisplayName.border.width = 1
|
||||
myDisplayNameText.focus ? myDisplayNameText.cursorPosition = myDisplayNameText.positionAt(mouseX, mouseY, TextInput.CursorOnCharacter) : myDisplayNameText.selectAll();
|
||||
myDisplayNameText.focus = true
|
||||
myDisplayNameText.color = "black"
|
||||
}
|
||||
onDoubleClicked: {
|
||||
myDisplayNameText.selectAll();
|
||||
myDisplayNameText.focus = true;
|
||||
}
|
||||
onEntered: myDisplayName.color = hifi.colors.lightGrayText
|
||||
onExited: myDisplayName.color = hifi.colors.textFieldLightBackground
|
||||
}
|
||||
// Edit pencil glyph
|
||||
HiFiGlyphs {
|
||||
id: editGlyph
|
||||
text: hifi.glyphs.editPencil
|
||||
// Text Size
|
||||
size: displayNameTextPixelSize*1.5
|
||||
// Anchors
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 5
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
// Style
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
color: hifi.colors.baseGray
|
||||
}
|
||||
}
|
||||
// Spacer for DisplayName for my card
|
||||
Rectangle {
|
||||
id: myDisplayNameSpacer
|
||||
width: myDisplayName.width
|
||||
// Anchors
|
||||
anchors.top: myDisplayName.bottom
|
||||
height: 5
|
||||
visible: isMyCard
|
||||
opacity: 0
|
||||
}
|
||||
// DisplayName Text for others' cards
|
||||
FiraSansSemiBold {
|
||||
id: displayNameText
|
||||
// Properties
|
||||
text: thisNameCard.displayName
|
||||
elide: Text.ElideRight
|
||||
visible: !isMyCard
|
||||
// Size
|
||||
width: parent.width
|
||||
// Anchors
|
||||
anchors.top: parent.top
|
||||
// Text Size
|
||||
size: thisNameCard.displayTextHeight
|
||||
size: displayNameTextPixelSize
|
||||
// Text Positioning
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
// Style
|
||||
|
@ -80,6 +176,8 @@ Row {
|
|||
visible: thisNameCard.displayName
|
||||
// Size
|
||||
width: parent.width
|
||||
// Anchors
|
||||
anchors.top: isMyCard ? myDisplayNameSpacer.bottom : displayNameText.bottom
|
||||
// Text Size
|
||||
size: thisNameCard.usernameTextHeight
|
||||
// Text Positioning
|
||||
|
@ -90,25 +188,56 @@ Row {
|
|||
|
||||
// Spacer
|
||||
Item {
|
||||
id: spacer
|
||||
height: 4
|
||||
width: parent.width
|
||||
// Anchors
|
||||
anchors.top: userNameText.bottom
|
||||
}
|
||||
|
||||
// VU Meter
|
||||
Rectangle { // CHANGEME to the appropriate type!
|
||||
Rectangle {
|
||||
id: nameCardVUMeter
|
||||
// Size
|
||||
width: parent.width
|
||||
width: isMyCard ? myDisplayName.width - 20 : ((gainSlider.value - gainSlider.minimumValue)/(gainSlider.maximumValue - gainSlider.minimumValue)) * parent.width
|
||||
height: 8
|
||||
// Anchors
|
||||
anchors.top: spacer.bottom
|
||||
// Style
|
||||
radius: 4
|
||||
color: "#c5c5c5"
|
||||
// Rectangle for the zero-gain point on the VU meter
|
||||
Rectangle {
|
||||
id: vuMeterZeroGain
|
||||
visible: gainSlider.visible
|
||||
// Size
|
||||
width: 4
|
||||
height: 18
|
||||
// Style
|
||||
color: hifi.colors.darkGray
|
||||
// Anchors
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: (-gainSlider.minimumValue)/(gainSlider.maximumValue - gainSlider.minimumValue) * gainSlider.width - 4
|
||||
}
|
||||
// Rectangle for the VU meter line
|
||||
Rectangle {
|
||||
id: vuMeterLine
|
||||
width: gainSlider.width
|
||||
visible: gainSlider.visible
|
||||
// Style
|
||||
color: vuMeterBase.color
|
||||
radius: nameCardVUMeter.radius
|
||||
height: nameCardVUMeter.height / 2
|
||||
anchors.verticalCenter: nameCardVUMeter.verticalCenter
|
||||
}
|
||||
// Rectangle for the VU meter base
|
||||
Rectangle {
|
||||
id: vuMeterBase
|
||||
// Anchors
|
||||
anchors.fill: parent
|
||||
// Style
|
||||
color: "#c5c5c5"
|
||||
color: parent.color
|
||||
radius: parent.radius
|
||||
}
|
||||
// Rectangle for the VU meter audio level
|
||||
|
@ -117,7 +246,7 @@ Row {
|
|||
// Size
|
||||
width: (thisNameCard.audioLevel) * parent.width
|
||||
// Style
|
||||
color: "#c5c5c5"
|
||||
color: parent.color
|
||||
radius: parent.radius
|
||||
// Anchors
|
||||
anchors.bottom: parent.bottom
|
||||
|
@ -138,5 +267,66 @@ Row {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Per-Avatar Gain Slider
|
||||
Slider {
|
||||
id: gainSlider
|
||||
// Size
|
||||
width: parent.width
|
||||
height: 14
|
||||
// Anchors
|
||||
anchors.verticalCenter: nameCardVUMeter.verticalCenter
|
||||
// Properties
|
||||
visible: !isMyCard && selected
|
||||
value: pal.gainSliderValueDB[uuid] ? pal.gainSliderValueDB[uuid] : 0.0
|
||||
minimumValue: -60.0
|
||||
maximumValue: 20.0
|
||||
stepSize: 5
|
||||
updateValueWhileDragging: true
|
||||
onValueChanged: updateGainFromQML(uuid, value)
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onWheel: {
|
||||
// Do nothing.
|
||||
}
|
||||
onDoubleClicked: {
|
||||
gainSlider.value = 0.0
|
||||
}
|
||||
onPressed: {
|
||||
// Pass through to Slider
|
||||
mouse.accepted = false
|
||||
}
|
||||
onReleased: {
|
||||
// Pass through to Slider
|
||||
mouse.accepted = false
|
||||
}
|
||||
}
|
||||
style: SliderStyle {
|
||||
groove: Rectangle {
|
||||
color: "#c5c5c5"
|
||||
implicitWidth: gainSlider.width
|
||||
implicitHeight: 4
|
||||
radius: 2
|
||||
opacity: 0
|
||||
}
|
||||
handle: Rectangle {
|
||||
anchors.centerIn: parent
|
||||
color: (control.pressed || control.hovered) ? "#00b4ef" : "#8F8F8F"
|
||||
implicitWidth: 10
|
||||
implicitHeight: 16
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateGainFromQML(avatarUuid, sliderValue) {
|
||||
if (pal.gainSliderValueDB[avatarUuid] !== sliderValue) {
|
||||
pal.gainSliderValueDB[avatarUuid] = sliderValue;
|
||||
var data = {
|
||||
sessionId: avatarUuid,
|
||||
gain: sliderValue
|
||||
};
|
||||
pal.sendToScript({method: 'updateGain', params: data});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ Rectangle {
|
|||
// Style
|
||||
color: "#E3E3E3"
|
||||
// Properties
|
||||
property int myCardHeight: 70
|
||||
property int myCardHeight: 90
|
||||
property int rowHeight: 70
|
||||
property int actionButtonWidth: 75
|
||||
property int nameCardWidth: palContainer.width - actionButtonWidth*(iAmAdmin ? 4 : 2) - 4 - hifi.dimensions.scrollbarBackgroundWidth
|
||||
|
@ -32,6 +32,20 @@ Rectangle {
|
|||
property var ignored: ({}); // Keep a local list of ignored avatars & their data. Necessary because HashMap is slow to respond after ignoring.
|
||||
property var userModelData: [] // This simple list is essentially a mirror of the userModel listModel without all the extra complexities.
|
||||
property bool iAmAdmin: false
|
||||
// Keep a local list of per-avatar gainSliderValueDBs. Far faster than fetching this data from the server.
|
||||
// NOTE: if another script modifies the per-avatar gain, this value won't be accurate!
|
||||
property var gainSliderValueDB: ({});
|
||||
|
||||
// The letterbox used for popup messages
|
||||
LetterboxMessage {
|
||||
id: letterboxMessage
|
||||
z: 999 // Force the popup on top of everything else
|
||||
}
|
||||
function letterbox(message) {
|
||||
letterboxMessage.text = message
|
||||
letterboxMessage.visible = true
|
||||
letterboxMessage.popupRadius = 0
|
||||
}
|
||||
|
||||
// This is the container for the PAL
|
||||
Rectangle {
|
||||
|
@ -51,7 +65,7 @@ Rectangle {
|
|||
id: myInfo
|
||||
// Size
|
||||
width: palContainer.width
|
||||
height: myCardHeight + 20
|
||||
height: myCardHeight
|
||||
// Style
|
||||
color: pal.color
|
||||
// Anchors
|
||||
|
@ -65,6 +79,7 @@ Rectangle {
|
|||
displayName: myData.displayName
|
||||
userName: myData.userName
|
||||
audioLevel: myData.audioLevel
|
||||
isMyCard: true
|
||||
// Size
|
||||
width: nameCardWidth
|
||||
height: parent.height
|
||||
|
@ -172,8 +187,6 @@ Rectangle {
|
|||
TableViewColumn {
|
||||
visible: iAmAdmin
|
||||
role: "kick"
|
||||
// The hacky spaces used to center text over the button, since I don't know how to apply a margin
|
||||
// to column header text.
|
||||
title: "BAN"
|
||||
width: actionButtonWidth
|
||||
movable: false
|
||||
|
@ -206,6 +219,8 @@ Rectangle {
|
|||
userName: model && model.userName
|
||||
audioLevel: model && model.audioLevel
|
||||
visible: !isCheckBox && !isButton
|
||||
uuid: model && model.sessionId
|
||||
selected: styleData.selected
|
||||
// Size
|
||||
width: nameCardWidth
|
||||
height: parent.height
|
||||
|
@ -331,11 +346,6 @@ Rectangle {
|
|||
visible: iAmAdmin
|
||||
color: hifi.colors.lightGrayText
|
||||
}
|
||||
function letterbox(message) {
|
||||
letterboxMessage.text = message;
|
||||
letterboxMessage.visible = true
|
||||
|
||||
}
|
||||
// This Rectangle refers to the [?] popup button next to "NAMES"
|
||||
Rectangle {
|
||||
color: hifi.colors.tableBackgroundLight
|
||||
|
@ -396,9 +406,6 @@ Rectangle {
|
|||
onExited: adminHelpText.color = hifi.colors.redHighlight
|
||||
}
|
||||
}
|
||||
LetterboxMessage {
|
||||
id: letterboxMessage
|
||||
}
|
||||
}
|
||||
|
||||
function findSessionIndex(sessionId, optionalData) { // no findIndex in .qml
|
||||
|
@ -492,8 +499,13 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
break;
|
||||
case 'clearIgnored':
|
||||
case 'clearLocalQMLData':
|
||||
ignored = {};
|
||||
gainSliderValueDB = {};
|
||||
break;
|
||||
case 'avatarDisconnected':
|
||||
var sessionID = message.params[0];
|
||||
delete ignored[sessionID];
|
||||
break;
|
||||
default:
|
||||
console.log('Unrecognized message:', JSON.stringify(message));
|
||||
|
|
|
@ -312,10 +312,11 @@ Item {
|
|||
readonly property string error: "="
|
||||
readonly property string settings: "@"
|
||||
readonly property string trash: "{"
|
||||
readonly property string objectGroup: ""
|
||||
readonly property string objectGroup: "\ue000"
|
||||
readonly property string cm: "}"
|
||||
readonly property string msvg79: "~"
|
||||
readonly property string deg: "\\"
|
||||
readonly property string px: "|"
|
||||
readonly property string editPencil: "\ue00d"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -252,7 +252,7 @@ public:
|
|||
static const unsigned long MAX_HEARTBEAT_AGE_USECS = 30 * USECS_PER_SECOND;
|
||||
static const int WARNING_ELAPSED_HEARTBEAT = 500 * USECS_PER_MSEC; // warn if elapsed heartbeat average is large
|
||||
static const int HEARTBEAT_SAMPLES = 100000; // ~5 seconds worth of samples
|
||||
|
||||
|
||||
// Set the heartbeat on launch
|
||||
DeadlockWatchdogThread() {
|
||||
setObjectName("Deadlock Watchdog");
|
||||
|
@ -616,7 +616,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
_window->setWindowTitle("Interface");
|
||||
|
||||
Model::setAbstractViewStateInterface(this); // The model class will sometimes need to know view state details from us
|
||||
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
// Set up a watchdog thread to intentionally crash the application on deadlocks
|
||||
|
@ -637,6 +637,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
qCDebug(interfaceapp) << "[VERSION] We will use DEVELOPMENT global services.";
|
||||
#endif
|
||||
|
||||
// set the OCULUS_STORE property so the oculus plugin can know if we ran from the Oculus Store
|
||||
static const QString OCULUS_STORE_ARG = "--oculus-store";
|
||||
setProperty(hifi::properties::OCULUS_STORE, arguments().indexOf(OCULUS_STORE_ARG) != -1);
|
||||
|
||||
static const QString NO_UPDATER_ARG = "--no-updater";
|
||||
static const bool noUpdater = arguments().indexOf(NO_UPDATER_ARG) != -1;
|
||||
|
@ -697,7 +700,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
audioIO->setPositionGetter([]{
|
||||
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||
auto myAvatar = avatarManager ? avatarManager->getMyAvatar() : nullptr;
|
||||
|
||||
|
||||
return myAvatar ? myAvatar->getPositionForAudio() : Vectors::ZERO;
|
||||
});
|
||||
audioIO->setOrientationGetter([]{
|
||||
|
@ -880,7 +883,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
#ifdef Q_OS_MAC
|
||||
auto cursorTarget = _window; // OSX doesn't seem to provide for hiding the cursor only on the GL widget
|
||||
#else
|
||||
// On windows and linux, hiding the top level cursor also means it's invisible when hovering over the
|
||||
// On windows and linux, hiding the top level cursor also means it's invisible when hovering over the
|
||||
// window menu, which is a pain, so only hide it for the GL surface
|
||||
auto cursorTarget = _glWidget;
|
||||
#endif
|
||||
|
@ -1125,7 +1128,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
loadSettings();
|
||||
|
||||
// Now that we've loaded the menu and thus switched to the previous display plugin
|
||||
// we can unlock the desktop repositioning code, since all the positions will be
|
||||
// we can unlock the desktop repositioning code, since all the positions will be
|
||||
// relative to the desktop size for this plugin
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
offscreenUi->getDesktop()->setProperty("repositionLocked", false);
|
||||
|
@ -1235,8 +1238,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
|
||||
// Add periodic checks to send user activity data
|
||||
static int CHECK_NEARBY_AVATARS_INTERVAL_MS = 10000;
|
||||
static int SEND_STATS_INTERVAL_MS = 10000;
|
||||
static int NEARBY_AVATAR_RADIUS_METERS = 10;
|
||||
|
||||
// setup the stats interval depending on if the 1s faster hearbeat was requested
|
||||
static const QString FAST_STATS_ARG = "--fast-heartbeat";
|
||||
static int SEND_STATS_INTERVAL_MS = arguments().indexOf(FAST_STATS_ARG) != -1 ? 1000 : 10000;
|
||||
|
||||
static glm::vec3 lastAvatarPosition = myAvatar->getPosition();
|
||||
static glm::mat4 lastHMDHeadPose = getHMDSensorPose();
|
||||
|
@ -1598,7 +1604,7 @@ void Application::checkChangeCursor() {
|
|||
#ifdef Q_OS_MAC
|
||||
auto cursorTarget = _window; // OSX doesn't seem to provide for hiding the cursor only on the GL widget
|
||||
#else
|
||||
// On windows and linux, hiding the top level cursor also means it's invisible when hovering over the
|
||||
// On windows and linux, hiding the top level cursor also means it's invisible when hovering over the
|
||||
// window menu, which is a pain, so only hide it for the GL surface
|
||||
auto cursorTarget = _glWidget;
|
||||
#endif
|
||||
|
@ -1785,7 +1791,7 @@ Application::~Application() {
|
|||
#endif
|
||||
// The window takes ownership of the menu, so this has the side effect of destroying it.
|
||||
_window->setMenuBar(nullptr);
|
||||
|
||||
|
||||
_window->deleteLater();
|
||||
|
||||
// Can't log to file passed this point, FileLogger about to be deleted
|
||||
|
@ -1811,10 +1817,10 @@ void Application::initializeGL() {
|
|||
|
||||
_glWidget->makeCurrent();
|
||||
gpu::Context::init<gpu::gl::GLBackend>();
|
||||
qApp->setProperty(hifi::properties::gl::MAKE_PROGRAM_CALLBACK,
|
||||
qApp->setProperty(hifi::properties::gl::MAKE_PROGRAM_CALLBACK,
|
||||
QVariant::fromValue((void*)(&gpu::gl::GLBackend::makeProgram)));
|
||||
_gpuContext = std::make_shared<gpu::Context>();
|
||||
// The gpu context can make child contexts for transfers, so
|
||||
// The gpu context can make child contexts for transfers, so
|
||||
// we need to restore primary rendering context
|
||||
_glWidget->makeCurrent();
|
||||
|
||||
|
@ -2034,7 +2040,7 @@ void Application::paintGL() {
|
|||
// FIXME not needed anymore?
|
||||
_offscreenContext->makeCurrent();
|
||||
|
||||
// If a display plugin loses it's underlying support, it
|
||||
// If a display plugin loses it's underlying support, it
|
||||
// needs to be able to signal us to not use it
|
||||
if (!displayPlugin->beginFrameRender(_frameCount)) {
|
||||
_inPaint = false;
|
||||
|
@ -2846,7 +2852,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
if (isMirrorChecked) {
|
||||
|
||||
// if we got here without coming in from a non-Full Screen mirror case, then our
|
||||
// _returnFromFullScreenMirrorTo is unknown. In that case we'll go to the old
|
||||
// _returnFromFullScreenMirrorTo is unknown. In that case we'll go to the old
|
||||
// behavior of returning to ThirdPerson
|
||||
if (_returnFromFullScreenMirrorTo.isEmpty()) {
|
||||
_returnFromFullScreenMirrorTo = MenuOption::ThirdPerson;
|
||||
|
@ -3020,7 +3026,7 @@ void Application::mouseMoveEvent(QMouseEvent* event) {
|
|||
maybeToggleMenuVisible(event);
|
||||
|
||||
auto& compositor = getApplicationCompositor();
|
||||
// if this is a real mouse event, and we're in HMD mode, then we should use it to move the
|
||||
// if this is a real mouse event, and we're in HMD mode, then we should use it to move the
|
||||
// compositor reticle
|
||||
// handleRealMouseMoveEvent() will return true, if we shouldn't process the event further
|
||||
if (!compositor.fakeEventActive() && compositor.handleRealMouseMoveEvent()) {
|
||||
|
@ -4109,7 +4115,7 @@ void Application::setKeyboardFocusEntity(EntityItemID entityItemID) {
|
|||
}
|
||||
_lastAcceptedKeyPress = usecTimestampNow();
|
||||
|
||||
setKeyboardFocusHighlight(entity->getPosition(), entity->getRotation(),
|
||||
setKeyboardFocusHighlight(entity->getPosition(), entity->getRotation(),
|
||||
entity->getDimensions() * FOCUS_HIGHLIGHT_EXPANSION_FACTOR);
|
||||
}
|
||||
}
|
||||
|
@ -4700,7 +4706,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
|
|||
_octreeQuery.setMaxQueryPacketsPerSecond(0);
|
||||
}
|
||||
|
||||
// if asked to forceResend, then set the query's position/orientation to be degenerate in a manner
|
||||
// if asked to forceResend, then set the query's position/orientation to be degenerate in a manner
|
||||
// that will cause our next query to be guarenteed to be different and the server will resend to us
|
||||
if (forceResend) {
|
||||
_octreeQuery.setCameraPosition(glm::vec3(-0.1, -0.1, -0.1));
|
||||
|
@ -5294,15 +5300,17 @@ bool Application::nearbyEntitiesAreReadyForPhysics() {
|
|||
if (_nearbyEntitiesStabilityCount >= MINIMUM_NEARBY_ENTITIES_STABILITY_COUNT) {
|
||||
// We've seen the same number of nearby entities for several stats packets in a row. assume we've got all
|
||||
// the local entities.
|
||||
bool result = true;
|
||||
foreach (EntityItemPointer entity, entities) {
|
||||
if (entity->shouldBePhysical() && !entity->isReadyToComputeShape()) {
|
||||
static QString repeatedMessage =
|
||||
LogHandler::getInstance().addRepeatedMessageRegex("Physics disabled until entity loads: .*");
|
||||
qCDebug(interfaceapp) << "Physics disabled until entity loads: " << entity->getID() << entity->getName();
|
||||
return false;
|
||||
// don't break here because we want all the relevant entities to start their downloads
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return result;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -5833,7 +5841,7 @@ void Application::addAssetToWorldWithNewMapping(QString filePath, QString mappin
|
|||
mapping = mapping.insert(mapping.lastIndexOf("."), "-" + QString::number(copy));
|
||||
addAssetToWorldWithNewMapping(filePath, mapping, copy);
|
||||
} else {
|
||||
QString errorInfo = "Too many copies of asset name: "
|
||||
QString errorInfo = "Too many copies of asset name: "
|
||||
+ mapping.left(mapping.length() - QString::number(copy).length() - 1);
|
||||
qWarning(interfaceapp) << "Error downloading model: " + errorInfo;
|
||||
addAssetToWorldError(filenameFromPath(filePath), errorInfo);
|
||||
|
@ -5900,7 +5908,7 @@ void Application::addAssetToWorldAddEntity(QString filePath, QString mapping) {
|
|||
|
||||
// Note: Model dimensions are not available here; model is scaled per FBX mesh in RenderableModelEntityItem::update() later
|
||||
// on. But FBX dimensions may be in cm, so we monitor for the dimension change and rescale again if warranted.
|
||||
|
||||
|
||||
if (entityID == QUuid()) {
|
||||
QString errorInfo = "Could not add model " + mapping + " to world.";
|
||||
qWarning(interfaceapp) << "Could not add model to world: " + errorInfo;
|
||||
|
@ -6364,7 +6372,7 @@ glm::uvec2 Application::getCanvasSize() const {
|
|||
}
|
||||
|
||||
QRect Application::getRenderingGeometry() const {
|
||||
auto geometry = _glWidget->geometry();
|
||||
auto geometry = _glWidget->geometry();
|
||||
auto topLeft = geometry.topLeft();
|
||||
auto topLeftScreen = _glWidget->mapToGlobal(topLeft);
|
||||
geometry.moveTopLeft(topLeftScreen);
|
||||
|
@ -6727,8 +6735,8 @@ bool Application::makeRenderingContextCurrent() {
|
|||
return _offscreenContext->makeCurrent();
|
||||
}
|
||||
|
||||
bool Application::isForeground() const {
|
||||
return _isForeground && !_window->isMinimized();
|
||||
bool Application::isForeground() const {
|
||||
return _isForeground && !_window->isMinimized();
|
||||
}
|
||||
|
||||
void Application::sendMousePressOnEntity(QUuid id, PointerEvent event) {
|
||||
|
|
|
@ -260,6 +260,10 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar
|
|||
}
|
||||
if (removalReason == KillAvatarReason::TheirAvatarEnteredYourBubble || removalReason == YourAvatarEnteredTheirBubble) {
|
||||
DependencyManager::get<NodeList>()->radiusIgnoreNodeBySessionID(avatar->getSessionUUID(), true);
|
||||
} else if (removalReason == KillAvatarReason::AvatarDisconnected) {
|
||||
// remove from node sets, if present
|
||||
DependencyManager::get<NodeList>()->removeFromIgnoreMuteSets(avatar->getSessionUUID());
|
||||
DependencyManager::get<UsersScriptingInterface>()->avatarDisconnected(avatar->getSessionUUID());
|
||||
}
|
||||
_avatarFades.push_back(removedAvatar);
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ int main(int argc, const char* argv[]) {
|
|||
QCoreApplication::setOrganizationDomain(BuildInfo::ORGANIZATION_DOMAIN);
|
||||
QCoreApplication::setApplicationVersion(BuildInfo::VERSION);
|
||||
|
||||
QString applicationName = "High Fidelity Interface - " + qgetenv("USERNAME");
|
||||
const QString& applicationName = getInterfaceSharedMemoryName();
|
||||
|
||||
bool instanceMightBeRunning = true;
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ public:
|
|||
void renderSilent(int16_t* input, float* output, int index, float azimuth, float distance, float gain, int numFrames);
|
||||
|
||||
//
|
||||
// HRTF local gain adjustment
|
||||
// HRTF local gain adjustment in amplitude (1.0 == unity)
|
||||
//
|
||||
void setGainAdjustment(float gain) { _gainAdjust = HRTF_GAIN * gain; };
|
||||
|
||||
|
|
|
@ -1047,7 +1047,7 @@ bool AvatarData::processAvatarIdentity(const Identity& identity) {
|
|||
}
|
||||
|
||||
if (identity.displayName != _displayName) {
|
||||
setDisplayName(identity.displayName);
|
||||
_displayName = identity.displayName;
|
||||
hasIdentityChanged = true;
|
||||
}
|
||||
maybeUpdateSessionDisplayNameFromTransport(identity.sessionDisplayName);
|
||||
|
@ -1094,6 +1094,9 @@ void AvatarData::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
|||
|
||||
void AvatarData::setDisplayName(const QString& displayName) {
|
||||
_displayName = displayName;
|
||||
_sessionDisplayName = "";
|
||||
|
||||
sendIdentityPacket();
|
||||
|
||||
qCDebug(avatars) << "Changing display name for avatar to" << displayName;
|
||||
}
|
||||
|
|
|
@ -646,6 +646,12 @@ bool RenderableModelEntityItem::isReadyToComputeShape() {
|
|||
// the model is still being downloaded.
|
||||
return false;
|
||||
} else if (type >= SHAPE_TYPE_SIMPLE_HULL && type <= SHAPE_TYPE_STATIC_MESH) {
|
||||
if (!_model) {
|
||||
EntityTreePointer tree = getTree();
|
||||
if (tree) {
|
||||
QMetaObject::invokeMethod(tree.get(), "callLoader", Qt::QueuedConnection, Q_ARG(EntityItemID, getID()));
|
||||
}
|
||||
}
|
||||
return (_model && _model->isLoaded());
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -972,7 +972,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
|
|||
static const QVariant EMISSIVE = QByteArray("Emissive");
|
||||
static const QVariant AMBIENT_FACTOR = QByteArray("AmbientFactor");
|
||||
static const QVariant SHININESS = QByteArray("Shininess");
|
||||
static const QVariant OPACITY = QByteArray("Shininess");
|
||||
static const QVariant OPACITY = QByteArray("Opacity");
|
||||
static const QVariant MAYA_USE_NORMAL_MAP = QByteArray("Maya|use_normal_map");
|
||||
static const QVariant MAYA_BASE_COLOR = QByteArray("Maya|base_color");
|
||||
static const QVariant MAYA_USE_COLOR_MAP = QByteArray("Maya|use_color_map");
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "AccountManager.h"
|
||||
#include "AddressManager.h"
|
||||
#include "Assignment.h"
|
||||
#include "AudioHelpers.h"
|
||||
#include "HifiSockAddr.h"
|
||||
#include "FingerprintUtils.h"
|
||||
|
||||
|
@ -846,6 +847,16 @@ void NodeList::ignoreNodeBySessionID(const QUuid& nodeID, bool ignoreEnabled) {
|
|||
}
|
||||
}
|
||||
|
||||
void NodeList::removeFromIgnoreMuteSets(const QUuid& nodeID) {
|
||||
// don't remove yourself, or nobody
|
||||
if (!nodeID.isNull() && _sessionUUID != nodeID) {
|
||||
QWriteLocker ignoredSetLocker{ &_ignoredSetLock };
|
||||
QWriteLocker personalMutedSetLocker{ &_personalMutedSetLock };
|
||||
_ignoredNodeIDs.unsafe_erase(nodeID);
|
||||
_personalMutedNodeIDs.unsafe_erase(nodeID);
|
||||
}
|
||||
}
|
||||
|
||||
bool NodeList::isIgnoringNode(const QUuid& nodeID) const {
|
||||
QReadLocker ignoredSetLocker{ &_ignoredSetLock };
|
||||
return _ignoredNodeIDs.find(nodeID) != _ignoredNodeIDs.cend();
|
||||
|
@ -951,6 +962,30 @@ void NodeList::maybeSendIgnoreSetToNode(SharedNodePointer newNode) {
|
|||
}
|
||||
}
|
||||
|
||||
void NodeList::setAvatarGain(const QUuid& nodeID, float gain) {
|
||||
// cannot set gain of yourself or nobody
|
||||
if (!nodeID.isNull() && _sessionUUID != nodeID) {
|
||||
auto audioMixer = soloNodeOfType(NodeType::AudioMixer);
|
||||
if (audioMixer) {
|
||||
// setup the packet
|
||||
auto setAvatarGainPacket = NLPacket::create(PacketType::PerAvatarGainSet, NUM_BYTES_RFC4122_UUID + sizeof(float), true);
|
||||
|
||||
// write the node ID to the packet
|
||||
setAvatarGainPacket->write(nodeID.toRfc4122());
|
||||
// We need to convert the gain in dB (from the script) to an amplitude before packing it.
|
||||
setAvatarGainPacket->writePrimitive(packFloatGainToByte(fastExp2f(gain / 6.0206f)));
|
||||
|
||||
qCDebug(networking) << "Sending Set Avatar Gain packet UUID: " << uuidStringWithoutCurlyBraces(nodeID) << "Gain:" << gain;
|
||||
|
||||
sendPacket(std::move(setAvatarGainPacket), *audioMixer);
|
||||
} else {
|
||||
qWarning() << "Couldn't find audio mixer to send set gain request";
|
||||
}
|
||||
} else {
|
||||
qWarning() << "NodeList::setAvatarGain called with an invalid ID or an ID which matches the current session ID:" << nodeID;
|
||||
}
|
||||
}
|
||||
|
||||
void NodeList::kickNodeBySessionID(const QUuid& nodeID) {
|
||||
// send a request to domain-server to kick the node with the given session ID
|
||||
// the domain-server will handle the persistence of the kick (via username or IP)
|
||||
|
|
|
@ -82,6 +82,7 @@ public:
|
|||
bool isIgnoringNode(const QUuid& nodeID) const;
|
||||
void personalMuteNodeBySessionID(const QUuid& nodeID, bool muteEnabled);
|
||||
bool isPersonalMutingNode(const QUuid& nodeID) const;
|
||||
void setAvatarGain(const QUuid& nodeID, float gain);
|
||||
|
||||
void kickNodeBySessionID(const QUuid& nodeID);
|
||||
void muteNodeBySessionID(const QUuid& nodeID);
|
||||
|
@ -89,6 +90,8 @@ public:
|
|||
bool getRequestsDomainListData() { return _requestsDomainListData; }
|
||||
void setRequestsDomainListData(bool isRequesting);
|
||||
|
||||
void removeFromIgnoreMuteSets(const QUuid& nodeID);
|
||||
|
||||
public slots:
|
||||
void reset();
|
||||
void sendDomainServerCheckIn();
|
||||
|
|
|
@ -106,7 +106,8 @@ public:
|
|||
ViewFrustum,
|
||||
RequestsDomainListData,
|
||||
ExitingSpaceBubble,
|
||||
LAST_PACKET_TYPE = ExitingSpaceBubble
|
||||
PerAvatarGainSet,
|
||||
LAST_PACKET_TYPE = PerAvatarGainSet
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -210,7 +211,8 @@ enum class AvatarMixerPacketVersion : PacketVersion {
|
|||
HandControllerJoints,
|
||||
HasKillAvatarReason,
|
||||
SessionDisplayName,
|
||||
Unignore
|
||||
Unignore,
|
||||
ImmediateSessionDisplayNameUpdates
|
||||
};
|
||||
|
||||
enum class DomainConnectRequestVersion : PacketVersion {
|
||||
|
|
|
@ -359,8 +359,11 @@ void ModelMeshPartPayload::notifyLocationChanged() {
|
|||
|
||||
}
|
||||
|
||||
void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transform, const QVector<glm::mat4>& clusterMatrices) {
|
||||
void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transform,
|
||||
const QVector<glm::mat4>& clusterMatrices,
|
||||
const QVector<glm::mat4>& cauterizedClusterMatrices) {
|
||||
_transform = transform;
|
||||
_cauterizedTransform = transform;
|
||||
|
||||
if (clusterMatrices.size() > 0) {
|
||||
_worldBound = AABox();
|
||||
|
@ -373,6 +376,11 @@ void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transf
|
|||
_worldBound.transform(transform);
|
||||
if (clusterMatrices.size() == 1) {
|
||||
_transform = _transform.worldTransform(Transform(clusterMatrices[0]));
|
||||
if (cauterizedClusterMatrices.size() != 0) {
|
||||
_cauterizedTransform = _cauterizedTransform.worldTransform(Transform(cauterizedClusterMatrices[0]));
|
||||
} else {
|
||||
_cauterizedTransform = _transform;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -527,9 +535,14 @@ void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline:
|
|||
} else {
|
||||
batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, state.clusterBuffer);
|
||||
}
|
||||
batch.setModelTransform(_transform);
|
||||
} else {
|
||||
if (canCauterize && _model->getCauterizeBones()) {
|
||||
batch.setModelTransform(_cauterizedTransform);
|
||||
} else {
|
||||
batch.setModelTransform(_transform);
|
||||
}
|
||||
}
|
||||
|
||||
batch.setModelTransform(_transform);
|
||||
}
|
||||
|
||||
void ModelMeshPartPayload::startFade() {
|
||||
|
|
|
@ -85,7 +85,9 @@ public:
|
|||
typedef Payload::DataPointer Pointer;
|
||||
|
||||
void notifyLocationChanged() override;
|
||||
void updateTransformForSkinnedMesh(const Transform& transform, const QVector<glm::mat4>& clusterMatrices);
|
||||
void updateTransformForSkinnedMesh(const Transform& transform,
|
||||
const QVector<glm::mat4>& clusterMatrices,
|
||||
const QVector<glm::mat4>& cauterizedClusterMatrices);
|
||||
|
||||
// Entity fade in
|
||||
void startFade();
|
||||
|
@ -106,6 +108,7 @@ public:
|
|||
|
||||
Model* _model;
|
||||
|
||||
Transform _cauterizedTransform;
|
||||
int _meshIndex;
|
||||
int _shapeID;
|
||||
|
||||
|
|
|
@ -238,7 +238,7 @@ void Model::updateRenderItems() {
|
|||
|
||||
// update the model transform and bounding box for this render item.
|
||||
const Model::MeshState& state = data._model->_meshStates.at(data._meshIndex);
|
||||
data.updateTransformForSkinnedMesh(modelTransform, state.clusterMatrices);
|
||||
data.updateTransformForSkinnedMesh(modelTransform, state.clusterMatrices, state.cauterizedClusterMatrices);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -42,6 +42,11 @@ bool UsersScriptingInterface::getPersonalMuteStatus(const QUuid& nodeID) {
|
|||
return DependencyManager::get<NodeList>()->isPersonalMutingNode(nodeID);
|
||||
}
|
||||
|
||||
void UsersScriptingInterface::setAvatarGain(const QUuid& nodeID, float gain) {
|
||||
// ask the NodeList to set the gain of the specified avatar
|
||||
DependencyManager::get<NodeList>()->setAvatarGain(nodeID, gain);
|
||||
}
|
||||
|
||||
void UsersScriptingInterface::kick(const QUuid& nodeID) {
|
||||
// ask the NodeList to kick the user with the given session ID
|
||||
DependencyManager::get<NodeList>()->kickNodeBySessionID(nodeID);
|
||||
|
|
|
@ -61,6 +61,15 @@ public slots:
|
|||
*/
|
||||
bool getPersonalMuteStatus(const QUuid& nodeID);
|
||||
|
||||
/**jsdoc
|
||||
* Sets an avatar's gain for you and you only.
|
||||
* Units are Decibels (dB)
|
||||
* @function Users.setAvatarGain
|
||||
* @param {nodeID} nodeID The node or session ID of the user whose gain you want to modify.
|
||||
* @param {float} gain The gain of the avatar you'd like to set. Units are dB.
|
||||
*/
|
||||
void setAvatarGain(const QUuid& nodeID, float gain);
|
||||
|
||||
/**jsdoc
|
||||
* Kick another user.
|
||||
* @function Users.kick
|
||||
|
@ -130,6 +139,13 @@ signals:
|
|||
*/
|
||||
void usernameFromIDReply(const QString& nodeID, const QString& username, const QString& machineFingerprint);
|
||||
|
||||
/**jsdoc
|
||||
* Notifies scripts that a user has disconnected from the domain
|
||||
* @function Users.avatar.avatarDisconnected
|
||||
* @param {nodeID} NodeID The session ID of the avatar that has disconnected
|
||||
*/
|
||||
void avatarDisconnected(const QUuid& nodeID);
|
||||
|
||||
private:
|
||||
bool getRequestsDomainListData();
|
||||
void setRequestsDomainListData(bool requests);
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#include <cctype>
|
||||
#include <time.h>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <set>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
|
@ -1022,4 +1024,54 @@ bool getProcessorInfo(ProcessorInfo& info) {
|
|||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const QString& getInterfaceSharedMemoryName() {
|
||||
static const QString applicationName = "High Fidelity Interface - " + qgetenv("USERNAME");
|
||||
return applicationName;
|
||||
}
|
||||
|
||||
const std::vector<uint8_t>& getAvailableCores() {
|
||||
static std::vector<uint8_t> availableCores;
|
||||
#ifdef Q_OS_WIN
|
||||
static std::once_flag once;
|
||||
std::call_once(once, [&] {
|
||||
DWORD_PTR defaultProcessAffinity = 0, defaultSystemAffinity = 0;
|
||||
HANDLE process = GetCurrentProcess();
|
||||
GetProcessAffinityMask(process, &defaultProcessAffinity, &defaultSystemAffinity);
|
||||
for (uint64_t i = 0; i < sizeof(DWORD_PTR) * BITS_IN_BYTE; ++i) {
|
||||
DWORD_PTR coreMask = 1;
|
||||
coreMask <<= i;
|
||||
if (0 != (defaultSystemAffinity & coreMask)) {
|
||||
availableCores.push_back(i);
|
||||
}
|
||||
}
|
||||
});
|
||||
#endif
|
||||
return availableCores;
|
||||
}
|
||||
|
||||
void setMaxCores(uint8_t maxCores) {
|
||||
#ifdef Q_OS_WIN
|
||||
HANDLE process = GetCurrentProcess();
|
||||
auto availableCores = getAvailableCores();
|
||||
if (availableCores.size() <= maxCores) {
|
||||
DWORD_PTR currentProcessAffinity = 0, currentSystemAffinity = 0;
|
||||
GetProcessAffinityMask(process, ¤tProcessAffinity, ¤tSystemAffinity);
|
||||
SetProcessAffinityMask(GetCurrentProcess(), currentSystemAffinity);
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD_PTR newProcessAffinity = 0;
|
||||
while (maxCores) {
|
||||
int index = randIntInRange(0, (int)availableCores.size() - 1);
|
||||
DWORD_PTR coreMask = 1;
|
||||
coreMask <<= availableCores[index];
|
||||
newProcessAffinity |= coreMask;
|
||||
availableCores.erase(availableCores.begin() + index);
|
||||
maxCores--;
|
||||
}
|
||||
SetProcessAffinityMask(process, newProcessAffinity);
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -231,4 +231,8 @@ struct ProcessorInfo {
|
|||
|
||||
bool getProcessorInfo(ProcessorInfo& info);
|
||||
|
||||
const QString& getInterfaceSharedMemoryName();
|
||||
|
||||
void setMaxCores(uint8_t maxCores);
|
||||
|
||||
#endif // hifi_SharedUtil_h
|
||||
|
|
|
@ -13,6 +13,7 @@ namespace hifi { namespace properties {
|
|||
const char* CRASHED = "com.highfidelity.crashed";
|
||||
const char* STEAM = "com.highfidelity.launchedFromSteam";
|
||||
const char* LOGGER = "com.highfidelity.logger";
|
||||
const char* OCULUS_STORE = "com.highfidelity.oculusStore";
|
||||
const char* TEST = "com.highfidelity.test";
|
||||
const char* TRACING = "com.highfidelity.tracing";
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ namespace hifi { namespace properties {
|
|||
extern const char* CRASHED;
|
||||
extern const char* STEAM;
|
||||
extern const char* LOGGER;
|
||||
extern const char* OCULUS_STORE;
|
||||
extern const char* TEST;
|
||||
extern const char* TRACING;
|
||||
|
||||
|
|
|
@ -8,21 +8,31 @@
|
|||
|
||||
if (WIN32)
|
||||
|
||||
# we're using static GLEW, so define GLEW_STATIC
|
||||
add_definitions(-DGLEW_STATIC)
|
||||
# we're using static GLEW, so define GLEW_STATIC
|
||||
add_definitions(-DGLEW_STATIC)
|
||||
|
||||
set(TARGET_NAME oculus)
|
||||
setup_hifi_plugin(Multimedia)
|
||||
link_hifi_libraries(shared gl gpu gpu-gl controllers ui
|
||||
plugins ui-plugins display-plugins input-plugins
|
||||
audio-client networking render-utils)
|
||||
|
||||
include_hifi_library_headers(octree)
|
||||
|
||||
add_dependency_external_projects(LibOVR)
|
||||
find_package(LibOVR REQUIRED)
|
||||
target_include_directories(${TARGET_NAME} PRIVATE ${LIBOVR_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET_NAME} ${LIBOVR_LIBRARIES})
|
||||
target_link_libraries(${TARGET_NAME} Winmm.lib)
|
||||
# if we were passed an Oculus App ID for entitlement checks, send that along
|
||||
if (DEFINED ENV{OCULUS_APP_ID})
|
||||
add_definitions(-DOCULUS_APP_ID="$ENV{OCULUS_APP_ID}")
|
||||
endif ()
|
||||
|
||||
set(TARGET_NAME oculus)
|
||||
setup_hifi_plugin(Multimedia)
|
||||
link_hifi_libraries(
|
||||
shared gl gpu gpu-gl controllers ui
|
||||
plugins ui-plugins display-plugins input-plugins
|
||||
audio-client networking render-utils
|
||||
)
|
||||
include_hifi_library_headers(octree)
|
||||
|
||||
add_dependency_external_projects(LibOVR)
|
||||
find_package(LibOVR REQUIRED)
|
||||
target_include_directories(${TARGET_NAME} PRIVATE ${LIBOVR_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET_NAME} ${LIBOVR_LIBRARIES})
|
||||
target_link_libraries(${TARGET_NAME} Winmm.lib)
|
||||
|
||||
add_dependency_external_projects(LibOVRPlatform)
|
||||
find_package(LibOVRPlatform REQUIRED)
|
||||
target_include_directories(${TARGET_NAME} PRIVATE ${LIBOVRPLATFORM_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET_NAME} ${LIBOVRPLATFORM_LIBRARIES})
|
||||
endif()
|
||||
|
|
|
@ -15,8 +15,12 @@
|
|||
#include <QtCore/QDir>
|
||||
#include <QtCore/QProcessEnvironment>
|
||||
|
||||
#define OVRPL_DISABLED
|
||||
#include <OVR_Platform.h>
|
||||
|
||||
#include <controllers/Input.h>
|
||||
#include <controllers/Pose.h>
|
||||
#include <shared/GlobalAppProperties.h>
|
||||
#include <NumericalConstants.h>
|
||||
|
||||
Q_LOGGING_CATEGORY(displayplugins, "hifi.plugins.display")
|
||||
|
@ -89,6 +93,18 @@ ovrSession acquireOculusSession() {
|
|||
return session;
|
||||
}
|
||||
|
||||
#ifdef OCULUS_APP_ID
|
||||
if (qApp->property(hifi::properties::OCULUS_STORE).toBool()) {
|
||||
if (ovr_PlatformInitializeWindows(OCULUS_APP_ID) != ovrPlatformInitialize_Success) {
|
||||
// we were unable to initialize the platform for entitlement check - fail the check
|
||||
_quitRequested = true;
|
||||
} else {
|
||||
qCDebug(oculus) << "Performing Oculus Platform entitlement check";
|
||||
ovr_Entitlement_GetIsViewerEntitled();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Q_ASSERT(0 == refCount);
|
||||
ovrGraphicsLuid luid;
|
||||
if (!OVR_SUCCESS(ovr_Create(&session, &luid))) {
|
||||
|
@ -127,6 +143,35 @@ void handleOVREvents() {
|
|||
|
||||
_quitRequested = status.ShouldQuit;
|
||||
_reorientRequested = status.ShouldRecenter;
|
||||
|
||||
#ifdef OCULUS_APP_ID
|
||||
|
||||
if (qApp->property(hifi::properties::OCULUS_STORE).toBool()) {
|
||||
// pop messages to see if we got a return for an entitlement check
|
||||
ovrMessageHandle message = ovr_PopMessage();
|
||||
|
||||
while (message) {
|
||||
switch (ovr_Message_GetType(message)) {
|
||||
case ovrMessage_Entitlement_GetIsViewerEntitled: {
|
||||
if (!ovr_Message_IsError(message)) {
|
||||
// this viewer is entitled, no need to flag anything
|
||||
qCDebug(oculus) << "Oculus Platform entitlement check succeeded, proceeding normally";
|
||||
} else {
|
||||
// we failed the entitlement check, set our flag so the app can stop
|
||||
qCDebug(oculus) << "Oculus Platform entitlement check failed, app will now quit" << OCULUS_APP_ID;
|
||||
_quitRequested = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// free the message handle to cleanup and not leak
|
||||
ovr_FreeMessage(message);
|
||||
|
||||
// pop the next message to check, if there is one
|
||||
message = ovr_PopMessage();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool quitRequested() {
|
||||
|
@ -217,4 +262,4 @@ controller::Pose ovrControllerPoseToHandPose(
|
|||
pose.velocity = toGlm(handPose.LinearVelocity);
|
||||
pose.valid = true;
|
||||
return pose;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3082,7 +3082,7 @@ var handleHandMessages = function(channel, message, sender) {
|
|||
|
||||
Messages.messageReceived.connect(handleHandMessages);
|
||||
|
||||
var TARGET_UPDATE_HZ = 50; // 50hz good enough (no change in logic)
|
||||
var TARGET_UPDATE_HZ = 60; // 50hz good enough, but we're using update
|
||||
var BASIC_TIMER_INTERVAL_MS = 1000 / TARGET_UPDATE_HZ;
|
||||
var lastInterval = Date.now();
|
||||
|
||||
|
@ -3095,7 +3095,7 @@ var updateTotalWork = 0;
|
|||
|
||||
var UPDATE_PERFORMANCE_DEBUGGING = false;
|
||||
|
||||
var updateIntervalTimer = Script.setInterval(function(){
|
||||
function updateWrapper(){
|
||||
|
||||
intervalCount++;
|
||||
var thisInterval = Date.now();
|
||||
|
@ -3141,11 +3141,12 @@ var updateIntervalTimer = Script.setInterval(function(){
|
|||
updateTotalWork = 0;
|
||||
}
|
||||
|
||||
}, BASIC_TIMER_INTERVAL_MS);
|
||||
}
|
||||
|
||||
Script.update.connect(updateWrapper);
|
||||
function cleanup() {
|
||||
Menu.removeMenuItem("Developer", "Show Grab Sphere");
|
||||
Script.clearInterval(updateIntervalTimer);
|
||||
Script.update.disconnect(updateWrapper);
|
||||
rightController.cleanup();
|
||||
leftController.cleanup();
|
||||
Controller.disableMapping(MAPPING_NAME);
|
||||
|
|
|
@ -233,6 +233,15 @@ pal.fromQml.connect(function (message) { // messages are {method, params}, like
|
|||
removeOverlays();
|
||||
populateUserList();
|
||||
break;
|
||||
case 'updateGain':
|
||||
data = message.params;
|
||||
Users.setAvatarGain(data['sessionId'], data['gain']);
|
||||
break;
|
||||
case 'displayNameUpdate':
|
||||
if (MyAvatar.displayName != message.params) {
|
||||
MyAvatar.displayName = message.params;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
print('Unrecognized message from Pal.qml:', JSON.stringify(message));
|
||||
}
|
||||
|
@ -448,50 +457,6 @@ triggerMapping.from(Controller.Standard.RTClick).peek().to(makeClickHandler(Cont
|
|||
triggerMapping.from(Controller.Standard.LTClick).peek().to(makeClickHandler(Controller.Standard.LeftHand));
|
||||
triggerPressMapping.from(Controller.Standard.RT).peek().to(makePressHandler(Controller.Standard.RightHand));
|
||||
triggerPressMapping.from(Controller.Standard.LT).peek().to(makePressHandler(Controller.Standard.LeftHand));
|
||||
//
|
||||
// Manage the connection between the button and the window.
|
||||
//
|
||||
var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system");
|
||||
var buttonName = "pal";
|
||||
var button = toolBar.addButton({
|
||||
objectName: buttonName,
|
||||
imageURL: Script.resolvePath("assets/images/tools/people.svg"),
|
||||
visible: true,
|
||||
hoverState: 2,
|
||||
defaultState: 1,
|
||||
buttonState: 1,
|
||||
alpha: 0.9
|
||||
});
|
||||
var isWired = false;
|
||||
function off() {
|
||||
if (isWired) { // It is not ok to disconnect these twice, hence guard.
|
||||
Script.update.disconnect(updateOverlays);
|
||||
Controller.mousePressEvent.disconnect(handleMouseEvent);
|
||||
Controller.mouseMoveEvent.disconnect(handleMouseMoveEvent);
|
||||
isWired = false;
|
||||
}
|
||||
triggerMapping.disable(); // It's ok if we disable twice.
|
||||
triggerPressMapping.disable(); // see above
|
||||
removeOverlays();
|
||||
Users.requestsDomainListData = false;
|
||||
}
|
||||
function onClicked() {
|
||||
if (!pal.visible) {
|
||||
Users.requestsDomainListData = true;
|
||||
populateUserList();
|
||||
pal.raise();
|
||||
isWired = true;
|
||||
Script.update.connect(updateOverlays);
|
||||
Controller.mousePressEvent.connect(handleMouseEvent);
|
||||
Controller.mouseMoveEvent.connect(handleMouseMoveEvent);
|
||||
triggerMapping.enable();
|
||||
triggerPressMapping.enable();
|
||||
} else {
|
||||
off();
|
||||
}
|
||||
pal.setVisible(!pal.visible);
|
||||
}
|
||||
|
||||
//
|
||||
// Message from other scripts, such as edit.js
|
||||
//
|
||||
|
@ -523,6 +488,7 @@ var LOUDNESS_SCALE = 2.8 / 5.0;
|
|||
var LOG2 = Math.log(2.0);
|
||||
var AUDIO_LEVEL_UPDATE_INTERVAL_MS = 100; // 10hz for now (change this and change the AVERAGING_RATIO too)
|
||||
var myData = {}; // we're not includied in ExtendedOverlay.get.
|
||||
var audioInterval;
|
||||
|
||||
function getAudioLevel(id) {
|
||||
// the VU meter should work similarly to the one in AvatarInputs: log scale, exponentially averaged
|
||||
|
@ -555,21 +521,74 @@ function getAudioLevel(id) {
|
|||
return audioLevel;
|
||||
}
|
||||
|
||||
function createAudioInterval() {
|
||||
// we will update the audioLevels periodically
|
||||
// TODO: tune for efficiency - expecially with large numbers of avatars
|
||||
return Script.setInterval(function () {
|
||||
if (pal.visible) {
|
||||
var param = {};
|
||||
AvatarList.getAvatarIdentifiers().forEach(function (id) {
|
||||
var level = getAudioLevel(id);
|
||||
// qml didn't like an object with null/empty string for a key, so...
|
||||
var userId = id || 0;
|
||||
param[userId] = level;
|
||||
});
|
||||
pal.sendToQml({method: 'updateAudioLevel', params: param});
|
||||
}
|
||||
}, AUDIO_LEVEL_UPDATE_INTERVAL_MS);
|
||||
}
|
||||
|
||||
// we will update the audioLevels periodically
|
||||
// TODO: tune for efficiency - expecially with large numbers of avatars
|
||||
Script.setInterval(function () {
|
||||
if (pal.visible) {
|
||||
var param = {};
|
||||
AvatarList.getAvatarIdentifiers().forEach(function (id) {
|
||||
var level = getAudioLevel(id);
|
||||
// qml didn't like an object with null/empty string for a key, so...
|
||||
var userId = id || 0;
|
||||
param[userId] = level;
|
||||
});
|
||||
pal.sendToQml({method: 'updateAudioLevel', params: param});
|
||||
//
|
||||
// Manage the connection between the button and the window.
|
||||
//
|
||||
var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system");
|
||||
var buttonName = "pal";
|
||||
var button = toolBar.addButton({
|
||||
objectName: buttonName,
|
||||
imageURL: Script.resolvePath("assets/images/tools/people.svg"),
|
||||
visible: true,
|
||||
hoverState: 2,
|
||||
defaultState: 1,
|
||||
buttonState: 1,
|
||||
alpha: 0.9
|
||||
});
|
||||
var isWired = false;
|
||||
function off() {
|
||||
if (isWired) { // It is not ok to disconnect these twice, hence guard.
|
||||
Script.update.disconnect(updateOverlays);
|
||||
Controller.mousePressEvent.disconnect(handleMouseEvent);
|
||||
Controller.mouseMoveEvent.disconnect(handleMouseMoveEvent);
|
||||
isWired = false;
|
||||
}
|
||||
}, AUDIO_LEVEL_UPDATE_INTERVAL_MS);
|
||||
triggerMapping.disable(); // It's ok if we disable twice.
|
||||
triggerPressMapping.disable(); // see above
|
||||
removeOverlays();
|
||||
Users.requestsDomainListData = false;
|
||||
if (audioInterval) {
|
||||
Script.clearInterval(audioInterval);
|
||||
}
|
||||
}
|
||||
function onClicked() {
|
||||
if (!pal.visible) {
|
||||
Users.requestsDomainListData = true;
|
||||
populateUserList();
|
||||
pal.raise();
|
||||
isWired = true;
|
||||
Script.update.connect(updateOverlays);
|
||||
Controller.mousePressEvent.connect(handleMouseEvent);
|
||||
Controller.mouseMoveEvent.connect(handleMouseMoveEvent);
|
||||
triggerMapping.enable();
|
||||
triggerPressMapping.enable();
|
||||
createAudioInterval();
|
||||
} else {
|
||||
off();
|
||||
}
|
||||
pal.setVisible(!pal.visible);
|
||||
}
|
||||
function avatarDisconnected(nodeID) {
|
||||
// remove from the pal list
|
||||
pal.sendToQml({method: 'avatarDisconnected', params: [nodeID]});
|
||||
}
|
||||
//
|
||||
// Button state.
|
||||
//
|
||||
|
@ -582,14 +601,16 @@ button.clicked.connect(onClicked);
|
|||
pal.visibleChanged.connect(onVisibleChanged);
|
||||
pal.closed.connect(off);
|
||||
Users.usernameFromIDReply.connect(usernameFromIDReply);
|
||||
function clearIgnoredInQMLAndClosePAL() {
|
||||
pal.sendToQml({ method: 'clearIgnored' });
|
||||
Users.avatarDisconnected.connect(avatarDisconnected);
|
||||
|
||||
function clearLocalQMLDataAndClosePAL() {
|
||||
pal.sendToQml({ method: 'clearLocalQMLData' });
|
||||
if (pal.visible) {
|
||||
onClicked(); // Close the PAL
|
||||
}
|
||||
}
|
||||
Window.domainChanged.connect(clearIgnoredInQMLAndClosePAL);
|
||||
Window.domainConnectionRefused.connect(clearIgnoredInQMLAndClosePAL);
|
||||
Window.domainChanged.connect(clearLocalQMLDataAndClosePAL);
|
||||
Window.domainConnectionRefused.connect(clearLocalQMLDataAndClosePAL);
|
||||
|
||||
//
|
||||
// Cleanup.
|
||||
|
@ -600,10 +621,11 @@ Script.scriptEnding.connect(function () {
|
|||
pal.visibleChanged.disconnect(onVisibleChanged);
|
||||
pal.closed.disconnect(off);
|
||||
Users.usernameFromIDReply.disconnect(usernameFromIDReply);
|
||||
Window.domainChanged.disconnect(clearIgnoredInQMLAndClosePAL);
|
||||
Window.domainConnectionRefused.disconnect(clearIgnoredInQMLAndClosePAL);
|
||||
Window.domainChanged.disconnect(clearLocalQMLDataAndClosePAL);
|
||||
Window.domainConnectionRefused.disconnect(clearLocalQMLDataAndClosePAL);
|
||||
Messages.unsubscribe(CHANNEL);
|
||||
Messages.messageReceived.disconnect(receiveMessage);
|
||||
Users.avatarDisconnected.disconnect(avatarDisconnected);
|
||||
off();
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in a new issue