mirror of
https://github.com/overte-org/overte.git
synced 2025-04-12 19:58:15 +02:00
Merge branch 'master' of github.com:highfidelity/hifi into privateUserData
This commit is contained in:
commit
2046fcfd09
68 changed files with 1412 additions and 504 deletions
|
@ -82,6 +82,7 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) :
|
|||
packetReceiver.registerListener(PacketType::BulkAvatarTraitsAck, this, "queueIncomingPacket");
|
||||
packetReceiver.registerListenerForTypes({ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase },
|
||||
this, "handleOctreePacket");
|
||||
packetReceiver.registerListener(PacketType::ChallengeOwnership, this, "handleChallengeOwnership");
|
||||
|
||||
packetReceiver.registerListenerForTypes({
|
||||
PacketType::ReplicatedAvatarIdentity,
|
||||
|
@ -367,10 +368,13 @@ void AvatarMixer::manageIdentityData(const SharedNodePointer& node) {
|
|||
return;
|
||||
}
|
||||
|
||||
bool sendIdentity = false;
|
||||
if (nodeData && nodeData->getAvatarSessionDisplayNameMustChange()) {
|
||||
AvatarData& avatar = nodeData->getAvatar();
|
||||
const QString& existingBaseDisplayName = nodeData->getAvatar().getSessionDisplayName();
|
||||
MixerAvatar& avatar = nodeData->getAvatar();
|
||||
bool sendIdentity = avatar.needsIdentityUpdate();
|
||||
if (sendIdentity) {
|
||||
nodeData->flagIdentityChange();
|
||||
}
|
||||
if (nodeData->getAvatarSessionDisplayNameMustChange()) {
|
||||
const QString& existingBaseDisplayName = avatar.getSessionDisplayName();
|
||||
if (!existingBaseDisplayName.isEmpty()) {
|
||||
SessionDisplayName existingDisplayName { existingBaseDisplayName };
|
||||
|
||||
|
@ -414,10 +418,11 @@ void AvatarMixer::manageIdentityData(const SharedNodePointer& node) {
|
|||
sendIdentityPacket(nodeData, node); // Tell node whose name changed about its new session display name or avatar.
|
||||
// since this packet includes a change to either the skeleton model URL or the display name
|
||||
// it needs a new sequence number
|
||||
nodeData->getAvatar().pushIdentitySequenceNumber();
|
||||
avatar.pushIdentitySequenceNumber();
|
||||
|
||||
// tell node whose name changed about its new session display name or avatar.
|
||||
sendIdentityPacket(nodeData, node);
|
||||
avatar.setNeedsIdentityUpdate(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1123,6 +1128,16 @@ void AvatarMixer::entityChange() {
|
|||
_dirtyHeroStatus = true;
|
||||
}
|
||||
|
||||
void AvatarMixer::handleChallengeOwnership(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
if (senderNode->getType() == NodeType::Agent && senderNode->getLinkedData()) {
|
||||
auto clientData = static_cast<AvatarMixerClientData*>(senderNode->getLinkedData());
|
||||
auto avatar = clientData->getAvatarSharedPointer();
|
||||
if (avatar) {
|
||||
avatar->handleChallengeResponse(message.data());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarMixer::aboutToFinish() {
|
||||
DependencyManager::destroy<ResourceManager>();
|
||||
DependencyManager::destroy<ResourceCacheSharedItems>();
|
||||
|
|
|
@ -65,6 +65,7 @@ private slots:
|
|||
void domainSettingsRequestComplete();
|
||||
void handlePacketVersionMismatch(PacketType type, const HifiSockAddr& senderSockAddr, const QUuid& senderUUID);
|
||||
void handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleChallengeOwnership(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void start();
|
||||
|
||||
private:
|
||||
|
|
|
@ -81,6 +81,10 @@ int AvatarMixerClientData::processPackets(const SlaveSharedData& slaveSharedData
|
|||
}
|
||||
assert(_packetQueue.empty());
|
||||
|
||||
if (_avatar) {
|
||||
_avatar->processCertifyEvents();
|
||||
}
|
||||
|
||||
return packetsProcessed;
|
||||
}
|
||||
|
||||
|
@ -200,6 +204,7 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message,
|
|||
if (traitType == AvatarTraits::SkeletonModelURL) {
|
||||
// special handling for skeleton model URL, since we need to make sure it is in the whitelist
|
||||
checkSkeletonURLAgainstWhitelist(slaveSharedData, sendingNode, packetTraitVersion);
|
||||
_avatar->fetchAvatarFST();
|
||||
}
|
||||
|
||||
anyTraitsChanged = true;
|
||||
|
|
|
@ -157,6 +157,11 @@ qint64 AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* lis
|
|||
++simpleReceivedIt;
|
||||
}
|
||||
|
||||
if (bytesWritten > 0 && sendingAvatar->isCertifyFailed()) {
|
||||
// Resend identity packet if certification failed:
|
||||
sendingAvatar->setNeedsIdentityUpdate();
|
||||
}
|
||||
|
||||
// enumerate the received instanced trait versions
|
||||
auto instancedReceivedIt = lastReceivedVersions.instancedCBegin();
|
||||
while (instancedReceivedIt != lastReceivedVersions.instancedCEnd()) {
|
||||
|
|
345
assignment-client/src/avatars/MixerAvatar.cpp
Normal file
345
assignment-client/src/avatars/MixerAvatar.cpp
Normal file
|
@ -0,0 +1,345 @@
|
|||
//
|
||||
// MixerAvatar.cpp
|
||||
// assignment-client/src/avatars
|
||||
//
|
||||
// Created by Simon Walton April 2019
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "MixerAvatar.h"
|
||||
|
||||
#include <QRegularExpression>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QNetworkReply>
|
||||
#include <QCryptographicHash>
|
||||
#include <QApplication>
|
||||
|
||||
#include <ResourceManager.h>
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <NetworkingConstants.h>
|
||||
#include <EntityItem.h>
|
||||
#include <EntityItemProperties.h>
|
||||
#include "ClientTraitsHandler.h"
|
||||
#include "AvatarLogging.h"
|
||||
|
||||
void MixerAvatar::fetchAvatarFST() {
|
||||
_verifyState = nonCertified;
|
||||
|
||||
_pendingEvent = false;
|
||||
|
||||
QUrl avatarURL = getSkeletonModelURL();
|
||||
if (avatarURL.isEmpty() || avatarURL.isLocalFile() || avatarURL.scheme() == "qrc") {
|
||||
// Not network FST.
|
||||
return;
|
||||
}
|
||||
_certificateIdFromURL.clear();
|
||||
_certificateIdFromFST.clear();
|
||||
_marketplaceIdFromURL.clear();
|
||||
_marketplaceIdFromFST.clear();
|
||||
auto resourceManager = DependencyManager::get<ResourceManager>();
|
||||
|
||||
// Match UUID + (optionally) URL cert
|
||||
static const QRegularExpression marketIdRegex{
|
||||
"^https://.*?highfidelity\\.com/api/.*?/commerce/entity_edition/([-0-9a-z]{36})(.*?certificate_id=([\\w/+%]+)|.*).*$"
|
||||
};
|
||||
auto marketIdMatch = marketIdRegex.match(avatarURL.toDisplayString());
|
||||
if (marketIdMatch.hasMatch()) {
|
||||
QMutexLocker certifyLocker(&_avatarCertifyLock);
|
||||
_marketplaceIdFromURL = marketIdMatch.captured(1);
|
||||
if (marketIdMatch.lastCapturedIndex() == 3) {
|
||||
_certificateIdFromURL = QUrl::fromPercentEncoding(marketIdMatch.captured(3).toUtf8());
|
||||
}
|
||||
}
|
||||
|
||||
ResourceRequest* fstRequest = resourceManager->createResourceRequest(this, avatarURL);
|
||||
if (fstRequest) {
|
||||
QMutexLocker certifyLocker(&_avatarCertifyLock);
|
||||
|
||||
_avatarRequest = fstRequest;
|
||||
_verifyState = requestingFST;
|
||||
connect(fstRequest, &ResourceRequest::finished, this, &MixerAvatar::fstRequestComplete);
|
||||
fstRequest->send();
|
||||
} else {
|
||||
qCDebug(avatars) << "Couldn't create FST request for" << avatarURL;
|
||||
_verifyState = error;
|
||||
}
|
||||
_needsIdentityUpdate = true;
|
||||
}
|
||||
|
||||
void MixerAvatar::fstRequestComplete() {
|
||||
ResourceRequest* fstRequest = static_cast<ResourceRequest*>(QObject::sender());
|
||||
QMutexLocker certifyLocker(&_avatarCertifyLock);
|
||||
if (fstRequest == _avatarRequest) {
|
||||
auto result = fstRequest->getResult();
|
||||
if (result != ResourceRequest::Success) {
|
||||
_verifyState = error;
|
||||
qCDebug(avatars) << "FST request for" << fstRequest->getUrl() << "failed:" << result;
|
||||
} else {
|
||||
_avatarFSTContents = fstRequest->getData();
|
||||
_verifyState = receivedFST;
|
||||
_pendingEvent = true;
|
||||
}
|
||||
_avatarRequest->deleteLater();
|
||||
_avatarRequest = nullptr;
|
||||
} else {
|
||||
qCDebug(avatars) << "Incorrect request for" << getDisplayName();
|
||||
}
|
||||
}
|
||||
|
||||
bool MixerAvatar::generateFSTHash() {
|
||||
if (_avatarFSTContents.length() == 0) {
|
||||
return false;
|
||||
}
|
||||
QByteArray hashJson = canonicalJson(_avatarFSTContents);
|
||||
QCryptographicHash fstHash(QCryptographicHash::Sha256);
|
||||
fstHash.addData(hashJson);
|
||||
_certificateHash = fstHash.result();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MixerAvatar::validateFSTHash(const QString& publicKey) {
|
||||
// Guess we should refactor this stuff into a Authorization namespace ...
|
||||
return EntityItemProperties::verifySignature(publicKey, _certificateHash,
|
||||
QByteArray::fromBase64(_certificateIdFromFST.toUtf8()));
|
||||
}
|
||||
|
||||
QByteArray MixerAvatar::canonicalJson(const QString fstFile) {
|
||||
QStringList fstLines = fstFile.split("\n", QString::SkipEmptyParts);
|
||||
static const QString fstKeywordsReg {
|
||||
"(marketplaceID|itemDescription|itemCategories|itemArtist|itemLicenseUrl|limitedRun|itemName|"
|
||||
"filename|texdir|script|editionNumber|certificateID)"
|
||||
};
|
||||
QRegularExpression fstLineRegExp { QString("^\\s*") + fstKeywordsReg + "\\s*=\\s*(\\S.*)$" };
|
||||
QStringListIterator fstLineIter(fstLines);
|
||||
|
||||
QJsonObject certifiedItems;
|
||||
QStringList scripts;
|
||||
while (fstLineIter.hasNext()) {
|
||||
auto line = fstLineIter.next();
|
||||
auto lineMatch = fstLineRegExp.match(line);
|
||||
if (lineMatch.hasMatch()) {
|
||||
QString key = lineMatch.captured(1);
|
||||
if (key == "certificateID") {
|
||||
_certificateIdFromFST = lineMatch.captured(2);
|
||||
} else if (key == "itemDescription") {
|
||||
// Item description can be multiline - intermediate lines end in <CR>
|
||||
QString itemDesc = lineMatch.captured(2);
|
||||
while (itemDesc.endsWith('\r') && fstLineIter.hasNext()) {
|
||||
itemDesc += '\n' + fstLineIter.next();
|
||||
}
|
||||
certifiedItems[key] = QJsonValue(itemDesc);
|
||||
} else if (key == "limitedRun" || key == "editionNumber") {
|
||||
double value = lineMatch.captured(2).toDouble();
|
||||
if (value != 0.0) {
|
||||
certifiedItems[key] = QJsonValue(value);
|
||||
}
|
||||
} else if (key == "script") {
|
||||
scripts.append(lineMatch.captured(2).trimmed());
|
||||
} else {
|
||||
certifiedItems[key] = QJsonValue(lineMatch.captured(2));
|
||||
if (key == "marketplaceID") {
|
||||
_marketplaceIdFromFST = lineMatch.captured(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!scripts.empty()) {
|
||||
scripts.sort();
|
||||
certifiedItems["script"] = QJsonArray::fromStringList(scripts);
|
||||
}
|
||||
|
||||
QJsonDocument jsonDocCertifiedItems(certifiedItems);
|
||||
//Example working form:
|
||||
//return R"({"editionNumber":34,"filename":"http://mpassets.highfidelity.com/7f142fde-541a-4902-b33a-25fa89dfba21-v1/Bridger/Hifi_Toon_Male_3.fbx","itemArtist":"EgyMax",
|
||||
//"itemCategories":"Avatars","itemDescription":"This is my first avatar. I hope you like it. More will come","itemName":"Bridger","limitedRun":-1,
|
||||
//"marketplaceID":"7f142fde-541a-4902-b33a-25fa89dfba21","texdir":"http://mpassets.highfidelity.com/7f142fde-541a-4902-b33a-25fa89dfba21-v1/Bridger/textures"})";
|
||||
return jsonDocCertifiedItems.toJson(QJsonDocument::Compact);
|
||||
}
|
||||
|
||||
void MixerAvatar::ownerRequestComplete() {
|
||||
QMutexLocker certifyLocker(&_avatarCertifyLock);
|
||||
QNetworkReply* networkReply = static_cast<QNetworkReply*>(QObject::sender());
|
||||
|
||||
if (networkReply->error() == QNetworkReply::NoError) {
|
||||
_dynamicMarketResponse = networkReply->readAll();
|
||||
_verifyState = ownerResponse;
|
||||
_pendingEvent = true;
|
||||
} else {
|
||||
auto jsonData = QJsonDocument::fromJson(networkReply->readAll())["data"];
|
||||
if (!jsonData.isUndefined() && !jsonData.toObject()["message"].isUndefined()) {
|
||||
qCDebug(avatars) << "Owner lookup failed for" << getDisplayName() << ":"
|
||||
<< jsonData.toObject()["message"].toString();
|
||||
_verifyState = error;
|
||||
_pendingEvent = false;
|
||||
}
|
||||
}
|
||||
networkReply->deleteLater();
|
||||
}
|
||||
|
||||
void MixerAvatar::processCertifyEvents() {
|
||||
if (!_pendingEvent) {
|
||||
return;
|
||||
}
|
||||
|
||||
QMutexLocker certifyLocker(&_avatarCertifyLock);
|
||||
switch (_verifyState) {
|
||||
|
||||
case receivedFST:
|
||||
{
|
||||
generateFSTHash();
|
||||
if (_certificateIdFromFST.length() != 0) {
|
||||
QString& marketplacePublicKey = EntityItem::_marketplacePublicKey;
|
||||
bool staticVerification = validateFSTHash(marketplacePublicKey);
|
||||
_verifyState = staticVerification ? staticValidation : verificationFailed;
|
||||
|
||||
if (_verifyState == staticValidation) {
|
||||
static const QString POP_MARKETPLACE_API { "/api/v1/commerce/proof_of_purchase_status/transfer" };
|
||||
auto& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkRequest networkRequest;
|
||||
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL();
|
||||
requestURL.setPath(POP_MARKETPLACE_API);
|
||||
networkRequest.setUrl(requestURL);
|
||||
|
||||
QJsonObject request;
|
||||
request["certificate_id"] = _certificateIdFromFST;
|
||||
_verifyState = requestingOwner;
|
||||
QNetworkReply* networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson());
|
||||
connect(networkReply, &QNetworkReply::finished, this, &MixerAvatar::ownerRequestComplete);
|
||||
} else {
|
||||
_needsIdentityUpdate = true;
|
||||
_pendingEvent = false;
|
||||
qCDebug(avatars) << "Avatar" << getDisplayName() << "FAILED static certification";
|
||||
}
|
||||
} else { // FST doesn't have a certificate, so noncertified rather than failed:
|
||||
_pendingEvent = false;
|
||||
_verifyState = nonCertified;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ownerResponse:
|
||||
{
|
||||
QJsonDocument responseJson = QJsonDocument::fromJson(_dynamicMarketResponse.toUtf8());
|
||||
QString ownerPublicKey;
|
||||
bool ownerValid = false;
|
||||
if (responseJson["status"].toString() == "success") {
|
||||
QJsonValue jsonData = responseJson["data"];
|
||||
if (jsonData.isObject()) {
|
||||
auto ownerJson = jsonData["transfer_recipient_key"];
|
||||
if (ownerJson.isString()) {
|
||||
ownerPublicKey = ownerJson.toString();
|
||||
}
|
||||
auto transferStatusJson = jsonData["transfer_status"];
|
||||
if (transferStatusJson.isArray() && transferStatusJson.toArray()[0].toString() == "confirmed") {
|
||||
ownerValid = true;
|
||||
}
|
||||
}
|
||||
if (ownerValid && !ownerPublicKey.isEmpty()) {
|
||||
if (ownerPublicKey.startsWith("-----BEGIN ")){
|
||||
_ownerPublicKey = ownerPublicKey;
|
||||
} else {
|
||||
_ownerPublicKey = "-----BEGIN PUBLIC KEY-----\n"
|
||||
+ ownerPublicKey
|
||||
+ "\n-----END PUBLIC KEY-----\n";
|
||||
}
|
||||
sendOwnerChallenge();
|
||||
_verifyState = challengeClient;
|
||||
} else {
|
||||
_verifyState = error;
|
||||
}
|
||||
} else {
|
||||
qCDebug(avatars) << "Get owner status failed for " << getDisplayName() << _marketplaceIdFromURL <<
|
||||
"message:" << responseJson["message"].toString();
|
||||
_verifyState = error;
|
||||
}
|
||||
_pendingEvent = false;
|
||||
break;
|
||||
}
|
||||
|
||||
case challengeResponse:
|
||||
{
|
||||
if (_challengeResponse.length() < 8) {
|
||||
_verifyState = error;
|
||||
_pendingEvent = false;
|
||||
break;
|
||||
}
|
||||
|
||||
int avatarIDLength;
|
||||
int signedNonceLength;
|
||||
{
|
||||
QDataStream responseStream(_challengeResponse);
|
||||
responseStream.setByteOrder(QDataStream::LittleEndian);
|
||||
responseStream >> avatarIDLength >> signedNonceLength;
|
||||
}
|
||||
QByteArray avatarID(_challengeResponse.data() + 2 * sizeof(int), avatarIDLength);
|
||||
QByteArray signedNonce(_challengeResponse.data() + 2 * sizeof(int) + avatarIDLength, signedNonceLength);
|
||||
|
||||
bool challengeResult = EntityItemProperties::verifySignature(_ownerPublicKey, _challengeNonceHash,
|
||||
QByteArray::fromBase64(signedNonce));
|
||||
_verifyState = challengeResult ? verificationSucceeded : verificationFailed;
|
||||
_needsIdentityUpdate = true;
|
||||
if (_verifyState == verificationFailed) {
|
||||
qCDebug(avatars) << "Dynamic verification FAILED for " << getDisplayName() << getSessionUUID();
|
||||
} else {
|
||||
qCDebug(avatars) << "Dynamic verification SUCCEEDED for " << getDisplayName() << getSessionUUID();
|
||||
}
|
||||
_pendingEvent = false;
|
||||
break;
|
||||
}
|
||||
|
||||
case requestingOwner:
|
||||
{ // Qt networking done on this thread:
|
||||
QCoreApplication::processEvents();
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
qCDebug(avatars) << "Unexpected verify state" << _verifyState;
|
||||
break;
|
||||
|
||||
} // close switch
|
||||
}
|
||||
|
||||
void MixerAvatar::sendOwnerChallenge() {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
QByteArray avatarID = ("{" + _marketplaceIdFromFST + "}").toUtf8();
|
||||
QByteArray nonce = QUuid::createUuid().toByteArray();
|
||||
|
||||
auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnership,
|
||||
2 * sizeof(int) + nonce.length() + avatarID.length(), true);
|
||||
challengeOwnershipPacket->writePrimitive(avatarID.length());
|
||||
challengeOwnershipPacket->writePrimitive(nonce.length());
|
||||
challengeOwnershipPacket->write(avatarID);
|
||||
challengeOwnershipPacket->write(nonce);
|
||||
|
||||
nodeList->sendPacket(std::move(challengeOwnershipPacket), *(nodeList->nodeWithUUID(getSessionUUID())) );
|
||||
QCryptographicHash nonceHash(QCryptographicHash::Sha256);
|
||||
nonceHash.addData(nonce);
|
||||
_challengeNonceHash = nonceHash.result();
|
||||
|
||||
static constexpr int CHALLENGE_TIMEOUT_MS = 10 * 1000; // 10 s
|
||||
_challengeTimeout.setInterval(CHALLENGE_TIMEOUT_MS);
|
||||
_challengeTimeout.connect(&_challengeTimeout, &QTimer::timeout, [this]() {
|
||||
_verifyState = verificationFailed;
|
||||
_needsIdentityUpdate = true;
|
||||
});
|
||||
}
|
||||
|
||||
void MixerAvatar::handleChallengeResponse(ReceivedMessage* response) {
|
||||
QByteArray avatarID;
|
||||
QByteArray encryptedNonce;
|
||||
QMutexLocker certifyLocker(&_avatarCertifyLock);
|
||||
if (_verifyState == challengeClient) {
|
||||
_challengeTimeout.stop();
|
||||
_challengeResponse = response->readAll();
|
||||
_verifyState = challengeResponse;
|
||||
_pendingEvent = true;
|
||||
}
|
||||
}
|
|
@ -17,14 +17,55 @@
|
|||
|
||||
#include <AvatarData.h>
|
||||
|
||||
class ResourceRequest;
|
||||
|
||||
class MixerAvatar : public AvatarData {
|
||||
public:
|
||||
bool getNeedsHeroCheck() const { return _needsHeroCheck; }
|
||||
void setNeedsHeroCheck(bool needsHeroCheck = true)
|
||||
{ _needsHeroCheck = needsHeroCheck; }
|
||||
void setNeedsHeroCheck(bool needsHeroCheck = true) { _needsHeroCheck = needsHeroCheck; }
|
||||
|
||||
void fetchAvatarFST();
|
||||
virtual bool isCertifyFailed() const override { return _verifyState == verificationFailed; }
|
||||
bool needsIdentityUpdate() const { return _needsIdentityUpdate; }
|
||||
void setNeedsIdentityUpdate(bool value = true) { _needsIdentityUpdate = value; }
|
||||
|
||||
void processCertifyEvents();
|
||||
void handleChallengeResponse(ReceivedMessage* response);
|
||||
|
||||
private:
|
||||
bool _needsHeroCheck { false };
|
||||
|
||||
// Avatar certification/verification:
|
||||
enum VerifyState { nonCertified, requestingFST, receivedFST, staticValidation, requestingOwner, ownerResponse,
|
||||
challengeClient, challengeResponse, verified, verificationFailed, verificationSucceeded, error };
|
||||
Q_ENUM(VerifyState);
|
||||
VerifyState _verifyState { nonCertified };
|
||||
std::atomic<bool> _pendingEvent { false };
|
||||
QMutex _avatarCertifyLock;
|
||||
ResourceRequest* _avatarRequest { nullptr };
|
||||
QString _marketplaceIdFromURL;
|
||||
QString _marketplaceIdFromFST;
|
||||
QByteArray _avatarFSTContents;
|
||||
QByteArray _certificateHash;
|
||||
QString _certificateIdFromURL;
|
||||
QString _certificateIdFromFST;
|
||||
QString _dynamicMarketResponse;
|
||||
QString _ownerPublicKey;
|
||||
QByteArray _challengeNonceHash;
|
||||
QByteArray _challengeResponse;
|
||||
QTimer _challengeTimeout;
|
||||
bool _needsIdentityUpdate { false };
|
||||
|
||||
bool generateFSTHash();
|
||||
bool validateFSTHash(const QString& publicKey);
|
||||
QByteArray canonicalJson(const QString fstFile);
|
||||
void sendOwnerChallenge();
|
||||
|
||||
static const QString VERIFY_FAIL_MODEL;
|
||||
|
||||
private slots:
|
||||
void fstRequestComplete();
|
||||
void ownerRequestComplete();
|
||||
};
|
||||
|
||||
using MixerAvatarSharedPointer = std::shared_ptr<MixerAvatar>;
|
||||
|
|
|
@ -89,7 +89,7 @@ if (APPLE)
|
|||
string(REGEX MATCH "^[0-9]+\\.[0-9]+" OSX_VERSION ${OSX_VERSION})
|
||||
message(STATUS "Detected OS X version = ${OSX_VERSION}")
|
||||
|
||||
set(OSX_SDK "${OSX_VERSION}" CACHE String "OS X SDK version to look for inside Xcode bundle or at OSX_SDK_PATH")
|
||||
set(OSX_SDK "${OSX_VERSION}" CACHE STRING "OS X SDK version to look for inside Xcode bundle or at OSX_SDK_PATH")
|
||||
|
||||
# set our OS X deployment target
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.9)
|
||||
|
|
22
cmake/externals/LibOVR/CMakeLists.txt
vendored
22
cmake/externals/LibOVR/CMakeLists.txt
vendored
|
@ -27,12 +27,12 @@ if (WIN32)
|
|||
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
|
||||
set(LIBOVR_DIR ${INSTALL_DIR})
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${LIBOVR_DIR}/Include CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${LIBOVR_DIR}/Lib/LibOVRd.lib CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${LIBOVR_DIR}/Lib/LibOVR.lib CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${LIBOVR_DIR}/Include CACHE STRING INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${LIBOVR_DIR}/Lib/LibOVRd.lib CACHE STRING INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${LIBOVR_DIR}/Lib/LibOVR.lib CACHE STRING INTERNAL)
|
||||
include(SelectLibraryConfigurations)
|
||||
select_library_configurations(LIBOVR)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${${EXTERNAL_NAME_UPPER}_LIBRARIES} CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${${EXTERNAL_NAME_UPPER}_LIBRARIES} CACHE STRING INTERNAL)
|
||||
|
||||
elseif(APPLE)
|
||||
|
||||
|
@ -50,11 +50,11 @@ elseif(APPLE)
|
|||
# In theory we should use the Headers path inside the framework, as seen here
|
||||
# but unfortunately Oculus doesn't seem to have figured out automated testing
|
||||
# so they released a framework with missing headers.
|
||||
#set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/LibOVR/Lib/Mac/Release/LibOVR.framework/Headers/ CACHE TYPE INTERNAL)
|
||||
#set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/LibOVR/Lib/Mac/Release/LibOVR.framework/Headers/ CACHE STRING INTERNAL)
|
||||
|
||||
# Work around the broken framework by using a different path for the headers.
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/LibOVR/Include CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/LibOVR/Lib/Mac/Release/LibOVR.framework/LibOVR CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/LibOVR/Include CACHE STRING INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/LibOVR/Lib/Mac/Release/LibOVR.framework/LibOVR CACHE STRING INTERNAL)
|
||||
|
||||
|
||||
|
||||
|
@ -74,8 +74,8 @@ elseif(NOT ANDROID)
|
|||
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libovr.a CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG "" CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libovr.a CACHE STRING INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG "" CACHE STRING INTERNAL)
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
find_package(X11 REQUIRED)
|
||||
|
@ -89,8 +89,8 @@ elseif(NOT ANDROID)
|
|||
|
||||
select_library_configurations(${EXTERNAL_NAME_UPPER})
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/LibOVR/Include ${SOURCE_DIR}/LibOVR/Src CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${${EXTERNAL_NAME_UPPER}_LIBRARY} ${${EXTERNAL_NAME_UPPER}_LIBRARY_EXTRAS} CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/LibOVR/Include ${SOURCE_DIR}/LibOVR/Src CACHE STRING INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${${EXTERNAL_NAME_UPPER}_LIBRARY} ${${EXTERNAL_NAME_UPPER}_LIBRARY_EXTRAS} CACHE STRING INTERNAL)
|
||||
endif()
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
|
|
|
@ -20,12 +20,12 @@ if (WIN32)
|
|||
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
|
||||
|
||||
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${SOURCE_DIR}/Windows/LibOVRPlatform64_1.lib CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${SOURCE_DIR}/Windows/LibOVRPlatform64_1.lib CACHE STRING INTERNAL)
|
||||
else()
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${SOURCE_DIR}/Windows/LibOVRPlatform32_1.lib CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${SOURCE_DIR}/Windows/LibOVRPlatform32_1.lib CACHE STRING INTERNAL)
|
||||
endif()
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/Include CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/Include CACHE STRING INTERNAL)
|
||||
endif ()
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
|
|
|
@ -36,10 +36,10 @@ set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
|||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE STRING INTERNAL)
|
||||
|
||||
if (WIN32)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/audio.lib CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/audio.lib CACHE STRING INTERNAL)
|
||||
else()
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/libaudio.a CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/libaudio.a CACHE STRING INTERNAL)
|
||||
endif()
|
||||
|
|
12
cmake/externals/neuron/CMakeLists.txt
vendored
12
cmake/externals/neuron/CMakeLists.txt
vendored
|
@ -21,9 +21,9 @@ set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
|||
|
||||
# set include dir
|
||||
if(WIN32)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS "${SOURCE_DIR}/NeuronDataReader_Windows/include" CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS "${SOURCE_DIR}/NeuronDataReader_Windows/include" CACHE STRING INTERNAL)
|
||||
elseif(APPLE)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS "${SOURCE_DIR}/NeuronDataReader_Mac/include" CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS "${SOURCE_DIR}/NeuronDataReader_Mac/include" CACHE STRING INTERNAL)
|
||||
else()
|
||||
# Unsupported
|
||||
endif()
|
||||
|
@ -37,16 +37,16 @@ if(WIN32)
|
|||
endif()
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_LIB_PATH "${SOURCE_DIR}/NeuronDataReader_Windows/lib/${ARCH_DIR}")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE "${${EXTERNAL_NAME_UPPER}_LIB_PATH}/NeuronDataReader.lib" CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES "${${EXTERNAL_NAME_UPPER}_LIB_PATH}/NeuronDataReader.lib" CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE "${${EXTERNAL_NAME_UPPER}_LIB_PATH}/NeuronDataReader.lib" CACHE STRING INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES "${${EXTERNAL_NAME_UPPER}_LIB_PATH}/NeuronDataReader.lib" CACHE STRING INTERNAL)
|
||||
|
||||
add_paths_to_fixup_libs("${${EXTERNAL_NAME_UPPER}_LIB_PATH}")
|
||||
|
||||
elseif(APPLE)
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_LIB_PATH "${SOURCE_DIR}/NeuronDataReader_Mac/dylib")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE "${${EXTERNAL_NAME_UPPER}_LIB_PATH}/NeuronDataReader.dylib" CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES "${${EXTERNAL_NAME_UPPER}_LIB_PATH}/NeuronDataReader.dylib" CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE "${${EXTERNAL_NAME_UPPER}_LIB_PATH}/NeuronDataReader.dylib" CACHE STRING INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES "${${EXTERNAL_NAME_UPPER}_LIB_PATH}/NeuronDataReader.dylib" CACHE STRING INTERNAL)
|
||||
|
||||
add_paths_to_fixup_libs("${${EXTERNAL_NAME_UPPER}_LIB_PATH}")
|
||||
|
||||
|
|
6
cmake/externals/sixense/CMakeLists.txt
vendored
6
cmake/externals/sixense/CMakeLists.txt
vendored
|
@ -30,7 +30,7 @@ set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
|||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE STRING INTERNAL)
|
||||
|
||||
if (WIN32)
|
||||
|
||||
|
@ -52,7 +52,7 @@ if (WIN32)
|
|||
set(${EXTERNAL_NAME_UPPER}_LIB_PATH "${SOURCE_DIR}/lib/${ARCH_DIR}/release_dll")
|
||||
endif()
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE "${${EXTERNAL_NAME_UPPER}_LIB_PATH}/sixense${ARCH_SUFFIX}.lib" CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE "${${EXTERNAL_NAME_UPPER}_LIB_PATH}/sixense${ARCH_SUFFIX}.lib" CACHE STRING INTERNAL)
|
||||
add_paths_to_fixup_libs("${${EXTERNAL_NAME_UPPER}_DLL_PATH}")
|
||||
|
||||
elseif(APPLE)
|
||||
|
@ -62,7 +62,7 @@ elseif(APPLE)
|
|||
elseif(NOT ANDROID)
|
||||
|
||||
# FIXME need to account for different architectures
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${SOURCE_DIR}/lib/linux_x64/release/libsixense_x64.so CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${SOURCE_DIR}/lib/linux_x64/release/libsixense_x64.so CACHE STRING INTERNAL)
|
||||
|
||||
endif()
|
||||
|
||||
|
|
8
cmake/externals/steamworks/CMakeLists.txt
vendored
8
cmake/externals/steamworks/CMakeLists.txt
vendored
|
@ -21,7 +21,7 @@ set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
|||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/public CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/public CACHE STRING INTERNAL)
|
||||
|
||||
if (WIN32)
|
||||
|
||||
|
@ -36,12 +36,12 @@ if (WIN32)
|
|||
set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${ARCH_DIR})
|
||||
set(${EXTERNAL_NAME_UPPER}_LIB_PATH ${ARCH_DIR})
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE "${${EXTERNAL_NAME_UPPER}_LIB_PATH}/steam_api${ARCH_SUFFIX}.lib" CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE "${${EXTERNAL_NAME_UPPER}_LIB_PATH}/steam_api${ARCH_SUFFIX}.lib" CACHE STRING INTERNAL)
|
||||
add_paths_to_fixup_libs("${${EXTERNAL_NAME_UPPER}_DLL_PATH}")
|
||||
|
||||
elseif(APPLE)
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${SOURCE_DIR}/redistributable_bin/osx32/libsteam_api.dylib CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${SOURCE_DIR}/redistributable_bin/osx32/libsteam_api.dylib CACHE STRING INTERNAL)
|
||||
|
||||
set(_STEAMWORKS_LIB_DIR "${SOURCE_DIR}/redistributable_bin/osx32")
|
||||
ExternalProject_Add_Step(
|
||||
|
@ -57,6 +57,6 @@ elseif(APPLE)
|
|||
elseif(NOT ANDROID)
|
||||
|
||||
# FIXME need to account for different architectures
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${SOURCE_DIR}/redistributable_bin/linux64/libsteam_api.so CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${SOURCE_DIR}/redistributable_bin/linux64/libsteam_api.so CACHE STRING INTERNAL)
|
||||
|
||||
endif()
|
||||
|
|
2
cmake/externals/tbb/CMakeLists.txt
vendored
2
cmake/externals/tbb/CMakeLists.txt
vendored
|
@ -102,6 +102,6 @@ if (DEFINED _TBB_LIB_DIR)
|
|||
endif ()
|
||||
|
||||
if (DEFINED ${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE TYPE "List of tbb include directories")
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE STRING "List of tbb include directories")
|
||||
endif ()
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
macro(TARGET_BREAKPAD)
|
||||
if (ANDROID)
|
||||
set(INSTALL_DIR ${HIFI_ANDROID_PRECOMPILED}/breakpad)
|
||||
set(BREAKPAD_INCLUDE_DIRS "${INSTALL_DIR}/include" CACHE TYPE INTERNAL)
|
||||
set(BREAKPAD_INCLUDE_DIRS "${INSTALL_DIR}/include" CACHE STRING INTERNAL)
|
||||
set(LIB_DIR ${INSTALL_DIR}/lib)
|
||||
list(APPEND BREAKPAD_LIBRARIES ${LIB_DIR}/libbreakpad_client.a)
|
||||
target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${BREAKPAD_INCLUDE_DIRS})
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
macro(TARGET_BULLET)
|
||||
if (ANDROID)
|
||||
set(INSTALL_DIR ${HIFI_ANDROID_PRECOMPILED}/bullet)
|
||||
set(BULLET_INCLUDE_DIRS "${INSTALL_DIR}/include/bullet" CACHE TYPE INTERNAL)
|
||||
set(BULLET_INCLUDE_DIRS "${INSTALL_DIR}/include/bullet" CACHE STRING INTERNAL)
|
||||
|
||||
set(LIB_DIR ${INSTALL_DIR}/lib)
|
||||
list(APPEND BULLET_LIBRARIES ${LIB_DIR}/libBulletDynamics.a)
|
||||
|
|
|
@ -3,7 +3,7 @@ macro(TARGET_DRACO)
|
|||
find_library(LIBPATH ${LIB} PATHS )
|
||||
if (ANDROID)
|
||||
set(INSTALL_DIR ${HIFI_ANDROID_PRECOMPILED}/draco)
|
||||
set(DRACO_INCLUDE_DIRS "${INSTALL_DIR}/include" CACHE TYPE INTERNAL)
|
||||
set(DRACO_INCLUDE_DIRS "${INSTALL_DIR}/include" CACHE STRING INTERNAL)
|
||||
set(LIB_DIR ${INSTALL_DIR}/lib)
|
||||
list(APPEND DRACO_LIBRARIES ${LIB_DIR}/libdraco.a)
|
||||
list(APPEND DRACO_LIBRARIES ${LIB_DIR}/libdracodec.a)
|
||||
|
@ -21,4 +21,4 @@ macro(TARGET_DRACO)
|
|||
select_library_configurations(DRACO)
|
||||
target_link_libraries(${TARGET_NAME} ${DRACO_LIBRARY})
|
||||
endif()
|
||||
endmacro()
|
||||
endmacro()
|
||||
|
|
|
@ -9,9 +9,9 @@ macro(TARGET_HIFIAUDIOCODEC)
|
|||
if (ANDROID)
|
||||
set(HIFIAC_INSTALL_DIR ${HIFI_ANDROID_PRECOMPILED}/hifiAC/codecSDK)
|
||||
set(HIFIAC_LIB_DIR "${HIFIAC_INSTALL_DIR}/Release")
|
||||
set(HIFIAC_INCLUDE_DIRS "${HIFIAC_INSTALL_DIR}/include" CACHE TYPE INTERNAL)
|
||||
set(HIFIAC_INCLUDE_DIRS "${HIFIAC_INSTALL_DIR}/include" CACHE STRING INTERNAL)
|
||||
list(APPEND HIFIAC_LIBS "${HIFIAC_LIB_DIR}/libaudio.a")
|
||||
set(HIFIAC_LIBRARIES ${HIFIAC_LIBS} CACHE TYPE INTERNAL)
|
||||
set(HIFIAC_LIBRARIES ${HIFIAC_LIBS} CACHE STRING INTERNAL)
|
||||
else()
|
||||
add_dependency_external_projects(hifiAudioCodec)
|
||||
target_include_directories(${TARGET_NAME} PRIVATE ${HIFIAUDIOCODEC_INCLUDE_DIRS})
|
||||
|
|
|
@ -9,12 +9,12 @@ macro(TARGET_NVTT)
|
|||
if (ANDROID)
|
||||
set(NVTT_INSTALL_DIR ${HIFI_ANDROID_PRECOMPILED}/nvtt)
|
||||
set(NVTT_LIB_DIR "${NVTT_INSTALL_DIR}/lib")
|
||||
set(NVTT_INCLUDE_DIRS "${NVTT_INSTALL_DIR}/include" CACHE TYPE INTERNAL)
|
||||
set(NVTT_INCLUDE_DIRS "${NVTT_INSTALL_DIR}/include" CACHE STRING INTERNAL)
|
||||
list(APPEND NVTT_LIBS "${NVTT_LIB_DIR}/libnvcore.so")
|
||||
list(APPEND NVTT_LIBS "${NVTT_LIB_DIR}/libnvmath.so")
|
||||
list(APPEND NVTT_LIBS "${NVTT_LIB_DIR}/libnvimage.so")
|
||||
list(APPEND NVTT_LIBS "${NVTT_LIB_DIR}/libnvtt.so")
|
||||
set(NVTT_LIBRARIES ${NVTT_LIBS} CACHE TYPE INTERNAL)
|
||||
set(NVTT_LIBRARIES ${NVTT_LIBS} CACHE STRING INTERNAL)
|
||||
target_include_directories(${TARGET_NAME} PRIVATE ${NVTT_INCLUDE_DIRS})
|
||||
else()
|
||||
find_library(NVTT_LIBRARY_RELEASE nvtt PATHS ${VCPKG_INSTALL_ROOT}/lib NO_DEFAULT_PATH)
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
macro(TARGET_OPENSSL)
|
||||
if (ANDROID)
|
||||
set(OPENSSL_INSTALL_DIR ${HIFI_ANDROID_PRECOMPILED}/openssl)
|
||||
set(OPENSSL_INCLUDE_DIR "${OPENSSL_INSTALL_DIR}/include" CACHE TYPE INTERNAL)
|
||||
set(OPENSSL_LIBRARIES "${OPENSSL_INSTALL_DIR}/lib/libcrypto.a;${OPENSSL_INSTALL_DIR}/lib/libssl.a" CACHE TYPE INTERNAL)
|
||||
set(OPENSSL_INCLUDE_DIR "${OPENSSL_INSTALL_DIR}/include" CACHE STRING INTERNAL)
|
||||
set(OPENSSL_LIBRARIES "${OPENSSL_INSTALL_DIR}/lib/libcrypto.a;${OPENSSL_INSTALL_DIR}/lib/libssl.a" CACHE STRING INTERNAL)
|
||||
else()
|
||||
# using VCPKG for OpenSSL
|
||||
find_package(OpenSSL REQUIRED)
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
macro(TARGET_POLYVOX)
|
||||
if (ANDROID)
|
||||
set(INSTALL_DIR ${HIFI_ANDROID_PRECOMPILED}/polyvox)
|
||||
set(POLYVOX_INCLUDE_DIRS "${INSTALL_DIR}/include" CACHE TYPE INTERNAL)
|
||||
set(POLYVOX_INCLUDE_DIRS "${INSTALL_DIR}/include" CACHE STRING INTERNAL)
|
||||
set(LIB_DIR ${INSTALL_DIR}/lib)
|
||||
list(APPEND POLYVOX_LIBRARIES ${LIB_DIR}/libPolyVoxUtil.so)
|
||||
list(APPEND POLYVOX_LIBRARIES ${LIB_DIR}/Release/libPolyVoxCore.so)
|
||||
|
|
|
@ -1357,8 +1357,8 @@
|
|||
"name": "connection_rate",
|
||||
"label": "Connection Rate",
|
||||
"help": "Number of new agents that can connect to the mixer every second",
|
||||
"placeholder": "50",
|
||||
"default": "50",
|
||||
"placeholder": "10000000",
|
||||
"default": "10000000",
|
||||
"advanced": true
|
||||
},
|
||||
{
|
||||
|
|
BIN
interface/resources/images/AvatarTheftBanner.png
Normal file
BIN
interface/resources/images/AvatarTheftBanner.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 48 KiB |
BIN
interface/resources/meshes/mannequin/man_stolen.fbx
Normal file
BIN
interface/resources/meshes/mannequin/man_stolen.fbx
Normal file
Binary file not shown.
86
interface/resources/meshes/verifyFailed.fst
Normal file
86
interface/resources/meshes/verifyFailed.fst
Normal file
|
@ -0,0 +1,86 @@
|
|||
name = mannequin2
|
||||
type = body+head
|
||||
scale = 1
|
||||
filename = mannequin/man_stolen.fbx
|
||||
texdir = textures
|
||||
joint = jointEyeLeft = LeftEye
|
||||
joint = jointRightHand = RightHand
|
||||
joint = jointHead = Head
|
||||
joint = jointEyeRight = RightEye
|
||||
joint = jointNeck = Neck
|
||||
joint = jointLeftHand = LeftHand
|
||||
joint = jointLean = Spine
|
||||
joint = jointRoot = Hips
|
||||
freeJoint = LeftArm
|
||||
freeJoint = LeftForeArm
|
||||
freeJoint = RightArm
|
||||
freeJoint = RightForeArm
|
||||
jointIndex = RightHandPinky2 = 19
|
||||
jointIndex = LeftHandPinky3 = 44
|
||||
jointIndex = RightToeBase = 9
|
||||
jointIndex = LeftHandRing4 = 49
|
||||
jointIndex = LeftHandPinky1 = 42
|
||||
jointIndex = LeftHandRing1 = 46
|
||||
jointIndex = LeftLeg = 2
|
||||
jointIndex = RightHandIndex4 = 29
|
||||
jointIndex = LeftHandRing3 = 48
|
||||
jointIndex = RightShoulder = 14
|
||||
jointIndex = RightArm = 15
|
||||
jointIndex = Neck = 62
|
||||
jointIndex = RightHandMiddle2 = 35
|
||||
jointIndex = HeadTop_End = 66
|
||||
jointIndex = LeftHandRing2 = 47
|
||||
jointIndex = RightHandThumb1 = 30
|
||||
jointIndex = RightHandRing3 = 24
|
||||
jointIndex = LeftHandIndex3 = 52
|
||||
jointIndex = LeftForeArm = 40
|
||||
jointIndex = face = 68
|
||||
jointIndex = LeftToe_End = 5
|
||||
jointIndex = RightHandThumb3 = 32
|
||||
jointIndex = RightEye = 65
|
||||
jointIndex = Spine = 11
|
||||
jointIndex = LeftEye = 64
|
||||
jointIndex = LeftToeBase = 4
|
||||
jointIndex = LeftHandIndex4 = 53
|
||||
jointIndex = RightHandPinky4 = 21
|
||||
jointIndex = RightHandMiddle1 = 34
|
||||
jointIndex = Spine1 = 12
|
||||
jointIndex = LeftHandIndex2 = 51
|
||||
jointIndex = RightToe_End = 10
|
||||
jointIndex = RightHand = 17
|
||||
jointIndex = LeftUpLeg = 1
|
||||
jointIndex = RightHandRing1 = 22
|
||||
jointIndex = RightUpLeg = 6
|
||||
jointIndex = RightHandMiddle4 = 37
|
||||
jointIndex = Head = 63
|
||||
jointIndex = RightHandMiddle3 = 36
|
||||
jointIndex = RightHandIndex1 = 26
|
||||
jointIndex = LeftHandMiddle4 = 61
|
||||
jointIndex = LeftHandPinky4 = 45
|
||||
jointIndex = Hips = 0
|
||||
jointIndex = body = 67
|
||||
jointIndex = RightHandThumb2 = 31
|
||||
jointIndex = LeftHandThumb2 = 55
|
||||
jointIndex = RightHandThumb4 = 33
|
||||
jointIndex = RightHandPinky3 = 20
|
||||
jointIndex = LeftHandPinky2 = 43
|
||||
jointIndex = LeftShoulder = 38
|
||||
jointIndex = RightHandIndex3 = 28
|
||||
jointIndex = LeftHandThumb4 = 57
|
||||
jointIndex = RightLeg = 7
|
||||
jointIndex = RightHandIndex2 = 27
|
||||
jointIndex = LeftHandMiddle3 = 60
|
||||
jointIndex = RightHandRing4 = 25
|
||||
jointIndex = LeftHandThumb1 = 54
|
||||
jointIndex = LeftArm = 39
|
||||
jointIndex = LeftHandThumb3 = 56
|
||||
jointIndex = LeftHandMiddle1 = 58
|
||||
jointIndex = RightHandPinky1 = 18
|
||||
jointIndex = Spine2 = 13
|
||||
jointIndex = RightHandRing2 = 23
|
||||
jointIndex = RightForeArm = 16
|
||||
jointIndex = LeftHandIndex1 = 50
|
||||
jointIndex = RightFoot = 8
|
||||
jointIndex = LeftHandMiddle2 = 59
|
||||
jointIndex = LeftHand = 41
|
||||
jointIndex = LeftFoot = 3
|
|
@ -664,9 +664,10 @@ private:
|
|||
|
||||
/**jsdoc
|
||||
* <p>The <code>Controller.Hardware.Application</code> object has properties representing Interface's state. The property
|
||||
* values are integer IDs, uniquely identifying each output. <em>Read-only.</em> These can be mapped to actions or functions or
|
||||
* <code>Controller.Standard</code> items in a {@link RouteObject} mapping (e.g., using the {@link RouteObject#when} method).
|
||||
* Each data value is either <code>1.0</code> for "true" or <code>0.0</code> for "false".</p>
|
||||
* values are integer IDs, uniquely identifying each output. <em>Read-only.</em></p>
|
||||
* <p>These states can be mapped to actions or functions or <code>Controller.Standard</code> items in a {@link RouteObject}
|
||||
* mapping (e.g., using the {@link RouteObject#when} method). Each data value is either <code>1.0</code> for "true" or
|
||||
* <code>0.0</code> for "false".</p>
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Property</th><th>Type</th><th>Data</th><th>Description</th></tr>
|
||||
|
@ -680,13 +681,17 @@ private:
|
|||
* <tr><td><code>CameraIndependent</code></td><td>number</td><td>number</td><td>The camera is in independent mode.</td></tr>
|
||||
* <tr><td><code>CameraEntity</code></td><td>number</td><td>number</td><td>The camera is in entity mode.</td></tr>
|
||||
* <tr><td><code>InHMD</code></td><td>number</td><td>number</td><td>The user is in HMD mode.</td></tr>
|
||||
* <tr><td><code>AdvancedMovement</code></td><td>number</td><td>number</td><td>Advanced movement controls are enabled.
|
||||
* </td></tr>
|
||||
* <tr><td><code>AdvancedMovement</code></td><td>number</td><td>number</td><td>Advanced movement (walking) controls are
|
||||
* enabled.</td></tr>
|
||||
* <tr><td><code>StrafeEnabled</code></td><td>number</td><td>number</td><td>Strafing is enabled</td></tr>
|
||||
* <tr><td><code>LeftHandDominant</code></td><td>number</td><td>number</td><td>Dominant hand set to left.</td></tr>
|
||||
* <tr><td><code>RightHandDominant</code></td><td>number</td><td>number</td><td>Dominant hand set to right.</td></tr>
|
||||
* <tr><td><code>SnapTurn</code></td><td>number</td><td>number</td><td>Snap turn is enabled.</td></tr>
|
||||
* <tr><td><code>Grounded</code></td><td>number</td><td>number</td><td>The user's avatar is on the ground.</td></tr>
|
||||
* <tr><td><code>NavigationFocused</code></td><td>number</td><td>number</td><td><em>Not used.</em></td></tr>
|
||||
* <tr><td><code>PlatformWindows</code></td><td>number</td><td>number</td><td>The operating system is Windows.</td></tr>
|
||||
* <tr><td><code>PlatformMac</code></td><td>number</td><td>number</td><td>The operating system is Mac.</td></tr>
|
||||
* <tr><td><code>PlatformAndroid</code></td><td>number</td><td>number</td><td>The operating system is Android.</td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef {object} Controller.Hardware-Application
|
||||
|
@ -1330,7 +1335,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
setCrashAnnotation("avatar", avatarURL.toString().toStdString());
|
||||
});
|
||||
|
||||
|
||||
// Inititalize sample before registering
|
||||
_sampleSound = DependencyManager::get<SoundCache>()->getSound(PathUtils::resourcesUrl("sounds/sample.wav"));
|
||||
|
||||
|
@ -1421,6 +1425,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
initializeDisplayPlugins();
|
||||
qCDebug(interfaceapp, "Initialized Display");
|
||||
|
||||
if (_displayPlugin && !_displayPlugin->isHmd()) {
|
||||
_preferredCursor.set(Cursor::Manager::getIconName(Cursor::Icon::SYSTEM));
|
||||
showCursor(Cursor::Manager::lookupIcon(_preferredCursor.get()));
|
||||
}
|
||||
// An audio device changed signal received before the display plugins are set up will cause a crash,
|
||||
// so we defer the setup of the `scripting::Audio` class until this point
|
||||
{
|
||||
|
@ -1440,8 +1448,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
audioScriptingInterface->environmentMuted();
|
||||
}
|
||||
});
|
||||
connect(this, &Application::activeDisplayPluginChanged,
|
||||
reinterpret_cast<scripting::Audio*>(audioScriptingInterface.data()), &scripting::Audio::onContextChanged);
|
||||
QSharedPointer<scripting::Audio> scriptingAudioSharedPointer = qSharedPointerDynamicCast<scripting::Audio>(DependencyManager::get<AudioScriptingInterface>());
|
||||
if (scriptingAudioSharedPointer) {
|
||||
connect(this, &Application::activeDisplayPluginChanged,
|
||||
scriptingAudioSharedPointer.data(), &scripting::Audio::onContextChanged);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the rendering engine. This can be slow on some machines due to lots of
|
||||
|
@ -1634,7 +1645,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
connect(userInputMapper.data(), &UserInputMapper::actionEvent, [this](int action, float state) {
|
||||
using namespace controller;
|
||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||
auto audioScriptingInterface = reinterpret_cast<scripting::Audio*>(DependencyManager::get<AudioScriptingInterface>().data());
|
||||
QSharedPointer<scripting::Audio> audioScriptingInterface = qSharedPointerDynamicCast<scripting::Audio>(DependencyManager::get<AudioScriptingInterface>());
|
||||
{
|
||||
auto actionEnum = static_cast<Action>(action);
|
||||
int key = Qt::Key_unknown;
|
||||
|
@ -1642,10 +1653,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
bool navAxis = false;
|
||||
switch (actionEnum) {
|
||||
case Action::TOGGLE_PUSHTOTALK:
|
||||
if (state > 0.0f) {
|
||||
audioScriptingInterface->setPushingToTalk(true);
|
||||
} else if (state <= 0.0f) {
|
||||
audioScriptingInterface->setPushingToTalk(false);
|
||||
if (audioScriptingInterface) {
|
||||
if (state > 0.0f) {
|
||||
audioScriptingInterface->setPushingToTalk(true);
|
||||
} else if (state <= 0.0f) {
|
||||
audioScriptingInterface->setPushingToTalk(false);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -3377,6 +3390,7 @@ void Application::setupQmlSurface(QQmlContext* surfaceContext, bool setAdditiona
|
|||
surfaceContext->setContextProperty("KeyboardScriptingInterface", DependencyManager::get<KeyboardScriptingInterface>().data());
|
||||
|
||||
if (setAdditionalContextProperties) {
|
||||
qDebug() << "setting additional context properties!";
|
||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||
auto flags = tabletScriptingInterface->getFlags();
|
||||
|
||||
|
@ -4064,9 +4078,6 @@ bool Application::event(QEvent* event) {
|
|||
case QEvent::KeyRelease:
|
||||
keyReleaseEvent(static_cast<QKeyEvent*>(event));
|
||||
return true;
|
||||
case QEvent::FocusIn:
|
||||
focusInEvent(static_cast<QFocusEvent*>(event));
|
||||
return true;
|
||||
case QEvent::FocusOut:
|
||||
focusOutEvent(static_cast<QFocusEvent*>(event));
|
||||
return true;
|
||||
|
@ -4109,6 +4120,11 @@ bool Application::eventFilter(QObject* object, QEvent* event) {
|
|||
return true;
|
||||
}
|
||||
|
||||
auto eventType = event->type();
|
||||
if (eventType == QEvent::KeyPress || eventType == QEvent::KeyRelease || eventType == QEvent::MouseMove) {
|
||||
getRefreshRateManager().resetInactiveTimer();
|
||||
}
|
||||
|
||||
if (event->type() == QEvent::Leave) {
|
||||
getApplicationCompositor().handleLeaveEvent();
|
||||
}
|
||||
|
@ -4298,6 +4314,10 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
if (isMeta) {
|
||||
auto audioClient = DependencyManager::get<AudioClient>();
|
||||
audioClient->setMuted(!audioClient->isMuted());
|
||||
QSharedPointer<scripting::Audio> audioScriptingInterface = qSharedPointerDynamicCast<scripting::Audio>(DependencyManager::get<AudioScriptingInterface>());
|
||||
if (audioScriptingInterface && audioScriptingInterface->getPTT()) {
|
||||
audioScriptingInterface->setPushingToTalk(!audioClient->isMuted());
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -4420,13 +4440,6 @@ void Application::keyReleaseEvent(QKeyEvent* event) {
|
|||
|
||||
}
|
||||
|
||||
void Application::focusInEvent(QFocusEvent* event) {
|
||||
if (!_aboutToQuit && _startUpFinished) {
|
||||
getRefreshRateManager().setRefreshRateRegime(RefreshRateManager::RefreshRateRegime::RUNNING);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Application::focusOutEvent(QFocusEvent* event) {
|
||||
auto inputPlugins = PluginManager::getInstance()->getInputPlugins();
|
||||
foreach(auto inputPlugin, inputPlugins) {
|
||||
|
@ -4434,10 +4447,6 @@ void Application::focusOutEvent(QFocusEvent* event) {
|
|||
inputPlugin->pluginFocusOutEvent();
|
||||
}
|
||||
}
|
||||
|
||||
if (!_aboutToQuit && _startUpFinished) {
|
||||
getRefreshRateManager().setRefreshRateRegime(RefreshRateManager::RefreshRateRegime::UNFOCUS);
|
||||
}
|
||||
// FIXME spacemouse code still needs cleanup
|
||||
#if 0
|
||||
//SpacemouseDevice::getInstance().focusOutEvent();
|
||||
|
@ -5359,8 +5368,10 @@ void Application::loadSettings() {
|
|||
}
|
||||
}
|
||||
|
||||
auto audioScriptingInterface = reinterpret_cast<scripting::Audio*>(DependencyManager::get<AudioScriptingInterface>().data());
|
||||
audioScriptingInterface->loadData();
|
||||
QSharedPointer<scripting::Audio> audioScriptingInterface = qSharedPointerDynamicCast<scripting::Audio>(DependencyManager::get<AudioScriptingInterface>());
|
||||
if (audioScriptingInterface) {
|
||||
audioScriptingInterface->loadData();
|
||||
}
|
||||
|
||||
getMyAvatar()->loadData();
|
||||
_settingsLoaded = true;
|
||||
|
@ -5371,8 +5382,10 @@ void Application::saveSettings() const {
|
|||
DependencyManager::get<AudioClient>()->saveSettings();
|
||||
DependencyManager::get<LODManager>()->saveSettings();
|
||||
|
||||
auto audioScriptingInterface = reinterpret_cast<scripting::Audio*>(DependencyManager::get<AudioScriptingInterface>().data());
|
||||
audioScriptingInterface->saveData();
|
||||
QSharedPointer<scripting::Audio> audioScriptingInterface = qSharedPointerDynamicCast<scripting::Audio>(DependencyManager::get<AudioScriptingInterface>());
|
||||
if (audioScriptingInterface) {
|
||||
audioScriptingInterface->saveData();
|
||||
}
|
||||
|
||||
Menu::getInstance()->saveSettings();
|
||||
getMyAvatar()->saveData();
|
||||
|
@ -5606,7 +5619,7 @@ void Application::resumeAfterLoginDialogActionTaken() {
|
|||
_myCamera.setMode(_previousCameraMode);
|
||||
cameraModeChanged();
|
||||
_startUpFinished = true;
|
||||
getRefreshRateManager().setRefreshRateRegime(RefreshRateManager::RefreshRateRegime::RUNNING);
|
||||
getRefreshRateManager().setRefreshRateRegime(RefreshRateManager::RefreshRateRegime::FOCUS_ACTIVE);
|
||||
}
|
||||
|
||||
void Application::loadAvatarScripts(const QVector<QString>& urls) {
|
||||
|
@ -8463,28 +8476,30 @@ bool Application::takeSnapshotOperators(std::queue<SnapshotOperator>& snapshotOp
|
|||
|
||||
void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio, const QString& filename) {
|
||||
addSnapshotOperator(std::make_tuple([notify, includeAnimated, aspectRatio, filename](const QImage& snapshot) {
|
||||
QString path = DependencyManager::get<Snapshot>()->saveSnapshot(snapshot, filename, TestScriptingInterface::getInstance()->getTestResultsLocation());
|
||||
qApp->postLambdaEvent([snapshot, notify, includeAnimated, aspectRatio, filename] {
|
||||
QString path = DependencyManager::get<Snapshot>()->saveSnapshot(snapshot, filename, TestScriptingInterface::getInstance()->getTestResultsLocation());
|
||||
|
||||
// If we're not doing an animated snapshot as well...
|
||||
if (!includeAnimated) {
|
||||
if (!path.isEmpty()) {
|
||||
// Tell the dependency manager that the capture of the still snapshot has taken place.
|
||||
emit DependencyManager::get<WindowScriptingInterface>()->stillSnapshotTaken(path, notify);
|
||||
}
|
||||
} else if (!SnapshotAnimated::isAlreadyTakingSnapshotAnimated()) {
|
||||
qApp->postLambdaEvent([path, aspectRatio] {
|
||||
// If we're not doing an animated snapshot as well...
|
||||
if (!includeAnimated) {
|
||||
if (!path.isEmpty()) {
|
||||
// Tell the dependency manager that the capture of the still snapshot has taken place.
|
||||
emit DependencyManager::get<WindowScriptingInterface>()->stillSnapshotTaken(path, notify);
|
||||
}
|
||||
} else if (!SnapshotAnimated::isAlreadyTakingSnapshotAnimated()) {
|
||||
// Get an animated GIF snapshot and save it
|
||||
SnapshotAnimated::saveSnapshotAnimated(path, aspectRatio, DependencyManager::get<WindowScriptingInterface>());
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}, aspectRatio, true));
|
||||
}
|
||||
|
||||
void Application::takeSecondaryCameraSnapshot(const bool& notify, const QString& filename) {
|
||||
addSnapshotOperator(std::make_tuple([notify, filename](const QImage& snapshot) {
|
||||
QString snapshotPath = DependencyManager::get<Snapshot>()->saveSnapshot(snapshot, filename, TestScriptingInterface::getInstance()->getTestResultsLocation());
|
||||
qApp->postLambdaEvent([snapshot, notify, filename] {
|
||||
QString snapshotPath = DependencyManager::get<Snapshot>()->saveSnapshot(snapshot, filename, TestScriptingInterface::getInstance()->getTestResultsLocation());
|
||||
|
||||
emit DependencyManager::get<WindowScriptingInterface>()->stillSnapshotTaken(snapshotPath, notify);
|
||||
emit DependencyManager::get<WindowScriptingInterface>()->stillSnapshotTaken(snapshotPath, notify);
|
||||
});
|
||||
}, 0.0f, false));
|
||||
}
|
||||
|
||||
|
@ -8549,11 +8564,20 @@ void Application::activeChanged(Qt::ApplicationState state) {
|
|||
switch (state) {
|
||||
case Qt::ApplicationActive:
|
||||
_isForeground = true;
|
||||
if (!_aboutToQuit && _startUpFinished) {
|
||||
getRefreshRateManager().setRefreshRateRegime(RefreshRateManager::RefreshRateRegime::FOCUS_ACTIVE);
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::ApplicationSuspended:
|
||||
break;
|
||||
case Qt::ApplicationHidden:
|
||||
break;
|
||||
case Qt::ApplicationInactive:
|
||||
if (!_aboutToQuit && _startUpFinished) {
|
||||
getRefreshRateManager().setRefreshRateRegime(RefreshRateManager::RefreshRateRegime::UNFOCUS);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
_isForeground = false;
|
||||
break;
|
||||
|
@ -8884,7 +8908,7 @@ void Application::setDisplayPlugin(DisplayPluginPointer newDisplayPlugin) {
|
|||
RefreshRateManager& refreshRateManager = getRefreshRateManager();
|
||||
refreshRateManager.setRefreshRateOperator(OpenGLDisplayPlugin::getRefreshRateOperator());
|
||||
bool isHmd = newDisplayPlugin->isHmd();
|
||||
RefreshRateManager::UXMode uxMode = isHmd ? RefreshRateManager::UXMode::HMD :
|
||||
RefreshRateManager::UXMode uxMode = isHmd ? RefreshRateManager::UXMode::VR :
|
||||
RefreshRateManager::UXMode::DESKTOP;
|
||||
|
||||
refreshRateManager.setUXMode(uxMode);
|
||||
|
|
|
@ -20,37 +20,40 @@
|
|||
|
||||
#include <display-plugins/hmd/HmdDisplayPlugin.h>
|
||||
|
||||
static const int HMD_TARGET_RATE = 90;
|
||||
static const int VR_TARGET_RATE = 90;
|
||||
|
||||
static const std::array<std::string, RefreshRateManager::RefreshRateProfile::PROFILE_NUM> REFRESH_RATE_PROFILE_TO_STRING =
|
||||
{ { "Eco", "Interactive", "Realtime" } };
|
||||
|
||||
static const std::array<std::string, RefreshRateManager::RefreshRateRegime::REGIME_NUM> REFRESH_RATE_REGIME_TO_STRING =
|
||||
{ { "Running", "Unfocus", "Minimized", "StartUp", "ShutDown" } };
|
||||
{ { "FocusActive", "FocusInactive", "Unfocus", "Minimized", "StartUp", "ShutDown" } };
|
||||
|
||||
static const std::array<std::string, RefreshRateManager::UXMode::UX_NUM> UX_MODE_TO_STRING =
|
||||
{ { "Desktop", "HMD" } };
|
||||
{ { "Desktop", "VR" } };
|
||||
|
||||
static const std::map<std::string, RefreshRateManager::RefreshRateProfile> REFRESH_RATE_PROFILE_FROM_STRING =
|
||||
{ { "Eco", RefreshRateManager::RefreshRateProfile::ECO },
|
||||
{ "Interactive", RefreshRateManager::RefreshRateProfile::INTERACTIVE },
|
||||
{ "Realtime", RefreshRateManager::RefreshRateProfile::REALTIME } };
|
||||
|
||||
static const std::array<int, RefreshRateManager::RefreshRateProfile::PROFILE_NUM> RUNNING_REGIME_PROFILES =
|
||||
{ { 5, 20, 60 } };
|
||||
|
||||
static const std::array<int, RefreshRateManager::RefreshRateProfile::PROFILE_NUM> UNFOCUS_REGIME_PROFILES =
|
||||
{ { 5, 5, 10 } };
|
||||
// Porfile regimes are:
|
||||
// { { "FocusActive", "FocusInactive", "Unfocus", "Minimized", "StartUp", "ShutDown" } }
|
||||
|
||||
static const std::array<int, RefreshRateManager::RefreshRateProfile::PROFILE_NUM> MINIMIZED_REGIME_PROFILE =
|
||||
{ { 2, 2, 2 } };
|
||||
static const std::array<int, RefreshRateManager::RefreshRateRegime::REGIME_NUM> ECO_PROFILE =
|
||||
{ { 20, 10, 5, 2, 30, 30 } };
|
||||
|
||||
static const std::array<int, RefreshRateManager::RefreshRateProfile::PROFILE_NUM> START_AND_SHUTDOWN_REGIME_PROFILES =
|
||||
{ { 30, 30, 30 } };
|
||||
static const std::array<int, RefreshRateManager::RefreshRateRegime::REGIME_NUM> INTERACTIVE_PROFILE =
|
||||
{ { 30, 20, 10, 2, 30, 30 } };
|
||||
|
||||
static const std::array<std::array<int, RefreshRateManager::RefreshRateProfile::PROFILE_NUM>, RefreshRateManager::RefreshRateRegime::REGIME_NUM> REFRESH_RATE_REGIMES =
|
||||
{ { RUNNING_REGIME_PROFILES, UNFOCUS_REGIME_PROFILES, MINIMIZED_REGIME_PROFILE,
|
||||
START_AND_SHUTDOWN_REGIME_PROFILES, START_AND_SHUTDOWN_REGIME_PROFILES } };
|
||||
static const std::array<int, RefreshRateManager::RefreshRateRegime::REGIME_NUM> REALTIME_PROFILE =
|
||||
{ { 60, 60, 10, 2, 30, 30} };
|
||||
|
||||
static const std::array<std::array<int, RefreshRateManager::RefreshRateRegime::REGIME_NUM>, RefreshRateManager::RefreshRateProfile::PROFILE_NUM> REFRESH_RATE_PROFILES =
|
||||
{ { ECO_PROFILE, INTERACTIVE_PROFILE, REALTIME_PROFILE } };
|
||||
|
||||
|
||||
static const int INACTIVE_TIMER_LIMIT = 3000;
|
||||
|
||||
|
||||
std::string RefreshRateManager::refreshRateProfileToString(RefreshRateManager::RefreshRateProfile refreshRateProfile) {
|
||||
|
@ -70,14 +73,36 @@ std::string RefreshRateManager::uxModeToString(RefreshRateManager::RefreshRateMa
|
|||
}
|
||||
|
||||
RefreshRateManager::RefreshRateManager() {
|
||||
_refreshRateProfile = (RefreshRateManager::RefreshRateProfile) _refreshRateMode.get();
|
||||
_refreshRateProfile = (RefreshRateManager::RefreshRateProfile) _refreshRateProfileSetting.get();
|
||||
_inactiveTimer->setInterval(INACTIVE_TIMER_LIMIT);
|
||||
_inactiveTimer->setSingleShot(true);
|
||||
QObject::connect(_inactiveTimer.get(), &QTimer::timeout, [&] {
|
||||
toggleInactive();
|
||||
});
|
||||
}
|
||||
|
||||
void RefreshRateManager::resetInactiveTimer() {
|
||||
if (_uxMode == RefreshRateManager::UXMode::DESKTOP) {
|
||||
auto regime = getRefreshRateRegime();
|
||||
if (regime == RefreshRateRegime::FOCUS_ACTIVE || regime == RefreshRateRegime::FOCUS_INACTIVE) {
|
||||
_inactiveTimer->start();
|
||||
setRefreshRateRegime(RefreshRateManager::RefreshRateRegime::FOCUS_ACTIVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RefreshRateManager::toggleInactive() {
|
||||
if (_uxMode == RefreshRateManager::UXMode::DESKTOP &&
|
||||
getRefreshRateRegime() == RefreshRateManager::RefreshRateRegime::FOCUS_ACTIVE) {
|
||||
setRefreshRateRegime(RefreshRateManager::RefreshRateRegime::FOCUS_INACTIVE);
|
||||
}
|
||||
}
|
||||
|
||||
void RefreshRateManager::setRefreshRateProfile(RefreshRateManager::RefreshRateProfile refreshRateProfile) {
|
||||
if (_refreshRateProfile != refreshRateProfile) {
|
||||
_refreshRateModeLock.withWriteLock([&] {
|
||||
_refreshRateProfileSettingLock.withWriteLock([&] {
|
||||
_refreshRateProfile = refreshRateProfile;
|
||||
_refreshRateMode.set((int) refreshRateProfile);
|
||||
_refreshRateProfileSetting.set((int) refreshRateProfile);
|
||||
});
|
||||
updateRefreshRateController();
|
||||
}
|
||||
|
@ -86,9 +111,9 @@ void RefreshRateManager::setRefreshRateProfile(RefreshRateManager::RefreshRatePr
|
|||
RefreshRateManager::RefreshRateProfile RefreshRateManager::getRefreshRateProfile() const {
|
||||
RefreshRateManager::RefreshRateProfile profile = RefreshRateManager::RefreshRateProfile::REALTIME;
|
||||
|
||||
if (getUXMode() != RefreshRateManager::UXMode::HMD) {
|
||||
profile =(RefreshRateManager::RefreshRateProfile) _refreshRateModeLock.resultWithReadLock<int>([&] {
|
||||
return _refreshRateMode.get();
|
||||
if (getUXMode() != RefreshRateManager::UXMode::VR) {
|
||||
profile =(RefreshRateManager::RefreshRateProfile) _refreshRateProfileSettingLock.resultWithReadLock<int>([&] {
|
||||
return _refreshRateProfileSetting.get();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -96,8 +121,11 @@ RefreshRateManager::RefreshRateProfile RefreshRateManager::getRefreshRateProfile
|
|||
}
|
||||
|
||||
RefreshRateManager::RefreshRateRegime RefreshRateManager::getRefreshRateRegime() const {
|
||||
return getUXMode() == RefreshRateManager::UXMode::HMD ? RefreshRateManager::RefreshRateRegime::RUNNING :
|
||||
_refreshRateRegime;
|
||||
if (getUXMode() == RefreshRateManager::UXMode::VR) {
|
||||
return RefreshRateManager::RefreshRateRegime::FOCUS_ACTIVE;
|
||||
} else {
|
||||
return _refreshRateRegime;
|
||||
}
|
||||
}
|
||||
|
||||
void RefreshRateManager::setRefreshRateRegime(RefreshRateManager::RefreshRateRegime refreshRateRegime) {
|
||||
|
@ -119,31 +147,12 @@ void RefreshRateManager::updateRefreshRateController() const {
|
|||
if (_refreshRateOperator) {
|
||||
int targetRefreshRate;
|
||||
if (_uxMode == RefreshRateManager::UXMode::DESKTOP) {
|
||||
if (_refreshRateRegime == RefreshRateManager::RefreshRateRegime::RUNNING &&
|
||||
_refreshRateProfile == RefreshRateManager::RefreshRateProfile::INTERACTIVE) {
|
||||
targetRefreshRate = getInteractiveRefreshRate();
|
||||
} else {
|
||||
targetRefreshRate = REFRESH_RATE_REGIMES[_refreshRateRegime][_refreshRateProfile];
|
||||
}
|
||||
targetRefreshRate = REFRESH_RATE_PROFILES[_refreshRateProfile][_refreshRateRegime];
|
||||
} else {
|
||||
targetRefreshRate = HMD_TARGET_RATE;
|
||||
targetRefreshRate = VR_TARGET_RATE;
|
||||
}
|
||||
|
||||
_refreshRateOperator(targetRefreshRate);
|
||||
_activeRefreshRate = targetRefreshRate;
|
||||
}
|
||||
}
|
||||
|
||||
void RefreshRateManager::setInteractiveRefreshRate(int refreshRate) {
|
||||
_refreshRateLock.withWriteLock([&] {
|
||||
_interactiveRefreshRate.set(refreshRate);
|
||||
});
|
||||
updateRefreshRateController();
|
||||
}
|
||||
|
||||
|
||||
int RefreshRateManager::getInteractiveRefreshRate() const {
|
||||
return _refreshRateLock.resultWithReadLock<int>([&] {
|
||||
return _interactiveRefreshRate.get();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
#include <SettingHandle.h>
|
||||
#include <shared/ReadWriteLockable.h>
|
||||
|
||||
|
@ -28,7 +30,8 @@ public:
|
|||
};
|
||||
|
||||
enum RefreshRateRegime {
|
||||
RUNNING = 0,
|
||||
FOCUS_ACTIVE = 0,
|
||||
FOCUS_INACTIVE,
|
||||
UNFOCUS,
|
||||
MINIMIZED,
|
||||
STARTUP,
|
||||
|
@ -38,7 +41,7 @@ public:
|
|||
|
||||
enum UXMode {
|
||||
DESKTOP = 0,
|
||||
HMD,
|
||||
VR,
|
||||
UX_NUM
|
||||
};
|
||||
|
||||
|
@ -57,8 +60,9 @@ public:
|
|||
void setRefreshRateOperator(std::function<void(int)> refreshRateOperator) { _refreshRateOperator = refreshRateOperator; }
|
||||
int getActiveRefreshRate() const { return _activeRefreshRate; }
|
||||
void updateRefreshRateController() const;
|
||||
void setInteractiveRefreshRate(int refreshRate);
|
||||
int getInteractiveRefreshRate() const;
|
||||
|
||||
void resetInactiveTimer();
|
||||
void toggleInactive();
|
||||
|
||||
static std::string refreshRateProfileToString(RefreshRateProfile refreshRateProfile);
|
||||
static RefreshRateProfile refreshRateProfileFromString(std::string refreshRateProfile);
|
||||
|
@ -66,18 +70,17 @@ public:
|
|||
static std::string refreshRateRegimeToString(RefreshRateRegime refreshRateRegime);
|
||||
|
||||
private:
|
||||
mutable ReadWriteLockable _refreshRateLock;
|
||||
mutable ReadWriteLockable _refreshRateModeLock;
|
||||
|
||||
mutable int _activeRefreshRate { 20 };
|
||||
RefreshRateProfile _refreshRateProfile { RefreshRateProfile::INTERACTIVE};
|
||||
RefreshRateRegime _refreshRateRegime { RefreshRateRegime::STARTUP };
|
||||
UXMode _uxMode;
|
||||
|
||||
Setting::Handle<int> _interactiveRefreshRate { "interactiveRefreshRate", 20};
|
||||
Setting::Handle<int> _refreshRateMode { "refreshRateProfile", INTERACTIVE };
|
||||
mutable ReadWriteLockable _refreshRateProfileSettingLock;
|
||||
Setting::Handle<int> _refreshRateProfileSetting { "refreshRateProfile", RefreshRateProfile::INTERACTIVE };
|
||||
|
||||
std::function<void(int)> _refreshRateOperator { nullptr };
|
||||
|
||||
std::shared_ptr<QTimer> _inactiveTimer { std::make_shared<QTimer>() };
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include "MyAvatar.h"
|
||||
#include "DebugDraw.h"
|
||||
#include "SceneScriptingInterface.h"
|
||||
#include "ui/AvatarCertifyBanner.h"
|
||||
|
||||
// 50 times per second - target is 45hz, but this helps account for any small deviations
|
||||
// in the update loop - this also results in ~30hz when in desktop mode which is essentially
|
||||
|
@ -178,6 +179,13 @@ void AvatarManager::updateMyAvatar(float deltaTime) {
|
|||
_lastSendAvatarDataTime = now;
|
||||
_myAvatarSendRate.increment();
|
||||
}
|
||||
|
||||
static AvatarCertifyBanner theftBanner;
|
||||
if (_myAvatar->isCertifyFailed()) {
|
||||
theftBanner.show(_myAvatar->getSessionUUID());
|
||||
} else {
|
||||
theftBanner.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#include <EntityItemID.h>
|
||||
|
||||
/**jsdoc
|
||||
* The Clipboard API enables you to export and import entities to and from JSON files.
|
||||
* The <code>Clipboard</code> API enables you to export and import entities to and from JSON files.
|
||||
*
|
||||
* @namespace Clipboard
|
||||
*
|
||||
|
@ -33,56 +33,92 @@ public:
|
|||
|
||||
public:
|
||||
/**jsdoc
|
||||
* Compute the extents of the contents held in the clipboard.
|
||||
* Gets the extents of the entities held in the clipboard.
|
||||
* @function Clipboard.getContentsDimensions
|
||||
* @returns {Vec3} The extents of the contents held in the clipboard.
|
||||
* @returns {Vec3} The extents of the content held in the clipboard.
|
||||
* @example <caption>Import entities to the clipboard and report their overall dimensions.</caption>
|
||||
* var filename = Window.browse("Import entities to clipboard", "", "*.json");
|
||||
* if (filename) {
|
||||
* if (Clipboard.importEntities(filename)) {
|
||||
* print("Clipboard dimensions: " + JSON.stringify(Clipboard.getContentsDimensions()));
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
Q_INVOKABLE glm::vec3 getContentsDimensions();
|
||||
|
||||
/**jsdoc
|
||||
* Compute the largest dimension of the extents of the contents held in the clipboard.
|
||||
* Gets the largest dimension of the extents of the entities held in the clipboard.
|
||||
* @function Clipboard.getClipboardContentsLargestDimension
|
||||
* @returns {number} The largest dimension computed.
|
||||
* @returns {number} The largest dimension of the extents of the content held in the clipboard.
|
||||
*/
|
||||
Q_INVOKABLE float getClipboardContentsLargestDimension();
|
||||
|
||||
/**jsdoc
|
||||
* Import entities from a JSON file containing entity data into the clipboard.
|
||||
* You can generate a JSON file using {@link Clipboard.exportEntities}.
|
||||
* Imports entities from a JSON file into the clipboard.
|
||||
* @function Clipboard.importEntities
|
||||
* @param {string} filename Path and name of file to import.
|
||||
* @param {boolean} does the ResourceRequestObserver observe this request?
|
||||
* @param {number} optional internal id of object causing this import.
|
||||
* @param {string} filename - The path and name of the JSON file to import.
|
||||
* @param {boolean} [isObservable=true] - <code>true</code> if the {@link ResourceRequestObserver} can observe this
|
||||
* request, <code>false</code> if it can't.
|
||||
* @param {number} [callerID=-1] - An integer ID that is passed through to the {@link ResourceRequestObserver}.
|
||||
* @returns {boolean} <code>true</code> if the import was successful, otherwise <code>false</code>.
|
||||
* @example <caption>Import entities and paste into the domain.</caption>
|
||||
* var filename = Window.browse("Import entities to clipboard", "", "*.json");
|
||||
* if (filename) {
|
||||
* if (Clipboard.importEntities(filename)) {
|
||||
* pastedEntities = Clipboard.pasteEntities(Vec3.sum(MyAvatar.position,
|
||||
* Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })));
|
||||
* print("Entities pasted: " + JSON.stringify(pastedEntities));
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
Q_INVOKABLE bool importEntities(const QString& filename, const bool isObservable = true, const qint64 callerId = -1);
|
||||
|
||||
/**jsdoc
|
||||
* Export the entities specified to a JSON file.
|
||||
* Exports specified entities to a JSON file.
|
||||
* @function Clipboard.exportEntities
|
||||
* @param {string} filename Path and name of the file to export the entities to. Should have the extension ".json".
|
||||
* @param {Uuid[]} entityIDs Array of IDs of the entities to export.
|
||||
* @returns {boolean} <code>true</code> if the export was successful, otherwise <code>false</code>.
|
||||
* @param {string} filename - Path and name of the file to export the entities to. Should have the extension ".json".
|
||||
* @param {Uuid[]} entityIDs - The IDs of the entities to export.
|
||||
* @returns {boolean} <code>true</code> if entities were found and the file was written, otherwise <code>false</code>.
|
||||
* @example <caption>Create and export a cube and a sphere.</caption>
|
||||
* // Create entities.
|
||||
* var box = Entities.addEntity({
|
||||
* type: "Box",
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: -0.2, y: 0, z: -3 })),
|
||||
* lifetime: 300 // Delete after 5 minutes.
|
||||
* });
|
||||
* var sphere = Entities.addEntity({
|
||||
* type: "Sphere",
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0.2, y: 0, z: -3 })),
|
||||
* lifetime: 300 // Delete after 5 minutes.
|
||||
* });
|
||||
*
|
||||
* // Export entities.
|
||||
* var filename = Window.save("Export entities to JSON file", Paths.resources, "*.json");
|
||||
* if (filename) {
|
||||
* Clipboard.exportEntities(filename, [box, sphere]);
|
||||
* }
|
||||
*/
|
||||
Q_INVOKABLE bool exportEntities(const QString& filename, const QVector<QUuid>& entityIDs);
|
||||
|
||||
/**jsdoc
|
||||
* Export the entities with centers within a cube to a JSON file.
|
||||
* Exports all entities that have centers within a cube to a JSON file.
|
||||
* @function Clipboard.exportEntities
|
||||
* @param {string} filename Path and name of the file to export the entities to. Should have the extension ".json".
|
||||
* @param {number} x X-coordinate of the cube center.
|
||||
* @param {number} y Y-coordinate of the cube center.
|
||||
* @param {number} z Z-coordinate of the cube center.
|
||||
* @param {number} scale Half dimension of the cube.
|
||||
* @returns {boolean} <code>true</code> if the export was successful, otherwise <code>false</code>.
|
||||
* @variation 0
|
||||
* @param {string} filename - Path and name of the file to export the entities to. Should have the extension ".json".
|
||||
* @param {number} x - X-coordinate of the cube center.
|
||||
* @param {number} y - Y-coordinate of the cube center.
|
||||
* @param {number} z - Z-coordinate of the cube center.
|
||||
* @param {number} scale - Half dimension of the cube.
|
||||
* @returns {boolean} <code>true</code> if entities were found and the file was written, otherwise <code>false</code>.
|
||||
*/
|
||||
Q_INVOKABLE bool exportEntities(const QString& filename, float x, float y, float z, float scale);
|
||||
|
||||
/**jsdoc
|
||||
* Paste the contents of the clipboard into the world.
|
||||
* Pastes the contents of the clipboard into the domain.
|
||||
* @function Clipboard.pasteEntities
|
||||
* @param {Vec3} position Position to paste the clipboard contents at.
|
||||
* @returns {Uuid[]} Array of entity IDs for the new entities that were created as a result of the paste operation.
|
||||
* @param {Vec3} position - The position to paste the clipboard contents at.
|
||||
* @returns {Uuid[]} The IDs of the new entities that were created as a result of the paste operation. If entities couldn't
|
||||
* be created then an empty array is returned.
|
||||
*/
|
||||
Q_INVOKABLE QVector<EntityItemID> pasteEntities(glm::vec3 position);
|
||||
};
|
||||
|
|
|
@ -26,18 +26,20 @@ class ScriptEngine;
|
|||
|
||||
|
||||
/**jsdoc
|
||||
* The Controller API provides facilities to interact with computer and controller hardware.
|
||||
* The <code>Controller</code> API provides facilities to interact with computer and controller hardware.
|
||||
*
|
||||
* <h5>Functions</h5>
|
||||
* <h3>Facilities</h3>
|
||||
*
|
||||
* <p>Properties</p>
|
||||
* <h4>Properties</h4>
|
||||
* <p>Get <code>Controller</code> property trees.</p>
|
||||
* <ul>
|
||||
* <li>{@link Controller.getActions|getActions}</li>
|
||||
* <li>{@link Controller.getHardware|getHardware}</li>
|
||||
* <li>{@link Controller.getStandard|getStandard}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Mappings</p>
|
||||
* <h4>Mappings</h4>
|
||||
* <p>Create and enable or disable <code>Controller</code> mappings.</p>
|
||||
* <ul>
|
||||
* <li>{@link Controller.disableMapping|disableMapping}</li>
|
||||
* <li>{@link Controller.enableMapping|enableMapping}</li>
|
||||
|
@ -46,7 +48,8 @@ class ScriptEngine;
|
|||
* <li>{@link Controller.parseMapping|parseMapping}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Input, Hardware, and Action Reflection</p>
|
||||
* <h4>Input, Hardware, and Action Reflection</h4>
|
||||
* <p>Information on the devices and actions available.</p>
|
||||
* <ul>
|
||||
* <li>{@link Controller.findAction|findAction}</li>
|
||||
* <li>{@link Controller.findDevice|findDevice}</li>
|
||||
|
@ -55,16 +58,20 @@ class ScriptEngine;
|
|||
* <li>{@link Controller.getAvailableInputs|getAvailableInputs}</li>
|
||||
* <li>{@link Controller.getDeviceName|getDeviceName}</li>
|
||||
* <li>{@link Controller.getDeviceNames|getDeviceNames}</li>
|
||||
* <li>{@link Controller.getRunningInputDevices|getRunningInputDevices}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Input, Hardware, and Action Events</p>
|
||||
* <h4>Input, Hardware, and Action Signals</h4>
|
||||
* <p>Notifications of device and action events.</p>
|
||||
* <ul>
|
||||
* <li>{@link Controller.actionEvent|actionEvent}</li>
|
||||
* <li>{@link Controller.hardwareChanged|hardwareChanged}</li>
|
||||
* <li>{@link Controller.inputDeviceRunningChanged|inputDeviceRunningChanged}</li>
|
||||
* <li>{@link Controller.inputEvent|inputEvent}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Mouse, Keyboard, and Touch Events</p>
|
||||
* <h4>Mouse, Keyboard, and Touch Signals</h4>
|
||||
* <p>Notifications of mouse, keyboard, and touch events.</p>
|
||||
* <ul>
|
||||
* <li>{@link Controller.keyPressEvent|keyPressEvent}</li>
|
||||
* <li>{@link Controller.keyReleaseEvent|keyReleaseEvent}</li>
|
||||
|
@ -78,29 +85,32 @@ class ScriptEngine;
|
|||
* <li>{@link Controller.wheelEvent|wheelEvent}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Control Capturing</p>
|
||||
* <h4>Control Capturing</h4>
|
||||
* <p>Disable and enable the processing of mouse and touch events.</p>
|
||||
* <ul>
|
||||
* <li>{@link Controller.captureMouseEvents|captureMouseEvents}</li>
|
||||
* <li>{@link Controller.captureTouchEvents|captureTouchEvents}</li>
|
||||
* <li>{@link Controller.captureWheelEvents|captureWheelEvents}</li>
|
||||
* <li>{@link Controller.captureTouchEvents|captureTouchEvents}</li>
|
||||
* <li>{@link Controller.releaseMouseEvents|releaseMouseEvents}</li>
|
||||
* <li>{@link Controller.releaseTouchEvents|releaseTouchEvents}</li>
|
||||
* <li>{@link Controller.releaseWheelEvents|releaseWheelEvents}</li>
|
||||
* <li>{@link Controller.releaseTouchEvents|releaseTouchEvents}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Action Capturing</p>
|
||||
* <h4>Action Capturing</h4>
|
||||
* <p>Disable and enable controller actions.</p>
|
||||
* <ul>
|
||||
* <li>{@link Controller.captureActionEvents|captureActionEvents}</li>
|
||||
* <li>{@link Controller.captureEntityClickEvents|captureEntityClickEvents}</li>
|
||||
* <li>{@link Controller.captureJoystick|captureJoystick}</li>
|
||||
* <li>{@link Controller.captureKeyEvents|captureKeyEvents}</li>
|
||||
* <li>{@link Controller.captureJoystick|captureJoystick}</li>
|
||||
* <li>{@link Controller.captureEntityClickEvents|captureEntityClickEvents}</li>
|
||||
* <li>{@link Controller.releaseActionEvents|releaseActionEvents}</li>
|
||||
* <li>{@link Controller.releaseEntityClickEvents|releaseEntityClickEvents}</li>
|
||||
* <li>{@link Controller.releaseJoystick|releaseJoystick}</li>
|
||||
* <li>{@link Controller.releaseKeyEvents|releaseKeyEvents}</li>
|
||||
* <li>{@link Controller.releaseJoystick|releaseJoystick}</li>
|
||||
* <li>{@link Controller.releaseEntityClickEvents|releaseEntityClickEvents}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Controller and Action Values</p>
|
||||
* <h4>Controller and Action Values</h4>
|
||||
* <p>Get the current value of controller outputs and actions.</p>
|
||||
* <ul>
|
||||
* <li>{@link Controller.getValue|getValue}</li>
|
||||
* <li>{@link Controller.getAxisValue|getAxisValue}</li>
|
||||
|
@ -108,7 +118,8 @@ class ScriptEngine;
|
|||
* <li>{@link Controller.getActionValue|getActionValue}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Haptics</p>
|
||||
* <h4>Haptics</h4>
|
||||
* <p>Trigger haptic pulses.</p>
|
||||
* <ul>
|
||||
* <li>{@link Controller.triggerHapticPulse|triggerHapticPulse}</li>
|
||||
* <li>{@link Controller.triggerHapticPulseOnDevice|triggerHapticPulseOnDevice}</li>
|
||||
|
@ -116,20 +127,23 @@ class ScriptEngine;
|
|||
* <li>{@link Controller.triggerShortHapticPulseOnDevice|triggerShortHapticPulseOnDevice}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Display Information</p>
|
||||
* <h4>Display Information</h4>
|
||||
* <p>Get information on the display.</p>
|
||||
* <ul>
|
||||
* <li>{@link Controller.getViewportDimensions|getViewportDimensions}</li>
|
||||
* <li>{@link Controller.getRecommendedHUDRect|getRecommendedHUDRect}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Virtual Game Pad</p>
|
||||
* <h4>Virtual Game Pad</h4>
|
||||
* <p>Use the virtual game pad which is available on some devices.</p>
|
||||
* <ul>
|
||||
* <li>{@link Controller.setVPadEnabled|setVPadEnabled}</li>
|
||||
* <li>{@link Controller.setVPadHidden|setVPadHidden}</li>
|
||||
* <li>{@link Controller.setVPadExtraBottomMargin|setVPadExtraBottomMargin}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Input Recordings</p>
|
||||
* <h4>Input Recordings</h4>
|
||||
* <p>Create and play input recordings.</p>
|
||||
* <ul>
|
||||
* <li>{@link Controller.startInputRecording|startInputRecording}</li>
|
||||
* <li>{@link Controller.stopInputRecording|stopInputRecording}</li>
|
||||
|
@ -140,10 +154,10 @@ class ScriptEngine;
|
|||
* <li>{@link Controller.stopInputPlayback|stopInputPlayback}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h5>Entity Methods:</h5>
|
||||
* <h3>Entity Methods</h3>
|
||||
*
|
||||
* <p>The default scripts implement hand controller actions that use {@link Entities.callEntityMethod} to call entity script
|
||||
* methods, if present in the entity being interacted with.</p>
|
||||
* methods, if present, in the entity being interacted with.</p>
|
||||
*
|
||||
* <table>
|
||||
* <thead>
|
||||
|
@ -203,7 +217,7 @@ class ScriptEngine;
|
|||
*
|
||||
* @property {Controller.Actions} Actions - Predefined actions on Interface and the user's avatar. These can be used as end
|
||||
* points in a {@link RouteObject} mapping. A synonym for <code>Controller.Hardware.Actions</code>.
|
||||
* <em>Read-only.</em><br />
|
||||
* <em>Read-only.</em><br /><br />
|
||||
* Default mappings are provided from the <code>Controller.Hardware.Keyboard</code> and <code>Controller.Standard</code> to
|
||||
* actions in
|
||||
* <a href="https://github.com/highfidelity/hifi/blob/master/interface/resources/controllers/keyboardMouse.json">
|
||||
|
@ -217,7 +231,7 @@ class ScriptEngine;
|
|||
* controller outputs. <em>Read-only.</em>
|
||||
*
|
||||
* @property {Controller.Standard} Standard - Standard controller outputs that can be mapped to <code>Actions</code> or
|
||||
* functions in a {@link RouteObject} mapping. <em>Read-only.</em><br />
|
||||
* functions in a {@link RouteObject} mapping. <em>Read-only.</em><br /><br />
|
||||
* Each hardware device has a mapping from its outputs to <code>Controller.Standard</code> items, specified in a JSON file.
|
||||
* For example, <a href="https://github.com/highfidelity/hifi/blob/master/interface/resources/controllers/leapmotion.json">
|
||||
* leapmotion.json</a> and
|
||||
|
@ -253,7 +267,7 @@ public:
|
|||
public slots:
|
||||
|
||||
/**jsdoc
|
||||
* Disable default Interface actions for a particular key event.
|
||||
* Disables default Interface actions for a particular key event.
|
||||
* @function Controller.captureKeyEvents
|
||||
* @param {KeyEvent} event - Details of the key event to be captured. The <code>key</code> property must be specified. The
|
||||
* <code>text</code> property is ignored. The other properties default to <code>false</code>.
|
||||
|
@ -272,7 +286,7 @@ public slots:
|
|||
virtual void captureKeyEvents(const KeyEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Re-enable default Interface actions for a particular key event that has been disabled using
|
||||
* Re-enables default Interface actions for a particular key event that has been disabled using
|
||||
* {@link Controller.captureKeyEvents|captureKeyEvents}.
|
||||
* @function Controller.releaseKeyEvents
|
||||
* @param {KeyEvent} event - Details of the key event to release from capture. The <code>key</code> property must be
|
||||
|
@ -281,7 +295,7 @@ public slots:
|
|||
virtual void releaseKeyEvents(const KeyEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Disable default Interface actions for a joystick.
|
||||
* Disables default Interface actions for a joystick.
|
||||
* @function Controller.captureJoystick
|
||||
* @param {number} joystickID - The integer ID of the joystick.
|
||||
* @deprecated This function is deprecated and will be removed. It no longer has any effect.
|
||||
|
@ -289,7 +303,7 @@ public slots:
|
|||
virtual void captureJoystick(int joystickIndex);
|
||||
|
||||
/**jsdoc
|
||||
* Re-enable default Interface actions for a joystick that has been disabled using
|
||||
* Re-enables default Interface actions for a joystick that has been disabled using
|
||||
* {@link Controller.captureJoystick|captureJoystick}.
|
||||
* @function Controller.releaseJoystick
|
||||
* @param {number} joystickID - The integer ID of the joystick.
|
||||
|
@ -298,7 +312,7 @@ public slots:
|
|||
virtual void releaseJoystick(int joystickIndex);
|
||||
|
||||
/**jsdoc
|
||||
* Disable {@link Entities.mousePressOnEntity} and {@link Entities.mouseDoublePressOnEntity} events on entities.
|
||||
* Disables {@link Entities.mousePressOnEntity} and {@link Entities.mouseDoublePressOnEntity} events on entities.
|
||||
* @function Controller.captureEntityClickEvents
|
||||
* @example <caption>Disable entity click events for a short period.</caption>
|
||||
* Entities.mousePressOnEntity.connect(function (entityID, event) {
|
||||
|
@ -316,7 +330,7 @@ public slots:
|
|||
virtual void captureEntityClickEvents();
|
||||
|
||||
/**jsdoc
|
||||
* Re-enable {@link Entities.mousePressOnEntity} and {@link Entities.mouseDoublePressOnEntity} events on entities that were
|
||||
* Re-enables {@link Entities.mousePressOnEntity} and {@link Entities.mouseDoublePressOnEntity} events on entities that were
|
||||
* disabled using {@link Controller.captureEntityClickEvents|captureEntityClickEvents}.
|
||||
* @function Controller.releaseEntityClickEvents
|
||||
*/
|
||||
|
@ -324,14 +338,14 @@ public slots:
|
|||
|
||||
|
||||
/**jsdoc
|
||||
* Get the dimensions of the Interface window's interior if in desktop mode or the HUD surface if in HMD mode.
|
||||
* Gets the dimensions of the Interface window's interior if in desktop mode or the HUD surface if in HMD mode.
|
||||
* @function Controller.getViewportDimensions
|
||||
* @returns {Vec2} The dimensions of the Interface window interior if in desktop mode or HUD surface if in HMD mode.
|
||||
*/
|
||||
virtual glm::vec2 getViewportDimensions() const;
|
||||
|
||||
/**jsdoc
|
||||
* Get the recommended area to position UI on the HUD surface if in HMD mode or Interface's window interior if in desktop
|
||||
* Gets the recommended area to position UI on the HUD surface if in HMD mode or Interface's window interior if in desktop
|
||||
* mode.
|
||||
* @function Controller.getRecommendedHUDRect
|
||||
* @returns {Rect} The recommended area in which to position UI.
|
||||
|
|
76
interface/src/ui/AvatarCertifyBanner.cpp
Normal file
76
interface/src/ui/AvatarCertifyBanner.cpp
Normal file
|
@ -0,0 +1,76 @@
|
|||
//
|
||||
// AvatarCertifyBanner.h
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Simon Walton, April 2019
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "AvatarCertifyBanner.h"
|
||||
|
||||
#include <QtGui/QDesktopServices>
|
||||
|
||||
#include "ui/TabletScriptingInterface.h"
|
||||
#include "EntityTreeRenderer.h"
|
||||
|
||||
namespace {
|
||||
const QUrl AVATAR_THEFT_BANNER_IMAGE = PathUtils::resourcesUrl("images/AvatarTheftBanner.png");
|
||||
const QString AVATAR_THEFT_BANNER_SCRIPT { "/system/clickToAvatarApp.js" };
|
||||
}
|
||||
|
||||
AvatarCertifyBanner::AvatarCertifyBanner(QQuickItem* parent) {
|
||||
}
|
||||
|
||||
void AvatarCertifyBanner::show(const QUuid& avatarID) {
|
||||
if (!_active) {
|
||||
auto entityTreeRenderer = DependencyManager::get<EntityTreeRenderer>();
|
||||
EntityTreePointer entityTree = entityTreeRenderer->getTree();
|
||||
if (!entityTree) {
|
||||
return;
|
||||
}
|
||||
const bool tabletShown = DependencyManager::get<TabletScriptingInterface>()->property("tabletShown").toBool();
|
||||
const auto& position = tabletShown ? glm::vec3(0.0f, 0.0f, -1.8f) : glm::vec3(0.0f, 0.0f, -0.7f);
|
||||
const float scaleFactor = tabletShown ? 2.6f : 1.0f;
|
||||
|
||||
EntityItemProperties entityProperties;
|
||||
entityProperties.setType(EntityTypes::Image);
|
||||
entityProperties.setEntityHostType(entity::HostType::LOCAL);
|
||||
entityProperties.setImageURL(AVATAR_THEFT_BANNER_IMAGE.toString());
|
||||
entityProperties.setName("hifi-avatar-notification-banner");
|
||||
entityProperties.setParentID(avatarID);
|
||||
entityProperties.setParentJointIndex(CAMERA_MATRIX_INDEX);
|
||||
entityProperties.setLocalPosition(position);
|
||||
entityProperties.setDimensions(glm::vec3(1.0f, 1.0f, 0.3f) * scaleFactor);
|
||||
entityProperties.setRenderLayer(tabletShown ? RenderLayer::WORLD : RenderLayer::FRONT);
|
||||
entityProperties.getGrab().setGrabbable(false);
|
||||
QString scriptPath = QUrl(PathUtils::defaultScriptsLocation("")).toString() + AVATAR_THEFT_BANNER_SCRIPT;
|
||||
entityProperties.setScript(scriptPath);
|
||||
entityProperties.setVisible(true);
|
||||
|
||||
entityTree->withWriteLock([&] {
|
||||
auto entityTreeItem = entityTree->addEntity(_bannerID, entityProperties);
|
||||
entityTreeItem->setLocalPosition(position);
|
||||
});
|
||||
|
||||
_active = true;
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarCertifyBanner::clear() {
|
||||
if (_active) {
|
||||
auto entityTreeRenderer = DependencyManager::get<EntityTreeRenderer>();
|
||||
EntityTreePointer entityTree = entityTreeRenderer->getTree();
|
||||
if (!entityTree) {
|
||||
return;
|
||||
}
|
||||
|
||||
entityTree->withWriteLock([&] {
|
||||
entityTree->deleteEntity(_bannerID);
|
||||
});
|
||||
|
||||
_active = false;
|
||||
}
|
||||
}
|
34
interface/src/ui/AvatarCertifyBanner.h
Normal file
34
interface/src/ui/AvatarCertifyBanner.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// AvatarCertifyBanner.h
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Simon Walton, April 2019
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_AvatarCertifyBanner_h
|
||||
#define hifi_AvatarCertifyBanner_h
|
||||
|
||||
#include <QUuid>
|
||||
#include "OffscreenQmlElement.h"
|
||||
#include "EntityItemID.h"
|
||||
|
||||
class EntityItemID;
|
||||
|
||||
class AvatarCertifyBanner : QObject {
|
||||
Q_OBJECT
|
||||
HIFI_QML_DECL
|
||||
public:
|
||||
AvatarCertifyBanner(QQuickItem* parent = nullptr);
|
||||
void show(const QUuid& avatarID);
|
||||
void clear();
|
||||
|
||||
private:
|
||||
const EntityItemID _bannerID { QUuid::createUuid() };
|
||||
bool _active { false };
|
||||
};
|
||||
|
||||
#endif // hifi_AvatarCertifyBanner_h
|
|
@ -1428,7 +1428,7 @@ int Avatar::getJointIndex(const QString& name) const {
|
|||
|
||||
withValidJointIndicesCache([&]() {
|
||||
if (_modelJointIndicesCache.contains(name)) {
|
||||
result = _modelJointIndicesCache[name] - 1;
|
||||
result = _modelJointIndicesCache.value(name) - 1;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
|
@ -1439,9 +1439,7 @@ QStringList Avatar::getJointNames() const {
|
|||
withValidJointIndicesCache([&]() {
|
||||
// find out how large the vector needs to be
|
||||
int maxJointIndex = -1;
|
||||
QHashIterator<QString, int> k(_modelJointIndicesCache);
|
||||
while (k.hasNext()) {
|
||||
k.next();
|
||||
for (auto k = _modelJointIndicesCache.constBegin(); k != _modelJointIndicesCache.constEnd(); k++) {
|
||||
int index = k.value();
|
||||
if (index > maxJointIndex) {
|
||||
maxJointIndex = index;
|
||||
|
@ -1450,9 +1448,7 @@ QStringList Avatar::getJointNames() const {
|
|||
// iterate through the hash and put joint names
|
||||
// into the vector at their indices
|
||||
QVector<QString> resultVector(maxJointIndex+1);
|
||||
QHashIterator<QString, int> i(_modelJointIndicesCache);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
for (auto i = _modelJointIndicesCache.constBegin(); i != _modelJointIndicesCache.constEnd(); i++) {
|
||||
int index = i.value();
|
||||
resultVector[index] = i.key();
|
||||
}
|
||||
|
|
|
@ -164,7 +164,7 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
|||
Parent::simulate(deltaTime, fullUpdate);
|
||||
}
|
||||
|
||||
// FIXME: This texture loading logic should probably live in Avatar, to mirror RenderableModelEntityItem and ModelOverlay,
|
||||
// FIXME: This texture loading logic should probably live in Avatar, to mirror RenderableModelEntityItem,
|
||||
// but Avatars don't get updates in the same way
|
||||
if (!_texturesLoaded && getGeometry() && getGeometry()->areTexturesLoaded()) {
|
||||
_texturesLoaded = true;
|
||||
|
|
|
@ -1955,8 +1955,7 @@ void AvatarData::processAvatarIdentity(QDataStream& packetStream, bool& identity
|
|||
>> identity.attachmentData
|
||||
>> identity.displayName
|
||||
>> identity.sessionDisplayName
|
||||
>> identity.isReplicated
|
||||
>> identity.lookAtSnappingEnabled
|
||||
>> identity.identityFlags
|
||||
;
|
||||
|
||||
if (incomingSequenceNumber > _identitySequenceNumber) {
|
||||
|
@ -1971,8 +1970,22 @@ void AvatarData::processAvatarIdentity(QDataStream& packetStream, bool& identity
|
|||
}
|
||||
maybeUpdateSessionDisplayNameFromTransport(identity.sessionDisplayName);
|
||||
|
||||
if (identity.isReplicated != _isReplicated) {
|
||||
_isReplicated = identity.isReplicated;
|
||||
bool flagValue;
|
||||
flagValue = identity.identityFlags.testFlag(AvatarDataPacket::IdentityFlag::isReplicated);
|
||||
if ( flagValue != _isReplicated) {
|
||||
_isReplicated = flagValue;
|
||||
identityChanged = true;
|
||||
}
|
||||
|
||||
flagValue = identity.identityFlags.testFlag(AvatarDataPacket::IdentityFlag::lookAtSnapping);
|
||||
if ( flagValue != _lookAtSnappingEnabled) {
|
||||
setProperty("lookAtSnappingEnabled", flagValue);
|
||||
identityChanged = true;
|
||||
}
|
||||
|
||||
flagValue = identity.identityFlags.testFlag(AvatarDataPacket::IdentityFlag::verificationFailed);
|
||||
if (flagValue != _verificationFailed) {
|
||||
_verificationFailed = flagValue;
|
||||
identityChanged = true;
|
||||
}
|
||||
|
||||
|
@ -1981,11 +1994,6 @@ void AvatarData::processAvatarIdentity(QDataStream& packetStream, bool& identity
|
|||
identityChanged = true;
|
||||
}
|
||||
|
||||
if (identity.lookAtSnappingEnabled != _lookAtSnappingEnabled) {
|
||||
setProperty("lookAtSnappingEnabled", identity.lookAtSnappingEnabled);
|
||||
identityChanged = true;
|
||||
}
|
||||
|
||||
#ifdef WANT_DEBUG
|
||||
qCDebug(avatars) << __FUNCTION__
|
||||
<< "identity.uuid:" << identity.uuid
|
||||
|
@ -2195,17 +2203,27 @@ void AvatarData::prepareResetTraitInstances() {
|
|||
QByteArray AvatarData::identityByteArray(bool setIsReplicated) const {
|
||||
QByteArray identityData;
|
||||
QDataStream identityStream(&identityData, QIODevice::Append);
|
||||
using namespace AvatarDataPacket;
|
||||
|
||||
// when mixers send identity packets to agents, they simply forward along the last incoming sequence number they received
|
||||
// whereas agents send a fresh outgoing sequence number when identity data has changed
|
||||
IdentityFlags identityFlags = IdentityFlag::none;
|
||||
if (_isReplicated || setIsReplicated) {
|
||||
identityFlags.setFlag(IdentityFlag::isReplicated);
|
||||
}
|
||||
if (_lookAtSnappingEnabled) {
|
||||
identityFlags.setFlag(IdentityFlag::lookAtSnapping);
|
||||
}
|
||||
if (isCertifyFailed()) {
|
||||
identityFlags.setFlag(IdentityFlag::verificationFailed);
|
||||
}
|
||||
|
||||
identityStream << getSessionUUID()
|
||||
<< (udt::SequenceNumber::Type) _identitySequenceNumber
|
||||
<< _attachmentData
|
||||
<< _displayName
|
||||
<< getSessionDisplayNameForTransport() // depends on _sessionDisplayName
|
||||
<< (_isReplicated || setIsReplicated)
|
||||
<< _lookAtSnappingEnabled;
|
||||
<< identityFlags;
|
||||
|
||||
return identityData;
|
||||
}
|
||||
|
|
|
@ -378,6 +378,10 @@ namespace AvatarDataPacket {
|
|||
|
||||
static const size_t MIN_BULK_PACKET_SIZE = NUM_BYTES_RFC4122_UUID + HEADER_SIZE;
|
||||
|
||||
// AvatarIdentity packet:
|
||||
enum class IdentityFlag: quint32 {none, isReplicated = 0x1, lookAtSnapping = 0x2, verificationFailed = 0x4};
|
||||
Q_DECLARE_FLAGS(IdentityFlags, IdentityFlag)
|
||||
|
||||
struct SendStatus {
|
||||
HasFlags itemFlags { 0 };
|
||||
bool sendUUID { false };
|
||||
|
@ -1182,6 +1186,7 @@ public:
|
|||
QString sessionDisplayName;
|
||||
bool isReplicated;
|
||||
bool lookAtSnappingEnabled;
|
||||
AvatarDataPacket::IdentityFlags identityFlags;
|
||||
};
|
||||
|
||||
// identityChanged returns true if identity has changed, false otherwise.
|
||||
|
@ -1213,6 +1218,7 @@ public:
|
|||
_sessionDisplayName = sessionDisplayName;
|
||||
markIdentityDataChanged();
|
||||
}
|
||||
virtual bool isCertifyFailed() const { return _verificationFailed; }
|
||||
|
||||
/**jsdoc
|
||||
* Gets information about the models currently attached to your avatar.
|
||||
|
@ -1694,6 +1700,7 @@ protected:
|
|||
QString _displayName;
|
||||
QString _sessionDisplayName { };
|
||||
bool _lookAtSnappingEnabled { true };
|
||||
bool _verificationFailed { false };
|
||||
|
||||
quint64 _errorLogExpiry; ///< time in future when to log an error
|
||||
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
|
||||
#include "Profile.h"
|
||||
|
||||
static const QString VERIFY_FAIL_MODEL { "/meshes/verifyFailed.fst" };
|
||||
|
||||
void AvatarReplicas::addReplica(const QUuid& parentID, AvatarSharedPointer replica) {
|
||||
if (parentID == QUuid()) {
|
||||
return;
|
||||
|
@ -324,6 +326,10 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer<ReceivedMessage>
|
|||
bool displayNameChanged = false;
|
||||
// In this case, the "sendingNode" is the Avatar Mixer.
|
||||
avatar->processAvatarIdentity(avatarIdentityStream, identityChanged, displayNameChanged);
|
||||
if (avatar->isCertifyFailed() && identityUUID != EMPTY) {
|
||||
qCDebug(avatars) << "Avatar" << avatar->getSessionDisplayName() << "marked as VERIFY-FAILED";
|
||||
avatar->setSkeletonModelURL(PathUtils::resourcesUrl(VERIFY_FAIL_MODEL));
|
||||
}
|
||||
_replicas.processAvatarIdentity(identityUUID, message->getMessage(), identityChanged, displayNameChanged);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,9 +35,9 @@ namespace controller {
|
|||
|
||||
/**jsdoc
|
||||
* <p>The <code>Controller.Actions</code> object has properties representing predefined actions on the user's avatar and
|
||||
* Interface. The property values are integer IDs, uniquely identifying each action. <em>Read-only.</em> These can be used
|
||||
* as end points in the routes of a {@link MappingObject}. The data routed to each action is either a number or a
|
||||
* {@link Pose}.</p>
|
||||
* Interface. The property values are integer IDs, uniquely identifying each action. <em>Read-only.</em></p>
|
||||
* <p>These actions can be used as end points in the routes of a {@link MappingObject}. The data item routed to each action
|
||||
* is either a number or a {@link Pose}.</p>
|
||||
*
|
||||
* <table>
|
||||
* <thead>
|
||||
|
@ -178,7 +178,7 @@ namespace controller {
|
|||
* person view.</td></tr>
|
||||
* <tr><td><code>CycleCamera</code></td><td>number</td><td>number</td><td>Cycle the camera view from first person, to
|
||||
* third person, to full screen mirror, then back to first person and repeat.</td></tr>
|
||||
* <tr><td><code>ContextMenu</code></td><td>number</td><td>number</td><td>Show / hide the tablet.</td></tr>
|
||||
* <tr><td><code>ContextMenu</code></td><td>number</td><td>number</td><td>Show/hide the tablet.</td></tr>
|
||||
* <tr><td><code>ToggleMute</code></td><td>number</td><td>number</td><td>Toggle the microphone mute.</td></tr>
|
||||
* <tr><td><code>TogglePushToTalk</code></td><td>number</td><td>number</td><td>Toggle push to talk.</td></tr>
|
||||
* <tr><td><code>ToggleOverlay</code></td><td>number</td><td>number</td><td>Toggle the display of overlays.</td></tr>
|
||||
|
@ -238,71 +238,49 @@ namespace controller {
|
|||
* <tr><td><code>LEFT_HAND</code></td><td>number</td><td>{@link Pose}</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use <code>LeftHand</code> instead.</span></td></tr>
|
||||
* <tr><td><code>RIGHT_HAND</code></td><td>number</td><td>{@link Pose}</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>RightHand</code> instead.</span></td></tr>
|
||||
* action is deprecated and will be removed. Use <code>RightHand</code> instead.</span></td></tr>
|
||||
* <tr><td><code>BOOM_IN</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>BoomIn</code> instead.</span></td></tr>
|
||||
* action is deprecated and will be removed. Use <code>BoomIn</code> instead.</span></td></tr>
|
||||
* <tr><td><code>BOOM_OUT</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>BoomOut</code> instead.</span></td></tr>
|
||||
* action is deprecated and will be removed. Use <code>BoomOut</code> instead.</span></td></tr>
|
||||
* <tr><td><code>CONTEXT_MENU</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>ContextMenu</code> instead.</span></td></tr>
|
||||
* action is deprecated and will be removed. Use <code>ContextMenu</code> instead.</span></td></tr>
|
||||
* <tr><td><code>TOGGLE_MUTE</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>ToggleMute</code> instead.</span></td></tr>
|
||||
* action is deprecated and will be removed. Use <code>ToggleMute</code> instead.</span></td></tr>
|
||||
* <tr><td><code>TOGGLE_PUSHTOTALK</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>TogglePushToTalk</code> instead.</span></td></tr>
|
||||
* action is deprecated and will be removed. Use <code>TogglePushToTalk</code> instead.</span></td></tr>
|
||||
* <tr><td><code>SPRINT</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>Sprint</code> instead.</span></td></tr>
|
||||
* action is deprecated and will be removed. Use <code>Sprint</code> instead.</span></td></tr>
|
||||
* <tr><td><code>LONGITUDINAL_BACKWARD</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>Backward</code> instead.</span></td></tr>
|
||||
* action is deprecated and will be removed. Use <code>Backward</code> instead.</span></td></tr>
|
||||
* <tr><td><code>LONGITUDINAL_FORWARD</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>Forward</code> instead.</span></td></tr>
|
||||
* action is deprecated and will be removed. Use <code>Forward</code> instead.</span></td></tr>
|
||||
* <tr><td><code>LATERAL_LEFT</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>StrafeLeft</code> instead.</span></td></tr>
|
||||
* action is deprecated and will be removed. Use <code>StrafeLeft</code> instead.</span></td></tr>
|
||||
* <tr><td><code>LATERAL_RIGHT</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>StrafeRight</code> instead.</span></td></tr>
|
||||
* action is deprecated and will be removed. Use <code>StrafeRight</code> instead.</span></td></tr>
|
||||
* <tr><td><code>VERTICAL_UP</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>Up</code> instead.</span></td></tr>
|
||||
* action is deprecated and will be removed. Use <code>Up</code> instead.</span></td></tr>
|
||||
* <tr><td><code>VERTICAL_DOWN</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>Down</code> instead.</span></td></tr>
|
||||
* action is deprecated and will be removed. Use <code>Down</code> instead.</span></td></tr>
|
||||
* <tr><td><code>PITCH_DOWN</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>PitchDown</code> instead.</span></td></tr>
|
||||
* action is deprecated and will be removed. Use <code>PitchDown</code> instead.</span></td></tr>
|
||||
* <tr><td><code>PITCH_UP</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>PitchUp</code> instead.</span></td></tr>
|
||||
* action is deprecated and will be removed. Use <code>PitchUp</code> instead.</span></td></tr>
|
||||
* <tr><td><code>YAW_LEFT</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>YawLeft</code> instead.</span></td></tr>
|
||||
* action is deprecated and will be removed. Use <code>YawLeft</code> instead.</span></td></tr>
|
||||
* <tr><td><code>YAW_RIGHT</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>YawRight</code> instead.</span></td></tr>
|
||||
* action is deprecated and will be removed. Use <code>YawRight</code> instead.</span></td></tr>
|
||||
* <tr><td><code>LEFT_HAND_CLICK</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>LeftHandClick</code> instead.</span></td></tr>
|
||||
* action is deprecated and will be removed. Use <code>LeftHandClick</code> instead.</span></td></tr>
|
||||
* <tr><td><code>RIGHT_HAND_CLICK</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>RightHandClick</code> instead.</span></td></tr>
|
||||
* action is deprecated and will be removed. Use <code>RightHandClick</code> instead.</span></td></tr>
|
||||
* <tr><td><code>SHIFT</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>Shift</code> instead.</span></td></tr>
|
||||
* action is deprecated and will be removed. Use <code>Shift</code> instead.</span></td></tr>
|
||||
* <tr><td><code>ACTION1</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>PrimaryAction</code> instead.</span></td></tr>
|
||||
* action is deprecated and will be removed. Use <code>PrimaryAction</code> instead.</span></td></tr>
|
||||
* <tr><td><code>ACTION2</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>SecondaryAction</code> instead.</span></td></tr>
|
||||
* action is deprecated and will be removed. Use <code>SecondaryAction</code> instead.</span></td></tr>
|
||||
*
|
||||
* <tr><td colSpan=4><strong>Deprecated Trackers</strong></td>
|
||||
* <tr><td><code>TrackedObject00</code></td><td>number</td><td>{@link Pose}</td><td><span class="important">Deprecated:
|
||||
|
|
|
@ -54,10 +54,9 @@ enum Hand {
|
|||
|
||||
/**jsdoc
|
||||
* <p>The <code>Controller.Hardware</code> object has properties representing standard and hardware-specific controller and
|
||||
* computer outputs, plus predefined actions on Interface and the user's avatar. <em>Read-only.</em> The outputs can be mapped
|
||||
* to actions or functions in a {@link RouteObject} mapping. Additionally, hardware-specific controller outputs can be mapped
|
||||
* to standard controller outputs.
|
||||
*
|
||||
* computer outputs, plus predefined actions on Interface and the user's avatar. <em>Read-only.</em></p>
|
||||
* <p>The outputs can be mapped to actions or functions in a {@link RouteObject} mapping. Additionally, hardware-specific
|
||||
* controller outputs can be mapped to standard controller outputs.
|
||||
* <p>Controllers typically implement a subset of the {@link Controller.Standard} controls, plus they may implement some extras.
|
||||
* Some common controllers are included in the table. You can see the outputs provided by these and others by
|
||||
* viewing their {@link Controller.MappingJSON|MappingJSON} files at
|
||||
|
|
|
@ -227,6 +227,7 @@ namespace controller {
|
|||
}
|
||||
|
||||
QObject* ScriptingInterface::loadMapping(const QString& jsonUrl) {
|
||||
// FIXME: Implement. https://highfidelity.manuscript.com/f/cases/14188/Implement-Controller-loadMappping
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ namespace controller {
|
|||
virtual ~ScriptingInterface() {};
|
||||
|
||||
/**jsdoc
|
||||
* Get a list of all available actions.
|
||||
* Gets a list of all available actions.
|
||||
* @function Controller.getAllActions
|
||||
* @returns {Action[]} All available actions.
|
||||
* @deprecated This function is deprecated and will be removed. It no longer works.
|
||||
|
@ -82,7 +82,7 @@ namespace controller {
|
|||
Q_INVOKABLE QVector<Action> getAllActions();
|
||||
|
||||
/**jsdoc
|
||||
* Get a list of all available inputs for a hardware device.
|
||||
* Gets a list of all available inputs for a hardware device.
|
||||
* @function Controller.getAvailableInputs
|
||||
* @param {number} deviceID - Integer ID of the hardware device.
|
||||
* @returns {NamedPair[]} All available inputs for the device.
|
||||
|
@ -92,7 +92,7 @@ namespace controller {
|
|||
Q_INVOKABLE QVector<Input::NamedPair> getAvailableInputs(unsigned int device);
|
||||
|
||||
/**jsdoc
|
||||
* Find the name of a particular controller from its device ID.
|
||||
* Finds the name of a particular controller from its device ID.
|
||||
* @function Controller.getDeviceName
|
||||
* @param {number} deviceID - The integer ID of the device.
|
||||
* @returns {string} The name of the device if found, otherwise <code>"unknown"</code>.
|
||||
|
@ -106,7 +106,7 @@ namespace controller {
|
|||
Q_INVOKABLE QString getDeviceName(unsigned int device);
|
||||
|
||||
/**jsdoc
|
||||
* Get the current value of an action.
|
||||
* Gets the current value of an action.
|
||||
* @function Controller.getActionValue
|
||||
* @param {number} actionID - The integer ID of the action.
|
||||
* @returns {number} The current value of the action.
|
||||
|
@ -121,7 +121,7 @@ namespace controller {
|
|||
Q_INVOKABLE float getActionValue(int action);
|
||||
|
||||
/**jsdoc
|
||||
* Find the ID of a specific controller from its device name.
|
||||
* Finds the ID of a specific controller from its device name.
|
||||
* @function Controller.findDevice
|
||||
* @param {string} deviceName - The name of the device to find.
|
||||
* @returns {number} The integer ID of the device if available, otherwise <code>65535</code>.
|
||||
|
@ -132,7 +132,7 @@ namespace controller {
|
|||
Q_INVOKABLE int findDevice(QString name);
|
||||
|
||||
/**jsdoc
|
||||
* Get the names of all currently available controller devices plus "Actions", "Application", and "Standard".
|
||||
* Gets the names of all currently available controller devices plus "Actions", "Application", and "Standard".
|
||||
* @function Controller.getDeviceNames
|
||||
* @returns {string[]} An array of device names.
|
||||
* @example <caption>Get the names of all currently available controller devices.</caption>
|
||||
|
@ -143,7 +143,7 @@ namespace controller {
|
|||
Q_INVOKABLE QVector<QString> getDeviceNames();
|
||||
|
||||
/**jsdoc
|
||||
* Find the ID of an action from its name.
|
||||
* Finds the ID of an action from its name.
|
||||
* @function Controller.findAction
|
||||
* @param {string} actionName - The name of the action: one of the {@link Controller.Actions} property names.
|
||||
* @returns {number} The integer ID of the action if found, otherwise <code>4095</code>. Note that this value is not
|
||||
|
@ -156,7 +156,7 @@ namespace controller {
|
|||
Q_INVOKABLE int findAction(QString actionName);
|
||||
|
||||
/**jsdoc
|
||||
* Get the names of all actions available as properties of {@link Controller.Actions}.
|
||||
* Gets the names of all actions available as properties of {@link Controller.Actions}.
|
||||
* @function Controller.getActionNames
|
||||
* @returns {string[]} An array of action names.
|
||||
* @example <caption>Get the names of all actions.</caption>
|
||||
|
@ -167,7 +167,7 @@ namespace controller {
|
|||
Q_INVOKABLE QVector<QString> getActionNames() const;
|
||||
|
||||
/**jsdoc
|
||||
* Get the value of a controller button or axis output. Note: Also gets the value of a controller axis output.
|
||||
* Gets the value of a controller button or axis output. Note: Also gets the value of a controller axis output.
|
||||
* @function Controller.getValue
|
||||
* @param {number} source - The {@link Controller.Standard} or {@link Controller.Hardware} item.
|
||||
* @returns {number} The current value of the controller item output if <code>source</code> is valid, otherwise
|
||||
|
@ -186,7 +186,7 @@ namespace controller {
|
|||
Q_INVOKABLE float getValue(const int& source) const;
|
||||
|
||||
/**jsdoc
|
||||
* Get the value of a controller axis output. Note: Also gets the value of a controller button output.
|
||||
* Gets the value of a controller axis output. Note: Also gets the value of a controller button output.
|
||||
* @function Controller.getAxisValue
|
||||
* @param {number} source - The {@link Controller.Standard} or {@link Controller.Hardware} item.
|
||||
* @returns {number} The current value of the controller item output if <code>source</code> is valid, otherwise
|
||||
|
@ -196,7 +196,7 @@ namespace controller {
|
|||
Q_INVOKABLE float getAxisValue(int source) const;
|
||||
|
||||
/**jsdoc
|
||||
* Get the value of a controller pose output.
|
||||
* Gets the value of a controller pose output.
|
||||
* @function Controller.getPoseValue
|
||||
* @param {number} source - The {@link Controller.Standard} or {@link Controller.Hardware} pose output.
|
||||
* @returns {Pose} The current value of the controller pose output if <code>source</code> is a pose output, otherwise
|
||||
|
@ -210,9 +210,9 @@ namespace controller {
|
|||
/**jsdoc
|
||||
* Triggers a haptic pulse on connected and enabled devices that have the capability.
|
||||
* @function Controller.triggerHapticPulse
|
||||
* @param {number} strength - The strength of the haptic pulse, <code>0.0</code> – <code>1.0</code>.
|
||||
* @param {number} strength - The strength of the haptic pulse, range <code>0.0</code> – <code>1.0</code>.
|
||||
* @param {number} duration - The duration of the haptic pulse, in milliseconds.
|
||||
* @param {Controller.Hand} hand=2 - The hand or hands to trigger the haptic pulse on.
|
||||
* @param {Controller.Hand} [hand=2] - The hand or hands to trigger the haptic pulse on.
|
||||
* @example <caption>Trigger a haptic pulse on the right hand.</caption>
|
||||
* var HAPTIC_STRENGTH = 0.5;
|
||||
* var HAPTIC_DURATION = 10;
|
||||
|
@ -224,8 +224,8 @@ namespace controller {
|
|||
/**jsdoc
|
||||
* Triggers a 250ms haptic pulse on connected and enabled devices that have the capability.
|
||||
* @function Controller.triggerShortHapticPulse
|
||||
* @param {number} strength - The strength of the haptic pulse, <code>0.0</code> – <code>1.0</code>.
|
||||
* @param {Controller.Hand} hand=2 - The hand or hands to trigger the haptic pulse on.
|
||||
* @param {number} strength - The strength of the haptic pulse, range <code>0.0</code> – <code>1.0</code>.
|
||||
* @param {Controller.Hand} [hand=2] - The hand or hands to trigger the haptic pulse on.
|
||||
*/
|
||||
Q_INVOKABLE bool triggerShortHapticPulse(float strength, controller::Hand hand = BOTH) const;
|
||||
|
||||
|
@ -233,9 +233,9 @@ namespace controller {
|
|||
* Triggers a haptic pulse on a particular device if connected and enabled and it has the capability.
|
||||
* @function Controller.triggerHapticPulseOnDevice
|
||||
* @param {number} deviceID - The ID of the device to trigger the haptic pulse on.
|
||||
* @param {number} strength - The strength of the haptic pulse, <code>0.0</code> – <code>1.0</code>.
|
||||
* @param {number} strength - The strength of the haptic pulse, range <code>0.0</code> – <code>1.0</code>.
|
||||
* @param {number} duration - The duration of the haptic pulse, in milliseconds.
|
||||
* @param {Controller.Hand} hand=2 - The hand or hands to trigger the haptic pulse on.
|
||||
* @param {Controller.Hand} [hand=2] - The hand or hands to trigger the haptic pulse on.
|
||||
* @example <caption>Trigger a haptic pulse on an Oculus Touch controller.</caption>
|
||||
* var HAPTIC_STRENGTH = 0.5;
|
||||
* var deviceID = Controller.findDevice("OculusTouch");
|
||||
|
@ -250,19 +250,19 @@ namespace controller {
|
|||
* Triggers a 250ms haptic pulse on a particular device if connected and enabled and it has the capability.
|
||||
* @function Controller.triggerShortHapticPulseOnDevice
|
||||
* @param {number} deviceID - The ID of the device to trigger the haptic pulse on.
|
||||
* @param {number} strength - The strength of the haptic pulse, <code>0.0</code> – <code>1.0</code>.
|
||||
* @param {Controller.Hand} hand=2 - The hand or hands to trigger the haptic pulse on.
|
||||
* @param {number} strength - The strength of the haptic pulse, range <code>0.0</code> – <code>1.0</code>.
|
||||
* @param {Controller.Hand} [hand=2] - The hand or hands to trigger the haptic pulse on.
|
||||
*/
|
||||
Q_INVOKABLE bool triggerShortHapticPulseOnDevice(unsigned int device, float strength, controller::Hand hand = BOTH)
|
||||
const;
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* Create a new controller mapping. Routes can then be added to the mapping using {@link MappingObject} methods and
|
||||
* Creates a new controller mapping. Routes can then be added to the mapping using {@link MappingObject} methods and
|
||||
* routed to <code>Standard</code> controls, <code>Actions</code>, or script functions using {@link RouteObject}
|
||||
* methods. The mapping can then be enabled using {@link Controller.enableMapping|enableMapping} for it to take effect.
|
||||
* @function Controller.newMapping
|
||||
* @param {string} mappingName=Uuid.generate() - A unique name for the mapping. If not specified a new UUID generated
|
||||
* @param {string} [mappingName=Uuid.generate()] - A unique name for the mapping. If not specified a new UUID generated
|
||||
* by {@link Uuid.generate} is used.
|
||||
* @returns {MappingObject} A controller mapping object.
|
||||
* @example <caption>Create a simple mapping that makes the right trigger move your avatar up.</caption>
|
||||
|
@ -279,22 +279,22 @@ namespace controller {
|
|||
Q_INVOKABLE QObject* newMapping(const QString& mappingName = QUuid::createUuid().toString());
|
||||
|
||||
/**jsdoc
|
||||
* Enable or disable a controller mapping. When enabled, the routes in the mapping have effect.
|
||||
* Enables or disables a controller mapping. When enabled, the routes in the mapping have effect.
|
||||
* @function Controller.enableMapping
|
||||
* @param {string} mappingName - The name of the mapping.
|
||||
* @param {boolean} enable=true - If <code>true</code> then the mapping is enabled, otherwise it is disabled.
|
||||
* @param {boolean} [[enable=true] - If <code>true</code> then the mapping is enabled, otherwise it is disabled.
|
||||
*/
|
||||
Q_INVOKABLE void enableMapping(const QString& mappingName, bool enable = true);
|
||||
|
||||
/**jsdoc
|
||||
* Disable a controller mapping. When disabled, the routes in the mapping have no effect.
|
||||
* Disables a controller mapping. When disabled, the routes in the mapping have no effect.
|
||||
* @function Controller.disableMapping
|
||||
* @param {string} mappingName - The name of the mapping.
|
||||
*/
|
||||
Q_INVOKABLE void disableMapping(const QString& mappingName) { enableMapping(mappingName, false); }
|
||||
|
||||
/**jsdoc
|
||||
* Create a new controller mapping from a {@link Controller.MappingJSON|MappingJSON} string. Use
|
||||
* Creates a new controller mapping from a {@link Controller.MappingJSON|MappingJSON} string. Use
|
||||
* {@link Controller.enableMapping|enableMapping} to enable the mapping for it to take effect.
|
||||
* @function Controller.parseMapping
|
||||
* @param {string} jsonString - A JSON string of the {@link Controller.MappingJSON|MappingJSON}.
|
||||
|
@ -317,19 +317,19 @@ namespace controller {
|
|||
Q_INVOKABLE QObject* parseMapping(const QString& json);
|
||||
|
||||
/**jsdoc
|
||||
* Create a new controller mapping from a {@link Controller.MappingJSON|MappingJSON} JSON file at a URL. Use
|
||||
* Creates a new controller mapping from a {@link Controller.MappingJSON|MappingJSON} JSON file at a URL. Use
|
||||
* {@link Controller.enableMapping|enableMapping} to enable the mapping for it to take effect.
|
||||
* <p><strong>Warning:</strong> This function is not yet implemented; it doesn't load a mapping and just returns
|
||||
* <code>null</code>.
|
||||
* @function Controller.loadMapping
|
||||
* @param {string} jsonURL - The URL the {@link Controller.MappingJSON|MappingJSON} JSON file.
|
||||
* @returns {MappingObject} A controller mapping object.
|
||||
* @todo <em>Implement this function. It currently does not load the mapping from the file; it just returns
|
||||
* <code>null</code>.</em>
|
||||
*/
|
||||
Q_INVOKABLE QObject* loadMapping(const QString& jsonUrl);
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* Get the {@link Controller.Hardware} property tree. Calling this function is the same as using the {@link Controller}
|
||||
* Gets the {@link Controller.Hardware} property tree. Calling this function is the same as using the {@link Controller}
|
||||
* property, <code>Controller.Hardware</code>.
|
||||
* @function Controller.getHardware
|
||||
* @returns {Controller.Hardware} The {@link Controller.Hardware} property tree.
|
||||
|
@ -337,7 +337,7 @@ namespace controller {
|
|||
Q_INVOKABLE const QVariantMap getHardware() { return _hardware; }
|
||||
|
||||
/**jsdoc
|
||||
* Get the {@link Controller.Actions} property tree. Calling this function is the same as using the {@link Controller}
|
||||
* Gets the {@link Controller.Actions} property tree. Calling this function is the same as using the {@link Controller}
|
||||
* property, <code>Controller.Actions</code>.
|
||||
* @function Controller.getActions
|
||||
* @returns {Controller.Actions} The {@link Controller.Actions} property tree.
|
||||
|
@ -345,7 +345,7 @@ namespace controller {
|
|||
Q_INVOKABLE const QVariantMap getActions() { return _actions; } //undefined
|
||||
|
||||
/**jsdoc
|
||||
* Get the {@link Controller.Standard} property tree. Calling this function is the same as using the {@link Controller}
|
||||
* Gets the {@link Controller.Standard} property tree. Calling this function is the same as using the {@link Controller}
|
||||
* property, <code>Controller.Standard</code>.
|
||||
* @function Controller.getStandard
|
||||
* @returns {Controller.Standard} The {@link Controller.Standard} property tree.
|
||||
|
@ -354,7 +354,7 @@ namespace controller {
|
|||
|
||||
|
||||
/**jsdoc
|
||||
* Start making a recording of currently active controllers.
|
||||
* Starts making a recording of currently active controllers.
|
||||
* @function Controller.startInputRecording
|
||||
* @example <caption>Make a controller recording.</caption>
|
||||
* // Delay start of recording for 2s.
|
||||
|
@ -374,13 +374,13 @@ namespace controller {
|
|||
Q_INVOKABLE void startInputRecording();
|
||||
|
||||
/**jsdoc
|
||||
* Stop making a recording started by {@link Controller.startInputRecording|startInputRecording}.
|
||||
* Stops making a recording started by {@link Controller.startInputRecording|startInputRecording}.
|
||||
* @function Controller.stopInputRecording
|
||||
*/
|
||||
Q_INVOKABLE void stopInputRecording();
|
||||
|
||||
/**jsdoc
|
||||
* Play back the current recording from the beginning. The current recording may have been recorded by
|
||||
* Plays back the current recording from the beginning. The current recording may have been recorded by
|
||||
* {@link Controller.startInputRecording|startInputRecording} and
|
||||
* {@link Controller.stopInputRecording|stopInputRecording}, or loaded by
|
||||
* {@link Controller.loadInputRecording|loadInputRecording}. Playback repeats in a loop until
|
||||
|
@ -403,13 +403,13 @@ namespace controller {
|
|||
Q_INVOKABLE void startInputPlayback();
|
||||
|
||||
/**jsdoc
|
||||
* Stop play back of a recording started by {@link Controller.startInputPlayback|startInputPlayback}.
|
||||
* Stops play back of a recording started by {@link Controller.startInputPlayback|startInputPlayback}.
|
||||
* @function Controller.stopInputPlayback
|
||||
*/
|
||||
Q_INVOKABLE void stopInputPlayback();
|
||||
|
||||
/**jsdoc
|
||||
* Save the current recording to a file. The current recording may have been recorded by
|
||||
* Saves the current recording to a file. The current recording may have been recorded by
|
||||
* {@link Controller.startInputRecording|startInputRecording} and
|
||||
* {@link Controller.stopInputRecording|stopInputRecording}, or loaded by
|
||||
* {@link Controller.loadInputRecording|loadInputRecording}. It is saved in the directory returned by
|
||||
|
@ -419,24 +419,26 @@ namespace controller {
|
|||
Q_INVOKABLE void saveInputRecording();
|
||||
|
||||
/**jsdoc
|
||||
* Load an input recording, ready for play back.
|
||||
* Loads an input recording, ready for play back.
|
||||
* @function Controller.loadInputRecording
|
||||
* @param {string} file - The path to the recording file, prefixed by <code>"file:///"</code>.
|
||||
*/
|
||||
Q_INVOKABLE void loadInputRecording(const QString& file);
|
||||
|
||||
/**jsdoc
|
||||
* Get the directory in which input recordings are saved.
|
||||
* Gets the directory in which input recordings are saved.
|
||||
* @function Controller.getInputRecorderSaveDirectory
|
||||
* @returns {string} The directory in which input recordings are saved.
|
||||
*/
|
||||
Q_INVOKABLE QString getInputRecorderSaveDirectory();
|
||||
|
||||
/**jsdoc
|
||||
* Get all the active and enabled (running) input devices
|
||||
* @function Controller.getRunningInputDevices
|
||||
* @returns {string[]} An array of strings with the names
|
||||
*/
|
||||
* Gets the names of all the active and running (enabled) input devices.
|
||||
* @function Controller.getRunningInputDevices
|
||||
* @returns {string[]} The list of current active and running input devices.
|
||||
* @example <caption>List all active and running input devices.</caption>
|
||||
* print("Running devices: " + JSON.stringify(Controller.getRunningInputDeviceNames()));
|
||||
*/
|
||||
Q_INVOKABLE QStringList getRunningInputDeviceNames();
|
||||
|
||||
bool isMouseCaptured() const { return _mouseCaptured; }
|
||||
|
@ -447,7 +449,7 @@ namespace controller {
|
|||
public slots:
|
||||
|
||||
/**jsdoc
|
||||
* Disable processing of mouse "move", "press", "double-press", and "release" events into
|
||||
* Disables processing of mouse "move", "press", "double-press", and "release" events into
|
||||
* {@link Controller.Hardware|Controller.Hardware.Keyboard} outputs.
|
||||
* @function Controller.captureMouseEvents
|
||||
* @example <caption>Disable Controller.Hardware.Keyboard mouse events for a short period.</caption>
|
||||
|
@ -475,7 +477,7 @@ namespace controller {
|
|||
virtual void captureMouseEvents() { _mouseCaptured = true; }
|
||||
|
||||
/**jsdoc
|
||||
* Enable processing of mouse "move", "press", "double-press", and "release" events into
|
||||
* Enables processing of mouse "move", "press", "double-press", and "release" events into
|
||||
* {@link Controller.Hardware-Keyboard|Controller.Hardware.Keyboard} outputs that were disabled using
|
||||
* {@link Controller.captureMouseEvents|captureMouseEvents}.
|
||||
* @function Controller.releaseMouseEvents
|
||||
|
@ -484,7 +486,7 @@ namespace controller {
|
|||
|
||||
|
||||
/**jsdoc
|
||||
* Disable processing of touch "begin", "update", and "end" events into
|
||||
* Disables processing of touch "begin", "update", and "end" events into
|
||||
* {@link Controller.Hardware|Controller.Hardware.Keyboard},
|
||||
* {@link Controller.Hardware|Controller.Hardware.Touchscreen}, and
|
||||
* {@link Controller.Hardware|Controller.Hardware.TouchscreenVirtualPad} outputs.
|
||||
|
@ -493,7 +495,7 @@ namespace controller {
|
|||
virtual void captureTouchEvents() { _touchCaptured = true; }
|
||||
|
||||
/**jsdoc
|
||||
* Enable processing of touch "begin", "update", and "end" events into
|
||||
* Enables processing of touch "begin", "update", and "end" events into
|
||||
* {@link Controller.Hardware|Controller.Hardware.Keyboard},
|
||||
* {@link Controller.Hardware|Controller.Hardware.Touchscreen}, and
|
||||
* {@link Controller.Hardware|Controller.Hardware.TouchscreenVirtualPad} outputs that were disabled using
|
||||
|
@ -504,14 +506,14 @@ namespace controller {
|
|||
|
||||
|
||||
/**jsdoc
|
||||
* Disable processing of mouse wheel rotation events into {@link Controller.Hardware|Controller.Hardware.Keyboard}
|
||||
* Disables processing of mouse wheel rotation events into {@link Controller.Hardware|Controller.Hardware.Keyboard}
|
||||
* outputs.
|
||||
* @function Controller.captureWheelEvents
|
||||
*/
|
||||
virtual void captureWheelEvents() { _wheelCaptured = true; }
|
||||
|
||||
/**jsdoc
|
||||
* Enable processing of mouse wheel rotation events into {@link Controller.Hardware|Controller.Hardware.Keyboard}
|
||||
* Enables processing of mouse wheel rotation events into {@link Controller.Hardware|Controller.Hardware.Keyboard}
|
||||
* outputs that wer disabled using {@link Controller.captureWheelEvents|captureWheelEvents}.
|
||||
* @function Controller.releaseWheelEvents
|
||||
*/
|
||||
|
@ -519,7 +521,7 @@ namespace controller {
|
|||
|
||||
|
||||
/**jsdoc
|
||||
* Disable translating and rotating the user's avatar in response to keyboard and controller controls.
|
||||
* Disables translating and rotating the user's avatar in response to keyboard and controller controls.
|
||||
* @function Controller.captureActionEvents
|
||||
* @example <caption>Disable avatar translation and rotation for a short period.</caption>
|
||||
* Script.setTimeout(function () {
|
||||
|
@ -533,12 +535,19 @@ namespace controller {
|
|||
virtual void captureActionEvents() { _actionsCaptured = true; }
|
||||
|
||||
/**jsdoc
|
||||
* Enable translating and rotating the user's avatar in response to keyboard and controller controls that were disabled
|
||||
* Enables translating and rotating the user's avatar in response to keyboard and controller controls that were disabled
|
||||
* using {@link Controller.captureActionEvents|captureActionEvents}.
|
||||
* @function Controller.releaseActionEvents
|
||||
*/
|
||||
virtual void releaseActionEvents() { _actionsCaptured = false; }
|
||||
|
||||
/**jsdoc
|
||||
* @function Controller.updateRunningInputDevices
|
||||
* @param {string} deviceName - Device name.
|
||||
* @param {boolean} isRunning - Is running.
|
||||
* @param {string[]} runningDevices - Running devices.
|
||||
* @deprecated This function is deprecated and will be removed.
|
||||
*/
|
||||
void updateRunningInputDevices(const QString& deviceName, bool isRunning, const QStringList& runningDevices);
|
||||
|
||||
signals:
|
||||
|
@ -593,7 +602,7 @@ namespace controller {
|
|||
|
||||
/**jsdoc
|
||||
* Triggered when a device is registered or unregistered by a plugin. Not all plugins generate
|
||||
* <code>hardwareChanged</code> events: for example connecting or disconnecting a mouse will not generate an event but
|
||||
* <code>hardwareChanged</code> events: for example, connecting or disconnecting a mouse will not generate an event but
|
||||
* connecting or disconnecting an Xbox controller will.
|
||||
* @function Controller.hardwareChanged
|
||||
* @returns {Signal}
|
||||
|
@ -601,13 +610,13 @@ namespace controller {
|
|||
void hardwareChanged();
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when a device is enabled/disabled
|
||||
* Enabling/Disabling Leapmotion on settings/controls will trigger this signal.
|
||||
* @function Controller.deviceRunningChanged
|
||||
* @param {string} deviceName - The name of the device that is getting enabled/disabled
|
||||
* @param {boolean} isEnabled - Return if the device is enabled.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
* Triggered when an input device starts or stops being active and running (enabled). For example, enabling or
|
||||
* disabling the LeapMotion in Settings > Controls > Calibration will trigger this signal.
|
||||
* @function Controller.inputDeviceRunningChanged
|
||||
* @param {string} deviceName - The name of the device.
|
||||
* @param {boolean} isRunning - <code>true</code> if the device is active and running, <code>false</code> if it isn't.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void inputDeviceRunningChanged(QString deviceName, bool isRunning);
|
||||
|
||||
|
||||
|
|
|
@ -30,17 +30,16 @@ void StandardController::focusOutEvent() {
|
|||
/**jsdoc
|
||||
* <p>The <code>Controller.Standard</code> object has properties representing standard controller outputs. Those for physical
|
||||
* controllers are based on the XBox controller, with aliases for PlayStation. The property values are integer IDs, uniquely
|
||||
* identifying each output. <em>Read-only.</em> These can be mapped to actions or functions in a {@link RouteObject}
|
||||
* mapping.</p>
|
||||
*
|
||||
* <p>The data value provided by each control is either a number or a {@link Pose}. Numbers are typically normalized to
|
||||
* <code>0.0</code> or <code>1.0</code> for button states, the range <code>0.0 – 1.0</code> for unidirectional scales,
|
||||
* and the range <code>-1.0 – 1.0</code> for bidirectional scales.</p>
|
||||
*
|
||||
* <p>Each hardware device has a mapping from its outputs to <code>Controller.Standard</code> items, specified in a JSON file.
|
||||
* For example, <a href="https://github.com/highfidelity/hifi/blob/master/interface/resources/controllers/leapmotion.json">
|
||||
* leapmotion.json</a> and
|
||||
* <a href="https://github.com/highfidelity/hifi/blob/master/interface/resources/controllers/vive.json">vive.json</a>.</p>
|
||||
* identifying each output. <em>Read-only.</em></p>
|
||||
* <p>These outputs can be mapped to actions or functions in a {@link RouteObject} mapping. The data value provided by each
|
||||
* control is either a number or a {@link Pose}. Numbers are typically normalized to <code>0.0</code> or <code>1.0</code> for
|
||||
* button states, the range <code>0.0</code> – </code>1.0</code> for unidirectional scales, and the range
|
||||
* <code>-1.0</code> – <code>1.0</code> for bidirectional scales.</p>
|
||||
* <p>Each hardware device has a mapping from its outputs to a subset of <code>Controller.Standard</code> items, specified in a
|
||||
* JSON file. For example,
|
||||
* <a href="https://github.com/highfidelity/hifi/blob/master/interface/resources/controllers/vive.json">vive.json</a>
|
||||
* and <a href="https://github.com/highfidelity/hifi/blob/master/interface/resources/controllers/leapmotion.json">
|
||||
* leapmotion.json</a>.</p>
|
||||
*
|
||||
* <table>
|
||||
* <thead>
|
||||
|
@ -119,12 +118,12 @@ void StandardController::focusOutEvent() {
|
|||
* button.</td></tr>
|
||||
* <tr><td><code>RightThumbUp</code></td><td>number</td><td>number</td><td>Right thumb not touching primary or secondary
|
||||
* thumb buttons.</td></tr>
|
||||
* <tr><td><code>LeftPrimaryIndex</code></td><td>number</td><td>number</td><td>Left primary index control pressed.
|
||||
* <strong>To Do:</strong> <em>Implement this for current controllers.</em></td></tr>
|
||||
* <tr><td><code>LeftPrimaryIndex</code></td><td>number</td><td>number</td><td>Left primary index control
|
||||
* pressed.</em></td></tr>
|
||||
* <tr><td><code>LeftSecondaryIndex</code></td><td>number</td><td>number</td><td>Left secondary index control pressed.
|
||||
* </td></tr>
|
||||
* <tr><td><code>RightPrimaryIndex</code></td><td>number</td><td>number</td><td>Right primary index control pressed.
|
||||
* <strong>To Do:</strong> <em>Implement this for current controllers.</em></td></tr>
|
||||
* </td></tr>
|
||||
* <tr><td><code>RightSecondaryIndex</code></td><td>number</td><td>number</td><td>Right secondary index control pressed.
|
||||
* </td></tr>
|
||||
* <tr><td><code>LeftPrimaryIndexTouch</code></td><td>number</td><td>number</td><td>Left index finger is touching primary
|
||||
|
|
|
@ -84,12 +84,12 @@ class UserInputMapper;
|
|||
/**jsdoc
|
||||
* A route in a {@link Controller.MappingJSON}.
|
||||
* @typedef {object} Controller.MappingJSONRoute
|
||||
* @property {string|Controller.MappingJSONAxis} from - The name of a {@link Controller.Hardware} property name or an axis
|
||||
* made from them. If a property name, the leading <code>"Controller.Hardware."</code> can be omitted.
|
||||
* @property {boolean} [peek=false] - If <codd>true</code> then peeking is enabled per {@link RouteObject#peek}.
|
||||
* @property {boolean} [debug=false] - If <code>true</code> then debug is enabled per {@link RouteObject#debug}.
|
||||
* @property {string|Controller.MappingJSONAxis} from - The name of a {@link Controller.Hardware} property or an axis made from
|
||||
* them. If a property name, the leading <code>"Controller.Hardware."</code> can be omitted.
|
||||
* @property {boolean} [peek=false] - If <codd>true</code>, then peeking is enabled per {@link RouteObject#peek}.
|
||||
* @property {boolean} [debug=false] - If <code>true</code>, then debug is enabled per {@link RouteObject#debug}.
|
||||
* @property {string|string[]} [when=[]] - One or more numeric {@link Controller.Hardware} property names which are evaluated
|
||||
* as booleans and ANDed together. Prepend with a <code>!</code> to use the logical NOT of the property value. The leading
|
||||
* as booleans and ANDed together. Prepend a property name with a <code>!</code> to do a logical NOT. The leading
|
||||
* <code>"Controller.Hardware."</code> can be omitted from the property names.
|
||||
* @property {Controller.MappingJSONFilter|Controller.MappingJSONFilter[]} [filters=[]] - One or more filters in the route.
|
||||
* @property {string} to - The name of a {@link Controller.Actions} or {@link Controller.Standard} property. The leading
|
||||
|
@ -134,7 +134,7 @@ public:
|
|||
: _parent(parent), _mapping(mapping) { }
|
||||
|
||||
/**jsdoc
|
||||
* Create a new {@link RouteObject} from a controller output, ready to be mapped to a standard control, action, or
|
||||
* Creates a new {@link RouteObject} from a controller output, ready to be mapped to a standard control, action, or
|
||||
* function.<br />
|
||||
* This is a QML-specific version of {@link MappingObject#from|from}: use this version in QML files.
|
||||
* @function MappingObject#fromQml
|
||||
|
@ -145,7 +145,7 @@ public:
|
|||
Q_INVOKABLE QObject* fromQml(const QJSValue& source);
|
||||
|
||||
/**jsdoc
|
||||
* Create a new {@link RouteObject} from two numeric {@link Controller.Hardware} outputs, one applied in the negative
|
||||
* Creates a new {@link RouteObject} from two numeric {@link Controller.Hardware} outputs, one applied in the negative
|
||||
* direction and the other in the positive direction, ready to be mapped to a standard control, action, or function.<br />
|
||||
* This is a QML-specific version of {@link MappingObject#makeAxis|makeAxis}: use this version in QML files.
|
||||
* @function MappingObject#makeAxisQml
|
||||
|
@ -157,7 +157,7 @@ public:
|
|||
Q_INVOKABLE QObject* makeAxisQml(const QJSValue& source1, const QJSValue& source2);
|
||||
|
||||
/**jsdoc
|
||||
* Create a new {@link RouteObject} from a controller output, ready to be mapped to a standard control, action, or
|
||||
* Creates a new {@link RouteObject} from a controller output, ready to be mapped to a standard control, action, or
|
||||
* function.
|
||||
* @function MappingObject#from
|
||||
* @param {Controller.Standard|Controller.Hardware|function} source - The controller output or function that is the source
|
||||
|
@ -167,7 +167,7 @@ public:
|
|||
Q_INVOKABLE QObject* from(const QScriptValue& source);
|
||||
|
||||
/**jsdoc
|
||||
* Create a new {@link RouteObject} from two numeric {@link Controller.Hardware} outputs, one applied in the negative
|
||||
* Creates a new {@link RouteObject} from two numeric {@link Controller.Hardware} outputs, one applied in the negative
|
||||
* direction and the other in the positive direction, ready to be mapped to a standard control, action, or function.
|
||||
* @function MappingObject#makeAxis
|
||||
* @param {Controller.Hardware} source1 - The first, negative-direction controller output.
|
||||
|
@ -189,7 +189,7 @@ public:
|
|||
Q_INVOKABLE QObject* makeAxis(const QScriptValue& source1, const QScriptValue& source2);
|
||||
|
||||
/**jsdoc
|
||||
* Enable or disable the mapping. When enabled, the routes in the mapping take effect.<br />
|
||||
* Enables or disables the mapping. When enabled, the routes in the mapping take effect.<br />
|
||||
* Synonymous with {@link Controller.enableMapping}.
|
||||
* @function MappingObject#enable
|
||||
* @param {boolean} enable=true - If <code>true</code> then the mapping is enabled, otherwise it is disabled.
|
||||
|
@ -198,7 +198,7 @@ public:
|
|||
Q_INVOKABLE QObject* enable(bool enable = true);
|
||||
|
||||
/**jsdoc
|
||||
* Disable the mapping. When disabled, the routes in the mapping have no effect.<br />
|
||||
* Disables the mapping. When disabled, the routes in the mapping have no effect.<br />
|
||||
* Synonymous with {@link Controller.disableMapping}.
|
||||
* @function MappingObject#disable
|
||||
* @returns {MappingObject} The mapping object, so that further routes can be added.
|
||||
|
|
|
@ -66,6 +66,8 @@ QObject* RouteBuilderProxy::peek(bool enable) {
|
|||
}
|
||||
|
||||
QObject* RouteBuilderProxy::when(const QScriptValue& expression) {
|
||||
// FIXME: Support "!" conditional in simple expression and array expression.
|
||||
// Note that "!" is supported when parsing a JSON file, in UserInputMapper::parseConditional().
|
||||
auto newConditional = _parent.conditionalFor(expression);
|
||||
if (_route->conditional) {
|
||||
_route->conditional = ConditionalPointer(new AndConditional(_route->conditional, newConditional));
|
||||
|
|
|
@ -51,7 +51,7 @@ class RouteBuilderProxy : public QObject {
|
|||
: _parent(parent), _mapping(mapping), _route(route) { }
|
||||
|
||||
/**jsdoc
|
||||
* Terminate the route with a standard control, an action, or a script function. The output value from the route is
|
||||
* Terminates the route with a standard control, an action, or a script function. The output value from the route is
|
||||
* sent to the specified destination.<br />
|
||||
* This is a QML-specific version of {@link MappingObject#to|to}: use this version in QML files.
|
||||
* @function RouteObject#toQml
|
||||
|
@ -62,7 +62,7 @@ class RouteBuilderProxy : public QObject {
|
|||
Q_INVOKABLE void toQml(const QJSValue& destination);
|
||||
|
||||
/**jsdoc
|
||||
* Process the route only if a condition is satisfied. The condition is evaluated before the route input is read, and
|
||||
* Processes the route only if a condition is satisfied. The condition is evaluated before the route input is read, and
|
||||
* the input is read only if the condition is <code>true</code>. Thus, if the condition is not met then subsequent
|
||||
* routes using the same input are processed.<br />
|
||||
* This is a QML-specific version of {@link MappingObject#to|to}: use this version in QML files.
|
||||
|
@ -81,7 +81,7 @@ class RouteBuilderProxy : public QObject {
|
|||
Q_INVOKABLE QObject* whenQml(const QJSValue& expression);
|
||||
|
||||
/**jsdoc
|
||||
* Terminate the route with a standard control, an action, or a script function. The output value from the route is
|
||||
* Terminates the route with a standard control, an action, or a script function. The output value from the route is
|
||||
* sent to the specified destination.
|
||||
* @function RouteObject#to
|
||||
* @param {Controller.Standard|Controller.Actions|function} destination - The standard control, action, or JavaScript
|
||||
|
@ -117,7 +117,7 @@ class RouteBuilderProxy : public QObject {
|
|||
Q_INVOKABLE void to(const QScriptValue& destination);
|
||||
|
||||
/**jsdoc
|
||||
* Enable and disabling writing debug information for a route to the program log.
|
||||
* Enables or disables writing debug information for a route to the program log.
|
||||
* @function RouteObject#debug
|
||||
* @param {boolean} [enable=true] - If <code>true</code> then writing debug information is enabled for the route,
|
||||
* otherwise it is disabled.
|
||||
|
@ -147,7 +147,7 @@ class RouteBuilderProxy : public QObject {
|
|||
Q_INVOKABLE QObject* debug(bool enable = true);
|
||||
|
||||
/**jsdoc
|
||||
* Process the route without marking the controller output as having been read, so that other routes from the same
|
||||
* Processes the route without marking the controller output as having been read, so that other routes from the same
|
||||
* controller output can also process.
|
||||
* @function RouteObject#peek
|
||||
* @param {boolean} [enable=true] - If <code>true</code> then the route is processed without marking the route's
|
||||
|
@ -157,7 +157,7 @@ class RouteBuilderProxy : public QObject {
|
|||
Q_INVOKABLE QObject* peek(bool enable = true);
|
||||
|
||||
/**jsdoc
|
||||
* Process the route only if a condition is satisfied. The condition is evaluated before the route input is read, and
|
||||
* Processes the route only if a condition is satisfied. The condition is evaluated before the route input is read, and
|
||||
* the input is read only if the condition is <code>true</code>. Thus, if the condition is not met then subsequent
|
||||
* routes using the same input are processed.
|
||||
* @function RouteObject#when
|
||||
|
@ -170,6 +170,8 @@ class RouteBuilderProxy : public QObject {
|
|||
* definition.</li>
|
||||
* </ul>
|
||||
* <p>If an array of conditions is provided, their values are ANDed together.</p>
|
||||
* <p><strong>Warning:</strong> The use of <code>!</code> is not currently supported in JavaScript <code>.when()</code>
|
||||
* calls.</p>
|
||||
* @returns {RouteObject} The <code>RouteObject</code> with the condition added.
|
||||
* @example <caption>Process the right trigger differently in HMD and desktop modes.</caption>
|
||||
* var MAPPING_NAME = "com.highfidelity.controllers.example.newMapping";
|
||||
|
@ -193,7 +195,7 @@ class RouteBuilderProxy : public QObject {
|
|||
Q_INVOKABLE QObject* when(const QScriptValue& expression);
|
||||
|
||||
/**jsdoc
|
||||
* Filter numeric route values to lie between two values; values outside this range are not passed on through the
|
||||
* Filters numeric route values to lie between two values; values outside this range are not passed on through the
|
||||
* route.
|
||||
* @function RouteObject#clamp
|
||||
* @param {number} min - The minimum value to pass through.
|
||||
|
@ -214,7 +216,7 @@ class RouteBuilderProxy : public QObject {
|
|||
Q_INVOKABLE QObject* clamp(float min, float max);
|
||||
|
||||
/**jsdoc
|
||||
* Filter numeric route values such that they are rounded to <code>0</code> or <code>1</code> without output values
|
||||
* Filters numeric route values such that they are rounded to <code>0</code> or <code>1</code> without output values
|
||||
* flickering when the input value hovers around <code>0.5</code>. For example, this enables you to use an analog input
|
||||
* as if it were a toggle.
|
||||
* @function RouteObject#hysteresis
|
||||
|
@ -239,7 +241,7 @@ class RouteBuilderProxy : public QObject {
|
|||
Q_INVOKABLE QObject* hysteresis(float min, float max);
|
||||
|
||||
/**jsdoc
|
||||
* Filter numeric route values to send at a specified interval.
|
||||
* Filters numeric route values to send at a specified interval.
|
||||
* @function RouteObject#pulse
|
||||
* @param {number} interval - The interval between sending values, in seconds.
|
||||
* @returns {RouteObject} The <code>RouteObject</code> with the filter applied.
|
||||
|
@ -258,7 +260,7 @@ class RouteBuilderProxy : public QObject {
|
|||
Q_INVOKABLE QObject* pulse(float interval);
|
||||
|
||||
/**jsdoc
|
||||
* Filter numeric and {@link Pose} route values to be scaled by a constant amount.
|
||||
* Filters numeric and {@link Pose} route values to be scaled by a constant amount.
|
||||
* @function RouteObject#scale
|
||||
* @param {number} multiplier - The scale to multiply the value by.
|
||||
* @returns {RouteObject} The <code>RouteObject</code> with the filter applied.
|
||||
|
@ -280,7 +282,7 @@ class RouteBuilderProxy : public QObject {
|
|||
Q_INVOKABLE QObject* scale(float multiplier);
|
||||
|
||||
/**jsdoc
|
||||
* Filter numeric and {@link Pose} route values to have the opposite sign, e.g., <code>0.5</code> is changed to
|
||||
* Filters numeric and {@link Pose} route values to have the opposite sign, e.g., <code>0.5</code> is changed to
|
||||
* <code>-0.5</code>.
|
||||
* @function RouteObject#invert
|
||||
* @returns {RouteObject} The <code>RouteObject</code> with the filter applied.
|
||||
|
@ -302,7 +304,7 @@ class RouteBuilderProxy : public QObject {
|
|||
Q_INVOKABLE QObject* invert();
|
||||
|
||||
/**jsdoc
|
||||
* Filter numeric route values such that they're sent only when the input value is outside a dead-zone. When the input
|
||||
* Filters numeric route values such that they're sent only when the input value is outside a dead-zone. When the input
|
||||
* passes the dead-zone value, output is sent starting at <code>0.0</code> and catching up with the input value. As the
|
||||
* input returns toward the dead-zone value, output values reduce to <code>0.0</code> at the dead-zone value.
|
||||
* @function RouteObject#deadZone
|
||||
|
@ -324,7 +326,7 @@ class RouteBuilderProxy : public QObject {
|
|||
Q_INVOKABLE QObject* deadZone(float min);
|
||||
|
||||
/**jsdoc
|
||||
* Filter numeric route values such that they are rounded to <code>-1</code>, <code>0</code>, or <code>1</code>.
|
||||
* Filters numeric route values such that they are rounded to <code>-1</code>, <code>0</code>, or <code>1</code>.
|
||||
* For example, this enables you to use an analog input as if it were a toggle or, in the case of a bidirectional axis,
|
||||
* a tri-state switch.
|
||||
* @function RouteObject#constrainToInteger
|
||||
|
@ -345,7 +347,7 @@ class RouteBuilderProxy : public QObject {
|
|||
Q_INVOKABLE QObject* constrainToInteger();
|
||||
|
||||
/**jsdoc
|
||||
* Filter numeric route values such that they are rounded to <code>0</code> or <code>1</code>. For example, this
|
||||
* Filters numeric route values such that they are rounded to <code>0</code> or <code>1</code>. For example, this
|
||||
* enables you to use an analog input as if it were a toggle.
|
||||
* @function RouteObject#constrainToPositiveInteger
|
||||
* @returns {RouteObject} The <code>RouteObject</code> with the filter applied.
|
||||
|
@ -364,7 +366,7 @@ class RouteBuilderProxy : public QObject {
|
|||
Q_INVOKABLE QObject* constrainToPositiveInteger();
|
||||
|
||||
/**jsdoc
|
||||
* Filter {@link Pose} route values to have a pre-translation applied.
|
||||
* Filters {@link Pose} route values to have a pre-translation applied.
|
||||
* @function RouteObject#translate
|
||||
* @param {Vec3} translate - The pre-translation to add to the pose.
|
||||
* @returns {RouteObject} The <code>RouteObject</code> with the pre-translation applied.
|
||||
|
@ -373,7 +375,7 @@ class RouteBuilderProxy : public QObject {
|
|||
Q_INVOKABLE QObject* translate(glm::vec3 translate);
|
||||
|
||||
/**jsdoc
|
||||
* Filter {@link Pose} route values to have a pre-transform applied.
|
||||
* Filters {@link Pose} route values to have a pre-transform applied.
|
||||
* @function RouteObject#transform
|
||||
* @param {Mat4} transform - The pre-transform to apply.
|
||||
* @returns {RouteObject} The <code>RouteObject</code> with the pre-transform applied.
|
||||
|
@ -382,7 +384,7 @@ class RouteBuilderProxy : public QObject {
|
|||
Q_INVOKABLE QObject* transform(glm::mat4 transform);
|
||||
|
||||
/**jsdoc
|
||||
* Filter {@link Pose} route values to have a post-transform applied.
|
||||
* Filters {@link Pose} route values to have a post-transform applied.
|
||||
* @function RouteObject#postTransform
|
||||
* @param {Mat4} transform - The post-transform to apply.
|
||||
* @returns {RouteObject} The <code>RouteObject</code> with the post-transform applied.
|
||||
|
@ -391,7 +393,7 @@ class RouteBuilderProxy : public QObject {
|
|||
Q_INVOKABLE QObject* postTransform(glm::mat4 transform);
|
||||
|
||||
/**jsdoc
|
||||
* Filter {@link Pose} route values to have a pre-rotation applied.
|
||||
* Filters {@link Pose} route values to have a pre-rotation applied.
|
||||
* @function RouteObject#rotate
|
||||
* @param {Quat} rotation - The pre-rotation to add to the pose.
|
||||
* @returns {RouteObject} The <code>RouteObject</code> with the pre-rotation applied.
|
||||
|
@ -400,7 +402,7 @@ class RouteBuilderProxy : public QObject {
|
|||
Q_INVOKABLE QObject* rotate(glm::quat rotation);
|
||||
|
||||
/**jsdoc
|
||||
* Filter {@link Pose} route values to be smoothed by a low velocity filter. The filter's rotation and translation
|
||||
* Filters {@link Pose} route values to be smoothed by a low velocity filter. The filter's rotation and translation
|
||||
* values are calculated as: <code>(1 - f) * currentValue + f * previousValue</code> where
|
||||
* <code>f = currentVelocity / filterConstant</code>. At low velocities, the filter value is largely the previous
|
||||
* value; at high velocities the value is wholly the current controller value.
|
||||
|
@ -415,7 +417,7 @@ class RouteBuilderProxy : public QObject {
|
|||
Q_INVOKABLE QObject* lowVelocity(float rotationConstant, float translationConstant);
|
||||
|
||||
/**jsdoc
|
||||
* Filter {@link Pose} route values to be smoothed by an exponential decay filter. The filter's rotation and
|
||||
* Filters {@link Pose} route values to be smoothed by an exponential decay filter. The filter's rotation and
|
||||
* translation values are calculated as: <code>filterConstant * currentValue + (1 - filterConstant) *
|
||||
* previousValue</code>. Values near 1 are less smooth with lower latency; values near 0 are more smooth with higher
|
||||
* latency.
|
||||
|
@ -428,7 +430,7 @@ class RouteBuilderProxy : public QObject {
|
|||
Q_INVOKABLE QObject* exponentialSmoothing(float rotationConstant, float translationConstant);
|
||||
|
||||
/**jsdoc
|
||||
* Filter numeric route values such that a value of <code>0.0</code> is changed to <code>1.0</code>, and other values
|
||||
* Filters numeric route values such that a value of <code>0.0</code> is changed to <code>1.0</code>, and other values
|
||||
* are changed to <code>0.0</code>.
|
||||
* @function RouteObject#logicalNot
|
||||
* @returns {RouteObject} The <code>RouteObject</code> with the filter applied.
|
||||
|
|
|
@ -205,8 +205,11 @@ void EntityTreeRenderer::stopDomainAndNonOwnedEntities() {
|
|||
foreach (const EntityItemID& entityID, entitiesWithEntityScripts) {
|
||||
EntityItemPointer entityItem = getTree()->findEntityByEntityItemID(entityID);
|
||||
|
||||
if (entityItem) {
|
||||
if (entityItem && !entityItem->getScript().isEmpty()) {
|
||||
if (!(entityItem->isLocalEntity() || (entityItem->isAvatarEntity() && entityItem->getOwningAvatarID() == getTree()->getMyAvatarSessionUUID()))) {
|
||||
if (entityItem->contains(_avatarPosition)) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
|
||||
}
|
||||
_entitiesScriptEngine->unloadEntityScript(entityID, true);
|
||||
}
|
||||
}
|
||||
|
@ -534,7 +537,7 @@ void EntityTreeRenderer::handleSpaceUpdate(std::pair<int32_t, glm::vec4> proxyUp
|
|||
_spaceUpdates.emplace_back(proxyUpdate.first, proxyUpdate.second);
|
||||
}
|
||||
|
||||
bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(QVector<EntityItemID>* entitiesContainingAvatar) {
|
||||
bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(QSet<EntityItemID>& entitiesContainingAvatar) {
|
||||
bool didUpdate = false;
|
||||
float radius = 0.01f; // for now, assume 0.01 meter radius, because we actually check the point inside later
|
||||
QVector<QUuid> entityIDs;
|
||||
|
@ -580,9 +583,7 @@ bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(QVector<EntityIt
|
|||
}
|
||||
|
||||
if ((!hasScript && isZone) || scriptHasLoaded) {
|
||||
if (entitiesContainingAvatar) {
|
||||
*entitiesContainingAvatar << entity->getEntityItemID();
|
||||
}
|
||||
entitiesContainingAvatar << entity->getEntityItemID();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -616,36 +617,36 @@ bool EntityTreeRenderer::checkEnterLeaveEntities() {
|
|||
auto movedEnough = glm::distance(avatarPosition, _avatarPosition) > ZONE_CHECK_DISTANCE;
|
||||
auto enoughTimeElapsed = (now - _lastZoneCheck) > ZONE_CHECK_INTERVAL;
|
||||
|
||||
if (movedEnough || enoughTimeElapsed) {
|
||||
if (_forceRecheckEntities || movedEnough || enoughTimeElapsed) {
|
||||
_avatarPosition = avatarPosition;
|
||||
_lastZoneCheck = now;
|
||||
QVector<EntityItemID> entitiesContainingAvatar;
|
||||
didUpdate = findBestZoneAndMaybeContainingEntities(&entitiesContainingAvatar);
|
||||
|
||||
_forceRecheckEntities = false;
|
||||
|
||||
QSet<EntityItemID> entitiesContainingAvatar;
|
||||
didUpdate = findBestZoneAndMaybeContainingEntities(entitiesContainingAvatar);
|
||||
|
||||
// Note: at this point we don't need to worry about the tree being locked, because we only deal with
|
||||
// EntityItemIDs from here. The callEntityScriptMethod() method is robust against attempting to call scripts
|
||||
// for entity IDs that no longer exist.
|
||||
|
||||
// for all of our previous containing entities, if they are no longer containing then send them a leave event
|
||||
foreach(const EntityItemID& entityID, _currentEntitiesInside) {
|
||||
if (!entitiesContainingAvatar.contains(entityID)) {
|
||||
emit leaveEntity(entityID);
|
||||
if (_entitiesScriptEngine) {
|
||||
if (_entitiesScriptEngine) {
|
||||
// for all of our previous containing entities, if they are no longer containing then send them a leave event
|
||||
foreach(const EntityItemID& entityID, _currentEntitiesInside) {
|
||||
if (!entitiesContainingAvatar.contains(entityID)) {
|
||||
emit leaveEntity(entityID);
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// for all of our new containing entities, if they weren't previously containing then send them an enter event
|
||||
foreach(const EntityItemID& entityID, entitiesContainingAvatar) {
|
||||
if (!_currentEntitiesInside.contains(entityID)) {
|
||||
emit enterEntity(entityID);
|
||||
if (_entitiesScriptEngine) {
|
||||
// for all of our new containing entities, if they weren't previously containing then send them an enter event
|
||||
foreach(const EntityItemID& entityID, entitiesContainingAvatar) {
|
||||
if (!_currentEntitiesInside.contains(entityID)) {
|
||||
emit enterEntity(entityID);
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "enterEntity");
|
||||
}
|
||||
}
|
||||
_currentEntitiesInside = entitiesContainingAvatar;
|
||||
}
|
||||
_currentEntitiesInside = entitiesContainingAvatar;
|
||||
}
|
||||
}
|
||||
return didUpdate;
|
||||
|
@ -653,7 +654,7 @@ bool EntityTreeRenderer::checkEnterLeaveEntities() {
|
|||
|
||||
void EntityTreeRenderer::leaveDomainAndNonOwnedEntities() {
|
||||
if (_tree && !_shuttingDown) {
|
||||
QVector<EntityItemID> currentEntitiesInsideToSave;
|
||||
QSet<EntityItemID> currentEntitiesInsideToSave;
|
||||
foreach (const EntityItemID& entityID, _currentEntitiesInside) {
|
||||
EntityItemPointer entityItem = getTree()->findEntityByEntityItemID(entityID);
|
||||
if (!(entityItem->isLocalEntity() || (entityItem->isAvatarEntity() && entityItem->getOwningAvatarID() == getTree()->getMyAvatarSessionUUID()))) {
|
||||
|
@ -662,7 +663,7 @@ void EntityTreeRenderer::leaveDomainAndNonOwnedEntities() {
|
|||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
|
||||
}
|
||||
} else {
|
||||
currentEntitiesInsideToSave.push_back(entityID);
|
||||
currentEntitiesInsideToSave.insert(entityID);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -687,9 +688,7 @@ void EntityTreeRenderer::leaveAllEntities() {
|
|||
}
|
||||
|
||||
void EntityTreeRenderer::forceRecheckEntities() {
|
||||
// make sure our "last avatar position" is something other than our current position,
|
||||
// so that on our next chance, we'll check for enter/leave entity events.
|
||||
_avatarPosition = _viewState->getAvatarPosition() + glm::vec3((float)TREE_SCALE);
|
||||
_forceRecheckEntities = true;
|
||||
}
|
||||
|
||||
bool EntityTreeRenderer::applyLayeredZones() {
|
||||
|
@ -992,7 +991,10 @@ void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (_tree && !_shuttingDown && _entitiesScriptEngine) {
|
||||
if (_tree && !_shuttingDown && _entitiesScriptEngine && !itr->second->getEntity()->getScript().isEmpty()) {
|
||||
if (itr->second->getEntity()->contains(_avatarPosition)) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
|
||||
}
|
||||
_entitiesScriptEngine->unloadEntityScript(entityID, true);
|
||||
}
|
||||
|
||||
|
@ -1038,6 +1040,9 @@ void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID, bool
|
|||
QString scriptUrl = entity->getScript();
|
||||
if ((shouldLoad && unloadFirst) || scriptUrl.isEmpty()) {
|
||||
if (_entitiesScriptEngine) {
|
||||
if (entity->contains(_avatarPosition)) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
|
||||
}
|
||||
_entitiesScriptEngine->unloadEntityScript(entityID);
|
||||
}
|
||||
entity->scriptHasUnloaded();
|
||||
|
|
|
@ -169,7 +169,7 @@ private:
|
|||
|
||||
void resetEntitiesScriptEngine();
|
||||
|
||||
bool findBestZoneAndMaybeContainingEntities(QVector<EntityItemID>* entitiesContainingAvatar = nullptr);
|
||||
bool findBestZoneAndMaybeContainingEntities(QSet<EntityItemID>& entitiesContainingAvatar);
|
||||
|
||||
bool applyLayeredZones();
|
||||
void stopDomainAndNonOwnedEntities();
|
||||
|
@ -186,7 +186,8 @@ private:
|
|||
void forceRecheckEntities();
|
||||
|
||||
glm::vec3 _avatarPosition { 0.0f };
|
||||
QVector<EntityItemID> _currentEntitiesInside;
|
||||
bool _forceRecheckEntities { true };
|
||||
QSet<EntityItemID> _currentEntitiesInside;
|
||||
|
||||
bool _wantScripts;
|
||||
ScriptEnginePointer _entitiesScriptEngine;
|
||||
|
|
|
@ -41,8 +41,14 @@ QVariant readBinaryArray(QDataStream& in, int& position) {
|
|||
quint32 compressedLength;
|
||||
|
||||
in >> arrayLength;
|
||||
if (arrayLength > std::numeric_limits<int>::max() / sizeof(T)) { // Upcoming byte containers are limited to max signed int
|
||||
throw QString("FBX file most likely corrupt: binary data exceeds data limits");
|
||||
}
|
||||
in >> encoding;
|
||||
in >> compressedLength;
|
||||
if (compressedLength > std::numeric_limits<int>::max() / sizeof(T)) { // Upcoming byte containers are limited to max signed int
|
||||
throw QString("FBX file most likely corrupt: compressed binary data exceeds data limits");
|
||||
}
|
||||
position += sizeof(quint32) * 3;
|
||||
|
||||
QVector<T> values;
|
||||
|
|
|
@ -219,9 +219,10 @@ controller::Input KeyboardMouseDevice::InputDevice::makeInput(KeyboardMouseDevic
|
|||
|
||||
/**jsdoc
|
||||
* <p>The <code>Controller.Hardware.Keyboard</code> object has properties representing keyboard, mouse, and display touch
|
||||
* events. The property values are integer IDs, uniquely identifying each output. <em>Read-only.</em> These can be mapped to
|
||||
* actions or functions or <code>Controller.Standard</code> items in a {@link RouteObject} mapping. For presses, each data
|
||||
* value is either <code>1.0</code> for "true" or <code>0.0</code> for "false".</p>
|
||||
* events. The property values are integer IDs, uniquely identifying each output. <em>Read-only.</em></p>
|
||||
* <p>These events can be mapped to actions or functions or <code>Controller.Standard</code> items in a {@link RouteObject}
|
||||
* mapping. For presses, each data value is either <code>1.0</code> for "true" or <code>0.0</code> for "false".</p>
|
||||
*
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Property</th><th>Type</th><td>Data</th><th>Description</th></tr>
|
||||
|
@ -269,14 +270,18 @@ controller::Input KeyboardMouseDevice::InputDevice::makeInput(KeyboardMouseDevic
|
|||
* new x-coordinate value.</td></tr>
|
||||
* <tr><td><code>MouseY</code></td><td>number</td><td>number</td><td>The mouse y-coordinate changed. The data value is its
|
||||
* new y-coordinate value.</td></tr>
|
||||
* <tr><td><code>MouseWheelRight</code></td><td>number</td><td>number</td><td>The mouse wheel rotated left. The data value
|
||||
* <tr><td><code>MouseWheelRight</code></td><td>number</td><td>number</td><td>The mouse wheel rotated right. The data value
|
||||
* is the number of units rotated (typically <code>1.0</code>).</td></tr>
|
||||
* <tr><td><code>MouseWheelLeft</code></td><td>number</td><td>number</td><td>The mouse wheel rotated left. The data value
|
||||
* is the number of units rotated (typically <code>1.0</code>).</td></tr>
|
||||
* <tr><td><code>MouseWheelUp</code></td><td>number</td><td>number</td><td>The mouse wheel rotated up. The data value
|
||||
* is the number of units rotated (typically <code>1.0</code>).</td></tr>
|
||||
* is the number of units rotated (typically <code>1.0</code>).<br />
|
||||
* <strong>Warning:</strong> The mouse wheel in an ordinary mouse generates left/right wheel events instead of
|
||||
* up/down.</td></tr>
|
||||
* <tr><td><code>MouseWheelDown</code></td><td>number</td><td>number</td><td>The mouse wheel rotated down. The data value
|
||||
* is the number of units rotated (typically <code>1.0</code>).</td></tr>
|
||||
* is the number of units rotated (typically <code>1.0</code>).<br />
|
||||
* <strong>Warning:</strong> The mouse wheel in an ordinary mouse generates left/right wheel events instead of
|
||||
* up/down.</td></tr>
|
||||
* <tr><td><code>TouchpadRight</code></td><td>number</td><td>number</td><td>The average touch on a touch-enabled device
|
||||
* moved right. The data value is how far the average position of all touch points moved.</td></tr>
|
||||
* <tr><td><code>TouchpadLeft</code></td><td>number</td><td>number</td><td>The average touch on a touch-enabled device
|
||||
|
@ -288,7 +293,6 @@ controller::Input KeyboardMouseDevice::InputDevice::makeInput(KeyboardMouseDevic
|
|||
* </tbody>
|
||||
* </table>
|
||||
* @typedef {object} Controller.Hardware-Keyboard
|
||||
* @todo <em>Currently, the mouse wheel in an ordinary mouse generates left/right wheel events instead of up/down.</em>
|
||||
*/
|
||||
controller::Input::NamedVector KeyboardMouseDevice::InputDevice::getAvailableInputs() const {
|
||||
using namespace controller;
|
||||
|
|
|
@ -261,6 +261,61 @@ void Midi::MidiCleanup() {
|
|||
}
|
||||
#endif
|
||||
|
||||
/**jsdoc
|
||||
* A MIDI message.
|
||||
* <p><strong>Warning:</strong> The <code>status</code> property is NOT a MIDI status value.</p>
|
||||
* @typedef {object} Midi.MidiMessage
|
||||
* @property {number} device - Device number.
|
||||
* @property {Midi.RawMidiMessage} raw - Raw MIDI message.
|
||||
* @property {number} status - Channel + status. <em>Legacy value.</em>
|
||||
* @property {number} channel - Channel: <code>1</code> – <code>16</code>.
|
||||
* @property {number} type - Status: {@link Midi.MidiStatus}; <code>8</code> – <code>15</code>.
|
||||
* @property {number} note - Note: <code>0</code> – <code>127</code>.
|
||||
* @property {number} velocity - Note velocity: <code>0</code> – <code>127</code>. (<code>0</code> means "note off".)
|
||||
* @property {number} bend - Pitch bend: <code>-8192</code> – <code>8191</code>.
|
||||
* @property {number} program - Program change: <code>0</code> – <code>127</code>.
|
||||
*/
|
||||
/**jsdoc
|
||||
* An integer DWORD (unsigned 32 bit) message with bits having values as follows:
|
||||
* <table>
|
||||
* <tbody>
|
||||
* <tr>
|
||||
* <td width=25%><code>00000000</code></td>
|
||||
* <td width=25%><code>0vvvvvvv</code></td>
|
||||
* <td width=25%><code>0nnnnnnn</code></td>
|
||||
* <td width=12%><code>1sss</code></td>
|
||||
* <td width=12%><code>cccc</code></td>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* <p>Where:</p>
|
||||
* <ul>
|
||||
* <li><code>v</code> = Velocity.
|
||||
* <li><code>n</code> = Note.
|
||||
* <li><code>s</code> = Status - {@link Midi.MidiStatus}
|
||||
* <li><code>c</code> = Channel.
|
||||
* </ul>
|
||||
* <p>The number in the first bit of each byte denotes whether it is a command (1) or data (0).
|
||||
* @typedef {number} Midi.RawMidiMessage
|
||||
*/
|
||||
/**jsdoc
|
||||
* <p>A MIDI status value. The following MIDI status values are supported:</p>
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Value</th><th>Description</th>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td><code>8</code></td><td>Note off.</td></tr>
|
||||
* <tr><td><code>9</code></td><td>Note on.</td></tr>
|
||||
* <tr><td><code>10</code></td><td>Polyphonic key pressure.</td></tr>
|
||||
* <tr><td><code>11</code></td><td>Control change.</td></tr>
|
||||
* <tr><td><code>12</code></td><td>Program change.</td></tr>
|
||||
* <tr><td><code>13</code></td><td>Channel pressure.</td></tr>
|
||||
* <tr><td><code>14</code></td><td>Pitch bend.</td></tr>
|
||||
* <tr><td><code>15</code></td><td>System message.</td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef {number} Midi.MidiStatus
|
||||
*/
|
||||
void Midi::midiReceived(int device, int raw, int channel, int status, int type, int note, int velocity, int bend, int program) {
|
||||
QVariantMap eventData;
|
||||
eventData["device"] = device;
|
||||
|
|
|
@ -21,6 +21,12 @@
|
|||
#include <string>
|
||||
|
||||
/**jsdoc
|
||||
* The <code>Midi</code> API provides the ability to connect Interface with musical instruments and other external or virtual
|
||||
* devices via the MIDI protocol. For further information and examples, see the tutorial:
|
||||
* <a href="https://docs.highfidelity.com/en/rc81/script/midi-tutorial.html">Use MIDI to Control Your Environment</a>.</p>
|
||||
*
|
||||
* <p><strong>Note:</strong> Only works on Windows.</p>
|
||||
*
|
||||
* @namespace Midi
|
||||
*
|
||||
* @hifi-interface
|
||||
|
@ -49,88 +55,112 @@ private:
|
|||
void MidiCleanup();
|
||||
|
||||
signals:
|
||||
void midiNote(QVariantMap eventData);
|
||||
void midiMessage(QVariantMap eventData);
|
||||
void midiReset();
|
||||
|
||||
public slots:
|
||||
|
||||
/**jsdoc
|
||||
* Send Raw MIDI packet to a particular device.
|
||||
* Triggered when a connected device sends an output.
|
||||
* @function Midi.midiNote
|
||||
* @param {Midi.MidiMessage} message - The MIDI message.
|
||||
* @returns {Signal}
|
||||
* @deprecated This signal is deprecated and will be removed. Use {@link Midi.midiMessage|midiMessage} instead.
|
||||
*/
|
||||
void midiNote(QVariantMap eventData);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when a connected device sends an output.
|
||||
* @function Midi.midiMessage
|
||||
* @param {Midi.MidiMessage} message - The MIDI message.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void midiMessage(QVariantMap eventData);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when the system detects there was a reset such as when a device is plugged in or unplugged.
|
||||
* @function Midi.midiReset
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void midiReset();
|
||||
|
||||
public slots:
|
||||
|
||||
/**jsdoc
|
||||
* Sends a raw MIDI packet to a particular device.
|
||||
* @function Midi.sendRawDword
|
||||
* @param {number} device - Integer device number.
|
||||
* @param {number} raw - Integer (DWORD) raw MIDI message.
|
||||
* @param {Midi.RawMidiMessage} raw - Raw MIDI message.
|
||||
*/
|
||||
Q_INVOKABLE void sendRawDword(int device, int raw);
|
||||
|
||||
/**jsdoc
|
||||
* Send MIDI message to a particular device.
|
||||
* Sends a MIDI message to a particular device.
|
||||
* @function Midi.sendMidiMessage
|
||||
* @param {number} device - Integer device number.
|
||||
* @param {number} channel - Integer channel number.
|
||||
* @param {number} type - 0x8 is note off, 0x9 is note on (if velocity=0, note off), etc.
|
||||
* @param {number} note - MIDI note number.
|
||||
* @param {number} velocity - Note velocity (0 means note off).
|
||||
* @param {Midi.MidiStatus} type - Integer status value.
|
||||
* @param {number} note - Note number.
|
||||
* @param {number} velocity - Note velocity. (<code>0</code> means "note off".)
|
||||
* @comment The "type" parameter has that name to match up with {@link Midi.MidiMessage}.
|
||||
*/
|
||||
Q_INVOKABLE void sendMidiMessage(int device, int channel, int type, int note, int velocity);
|
||||
|
||||
/**jsdoc
|
||||
* Play a note on all connected devices.
|
||||
* Plays a note on all connected devices.
|
||||
* @function Midi.playMidiNote
|
||||
* @param {number} status - 0x80 is note off, 0x90 is note on (if velocity=0, note off), etc.
|
||||
* @param {number} note - MIDI note number.
|
||||
* @param {number} velocity - Note velocity (0 means note off).
|
||||
* @param {MidiStatus} status - Note status.
|
||||
* @param {number} note - Note number.
|
||||
* @param {number} velocity - Note velocity. (<code>0</code> means "note off".)
|
||||
*/
|
||||
Q_INVOKABLE void playMidiNote(int status, int note, int velocity);
|
||||
|
||||
/**jsdoc
|
||||
* Turn off all notes on all connected devices.
|
||||
* Turns off all notes on all connected MIDI devices.
|
||||
* @function Midi.allNotesOff
|
||||
*/
|
||||
Q_INVOKABLE void allNotesOff();
|
||||
|
||||
/**jsdoc
|
||||
* Clean up and re-discover attached devices.
|
||||
* Cleans up and rediscovers attached MIDI devices.
|
||||
* @function Midi.resetDevices
|
||||
*/
|
||||
Q_INVOKABLE void resetDevices();
|
||||
|
||||
/**jsdoc
|
||||
* Get a list of inputs/outputs.
|
||||
* Gets a list of MIDI input or output devices.
|
||||
* @function Midi.listMidiDevices
|
||||
* @param {boolean} output
|
||||
* @param {boolean} output - <code>true</code> to list output devices, <code>false</code> to list input devices.
|
||||
* @returns {string[]}
|
||||
*/
|
||||
Q_INVOKABLE QStringList listMidiDevices(bool output);
|
||||
|
||||
/**jsdoc
|
||||
* Block an input/output by name.
|
||||
* Blocks a MIDI device's input or output.
|
||||
* @function Midi.blockMidiDevice
|
||||
* @param {string} name
|
||||
* @param {boolean} output
|
||||
* @param {string} name - The name of the MIDI device to block.
|
||||
* @param {boolean} output - <code>true</code> to block the device's output, <code>false</code> to block its input.
|
||||
*/
|
||||
Q_INVOKABLE void blockMidiDevice(QString name, bool output);
|
||||
|
||||
/**jsdoc
|
||||
* Unblock an input/output by name.
|
||||
* Unblocks a MIDI device's input or output.
|
||||
* @function Midi.unblockMidiDevice
|
||||
* @param {string} name
|
||||
* @param {boolean} output
|
||||
* @param {string} name- The name of the MIDI device to unblock.
|
||||
* @param {boolean} output - <code>true</code> to unblock the device's output, <code>false</code> to unblock its input.
|
||||
*/
|
||||
Q_INVOKABLE void unblockMidiDevice(QString name, bool output);
|
||||
|
||||
/**jsdoc
|
||||
* Repeat all incoming notes to all outputs (default disabled).
|
||||
* Enables or disables repeating all incoming notes to all outputs. (Default is disabled.)
|
||||
* @function Midi.thruModeEnable
|
||||
* @param {boolean} enable
|
||||
* @param {boolean} enable - <code>true</code> to enable repeating all incoming notes to all output, <code>false</code> to
|
||||
* disable.
|
||||
*/
|
||||
Q_INVOKABLE void thruModeEnable(bool enable);
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* Broadcast on all unblocked devices.
|
||||
* Enables or disables broadcasts to all unblocked devices.
|
||||
* @function Midi.broadcastEnable
|
||||
* @param {boolean} enable
|
||||
* @param {boolean} enable - <code>true</code> to have "send" functions broadcast to all devices, <code>false</code> to
|
||||
* have them send to specific output devices.
|
||||
*/
|
||||
Q_INVOKABLE void broadcastEnable(bool enable);
|
||||
|
||||
|
@ -138,50 +168,58 @@ signals:
|
|||
/// filter by event types
|
||||
|
||||
/**jsdoc
|
||||
* Enables or disables note off events.
|
||||
* @function Midi.typeNoteOffEnable
|
||||
* @param {boolean} enable
|
||||
* @param {boolean} enable - <code>true</code> to enable, <code>false</code> to disable.
|
||||
*/
|
||||
Q_INVOKABLE void typeNoteOffEnable(bool enable);
|
||||
|
||||
/**jsdoc
|
||||
* Enables or disables note on events.
|
||||
* @function Midi.typeNoteOnEnable
|
||||
* @param {boolean} enable
|
||||
* @param {boolean} enable - <code>true</code> to enable, <code>false</code> to disable.
|
||||
*/
|
||||
Q_INVOKABLE void typeNoteOnEnable(bool enable);
|
||||
|
||||
/**jsdoc
|
||||
* Enables or disables poly key pressure events.
|
||||
* @function Midi.typePolyKeyPressureEnable
|
||||
* @param {boolean} enable
|
||||
* @param {boolean} enable - <code>true</code> to enable, <code>false</code> to disable.
|
||||
*/
|
||||
Q_INVOKABLE void typePolyKeyPressureEnable(bool enable);
|
||||
|
||||
/**jsdoc
|
||||
* Enables or disables control change events.
|
||||
* @function Midi.typeControlChangeEnable
|
||||
* @param {boolean} enable
|
||||
* @param {boolean} enable - <code>true</code> to enable, <code>false</code> to disable.
|
||||
*/
|
||||
Q_INVOKABLE void typeControlChangeEnable(bool enable);
|
||||
|
||||
/**jsdoc
|
||||
* Enables or disables program change events.
|
||||
* @function Midi.typeProgramChangeEnable
|
||||
* @param {boolean} enable
|
||||
* @param {boolean} enable - <code>true</code> to enable, <code>false</code> to disable.
|
||||
*/
|
||||
Q_INVOKABLE void typeProgramChangeEnable(bool enable);
|
||||
|
||||
/**jsdoc
|
||||
* Enables or disables channel pressure events.
|
||||
* @function Midi.typeChanPressureEnable
|
||||
* @param {boolean} enable
|
||||
* @param {boolean} enable - <code>true</code> to enable, <code>false</code> to disable.
|
||||
*/
|
||||
Q_INVOKABLE void typeChanPressureEnable(bool enable);
|
||||
|
||||
/**jsdoc
|
||||
* Enables or disables pitch bend events.
|
||||
* @function Midi.typePitchBendEnable
|
||||
* @param {boolean} enable
|
||||
* @param {boolean} enable - <code>true</code> to enable, <code>false</code> to disable.
|
||||
*/
|
||||
Q_INVOKABLE void typePitchBendEnable(bool enable);
|
||||
|
||||
/**jsdoc
|
||||
* Enables or disables system message events.
|
||||
* @function Midi.typeSystemMessageEnable
|
||||
* @param {boolean} enable
|
||||
* @param {boolean} enable - <code>true</code> to enable, <code>false</code> to disable.
|
||||
*/
|
||||
Q_INVOKABLE void typeSystemMessageEnable(bool enable);
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ void CongestionControl::setMaxBandwidth(int maxBandwidth) {
|
|||
void CongestionControl::setPacketSendPeriod(double newSendPeriod) {
|
||||
Q_ASSERT_X(newSendPeriod >= 0, "CongestionControl::setPacketPeriod", "Can not set a negative packet send period");
|
||||
|
||||
auto packetsPerSecond = (double)_maxBandwidth / (BITS_PER_BYTE * _mss);
|
||||
auto packetsPerSecond = _mss > 0 ? (double)_maxBandwidth / (BITS_PER_BYTE * _mss) : -1.0;
|
||||
if (packetsPerSecond > 0.0) {
|
||||
// anytime the packet send period is about to be increased, make sure it stays below the minimum period,
|
||||
// calculated based on the maximum desired bandwidth
|
||||
|
|
|
@ -38,10 +38,10 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
return static_cast<PacketVersion>(EntityQueryPacketVersion::ConicalFrustums);
|
||||
case PacketType::AvatarIdentity:
|
||||
case PacketType::AvatarData:
|
||||
return static_cast<PacketVersion>(AvatarMixerPacketVersion::HandControllerSection);
|
||||
return static_cast<PacketVersion>(AvatarMixerPacketVersion::SendVerificationFailed);
|
||||
case PacketType::BulkAvatarData:
|
||||
case PacketType::KillAvatar:
|
||||
return static_cast<PacketVersion>(AvatarMixerPacketVersion::HandControllerSection);
|
||||
return static_cast<PacketVersion>(AvatarMixerPacketVersion::SendVerificationFailed);
|
||||
case PacketType::MessagesData:
|
||||
return static_cast<PacketVersion>(MessageDataVersion::TextOrBinaryData);
|
||||
// ICE packets
|
||||
|
|
|
@ -334,6 +334,7 @@ enum class AvatarMixerPacketVersion : PacketVersion {
|
|||
SendMaxTranslationDimension,
|
||||
FBXJointOrderChange,
|
||||
HandControllerSection,
|
||||
SendVerificationFailed
|
||||
};
|
||||
|
||||
enum class DomainConnectRequestVersion : PacketVersion {
|
||||
|
|
|
@ -237,9 +237,11 @@ void CauterizedModel::updateRenderItems() {
|
|||
if (useDualQuaternionSkinning) {
|
||||
data.updateClusterBuffer(meshState.clusterDualQuaternions,
|
||||
cauterizedMeshState.clusterDualQuaternions);
|
||||
data.computeAdjustedLocalBound(meshState.clusterDualQuaternions);
|
||||
} else {
|
||||
data.updateClusterBuffer(meshState.clusterMatrices,
|
||||
cauterizedMeshState.clusterMatrices);
|
||||
data.computeAdjustedLocalBound(meshState.clusterMatrices);
|
||||
}
|
||||
|
||||
Transform renderTransform = modelTransform;
|
||||
|
|
|
@ -450,9 +450,9 @@ void ModelMeshPartPayload::render(RenderArgs* args) {
|
|||
void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector<glm::mat4>& clusterMatrices) {
|
||||
_adjustedLocalBound = _localBound;
|
||||
if (clusterMatrices.size() > 0) {
|
||||
_adjustedLocalBound.transform(clusterMatrices[0]);
|
||||
_adjustedLocalBound.transform(clusterMatrices.back());
|
||||
|
||||
for (int i = 1; i < (int)clusterMatrices.size(); ++i) {
|
||||
for (int i = 0; i < (int)clusterMatrices.size() - 1; ++i) {
|
||||
AABox clusterBound = _localBound;
|
||||
clusterBound.transform(clusterMatrices[i]);
|
||||
_adjustedLocalBound += clusterBound;
|
||||
|
@ -463,12 +463,12 @@ void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector<glm::mat4
|
|||
void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector<Model::TransformDualQuaternion>& clusterDualQuaternions) {
|
||||
_adjustedLocalBound = _localBound;
|
||||
if (clusterDualQuaternions.size() > 0) {
|
||||
Transform rootTransform(clusterDualQuaternions[0].getRotation(),
|
||||
clusterDualQuaternions[0].getScale(),
|
||||
clusterDualQuaternions[0].getTranslation());
|
||||
Transform rootTransform(clusterDualQuaternions.back().getRotation(),
|
||||
clusterDualQuaternions.back().getScale(),
|
||||
clusterDualQuaternions.back().getTranslation());
|
||||
_adjustedLocalBound.transform(rootTransform);
|
||||
|
||||
for (int i = 1; i < (int)clusterDualQuaternions.size(); ++i) {
|
||||
for (int i = 0; i < (int)clusterDualQuaternions.size() - 1; ++i) {
|
||||
AABox clusterBound = _localBound;
|
||||
Transform transform(clusterDualQuaternions[i].getRotation(),
|
||||
clusterDualQuaternions[i].getScale(),
|
||||
|
|
|
@ -241,8 +241,10 @@ void Model::updateRenderItems() {
|
|||
invalidatePayloadShapeKey, primitiveMode, renderItemKeyGlobalFlags, cauterized](ModelMeshPartPayload& data) {
|
||||
if (useDualQuaternionSkinning) {
|
||||
data.updateClusterBuffer(meshState.clusterDualQuaternions);
|
||||
data.computeAdjustedLocalBound(meshState.clusterDualQuaternions);
|
||||
} else {
|
||||
data.updateClusterBuffer(meshState.clusterMatrices);
|
||||
data.computeAdjustedLocalBound(meshState.clusterMatrices);
|
||||
}
|
||||
|
||||
Transform renderTransform = modelTransform;
|
||||
|
@ -774,11 +776,14 @@ scriptable::ScriptableModelBase Model::getScriptableModel() {
|
|||
auto& materialName = _modelMeshMaterialNames[shapeID];
|
||||
result.appendMaterial(graphics::MaterialLayer(getGeometry()->getShapeMaterial(shapeID), 0), shapeID, materialName);
|
||||
|
||||
auto mappedMaterialIter = _materialMapping.find(shapeID);
|
||||
if (mappedMaterialIter != _materialMapping.end()) {
|
||||
auto mappedMaterials = mappedMaterialIter->second;
|
||||
for (auto& mappedMaterial : mappedMaterials) {
|
||||
result.appendMaterial(mappedMaterial, shapeID, materialName);
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_materialMappingMutex);
|
||||
auto mappedMaterialIter = _materialMapping.find(shapeID);
|
||||
if (mappedMaterialIter != _materialMapping.end()) {
|
||||
auto mappedMaterials = mappedMaterialIter->second;
|
||||
for (auto& mappedMaterial : mappedMaterials) {
|
||||
result.appendMaterial(mappedMaterial, shapeID, materialName);
|
||||
}
|
||||
}
|
||||
}
|
||||
shapeID++;
|
||||
|
@ -1367,8 +1372,6 @@ void Model::simulate(float deltaTime, bool fullUpdate) {
|
|||
// update the world space transforms for all joints
|
||||
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset);
|
||||
updateRig(deltaTime, parentTransform);
|
||||
|
||||
computeMeshPartLocalBounds();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1379,17 +1382,6 @@ void Model::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
|||
_rig.updateAnimations(deltaTime, parentTransform, rigToWorldTransform);
|
||||
}
|
||||
|
||||
void Model::computeMeshPartLocalBounds() {
|
||||
for (auto& part : _modelMeshRenderItems) {
|
||||
const Model::MeshState& state = _meshStates.at(part->_meshIndex);
|
||||
if (_useDualQuaternionSkinning) {
|
||||
part->computeAdjustedLocalBound(state.clusterDualQuaternions);
|
||||
} else {
|
||||
part->computeAdjustedLocalBound(state.clusterMatrices);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// virtual
|
||||
void Model::updateClusterMatrices() {
|
||||
DETAILED_PERFORMANCE_TIMER("Model::updateClusterMatrices");
|
||||
|
@ -1569,6 +1561,13 @@ void Model::applyMaterialMapping() {
|
|||
auto renderItemsKey = _renderItemKeyGlobalFlags;
|
||||
PrimitiveMode primitiveMode = getPrimitiveMode();
|
||||
bool useDualQuaternionSkinning = _useDualQuaternionSkinning;
|
||||
auto modelMeshRenderItemIDs = _modelMeshRenderItemIDs;
|
||||
auto modelMeshRenderItemShapes = _modelMeshRenderItemShapes;
|
||||
std::unordered_map<int, bool> shouldInvalidatePayloadShapeKeyMap;
|
||||
|
||||
for (auto& shape : _modelMeshRenderItemShapes) {
|
||||
shouldInvalidatePayloadShapeKeyMap[shape.meshIndex] = shouldInvalidatePayloadShapeKey(shape.meshIndex);
|
||||
}
|
||||
|
||||
auto& materialMapping = getMaterialMapping();
|
||||
for (auto& mapping : materialMapping) {
|
||||
|
@ -1588,7 +1587,8 @@ void Model::applyMaterialMapping() {
|
|||
priorityMapPerResource[shapeID] = ++_priorityMap[shapeID];
|
||||
}
|
||||
|
||||
auto materialLoaded = [this, networkMaterialResource, shapeIDs, priorityMapPerResource, renderItemsKey, primitiveMode, useDualQuaternionSkinning]() {
|
||||
auto materialLoaded = [this, networkMaterialResource, shapeIDs, priorityMapPerResource, renderItemsKey, primitiveMode, useDualQuaternionSkinning,
|
||||
modelMeshRenderItemIDs, modelMeshRenderItemShapes, shouldInvalidatePayloadShapeKeyMap]() {
|
||||
if (networkMaterialResource->isFailed() || networkMaterialResource->parsedMaterials.names.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -1611,12 +1611,15 @@ void Model::applyMaterialMapping() {
|
|||
}
|
||||
}
|
||||
for (auto shapeID : shapeIDs) {
|
||||
if (shapeID < _modelMeshRenderItemIDs.size()) {
|
||||
auto itemID = _modelMeshRenderItemIDs[shapeID];
|
||||
auto meshIndex = _modelMeshRenderItemShapes[shapeID].meshIndex;
|
||||
bool invalidatePayloadShapeKey = shouldInvalidatePayloadShapeKey(meshIndex);
|
||||
if (shapeID < modelMeshRenderItemIDs.size()) {
|
||||
auto itemID = modelMeshRenderItemIDs[shapeID];
|
||||
auto meshIndex = modelMeshRenderItemShapes[shapeID].meshIndex;
|
||||
bool invalidatePayloadShapeKey = shouldInvalidatePayloadShapeKeyMap.at(meshIndex);
|
||||
graphics::MaterialLayer material = graphics::MaterialLayer(networkMaterial, priorityMapPerResource.at(shapeID));
|
||||
_materialMapping[shapeID].push_back(material);
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_materialMappingMutex);
|
||||
_materialMapping[shapeID].push_back(material);
|
||||
}
|
||||
transaction.updateItem<ModelMeshPartPayload>(itemID, [material, renderItemsKey,
|
||||
invalidatePayloadShapeKey, primitiveMode, useDualQuaternionSkinning](ModelMeshPartPayload& data) {
|
||||
data.addMaterial(material);
|
||||
|
|
|
@ -379,6 +379,7 @@ protected:
|
|||
|
||||
std::unordered_map<unsigned int, quint16> _priorityMap; // only used for materialMapping
|
||||
std::unordered_map<unsigned int, std::vector<graphics::MaterialLayer>> _materialMapping; // generated during applyMaterialMapping
|
||||
std::mutex _materialMappingMutex;
|
||||
void applyMaterialMapping();
|
||||
|
||||
void setBlendshapeCoefficients(const QVector<float>& coefficients) { _blendshapeCoefficients = coefficients; }
|
||||
|
@ -427,7 +428,6 @@ protected:
|
|||
void setScaleInternal(const glm::vec3& scale);
|
||||
void snapToRegistrationPoint();
|
||||
|
||||
void computeMeshPartLocalBounds();
|
||||
virtual void updateRig(float deltaTime, glm::mat4 parentTransform);
|
||||
|
||||
/// Allow sub classes to force invalidating the bboxes
|
||||
|
|
|
@ -392,6 +392,10 @@ void Scene::updateItems(const Transaction::Updates& transactions) {
|
|||
void Scene::transitionItems(const Transaction::TransitionAdds& transactions) {
|
||||
auto transitionStage = getStage<TransitionStage>(TransitionStage::getName());
|
||||
|
||||
if (!transitionStage) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& add : transactions) {
|
||||
auto itemId = std::get<0>(add);
|
||||
// Access the true item
|
||||
|
@ -433,6 +437,10 @@ void Scene::reApplyTransitions(const Transaction::TransitionReApplies& transacti
|
|||
void Scene::queryTransitionItems(const Transaction::TransitionQueries& transactions) {
|
||||
auto transitionStage = getStage<TransitionStage>(TransitionStage::getName());
|
||||
|
||||
if (!transitionStage) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& query : transactions) {
|
||||
auto itemId = std::get<0>(query);
|
||||
// Access the true item
|
||||
|
@ -553,11 +561,14 @@ void Scene::setItemTransition(ItemID itemId, Index transitionId) {
|
|||
}
|
||||
|
||||
void Scene::resetItemTransition(ItemID itemId) {
|
||||
auto transitionStage = getStage<TransitionStage>(TransitionStage::getName());
|
||||
if (!transitionStage) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& item = _items[itemId];
|
||||
TransitionStage::Index transitionId = item.getTransitionId();
|
||||
if (!render::TransitionStage::isIndexInvalid(transitionId)) {
|
||||
auto transitionStage = getStage<TransitionStage>(TransitionStage::getName());
|
||||
|
||||
auto finishedOperators = _transitionFinishedOperatorMap[transitionId];
|
||||
for (auto finishedOperator : finishedOperators) {
|
||||
if (finishedOperator) {
|
||||
|
|
|
@ -16,6 +16,13 @@
|
|||
#include <QString>
|
||||
#include <QUrl>
|
||||
|
||||
/**jsdoc
|
||||
* Information about a resource request.
|
||||
* @typedef {object} ResourceRequestObserver.ResourceRequest
|
||||
* @property {string} url - The URL of the resource request.
|
||||
* @property {number} callerId - An ID identifying the request.
|
||||
* @property {string} extra - Extra information about the request.
|
||||
*/
|
||||
void ResourceRequestObserver::update(const QUrl& requestUrl,
|
||||
const qint64 callerId,
|
||||
const QString& extra) {
|
||||
|
|
|
@ -16,7 +16,15 @@
|
|||
|
||||
#include "DependencyManager.h"
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* The <code>ResourceRequestObserver</code> API provides notifications when an observable resource request is made.
|
||||
*
|
||||
* @namespace ResourceRequestObserver
|
||||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
*/
|
||||
class ResourceRequestObserver : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
@ -25,5 +33,29 @@ public:
|
|||
void update(const QUrl& requestUrl, const qint64 callerId = -1, const QString& extra = "");
|
||||
|
||||
signals:
|
||||
/**jsdoc
|
||||
* Triggered when an observable resource request is made.
|
||||
* @function ResourceRequestObserver.resourceRequestEvent
|
||||
* @param {ResourceRequestObserver.ResourceRequest} request - Information about the resource request.
|
||||
* @returns {Signal}
|
||||
* @example <caption>Report when a particular Clipboard.importEntities() resource request is made.</caption>
|
||||
* ResourceRequestObserver.resourceRequestEvent.connect(function (request) {
|
||||
* if (request.callerId === 100) {
|
||||
* print("Resource request: " + JSON.stringify(request));
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* function importEntities() {
|
||||
* var filename = Window.browse("Import entities to clipboard", "", "*.json");
|
||||
* if (filename) {
|
||||
* Clipboard.importEntities(filename, true, 100);
|
||||
* pastedEntities = Clipboard.pasteEntities(Vec3.sum(MyAvatar.position,
|
||||
* Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })));
|
||||
* print("Entities pasted: " + JSON.stringify(pastedEntities));
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* Script.setTimeout(importEntities, 2000);
|
||||
*/
|
||||
void resourceRequestEvent(QVariantMap result);
|
||||
};
|
||||
|
|
|
@ -252,7 +252,9 @@ private slots:
|
|||
_finished = true;
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
emit response(_result);
|
||||
offscreenUi->removeModalDialog(qobject_cast<QObject*>(this));
|
||||
if (!offscreenUi.isNull()) {
|
||||
offscreenUi->removeModalDialog(qobject_cast<QObject*>(this));
|
||||
}
|
||||
disconnect(_dialog);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -409,9 +409,10 @@ void OculusControllerManager::TouchDevice::stopHapticPulse(bool leftHand) {
|
|||
}
|
||||
|
||||
/**jsdoc
|
||||
* <p>The <code>Controller.Hardware.OculusTouch</code> object has properties representing Oculus Rift. The property values are
|
||||
* integer IDs, uniquely identifying each output. <em>Read-only.</em> These can be mapped to actions or functions or
|
||||
* <code>Controller.Standard</code> items in a {@link RouteObject} mapping.</p>
|
||||
* <p>The <code>Controller.Hardware.OculusTouch</code> object has properties representing the Oculus Rift. The property values
|
||||
* are integer IDs, uniquely identifying each output. <em>Read-only.</em></p>
|
||||
* <p>These outputs can be mapped to actions or functions or <code>Controller.Standard</code> items in a {@link RouteObject}
|
||||
* mapping.</p>
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Property</th><th>Type</th><th>Data</th><th>Description</th></tr>
|
||||
|
|
|
@ -1299,14 +1299,20 @@ void ViveControllerManager::InputDevice::setConfigFromString(const QString& valu
|
|||
}
|
||||
|
||||
/**jsdoc
|
||||
* <p>The <code>Controller.Hardware.Vive</code> object has properties representing Vive. The property values are integer IDs,
|
||||
* uniquely identifying each output. <em>Read-only.</em> These can be mapped to actions or functions or
|
||||
* <code>Controller.Standard</code> items in a {@link RouteObject} mapping.</p>
|
||||
* <p>The <code>Controller.Hardware.Vive</code> object has properties representing the Vive. The property values are integer
|
||||
* IDs, uniquely identifying each output. <em>Read-only.</em></p>
|
||||
* <p>These outputs can be mapped to actions or functions or <code>Controller.Standard</code> items in a {@link RouteObject}
|
||||
* mapping.</p>
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Property</th><th>Type</th><th>Data</th><th>Description</th></tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td colspan="4"><strong>Buttons</strong></td></tr>
|
||||
* <tr><td><code>LeftApplicationMenu</code></td><td>number</td><td>number</td><td>Left application menu button pressed.
|
||||
* </td></tr>
|
||||
* <tr><td><code>RightApplicationMenu</code></td><td>number</td><td>number</td><td>Right application menu button pressed.
|
||||
* </td></tr>
|
||||
* <tr><td colspan="4"><strong>Touch Pad (Sticks)</strong></td></tr>
|
||||
* <tr><td><code>LX</code></td><td>number</td><td>number</td><td>Left touch pad x-axis scale.</td></tr>
|
||||
* <tr><td><code>LY</code></td><td>number</td><td>number</td><td>Left touch pad y-axis scale.</td></tr>
|
||||
|
|
|
@ -94,7 +94,7 @@ def parse_args():
|
|||
parser.add_argument('--vcpkg-root', type=str, help='The location of the vcpkg distribution')
|
||||
parser.add_argument('--build-root', required=True, type=str, help='The location of the cmake build')
|
||||
parser.add_argument('--ports-path', type=str, default=defaultPortsPath)
|
||||
parser.add_argument('--ci-build', action='store_true')
|
||||
parser.add_argument('--ci-build', action='store_true', default=os.getenv('CI_BUILD') is not None)
|
||||
if True:
|
||||
args = parser.parse_args()
|
||||
else:
|
||||
|
|
|
@ -203,7 +203,7 @@ function setAwayProperties() {
|
|||
if (!wasMuted) {
|
||||
Audio.muted = !Audio.muted;
|
||||
}
|
||||
MyAvatar.setEnableMeshVisible(false); // just for our own display, without changing point of view
|
||||
MyAvatar.setEnableMeshVisible(false); // just for our own display, without changing point of view
|
||||
playAwayAnimation(); // animation is still seen by others
|
||||
showOverlay();
|
||||
|
||||
|
@ -223,8 +223,8 @@ function setAwayProperties() {
|
|||
|
||||
function setActiveProperties() {
|
||||
isAway = false;
|
||||
if (!wasMuted) {
|
||||
Audio.muted = !Audio.muted;
|
||||
if (Audio.muted && !wasMuted) {
|
||||
Audio.muted = false;
|
||||
}
|
||||
MyAvatar.setEnableMeshVisible(true); // IWBNI we respected Developer->Avatar->Draw Mesh setting.
|
||||
stopAwayAnimation();
|
||||
|
@ -254,7 +254,7 @@ function setActiveProperties() {
|
|||
}
|
||||
|
||||
function maybeGoActive(event) {
|
||||
if (event.isAutoRepeat) { // isAutoRepeat is true when held down (or when Windows feels like it)
|
||||
if (event.isAutoRepeat) { // isAutoRepeat is true when held down (or when Windows feels like it)
|
||||
return;
|
||||
}
|
||||
if (!isAway && (event.text === 'ESC')) {
|
||||
|
@ -314,6 +314,13 @@ function setEnabled(value) {
|
|||
isEnabled = value;
|
||||
}
|
||||
|
||||
function checkAudioToggled() {
|
||||
if (isAway && !Audio.muted) {
|
||||
goActive();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var CHANNEL_AWAY_ENABLE = "Hifi-Away-Enable";
|
||||
var handleMessage = function(channel, message, sender) {
|
||||
if (channel === CHANNEL_AWAY_ENABLE && sender === MyAvatar.sessionUUID) {
|
||||
|
@ -324,9 +331,10 @@ var handleMessage = function(channel, message, sender) {
|
|||
Messages.subscribe(CHANNEL_AWAY_ENABLE);
|
||||
Messages.messageReceived.connect(handleMessage);
|
||||
|
||||
var maybeIntervalTimer = Script.setInterval(function(){
|
||||
var maybeIntervalTimer = Script.setInterval(function() {
|
||||
maybeMoveOverlay();
|
||||
maybeGoAway();
|
||||
checkAudioToggled();
|
||||
}, BASIC_TIMER_INTERVAL);
|
||||
|
||||
|
||||
|
|
7
scripts/system/clickToAvatarApp.js
Normal file
7
scripts/system/clickToAvatarApp.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
(function () {
|
||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
this.clickDownOnEntity = function (entityID, mouseEvent) {
|
||||
tablet.loadQMLSource("hifi/AvatarApp.qml");
|
||||
};
|
||||
}
|
||||
);
|
Loading…
Reference in a new issue