mirror of
https://github.com/lubosz/overte.git
synced 2025-08-07 18:41:10 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into tuneAvatarInfo
This commit is contained in:
commit
ef7b9bfde0
37 changed files with 602 additions and 118 deletions
|
@ -68,6 +68,7 @@ AudioMixer::AudioMixer(ReceivedMessage& message) :
|
||||||
packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket");
|
packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket");
|
||||||
packetReceiver.registerListener(PacketType::NodeMuteRequest, this, "handleNodeMuteRequestPacket");
|
packetReceiver.registerListener(PacketType::NodeMuteRequest, this, "handleNodeMuteRequestPacket");
|
||||||
packetReceiver.registerListener(PacketType::RadiusIgnoreRequest, this, "handleRadiusIgnoreRequestPacket");
|
packetReceiver.registerListener(PacketType::RadiusIgnoreRequest, this, "handleRadiusIgnoreRequestPacket");
|
||||||
|
packetReceiver.registerListener(PacketType::RequestsDomainListData, this, "handleRequestsDomainListDataPacket");
|
||||||
|
|
||||||
connect(nodeList.data(), &NodeList::nodeKilled, this, &AudioMixer::handleNodeKilled);
|
connect(nodeList.data(), &NodeList::nodeKilled, this, &AudioMixer::handleNodeKilled);
|
||||||
}
|
}
|
||||||
|
@ -221,6 +222,20 @@ void AudioMixer::handleKillAvatarPacket(QSharedPointer<ReceivedMessage> packet,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AudioMixer::handleRequestsDomainListDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||||
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
nodeList->getOrCreateLinkedData(senderNode);
|
||||||
|
|
||||||
|
if (senderNode->getLinkedData()) {
|
||||||
|
AudioMixerClientData* nodeData = dynamic_cast<AudioMixerClientData*>(senderNode->getLinkedData());
|
||||||
|
if (nodeData != nullptr) {
|
||||||
|
bool isRequesting;
|
||||||
|
message->readPrimitive(&isRequesting);
|
||||||
|
nodeData->setRequestsDomainListData(isRequesting);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void AudioMixer::handleNodeIgnoreRequestPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode) {
|
void AudioMixer::handleNodeIgnoreRequestPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode) {
|
||||||
sendingNode->parseIgnoreRequestMessage(packet);
|
sendingNode->parseIgnoreRequestMessage(packet);
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,7 @@ private slots:
|
||||||
void handleMuteEnvironmentPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
|
void handleMuteEnvironmentPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
|
||||||
void handleNegotiateAudioFormat(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
|
void handleNegotiateAudioFormat(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
|
||||||
void handleNodeKilled(SharedNodePointer killedNode);
|
void handleNodeKilled(SharedNodePointer killedNode);
|
||||||
|
void handleRequestsDomainListDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||||
void handleNodeIgnoreRequestPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
|
void handleNodeIgnoreRequestPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
|
||||||
void handleRadiusIgnoreRequestPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
|
void handleRadiusIgnoreRequestPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
|
||||||
void handleKillAvatarPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
|
void handleKillAvatarPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
|
||||||
|
|
|
@ -92,6 +92,8 @@ public:
|
||||||
glm::vec3 getPosition() { return getAvatarAudioStream() ? getAvatarAudioStream()->getPosition() : glm::vec3(0); }
|
glm::vec3 getPosition() { return getAvatarAudioStream() ? getAvatarAudioStream()->getPosition() : glm::vec3(0); }
|
||||||
glm::vec3 getAvatarBoundingBoxCorner() { return getAvatarAudioStream() ? getAvatarAudioStream()->getAvatarBoundingBoxCorner() : glm::vec3(0); }
|
glm::vec3 getAvatarBoundingBoxCorner() { return getAvatarAudioStream() ? getAvatarAudioStream()->getAvatarBoundingBoxCorner() : glm::vec3(0); }
|
||||||
glm::vec3 getAvatarBoundingBoxScale() { return getAvatarAudioStream() ? getAvatarAudioStream()->getAvatarBoundingBoxScale() : glm::vec3(0); }
|
glm::vec3 getAvatarBoundingBoxScale() { return getAvatarAudioStream() ? getAvatarAudioStream()->getAvatarBoundingBoxScale() : glm::vec3(0); }
|
||||||
|
bool getRequestsDomainListData() { return _requestsDomainListData; }
|
||||||
|
void setRequestsDomainListData(bool requesting) { _requestsDomainListData = requesting; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void injectorStreamFinished(const QUuid& streamIdentifier);
|
void injectorStreamFinished(const QUuid& streamIdentifier);
|
||||||
|
@ -122,6 +124,7 @@ private:
|
||||||
bool _shouldFlushEncoder { false };
|
bool _shouldFlushEncoder { false };
|
||||||
|
|
||||||
bool _shouldMuteClient { false };
|
bool _shouldMuteClient { false };
|
||||||
|
bool _requestsDomainListData { false };
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_AudioMixerClientData_h
|
#endif // hifi_AudioMixerClientData_h
|
||||||
|
|
|
@ -209,8 +209,13 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& node) {
|
||||||
// and that it isn't being ignored by our listening node
|
// and that it isn't being ignored by our listening node
|
||||||
// and that it isn't ignoring our listening node
|
// and that it isn't ignoring our listening node
|
||||||
AudioMixerClientData* otherData = static_cast<AudioMixerClientData*>(otherNode->getLinkedData());
|
AudioMixerClientData* otherData = static_cast<AudioMixerClientData*>(otherNode->getLinkedData());
|
||||||
|
|
||||||
|
// When this is true, the AudioMixer will send Audio data to a client about avatars that have ignored them
|
||||||
|
bool getsAnyIgnored = nodeData->getRequestsDomainListData() && node->getCanKick();
|
||||||
|
|
||||||
if (otherData
|
if (otherData
|
||||||
&& !node->isIgnoringNodeWithID(otherNode->getUUID()) && !otherNode->isIgnoringNodeWithID(node->getUUID())) {
|
&& (!node->isIgnoringNodeWithID(otherNode->getUUID()) || (otherData->getRequestsDomainListData() && otherNode->getCanKick()))
|
||||||
|
&& (!otherNode->isIgnoringNodeWithID(node->getUUID()) || getsAnyIgnored)) {
|
||||||
|
|
||||||
// check to see if we're ignoring in radius
|
// check to see if we're ignoring in radius
|
||||||
bool insideIgnoreRadius = false;
|
bool insideIgnoreRadius = false;
|
||||||
|
@ -219,7 +224,7 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& node) {
|
||||||
// We'll always be inside the radius in that case.
|
// We'll always be inside the radius in that case.
|
||||||
insideIgnoreRadius = true;
|
insideIgnoreRadius = true;
|
||||||
// Check to see if the space bubble is enabled
|
// Check to see if the space bubble is enabled
|
||||||
} else if ((node->isIgnoreRadiusEnabled() || otherNode->isIgnoreRadiusEnabled()) && (*otherNode != *node)) {
|
} else if ((node->isIgnoreRadiusEnabled() || otherNode->isIgnoreRadiusEnabled())) {
|
||||||
// Define the minimum bubble size
|
// Define the minimum bubble size
|
||||||
static const glm::vec3 minBubbleSize = glm::vec3(0.3f, 1.3f, 0.3f);
|
static const glm::vec3 minBubbleSize = glm::vec3(0.3f, 1.3f, 0.3f);
|
||||||
AudioMixerClientData* nodeData = reinterpret_cast<AudioMixerClientData*>(node->getLinkedData());
|
AudioMixerClientData* nodeData = reinterpret_cast<AudioMixerClientData*>(node->getLinkedData());
|
||||||
|
|
|
@ -299,7 +299,7 @@ void AvatarMixer::broadcastAvatarData() {
|
||||||
AvatarMixerClientData* otherData = reinterpret_cast<AvatarMixerClientData*>(otherNode->getLinkedData());
|
AvatarMixerClientData* otherData = reinterpret_cast<AvatarMixerClientData*>(otherNode->getLinkedData());
|
||||||
AvatarMixerClientData* nodeData = reinterpret_cast<AvatarMixerClientData*>(node->getLinkedData());
|
AvatarMixerClientData* nodeData = reinterpret_cast<AvatarMixerClientData*>(node->getLinkedData());
|
||||||
// Check to see if the space bubble is enabled
|
// Check to see if the space bubble is enabled
|
||||||
if ((node->isIgnoreRadiusEnabled() && !getsIgnoredByMe) || (otherNode->isIgnoreRadiusEnabled() && !getsAnyIgnored)) {
|
if (node->isIgnoreRadiusEnabled() || otherNode->isIgnoreRadiusEnabled()) {
|
||||||
// Define the minimum bubble size
|
// Define the minimum bubble size
|
||||||
static const glm::vec3 minBubbleSize = glm::vec3(0.3f, 1.3f, 0.3f);
|
static const glm::vec3 minBubbleSize = glm::vec3(0.3f, 1.3f, 0.3f);
|
||||||
// Define the scale of the box for the current node
|
// Define the scale of the box for the current node
|
||||||
|
@ -326,11 +326,11 @@ void AvatarMixer::broadcastAvatarData() {
|
||||||
// Perform the collision check between the two bounding boxes
|
// Perform the collision check between the two bounding boxes
|
||||||
if (nodeBox.touches(otherNodeBox)) {
|
if (nodeBox.touches(otherNodeBox)) {
|
||||||
nodeData->ignoreOther(node, otherNode);
|
nodeData->ignoreOther(node, otherNode);
|
||||||
return false;
|
return getsAnyIgnored;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Not close enough to ignore
|
// Not close enough to ignore
|
||||||
nodeData->removeFromRadiusIgnoringSet(otherNode->getUUID());
|
nodeData->removeFromRadiusIgnoringSet(node, otherNode->getUUID());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -58,6 +58,15 @@ void AvatarMixerClientData::ignoreOther(SharedNodePointer self, SharedNodePointe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AvatarMixerClientData::removeFromRadiusIgnoringSet(SharedNodePointer self, const QUuid& other) {
|
||||||
|
if (isRadiusIgnoring(other)) {
|
||||||
|
_radiusIgnoredOthers.erase(other);
|
||||||
|
auto exitingSpaceBubblePacket = NLPacket::create(PacketType::ExitingSpaceBubble, NUM_BYTES_RFC4122_UUID);
|
||||||
|
exitingSpaceBubblePacket->write(other.toRfc4122());
|
||||||
|
DependencyManager::get<NodeList>()->sendUnreliablePacket(*exitingSpaceBubblePacket, *self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void AvatarMixerClientData::readViewFrustumPacket(const QByteArray& message) {
|
void AvatarMixerClientData::readViewFrustumPacket(const QByteArray& message) {
|
||||||
_currentViewFrustum.fromByteArray(message);
|
_currentViewFrustum.fromByteArray(message);
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,7 +89,7 @@ public:
|
||||||
glm::vec3 getGlobalBoundingBoxCorner() { return _avatar ? _avatar->getGlobalBoundingBoxCorner() : glm::vec3(0); }
|
glm::vec3 getGlobalBoundingBoxCorner() { return _avatar ? _avatar->getGlobalBoundingBoxCorner() : glm::vec3(0); }
|
||||||
bool isRadiusIgnoring(const QUuid& other) { return _radiusIgnoredOthers.find(other) != _radiusIgnoredOthers.end(); }
|
bool isRadiusIgnoring(const QUuid& other) { return _radiusIgnoredOthers.find(other) != _radiusIgnoredOthers.end(); }
|
||||||
void addToRadiusIgnoringSet(const QUuid& other) { _radiusIgnoredOthers.insert(other); }
|
void addToRadiusIgnoringSet(const QUuid& other) { _radiusIgnoredOthers.insert(other); }
|
||||||
void removeFromRadiusIgnoringSet(const QUuid& other) { _radiusIgnoredOthers.erase(other); }
|
void removeFromRadiusIgnoringSet(SharedNodePointer self, const QUuid& other);
|
||||||
void ignoreOther(SharedNodePointer self, SharedNodePointer other);
|
void ignoreOther(SharedNodePointer self, SharedNodePointer other);
|
||||||
|
|
||||||
void readViewFrustumPacket(const QByteArray& message);
|
void readViewFrustumPacket(const QByteArray& message);
|
||||||
|
|
Binary file not shown.
|
@ -83,6 +83,18 @@ Item {
|
||||||
text: "Missed Frame Count: " + root.appdropped;
|
text: "Missed Frame Count: " + root.appdropped;
|
||||||
visible: root.appdropped > 0;
|
visible: root.appdropped > 0;
|
||||||
}
|
}
|
||||||
|
StatText {
|
||||||
|
text: "Long Render Count: " + root.longrenders;
|
||||||
|
visible: root.longrenders > 0;
|
||||||
|
}
|
||||||
|
StatText {
|
||||||
|
text: "Long Submit Count: " + root.longsubmits;
|
||||||
|
visible: root.longsubmits > 0;
|
||||||
|
}
|
||||||
|
StatText {
|
||||||
|
text: "Long Frame Count: " + root.longframes;
|
||||||
|
visible: root.longframes > 0;
|
||||||
|
}
|
||||||
StatText {
|
StatText {
|
||||||
text: "Packets In/Out: " + root.packetInCount + "/" + root.packetOutCount
|
text: "Packets In/Out: " + root.packetInCount + "/" + root.packetOutCount
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,7 +76,7 @@ OriginalDesktop.Desktop {
|
||||||
WebEngine.settings.localContentCanAccessRemoteUrls = true;
|
WebEngine.settings.localContentCanAccessRemoteUrls = true;
|
||||||
|
|
||||||
[ // Allocate the standard buttons in the correct order. They will get images, etc., via scripts.
|
[ // Allocate the standard buttons in the correct order. They will get images, etc., via scripts.
|
||||||
"hmdToggle", "mute", "mod", "bubble", "help",
|
"hmdToggle", "mute", "pal", "bubble", "help",
|
||||||
"hudToggle",
|
"hudToggle",
|
||||||
"com.highfidelity.interface.system.editButton", "marketplace", "snapshot", "goto"
|
"com.highfidelity.interface.system.editButton", "marketplace", "snapshot", "goto"
|
||||||
].forEach(function (name) {
|
].forEach(function (name) {
|
||||||
|
|
52
interface/resources/qml/hifi/LetterboxMessage.qml
Normal file
52
interface/resources/qml/hifi/LetterboxMessage.qml
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
//
|
||||||
|
// LetterboxMessage.qml
|
||||||
|
// qml/hifi
|
||||||
|
//
|
||||||
|
// Created by Zach Fox and Howard Stearns on 1/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
|
||||||
|
//
|
||||||
|
|
||||||
|
import QtQuick 2.5
|
||||||
|
import QtQuick.Controls 1.4
|
||||||
|
import "../styles-uit"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
property alias text: popupText.text
|
||||||
|
property real radius: hifi.dimensions.borderRadius
|
||||||
|
visible: false
|
||||||
|
id: letterbox
|
||||||
|
anchors.fill: parent
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: "black"
|
||||||
|
opacity: 0.5
|
||||||
|
radius: radius
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
width: Math.max(parent.width * 0.75, 400)
|
||||||
|
height: popupText.contentHeight*1.5
|
||||||
|
anchors.centerIn: parent
|
||||||
|
radius: radius
|
||||||
|
color: "white"
|
||||||
|
FiraSansSemiBold {
|
||||||
|
id: popupText
|
||||||
|
size: hifi.fontSizes.textFieldInput
|
||||||
|
color: hifi.colors.darkGray
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: 15
|
||||||
|
anchors.rightMargin: 15
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
acceptedButtons: Qt.LeftButton
|
||||||
|
onClicked: {
|
||||||
|
letterbox.visible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,21 +11,6 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
/* TODO:
|
|
||||||
|
|
||||||
prototype:
|
|
||||||
- only show kick/mute when canKick
|
|
||||||
- margins everywhere
|
|
||||||
- column head centering
|
|
||||||
- column head font
|
|
||||||
- proper button .svg on toolbar
|
|
||||||
|
|
||||||
mvp:
|
|
||||||
- Show all participants, including ignored, and populate initial ignore/mute status.
|
|
||||||
- If name is elided, hover should scroll name left so the full name can be read.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
import QtQuick 2.5
|
import QtQuick 2.5
|
||||||
import QtQuick.Controls 1.4
|
import QtQuick.Controls 1.4
|
||||||
import "../styles-uit"
|
import "../styles-uit"
|
||||||
|
@ -104,6 +89,7 @@ Item {
|
||||||
border.width: 2
|
border.width: 2
|
||||||
// "ADMIN" text
|
// "ADMIN" text
|
||||||
RalewaySemiBold {
|
RalewaySemiBold {
|
||||||
|
id: adminTabText
|
||||||
text: "ADMIN"
|
text: "ADMIN"
|
||||||
// Text size
|
// Text size
|
||||||
size: hifi.fontSizes.tableHeading + 2
|
size: hifi.fontSizes.tableHeading + 2
|
||||||
|
@ -325,7 +311,12 @@ Item {
|
||||||
visible: iAmAdmin
|
visible: iAmAdmin
|
||||||
color: hifi.colors.lightGrayText
|
color: hifi.colors.lightGrayText
|
||||||
}
|
}
|
||||||
// This Rectangle refers to the [?] popup button
|
function letterbox(message) {
|
||||||
|
letterboxMessage.text = message;
|
||||||
|
letterboxMessage.visible = true
|
||||||
|
|
||||||
|
}
|
||||||
|
// This Rectangle refers to the [?] popup button next to "NAMES"
|
||||||
Rectangle {
|
Rectangle {
|
||||||
color: hifi.colors.tableBackgroundLight
|
color: hifi.colors.tableBackgroundLight
|
||||||
width: 20
|
width: 20
|
||||||
|
@ -348,50 +339,46 @@ Item {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
acceptedButtons: Qt.LeftButton
|
acceptedButtons: Qt.LeftButton
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
onClicked: namesPopup.visible = true
|
onClicked: letterbox("Bold names in the list are Avatar Display Names.\n" +
|
||||||
|
"If a Display Name isn't set, a unique Session Display Name is assigned." +
|
||||||
|
"\n\nAdministrators of this domain can also see the Username or Machine ID associated with each avatar present.")
|
||||||
onEntered: helpText.color = hifi.colors.baseGrayHighlight
|
onEntered: helpText.color = hifi.colors.baseGrayHighlight
|
||||||
onExited: helpText.color = hifi.colors.darkGray
|
onExited: helpText.color = hifi.colors.darkGray
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Explanitory popup upon clicking "[?]"
|
// This Rectangle refers to the [?] popup button next to "ADMIN"
|
||||||
Item {
|
Rectangle {
|
||||||
visible: false
|
visible: iAmAdmin
|
||||||
id: namesPopup
|
color: adminTab.color
|
||||||
anchors.fill: pal
|
width: 20
|
||||||
Rectangle {
|
height: 28
|
||||||
|
anchors.right: adminTab.right
|
||||||
|
anchors.rightMargin: 31
|
||||||
|
anchors.top: adminTab.top
|
||||||
|
anchors.topMargin: 2
|
||||||
|
RalewayRegular {
|
||||||
|
id: adminHelpText
|
||||||
|
text: "[?]"
|
||||||
|
size: hifi.fontSizes.tableHeading + 2
|
||||||
|
font.capitalization: Font.AllUppercase
|
||||||
|
color: hifi.colors.redHighlight
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: "black"
|
|
||||||
opacity: 0.5
|
|
||||||
radius: hifi.dimensions.borderRadius
|
|
||||||
}
|
|
||||||
Rectangle {
|
|
||||||
width: Math.max(parent.width * 0.75, 400)
|
|
||||||
height: popupText.contentHeight*1.5
|
|
||||||
anchors.centerIn: parent
|
|
||||||
radius: hifi.dimensions.borderRadius
|
|
||||||
color: "white"
|
|
||||||
FiraSansSemiBold {
|
|
||||||
id: popupText
|
|
||||||
text: "Bold names in the list are Avatar Display Names.\n" +
|
|
||||||
"If a Display Name isn't set, a unique Session Display Name is assigned." +
|
|
||||||
"\n\nAdministrators of this domain can also see the Username or Machine ID associated with each avatar present."
|
|
||||||
size: hifi.fontSizes.textFieldInput
|
|
||||||
color: hifi.colors.darkGray
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.leftMargin: 15
|
|
||||||
anchors.rightMargin: 15
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
acceptedButtons: Qt.LeftButton
|
acceptedButtons: Qt.LeftButton
|
||||||
onClicked: {
|
hoverEnabled: true
|
||||||
namesPopup.visible = false
|
onClicked: letterbox('Silencing a user mutes their microphone. Silenced users can unmute themselves by clicking the "UNMUTE" button on their HUD.\n\n' +
|
||||||
}
|
"Banning a user will remove them from this domain and prevent them from returning. You can un-ban users from your domain's settings page.)")
|
||||||
|
onEntered: adminHelpText.color = "#94132e"
|
||||||
|
onExited: adminHelpText.color = hifi.colors.redHighlight
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
LetterboxMessage {
|
||||||
|
id: letterboxMessage
|
||||||
|
}
|
||||||
|
|
||||||
function findSessionIndex(sessionId, optionalData) { // no findIndex in .qml
|
function findSessionIndex(sessionId, optionalData) { // no findIndex in .qml
|
||||||
var data = optionalData || userModelData, length = data.length;
|
var data = optionalData || userModelData, length = data.length;
|
||||||
|
@ -427,14 +414,20 @@ Item {
|
||||||
sortModel();
|
sortModel();
|
||||||
break;
|
break;
|
||||||
case 'select':
|
case 'select':
|
||||||
var sessionId = message.params[0];
|
var sessionIds = message.params[0];
|
||||||
var selected = message.params[1];
|
var selected = message.params[1];
|
||||||
var userIndex = findSessionIndex(sessionId);
|
var userIndex = findSessionIndex(sessionIds[0]);
|
||||||
if (selected) {
|
if (sessionIds.length > 1) {
|
||||||
table.selection.clear(); // for now, no multi-select
|
letterbox('Only one user can be selected at a time.');
|
||||||
table.selection.select(userIndex);
|
} else if (userIndex < 0) {
|
||||||
|
letterbox('The last editor is not among this list of users.');
|
||||||
} else {
|
} else {
|
||||||
table.selection.deselect(userIndex);
|
if (selected) {
|
||||||
|
table.selection.clear(); // for now, no multi-select
|
||||||
|
table.selection.select(userIndex);
|
||||||
|
} else {
|
||||||
|
table.selection.deselect(userIndex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
// Received an "updateUsername()" request from the JS
|
// Received an "updateUsername()" request from the JS
|
||||||
|
|
|
@ -95,7 +95,7 @@ Item {
|
||||||
readonly property color tableRowDarkEven: "#1c1c1c" // Equivalent to "#a6181818" over #404040 background
|
readonly property color tableRowDarkEven: "#1c1c1c" // Equivalent to "#a6181818" over #404040 background
|
||||||
readonly property color tableBackgroundLight: tableRowLightEven
|
readonly property color tableBackgroundLight: tableRowLightEven
|
||||||
readonly property color tableBackgroundDark: tableRowDarkEven
|
readonly property color tableBackgroundDark: tableRowDarkEven
|
||||||
readonly property color tableScrollHandleLight: tableRowLightOdd
|
readonly property color tableScrollHandleLight: "#8F8F8F"
|
||||||
readonly property color tableScrollHandleDark: "#707070"
|
readonly property color tableScrollHandleDark: "#707070"
|
||||||
readonly property color tableScrollBackgroundLight: tableRowLightEven
|
readonly property color tableScrollBackgroundLight: tableRowLightEven
|
||||||
readonly property color tableScrollBackgroundDark: "#323232"
|
readonly property color tableScrollBackgroundDark: "#323232"
|
||||||
|
|
|
@ -6145,7 +6145,7 @@ void Application::loadScriptURLDialog() const {
|
||||||
|
|
||||||
void Application::toggleLogDialog() {
|
void Application::toggleLogDialog() {
|
||||||
if (! _logDialog) {
|
if (! _logDialog) {
|
||||||
_logDialog = new LogDialog(_glWidget, getLogger());
|
_logDialog = new LogDialog(nullptr, getLogger());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_logDialog->isVisible()) {
|
if (_logDialog->isVisible()) {
|
||||||
|
|
|
@ -1339,7 +1339,10 @@ void Avatar::addToScene(AvatarSharedPointer myHandle) {
|
||||||
render::ScenePointer scene = qApp->getMain3DScene();
|
render::ScenePointer scene = qApp->getMain3DScene();
|
||||||
if (scene) {
|
if (scene) {
|
||||||
render::PendingChanges pendingChanges;
|
render::PendingChanges pendingChanges;
|
||||||
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderAvatars() && !DependencyManager::get<NodeList>()->isIgnoringNode(getSessionUUID())) {
|
auto nodelist = DependencyManager::get<NodeList>();
|
||||||
|
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderAvatars()
|
||||||
|
&& !nodelist->isIgnoringNode(getSessionUUID())
|
||||||
|
&& !nodelist->isRadiusIgnoringNode(getSessionUUID())) {
|
||||||
addToScene(myHandle, scene, pendingChanges);
|
addToScene(myHandle, scene, pendingChanges);
|
||||||
}
|
}
|
||||||
scene->enqueuePendingChanges(pendingChanges);
|
scene->enqueuePendingChanges(pendingChanges);
|
||||||
|
|
|
@ -79,6 +79,7 @@ AvatarManager::AvatarManager(QObject* parent) :
|
||||||
packetReceiver.registerListener(PacketType::BulkAvatarData, this, "processAvatarDataPacket");
|
packetReceiver.registerListener(PacketType::BulkAvatarData, this, "processAvatarDataPacket");
|
||||||
packetReceiver.registerListener(PacketType::KillAvatar, this, "processKillAvatar");
|
packetReceiver.registerListener(PacketType::KillAvatar, this, "processKillAvatar");
|
||||||
packetReceiver.registerListener(PacketType::AvatarIdentity, this, "processAvatarIdentityPacket");
|
packetReceiver.registerListener(PacketType::AvatarIdentity, this, "processAvatarIdentityPacket");
|
||||||
|
packetReceiver.registerListener(PacketType::ExitingSpaceBubble, this, "processExitingSpaceBubble");
|
||||||
|
|
||||||
// when we hear that the user has ignored an avatar by session UUID
|
// when we hear that the user has ignored an avatar by session UUID
|
||||||
// immediately remove that avatar instead of waiting for the absence of packets from avatar mixer
|
// immediately remove that avatar instead of waiting for the absence of packets from avatar mixer
|
||||||
|
@ -257,6 +258,9 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar
|
||||||
if (removalReason == KillAvatarReason::TheirAvatarEnteredYourBubble) {
|
if (removalReason == KillAvatarReason::TheirAvatarEnteredYourBubble) {
|
||||||
emit DependencyManager::get<UsersScriptingInterface>()->enteredIgnoreRadius();
|
emit DependencyManager::get<UsersScriptingInterface>()->enteredIgnoreRadius();
|
||||||
}
|
}
|
||||||
|
if (removalReason == KillAvatarReason::TheirAvatarEnteredYourBubble || removalReason == YourAvatarEnteredTheirBubble) {
|
||||||
|
DependencyManager::get<NodeList>()->radiusIgnoreNodeBySessionID(avatar->getSessionUUID(), true);
|
||||||
|
}
|
||||||
_avatarFades.push_back(removedAvatar);
|
_avatarFades.push_back(removedAvatar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ const QString HIGHLIGHT_COLOR = "#3366CC";
|
||||||
int qTextCursorMeta = qRegisterMetaType<QTextCursor>("QTextCursor");
|
int qTextCursorMeta = qRegisterMetaType<QTextCursor>("QTextCursor");
|
||||||
int qTextBlockMeta = qRegisterMetaType<QTextBlock>("QTextBlock");
|
int qTextBlockMeta = qRegisterMetaType<QTextBlock>("QTextBlock");
|
||||||
|
|
||||||
LogDialog::LogDialog(QWidget* parent, AbstractLoggerInterface* logger) : QDialog(parent, Qt::Dialog) {
|
LogDialog::LogDialog(QWidget* parent, AbstractLoggerInterface* logger) : QDialog(parent, Qt::Window) {
|
||||||
|
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
setWindowTitle("Log");
|
setWindowTitle("Log");
|
||||||
|
|
|
@ -127,12 +127,18 @@ void Stats::updateStats(bool force) {
|
||||||
auto displayPlugin = qApp->getActiveDisplayPlugin();
|
auto displayPlugin = qApp->getActiveDisplayPlugin();
|
||||||
auto stats = displayPlugin->getHardwareStats();
|
auto stats = displayPlugin->getHardwareStats();
|
||||||
STAT_UPDATE(appdropped, stats["app_dropped_frame_count"].toInt());
|
STAT_UPDATE(appdropped, stats["app_dropped_frame_count"].toInt());
|
||||||
|
STAT_UPDATE(longrenders, stats["long_render_count"].toInt());
|
||||||
|
STAT_UPDATE(longsubmits, stats["long_submit_count"].toInt());
|
||||||
|
STAT_UPDATE(longframes, stats["long_frame_count"].toInt());
|
||||||
STAT_UPDATE(renderrate, displayPlugin->renderRate());
|
STAT_UPDATE(renderrate, displayPlugin->renderRate());
|
||||||
STAT_UPDATE(presentrate, displayPlugin->presentRate());
|
STAT_UPDATE(presentrate, displayPlugin->presentRate());
|
||||||
STAT_UPDATE(presentnewrate, displayPlugin->newFramePresentRate());
|
STAT_UPDATE(presentnewrate, displayPlugin->newFramePresentRate());
|
||||||
STAT_UPDATE(presentdroprate, displayPlugin->droppedFrameRate());
|
STAT_UPDATE(presentdroprate, displayPlugin->droppedFrameRate());
|
||||||
STAT_UPDATE(stutterrate, displayPlugin->stutterRate());
|
STAT_UPDATE(stutterrate, displayPlugin->stutterRate());
|
||||||
} else {
|
} else {
|
||||||
|
STAT_UPDATE(appdropped, -1);
|
||||||
|
STAT_UPDATE(longrenders, -1);
|
||||||
|
STAT_UPDATE(longsubmits, -1);
|
||||||
STAT_UPDATE(presentrate, -1);
|
STAT_UPDATE(presentrate, -1);
|
||||||
STAT_UPDATE(presentnewrate, -1);
|
STAT_UPDATE(presentnewrate, -1);
|
||||||
STAT_UPDATE(presentdroprate, -1);
|
STAT_UPDATE(presentdroprate, -1);
|
||||||
|
|
|
@ -40,6 +40,9 @@ class Stats : public QQuickItem {
|
||||||
STATS_PROPERTY(float, stutterrate, 0)
|
STATS_PROPERTY(float, stutterrate, 0)
|
||||||
|
|
||||||
STATS_PROPERTY(int, appdropped, 0)
|
STATS_PROPERTY(int, appdropped, 0)
|
||||||
|
STATS_PROPERTY(int, longsubmits, 0)
|
||||||
|
STATS_PROPERTY(int, longrenders, 0)
|
||||||
|
STATS_PROPERTY(int, longframes, 0)
|
||||||
|
|
||||||
STATS_PROPERTY(float, presentnewrate, 0)
|
STATS_PROPERTY(float, presentnewrate, 0)
|
||||||
STATS_PROPERTY(float, presentdroprate, 0)
|
STATS_PROPERTY(float, presentdroprate, 0)
|
||||||
|
@ -137,6 +140,9 @@ public slots:
|
||||||
void forceUpdateStats() { updateStats(true); }
|
void forceUpdateStats() { updateStats(true); }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
void longsubmitsChanged();
|
||||||
|
void longrendersChanged();
|
||||||
|
void longframesChanged();
|
||||||
void appdroppedChanged();
|
void appdroppedChanged();
|
||||||
void framerateChanged();
|
void framerateChanged();
|
||||||
void expandedChanged();
|
void expandedChanged();
|
||||||
|
|
|
@ -161,6 +161,13 @@ void AvatarHashMap::processKillAvatar(QSharedPointer<ReceivedMessage> message, S
|
||||||
removeAvatar(sessionUUID, reason);
|
removeAvatar(sessionUUID, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AvatarHashMap::processExitingSpaceBubble(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
|
||||||
|
// read the node id
|
||||||
|
QUuid sessionUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
||||||
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
nodeList->radiusIgnoreNodeBySessionID(sessionUUID, false);
|
||||||
|
}
|
||||||
|
|
||||||
void AvatarHashMap::removeAvatar(const QUuid& sessionUUID, KillAvatarReason removalReason) {
|
void AvatarHashMap::removeAvatar(const QUuid& sessionUUID, KillAvatarReason removalReason) {
|
||||||
QWriteLocker locker(&_hashLock);
|
QWriteLocker locker(&_hashLock);
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,7 @@ private slots:
|
||||||
void processAvatarDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
|
void processAvatarDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
|
||||||
void processAvatarIdentityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
|
void processAvatarIdentityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
|
||||||
void processKillAvatar(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
|
void processKillAvatar(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
|
||||||
|
void processExitingSpaceBubble(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
AvatarHashMap();
|
AvatarHashMap();
|
||||||
|
|
|
@ -165,6 +165,7 @@ public:
|
||||||
|
|
||||||
if (newPlugin) {
|
if (newPlugin) {
|
||||||
bool hasVsync = true;
|
bool hasVsync = true;
|
||||||
|
QThread::setPriority(newPlugin->getPresentPriority());
|
||||||
bool wantVsync = newPlugin->wantVsync();
|
bool wantVsync = newPlugin->wantVsync();
|
||||||
_context->makeCurrent();
|
_context->makeCurrent();
|
||||||
#if defined(Q_OS_WIN)
|
#if defined(Q_OS_WIN)
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
|
||||||
|
#include <QtCore/QThread>
|
||||||
#include <QtCore/QTimer>
|
#include <QtCore/QTimer>
|
||||||
#include <QtGui/QImage>
|
#include <QtGui/QImage>
|
||||||
|
|
||||||
|
@ -80,6 +81,7 @@ protected:
|
||||||
|
|
||||||
void updateCompositeFramebuffer();
|
void updateCompositeFramebuffer();
|
||||||
|
|
||||||
|
virtual QThread::Priority getPresentPriority() { return QThread::HighPriority; }
|
||||||
virtual void compositeLayers();
|
virtual void compositeLayers();
|
||||||
virtual void compositeScene();
|
virtual void compositeScene();
|
||||||
virtual void compositeOverlay();
|
virtual void compositeOverlay();
|
||||||
|
|
|
@ -238,6 +238,10 @@ void NodeList::reset() {
|
||||||
|
|
||||||
_numNoReplyDomainCheckIns = 0;
|
_numNoReplyDomainCheckIns = 0;
|
||||||
|
|
||||||
|
// lock and clear our set of radius ignored IDs
|
||||||
|
_radiusIgnoredSetLock.lockForWrite();
|
||||||
|
_radiusIgnoredNodeIDs.clear();
|
||||||
|
_radiusIgnoredSetLock.unlock();
|
||||||
// lock and clear our set of ignored IDs
|
// lock and clear our set of ignored IDs
|
||||||
_ignoredSetLock.lockForWrite();
|
_ignoredSetLock.lockForWrite();
|
||||||
_ignoredNodeIDs.clear();
|
_ignoredNodeIDs.clear();
|
||||||
|
@ -781,6 +785,22 @@ void NodeList::sendIgnoreRadiusStateToNode(const SharedNodePointer& destinationN
|
||||||
sendPacket(std::move(ignorePacket), *destinationNode);
|
sendPacket(std::move(ignorePacket), *destinationNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NodeList::radiusIgnoreNodeBySessionID(const QUuid& nodeID, bool radiusIgnoreEnabled) {
|
||||||
|
if (radiusIgnoreEnabled) {
|
||||||
|
QReadLocker radiusIgnoredSetLocker{ &_radiusIgnoredSetLock }; // read lock for insert
|
||||||
|
// add this nodeID to our set of ignored IDs
|
||||||
|
_radiusIgnoredNodeIDs.insert(nodeID);
|
||||||
|
} else {
|
||||||
|
QWriteLocker radiusIgnoredSetLocker{ &_radiusIgnoredSetLock }; // write lock for unsafe_erase
|
||||||
|
_radiusIgnoredNodeIDs.unsafe_erase(nodeID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NodeList::isRadiusIgnoringNode(const QUuid& nodeID) const {
|
||||||
|
QReadLocker radiusIgnoredSetLocker{ &_radiusIgnoredSetLock }; // read lock for reading
|
||||||
|
return _radiusIgnoredNodeIDs.find(nodeID) != _radiusIgnoredNodeIDs.cend();
|
||||||
|
}
|
||||||
|
|
||||||
void NodeList::ignoreNodeBySessionID(const QUuid& nodeID, bool ignoreEnabled) {
|
void NodeList::ignoreNodeBySessionID(const QUuid& nodeID, bool ignoreEnabled) {
|
||||||
// enumerate the nodes to send a reliable ignore packet to each that can leverage it
|
// enumerate the nodes to send a reliable ignore packet to each that can leverage it
|
||||||
if (!nodeID.isNull() && _sessionUUID != nodeID) {
|
if (!nodeID.isNull() && _sessionUUID != nodeID) {
|
||||||
|
@ -1020,12 +1040,12 @@ void NodeList::processUsernameFromIDReply(QSharedPointer<ReceivedMessage> messag
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeList::setRequestsDomainListData(bool isRequesting) {
|
void NodeList::setRequestsDomainListData(bool isRequesting) {
|
||||||
// Tell the avatar mixer whether I want to receive any additional data to which I might be entitled
|
// Tell the avatar mixer and audio mixer whether I want to receive any additional data to which I might be entitled
|
||||||
if (_requestsDomainListData == isRequesting) {
|
if (_requestsDomainListData == isRequesting) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
eachMatchingNode([](const SharedNodePointer& node)->bool {
|
eachMatchingNode([](const SharedNodePointer& node)->bool {
|
||||||
return node->getType() == NodeType::AvatarMixer;
|
return (node->getType() == NodeType::AudioMixer || node->getType() == NodeType::AvatarMixer);
|
||||||
}, [this, isRequesting](const SharedNodePointer& destinationNode) {
|
}, [this, isRequesting](const SharedNodePointer& destinationNode) {
|
||||||
auto packet = NLPacket::create(PacketType::RequestsDomainListData, sizeof(bool), true); // reliable
|
auto packet = NLPacket::create(PacketType::RequestsDomainListData, sizeof(bool), true); // reliable
|
||||||
packet->writePrimitive(isRequesting);
|
packet->writePrimitive(isRequesting);
|
||||||
|
|
|
@ -76,6 +76,8 @@ public:
|
||||||
void toggleIgnoreRadius() { ignoreNodesInRadius(!getIgnoreRadiusEnabled()); }
|
void toggleIgnoreRadius() { ignoreNodesInRadius(!getIgnoreRadiusEnabled()); }
|
||||||
void enableIgnoreRadius() { ignoreNodesInRadius(true); }
|
void enableIgnoreRadius() { ignoreNodesInRadius(true); }
|
||||||
void disableIgnoreRadius() { ignoreNodesInRadius(false); }
|
void disableIgnoreRadius() { ignoreNodesInRadius(false); }
|
||||||
|
void radiusIgnoreNodeBySessionID(const QUuid& nodeID, bool radiusIgnoreEnabled);
|
||||||
|
bool isRadiusIgnoringNode(const QUuid& other) const;
|
||||||
void ignoreNodeBySessionID(const QUuid& nodeID, bool ignoreEnabled);
|
void ignoreNodeBySessionID(const QUuid& nodeID, bool ignoreEnabled);
|
||||||
bool isIgnoringNode(const QUuid& nodeID) const;
|
bool isIgnoringNode(const QUuid& nodeID) const;
|
||||||
void personalMuteNodeBySessionID(const QUuid& nodeID, bool muteEnabled);
|
void personalMuteNodeBySessionID(const QUuid& nodeID, bool muteEnabled);
|
||||||
|
@ -159,6 +161,8 @@ private:
|
||||||
QTimer _keepAlivePingTimer;
|
QTimer _keepAlivePingTimer;
|
||||||
bool _requestsDomainListData;
|
bool _requestsDomainListData;
|
||||||
|
|
||||||
|
mutable QReadWriteLock _radiusIgnoredSetLock;
|
||||||
|
tbb::concurrent_unordered_set<QUuid, UUIDHasher> _radiusIgnoredNodeIDs;
|
||||||
mutable QReadWriteLock _ignoredSetLock;
|
mutable QReadWriteLock _ignoredSetLock;
|
||||||
tbb::concurrent_unordered_set<QUuid, UUIDHasher> _ignoredNodeIDs;
|
tbb::concurrent_unordered_set<QUuid, UUIDHasher> _ignoredNodeIDs;
|
||||||
mutable QReadWriteLock _personalMutedSetLock;
|
mutable QReadWriteLock _personalMutedSetLock;
|
||||||
|
|
|
@ -105,7 +105,8 @@ public:
|
||||||
UsernameFromIDReply,
|
UsernameFromIDReply,
|
||||||
ViewFrustum,
|
ViewFrustum,
|
||||||
RequestsDomainListData,
|
RequestsDomainListData,
|
||||||
LAST_PACKET_TYPE = RequestsDomainListData
|
ExitingSpaceBubble,
|
||||||
|
LAST_PACKET_TYPE = ExitingSpaceBubble
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ public slots:
|
||||||
* @param {nodeID} nodeID The node or session ID of the user you want to ignore.
|
* @param {nodeID} nodeID The node or session ID of the user you want to ignore.
|
||||||
* @param {bool} enable True for ignored; false for un-ignored.
|
* @param {bool} enable True for ignored; false for un-ignored.
|
||||||
*/
|
*/
|
||||||
void ignore(const QUuid& nodeID, bool ignoreEnabled);
|
void ignore(const QUuid& nodeID, bool ignoreEnabled = true);
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* Gets a bool containing whether you have ignored the given Avatar UUID.
|
* Gets a bool containing whether you have ignored the given Avatar UUID.
|
||||||
|
@ -52,7 +52,7 @@ public slots:
|
||||||
* @param {nodeID} nodeID The node or session ID of the user you want to mute.
|
* @param {nodeID} nodeID The node or session ID of the user you want to mute.
|
||||||
* @param {bool} enable True for enabled; false for disabled.
|
* @param {bool} enable True for enabled; false for disabled.
|
||||||
*/
|
*/
|
||||||
void personalMute(const QUuid& nodeID, bool muteEnabled);
|
void personalMute(const QUuid& nodeID, bool muteEnabled = true);
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* Requests a bool containing whether you have personally muted the given Avatar UUID.
|
* Requests a bool containing whether you have personally muted the given Avatar UUID.
|
||||||
|
|
|
@ -31,6 +31,10 @@ OculusDisplayPlugin::OculusDisplayPlugin() {
|
||||||
|
|
||||||
bool OculusDisplayPlugin::internalActivate() {
|
bool OculusDisplayPlugin::internalActivate() {
|
||||||
bool result = Parent::internalActivate();
|
bool result = Parent::internalActivate();
|
||||||
|
_longSubmits = 0;
|
||||||
|
_longRenders = 0;
|
||||||
|
_longFrames = 0;
|
||||||
|
|
||||||
currentDebugMode = ovrPerfHud_Off;
|
currentDebugMode = ovrPerfHud_Off;
|
||||||
if (result && _session) {
|
if (result && _session) {
|
||||||
ovr_SetInt(_session, OVR_PERF_HUD_MODE, currentDebugMode);
|
ovr_SetInt(_session, OVR_PERF_HUD_MODE, currentDebugMode);
|
||||||
|
@ -112,35 +116,43 @@ void OculusDisplayPlugin::uncustomizeContext() {
|
||||||
Parent::uncustomizeContext();
|
Parent::uncustomizeContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const uint64_t FRAME_BUDGET = (11 * USECS_PER_MSEC);
|
||||||
|
static const uint64_t FRAME_OVER_BUDGET = (15 * USECS_PER_MSEC);
|
||||||
|
|
||||||
void OculusDisplayPlugin::hmdPresent() {
|
void OculusDisplayPlugin::hmdPresent() {
|
||||||
|
static uint64_t lastSubmitEnd = 0;
|
||||||
|
|
||||||
if (!_customized) {
|
if (!_customized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
PROFILE_RANGE_EX(render, __FUNCTION__, 0xff00ff00, (uint64_t)_currentFrame->frameIndex)
|
PROFILE_RANGE_EX(render, __FUNCTION__, 0xff00ff00, (uint64_t)_currentFrame->frameIndex)
|
||||||
|
|
||||||
int curIndex;
|
{
|
||||||
ovr_GetTextureSwapChainCurrentIndex(_session, _textureSwapChain, &curIndex);
|
PROFILE_RANGE_EX(render, "Oculus Blit", 0xff00ff00, (uint64_t)_currentFrame->frameIndex)
|
||||||
GLuint curTexId;
|
int curIndex;
|
||||||
ovr_GetTextureSwapChainBufferGL(_session, _textureSwapChain, curIndex, &curTexId);
|
ovr_GetTextureSwapChainCurrentIndex(_session, _textureSwapChain, &curIndex);
|
||||||
|
GLuint curTexId;
|
||||||
|
ovr_GetTextureSwapChainBufferGL(_session, _textureSwapChain, curIndex, &curTexId);
|
||||||
|
|
||||||
// Manually bind the texture to the FBO
|
// Manually bind the texture to the FBO
|
||||||
// FIXME we should have a way of wrapping raw GL ids in GPU objects without
|
// FIXME we should have a way of wrapping raw GL ids in GPU objects without
|
||||||
// taking ownership of the object
|
// taking ownership of the object
|
||||||
auto fbo = getGLBackend()->getFramebufferID(_outputFramebuffer);
|
auto fbo = getGLBackend()->getFramebufferID(_outputFramebuffer);
|
||||||
glNamedFramebufferTexture(fbo, GL_COLOR_ATTACHMENT0, curTexId, 0);
|
glNamedFramebufferTexture(fbo, GL_COLOR_ATTACHMENT0, curTexId, 0);
|
||||||
render([&](gpu::Batch& batch) {
|
render([&](gpu::Batch& batch) {
|
||||||
batch.enableStereo(false);
|
batch.enableStereo(false);
|
||||||
batch.setFramebuffer(_outputFramebuffer);
|
batch.setFramebuffer(_outputFramebuffer);
|
||||||
batch.setViewportTransform(ivec4(uvec2(), _outputFramebuffer->getSize()));
|
batch.setViewportTransform(ivec4(uvec2(), _outputFramebuffer->getSize()));
|
||||||
batch.setStateScissorRect(ivec4(uvec2(), _outputFramebuffer->getSize()));
|
batch.setStateScissorRect(ivec4(uvec2(), _outputFramebuffer->getSize()));
|
||||||
batch.resetViewTransform();
|
batch.resetViewTransform();
|
||||||
batch.setProjectionTransform(mat4());
|
batch.setProjectionTransform(mat4());
|
||||||
batch.setPipeline(_presentPipeline);
|
batch.setPipeline(_presentPipeline);
|
||||||
batch.setResourceTexture(0, _compositeFramebuffer->getRenderBuffer(0));
|
batch.setResourceTexture(0, _compositeFramebuffer->getRenderBuffer(0));
|
||||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||||
});
|
});
|
||||||
glNamedFramebufferTexture(fbo, GL_COLOR_ATTACHMENT0, 0, 0);
|
glNamedFramebufferTexture(fbo, GL_COLOR_ATTACHMENT0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
auto result = ovr_CommitTextureSwapChain(_session, _textureSwapChain);
|
auto result = ovr_CommitTextureSwapChain(_session, _textureSwapChain);
|
||||||
|
@ -148,8 +160,33 @@ void OculusDisplayPlugin::hmdPresent() {
|
||||||
_sceneLayer.SensorSampleTime = _currentPresentFrameInfo.sensorSampleTime;
|
_sceneLayer.SensorSampleTime = _currentPresentFrameInfo.sensorSampleTime;
|
||||||
_sceneLayer.RenderPose[ovrEyeType::ovrEye_Left] = ovrPoseFromGlm(_currentPresentFrameInfo.renderPose);
|
_sceneLayer.RenderPose[ovrEyeType::ovrEye_Left] = ovrPoseFromGlm(_currentPresentFrameInfo.renderPose);
|
||||||
_sceneLayer.RenderPose[ovrEyeType::ovrEye_Right] = ovrPoseFromGlm(_currentPresentFrameInfo.renderPose);
|
_sceneLayer.RenderPose[ovrEyeType::ovrEye_Right] = ovrPoseFromGlm(_currentPresentFrameInfo.renderPose);
|
||||||
|
|
||||||
|
auto submitStart = usecTimestampNow();
|
||||||
|
uint64_t nonSubmitInterval = 0;
|
||||||
|
if (lastSubmitEnd != 0) {
|
||||||
|
nonSubmitInterval = submitStart - lastSubmitEnd;
|
||||||
|
if (nonSubmitInterval > FRAME_BUDGET) {
|
||||||
|
++_longRenders;
|
||||||
|
}
|
||||||
|
}
|
||||||
ovrLayerHeader* layers = &_sceneLayer.Header;
|
ovrLayerHeader* layers = &_sceneLayer.Header;
|
||||||
result = ovr_SubmitFrame(_session, _currentFrame->frameIndex, &_viewScaleDesc, &layers, 1);
|
{
|
||||||
|
PROFILE_RANGE_EX(render, "Oculus Submit", 0xff00ff00, (uint64_t)_currentFrame->frameIndex)
|
||||||
|
result = ovr_SubmitFrame(_session, _currentFrame->frameIndex, &_viewScaleDesc, &layers, 1);
|
||||||
|
}
|
||||||
|
lastSubmitEnd = usecTimestampNow();
|
||||||
|
if (nonSubmitInterval != 0) {
|
||||||
|
auto submitInterval = lastSubmitEnd - submitStart;
|
||||||
|
if (nonSubmitInterval < FRAME_BUDGET && submitInterval > FRAME_BUDGET) {
|
||||||
|
++_longSubmits;
|
||||||
|
}
|
||||||
|
if ((nonSubmitInterval + submitInterval) > FRAME_OVER_BUDGET) {
|
||||||
|
++_longFrames;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (!OVR_SUCCESS(result)) {
|
if (!OVR_SUCCESS(result)) {
|
||||||
logWarning("Failed to present");
|
logWarning("Failed to present");
|
||||||
}
|
}
|
||||||
|
@ -168,6 +205,7 @@ void OculusDisplayPlugin::hmdPresent() {
|
||||||
_appDroppedFrames.store(appDroppedFrames);
|
_appDroppedFrames.store(appDroppedFrames);
|
||||||
_compositorDroppedFrames.store(compositorDroppedFrames);
|
_compositorDroppedFrames.store(compositorDroppedFrames);
|
||||||
}
|
}
|
||||||
|
|
||||||
_presentRate.increment();
|
_presentRate.increment();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,6 +214,9 @@ QJsonObject OculusDisplayPlugin::getHardwareStats() const {
|
||||||
QJsonObject hardwareStats;
|
QJsonObject hardwareStats;
|
||||||
hardwareStats["app_dropped_frame_count"] = _appDroppedFrames.load();
|
hardwareStats["app_dropped_frame_count"] = _appDroppedFrames.load();
|
||||||
hardwareStats["compositor_dropped_frame_count"] = _compositorDroppedFrames.load();
|
hardwareStats["compositor_dropped_frame_count"] = _compositorDroppedFrames.load();
|
||||||
|
hardwareStats["long_render_count"] = _longRenders.load();
|
||||||
|
hardwareStats["long_submit_count"] = _longSubmits.load();
|
||||||
|
hardwareStats["long_frame_count"] = _longFrames.load();
|
||||||
return hardwareStats;
|
return hardwareStats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,8 @@ public:
|
||||||
virtual QJsonObject getHardwareStats() const;
|
virtual QJsonObject getHardwareStats() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
QThread::Priority getPresentPriority() override { return QThread::TimeCriticalPriority; }
|
||||||
|
|
||||||
bool internalActivate() override;
|
bool internalActivate() override;
|
||||||
void hmdPresent() override;
|
void hmdPresent() override;
|
||||||
bool isHmdMounted() const override;
|
bool isHmdMounted() const override;
|
||||||
|
@ -39,5 +41,8 @@ private:
|
||||||
|
|
||||||
std::atomic_int _compositorDroppedFrames;
|
std::atomic_int _compositorDroppedFrames;
|
||||||
std::atomic_int _appDroppedFrames;
|
std::atomic_int _appDroppedFrames;
|
||||||
|
std::atomic_int _longSubmits;
|
||||||
|
std::atomic_int _longRenders;
|
||||||
|
std::atomic_int _longFrames;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -18,9 +18,9 @@ var DEFAULT_SCRIPTS = [
|
||||||
"system/mute.js",
|
"system/mute.js",
|
||||||
"system/goto.js",
|
"system/goto.js",
|
||||||
"system/hmd.js",
|
"system/hmd.js",
|
||||||
"system/marketplaces/marketplace.js",
|
"system/marketplaces/marketplaces.js",
|
||||||
"system/edit.js",
|
"system/edit.js",
|
||||||
"system/mod.js",
|
"system/pal.js", //"system/mod.js", // older UX, if you prefer
|
||||||
"system/selectAudioDevice.js",
|
"system/selectAudioDevice.js",
|
||||||
"system/notifications.js",
|
"system/notifications.js",
|
||||||
"system/controllers/controllerDisplayManager.js",
|
"system/controllers/controllerDisplayManager.js",
|
||||||
|
|
BIN
scripts/system/assets/models/Avatar-Overlay-v1.fbx
Normal file
BIN
scripts/system/assets/models/Avatar-Overlay-v1.fbx
Normal file
Binary file not shown.
|
@ -20,6 +20,9 @@
|
||||||
// When partially squeezing over a HUD element, a laser or the reticle is shown where the active hand
|
// When partially squeezing over a HUD element, a laser or the reticle is shown where the active hand
|
||||||
// controller beam intersects the HUD.
|
// controller beam intersects the HUD.
|
||||||
|
|
||||||
|
var systemLaserOn = false;
|
||||||
|
|
||||||
|
|
||||||
Script.include("../libraries/controllers.js");
|
Script.include("../libraries/controllers.js");
|
||||||
|
|
||||||
// UTILITIES -------------
|
// UTILITIES -------------
|
||||||
|
@ -121,6 +124,12 @@ function ignoreMouseActivity() {
|
||||||
if (!Reticle.allowMouseCapture) {
|
if (!Reticle.allowMouseCapture) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if the lasers are on, then reticle/mouse should be hidden and we can ignore it for seeking or depth updating
|
||||||
|
if (systemLaserOn) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
var pos = Reticle.position;
|
var pos = Reticle.position;
|
||||||
if (!pos || (pos.x == -1 && pos.y == -1)) {
|
if (!pos || (pos.x == -1 && pos.y == -1)) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -261,6 +270,12 @@ var ONE_MINUS_WEIGHTING = 1 - WEIGHTING;
|
||||||
var AVERAGE_MOUSE_VELOCITY_FOR_SEEK_TO = 20;
|
var AVERAGE_MOUSE_VELOCITY_FOR_SEEK_TO = 20;
|
||||||
function isShakingMouse() { // True if the person is waving the mouse around trying to find it.
|
function isShakingMouse() { // True if the person is waving the mouse around trying to find it.
|
||||||
var now = Date.now(), mouse = Reticle.position, isShaking = false;
|
var now = Date.now(), mouse = Reticle.position, isShaking = false;
|
||||||
|
|
||||||
|
// if the lasers are on, then we ignore mouse shaking
|
||||||
|
if (systemLaserOn) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (lastIntegration && (lastIntegration !== now)) {
|
if (lastIntegration && (lastIntegration !== now)) {
|
||||||
var velocity = Vec3.length(Vec3.subtract(mouse, lastMouse)) / (now - lastIntegration);
|
var velocity = Vec3.length(Vec3.subtract(mouse, lastMouse)) / (now - lastIntegration);
|
||||||
averageMouseVelocity = (ONE_MINUS_WEIGHTING * averageMouseVelocity) + (WEIGHTING * velocity);
|
averageMouseVelocity = (ONE_MINUS_WEIGHTING * averageMouseVelocity) + (WEIGHTING * velocity);
|
||||||
|
@ -275,9 +290,14 @@ function isShakingMouse() { // True if the person is waving the mouse around try
|
||||||
var NON_LINEAR_DIVISOR = 2;
|
var NON_LINEAR_DIVISOR = 2;
|
||||||
var MINIMUM_SEEK_DISTANCE = 0.1;
|
var MINIMUM_SEEK_DISTANCE = 0.1;
|
||||||
function updateSeeking(doNotStartSeeking) {
|
function updateSeeking(doNotStartSeeking) {
|
||||||
|
// if the lasers are on, then we never do seeking
|
||||||
|
if (systemLaserOn) {
|
||||||
|
isSeeking = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!doNotStartSeeking && (!Reticle.visible || isShakingMouse())) {
|
if (!doNotStartSeeking && (!Reticle.visible || isShakingMouse())) {
|
||||||
if (!isSeeking) {
|
if (!isSeeking) {
|
||||||
print('Start seeking mouse.');
|
|
||||||
isSeeking = true;
|
isSeeking = true;
|
||||||
}
|
}
|
||||||
} // e.g., if we're about to turn it on with first movement.
|
} // e.g., if we're about to turn it on with first movement.
|
||||||
|
@ -287,7 +307,6 @@ function updateSeeking(doNotStartSeeking) {
|
||||||
averageMouseVelocity = lastIntegration = 0;
|
averageMouseVelocity = lastIntegration = 0;
|
||||||
var lookAt2D = HMD.getHUDLookAtPosition2D();
|
var lookAt2D = HMD.getHUDLookAtPosition2D();
|
||||||
if (!lookAt2D) { // If this happens, something has gone terribly wrong.
|
if (!lookAt2D) { // If this happens, something has gone terribly wrong.
|
||||||
print('Cannot seek without lookAt position');
|
|
||||||
isSeeking = false;
|
isSeeking = false;
|
||||||
return; // E.g., if parallel to location in HUD
|
return; // E.g., if parallel to location in HUD
|
||||||
}
|
}
|
||||||
|
@ -303,7 +322,6 @@ function updateSeeking(doNotStartSeeking) {
|
||||||
}
|
}
|
||||||
var okX = !updateDimension('x'), okY = !updateDimension('y'); // Evaluate both. Don't short-circuit.
|
var okX = !updateDimension('x'), okY = !updateDimension('y'); // Evaluate both. Don't short-circuit.
|
||||||
if (okX && okY) {
|
if (okX && okY) {
|
||||||
print('Finished seeking mouse');
|
|
||||||
isSeeking = false;
|
isSeeking = false;
|
||||||
} else {
|
} else {
|
||||||
Reticle.setPosition(copy); // Not setReticlePosition
|
Reticle.setPosition(copy); // Not setReticlePosition
|
||||||
|
@ -322,7 +340,7 @@ function updateMouseActivity(isClick) {
|
||||||
return;
|
return;
|
||||||
} // Bug: mouse clicks should keep going. Just not hand controller clicks
|
} // Bug: mouse clicks should keep going. Just not hand controller clicks
|
||||||
handControllerLockOut.update(now);
|
handControllerLockOut.update(now);
|
||||||
Reticle.visible = true;
|
Reticle.visible = !systemLaserOn;
|
||||||
}
|
}
|
||||||
function expireMouseCursor(now) {
|
function expireMouseCursor(now) {
|
||||||
if (!isPointingAtOverlay() && mouseCursorActivity.expired(now)) {
|
if (!isPointingAtOverlay() && mouseCursorActivity.expired(now)) {
|
||||||
|
@ -474,7 +492,6 @@ var LASER_ALPHA = 0.5;
|
||||||
var LASER_SEARCH_COLOR_XYZW = {x: 10 / 255, y: 10 / 255, z: 255 / 255, w: LASER_ALPHA};
|
var LASER_SEARCH_COLOR_XYZW = {x: 10 / 255, y: 10 / 255, z: 255 / 255, w: LASER_ALPHA};
|
||||||
var LASER_TRIGGER_COLOR_XYZW = {x: 250 / 255, y: 10 / 255, z: 10 / 255, w: LASER_ALPHA};
|
var LASER_TRIGGER_COLOR_XYZW = {x: 250 / 255, y: 10 / 255, z: 10 / 255, w: LASER_ALPHA};
|
||||||
var SYSTEM_LASER_DIRECTION = {x: 0, y: 0, z: -1};
|
var SYSTEM_LASER_DIRECTION = {x: 0, y: 0, z: -1};
|
||||||
var systemLaserOn = false;
|
|
||||||
function clearSystemLaser() {
|
function clearSystemLaser() {
|
||||||
if (!systemLaserOn) {
|
if (!systemLaserOn) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
<input type="button" id="visible" class="glyph" value="" />
|
<input type="button" id="visible" class="glyph" value="" />
|
||||||
</div>
|
</div>
|
||||||
<input type="button" id="teleport" value="Jump To Selection" />
|
<input type="button" id="teleport" value="Jump To Selection" />
|
||||||
|
<input type="button" id="pal" class="glyph" value="" />
|
||||||
<input type="button" class="red" id="delete" value="Delete" />
|
<input type="button" class="red" id="delete" value="Delete" />
|
||||||
</div>
|
</div>
|
||||||
<div id="entity-list">
|
<div id="entity-list">
|
||||||
|
@ -94,4 +95,4 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -39,6 +39,7 @@ function loaded() {
|
||||||
elInView = document.getElementById("in-view")
|
elInView = document.getElementById("in-view")
|
||||||
elRadius = document.getElementById("radius");
|
elRadius = document.getElementById("radius");
|
||||||
elTeleport = document.getElementById("teleport");
|
elTeleport = document.getElementById("teleport");
|
||||||
|
elPal = document.getElementById("pal");
|
||||||
elEntityTable = document.getElementById("entity-table");
|
elEntityTable = document.getElementById("entity-table");
|
||||||
elInfoToggle = document.getElementById("info-toggle");
|
elInfoToggle = document.getElementById("info-toggle");
|
||||||
elInfoToggleGlyph = elInfoToggle.firstChild;
|
elInfoToggleGlyph = elInfoToggle.firstChild;
|
||||||
|
@ -274,6 +275,9 @@ function loaded() {
|
||||||
elTeleport.onclick = function () {
|
elTeleport.onclick = function () {
|
||||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'teleport' }));
|
EventBridge.emitWebEvent(JSON.stringify({ type: 'teleport' }));
|
||||||
}
|
}
|
||||||
|
elPal.onclick = function () {
|
||||||
|
EventBridge.emitWebEvent(JSON.stringify({ type: 'pal' }));
|
||||||
|
}
|
||||||
elDelete.onclick = function() {
|
elDelete.onclick = function() {
|
||||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' }));
|
EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' }));
|
||||||
refreshEntities();
|
refreshEntities();
|
||||||
|
|
|
@ -98,7 +98,6 @@ EntityListTool = function(opts) {
|
||||||
webView.emitScriptEvent(JSON.stringify(data));
|
webView.emitScriptEvent(JSON.stringify(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
webView.webEventReceived.connect(function(data) {
|
webView.webEventReceived.connect(function(data) {
|
||||||
data = JSON.parse(data);
|
data = JSON.parse(data);
|
||||||
if (data.type == "selectionUpdate") {
|
if (data.type == "selectionUpdate") {
|
||||||
|
@ -120,6 +119,23 @@ EntityListTool = function(opts) {
|
||||||
if (selectionManager.hasSelection()) {
|
if (selectionManager.hasSelection()) {
|
||||||
MyAvatar.position = selectionManager.worldPosition;
|
MyAvatar.position = selectionManager.worldPosition;
|
||||||
}
|
}
|
||||||
|
} else if (data.type == "pal") {
|
||||||
|
var sessionIds = {}; // Collect the sessionsIds of all selected entitities, w/o duplicates.
|
||||||
|
selectionManager.selections.forEach(function (id) {
|
||||||
|
var lastEditedBy = Entities.getEntityProperties(id, 'lastEditedBy').lastEditedBy;
|
||||||
|
if (lastEditedBy) {
|
||||||
|
sessionIds[lastEditedBy] = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var dedupped = Object.keys(sessionIds);
|
||||||
|
if (!selectionManager.selections.length) {
|
||||||
|
Window.alert('No objects selected.');
|
||||||
|
} else if (!dedupped.length) {
|
||||||
|
Window.alert('There were no recent users of the ' + selectionManager.selections.length + ' selected objects.');
|
||||||
|
} else {
|
||||||
|
// No need to subscribe if we're just sending.
|
||||||
|
Messages.sendMessage('com.highfidelity.pal', JSON.stringify({method: 'select', params: [dedupped, true]}), 'local');
|
||||||
|
}
|
||||||
} else if (data.type == "delete") {
|
} else if (data.type == "delete") {
|
||||||
deleteSelectedEntities();
|
deleteSelectedEntities();
|
||||||
} else if (data.type == "toggleLocked") {
|
} else if (data.type == "toggleLocked") {
|
||||||
|
@ -140,4 +156,4 @@ EntityListTool = function(opts) {
|
||||||
});
|
});
|
||||||
|
|
||||||
return that;
|
return that;
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,7 +11,19 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
// FIXME when we make this a defaultScript: (function() { // BEGIN LOCAL_SCOPE
|
// hardcoding these as it appears we cannot traverse the originalTextures in overlays??? Maybe I've missed
|
||||||
|
// something, will revisit as this is sorta horrible.
|
||||||
|
const UNSELECTED_TEXTURES = {"idle-D": Script.resolvePath("./assets/models/Avatar-Overlay-v1.fbx/Avatar-Overlay-v1.fbm/avatar-overlay-idle.png"),
|
||||||
|
"idle-E": Script.resolvePath("./assets/models/Avatar-Overlay-v1.fbx/Avatar-Overlay-v1.fbm/avatar-overlay-idle.png")
|
||||||
|
};
|
||||||
|
const SELECTED_TEXTURES = { "idle-D": Script.resolvePath("./assets/models/Avatar-Overlay-v1.fbx/Avatar-Overlay-v1.fbm/avatar-overlay-selected.png"),
|
||||||
|
"idle-E": Script.resolvePath("./assets/models/Avatar-Overlay-v1.fbx/Avatar-Overlay-v1.fbm/avatar-overlay-selected.png")
|
||||||
|
};
|
||||||
|
|
||||||
|
const UNSELECTED_COLOR = { red: 0x1F, green: 0xC6, blue: 0xA6};
|
||||||
|
const SELECTED_COLOR = {red: 0xf3, green: 0x91, blue: 0x29};
|
||||||
|
|
||||||
|
(function() { // BEGIN LOCAL_SCOPE
|
||||||
|
|
||||||
Script.include("/~/system/libraries/controllers.js");
|
Script.include("/~/system/libraries/controllers.js");
|
||||||
|
|
||||||
|
@ -19,8 +31,19 @@ Script.include("/~/system/libraries/controllers.js");
|
||||||
// Overlays.
|
// Overlays.
|
||||||
//
|
//
|
||||||
var overlays = {}; // Keeps track of all our extended overlay data objects, keyed by target identifier.
|
var overlays = {}; // Keeps track of all our extended overlay data objects, keyed by target identifier.
|
||||||
function ExtendedOverlay(key, type, properties, selected) { // A wrapper around overlays to store the key it is associated with.
|
|
||||||
|
function ExtendedOverlay(key, type, properties, selected, hasModel) { // A wrapper around overlays to store the key it is associated with.
|
||||||
overlays[key] = this;
|
overlays[key] = this;
|
||||||
|
if (hasModel) {
|
||||||
|
var modelKey = key + "-m";
|
||||||
|
this.model = new ExtendedOverlay(modelKey, "model", {
|
||||||
|
url: Script.resolvePath("./assets/models/Avatar-Overlay-v1.fbx"),
|
||||||
|
textures: textures(selected),
|
||||||
|
ignoreRayIntersection: true
|
||||||
|
}, false, false);
|
||||||
|
} else {
|
||||||
|
this.model = undefined;
|
||||||
|
}
|
||||||
this.key = key;
|
this.key = key;
|
||||||
this.selected = selected || false; // not undefined
|
this.selected = selected || false; // not undefined
|
||||||
this.activeOverlay = Overlays.addOverlay(type, properties); // We could use different overlays for (un)selected...
|
this.activeOverlay = Overlays.addOverlay(type, properties); // We could use different overlays for (un)selected...
|
||||||
|
@ -34,14 +57,24 @@ ExtendedOverlay.prototype.deleteOverlay = function () { // remove display and da
|
||||||
ExtendedOverlay.prototype.editOverlay = function (properties) { // change display of this overlay
|
ExtendedOverlay.prototype.editOverlay = function (properties) { // change display of this overlay
|
||||||
Overlays.editOverlay(this.activeOverlay, properties);
|
Overlays.editOverlay(this.activeOverlay, properties);
|
||||||
};
|
};
|
||||||
const UNSELECTED_COLOR = {red: 20, green: 250, blue: 20};
|
|
||||||
const SELECTED_COLOR = {red: 250, green: 20, blue: 20};
|
function color(selected) {
|
||||||
function color(selected) { return selected ? SELECTED_COLOR : UNSELECTED_COLOR; }
|
return selected ? SELECTED_COLOR : UNSELECTED_COLOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
function textures(selected) {
|
||||||
|
return selected ? SELECTED_TEXTURES : UNSELECTED_TEXTURES;
|
||||||
|
}
|
||||||
|
|
||||||
ExtendedOverlay.prototype.select = function (selected) {
|
ExtendedOverlay.prototype.select = function (selected) {
|
||||||
if (this.selected === selected) {
|
if (this.selected === selected) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.editOverlay({color: color(selected)});
|
this.editOverlay({color: color(selected)});
|
||||||
|
if (this.model) {
|
||||||
|
this.model.editOverlay({textures: textures(selected)});
|
||||||
|
}
|
||||||
this.selected = selected;
|
this.selected = selected;
|
||||||
};
|
};
|
||||||
// Class methods:
|
// Class methods:
|
||||||
|
@ -91,7 +124,7 @@ function HighlightedEntity(id, entityProperties) {
|
||||||
},
|
},
|
||||||
lineWidth: 1.0,
|
lineWidth: 1.0,
|
||||||
ignoreRayIntersection: true,
|
ignoreRayIntersection: true,
|
||||||
drawInFront: true
|
drawInFront: false // Arguable. For now, let's not distract with mysterious wires around the scene.
|
||||||
});
|
});
|
||||||
HighlightedEntity.overlays.push(this);
|
HighlightedEntity.overlays.push(this);
|
||||||
}
|
}
|
||||||
|
@ -167,12 +200,12 @@ pal.fromQml.connect(function (message) { // messages are {method, params}, like
|
||||||
//
|
//
|
||||||
function addAvatarNode(id) {
|
function addAvatarNode(id) {
|
||||||
var selected = ExtendedOverlay.isSelected(id);
|
var selected = ExtendedOverlay.isSelected(id);
|
||||||
return new ExtendedOverlay(id, "sphere", { // 3d so we don't go cross-eyed looking at it, but on top of everything
|
return new ExtendedOverlay(id, "sphere", {
|
||||||
solid: true,
|
drawInFront: true,
|
||||||
alpha: 0.8,
|
solid: true,
|
||||||
color: color(selected),
|
alpha: 0.8,
|
||||||
drawInFront: true
|
color: color(selected),
|
||||||
}, selected);
|
ignoreRayIntersection: false}, selected, true);
|
||||||
}
|
}
|
||||||
function populateUserList() {
|
function populateUserList() {
|
||||||
var data = [];
|
var data = [];
|
||||||
|
@ -227,6 +260,7 @@ function updateOverlays() {
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return; // don't update ourself
|
return; // don't update ourself
|
||||||
}
|
}
|
||||||
|
|
||||||
var overlay = ExtendedOverlay.get(id);
|
var overlay = ExtendedOverlay.get(id);
|
||||||
if (!overlay) { // For now, we're treating this as a temporary loss, as from the personal space bubble. Add it back.
|
if (!overlay) { // For now, we're treating this as a temporary loss, as from the personal space bubble. Add it back.
|
||||||
print('Adding non-PAL avatar node', id);
|
print('Adding non-PAL avatar node', id);
|
||||||
|
@ -235,11 +269,36 @@ function updateOverlays() {
|
||||||
var avatar = AvatarList.getAvatar(id);
|
var avatar = AvatarList.getAvatar(id);
|
||||||
var target = avatar.position;
|
var target = avatar.position;
|
||||||
var distance = Vec3.distance(target, eye);
|
var distance = Vec3.distance(target, eye);
|
||||||
|
var offset = 0.2;
|
||||||
|
|
||||||
|
// base offset on 1/2 distance from hips to head if we can
|
||||||
|
var headIndex = avatar.getJointIndex("Head");
|
||||||
|
if (headIndex > 0) {
|
||||||
|
offset = avatar.getAbsoluteJointTranslationInObjectFrame(headIndex).y / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get diff between target and eye (a vector pointing to the eye from avatar position)
|
||||||
|
var diff = Vec3.subtract(target, eye);
|
||||||
|
|
||||||
|
// move a bit in front, towards the camera
|
||||||
|
target = Vec3.subtract(target, Vec3.multiply(Vec3.normalize(diff), offset));
|
||||||
|
|
||||||
|
// now bump it up a bit
|
||||||
|
target.y = target.y + offset;
|
||||||
|
|
||||||
overlay.ping = pingPong;
|
overlay.ping = pingPong;
|
||||||
overlay.editOverlay({
|
overlay.editOverlay({
|
||||||
position: target,
|
position: target,
|
||||||
dimensions: 0.05 * distance // constant apparent size
|
dimensions: 0.032 * distance
|
||||||
});
|
});
|
||||||
|
if (overlay.model) {
|
||||||
|
overlay.model.ping = pingPong;
|
||||||
|
overlay.model.editOverlay({
|
||||||
|
position: target,
|
||||||
|
scale: 0.2 * distance, // constant apparent size
|
||||||
|
rotation: Camera.orientation
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
pingPong = !pingPong;
|
pingPong = !pingPong;
|
||||||
ExtendedOverlay.some(function (overlay) { // Remove any that weren't updated. (User is gone.)
|
ExtendedOverlay.some(function (overlay) { // Remove any that weren't updated. (User is gone.)
|
||||||
|
@ -262,7 +321,7 @@ function removeOverlays() {
|
||||||
function handleClick(pickRay) {
|
function handleClick(pickRay) {
|
||||||
ExtendedOverlay.applyPickRay(pickRay, function (overlay) {
|
ExtendedOverlay.applyPickRay(pickRay, function (overlay) {
|
||||||
// Don't select directly. Tell qml, who will give us back a list of ids.
|
// Don't select directly. Tell qml, who will give us back a list of ids.
|
||||||
var message = {method: 'select', params: [overlay.key, !overlay.selected]};
|
var message = {method: 'select', params: [[overlay.key], !overlay.selected]};
|
||||||
pal.sendToQml(message);
|
pal.sendToQml(message);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
@ -333,6 +392,31 @@ function onClicked() {
|
||||||
pal.setVisible(!pal.visible);
|
pal.setVisible(!pal.visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Message from other scripts, such as edit.js
|
||||||
|
//
|
||||||
|
var CHANNEL = 'com.highfidelity.pal';
|
||||||
|
function receiveMessage(channel, messageString, senderID) {
|
||||||
|
if ((channel !== CHANNEL) ||
|
||||||
|
(senderID !== MyAvatar.sessionUUID)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var message = JSON.parse(messageString);
|
||||||
|
switch (message.method) {
|
||||||
|
case 'select':
|
||||||
|
if (!pal.visible) {
|
||||||
|
onClicked();
|
||||||
|
}
|
||||||
|
pal.sendToQml(message); // Accepts objects, not just strings.
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
print('Unrecognized PAL message', messageString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Messages.subscribe(CHANNEL);
|
||||||
|
Messages.messageReceived.connect(receiveMessage);
|
||||||
|
|
||||||
|
|
||||||
var AVERAGING_RATIO = 0.05;
|
var AVERAGING_RATIO = 0.05;
|
||||||
var LOUDNESS_FLOOR = 11.0;
|
var LOUDNESS_FLOOR = 11.0;
|
||||||
var LOUDNESS_SCALE = 2.8 / 5.0;
|
var LOUDNESS_SCALE = 2.8 / 5.0;
|
||||||
|
@ -412,8 +496,10 @@ Script.scriptEnding.connect(function () {
|
||||||
Users.usernameFromIDReply.disconnect(usernameFromIDReply);
|
Users.usernameFromIDReply.disconnect(usernameFromIDReply);
|
||||||
Window.domainChanged.disconnect(clearIgnoredInQMLAndClosePAL);
|
Window.domainChanged.disconnect(clearIgnoredInQMLAndClosePAL);
|
||||||
Window.domainConnectionRefused.disconnect(clearIgnoredInQMLAndClosePAL);
|
Window.domainConnectionRefused.disconnect(clearIgnoredInQMLAndClosePAL);
|
||||||
|
Messages.unsubscribe(CHANNEL);
|
||||||
|
Messages.messageReceived.disconnect(receiveMessage);
|
||||||
off();
|
off();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// FIXME: }()); // END LOCAL_SCOPE
|
}()); // END LOCAL_SCOPE
|
||||||
|
|
169
scripts/tutorials/entity_scripts/chair.js
Normal file
169
scripts/tutorials/entity_scripts/chair.js
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
// chair.js
|
||||||
|
//
|
||||||
|
// Restrictions right now:
|
||||||
|
// Chair objects need to be set as not colliding with avatars, so that they pull avatar
|
||||||
|
// avatar into collision with them. Also they need to be at or above standing height
|
||||||
|
// (like a stool).
|
||||||
|
//
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
|
||||||
|
(function(){
|
||||||
|
|
||||||
|
var CHECK_INTERVAL_MSECS = 250; // When sitting, check for need to stand up
|
||||||
|
var SETTINGS_INTERVAL_MSECS = 1000; // Periodically check user data for updates
|
||||||
|
var DEFAULT_SIT_DISTANCE = 1.0; // How far away from the chair can you sit?
|
||||||
|
var HYSTERESIS = 1.1;
|
||||||
|
|
||||||
|
var sitTarget = { x: 0, y: 0, z: 0 }; // Offset where your butt should go relative
|
||||||
|
// to the object's center.
|
||||||
|
var SITTING = 0;
|
||||||
|
var STANDING = 1;
|
||||||
|
|
||||||
|
var state = STANDING;
|
||||||
|
var sitDistance = DEFAULT_SIT_DISTANCE;
|
||||||
|
|
||||||
|
var entity;
|
||||||
|
var props;
|
||||||
|
var checkTimer = false;
|
||||||
|
var settingsTimer = false;
|
||||||
|
var sitting = false;
|
||||||
|
|
||||||
|
var _this;
|
||||||
|
|
||||||
|
var WANT_DEBUG = false;
|
||||||
|
function debugPrint(string) {
|
||||||
|
if (WANT_DEBUG) {
|
||||||
|
print(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function howFarAway(position) {
|
||||||
|
return Vec3.distance(MyAvatar.position, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isSeatOpen(position, distance) {
|
||||||
|
closest = true;
|
||||||
|
AvatarList.getAvatarIdentifiers().forEach(function(avatarSessionUUID) {
|
||||||
|
var avatar = AvatarList.getAvatar(avatarSessionUUID);
|
||||||
|
if (avatarSessionUUID && Vec3.distance(avatar.position, position) < distance) {
|
||||||
|
debugPrint("Seat Occupied!");
|
||||||
|
closest = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return closest;
|
||||||
|
}
|
||||||
|
|
||||||
|
function enterSitPose() {
|
||||||
|
var rot;
|
||||||
|
var UPPER_LEG_ANGLE = 240;
|
||||||
|
var LOWER_LEG_ANGLE = -80;
|
||||||
|
rot = Quat.safeEulerAngles(MyAvatar.getJointRotation("RightUpLeg"));
|
||||||
|
MyAvatar.setJointData("RightUpLeg", Quat.fromPitchYawRollDegrees(UPPER_LEG_ANGLE, rot.y, rot.z), MyAvatar.getJointTranslation("RightUpLeg"));
|
||||||
|
rot = Quat.safeEulerAngles(MyAvatar.getJointRotation("RightLeg"));
|
||||||
|
MyAvatar.setJointData("RightLeg", Quat.fromPitchYawRollDegrees(LOWER_LEG_ANGLE, rot.y, rot.z), MyAvatar.getJointTranslation("RightLeg"));
|
||||||
|
rot = Quat.safeEulerAngles(MyAvatar.getJointRotation("LeftUpLeg"));
|
||||||
|
MyAvatar.setJointData("LeftUpLeg", Quat.fromPitchYawRollDegrees(UPPER_LEG_ANGLE, rot.y, rot.z), MyAvatar.getJointTranslation("LeftUpLeg"));
|
||||||
|
rot = Quat.safeEulerAngles(MyAvatar.getJointRotation("LeftLeg"));
|
||||||
|
MyAvatar.setJointData("LeftLeg", Quat.fromPitchYawRollDegrees(LOWER_LEG_ANGLE, rot.y, rot.z), MyAvatar.getJointTranslation("LeftLeg"));
|
||||||
|
}
|
||||||
|
|
||||||
|
function leaveSitPose() {
|
||||||
|
MyAvatar.clearJointData("RightUpLeg");
|
||||||
|
MyAvatar.clearJointData("LeftUpLeg");
|
||||||
|
MyAvatar.clearJointData("RightLeg");
|
||||||
|
MyAvatar.clearJointData("LeftLeg");
|
||||||
|
}
|
||||||
|
|
||||||
|
function sitDown(position, rotation) {
|
||||||
|
var eulers = Quat.safeEulerAngles(MyAvatar.orientation);
|
||||||
|
eulers.y = Quat.safeEulerAngles(rotation).y;
|
||||||
|
MyAvatar.position = Vec3.sum(position, Vec3.multiplyQbyV(props.rotation, sitTarget));
|
||||||
|
MyAvatar.orientation = Quat.fromPitchYawRollDegrees(eulers.x, eulers.y, eulers.z);
|
||||||
|
|
||||||
|
enterSitPose();
|
||||||
|
state = SITTING;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.preload = function(entityID) {
|
||||||
|
// Load the sound and range from the entity userData fields, and note the position of the entity.
|
||||||
|
debugPrint("chair preload");
|
||||||
|
entity = entityID;
|
||||||
|
_this = this;
|
||||||
|
settingsTimer = Script.setInterval(this.checkSettings, SETTINGS_INTERVAL_MSECS);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.maybeStand = function() {
|
||||||
|
props = Entities.getEntityProperties(entity, [ "position", "rotation" ]);
|
||||||
|
// First, check if the entity is far enough away to not need to do anything with it
|
||||||
|
var howFar = howFarAway(props.position);
|
||||||
|
if ((state === SITTING) && (howFar > sitDistance * HYSTERESIS)) {
|
||||||
|
leaveSitPose();
|
||||||
|
Script.clearInterval(checkTimer);
|
||||||
|
checkTimer = null;
|
||||||
|
state = STANDING;
|
||||||
|
debugPrint("Standing");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.clickDownOnEntity = function(entityID, mouseEvent) {
|
||||||
|
// If entity is clicked, sit
|
||||||
|
props = Entities.getEntityProperties(entity, [ "position", "rotation" ]);
|
||||||
|
if ((state === STANDING) && isSeatOpen(props.position, sitDistance)) {
|
||||||
|
sitDown(props.position, props.rotation);
|
||||||
|
checkTimer = Script.setInterval(this.maybeStand, CHECK_INTERVAL_MSECS);
|
||||||
|
debugPrint("Sitting from mouse click");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.startFarTrigger = function() {
|
||||||
|
// If entity is far clicked, sit
|
||||||
|
props = Entities.getEntityProperties(entity, [ "position", "rotation" ]);
|
||||||
|
if ((state === STANDING) && isSeatOpen(props.position, sitDistance)) {
|
||||||
|
sitDown(props.position, props.rotation);
|
||||||
|
checkTimer = Script.setInterval(this.maybeStand, CHECK_INTERVAL_MSECS);
|
||||||
|
debugPrint("Sitting from far trigger");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.checkSettings = function() {
|
||||||
|
var dataProps = Entities.getEntityProperties(entity, [ "userData" ]);
|
||||||
|
if (dataProps.userData) {
|
||||||
|
var data = JSON.parse(dataProps.userData);
|
||||||
|
if (data.sitDistance) {
|
||||||
|
if (!(sitDistance === data.sitDistance)) {
|
||||||
|
debugPrint("Read new sit distance: " + data.sitDistance);
|
||||||
|
}
|
||||||
|
sitDistance = data.sitDistance;
|
||||||
|
}
|
||||||
|
if (data.sitTarget) {
|
||||||
|
if (data.sitTarget.y && (data.sitTarget.y != sitTarget.y)) {
|
||||||
|
debugPrint("Read new sitTarget.y: " + data.sitTarget.y);
|
||||||
|
sitTarget.y = data.sitTarget.y;
|
||||||
|
}
|
||||||
|
if (data.sitTarget.x && (data.sitTarget.x != sitTarget.x)) {
|
||||||
|
debugPrint("Read new sitTarget.x: " + data.sitTarget.x);
|
||||||
|
sitTarget.x = data.sitTarget.x;
|
||||||
|
}
|
||||||
|
if (data.sitTarget.z && (data.sitTarget.z != sitTarget.z)) {
|
||||||
|
debugPrint("Read new sitTarget.z: " + data.sitTarget.z);
|
||||||
|
sitTarget.z = data.sitTarget.z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.unload = function(entityID) {
|
||||||
|
debugPrint("chair unload");
|
||||||
|
if (checkTimer) {
|
||||||
|
Script.clearInterval(checkTimer);
|
||||||
|
}
|
||||||
|
if (settingsTimer) {
|
||||||
|
Script.clearInterval(settingsTimer);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
})
|
Loading…
Reference in a new issue