mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-30 10:02:53 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into location-cleanup
This commit is contained in:
commit
5b216578d7
39 changed files with 921 additions and 260 deletions
assignment-client/src
domain-server
resources
src
interface/src/ui
libraries
display-plugins/src/display-plugins/hmd
entities-renderer/src
networking/src
render-utils/src
script-engine/src
scripts
|
@ -15,7 +15,6 @@
|
|||
// this should send a signal every 10ms, with pretty good precision. Hardcoding
|
||||
// to 10ms since that's what you'd want for audio.
|
||||
void AvatarAudioTimer::start() {
|
||||
qDebug() << __FUNCTION__;
|
||||
auto startTime = usecTimestampNow();
|
||||
quint64 frameCounter = 0;
|
||||
const int TARGET_INTERVAL_USEC = 10000; // 10ms
|
||||
|
|
|
@ -95,7 +95,7 @@ AudioMixer::AudioMixer(ReceivedMessage& message) :
|
|||
packetReceiver.registerListener(PacketType::NodeIgnoreRequest, this, "handleNodeIgnoreRequestPacket");
|
||||
packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket");
|
||||
packetReceiver.registerListener(PacketType::NodeMuteRequest, this, "handleNodeMuteRequestPacket");
|
||||
|
||||
packetReceiver.registerListener(PacketType::RadiusIgnoreRequest, this, "handleRadiusIgnoreRequestPacket");
|
||||
connect(nodeList.data(), &NodeList::nodeKilled, this, &AudioMixer::handleNodeKilled);
|
||||
}
|
||||
|
||||
|
@ -393,16 +393,26 @@ bool AudioMixer::prepareMixForListeningNode(Node* node) {
|
|||
&& !node->isIgnoringNodeWithID(otherNode->getUUID()) && !otherNode->isIgnoringNodeWithID(node->getUUID())) {
|
||||
AudioMixerClientData* otherNodeClientData = (AudioMixerClientData*) otherNode->getLinkedData();
|
||||
|
||||
// enumerate the ARBs attached to the otherNode and add all that should be added to mix
|
||||
auto streamsCopy = otherNodeClientData->getAudioStreams();
|
||||
// check to see if we're ignoring in radius
|
||||
bool insideIgnoreRadius = false;
|
||||
if (node->isIgnoreRadiusEnabled() || otherNode->isIgnoreRadiusEnabled()) {
|
||||
AudioMixerClientData* otherData = reinterpret_cast<AudioMixerClientData*>(otherNode->getLinkedData());
|
||||
AudioMixerClientData* nodeData = reinterpret_cast<AudioMixerClientData*>(node->getLinkedData());
|
||||
float ignoreRadius = glm::min(node->getIgnoreRadius(), otherNode->getIgnoreRadius());
|
||||
if (glm::distance(nodeData->getPosition(), otherData->getPosition()) < ignoreRadius) {
|
||||
insideIgnoreRadius = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& streamPair : streamsCopy) {
|
||||
|
||||
auto otherNodeStream = streamPair.second;
|
||||
|
||||
if (*otherNode != *node || otherNodeStream->shouldLoopbackForNode()) {
|
||||
addStreamToMixForListeningNodeWithStream(*listenerNodeData, *otherNodeStream, otherNode->getUUID(),
|
||||
*nodeAudioStream);
|
||||
if (!insideIgnoreRadius) {
|
||||
// enumerate the ARBs attached to the otherNode and add all that should be added to mix
|
||||
auto streamsCopy = otherNodeClientData->getAudioStreams();
|
||||
for (auto& streamPair : streamsCopy) {
|
||||
auto otherNodeStream = streamPair.second;
|
||||
if (*otherNode != *node || otherNodeStream->shouldLoopbackForNode()) {
|
||||
addStreamToMixForListeningNodeWithStream(*listenerNodeData, *otherNodeStream, otherNode->getUUID(),
|
||||
*nodeAudioStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -634,11 +644,14 @@ void AudioMixer::handleKillAvatarPacket(QSharedPointer<ReceivedMessage> packet,
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void AudioMixer::handleNodeIgnoreRequestPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode) {
|
||||
sendingNode->parseIgnoreRequestMessage(packet);
|
||||
}
|
||||
|
||||
void AudioMixer::handleRadiusIgnoreRequestPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode) {
|
||||
sendingNode->parseIgnoreRadiusRequestMessage(packet);
|
||||
}
|
||||
|
||||
void AudioMixer::removeHRTFsForFinishedInjector(const QUuid& streamID) {
|
||||
auto injectorClientData = qobject_cast<AudioMixerClientData*>(sender());
|
||||
if (injectorClientData) {
|
||||
|
|
|
@ -48,6 +48,7 @@ private slots:
|
|||
void handleNegotiateAudioFormat(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
|
||||
void handleNodeKilled(SharedNodePointer killedNode);
|
||||
void handleNodeIgnoreRequestPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
|
||||
void handleRadiusIgnoreRequestPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
|
||||
void handleKillAvatarPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
|
||||
void handleNodeMuteRequestPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
|
||||
|
||||
|
|
|
@ -365,10 +365,6 @@ QJsonObject AudioMixerClientData::getAudioStreamStats() {
|
|||
}
|
||||
|
||||
void AudioMixerClientData::handleMismatchAudioFormat(SharedNodePointer node, const QString& currentCodec, const QString& recievedCodec) {
|
||||
qDebug() << __FUNCTION__ <<
|
||||
"sendingNode:" << *node <<
|
||||
"currentCodec:" << currentCodec <<
|
||||
"receivedCodec:" << recievedCodec;
|
||||
sendSelectAudioFormat(node, currentCodec);
|
||||
}
|
||||
|
||||
|
|
|
@ -89,6 +89,7 @@ public:
|
|||
|
||||
bool shouldMuteClient() { return _shouldMuteClient; }
|
||||
void setShouldMuteClient(bool shouldMuteClient) { _shouldMuteClient = shouldMuteClient; }
|
||||
glm::vec3 getPosition() { return getAvatarAudioStream() ? getAvatarAudioStream()->getPosition() : glm::vec3(0); }
|
||||
|
||||
signals:
|
||||
void injectorStreamFinished(const QUuid& streamIdentifier);
|
||||
|
|
|
@ -46,6 +46,7 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) :
|
|||
packetReceiver.registerListener(PacketType::AvatarIdentity, this, "handleAvatarIdentityPacket");
|
||||
packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket");
|
||||
packetReceiver.registerListener(PacketType::NodeIgnoreRequest, this, "handleNodeIgnoreRequestPacket");
|
||||
packetReceiver.registerListener(PacketType::RadiusIgnoreRequest, this, "handleRadiusIgnoreRequestPacket");
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
connect(nodeList.data(), &NodeList::packetVersionMismatch, this, &AvatarMixer::handlePacketVersionMismatch);
|
||||
|
@ -237,6 +238,20 @@ void AvatarMixer::broadcastAvatarData() {
|
|||
|| otherNode->isIgnoringNodeWithID(node->getUUID())) {
|
||||
return false;
|
||||
} else {
|
||||
AvatarMixerClientData* otherData = reinterpret_cast<AvatarMixerClientData*>(otherNode->getLinkedData());
|
||||
AvatarMixerClientData* nodeData = reinterpret_cast<AvatarMixerClientData*>(node->getLinkedData());
|
||||
// check to see if we're ignoring in radius
|
||||
if (node->isIgnoreRadiusEnabled() || otherNode->isIgnoreRadiusEnabled()) {
|
||||
float ignoreRadius = glm::min(node->getIgnoreRadius(), otherNode->getIgnoreRadius());
|
||||
if (glm::distance(nodeData->getPosition(), otherData->getPosition()) < ignoreRadius) {
|
||||
nodeData->ignoreOther(node, otherNode);
|
||||
otherData->ignoreOther(otherNode, node);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// not close enough to ignore
|
||||
nodeData->removeFromRadiusIgnoringSet(otherNode->getUUID());
|
||||
otherData->removeFromRadiusIgnoringSet(node->getUUID());
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
@ -442,6 +457,10 @@ void AvatarMixer::handleNodeIgnoreRequestPacket(QSharedPointer<ReceivedMessage>
|
|||
senderNode->parseIgnoreRequestMessage(message);
|
||||
}
|
||||
|
||||
void AvatarMixer::handleRadiusIgnoreRequestPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode) {
|
||||
sendingNode->parseIgnoreRadiusRequestMessage(packet);
|
||||
}
|
||||
|
||||
void AvatarMixer::sendStatsPacket() {
|
||||
QJsonObject statsObject;
|
||||
statsObject["average_listeners_last_second"] = (float) _sumListeners / (float) _numStatFrames;
|
||||
|
|
|
@ -38,6 +38,7 @@ private slots:
|
|||
void handleAvatarIdentityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleKillAvatarPacket(QSharedPointer<ReceivedMessage> message);
|
||||
void handleNodeIgnoreRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleRadiusIgnoreRequestPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
|
||||
void domainSettingsRequestComplete();
|
||||
void handlePacketVersionMismatch(PacketType type, const HifiSockAddr& senderSockAddr, const QUuid& senderUUID);
|
||||
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
|
||||
#include <udt/PacketHeaders.h>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
#include <NodeList.h>
|
||||
|
||||
#include "AvatarMixerClientData.h"
|
||||
|
||||
int AvatarMixerClientData::parseData(ReceivedMessage& message) {
|
||||
|
@ -39,6 +42,16 @@ uint16_t AvatarMixerClientData::getLastBroadcastSequenceNumber(const QUuid& node
|
|||
}
|
||||
}
|
||||
|
||||
void AvatarMixerClientData::ignoreOther(SharedNodePointer self, SharedNodePointer other) {
|
||||
if (!isRadiusIgnoring(other->getUUID())) {
|
||||
addToRadiusIgnoringSet(other->getUUID());
|
||||
auto killPacket = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID);
|
||||
killPacket->write(other->getUUID().toRfc4122());
|
||||
DependencyManager::get<NodeList>()->sendUnreliablePacket(*killPacket, *self);
|
||||
_hasReceivedFirstPacketsFrom.erase(other->getUUID());
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarMixerClientData::loadJSONStats(QJsonObject& jsonObject) const {
|
||||
jsonObject["display_name"] = _avatar->getDisplayName();
|
||||
jsonObject["full_rate_distance"] = _fullRateDistance;
|
||||
|
|
|
@ -79,6 +79,13 @@ public:
|
|||
{ return _avgOtherAvatarDataRate.getAverageSampleValuePerSecond() / (float) BYTES_PER_KILOBIT; }
|
||||
|
||||
void loadJSONStats(QJsonObject& jsonObject) const;
|
||||
|
||||
glm::vec3 getPosition() { return _avatar ? _avatar->getPosition() : glm::vec3(0); }
|
||||
bool isRadiusIgnoring(const QUuid& other) { return _radiusIgnoredOthers.find(other) != _radiusIgnoredOthers.end(); }
|
||||
void addToRadiusIgnoringSet(const QUuid& other) { _radiusIgnoredOthers.insert(other); }
|
||||
void removeFromRadiusIgnoringSet(const QUuid& other) { _radiusIgnoredOthers.erase(other); }
|
||||
void ignoreOther(SharedNodePointer self, SharedNodePointer other);
|
||||
|
||||
private:
|
||||
AvatarSharedPointer _avatar { new AvatarData() };
|
||||
|
||||
|
@ -99,6 +106,7 @@ private:
|
|||
int _numOutOfOrderSends = 0;
|
||||
|
||||
SimpleMovingAverage _avgOtherAvatarDataRate;
|
||||
std::unordered_set<QUuid> _radiusIgnoredOthers;
|
||||
};
|
||||
|
||||
#endif // hifi_AvatarMixerClientData_h
|
||||
|
|
|
@ -684,6 +684,79 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "permissions",
|
||||
"type": "table",
|
||||
"caption": "Permissions for Specific Users",
|
||||
"can_add_new_rows": true,
|
||||
|
||||
"groups": [
|
||||
{
|
||||
"label": "User",
|
||||
"span": 1
|
||||
},
|
||||
{
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level or group permissions that might otherwise apply to that user.</p>'>?</a>",
|
||||
"span": 7
|
||||
}
|
||||
],
|
||||
|
||||
"columns": [
|
||||
{
|
||||
"name": "permissions_id",
|
||||
"label": ""
|
||||
},
|
||||
{
|
||||
"name": "id_can_connect",
|
||||
"label": "Connect",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_adjust_locks",
|
||||
"label": "Lock / Unlock",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_rez",
|
||||
"label": "Rez",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_rez_tmp",
|
||||
"label": "Rez Temporary",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_write_to_asset_server",
|
||||
"label": "Write Assets",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_connect_past_max_capacity",
|
||||
"label": "Ignore Max Capacity",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_kick",
|
||||
"label": "Kick Users",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ip_permissions",
|
||||
"type": "table",
|
||||
|
@ -757,18 +830,17 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"name": "permissions",
|
||||
"name": "mac_permissions",
|
||||
"type": "table",
|
||||
"caption": "Permissions for Specific Users",
|
||||
"caption": "Permissions for Users with MAC Addresses",
|
||||
"can_add_new_rows": true,
|
||||
|
||||
"groups": [
|
||||
{
|
||||
"label": "User",
|
||||
"label": "MAC Address",
|
||||
"span": 1
|
||||
},
|
||||
{
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level or group permissions that might otherwise apply to that user.</p>'>?</a>",
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide MAC Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users with specific MACs can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific MACs can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users with specific MACs can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users with specific MACs can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users with specific MACs can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users with specific MACs can connect even if the domain has reached or exceeded its maximum allowed agents.</li></ul><p>Note that permissions assigned to a specific MAC will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). MAC address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
|
||||
"span": 7
|
||||
}
|
||||
],
|
||||
|
|
|
@ -119,15 +119,20 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<ReceivedMessag
|
|||
nodeData->setNodeInterestSet(safeInterestSet);
|
||||
nodeData->setPlaceName(nodeConnection.placeName);
|
||||
|
||||
qDebug() << "Allowed connection from node" << uuidStringWithoutCurlyBraces(node->getUUID())
|
||||
<< "on" << message->getSenderSockAddr() << "with MAC" << nodeConnection.hardwareAddress;
|
||||
|
||||
// signal that we just connected a node so the DomainServer can get it a list
|
||||
// and broadcast its presence right away
|
||||
emit connectedNode(node);
|
||||
} else {
|
||||
qDebug() << "Refusing connection from node at" << message->getSenderSockAddr();
|
||||
qDebug() << "Refusing connection from node at" << message->getSenderSockAddr()
|
||||
<< "with hardware address" << nodeConnection.hardwareAddress;
|
||||
}
|
||||
}
|
||||
|
||||
NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QString verifiedUsername, const QHostAddress& senderAddress) {
|
||||
NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QString verifiedUsername,
|
||||
const QHostAddress& senderAddress, const QString& hardwareAddress) {
|
||||
NodePermissions userPerms;
|
||||
|
||||
userPerms.setAll(false);
|
||||
|
@ -144,8 +149,14 @@ NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QStrin
|
|||
#ifdef WANT_DEBUG
|
||||
qDebug() << "| user-permissions: unverified or no username for" << userPerms.getID() << ", so:" << userPerms;
|
||||
#endif
|
||||
if (!hardwareAddress.isEmpty() && _server->_settingsManager.hasPermissionsForMAC(hardwareAddress)) {
|
||||
// this user comes from a MAC we have in our permissions table, apply those permissions
|
||||
userPerms = _server->_settingsManager.getPermissionsForMAC(hardwareAddress);
|
||||
|
||||
if (_server->_settingsManager.hasPermissionsForIP(senderAddress)) {
|
||||
#ifdef WANT_DEBUG
|
||||
qDebug() << "| user-permissions: specific MAC matches, so:" << userPerms;
|
||||
#endif
|
||||
} else if (_server->_settingsManager.hasPermissionsForIP(senderAddress)) {
|
||||
// this user comes from an IP we have in our permissions table, apply those permissions
|
||||
userPerms = _server->_settingsManager.getPermissionsForIP(senderAddress);
|
||||
|
||||
|
@ -158,6 +169,13 @@ NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QStrin
|
|||
userPerms = _server->_settingsManager.getPermissionsForName(verifiedUsername);
|
||||
#ifdef WANT_DEBUG
|
||||
qDebug() << "| user-permissions: specific user matches, so:" << userPerms;
|
||||
#endif
|
||||
} else if (!hardwareAddress.isEmpty() && _server->_settingsManager.hasPermissionsForMAC(hardwareAddress)) {
|
||||
// this user comes from a MAC we have in our permissions table, apply those permissions
|
||||
userPerms = _server->_settingsManager.getPermissionsForMAC(hardwareAddress);
|
||||
|
||||
#ifdef WANT_DEBUG
|
||||
qDebug() << "| user-permissions: specific MAC matches, so:" << userPerms;
|
||||
#endif
|
||||
} else if (_server->_settingsManager.hasPermissionsForIP(senderAddress)) {
|
||||
// this user comes from an IP we have in our permissions table, apply those permissions
|
||||
|
@ -255,7 +273,14 @@ void DomainGatekeeper::updateNodePermissions() {
|
|||
// or the public socket if we haven't activated a socket for the node yet
|
||||
HifiSockAddr connectingAddr = node->getActiveSocket() ? *node->getActiveSocket() : node->getPublicSocket();
|
||||
|
||||
userPerms = setPermissionsForUser(isLocalUser, verifiedUsername, connectingAddr.getAddress());
|
||||
QString hardwareAddress;
|
||||
|
||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||
if (nodeData) {
|
||||
hardwareAddress = nodeData->getHardwareAddress();
|
||||
}
|
||||
|
||||
userPerms = setPermissionsForUser(isLocalUser, verifiedUsername, connectingAddr.getAddress(), hardwareAddress);
|
||||
}
|
||||
|
||||
node->setPermissions(userPerms);
|
||||
|
@ -308,6 +333,7 @@ SharedNodePointer DomainGatekeeper::processAssignmentConnectRequest(const NodeCo
|
|||
nodeData->setAssignmentUUID(matchingQueuedAssignment->getUUID());
|
||||
nodeData->setWalletUUID(it->second.getWalletUUID());
|
||||
nodeData->setNodeVersion(it->second.getNodeVersion());
|
||||
nodeData->setHardwareAddress(nodeConnection.hardwareAddress);
|
||||
nodeData->setWasAssigned(true);
|
||||
|
||||
// cleanup the PendingAssignedNodeData for this assignment now that it's connecting
|
||||
|
@ -369,7 +395,8 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
|
|||
}
|
||||
}
|
||||
|
||||
userPerms = setPermissionsForUser(isLocalUser, verifiedUsername, nodeConnection.senderSockAddr.getAddress());
|
||||
userPerms = setPermissionsForUser(isLocalUser, verifiedUsername, nodeConnection.senderSockAddr.getAddress(),
|
||||
nodeConnection.hardwareAddress);
|
||||
|
||||
if (!userPerms.can(NodePermissions::Permission::canConnectToDomain)) {
|
||||
sendConnectionDeniedPacket("You lack the required permissions to connect to this domain.",
|
||||
|
@ -425,6 +452,9 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
|
|||
// if we have a username from the connect request, set it on the DomainServerNodeData
|
||||
nodeData->setUsername(username);
|
||||
|
||||
// set the hardware address passed in the connect request
|
||||
nodeData->setHardwareAddress(nodeConnection.hardwareAddress);
|
||||
|
||||
// also add an interpolation to DomainServerNodeData so that servers can get username in stats
|
||||
nodeData->addOverrideForKey(USERNAME_UUID_REPLACEMENT_STATS_KEY,
|
||||
uuidStringWithoutCurlyBraces(newNode->getUUID()), username);
|
||||
|
|
|
@ -107,7 +107,8 @@ private:
|
|||
QSet<QString> _domainOwnerFriends; // keep track of friends of the domain owner
|
||||
QSet<QString> _inFlightGroupMembershipsRequests; // keep track of which we've already asked for
|
||||
|
||||
NodePermissions setPermissionsForUser(bool isLocalUser, QString verifiedUsername, const QHostAddress& senderAddress);
|
||||
NodePermissions setPermissionsForUser(bool isLocalUser, QString verifiedUsername,
|
||||
const QHostAddress& senderAddress, const QString& hardwareAddress);
|
||||
|
||||
void getGroupMemberships(const QString& username);
|
||||
// void getIsGroupMember(const QString& username, const QUuid groupID);
|
||||
|
|
|
@ -53,6 +53,9 @@ public:
|
|||
|
||||
void setNodeVersion(const QString& nodeVersion) { _nodeVersion = nodeVersion; }
|
||||
const QString& getNodeVersion() { return _nodeVersion; }
|
||||
|
||||
void setHardwareAddress(const QString& hardwareAddress) { _hardwareAddress = hardwareAddress; }
|
||||
const QString& getHardwareAddress() { return _hardwareAddress; }
|
||||
|
||||
void addOverrideForKey(const QString& key, const QString& value, const QString& overrideValue);
|
||||
void removeOverrideForKey(const QString& key, const QString& value);
|
||||
|
@ -81,6 +84,7 @@ private:
|
|||
bool _isAuthenticated = true;
|
||||
NodeSet _nodeInterestSet;
|
||||
QString _nodeVersion;
|
||||
QString _hardwareAddress;
|
||||
|
||||
QString _placeName;
|
||||
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
#include <NLPacketList.h>
|
||||
#include <NumericalConstants.h>
|
||||
|
||||
#include "DomainServerNodeData.h"
|
||||
|
||||
#include "DomainServerSettingsManager.h"
|
||||
|
||||
const QString SETTINGS_DESCRIPTION_RELATIVE_PATH = "/resources/describe-settings.json";
|
||||
|
@ -439,6 +441,9 @@ void DomainServerSettingsManager::packPermissions() {
|
|||
// save settings for IP addresses
|
||||
packPermissionsForMap("permissions", _ipPermissions, IP_PERMISSIONS_KEYPATH);
|
||||
|
||||
// save settings for MAC addresses
|
||||
packPermissionsForMap("permissions", _macPermissions, MAC_PERMISSIONS_KEYPATH);
|
||||
|
||||
// save settings for groups
|
||||
packPermissionsForMap("permissions", _groupPermissions, GROUP_PERMISSIONS_KEYPATH);
|
||||
|
||||
|
@ -506,6 +511,17 @@ void DomainServerSettingsManager::unpackPermissions() {
|
|||
}
|
||||
});
|
||||
|
||||
needPack |= unpackPermissionsForKeypath(MAC_PERMISSIONS_KEYPATH, &_macPermissions,
|
||||
[&](NodePermissionsPointer perms){
|
||||
// make sure that this permission row is for a non-empty hardware
|
||||
if (perms->getKey().first.isEmpty()) {
|
||||
_macPermissions.remove(perms->getKey());
|
||||
|
||||
// we removed a row from the MAC permissions, we'll need a re-pack
|
||||
needPack = true;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
needPack |= unpackPermissionsForKeypath(GROUP_PERMISSIONS_KEYPATH, &_groupPermissions,
|
||||
[&](NodePermissionsPointer perms){
|
||||
|
@ -558,7 +574,8 @@ void DomainServerSettingsManager::unpackPermissions() {
|
|||
qDebug() << "--------------- permissions ---------------------";
|
||||
QList<QHash<NodePermissionsKey, NodePermissionsPointer>> permissionsSets;
|
||||
permissionsSets << _standardAgentPermissions.get() << _agentPermissions.get()
|
||||
<< _groupPermissions.get() << _groupForbiddens.get() << _ipPermissions.get();
|
||||
<< _groupPermissions.get() << _groupForbiddens.get()
|
||||
<< _ipPermissions.get() << _macPermissions.get();
|
||||
foreach (auto permissionSet, permissionsSets) {
|
||||
QHashIterator<NodePermissionsKey, NodePermissionsPointer> i(permissionSet);
|
||||
while (i.hasNext()) {
|
||||
|
@ -653,19 +670,25 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointer<Re
|
|||
|
||||
auto verifiedUsername = matchingNode->getPermissions().getVerifiedUserName();
|
||||
|
||||
bool hadExistingPermissions = false;
|
||||
bool newPermissions = false;
|
||||
|
||||
if (!verifiedUsername.isEmpty()) {
|
||||
// if we have a verified user name for this user, we apply the kick to the username
|
||||
|
||||
// check if there were already permissions
|
||||
hadExistingPermissions = havePermissionsForName(verifiedUsername);
|
||||
bool hadPermissions = havePermissionsForName(verifiedUsername);
|
||||
|
||||
// grab or create permissions for the given username
|
||||
destinationPermissions = _agentPermissions[matchingNode->getPermissions().getKey()];
|
||||
auto userPermissions = _agentPermissions[matchingNode->getPermissions().getKey()];
|
||||
|
||||
newPermissions = !hadPermissions || userPermissions->can(NodePermissions::Permission::canConnectToDomain);
|
||||
|
||||
// ensure that the connect permission is clear
|
||||
userPermissions->clear(NodePermissions::Permission::canConnectToDomain);
|
||||
} else {
|
||||
// otherwise we apply the kick to the IP from active socket for this node
|
||||
// (falling back to the public socket if not yet active)
|
||||
// otherwise we apply the kick to the IP from active socket for this node and the MAC address
|
||||
|
||||
// remove connect permissions for the IP (falling back to the public socket if not yet active)
|
||||
auto& kickAddress = matchingNode->getActiveSocket()
|
||||
? matchingNode->getActiveSocket()->getAddress()
|
||||
: matchingNode->getPublicSocket().getAddress();
|
||||
|
@ -673,32 +696,41 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointer<Re
|
|||
NodePermissionsKey ipAddressKey(kickAddress.toString(), QUuid());
|
||||
|
||||
// check if there were already permissions for the IP
|
||||
hadExistingPermissions = hasPermissionsForIP(kickAddress);
|
||||
bool hadIPPermissions = hasPermissionsForIP(kickAddress);
|
||||
|
||||
// grab or create permissions for the given IP address
|
||||
destinationPermissions = _ipPermissions[ipAddressKey];
|
||||
auto ipPermissions = _ipPermissions[ipAddressKey];
|
||||
|
||||
if (!hadIPPermissions || ipPermissions->can(NodePermissions::Permission::canConnectToDomain)) {
|
||||
newPermissions = true;
|
||||
|
||||
ipPermissions->clear(NodePermissions::Permission::canConnectToDomain);
|
||||
}
|
||||
|
||||
// potentially remove connect permissions for the MAC address
|
||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(matchingNode->getLinkedData());
|
||||
if (nodeData) {
|
||||
NodePermissionsKey macAddressKey(nodeData->getHardwareAddress(), 0);
|
||||
|
||||
bool hadMACPermissions = hasPermissionsForMAC(nodeData->getHardwareAddress());
|
||||
|
||||
auto macPermissions = _macPermissions[macAddressKey];
|
||||
|
||||
if (!hadMACPermissions || macPermissions->can(NodePermissions::Permission::canConnectToDomain)) {
|
||||
newPermissions = true;
|
||||
|
||||
macPermissions->clear(NodePermissions::Permission::canConnectToDomain);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// make sure we didn't already have existing permissions that disallowed connect
|
||||
if (!hadExistingPermissions
|
||||
|| destinationPermissions->can(NodePermissions::Permission::canConnectToDomain)) {
|
||||
|
||||
if (newPermissions) {
|
||||
qDebug() << "Removing connect permission for node" << uuidStringWithoutCurlyBraces(matchingNode->getUUID())
|
||||
<< "after kick request";
|
||||
|
||||
// ensure that the connect permission is clear
|
||||
destinationPermissions->clear(NodePermissions::Permission::canConnectToDomain);
|
||||
<< "after kick request from" << uuidStringWithoutCurlyBraces(sendingNode->getUUID());
|
||||
|
||||
// we've changed permissions, time to store them to disk and emit our signal to say they have changed
|
||||
packPermissions();
|
||||
|
||||
emit updateNodePermissions();
|
||||
} else {
|
||||
qWarning() << "Received kick request for node" << uuidStringWithoutCurlyBraces(matchingNode->getUUID())
|
||||
<< "that already did not have permission to connect";
|
||||
|
||||
// in this case, though we don't expect the node to be connected to the domain, it is
|
||||
// emit updateNodePermissions so that the DomainGatekeeper kicks it out
|
||||
emit updateNodePermissions();
|
||||
}
|
||||
|
||||
|
@ -753,6 +785,16 @@ NodePermissions DomainServerSettingsManager::getPermissionsForIP(const QHostAddr
|
|||
return nullPermissions;
|
||||
}
|
||||
|
||||
NodePermissions DomainServerSettingsManager::getPermissionsForMAC(const QString& macAddress) const {
|
||||
NodePermissionsKey macKey = NodePermissionsKey(macAddress, 0);
|
||||
if (_macPermissions.contains(macKey)) {
|
||||
return *(_macPermissions[macKey].get());
|
||||
}
|
||||
NodePermissions nullPermissions;
|
||||
nullPermissions.setAll(false);
|
||||
return nullPermissions;
|
||||
}
|
||||
|
||||
NodePermissions DomainServerSettingsManager::getPermissionsForGroup(const QString& groupName, QUuid rankID) const {
|
||||
NodePermissionsKey groupRankKey = NodePermissionsKey(groupName, rankID);
|
||||
if (_groupPermissions.contains(groupRankKey)) {
|
||||
|
|
|
@ -28,6 +28,7 @@ const QString SETTINGS_PATH_JSON = SETTINGS_PATH + ".json";
|
|||
const QString AGENT_STANDARD_PERMISSIONS_KEYPATH = "security.standard_permissions";
|
||||
const QString AGENT_PERMISSIONS_KEYPATH = "security.permissions";
|
||||
const QString IP_PERMISSIONS_KEYPATH = "security.ip_permissions";
|
||||
const QString MAC_PERMISSIONS_KEYPATH = "security.mac_permissions";
|
||||
const QString GROUP_PERMISSIONS_KEYPATH = "security.group_permissions";
|
||||
const QString GROUP_FORBIDDENS_KEYPATH = "security.group_forbiddens";
|
||||
|
||||
|
@ -62,6 +63,10 @@ public:
|
|||
bool hasPermissionsForIP(const QHostAddress& address) const { return _ipPermissions.contains(address.toString(), 0); }
|
||||
NodePermissions getPermissionsForIP(const QHostAddress& address) const;
|
||||
|
||||
// these give access to permissions for specific MACs from the domain-server settings page
|
||||
bool hasPermissionsForMAC(const QString& macAddress) const { return _macPermissions.contains(macAddress, 0); }
|
||||
NodePermissions getPermissionsForMAC(const QString& macAddress) const;
|
||||
|
||||
// these give access to permissions for specific groups from the domain-server settings page
|
||||
bool havePermissionsForGroup(const QString& groupName, QUuid rankID) const {
|
||||
return _groupPermissions.contains(groupName, rankID);
|
||||
|
@ -142,6 +147,7 @@ private:
|
|||
NodePermissionsMap _agentPermissions; // specific account-names
|
||||
|
||||
NodePermissionsMap _ipPermissions; // permissions granted by node IP address
|
||||
NodePermissionsMap _macPermissions; // permissions granted by node MAC address
|
||||
|
||||
NodePermissionsMap _groupPermissions; // permissions granted by membership to specific groups
|
||||
NodePermissionsMap _groupForbiddens; // permissions denied due to membership in a specific group
|
||||
|
|
|
@ -29,6 +29,9 @@ NodeConnectionData NodeConnectionData::fromDataStream(QDataStream& dataStream, c
|
|||
|
||||
// NOTE: QDataStream::readBytes() - The buffer is allocated using new []. Destroy it with the delete [] operator.
|
||||
delete[] rawBytes;
|
||||
|
||||
// read the hardware address sent by the client
|
||||
dataStream >> newHeader.hardwareAddress;
|
||||
}
|
||||
|
||||
dataStream >> newHeader.nodeType
|
||||
|
|
|
@ -28,6 +28,7 @@ public:
|
|||
HifiSockAddr senderSockAddr;
|
||||
QList<NodeType_t> interestList;
|
||||
QString placeName;
|
||||
QString hardwareAddress;
|
||||
|
||||
QByteArray protocolVersion;
|
||||
};
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
|
||||
void setupPreferences() {
|
||||
auto preferences = DependencyManager::get<Preferences>();
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
static const QString AVATAR_BASICS { "Avatar Basics" };
|
||||
{
|
||||
|
@ -68,6 +68,18 @@ void setupPreferences() {
|
|||
auto setter = [=](bool value) { myAvatar->setClearOverlayWhenMoving(value); };
|
||||
preferences->addPreference(new CheckPreference(AVATAR_BASICS, "Clear overlays when moving", getter, setter));
|
||||
}
|
||||
{
|
||||
auto getter = [=]()->float { return nodeList->getIgnoreRadius(); };
|
||||
auto setter = [=](float value) {
|
||||
nodeList->ignoreNodesInRadius(value, nodeList->getIgnoreRadiusEnabled());
|
||||
};
|
||||
auto preference = new SpinnerPreference(AVATAR_BASICS, "Personal space bubble radius (default is 1m)", getter, setter);
|
||||
preference->setMin(0.01f);
|
||||
preference->setMax(99.9f);
|
||||
preference->setDecimals(2);
|
||||
preference->setStep(0.25);
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
|
||||
// UI
|
||||
{
|
||||
|
|
|
@ -39,11 +39,11 @@ bool DebugHmdDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
|
|||
_uiModelTransform = DependencyManager::get<CompositorHelper>()->getModelTransform();
|
||||
_frameInfos[frameIndex] = _currentRenderFrameInfo;
|
||||
|
||||
_handPoses[0] = glm::translate(mat4(), vec3(-0.3f, 0.0f, 0.0f));
|
||||
_handPoses[0] = glm::translate(mat4(), vec3(0.3f * cosf(secTimestampNow() * 3.0f), -0.3f * sinf(secTimestampNow() * 5.0f), 0.0f));
|
||||
_handLasers[0].color = vec4(1, 0, 0, 1);
|
||||
_handLasers[0].mode = HandLaserMode::Overlay;
|
||||
|
||||
_handPoses[1] = glm::translate(mat4(), vec3(0.3f, 0.0f, 0.0f));
|
||||
_handPoses[1] = glm::translate(mat4(), vec3(0.3f * sinf(secTimestampNow() * 3.0f), -0.3f * cosf(secTimestampNow() * 5.0f), 0.0f));
|
||||
_handLasers[1].color = vec4(0, 1, 1, 1);
|
||||
_handLasers[1].mode = HandLaserMode::Overlay;
|
||||
});
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#include <CursorManager.h>
|
||||
#include <gl/GLWidget.h>
|
||||
#include <shared/NsightHelpers.h>
|
||||
#include <GeometryCache.h>
|
||||
#include <gpu/Context.h>
|
||||
#include <gpu/StandardShaderLib.h>
|
||||
#include <gpu/gl/GLBackend.h>
|
||||
|
@ -32,6 +31,9 @@
|
|||
|
||||
#include "../Logging.h"
|
||||
#include "../CompositorHelper.h"
|
||||
#include <../render-utils/shaders/render-utils/glowLine_vert.h>
|
||||
#include <../render-utils/shaders/render-utils/glowLine_frag.h>
|
||||
|
||||
|
||||
static const QString MONO_PREVIEW = "Mono Preview";
|
||||
static const QString DISABLE_PREVIEW = "Disable Preview";
|
||||
|
@ -47,6 +49,12 @@ static const size_t NUMBER_OF_HANDS = 2;
|
|||
//#define LIVE_SHADER_RELOAD 1
|
||||
extern glm::vec3 getPoint(float yaw, float pitch);
|
||||
|
||||
struct HandLaserData {
|
||||
vec4 p1;
|
||||
vec4 p2;
|
||||
vec4 color;
|
||||
};
|
||||
|
||||
static QString readFile(const QString& filename) {
|
||||
QFile file(filename);
|
||||
file.open(QFile::Text | QFile::ReadOnly);
|
||||
|
@ -112,11 +120,28 @@ void HmdDisplayPlugin::internalDeactivate() {
|
|||
void HmdDisplayPlugin::customizeContext() {
|
||||
Parent::customizeContext();
|
||||
_overlayRenderer.build();
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
for (size_t i = 0; i < _geometryIds.size(); ++i) {
|
||||
_geometryIds[i] = geometryCache->allocateID();
|
||||
}
|
||||
_extraLaserID = geometryCache->allocateID();
|
||||
|
||||
{
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
auto VS = gpu::Shader::createVertex(std::string(glowLine_vert));
|
||||
auto PS = gpu::Shader::createPixel(std::string(glowLine_frag));
|
||||
auto program = gpu::Shader::createProgram(VS, PS);
|
||||
state->setCullMode(gpu::State::CULL_NONE);
|
||||
state->setDepthTest(true, false, gpu::LESS_EQUAL);
|
||||
state->setBlendFunction(true,
|
||||
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
||||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
gpu::Shader::makeProgram(*program, gpu::Shader::BindingSet());
|
||||
_glowLinePipeline = gpu::Pipeline::create(program, state);
|
||||
for (const auto& buffer : program->getBuffers()) {
|
||||
if (buffer._name == "lineData") {
|
||||
_handLaserUniformSlot = buffer._location;
|
||||
}
|
||||
}
|
||||
_handLaserUniforms = std::array<gpu::BufferPointer, 2>{ { std::make_shared<gpu::Buffer>(), std::make_shared<gpu::Buffer>() } };
|
||||
_extraLaserUniforms = std::make_shared<gpu::Buffer>();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void HmdDisplayPlugin::uncustomizeContext() {
|
||||
|
@ -131,12 +156,10 @@ void HmdDisplayPlugin::uncustomizeContext() {
|
|||
});
|
||||
_overlayRenderer = OverlayRenderer();
|
||||
_previewTexture.reset();
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
for (size_t i = 0; i < _geometryIds.size(); ++i) {
|
||||
geometryCache->releaseID(_geometryIds[i]);
|
||||
}
|
||||
geometryCache->releaseID(_extraLaserID);
|
||||
_handLaserUniforms[0].reset();
|
||||
_handLaserUniforms[1].reset();
|
||||
_extraLaserUniforms.reset();
|
||||
_glowLinePipeline.reset();
|
||||
Parent::uncustomizeContext();
|
||||
}
|
||||
|
||||
|
@ -682,12 +705,16 @@ void HmdDisplayPlugin::compositeExtra() {
|
|||
if (_presentHandPoses[0] == IDENTITY_MATRIX && _presentHandPoses[1] == IDENTITY_MATRIX && !_presentExtraLaser.valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
||||
render([&](gpu::Batch& batch) {
|
||||
batch.setFramebuffer(_compositeFramebuffer);
|
||||
batch.setModelTransform(Transform());
|
||||
batch.setViewportTransform(ivec4(uvec2(0), _renderTargetSize));
|
||||
batch.setViewTransform(_currentPresentFrameInfo.presentPose, false);
|
||||
// Compile the shaders
|
||||
batch.setPipeline(_glowLinePipeline);
|
||||
|
||||
|
||||
bilateral::for_each_side([&](bilateral::Side side){
|
||||
auto index = bilateral::index(side);
|
||||
if (_presentHandPoses[index] == IDENTITY_MATRIX) {
|
||||
|
@ -696,13 +723,19 @@ void HmdDisplayPlugin::compositeExtra() {
|
|||
const auto& laser = _presentHandLasers[index];
|
||||
if (laser.valid()) {
|
||||
const auto& points = _presentHandLaserPoints[index];
|
||||
geometryCache->renderGlowLine(batch, points.first, points.second, laser.color, _geometryIds[index]);
|
||||
_handLaserUniforms[index]->resize(sizeof(HandLaserData));
|
||||
_handLaserUniforms[index]->setSubData(0, HandLaserData { vec4(points.first, 1.0f), vec4(points.second, 1.0f), _handLasers[index].color });
|
||||
batch.setUniformBuffer(_handLaserUniformSlot, _handLaserUniforms[index]);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4, 0);
|
||||
}
|
||||
});
|
||||
|
||||
if (_presentExtraLaser.valid()) {
|
||||
const auto& points = _presentExtraLaserPoints;
|
||||
geometryCache->renderGlowLine(batch, points.first, points.second, _presentExtraLaser.color, _extraLaserID);
|
||||
_extraLaserUniforms->resize(sizeof(HandLaserData));
|
||||
_extraLaserUniforms->setSubData(0, HandLaserData { vec4(points.first, 1.0f), vec4(points.second, 1.0f), _presentExtraLaser.color });
|
||||
batch.setUniformBuffer(_handLaserUniformSlot, _extraLaserUniforms);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4, 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -80,8 +80,6 @@ protected:
|
|||
|
||||
Transform _presentUiModelTransform;
|
||||
std::array<HandLaserInfo, 2> _presentHandLasers;
|
||||
std::array<int, 2> _geometryIds;
|
||||
int _extraLaserID;
|
||||
std::array<mat4, 2> _presentHandPoses;
|
||||
std::array<std::pair<vec3, vec3>, 2> _presentHandLaserPoints;
|
||||
|
||||
|
@ -120,6 +118,10 @@ private:
|
|||
bool _disablePreviewItemAdded { false };
|
||||
bool _monoPreview { true };
|
||||
bool _clearPreviewFlag { false };
|
||||
std::array<gpu::BufferPointer, 2> _handLaserUniforms;
|
||||
uint32_t _handLaserUniformSlot { 0 };
|
||||
gpu::BufferPointer _extraLaserUniforms;
|
||||
gpu::PipelinePointer _glowLinePipeline;
|
||||
gpu::TexturePointer _previewTexture;
|
||||
glm::vec2 _lastWindowSize;
|
||||
|
||||
|
|
|
@ -580,6 +580,11 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) {
|
|||
voxelVolumeSize = _voxelVolumeSize;
|
||||
});
|
||||
|
||||
if (!mesh ||
|
||||
!mesh->getIndexBuffer()._buffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_pipeline) {
|
||||
gpu::ShaderPointer vertexShader = gpu::Shader::createVertex(std::string(polyvox_vert));
|
||||
gpu::ShaderPointer pixelShader = gpu::Shader::createPixel(std::string(polyvox_frag));
|
||||
|
@ -600,17 +605,23 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) {
|
|||
_pipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
|
||||
if (!_vertexFormat) {
|
||||
auto vf = std::make_shared<gpu::Stream::Format>();
|
||||
vf->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0);
|
||||
vf->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 12);
|
||||
_vertexFormat = vf;
|
||||
}
|
||||
|
||||
gpu::Batch& batch = *args->_batch;
|
||||
batch.setPipeline(_pipeline);
|
||||
|
||||
Transform transform(voxelToWorldMatrix());
|
||||
batch.setModelTransform(transform);
|
||||
batch.setInputFormat(mesh->getVertexFormat());
|
||||
batch.setInputBuffer(gpu::Stream::POSITION, mesh->getVertexBuffer());
|
||||
batch.setInputBuffer(gpu::Stream::NORMAL,
|
||||
mesh->getVertexBuffer()._buffer,
|
||||
sizeof(float) * 3,
|
||||
mesh->getVertexBuffer()._stride);
|
||||
batch.setInputFormat(_vertexFormat);
|
||||
batch.setInputBuffer(gpu::Stream::POSITION, mesh->getVertexBuffer()._buffer,
|
||||
0,
|
||||
sizeof(PolyVox::PositionMaterialNormal));
|
||||
|
||||
batch.setIndexBuffer(gpu::UINT32, mesh->getIndexBuffer()._buffer, 0);
|
||||
|
||||
if (!_xTextureURL.isEmpty() && !_xTexture) {
|
||||
|
@ -1097,7 +1108,6 @@ void RenderablePolyVoxEntityItem::getMesh() {
|
|||
|
||||
auto entity = std::static_pointer_cast<RenderablePolyVoxEntityItem>(getThisPointer());
|
||||
|
||||
|
||||
QtConcurrent::run([entity, voxelSurfaceStyle] {
|
||||
model::MeshPointer mesh(new model::Mesh());
|
||||
|
||||
|
@ -1146,18 +1156,14 @@ void RenderablePolyVoxEntityItem::getMesh() {
|
|||
auto vertexBuffer = std::make_shared<gpu::Buffer>(vecVertices.size() * sizeof(PolyVox::PositionMaterialNormal),
|
||||
(gpu::Byte*)vecVertices.data());
|
||||
auto vertexBufferPtr = gpu::BufferPointer(vertexBuffer);
|
||||
gpu::Resource::Size vertexBufferSize = 0;
|
||||
if (vertexBufferPtr->getSize() > sizeof(float) * 3) {
|
||||
vertexBufferSize = vertexBufferPtr->getSize() - sizeof(float) * 3;
|
||||
}
|
||||
gpu::BufferView vertexBufferView(vertexBufferPtr, 0, vertexBufferSize,
|
||||
gpu::BufferView vertexBufferView(vertexBufferPtr, 0,
|
||||
vertexBufferPtr->getSize(),
|
||||
sizeof(PolyVox::PositionMaterialNormal),
|
||||
gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RAW));
|
||||
mesh->setVertexBuffer(vertexBufferView);
|
||||
mesh->addAttribute(gpu::Stream::NORMAL,
|
||||
gpu::BufferView(vertexBufferPtr,
|
||||
sizeof(float) * 3,
|
||||
vertexBufferPtr->getSize() - sizeof(float) * 3,
|
||||
gpu::BufferView(vertexBufferPtr, sizeof(float) * 3,
|
||||
vertexBufferPtr->getSize() ,
|
||||
sizeof(PolyVox::PositionMaterialNormal),
|
||||
gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RAW)));
|
||||
entity->setMesh(mesh);
|
||||
|
|
|
@ -149,6 +149,7 @@ private:
|
|||
// may not match _voxelVolumeSize.
|
||||
|
||||
model::MeshPointer _mesh;
|
||||
gpu::Stream::FormatPointer _vertexFormat;
|
||||
bool _meshDirty { true }; // does collision-shape need to be recomputed?
|
||||
bool _meshInitialized { false };
|
||||
|
||||
|
|
|
@ -64,6 +64,7 @@ Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket,
|
|||
{
|
||||
// Update socket's object name
|
||||
setType(_type);
|
||||
_ignoreRadiusEnabled = false;
|
||||
}
|
||||
|
||||
void Node::setType(char type) {
|
||||
|
@ -101,6 +102,15 @@ void Node::addIgnoredNode(const QUuid& otherNodeID) {
|
|||
}
|
||||
}
|
||||
|
||||
void Node::parseIgnoreRadiusRequestMessage(QSharedPointer<ReceivedMessage> message) {
|
||||
bool enabled;
|
||||
float radius;
|
||||
message->readPrimitive(&enabled);
|
||||
message->readPrimitive(&radius);
|
||||
_ignoreRadiusEnabled = enabled;
|
||||
_ignoreRadius = radius;
|
||||
}
|
||||
|
||||
QDataStream& operator<<(QDataStream& out, const Node& node) {
|
||||
out << node._type;
|
||||
out << node._uuid;
|
||||
|
|
|
@ -74,10 +74,14 @@ public:
|
|||
void parseIgnoreRequestMessage(QSharedPointer<ReceivedMessage> message);
|
||||
void addIgnoredNode(const QUuid& otherNodeID);
|
||||
bool isIgnoringNodeWithID(const QUuid& nodeID) const { return _ignoredNodeIDSet.find(nodeID) != _ignoredNodeIDSet.cend(); }
|
||||
void parseIgnoreRadiusRequestMessage(QSharedPointer<ReceivedMessage> message);
|
||||
|
||||
friend QDataStream& operator<<(QDataStream& out, const Node& node);
|
||||
friend QDataStream& operator>>(QDataStream& in, Node& node);
|
||||
|
||||
bool isIgnoreRadiusEnabled() const { return _ignoreRadiusEnabled; }
|
||||
float getIgnoreRadius() { return _ignoreRadiusEnabled ? _ignoreRadius.load() : std::numeric_limits<float>::max(); }
|
||||
|
||||
private:
|
||||
// privatize copy and assignment operator to disallow Node copying
|
||||
Node(const Node &otherNode);
|
||||
|
@ -94,6 +98,9 @@ private:
|
|||
MovingPercentile _clockSkewMovingPercentile;
|
||||
NodePermissions _permissions;
|
||||
tbb::concurrent_unordered_set<QUuid, UUIDHasher> _ignoredNodeIDSet;
|
||||
|
||||
std::atomic_bool _ignoreRadiusEnabled;
|
||||
std::atomic<float> _ignoreRadius { 0.0f };
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(Node*)
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <QtCore/QUrl>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtNetwork/QHostInfo>
|
||||
#include <QtNetwork/QNetworkInterface>
|
||||
|
||||
#include <LogHandler.h>
|
||||
#include <UUID.h>
|
||||
|
@ -346,6 +347,28 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
// include the protocol version signature in our connect request
|
||||
QByteArray protocolVersionSig = protocolVersionsSignature();
|
||||
packetStream.writeBytes(protocolVersionSig.constData(), protocolVersionSig.size());
|
||||
|
||||
// if possible, include the MAC address for the current interface in our connect request
|
||||
QString hardwareAddress;
|
||||
|
||||
for (auto networkInterface : QNetworkInterface::allInterfaces()) {
|
||||
for (auto interfaceAddress : networkInterface.addressEntries()) {
|
||||
if (interfaceAddress.ip() == _localSockAddr.getAddress()) {
|
||||
// this is the interface whose local IP matches what we've detected the current IP to be
|
||||
hardwareAddress = networkInterface.hardwareAddress();
|
||||
|
||||
// stop checking interfaces and addresses
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// stop looping if this was the current interface
|
||||
if (!hardwareAddress.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
packetStream << hardwareAddress;
|
||||
}
|
||||
|
||||
// pack our data to send to the domain-server including
|
||||
|
@ -727,9 +750,26 @@ bool NodeList::sockAddrBelongsToDomainOrNode(const HifiSockAddr& sockAddr) {
|
|||
return _domainHandler.getSockAddr() == sockAddr || LimitedNodeList::sockAddrBelongsToNode(sockAddr);
|
||||
}
|
||||
|
||||
void NodeList::ignoreNodesInRadius(float radiusToIgnore, bool enabled) {
|
||||
_ignoreRadiusEnabled.set(enabled);
|
||||
_ignoreRadius.set(radiusToIgnore);
|
||||
|
||||
eachMatchingNode([](const SharedNodePointer& node)->bool {
|
||||
return (node->getType() == NodeType::AudioMixer || node->getType() == NodeType::AvatarMixer);
|
||||
}, [this](const SharedNodePointer& destinationNode) {
|
||||
sendIgnoreRadiusStateToNode(destinationNode);
|
||||
});
|
||||
}
|
||||
|
||||
void NodeList::sendIgnoreRadiusStateToNode(const SharedNodePointer& destinationNode) {
|
||||
auto ignorePacket = NLPacket::create(PacketType::RadiusIgnoreRequest, sizeof(bool) + sizeof(float), true);
|
||||
ignorePacket->writePrimitive(_ignoreRadiusEnabled.get());
|
||||
ignorePacket->writePrimitive(_ignoreRadius.get());
|
||||
sendPacket(std::move(ignorePacket), *destinationNode);
|
||||
}
|
||||
|
||||
void NodeList::ignoreNodeBySessionID(const QUuid& nodeID) {
|
||||
// enumerate the nodes to send a reliable ignore packet to each that can leverage it
|
||||
|
||||
if (!nodeID.isNull() && _sessionUUID != nodeID) {
|
||||
eachMatchingNode([&nodeID](const SharedNodePointer& node)->bool {
|
||||
if (node->getType() == NodeType::AudioMixer || node->getType() == NodeType::AvatarMixer) {
|
||||
|
@ -788,6 +828,9 @@ void NodeList::maybeSendIgnoreSetToNode(SharedNodePointer newNode) {
|
|||
// send this NLPacketList to the new node
|
||||
sendPacketList(std::move(ignorePacketList), *newNode);
|
||||
}
|
||||
|
||||
// also send them the current ignore radius state.
|
||||
sendIgnoreRadiusStateToNode(newNode);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include <QtNetwork/QUdpSocket>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
#include <SettingHandle.h>
|
||||
|
||||
#include "DomainHandler.h"
|
||||
#include "LimitedNodeList.h"
|
||||
|
@ -70,6 +71,12 @@ public:
|
|||
|
||||
void setIsShuttingDown(bool isShuttingDown) { _isShuttingDown = isShuttingDown; }
|
||||
|
||||
void ignoreNodesInRadius(float radiusToIgnore, bool enabled = true);
|
||||
float getIgnoreRadius() const { return _ignoreRadius.get(); }
|
||||
bool getIgnoreRadiusEnabled() const { return _ignoreRadiusEnabled.get(); }
|
||||
void toggleIgnoreRadius() { ignoreNodesInRadius(getIgnoreRadius(), !getIgnoreRadiusEnabled()); }
|
||||
void enableIgnoreRadius() { ignoreNodesInRadius(getIgnoreRadius(), true); }
|
||||
void disableIgnoreRadius() { ignoreNodesInRadius(getIgnoreRadius(), false); }
|
||||
void ignoreNodeBySessionID(const QUuid& nodeID);
|
||||
bool isIgnoringNode(const QUuid& nodeID) const;
|
||||
|
||||
|
@ -101,7 +108,7 @@ signals:
|
|||
void limitOfSilentDomainCheckInsReached();
|
||||
void receivedDomainServerList();
|
||||
void ignoredNode(const QUuid& nodeID);
|
||||
|
||||
|
||||
private slots:
|
||||
void stopKeepalivePingTimer();
|
||||
void sendPendingDSPathQuery();
|
||||
|
@ -146,6 +153,10 @@ private:
|
|||
mutable QReadWriteLock _ignoredSetLock;
|
||||
tbb::concurrent_unordered_set<QUuid, UUIDHasher> _ignoredNodeIDs;
|
||||
|
||||
void sendIgnoreRadiusStateToNode(const SharedNodePointer& destinationNode);
|
||||
Setting::Handle<bool> _ignoreRadiusEnabled { "IgnoreRadiusEnabled", false };
|
||||
Setting::Handle<float> _ignoreRadius { "IgnoreRadius", 1.0f };
|
||||
|
||||
#if (PR_BUILD || DEV_BUILD)
|
||||
bool _shouldSendNewerVersion { false };
|
||||
#endif
|
||||
|
|
|
@ -67,7 +67,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
return static_cast<PacketVersion>(DomainConnectionDeniedVersion::IncludesExtraInfo);
|
||||
|
||||
case PacketType::DomainConnectRequest:
|
||||
return static_cast<PacketVersion>(DomainConnectRequestVersion::HasProtocolVersions);
|
||||
return static_cast<PacketVersion>(DomainConnectRequestVersion::HasMACAddress);
|
||||
|
||||
case PacketType::DomainServerAddedNode:
|
||||
return static_cast<PacketVersion>(DomainServerAddedNodeVersion::PermissionsGrid);
|
||||
|
|
|
@ -100,7 +100,8 @@ public:
|
|||
MoreEntityShapes,
|
||||
NodeKickRequest,
|
||||
NodeMuteRequest,
|
||||
LAST_PACKET_TYPE = NodeMuteRequest
|
||||
RadiusIgnoreRequest,
|
||||
LAST_PACKET_TYPE = RadiusIgnoreRequest
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -207,7 +208,8 @@ enum class AvatarMixerPacketVersion : PacketVersion {
|
|||
enum class DomainConnectRequestVersion : PacketVersion {
|
||||
NoHostname = 17,
|
||||
HasHostname,
|
||||
HasProtocolVersions
|
||||
HasProtocolVersions,
|
||||
HasMACAddress
|
||||
};
|
||||
|
||||
enum class DomainConnectionDeniedVersion : PacketVersion {
|
||||
|
|
|
@ -38,7 +38,6 @@
|
|||
#include "simple_opaque_web_browser_frag.h"
|
||||
#include "simple_transparent_web_browser_frag.h"
|
||||
#include "glowLine_vert.h"
|
||||
#include "glowLine_geom.h"
|
||||
#include "glowLine_frag.h"
|
||||
|
||||
#include "grid_frag.h"
|
||||
|
@ -1405,6 +1404,7 @@ GeometryCache::BatchItemDetails::~BatchItemDetails() {
|
|||
|
||||
void GeometryCache::BatchItemDetails::clear() {
|
||||
isCreated = false;
|
||||
uniformBuffer.reset();
|
||||
verticesBuffer.reset();
|
||||
colorBuffer.reset();
|
||||
streamFormat.reset();
|
||||
|
@ -1593,8 +1593,6 @@ void GeometryCache::renderGlowLine(gpu::Batch& batch, const glm::vec3& p1, const
|
|||
glowIntensity = 0.0f;
|
||||
#endif
|
||||
|
||||
glowIntensity = 0.0f;
|
||||
|
||||
if (glowIntensity <= 0) {
|
||||
bindSimpleProgram(batch, false, false, false, true, false);
|
||||
renderLine(batch, p1, p2, color, id);
|
||||
|
@ -1602,20 +1600,20 @@ void GeometryCache::renderGlowLine(gpu::Batch& batch, const glm::vec3& p1, const
|
|||
}
|
||||
|
||||
// Compile the shaders
|
||||
static const uint32_t LINE_DATA_SLOT = 1;
|
||||
static std::once_flag once;
|
||||
std::call_once(once, [&] {
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
auto VS = gpu::Shader::createVertex(std::string(glowLine_vert));
|
||||
auto GS = gpu::Shader::createGeometry(std::string(glowLine_geom));
|
||||
auto PS = gpu::Shader::createPixel(std::string(glowLine_frag));
|
||||
auto program = gpu::Shader::createProgram(VS, GS, PS);
|
||||
auto program = gpu::Shader::createProgram(VS, PS);
|
||||
state->setCullMode(gpu::State::CULL_NONE);
|
||||
state->setDepthTest(true, false, gpu::LESS_EQUAL);
|
||||
state->setBlendFunction(true,
|
||||
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
||||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("normalFittingMap"), render::ShapePipeline::Slot::MAP::NORMAL_FITTING));
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("lineData"), LINE_DATA_SLOT));
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
_glowLinePipeline = gpu::Pipeline::create(program, state);
|
||||
});
|
||||
|
@ -1626,11 +1624,6 @@ void GeometryCache::renderGlowLine(gpu::Batch& batch, const glm::vec3& p1, const
|
|||
bool registered = (id != UNKNOWN_ID);
|
||||
BatchItemDetails& details = _registeredLine3DVBOs[id];
|
||||
|
||||
int compactColor = ((int(color.x * 255.0f) & 0xFF)) |
|
||||
((int(color.y * 255.0f) & 0xFF) << 8) |
|
||||
((int(color.z * 255.0f) & 0xFF) << 16) |
|
||||
((int(color.w * 255.0f) & 0xFF) << 24);
|
||||
|
||||
// if this is a registered quad, and we have buffers, then check to see if the geometry changed and rebuild if needed
|
||||
if (registered && details.isCreated) {
|
||||
Vec3Pair& lastKey = _lastRegisteredLine3D[id];
|
||||
|
@ -1640,47 +1633,25 @@ void GeometryCache::renderGlowLine(gpu::Batch& batch, const glm::vec3& p1, const
|
|||
}
|
||||
}
|
||||
|
||||
const int FLOATS_PER_VERTEX = 3 + 3; // vertices + normals
|
||||
const int NUM_POS_COORDS = 3;
|
||||
const int VERTEX_NORMAL_OFFSET = NUM_POS_COORDS * sizeof(float);
|
||||
const int vertices = 2;
|
||||
const int NUM_VERTICES = 4;
|
||||
if (!details.isCreated) {
|
||||
details.isCreated = true;
|
||||
details.vertices = vertices;
|
||||
details.vertexSize = FLOATS_PER_VERTEX;
|
||||
details.uniformBuffer = std::make_shared<gpu::Buffer>();
|
||||
|
||||
auto verticesBuffer = std::make_shared<gpu::Buffer>();
|
||||
auto colorBuffer = std::make_shared<gpu::Buffer>();
|
||||
auto streamFormat = std::make_shared<gpu::Stream::Format>();
|
||||
auto stream = std::make_shared<gpu::BufferStream>();
|
||||
struct LineData {
|
||||
vec4 p1;
|
||||
vec4 p2;
|
||||
vec4 color;
|
||||
};
|
||||
|
||||
details.verticesBuffer = verticesBuffer;
|
||||
details.colorBuffer = colorBuffer;
|
||||
details.streamFormat = streamFormat;
|
||||
details.stream = stream;
|
||||
|
||||
details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0);
|
||||
details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET);
|
||||
details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA));
|
||||
|
||||
details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride);
|
||||
details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride);
|
||||
|
||||
const glm::vec3 NORMAL(1.0f, 0.0f, 0.0f);
|
||||
float vertexBuffer[vertices * FLOATS_PER_VERTEX] = {
|
||||
p1.x, p1.y, p1.z, NORMAL.x, NORMAL.y, NORMAL.z,
|
||||
p2.x, p2.y, p2.z, NORMAL.x, NORMAL.y, NORMAL.z };
|
||||
|
||||
const int NUM_COLOR_SCALARS = 2;
|
||||
int colors[NUM_COLOR_SCALARS] = { compactColor, compactColor };
|
||||
details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer);
|
||||
details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors);
|
||||
LineData lineData { vec4(p1, 1.0f), vec4(p2, 1.0f), color };
|
||||
details.uniformBuffer->resize(sizeof(LineData));
|
||||
details.uniformBuffer->setSubData(0, lineData);
|
||||
}
|
||||
|
||||
// this is what it takes to render a quad
|
||||
batch.setInputFormat(details.streamFormat);
|
||||
batch.setInputStream(0, *details.stream);
|
||||
batch.draw(gpu::LINES, 2, 0);
|
||||
// The shader requires no vertices, only uniforms.
|
||||
batch.setUniformBuffer(LINE_DATA_SLOT, details.uniformBuffer);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, NUM_VERTICES, 0);
|
||||
}
|
||||
|
||||
void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) {
|
||||
|
|
|
@ -369,6 +369,7 @@ private:
|
|||
static int population;
|
||||
gpu::BufferPointer verticesBuffer;
|
||||
gpu::BufferPointer colorBuffer;
|
||||
gpu::BufferPointer uniformBuffer;
|
||||
gpu::Stream::FormatPointer streamFormat;
|
||||
gpu::BufferStreamPointer stream;
|
||||
|
||||
|
|
|
@ -10,26 +10,24 @@
|
|||
//
|
||||
|
||||
layout(location = 0) in vec4 inColor;
|
||||
layout(location = 1) in vec3 inLineDistance;
|
||||
|
||||
out vec4 _fragColor;
|
||||
|
||||
void main(void) {
|
||||
vec2 d = inLineDistance.xy;
|
||||
d.y = abs(d.y);
|
||||
d.x = abs(d.x);
|
||||
if (d.x > 1.0) {
|
||||
d.x = (d.x - 1.0) / 0.02;
|
||||
} else {
|
||||
d.x = 0.0;
|
||||
}
|
||||
float alpha = 1.0 - length(d);
|
||||
if (alpha <= 0.0) {
|
||||
discard;
|
||||
}
|
||||
alpha = pow(alpha, 10.0);
|
||||
if (alpha < 0.05) {
|
||||
// The incoming value actually ranges from -1 to 1, so modify it
|
||||
// so that it goes from 0 -> 1 -> 0 with the solid alpha being at
|
||||
// the center of the line
|
||||
float alpha = 1.0 - abs(inColor.a);
|
||||
|
||||
// Convert from a linear alpha curve to a sharp peaked one
|
||||
alpha = pow(alpha, 10);
|
||||
|
||||
// Drop everything where the curve falls off to nearly nothing
|
||||
if (alpha <= 0.05) {
|
||||
discard;
|
||||
}
|
||||
|
||||
// Emit the color
|
||||
_fragColor = vec4(inColor.rgb, alpha);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1,102 +0,0 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
//
|
||||
// Created by Bradley Austin Davis on 2016/07/05
|
||||
// Copyright 2013-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
|
||||
//
|
||||
#extension GL_EXT_geometry_shader4 : enable
|
||||
|
||||
<@include gpu/Transform.slh@>
|
||||
<$declareStandardCameraTransform()$>
|
||||
|
||||
layout(location = 0) in vec4 inColor[];
|
||||
|
||||
layout(location = 0) out vec4 outColor;
|
||||
layout(location = 1) out vec3 outLineDistance;
|
||||
|
||||
layout(lines) in;
|
||||
layout(triangle_strip, max_vertices = 24) out;
|
||||
|
||||
vec3 ndcToEyeSpace(in vec4 v) {
|
||||
TransformCamera cam = getTransformCamera();
|
||||
vec4 u = cam._projectionInverse * v;
|
||||
return u.xyz / u.w;
|
||||
}
|
||||
|
||||
vec2 toScreenSpace(in vec4 v)
|
||||
{
|
||||
TransformCamera cam = getTransformCamera();
|
||||
vec4 u = cam._projection * cam._view * v;
|
||||
return u.xy / u.w;
|
||||
}
|
||||
|
||||
vec3[2] getOrthogonals(in vec3 n, float scale) {
|
||||
float yDot = abs(dot(n, vec3(0, 1, 0)));
|
||||
|
||||
vec3 result[2];
|
||||
if (yDot < 0.9) {
|
||||
result[0] = normalize(cross(n, vec3(0, 1, 0)));
|
||||
} else {
|
||||
result[0] = normalize(cross(n, vec3(1, 0, 0)));
|
||||
}
|
||||
// The cross of result[0] and n is orthogonal to both, which are orthogonal to each other
|
||||
result[1] = cross(result[0], n);
|
||||
result[0] *= scale;
|
||||
result[1] *= scale;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
vec2 orthogonal(vec2 v) {
|
||||
vec2 result = v.yx;
|
||||
result.y *= -1.0;
|
||||
return result;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 endpoints[2];
|
||||
vec3 eyeSpace[2];
|
||||
TransformCamera cam = getTransformCamera();
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
eyeSpace[i] = ndcToEyeSpace(gl_PositionIn[i]);
|
||||
endpoints[i] = gl_PositionIn[i].xy / gl_PositionIn[i].w;
|
||||
}
|
||||
vec2 lineNormal = normalize(endpoints[1] - endpoints[0]);
|
||||
vec2 lineOrthogonal = orthogonal(lineNormal);
|
||||
lineNormal *= 0.02;
|
||||
lineOrthogonal *= 0.02;
|
||||
|
||||
gl_Position = gl_PositionIn[0];
|
||||
gl_Position.xy -= lineOrthogonal;
|
||||
outColor = inColor[0];
|
||||
outLineDistance = vec3(-1.02, -1, gl_Position.z);
|
||||
EmitVertex();
|
||||
|
||||
gl_Position = gl_PositionIn[0];
|
||||
gl_Position.xy += lineOrthogonal;
|
||||
outColor = inColor[0];
|
||||
outLineDistance = vec3(-1.02, 1, gl_Position.z);
|
||||
EmitVertex();
|
||||
|
||||
gl_Position = gl_PositionIn[1];
|
||||
gl_Position.xy -= lineOrthogonal;
|
||||
outColor = inColor[1];
|
||||
outLineDistance = vec3(1.02, -1, gl_Position.z);
|
||||
EmitVertex();
|
||||
|
||||
gl_Position = gl_PositionIn[1];
|
||||
gl_Position.xy += lineOrthogonal;
|
||||
outColor = inColor[1];
|
||||
outLineDistance = vec3(1.02, 1, gl_Position.z);
|
||||
EmitVertex();
|
||||
|
||||
EndPrimitive();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -9,18 +9,50 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
<@include gpu/Inputs.slh@>
|
||||
<@include gpu/Color.slh@>
|
||||
<@include gpu/Transform.slh@>
|
||||
<$declareStandardTransform()$>
|
||||
|
||||
layout(std140) uniform lineData {
|
||||
vec4 p1;
|
||||
vec4 p2;
|
||||
vec4 color;
|
||||
};
|
||||
|
||||
layout(location = 0) out vec4 _color;
|
||||
|
||||
void main(void) {
|
||||
_color = inColor;
|
||||
_color = color;
|
||||
|
||||
// standard transform
|
||||
TransformCamera cam = getTransformCamera();
|
||||
TransformObject obj = getTransformObject();
|
||||
<$transformModelToClipPos(cam, obj, inPosition, gl_Position)$>
|
||||
|
||||
vec4 p1eye, p2eye;
|
||||
<$transformModelToEyePos(cam, obj, p1, p1eye)$>
|
||||
<$transformModelToEyePos(cam, obj, p2, p2eye)$>
|
||||
p1eye /= p1eye.w;
|
||||
p2eye /= p2eye.w;
|
||||
|
||||
// Find the line direction
|
||||
vec3 v1 = normalize(p1eye.xyz - p2eye.xyz);
|
||||
// Find the vector from the eye to one of the points
|
||||
vec3 v2 = normalize(p1eye.xyz);
|
||||
// The orthogonal vector is the cross product of these two
|
||||
vec3 orthogonal = cross(v1, v2) * 0.02;
|
||||
|
||||
// Deteremine which end to emit based on the vertex id (even / odd)
|
||||
vec4 eye = (0 == gl_VertexID % 2) ? p1eye : p2eye;
|
||||
|
||||
// Add or subtract the orthogonal vector based on a different vertex ID
|
||||
// calculation
|
||||
if (gl_VertexID < 2) {
|
||||
// Use the alpha channel to store the distance from the center in 'quad space'
|
||||
_color.a = -1.0;
|
||||
eye.xyz -= orthogonal;
|
||||
} else {
|
||||
_color.a = 1.0;
|
||||
eye.xyz += orthogonal;
|
||||
}
|
||||
|
||||
// Finally, put the eyespace vertex into clip space
|
||||
<$transformEyeToClipPos(cam, eye, gl_Position)$>
|
||||
}
|
|
@ -38,3 +38,27 @@ bool UsersScriptingInterface::getCanKick() {
|
|||
// ask the NodeList to return our ability to kick
|
||||
return DependencyManager::get<NodeList>()->getThisNodeCanKick();
|
||||
}
|
||||
|
||||
void UsersScriptingInterface::toggleIgnoreRadius() {
|
||||
DependencyManager::get<NodeList>()->toggleIgnoreRadius();
|
||||
}
|
||||
|
||||
void UsersScriptingInterface::enableIgnoreRadius() {
|
||||
DependencyManager::get<NodeList>()->enableIgnoreRadius();
|
||||
}
|
||||
|
||||
void UsersScriptingInterface::disableIgnoreRadius() {
|
||||
DependencyManager::get<NodeList>()->disableIgnoreRadius();
|
||||
}
|
||||
|
||||
void UsersScriptingInterface::setIgnoreRadius(float radius, bool enabled) {
|
||||
DependencyManager::get<NodeList>()->ignoreNodesInRadius(radius, enabled);
|
||||
}
|
||||
|
||||
float UsersScriptingInterface::getIgnoreRadius() {
|
||||
return DependencyManager::get<NodeList>()->getIgnoreRadius();
|
||||
}
|
||||
|
||||
bool UsersScriptingInterface::getIgnoreRadiusEnabled() {
|
||||
return DependencyManager::get<NodeList>()->getIgnoreRadiusEnabled();
|
||||
}
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
|
||||
#include <DependencyManager.h>
|
||||
|
||||
/**jsdoc
|
||||
* @namespace Users
|
||||
*/
|
||||
class UsersScriptingInterface : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
@ -26,12 +29,75 @@ public:
|
|||
UsersScriptingInterface();
|
||||
|
||||
public slots:
|
||||
|
||||
/**jsdoc
|
||||
* Ignore another user.
|
||||
* @function Users.ignore
|
||||
* @param {nodeID} nodeID The node or session ID of the user you want to ignore.
|
||||
*/
|
||||
void ignore(const QUuid& nodeID);
|
||||
|
||||
/**jsdoc
|
||||
* Kick another user.
|
||||
* @function Users.kick
|
||||
* @param {nodeID} nodeID The node or session ID of the user you want to kick.
|
||||
*/
|
||||
void kick(const QUuid& nodeID);
|
||||
|
||||
/**jsdoc
|
||||
* Mute another user.
|
||||
* @function Users.mute
|
||||
* @param {nodeID} nodeID The node or session ID of the user you want to mute.
|
||||
*/
|
||||
void mute(const QUuid& nodeID);
|
||||
|
||||
/**jsdoc
|
||||
* Returns `true` if the DomainServer will allow this Node/Avatar to make kick
|
||||
* @function Users.getCanKick
|
||||
* @return {bool} `true` if the client can kick other users, `false` if not.
|
||||
*/
|
||||
bool getCanKick();
|
||||
|
||||
/**jsdoc
|
||||
* Toggle the state of the ignore in radius feature
|
||||
* @function Users.toggleIgnoreRadius
|
||||
*/
|
||||
void toggleIgnoreRadius();
|
||||
|
||||
/**jsdoc
|
||||
* Enables the ignore radius feature.
|
||||
* @function Users.enableIgnoreRadius
|
||||
*/
|
||||
void enableIgnoreRadius();
|
||||
|
||||
/**jsdoc
|
||||
* Disables the ignore radius feature.
|
||||
* @function Users.disableIgnoreRadius
|
||||
*/
|
||||
void disableIgnoreRadius();
|
||||
|
||||
/**jsdoc
|
||||
* sets the parameters for the ignore radius feature.
|
||||
* @function Users.setIgnoreRadius
|
||||
* @param {number} radius The radius for the auto ignore in radius feature
|
||||
* @param {bool} [enabled=true] Whether the ignore in radius feature should be enabled
|
||||
*/
|
||||
void setIgnoreRadius(float radius, bool enabled = true);
|
||||
|
||||
/**jsdoc
|
||||
* Returns the effective radius of the ingore radius feature if it is enabled.
|
||||
* @function Users.getIgnoreRadius
|
||||
* @return {number} radius of the ignore feature
|
||||
*/
|
||||
float getIgnoreRadius();
|
||||
|
||||
/**jsdoc
|
||||
* Returns `true` if the ignore in radius feature is enabled
|
||||
* @function Users.getIgnoreRadiusEnabled
|
||||
* @return {bool} `true` if the ignore in radius feature is enabled, `false` if not.
|
||||
*/
|
||||
bool getIgnoreRadiusEnabled();
|
||||
|
||||
signals:
|
||||
void canKickChanged(bool canKick);
|
||||
};
|
||||
|
|
|
@ -33,7 +33,8 @@ var DEFAULT_SCRIPTS = [
|
|||
"system/dialTone.js",
|
||||
"system/firstPersonHMD.js",
|
||||
"system/snapshot.js",
|
||||
"system/help.js"
|
||||
"system/help.js",
|
||||
"system/bubble.js"
|
||||
];
|
||||
|
||||
// add a menu item for debugging
|
||||
|
|
275
scripts/system/assets/images/tools/bubble.svg
Normal file
275
scripts/system/assets/images/tools/bubble.svg
Normal file
|
@ -0,0 +1,275 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 50 200.1" style="enable-background:new 0 0 50 200.1;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#414042;}
|
||||
.st1{fill:#FFFFFF;}
|
||||
.st2{fill:#1E1E1E;}
|
||||
.st3{fill:#333333;}
|
||||
</style>
|
||||
<g id="Layer_2">
|
||||
<g>
|
||||
<g>
|
||||
<path class="st0" d="M50.1,146.1c0,2.2-1.8,4-4,4h-42c-2.2,0-4-1.8-4-4v-42c0-2.2,1.8-4,4-4h42c2.2,0,4,1.8,4,4V146.1z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st0" d="M50,196.1c0,2.2-1.8,4-4,4H4c-2.2,0-4-1.8-4-4v-42c0-2.2,1.8-4,4-4h42c2.2,0,4,1.8,4,4V196.1z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st1" d="M50,46c0,2.2-1.8,4-4,4H4c-2.2,0-4-1.8-4-4V4c0-2.2,1.8-4,4-4h42c2.2,0,4,1.8,4,4V46z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st2" d="M50,96.1c0,2.2-1.8,4-4,4H4c-2.2,0-4-1.8-4-4v-42c0-2.2,1.8-4,4-4h42c2.2,0,4,1.8,4,4V96.1z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="Layer_3">
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M24.3,19.6c-0.5,0.5-0.9,1-1.3,1.5c-0.1,0.1-0.1,0.3-0.1,0.4c0,1,0,2,0.1,3c0,0.4-0.2,0.7-0.7,0.8
|
||||
c-0.4,0-0.8-0.2-0.8-0.7c-0.1-1.3-0.1-2.5-0.2-3.8c0-0.2,0.1-0.4,0.2-0.6c0.7-0.9,1.4-1.7,2-2.6c0.3-0.4,1-0.9,1.5-0.9
|
||||
c0.2,0,0.6,0,0.8,0c0.5,0,0.8,0.1,1.1,0.6c0.5,0.7,0.9,1.4,1.4,2.1c0.4,0.7,0.8,1,1.7,1.2c0.6,0.1,1.3,0.3,1.9,0.5
|
||||
c0.2,0,0.3,0.1,0.5,0.2c0.3,0.2,0.4,0.5,0.3,0.8c-0.1,0.3-0.3,0.4-0.6,0.4c-0.2,0-0.5,0-0.7-0.1c-0.8-0.2-1.6-0.4-2.4-0.6
|
||||
c-0.5-0.1-0.8-0.4-1.2-0.7c-0.2-0.2-0.3-0.3-0.5-0.5c0,0.2,0,0.3,0,0.4c0,0.7,0,1.4-0.1,2.1c0,0.2-0.2,6.4-0.3,8.9
|
||||
c0,0.3-0.1,0.6-0.2,0.9c-0.1,0.4-0.4,0.6-0.9,0.6c-0.4,0-0.8-0.3-0.9-0.7c-0.1-0.3-0.1-0.6-0.1-1c0-2.7-0.4-6.5-0.5-7
|
||||
c0-0.5-0.1-0.6-0.1-1.2c-0.1-0.3-0.1-0.5-0.1-0.7C24.2,21.8,24.3,20.8,24.3,19.6z"/>
|
||||
<path class="st3" d="M27.1,14.7c0,0.7-0.6,1.3-1.4,1.3l-0.3,0c-0.7,0-1.3-0.6-1.3-1.4l0-1.3c0-0.7,0.6-1.3,1.4-1.3l0.3,0
|
||||
c0.7,0,1.3,0.6,1.3,1.4L27.1,14.7z"/>
|
||||
</g>
|
||||
<path class="st3" d="M22,31.5L22,31.5L22,31.5c-0.1,0-0.2,0-0.4-0.1c-2.2-0.7-4.2-2.1-5.7-3.9c-0.5-0.5-0.4-1.4,0.2-1.8
|
||||
c0.2-0.2,0.5-0.3,0.9-0.3c0.4,0,0.7,0.2,1,0.5c0.6,0.7,1.3,1.4,2.1,1.9c0.8,0.5,1.6,0.9,2.5,1.2l0,0c0.3,0.1,0.6,0.3,0.7,0.6
|
||||
c0.2,0.3,0.2,0.7,0.1,1c-0.1,0.3-0.3,0.5-0.5,0.7C22.5,31.5,22.2,31.6,22,31.5z"/>
|
||||
<path class="st3" d="M29.5,31.3c-0.5,0-1-0.3-1.2-0.8c-0.1-0.3-0.1-0.7,0-1s0.4-0.6,0.7-0.7c0.8-0.3,1.5-0.7,2.1-1.2
|
||||
c0.8-0.6,1.6-1.4,2.2-2.2l0,0c0.2-0.3,0.7-0.5,1.1-0.5c0.3,0,0.5,0.1,0.7,0.2c0.3,0.2,0.5,0.5,0.5,0.8c0.1,0.3,0,0.7-0.2,1
|
||||
c-0.8,1.1-1.7,2-2.8,2.8c-0.8,0.6-1.7,1.1-2.7,1.5C29.9,31.3,29.7,31.3,29.5,31.3z"/>
|
||||
<path class="st3" d="M14.9,24.7c-0.3,0-0.5-0.1-0.7-0.2c-0.2-0.2-0.4-0.4-0.5-0.6c-0.2-0.7,0.1-1.4,0.8-1.7c0.2-0.1,0.3-0.1,0.5-0.1
|
||||
c0.3,0,0.5,0.1,0.7,0.2c0.2,0.2,0.4,0.4,0.5,0.6c0.1,0.3,0.1,0.7,0,1c-0.1,0.3-0.4,0.5-0.7,0.7C15.2,24.7,15,24.7,14.9,24.7z"/>
|
||||
<path class="st3" d="M36.1,24L36.1,24L36.1,24c-0.1,0-0.2,0-0.3-0.1c-0.3-0.1-0.6-0.3-0.8-0.6c-0.2-0.3-0.2-0.6-0.1-1
|
||||
c0.2-0.6,0.7-0.9,1.3-0.9c0.1,0,0.2,0,0.3,0.1c0.3,0.1,0.6,0.3,0.8,0.6c0.2,0.3,0.2,0.6,0.1,1C37.2,23.6,36.7,24,36.1,24z"/>
|
||||
<path class="st3" d="M14.2,21c-0.3,0-0.7-0.1-0.9-0.4c-0.2-0.2-0.4-0.6-0.4-0.9c0-0.1,0-0.2,0-0.4c0.1-2.2,0.7-4.4,1.9-6.3
|
||||
c0.1-0.2,0.3-0.4,0.5-0.5c0.2-0.1,0.4-0.2,0.6-0.1c0.2,0,0.5,0.1,0.6,0.2c0.6,0.4,0.8,1.2,0.4,1.8c-0.9,1.5-1.5,3.2-1.5,5
|
||||
c0,0.1,0,0.2,0,0.3c0,0.3-0.1,0.7-0.4,0.9C14.9,20.8,14.6,21,14.2,21C14.2,21,14.2,21,14.2,21z"/>
|
||||
<path class="st3" d="M36.5,20.2c-0.3,0-0.6-0.1-0.9-0.4c-0.2-0.2-0.4-0.5-0.4-0.8c-0.1-0.9-0.2-1.8-0.6-2.7
|
||||
c-0.3-0.9-0.8-1.7-1.3-2.5l0,0c-0.2-0.3-0.3-0.6-0.2-1c0.1-0.3,0.2-0.6,0.5-0.8c0.2-0.2,0.5-0.3,0.8-0.2c0.4,0,0.8,0.2,1,0.5
|
||||
c0,0,0,0,0,0l0,0c1.4,1.9,2.2,4.2,2.4,6.5c0,0.3-0.1,0.7-0.3,0.9c-0.2,0.3-0.5,0.4-0.9,0.4C36.6,20.2,36.6,20.2,36.5,20.2z"/>
|
||||
<path class="st3" d="M18.5,12.1c-0.4,0-0.7-0.2-1-0.5c-0.2-0.3-0.3-0.6-0.3-1c0-0.3,0.2-0.6,0.5-0.9c0.2-0.2,0.5-0.3,0.8-0.3
|
||||
c0.4,0,0.7,0.2,1,0.5c0.2,0.3,0.3,0.6,0.3,1c0,0.3-0.2,0.6-0.5,0.9C19.1,12,18.8,12.1,18.5,12.1z"/>
|
||||
<path class="st3" d="M31.6,11.7c-0.2,0-0.5-0.1-0.7-0.2c-0.3-0.2-0.5-0.5-0.5-0.8c-0.1-0.3,0-0.7,0.2-1c0.2-0.4,0.7-0.6,1.1-0.6
|
||||
c0.2,0,0.5,0.1,0.7,0.2c0.6,0.4,0.7,1.2,0.3,1.8C32.5,11.5,32.1,11.7,31.6,11.7z"/>
|
||||
<path class="st3" d="M21.9,10.3c-0.5,0-1-0.4-1.2-0.9c-0.1-0.3-0.1-0.7,0.1-1c0.2-0.3,0.4-0.5,0.7-0.6c1.4-0.4,2.8-0.7,4.2-0.6
|
||||
c0.9,0,1.8,0.1,2.7,0.4c0.3,0.1,0.6,0.3,0.8,0.6c0.2,0.3,0.2,0.6,0.1,1c-0.1,0.3-0.2,0.5-0.5,0.7c-0.3,0.2-0.7,0.3-1.1,0.2
|
||||
c-0.7-0.2-1.4-0.3-2.2-0.3c-1.1,0-2.3,0.1-3.3,0.5C22.2,10.3,22,10.3,21.9,10.3z"/>
|
||||
<g>
|
||||
<path class="st3" d="M13.8,40.8c0,0.3-0.1,0.5-0.2,0.7c-0.1,0.2-0.3,0.4-0.4,0.5c-0.2,0.1-0.4,0.2-0.7,0.3
|
||||
c-0.2,0.1-0.5,0.1-0.8,0.1H8.6v-6.4h3.4c0.2,0,0.4,0,0.6,0.1c0.2,0.1,0.3,0.2,0.5,0.4s0.2,0.3,0.3,0.5c0.1,0.2,0.1,0.4,0.1,0.6
|
||||
c0,0.3-0.1,0.6-0.2,0.9c-0.2,0.3-0.4,0.5-0.7,0.6c0.4,0.1,0.7,0.3,0.9,0.6C13.7,40.1,13.8,40.4,13.8,40.8z M9.9,37.1v1.6h1.7
|
||||
c0.2,0,0.4-0.1,0.5-0.2c0.1-0.1,0.2-0.3,0.2-0.6c0-0.2-0.1-0.4-0.2-0.6c-0.1-0.1-0.3-0.2-0.5-0.2H9.9z M12.5,40.6
|
||||
c0-0.1,0-0.2-0.1-0.3c0-0.1-0.1-0.2-0.2-0.3c-0.1-0.1-0.1-0.1-0.2-0.2s-0.2-0.1-0.3-0.1H9.9v1.7h1.8c0.1,0,0.2,0,0.3-0.1
|
||||
c0.1,0,0.2-0.1,0.3-0.2c0.1-0.1,0.1-0.2,0.2-0.3S12.5,40.7,12.5,40.6z"/>
|
||||
<path class="st3" d="M17.7,41.4c0.3,0,0.5-0.1,0.7-0.2s0.4-0.3,0.5-0.5c0.1-0.2,0.2-0.4,0.3-0.7s0.1-0.5,0.1-0.8v-3.3h1.2v3.3
|
||||
c0,0.4-0.1,0.8-0.2,1.2c-0.1,0.4-0.3,0.7-0.5,1c-0.2,0.3-0.5,0.5-0.9,0.7c-0.4,0.2-0.8,0.3-1.3,0.3c-0.5,0-0.9-0.1-1.3-0.3
|
||||
c-0.4-0.2-0.6-0.4-0.9-0.7c-0.2-0.3-0.4-0.6-0.5-1c-0.1-0.4-0.1-0.8-0.1-1.2v-3.3h1.2v3.3c0,0.3,0,0.5,0.1,0.8
|
||||
c0.1,0.2,0.1,0.5,0.3,0.7s0.3,0.3,0.5,0.5S17.4,41.4,17.7,41.4z"/>
|
||||
<path class="st3" d="M27.1,40.8c0,0.3-0.1,0.5-0.2,0.7c-0.1,0.2-0.3,0.4-0.4,0.5c-0.2,0.1-0.4,0.2-0.7,0.3
|
||||
c-0.2,0.1-0.5,0.1-0.8,0.1h-3.1v-6.4h3.4c0.2,0,0.4,0,0.6,0.1c0.2,0.1,0.3,0.2,0.5,0.4s0.2,0.3,0.3,0.5c0.1,0.2,0.1,0.4,0.1,0.6
|
||||
c0,0.3-0.1,0.6-0.2,0.9c-0.2,0.3-0.4,0.5-0.7,0.6c0.4,0.1,0.7,0.3,0.9,0.6C27,40.1,27.1,40.4,27.1,40.8z M23.2,37.1v1.6h1.7
|
||||
c0.2,0,0.4-0.1,0.5-0.2c0.1-0.1,0.2-0.3,0.2-0.6c0-0.2-0.1-0.4-0.2-0.6c-0.1-0.1-0.3-0.2-0.5-0.2H23.2z M25.8,40.6
|
||||
c0-0.1,0-0.2-0.1-0.3c0-0.1-0.1-0.2-0.2-0.3c-0.1-0.1-0.1-0.1-0.2-0.2s-0.2-0.1-0.3-0.1h-1.9v1.7H25c0.1,0,0.2,0,0.3-0.1
|
||||
c0.1,0,0.2-0.1,0.3-0.2c0.1-0.1,0.1-0.2,0.2-0.3S25.8,40.7,25.8,40.6z"/>
|
||||
<path class="st3" d="M33.4,40.8c0,0.3-0.1,0.5-0.2,0.7c-0.1,0.2-0.3,0.4-0.4,0.5c-0.2,0.1-0.4,0.2-0.7,0.3
|
||||
c-0.2,0.1-0.5,0.1-0.8,0.1h-3.1v-6.4h3.4c0.2,0,0.4,0,0.6,0.1c0.2,0.1,0.3,0.2,0.5,0.4s0.2,0.3,0.3,0.5c0.1,0.2,0.1,0.4,0.1,0.6
|
||||
c0,0.3-0.1,0.6-0.2,0.9c-0.2,0.3-0.4,0.5-0.7,0.6c0.4,0.1,0.7,0.3,0.9,0.6C33.3,40.1,33.4,40.4,33.4,40.8z M29.5,37.1v1.6h1.7
|
||||
c0.2,0,0.4-0.1,0.5-0.2c0.1-0.1,0.2-0.3,0.2-0.6c0-0.2-0.1-0.4-0.2-0.6c-0.1-0.1-0.3-0.2-0.5-0.2H29.5z M32.2,40.6
|
||||
c0-0.1,0-0.2-0.1-0.3c0-0.1-0.1-0.2-0.2-0.3c-0.1-0.1-0.1-0.1-0.2-0.2s-0.2-0.1-0.3-0.1h-1.9v1.7h1.8c0.1,0,0.2,0,0.3-0.1
|
||||
c0.1,0,0.2-0.1,0.3-0.2c0.1-0.1,0.1-0.2,0.2-0.3S32.2,40.7,32.2,40.6z"/>
|
||||
<path class="st3" d="M34.6,42.5v-6.4h1.2v5.3h3.3v1.1H34.6z"/>
|
||||
<path class="st3" d="M44.6,41.4v1.1h-4.4v-6.4h4.4v1.1h-3.1v1.5h2.7v1h-2.7v1.7H44.6z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M24.3,69.6c-0.5,0.5-0.9,1-1.3,1.5c-0.1,0.1-0.1,0.3-0.1,0.4c0,1,0,2,0.1,3c0,0.4-0.2,0.7-0.7,0.8
|
||||
c-0.4,0-0.8-0.2-0.8-0.7c-0.1-1.3-0.1-2.5-0.2-3.8c0-0.2,0.1-0.4,0.2-0.6c0.7-0.9,1.4-1.7,2-2.6c0.3-0.4,1-0.9,1.5-0.9
|
||||
c0.2,0,0.6,0,0.8,0c0.5,0,0.8,0.1,1.1,0.6c0.5,0.7,0.9,1.4,1.4,2.1c0.4,0.7,0.8,1,1.7,1.2c0.6,0.1,1.3,0.3,1.9,0.5
|
||||
c0.2,0,0.3,0.1,0.5,0.2c0.3,0.2,0.4,0.5,0.3,0.8c-0.1,0.3-0.3,0.4-0.6,0.4c-0.2,0-0.5,0-0.7-0.1c-0.8-0.2-1.6-0.4-2.4-0.6
|
||||
c-0.5-0.1-0.8-0.4-1.2-0.7c-0.2-0.2-0.3-0.3-0.5-0.5c0,0.2,0,0.3,0,0.4c0,0.7,0,1.4-0.1,2.1c0,0.2-0.2,6.4-0.3,8.9
|
||||
c0,0.3-0.1,0.6-0.2,0.9c-0.1,0.4-0.4,0.6-0.9,0.6c-0.4,0-0.8-0.3-0.9-0.7c-0.1-0.3-0.1-0.6-0.1-1c0-2.7-0.4-6.5-0.5-7
|
||||
c0-0.5-0.1-0.6-0.1-1.2c-0.1-0.3-0.1-0.5-0.1-0.7C24.2,71.8,24.3,70.8,24.3,69.6z"/>
|
||||
<path class="st1" d="M27.1,64.7c0,0.7-0.6,1.3-1.4,1.3l-0.3,0c-0.7,0-1.3-0.6-1.3-1.4l0-1.3c0-0.7,0.6-1.3,1.4-1.3l0.3,0
|
||||
c0.7,0,1.3,0.6,1.3,1.4L27.1,64.7z"/>
|
||||
</g>
|
||||
<path class="st1" d="M22,81.5L22,81.5L22,81.5c-0.1,0-0.2,0-0.4-0.1c-2.2-0.7-4.2-2.1-5.7-3.9c-0.5-0.5-0.4-1.4,0.2-1.8
|
||||
c0.2-0.2,0.5-0.3,0.9-0.3c0.4,0,0.7,0.2,1,0.5c0.6,0.7,1.3,1.4,2.1,1.9c0.8,0.5,1.6,0.9,2.5,1.2l0,0c0.3,0.1,0.6,0.3,0.7,0.6
|
||||
c0.2,0.3,0.2,0.7,0.1,1c-0.1,0.3-0.3,0.5-0.5,0.7C22.5,81.5,22.2,81.6,22,81.5z"/>
|
||||
<path class="st1" d="M29.5,81.3c-0.5,0-1-0.3-1.2-0.8c-0.1-0.3-0.1-0.7,0-1s0.4-0.6,0.7-0.7c0.8-0.3,1.5-0.7,2.1-1.2
|
||||
c0.8-0.6,1.6-1.4,2.2-2.2l0,0c0.2-0.3,0.7-0.5,1.1-0.5c0.3,0,0.5,0.1,0.7,0.2c0.3,0.2,0.5,0.5,0.5,0.8c0.1,0.3,0,0.7-0.2,1
|
||||
c-0.8,1.1-1.7,2-2.8,2.8c-0.8,0.6-1.7,1.1-2.7,1.5C29.9,81.3,29.7,81.3,29.5,81.3z"/>
|
||||
<path class="st1" d="M14.9,74.7c-0.3,0-0.5-0.1-0.7-0.2c-0.2-0.2-0.4-0.4-0.5-0.6c-0.2-0.7,0.1-1.4,0.8-1.7c0.2-0.1,0.3-0.1,0.5-0.1
|
||||
c0.3,0,0.5,0.1,0.7,0.2c0.2,0.2,0.4,0.4,0.5,0.6c0.1,0.3,0.1,0.7,0,1c-0.1,0.3-0.4,0.5-0.7,0.7C15.2,74.7,15,74.7,14.9,74.7z"/>
|
||||
<path class="st1" d="M36.1,74L36.1,74L36.1,74c-0.1,0-0.2,0-0.3-0.1c-0.3-0.1-0.6-0.3-0.8-0.6c-0.2-0.3-0.2-0.6-0.1-1
|
||||
c0.2-0.6,0.7-0.9,1.3-0.9c0.1,0,0.2,0,0.3,0.1c0.3,0.1,0.6,0.3,0.8,0.6c0.2,0.3,0.2,0.6,0.1,1C37.2,73.6,36.7,74,36.1,74z"/>
|
||||
<path class="st1" d="M14.2,71c-0.3,0-0.7-0.1-0.9-0.4c-0.2-0.2-0.4-0.6-0.4-0.9c0-0.1,0-0.2,0-0.4c0.1-2.2,0.7-4.4,1.9-6.3
|
||||
c0.1-0.2,0.3-0.4,0.5-0.5c0.2-0.1,0.4-0.2,0.6-0.1c0.2,0,0.5,0.1,0.6,0.2c0.6,0.4,0.8,1.2,0.4,1.8c-0.9,1.5-1.5,3.2-1.5,5
|
||||
c0,0.1,0,0.2,0,0.3c0,0.3-0.1,0.7-0.4,0.9C14.9,70.8,14.6,71,14.2,71C14.2,71,14.2,71,14.2,71z"/>
|
||||
<path class="st1" d="M36.5,70.2c-0.3,0-0.6-0.1-0.9-0.4c-0.2-0.2-0.4-0.5-0.4-0.8c-0.1-0.9-0.2-1.8-0.6-2.7
|
||||
c-0.3-0.9-0.8-1.7-1.3-2.5l0,0c-0.2-0.3-0.3-0.6-0.2-1c0.1-0.3,0.2-0.6,0.5-0.8c0.2-0.2,0.5-0.3,0.8-0.2c0.4,0,0.8,0.2,1,0.5
|
||||
c0,0,0,0,0,0l0,0c1.4,1.9,2.2,4.2,2.4,6.5c0,0.3-0.1,0.7-0.3,0.9c-0.2,0.3-0.5,0.4-0.9,0.4C36.6,70.2,36.6,70.2,36.5,70.2z"/>
|
||||
<path class="st1" d="M18.5,62.1c-0.4,0-0.7-0.2-1-0.5c-0.2-0.3-0.3-0.6-0.3-1c0-0.3,0.2-0.6,0.5-0.9c0.2-0.2,0.5-0.3,0.8-0.3
|
||||
c0.4,0,0.7,0.2,1,0.5c0.2,0.3,0.3,0.6,0.3,1c0,0.3-0.2,0.6-0.5,0.9C19.1,62,18.8,62.1,18.5,62.1z"/>
|
||||
<path class="st1" d="M31.6,61.7c-0.2,0-0.5-0.1-0.7-0.2c-0.3-0.2-0.5-0.5-0.5-0.8s0-0.7,0.2-1c0.2-0.4,0.7-0.6,1.1-0.6
|
||||
c0.2,0,0.5,0.1,0.7,0.2c0.6,0.4,0.7,1.2,0.3,1.8C32.5,61.5,32.1,61.7,31.6,61.7z"/>
|
||||
<path class="st1" d="M21.9,60.3c-0.5,0-1-0.4-1.2-0.9c-0.1-0.3-0.1-0.7,0.1-1c0.2-0.3,0.4-0.5,0.7-0.6c1.4-0.4,2.8-0.7,4.2-0.6
|
||||
c0.9,0,1.8,0.1,2.7,0.4c0.3,0.1,0.6,0.3,0.8,0.6c0.2,0.3,0.2,0.6,0.1,1c-0.1,0.3-0.2,0.5-0.5,0.7c-0.3,0.2-0.7,0.3-1.1,0.2
|
||||
c-0.7-0.2-1.4-0.3-2.2-0.3c-1.1,0-2.3,0.1-3.3,0.5C22.2,60.3,22,60.3,21.9,60.3z"/>
|
||||
<g>
|
||||
<path class="st1" d="M13.8,90.8c0,0.3-0.1,0.5-0.2,0.7c-0.1,0.2-0.3,0.4-0.4,0.5c-0.2,0.1-0.4,0.2-0.7,0.3
|
||||
c-0.2,0.1-0.5,0.1-0.8,0.1H8.6v-6.4h3.4c0.2,0,0.4,0,0.6,0.1c0.2,0.1,0.3,0.2,0.5,0.4s0.2,0.3,0.3,0.5c0.1,0.2,0.1,0.4,0.1,0.6
|
||||
c0,0.3-0.1,0.6-0.2,0.9c-0.2,0.3-0.4,0.5-0.7,0.6c0.4,0.1,0.7,0.3,0.9,0.6C13.7,90.1,13.8,90.4,13.8,90.8z M9.9,87.1v1.6h1.7
|
||||
c0.2,0,0.4-0.1,0.5-0.2c0.1-0.1,0.2-0.3,0.2-0.6c0-0.2-0.1-0.4-0.2-0.6c-0.1-0.1-0.3-0.2-0.5-0.2H9.9z M12.5,90.6
|
||||
c0-0.1,0-0.2-0.1-0.3c0-0.1-0.1-0.2-0.2-0.3c-0.1-0.1-0.1-0.1-0.2-0.2s-0.2-0.1-0.3-0.1H9.9v1.7h1.8c0.1,0,0.2,0,0.3-0.1
|
||||
c0.1,0,0.2-0.1,0.3-0.2c0.1-0.1,0.1-0.2,0.2-0.3S12.5,90.7,12.5,90.6z"/>
|
||||
<path class="st1" d="M17.7,91.4c0.3,0,0.5-0.1,0.7-0.2s0.4-0.3,0.5-0.5c0.1-0.2,0.2-0.4,0.3-0.7s0.1-0.5,0.1-0.8v-3.3h1.2v3.3
|
||||
c0,0.4-0.1,0.8-0.2,1.2c-0.1,0.4-0.3,0.7-0.5,1c-0.2,0.3-0.5,0.5-0.9,0.7c-0.4,0.2-0.8,0.3-1.3,0.3c-0.5,0-0.9-0.1-1.3-0.3
|
||||
c-0.4-0.2-0.6-0.4-0.9-0.7c-0.2-0.3-0.4-0.6-0.5-1c-0.1-0.4-0.1-0.8-0.1-1.2v-3.3h1.2v3.3c0,0.3,0,0.5,0.1,0.8
|
||||
c0.1,0.2,0.1,0.5,0.3,0.7s0.3,0.3,0.5,0.5S17.4,91.4,17.7,91.4z"/>
|
||||
<path class="st1" d="M27.1,90.8c0,0.3-0.1,0.5-0.2,0.7c-0.1,0.2-0.3,0.4-0.4,0.5c-0.2,0.1-0.4,0.2-0.7,0.3
|
||||
c-0.2,0.1-0.5,0.1-0.8,0.1h-3.1v-6.4h3.4c0.2,0,0.4,0,0.6,0.1c0.2,0.1,0.3,0.2,0.5,0.4s0.2,0.3,0.3,0.5c0.1,0.2,0.1,0.4,0.1,0.6
|
||||
c0,0.3-0.1,0.6-0.2,0.9c-0.2,0.3-0.4,0.5-0.7,0.6c0.4,0.1,0.7,0.3,0.9,0.6C27,90.1,27.1,90.4,27.1,90.8z M23.2,87.1v1.6h1.7
|
||||
c0.2,0,0.4-0.1,0.5-0.2c0.1-0.1,0.2-0.3,0.2-0.6c0-0.2-0.1-0.4-0.2-0.6c-0.1-0.1-0.3-0.2-0.5-0.2H23.2z M25.8,90.6
|
||||
c0-0.1,0-0.2-0.1-0.3c0-0.1-0.1-0.2-0.2-0.3c-0.1-0.1-0.1-0.1-0.2-0.2s-0.2-0.1-0.3-0.1h-1.9v1.7H25c0.1,0,0.2,0,0.3-0.1
|
||||
c0.1,0,0.2-0.1,0.3-0.2c0.1-0.1,0.1-0.2,0.2-0.3S25.8,90.7,25.8,90.6z"/>
|
||||
<path class="st1" d="M33.4,90.8c0,0.3-0.1,0.5-0.2,0.7c-0.1,0.2-0.3,0.4-0.4,0.5c-0.2,0.1-0.4,0.2-0.7,0.3
|
||||
c-0.2,0.1-0.5,0.1-0.8,0.1h-3.1v-6.4h3.4c0.2,0,0.4,0,0.6,0.1c0.2,0.1,0.3,0.2,0.5,0.4s0.2,0.3,0.3,0.5c0.1,0.2,0.1,0.4,0.1,0.6
|
||||
c0,0.3-0.1,0.6-0.2,0.9c-0.2,0.3-0.4,0.5-0.7,0.6c0.4,0.1,0.7,0.3,0.9,0.6C33.3,90.1,33.4,90.4,33.4,90.8z M29.5,87.1v1.6h1.7
|
||||
c0.2,0,0.4-0.1,0.5-0.2c0.1-0.1,0.2-0.3,0.2-0.6c0-0.2-0.1-0.4-0.2-0.6c-0.1-0.1-0.3-0.2-0.5-0.2H29.5z M32.2,90.6
|
||||
c0-0.1,0-0.2-0.1-0.3c0-0.1-0.1-0.2-0.2-0.3c-0.1-0.1-0.1-0.1-0.2-0.2s-0.2-0.1-0.3-0.1h-1.9v1.7h1.8c0.1,0,0.2,0,0.3-0.1
|
||||
c0.1,0,0.2-0.1,0.3-0.2c0.1-0.1,0.1-0.2,0.2-0.3S32.2,90.7,32.2,90.6z"/>
|
||||
<path class="st1" d="M34.6,92.5v-6.4h1.2v5.3h3.3v1.1H34.6z"/>
|
||||
<path class="st1" d="M44.6,91.4v1.1h-4.4v-6.4h4.4v1.1h-3.1v1.5h2.7v1h-2.7v1.7H44.6z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M24.3,119.6c-0.5,0.5-0.9,1-1.3,1.5c-0.1,0.1-0.1,0.3-0.1,0.4c0,1,0,2,0.1,3c0,0.4-0.2,0.7-0.7,0.8
|
||||
c-0.4,0-0.8-0.2-0.8-0.7c-0.1-1.3-0.1-2.5-0.2-3.8c0-0.2,0.1-0.4,0.2-0.6c0.7-0.9,1.4-1.7,2-2.6c0.3-0.4,1-0.9,1.5-0.9
|
||||
c0.2,0,0.6,0,0.8,0c0.5,0,0.8,0.1,1.1,0.6c0.5,0.7,0.9,1.4,1.4,2.1c0.4,0.7,0.8,1,1.7,1.2c0.6,0.1,1.3,0.3,1.9,0.5
|
||||
c0.2,0,0.3,0.1,0.5,0.2c0.3,0.2,0.4,0.5,0.3,0.8c-0.1,0.3-0.3,0.4-0.6,0.4c-0.2,0-0.5,0-0.7-0.1c-0.8-0.2-1.6-0.4-2.4-0.6
|
||||
c-0.5-0.1-0.8-0.4-1.2-0.7c-0.2-0.2-0.3-0.3-0.5-0.5c0,0.2,0,0.3,0,0.4c0,0.7,0,1.4-0.1,2.1c0,0.2-0.2,6.4-0.3,8.9
|
||||
c0,0.3-0.1,0.6-0.2,0.9c-0.1,0.4-0.4,0.6-0.9,0.6c-0.4,0-0.8-0.3-0.9-0.7c-0.1-0.3-0.1-0.6-0.1-1c0-2.7-0.4-6.5-0.5-7
|
||||
c0-0.5-0.1-0.6-0.1-1.2c-0.1-0.3-0.1-0.5-0.1-0.7C24.2,121.8,24.3,120.8,24.3,119.6z"/>
|
||||
<path class="st1" d="M27.1,114.7c0,0.7-0.6,1.3-1.4,1.3l-0.3,0c-0.7,0-1.3-0.6-1.3-1.4l0-1.3c0-0.7,0.6-1.3,1.4-1.3l0.3,0
|
||||
c0.7,0,1.3,0.6,1.3,1.4L27.1,114.7z"/>
|
||||
</g>
|
||||
<path class="st1" d="M22,131.5L22,131.5L22,131.5c-0.1,0-0.2,0-0.4-0.1c-2.2-0.7-4.2-2.1-5.7-3.9c-0.5-0.5-0.4-1.4,0.2-1.8
|
||||
c0.2-0.2,0.5-0.3,0.9-0.3c0.4,0,0.7,0.2,1,0.5c0.6,0.7,1.3,1.4,2.1,1.9c0.8,0.5,1.6,0.9,2.5,1.2l0,0c0.3,0.1,0.6,0.3,0.7,0.6
|
||||
c0.2,0.3,0.2,0.7,0.1,1c-0.1,0.3-0.3,0.5-0.5,0.7C22.5,131.5,22.2,131.6,22,131.5z"/>
|
||||
<path class="st1" d="M29.5,131.3c-0.5,0-1-0.3-1.2-0.8c-0.1-0.3-0.1-0.7,0-1c0.1-0.3,0.4-0.6,0.7-0.7c0.8-0.3,1.5-0.7,2.1-1.2
|
||||
c0.8-0.6,1.6-1.4,2.2-2.2l0,0c0.2-0.3,0.7-0.5,1.1-0.5c0.3,0,0.5,0.1,0.7,0.2c0.3,0.2,0.5,0.5,0.5,0.8c0.1,0.3,0,0.7-0.2,1
|
||||
c-0.8,1.1-1.7,2-2.8,2.8c-0.8,0.6-1.7,1.1-2.7,1.5C29.9,131.3,29.7,131.3,29.5,131.3z"/>
|
||||
<path class="st1" d="M14.9,124.7c-0.3,0-0.5-0.1-0.7-0.2c-0.2-0.2-0.4-0.4-0.5-0.6c-0.2-0.7,0.1-1.4,0.8-1.7
|
||||
c0.2-0.1,0.3-0.1,0.5-0.1c0.3,0,0.5,0.1,0.7,0.2c0.2,0.2,0.4,0.4,0.5,0.6c0.1,0.3,0.1,0.7,0,1c-0.1,0.3-0.4,0.5-0.7,0.7
|
||||
C15.2,124.7,15,124.7,14.9,124.7z"/>
|
||||
<path class="st1" d="M36.1,124L36.1,124L36.1,124c-0.1,0-0.2,0-0.3-0.1c-0.3-0.1-0.6-0.3-0.8-0.6c-0.2-0.3-0.2-0.6-0.1-1
|
||||
c0.2-0.6,0.7-0.9,1.3-0.9c0.1,0,0.2,0,0.3,0.1c0.3,0.1,0.6,0.3,0.8,0.6c0.2,0.3,0.2,0.6,0.1,1C37.2,123.6,36.7,124,36.1,124z"/>
|
||||
<path class="st1" d="M14.2,121c-0.3,0-0.7-0.1-0.9-0.4c-0.2-0.2-0.4-0.6-0.4-0.9c0-0.1,0-0.2,0-0.4c0.1-2.2,0.7-4.4,1.9-6.3
|
||||
c0.1-0.2,0.3-0.4,0.5-0.5c0.2-0.1,0.4-0.2,0.6-0.1c0.2,0,0.5,0.1,0.6,0.2c0.6,0.4,0.8,1.2,0.4,1.8c-0.9,1.5-1.5,3.2-1.5,5
|
||||
c0,0.1,0,0.2,0,0.3c0,0.3-0.1,0.7-0.4,0.9C14.9,120.8,14.6,121,14.2,121C14.2,121,14.2,121,14.2,121z"/>
|
||||
<path class="st1" d="M36.5,120.2c-0.3,0-0.6-0.1-0.9-0.4c-0.2-0.2-0.4-0.5-0.4-0.8c-0.1-0.9-0.2-1.8-0.6-2.7
|
||||
c-0.3-0.9-0.8-1.7-1.3-2.5l0,0c-0.2-0.3-0.3-0.6-0.2-1c0.1-0.3,0.2-0.6,0.5-0.8c0.2-0.2,0.5-0.3,0.8-0.2c0.4,0,0.8,0.2,1,0.5
|
||||
c0,0,0,0,0,0l0,0c1.4,1.9,2.2,4.2,2.4,6.5c0,0.3-0.1,0.7-0.3,0.9c-0.2,0.3-0.5,0.4-0.9,0.4C36.6,120.2,36.6,120.2,36.5,120.2z"/>
|
||||
<path class="st1" d="M18.5,112.1c-0.4,0-0.7-0.2-1-0.5c-0.2-0.3-0.3-0.6-0.3-1c0-0.3,0.2-0.6,0.5-0.9c0.2-0.2,0.5-0.3,0.8-0.3
|
||||
c0.4,0,0.7,0.2,1,0.5c0.2,0.3,0.3,0.6,0.3,1c0,0.3-0.2,0.6-0.5,0.9C19.1,112,18.8,112.1,18.5,112.1z"/>
|
||||
<path class="st1" d="M31.6,111.7c-0.2,0-0.5-0.1-0.7-0.2c-0.3-0.2-0.5-0.5-0.5-0.8s0-0.7,0.2-1c0.2-0.4,0.7-0.6,1.1-0.6
|
||||
c0.2,0,0.5,0.1,0.7,0.2c0.6,0.4,0.7,1.2,0.3,1.8C32.5,111.5,32.1,111.7,31.6,111.7z"/>
|
||||
<path class="st1" d="M21.9,110.3c-0.5,0-1-0.4-1.2-0.9c-0.1-0.3-0.1-0.7,0.1-1c0.2-0.3,0.4-0.5,0.7-0.6c1.4-0.4,2.8-0.7,4.2-0.6
|
||||
c0.9,0,1.8,0.1,2.7,0.4c0.3,0.1,0.6,0.3,0.8,0.6c0.2,0.3,0.2,0.6,0.1,1c-0.1,0.3-0.2,0.5-0.5,0.7c-0.3,0.2-0.7,0.3-1.1,0.2
|
||||
c-0.7-0.2-1.4-0.3-2.2-0.3c-1.1,0-2.3,0.1-3.3,0.5C22.2,110.3,22,110.3,21.9,110.3z"/>
|
||||
<g>
|
||||
<path class="st1" d="M13.8,140.8c0,0.3-0.1,0.5-0.2,0.7c-0.1,0.2-0.3,0.4-0.4,0.5c-0.2,0.1-0.4,0.2-0.7,0.3
|
||||
c-0.2,0.1-0.5,0.1-0.8,0.1H8.6v-6.4h3.4c0.2,0,0.4,0,0.6,0.1c0.2,0.1,0.3,0.2,0.5,0.4s0.2,0.3,0.3,0.5c0.1,0.2,0.1,0.4,0.1,0.6
|
||||
c0,0.3-0.1,0.6-0.2,0.9c-0.2,0.3-0.4,0.5-0.7,0.6c0.4,0.1,0.7,0.3,0.9,0.6C13.7,140.1,13.8,140.4,13.8,140.8z M9.9,137.1v1.6h1.7
|
||||
c0.2,0,0.4-0.1,0.5-0.2c0.1-0.1,0.2-0.3,0.2-0.6c0-0.2-0.1-0.4-0.2-0.6c-0.1-0.1-0.3-0.2-0.5-0.2H9.9z M12.5,140.6
|
||||
c0-0.1,0-0.2-0.1-0.3c0-0.1-0.1-0.2-0.2-0.3c-0.1-0.1-0.1-0.1-0.2-0.2s-0.2-0.1-0.3-0.1H9.9v1.7h1.8c0.1,0,0.2,0,0.3-0.1
|
||||
c0.1,0,0.2-0.1,0.3-0.2c0.1-0.1,0.1-0.2,0.2-0.3S12.5,140.7,12.5,140.6z"/>
|
||||
<path class="st1" d="M17.7,141.4c0.3,0,0.5-0.1,0.7-0.2s0.4-0.3,0.5-0.5c0.1-0.2,0.2-0.4,0.3-0.7s0.1-0.5,0.1-0.8v-3.3h1.2v3.3
|
||||
c0,0.4-0.1,0.8-0.2,1.2c-0.1,0.4-0.3,0.7-0.5,1c-0.2,0.3-0.5,0.5-0.9,0.7c-0.4,0.2-0.8,0.3-1.3,0.3c-0.5,0-0.9-0.1-1.3-0.3
|
||||
c-0.4-0.2-0.6-0.4-0.9-0.7c-0.2-0.3-0.4-0.6-0.5-1c-0.1-0.4-0.1-0.8-0.1-1.2v-3.3h1.2v3.3c0,0.3,0,0.5,0.1,0.8
|
||||
c0.1,0.2,0.1,0.5,0.3,0.7s0.3,0.3,0.5,0.5S17.4,141.4,17.7,141.4z"/>
|
||||
<path class="st1" d="M27.1,140.8c0,0.3-0.1,0.5-0.2,0.7c-0.1,0.2-0.3,0.4-0.4,0.5c-0.2,0.1-0.4,0.2-0.7,0.3
|
||||
c-0.2,0.1-0.5,0.1-0.8,0.1h-3.1v-6.4h3.4c0.2,0,0.4,0,0.6,0.1c0.2,0.1,0.3,0.2,0.5,0.4s0.2,0.3,0.3,0.5c0.1,0.2,0.1,0.4,0.1,0.6
|
||||
c0,0.3-0.1,0.6-0.2,0.9c-0.2,0.3-0.4,0.5-0.7,0.6c0.4,0.1,0.7,0.3,0.9,0.6C27,140.1,27.1,140.4,27.1,140.8z M23.2,137.1v1.6h1.7
|
||||
c0.2,0,0.4-0.1,0.5-0.2c0.1-0.1,0.2-0.3,0.2-0.6c0-0.2-0.1-0.4-0.2-0.6c-0.1-0.1-0.3-0.2-0.5-0.2H23.2z M25.8,140.6
|
||||
c0-0.1,0-0.2-0.1-0.3c0-0.1-0.1-0.2-0.2-0.3c-0.1-0.1-0.1-0.1-0.2-0.2s-0.2-0.1-0.3-0.1h-1.9v1.7H25c0.1,0,0.2,0,0.3-0.1
|
||||
c0.1,0,0.2-0.1,0.3-0.2c0.1-0.1,0.1-0.2,0.2-0.3S25.8,140.7,25.8,140.6z"/>
|
||||
<path class="st1" d="M33.4,140.8c0,0.3-0.1,0.5-0.2,0.7c-0.1,0.2-0.3,0.4-0.4,0.5c-0.2,0.1-0.4,0.2-0.7,0.3
|
||||
c-0.2,0.1-0.5,0.1-0.8,0.1h-3.1v-6.4h3.4c0.2,0,0.4,0,0.6,0.1c0.2,0.1,0.3,0.2,0.5,0.4s0.2,0.3,0.3,0.5c0.1,0.2,0.1,0.4,0.1,0.6
|
||||
c0,0.3-0.1,0.6-0.2,0.9c-0.2,0.3-0.4,0.5-0.7,0.6c0.4,0.1,0.7,0.3,0.9,0.6C33.3,140.1,33.4,140.4,33.4,140.8z M29.5,137.1v1.6h1.7
|
||||
c0.2,0,0.4-0.1,0.5-0.2c0.1-0.1,0.2-0.3,0.2-0.6c0-0.2-0.1-0.4-0.2-0.6c-0.1-0.1-0.3-0.2-0.5-0.2H29.5z M32.2,140.6
|
||||
c0-0.1,0-0.2-0.1-0.3c0-0.1-0.1-0.2-0.2-0.3c-0.1-0.1-0.1-0.1-0.2-0.2s-0.2-0.1-0.3-0.1h-1.9v1.7h1.8c0.1,0,0.2,0,0.3-0.1
|
||||
c0.1,0,0.2-0.1,0.3-0.2c0.1-0.1,0.1-0.2,0.2-0.3S32.2,140.7,32.2,140.6z"/>
|
||||
<path class="st1" d="M34.6,142.5v-6.4h1.2v5.3h3.3v1.1H34.6z"/>
|
||||
<path class="st1" d="M44.6,141.4v1.1h-4.4v-6.4h4.4v1.1h-3.1v1.5h2.7v1h-2.7v1.7H44.6z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M24.3,169.6c-0.5,0.5-0.9,1-1.3,1.5c-0.1,0.1-0.1,0.3-0.1,0.4c0,1,0,2,0.1,3c0,0.4-0.2,0.7-0.7,0.8
|
||||
c-0.4,0-0.8-0.2-0.8-0.7c-0.1-1.3-0.1-2.5-0.2-3.8c0-0.2,0.1-0.4,0.2-0.6c0.7-0.9,1.4-1.7,2-2.6c0.3-0.4,1-0.9,1.5-0.9
|
||||
c0.2,0,0.6,0,0.8,0c0.5,0,0.8,0.1,1.1,0.6c0.5,0.7,0.9,1.4,1.4,2.1c0.4,0.7,0.8,1,1.7,1.2c0.6,0.1,1.3,0.3,1.9,0.5
|
||||
c0.2,0,0.3,0.1,0.5,0.2c0.3,0.2,0.4,0.5,0.3,0.8c-0.1,0.3-0.3,0.4-0.6,0.4c-0.2,0-0.5,0-0.7-0.1c-0.8-0.2-1.6-0.4-2.4-0.6
|
||||
c-0.5-0.1-0.8-0.4-1.2-0.7c-0.2-0.2-0.3-0.3-0.5-0.5c0,0.2,0,0.3,0,0.4c0,0.7,0,1.4-0.1,2.1c0,0.2-0.2,6.4-0.3,8.9
|
||||
c0,0.3-0.1,0.6-0.2,0.9c-0.1,0.4-0.4,0.6-0.9,0.6c-0.4,0-0.8-0.3-0.9-0.7c-0.1-0.3-0.1-0.6-0.1-1c0-2.7-0.4-6.5-0.5-7
|
||||
c0-0.5-0.1-0.6-0.1-1.2c-0.1-0.3-0.1-0.5-0.1-0.7C24.2,171.8,24.3,170.8,24.3,169.6z"/>
|
||||
<path class="st1" d="M27.1,164.7c0,0.7-0.6,1.3-1.4,1.3l-0.3,0c-0.7,0-1.3-0.6-1.3-1.4l0-1.3c0-0.7,0.6-1.3,1.4-1.3l0.3,0
|
||||
c0.7,0,1.3,0.6,1.3,1.4L27.1,164.7z"/>
|
||||
</g>
|
||||
<path class="st1" d="M22,181.5L22,181.5L22,181.5c-0.1,0-0.2,0-0.4-0.1c-2.2-0.7-4.2-2.1-5.7-3.9c-0.5-0.5-0.4-1.4,0.2-1.8
|
||||
c0.2-0.2,0.5-0.3,0.9-0.3c0.4,0,0.7,0.2,1,0.5c0.6,0.7,1.3,1.4,2.1,1.9c0.8,0.5,1.6,0.9,2.5,1.2l0,0c0.3,0.1,0.6,0.3,0.7,0.6
|
||||
c0.2,0.3,0.2,0.7,0.1,1c-0.1,0.3-0.3,0.5-0.5,0.7C22.5,181.5,22.2,181.6,22,181.5z"/>
|
||||
<path class="st1" d="M29.5,181.3c-0.5,0-1-0.3-1.2-0.8c-0.1-0.3-0.1-0.7,0-1c0.1-0.3,0.4-0.6,0.7-0.7c0.8-0.3,1.5-0.7,2.1-1.2
|
||||
c0.8-0.6,1.6-1.4,2.2-2.2l0,0c0.2-0.3,0.7-0.5,1.1-0.5c0.3,0,0.5,0.1,0.7,0.2c0.3,0.2,0.5,0.5,0.5,0.8c0.1,0.3,0,0.7-0.2,1
|
||||
c-0.8,1.1-1.7,2-2.8,2.8c-0.8,0.6-1.7,1.1-2.7,1.5C29.9,181.3,29.7,181.3,29.5,181.3z"/>
|
||||
<path class="st1" d="M14.9,174.7c-0.3,0-0.5-0.1-0.7-0.2c-0.2-0.2-0.4-0.4-0.5-0.6c-0.2-0.7,0.1-1.4,0.8-1.7
|
||||
c0.2-0.1,0.3-0.1,0.5-0.1c0.3,0,0.5,0.1,0.7,0.2c0.2,0.2,0.4,0.4,0.5,0.6c0.1,0.3,0.1,0.7,0,1c-0.1,0.3-0.4,0.5-0.7,0.7
|
||||
C15.2,174.7,15,174.7,14.9,174.7z"/>
|
||||
<path class="st1" d="M36.1,174L36.1,174L36.1,174c-0.1,0-0.2,0-0.3-0.1c-0.3-0.1-0.6-0.3-0.8-0.6c-0.2-0.3-0.2-0.6-0.1-1
|
||||
c0.2-0.6,0.7-0.9,1.3-0.9c0.1,0,0.2,0,0.3,0.1c0.3,0.1,0.6,0.3,0.8,0.6c0.2,0.3,0.2,0.6,0.1,1C37.2,173.6,36.7,174,36.1,174z"/>
|
||||
<path class="st1" d="M14.2,171c-0.3,0-0.7-0.1-0.9-0.4c-0.2-0.2-0.4-0.6-0.4-0.9c0-0.1,0-0.2,0-0.4c0.1-2.2,0.7-4.4,1.9-6.3
|
||||
c0.1-0.2,0.3-0.4,0.5-0.5c0.2-0.1,0.4-0.2,0.6-0.1c0.2,0,0.5,0.1,0.6,0.2c0.6,0.4,0.8,1.2,0.4,1.8c-0.9,1.5-1.5,3.2-1.5,5
|
||||
c0,0.1,0,0.2,0,0.3c0,0.3-0.1,0.7-0.4,0.9C14.9,170.8,14.6,171,14.2,171C14.2,171,14.2,171,14.2,171z"/>
|
||||
<path class="st1" d="M36.5,170.2c-0.3,0-0.6-0.1-0.9-0.4c-0.2-0.2-0.4-0.5-0.4-0.8c-0.1-0.9-0.2-1.8-0.6-2.7
|
||||
c-0.3-0.9-0.8-1.7-1.3-2.5l0,0c-0.2-0.3-0.3-0.6-0.2-1c0.1-0.3,0.2-0.6,0.5-0.8c0.2-0.2,0.5-0.3,0.8-0.2c0.4,0,0.8,0.2,1,0.5
|
||||
c0,0,0,0,0,0l0,0c1.4,1.9,2.2,4.2,2.4,6.5c0,0.3-0.1,0.7-0.3,0.9c-0.2,0.3-0.5,0.4-0.9,0.4C36.6,170.2,36.6,170.2,36.5,170.2z"/>
|
||||
<path class="st1" d="M18.5,162.1c-0.4,0-0.7-0.2-1-0.5c-0.2-0.3-0.3-0.6-0.3-1c0-0.3,0.2-0.6,0.5-0.9c0.2-0.2,0.5-0.3,0.8-0.3
|
||||
c0.4,0,0.7,0.2,1,0.5c0.2,0.3,0.3,0.6,0.3,1c0,0.3-0.2,0.6-0.5,0.9C19.1,162,18.8,162.1,18.5,162.1z"/>
|
||||
<path class="st1" d="M31.6,161.7c-0.2,0-0.5-0.1-0.7-0.2c-0.3-0.2-0.5-0.5-0.5-0.8s0-0.7,0.2-1c0.2-0.4,0.7-0.6,1.1-0.6
|
||||
c0.2,0,0.5,0.1,0.7,0.2c0.6,0.4,0.7,1.2,0.3,1.8C32.5,161.5,32.1,161.7,31.6,161.7z"/>
|
||||
<path class="st1" d="M21.9,160.3c-0.5,0-1-0.4-1.2-0.9c-0.1-0.3-0.1-0.7,0.1-1c0.2-0.3,0.4-0.5,0.7-0.6c1.4-0.4,2.8-0.7,4.2-0.6
|
||||
c0.9,0,1.8,0.1,2.7,0.4c0.3,0.1,0.6,0.3,0.8,0.6c0.2,0.3,0.2,0.6,0.1,1c-0.1,0.3-0.2,0.5-0.5,0.7c-0.3,0.2-0.7,0.3-1.1,0.2
|
||||
c-0.7-0.2-1.4-0.3-2.2-0.3c-1.1,0-2.3,0.1-3.3,0.5C22.2,160.3,22,160.3,21.9,160.3z"/>
|
||||
<g>
|
||||
<path class="st1" d="M13.8,190.8c0,0.3-0.1,0.5-0.2,0.7c-0.1,0.2-0.3,0.4-0.4,0.5c-0.2,0.1-0.4,0.2-0.7,0.3
|
||||
c-0.2,0.1-0.5,0.1-0.8,0.1H8.6v-6.4h3.4c0.2,0,0.4,0,0.6,0.1c0.2,0.1,0.3,0.2,0.5,0.4s0.2,0.3,0.3,0.5c0.1,0.2,0.1,0.4,0.1,0.6
|
||||
c0,0.3-0.1,0.6-0.2,0.9c-0.2,0.3-0.4,0.5-0.7,0.6c0.4,0.1,0.7,0.3,0.9,0.6C13.7,190.1,13.8,190.4,13.8,190.8z M9.9,187.1v1.6h1.7
|
||||
c0.2,0,0.4-0.1,0.5-0.2c0.1-0.1,0.2-0.3,0.2-0.6c0-0.2-0.1-0.4-0.2-0.6c-0.1-0.1-0.3-0.2-0.5-0.2H9.9z M12.5,190.6
|
||||
c0-0.1,0-0.2-0.1-0.3c0-0.1-0.1-0.2-0.2-0.3c-0.1-0.1-0.1-0.1-0.2-0.2s-0.2-0.1-0.3-0.1H9.9v1.7h1.8c0.1,0,0.2,0,0.3-0.1
|
||||
c0.1,0,0.2-0.1,0.3-0.2c0.1-0.1,0.1-0.2,0.2-0.3S12.5,190.7,12.5,190.6z"/>
|
||||
<path class="st1" d="M17.7,191.4c0.3,0,0.5-0.1,0.7-0.2s0.4-0.3,0.5-0.5c0.1-0.2,0.2-0.4,0.3-0.7s0.1-0.5,0.1-0.8v-3.3h1.2v3.3
|
||||
c0,0.4-0.1,0.8-0.2,1.2c-0.1,0.4-0.3,0.7-0.5,1c-0.2,0.3-0.5,0.5-0.9,0.7c-0.4,0.2-0.8,0.3-1.3,0.3c-0.5,0-0.9-0.1-1.3-0.3
|
||||
c-0.4-0.2-0.6-0.4-0.9-0.7c-0.2-0.3-0.4-0.6-0.5-1c-0.1-0.4-0.1-0.8-0.1-1.2v-3.3h1.2v3.3c0,0.3,0,0.5,0.1,0.8
|
||||
c0.1,0.2,0.1,0.5,0.3,0.7s0.3,0.3,0.5,0.5S17.4,191.4,17.7,191.4z"/>
|
||||
<path class="st1" d="M27.1,190.8c0,0.3-0.1,0.5-0.2,0.7c-0.1,0.2-0.3,0.4-0.4,0.5c-0.2,0.1-0.4,0.2-0.7,0.3
|
||||
c-0.2,0.1-0.5,0.1-0.8,0.1h-3.1v-6.4h3.4c0.2,0,0.4,0,0.6,0.1c0.2,0.1,0.3,0.2,0.5,0.4s0.2,0.3,0.3,0.5c0.1,0.2,0.1,0.4,0.1,0.6
|
||||
c0,0.3-0.1,0.6-0.2,0.9c-0.2,0.3-0.4,0.5-0.7,0.6c0.4,0.1,0.7,0.3,0.9,0.6C27,190.1,27.1,190.4,27.1,190.8z M23.2,187.1v1.6h1.7
|
||||
c0.2,0,0.4-0.1,0.5-0.2c0.1-0.1,0.2-0.3,0.2-0.6c0-0.2-0.1-0.4-0.2-0.6c-0.1-0.1-0.3-0.2-0.5-0.2H23.2z M25.8,190.6
|
||||
c0-0.1,0-0.2-0.1-0.3c0-0.1-0.1-0.2-0.2-0.3c-0.1-0.1-0.1-0.1-0.2-0.2s-0.2-0.1-0.3-0.1h-1.9v1.7H25c0.1,0,0.2,0,0.3-0.1
|
||||
c0.1,0,0.2-0.1,0.3-0.2c0.1-0.1,0.1-0.2,0.2-0.3S25.8,190.7,25.8,190.6z"/>
|
||||
<path class="st1" d="M33.4,190.8c0,0.3-0.1,0.5-0.2,0.7c-0.1,0.2-0.3,0.4-0.4,0.5c-0.2,0.1-0.4,0.2-0.7,0.3
|
||||
c-0.2,0.1-0.5,0.1-0.8,0.1h-3.1v-6.4h3.4c0.2,0,0.4,0,0.6,0.1c0.2,0.1,0.3,0.2,0.5,0.4s0.2,0.3,0.3,0.5c0.1,0.2,0.1,0.4,0.1,0.6
|
||||
c0,0.3-0.1,0.6-0.2,0.9c-0.2,0.3-0.4,0.5-0.7,0.6c0.4,0.1,0.7,0.3,0.9,0.6C33.3,190.1,33.4,190.4,33.4,190.8z M29.5,187.1v1.6h1.7
|
||||
c0.2,0,0.4-0.1,0.5-0.2c0.1-0.1,0.2-0.3,0.2-0.6c0-0.2-0.1-0.4-0.2-0.6c-0.1-0.1-0.3-0.2-0.5-0.2H29.5z M32.2,190.6
|
||||
c0-0.1,0-0.2-0.1-0.3c0-0.1-0.1-0.2-0.2-0.3c-0.1-0.1-0.1-0.1-0.2-0.2s-0.2-0.1-0.3-0.1h-1.9v1.7h1.8c0.1,0,0.2,0,0.3-0.1
|
||||
c0.1,0,0.2-0.1,0.3-0.2c0.1-0.1,0.1-0.2,0.2-0.3S32.2,190.7,32.2,190.6z"/>
|
||||
<path class="st1" d="M34.6,192.5v-6.4h1.2v5.3h3.3v1.1H34.6z"/>
|
||||
<path class="st1" d="M44.6,191.4v1.1h-4.4v-6.4h4.4v1.1h-3.1v1.5h2.7v1h-2.7v1.7H44.6z"/>
|
||||
</g>
|
||||
</svg>
|
After (image error) Size: 25 KiB |
58
scripts/system/bubble.js
Normal file
58
scripts/system/bubble.js
Normal file
|
@ -0,0 +1,58 @@
|
|||
"use strict";
|
||||
|
||||
//
|
||||
// bubble.js
|
||||
// scripts/system/
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 11/18/2016
|
||||
// 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
|
||||
//
|
||||
/* global Toolbars, Script, Users, Overlays, AvatarList, Controller, Camera, getControllerWorldLocation */
|
||||
|
||||
|
||||
(function() { // BEGIN LOCAL_SCOPE
|
||||
|
||||
// grab the toolbar
|
||||
var toolbar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system");
|
||||
|
||||
var ASSETS_PATH = Script.resolvePath("assets");
|
||||
var TOOLS_PATH = Script.resolvePath("assets/images/tools/");
|
||||
|
||||
function buttonImageURL() {
|
||||
return TOOLS_PATH + 'bubble.svg';
|
||||
}
|
||||
|
||||
var bubbleActive = Users.getIgnoreRadiusEnabled();
|
||||
|
||||
// setup the mod button and add it to the toolbar
|
||||
var button = toolbar.addButton({
|
||||
objectName: 'bubble',
|
||||
imageURL: buttonImageURL(),
|
||||
visible: true,
|
||||
buttonState: bubbleActive ? 0 : 1,
|
||||
defaultState: bubbleActive ? 0 : 1,
|
||||
hoverState: bubbleActive ? 2 : 3,
|
||||
alpha: 0.9
|
||||
});
|
||||
|
||||
|
||||
// handle clicks on the toolbar button
|
||||
function buttonClicked(){
|
||||
Users.toggleIgnoreRadius();
|
||||
bubbleActive = Users.getIgnoreRadiusEnabled();
|
||||
button.writeProperty('buttonState', bubbleActive ? 0 : 1);
|
||||
button.writeProperty('defaultState', bubbleActive ? 0 : 1);
|
||||
button.writeProperty('hoverState', bubbleActive ? 2 : 3);
|
||||
}
|
||||
|
||||
button.clicked.connect(buttonClicked);
|
||||
|
||||
// cleanup the toolbar button and overlays when script is stopped
|
||||
Script.scriptEnding.connect(function() {
|
||||
toolbar.removeButton('bubble');
|
||||
});
|
||||
|
||||
}()); // END LOCAL_SCOPE
|
Loading…
Reference in a new issue