mirror of
https://github.com/lubosz/overte.git
synced 2025-04-26 19:35:48 +02:00
Merge remote-tracking branch 'upstream/master' into android_goto_splash
This commit is contained in:
commit
001cd5e937
140 changed files with 3734 additions and 815 deletions
.gitignore
assignment-client/src
domain-server/src
interface/src
libraries
audio-client/src
audio/src
controllers/src/controllers
entities-renderer/src
entities/src
gpu-gl-common/src/gpu/gl
gpu-gl/src/gpu
gpu-gles/src/gpu/gles
graphics/src/graphics
input-plugins/src/input-plugins
networking/src
AddressManager.hDomainHandler.hHMACAuth.cppHMACAuth.hLimitedNodeList.cppLimitedNodeList.hNLPacket.cppNLPacket.hNLPacketList.hNetworkPeer.cppNetworkPeer.hNode.cppNode.hNodeList.cppNodeList.hPacketReceiver.cppReceivedMessage.cppReceivedMessage.hSequenceNumberStats.cppSequenceNumberStats.h
udt
octree/src
render-utils/src
LightingModel.slhdirectional_ambient_light.slfdirectional_ambient_light_shadow.slfdirectional_skybox_light.slfdirectional_skybox_light_shadow.slfforward_model.slfforward_model_translucent.slfforward_model_unlit.slfforward_simple.slfforward_simple_textured.slfforward_simple_textured_transparent.slfforward_simple_transparent.slflocal_lights_drawOutline.slflocal_lights_shading.slfmodel.slfmodel.slvmodel_fade.slfmodel_lightmap.slfmodel_lightmap.slvmodel_lightmap_fade.slfmodel_lightmap_fade.slvmodel_lightmap_normal_map.slfmodel_lightmap_normal_map.slvmodel_lightmap_normal_map_fade.slfmodel_lightmap_normal_map_fade.slvmodel_shadow_fade.slfmodel_shadow_fade.slvmodel_unlit.slfoverlay3D.slfoverlay3D.slvoverlay3D_model.slfoverlay3D_model_translucent.slfoverlay3D_model_translucent_unlit.slfoverlay3D_model_unlit.slfoverlay3D_translucent.slf
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -91,3 +91,6 @@ interface/compiledResources
|
|||
|
||||
# GPUCache
|
||||
interface/resources/GPUCache/*
|
||||
|
||||
# package lock file for JSDoc tool
|
||||
tools/jsdoc/package-lock.json
|
||||
|
|
|
@ -380,7 +380,7 @@ void Agent::executeScript() {
|
|||
|
||||
using namespace recording;
|
||||
static const FrameType AVATAR_FRAME_TYPE = Frame::registerFrameType(AvatarData::FRAME_NAME);
|
||||
Frame::registerFrameHandler(AVATAR_FRAME_TYPE, [this, scriptedAvatar](Frame::ConstPointer frame) {
|
||||
Frame::registerFrameHandler(AVATAR_FRAME_TYPE, [scriptedAvatar](Frame::ConstPointer frame) {
|
||||
|
||||
auto recordingInterface = DependencyManager::get<RecordingScriptingInterface>();
|
||||
bool useFrameSkeleton = recordingInterface->getPlayerUseSkeletonModel();
|
||||
|
|
|
@ -901,7 +901,7 @@ void AssetServer::handleAssetUpload(QSharedPointer<ReceivedMessage> message, Sha
|
|||
|
||||
|
||||
if (canWriteToAssetServer) {
|
||||
qCDebug(asset_server) << "Starting an UploadAssetTask for upload from" << uuidStringWithoutCurlyBraces(message->getSourceID());
|
||||
qCDebug(asset_server) << "Starting an UploadAssetTask for upload from" << message->getSourceID();
|
||||
|
||||
auto task = new UploadAssetTask(message, senderNode, _filesDirectory, _filesizeLimit);
|
||||
_transferTaskPool.start(task);
|
||||
|
|
|
@ -117,12 +117,13 @@ void AudioMixer::queueAudioPacket(QSharedPointer<ReceivedMessage> message, Share
|
|||
void AudioMixer::queueReplicatedAudioPacket(QSharedPointer<ReceivedMessage> message) {
|
||||
// make sure we have a replicated node for the original sender of the packet
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
QUuid nodeID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
// Node ID is now part of user data, since replicated audio packets are non-sourced.
|
||||
QUuid nodeID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
auto replicatedNode = nodeList->addOrUpdateNode(nodeID, NodeType::Agent,
|
||||
message->getSenderSockAddr(), message->getSenderSockAddr(),
|
||||
true, true);
|
||||
Node::NULL_LOCAL_ID, true, true);
|
||||
replicatedNode->setLastHeardMicrostamp(usecTimestampNow());
|
||||
|
||||
// construct a "fake" audio received message from the byte array and packet list information
|
||||
|
@ -136,7 +137,7 @@ void AudioMixer::queueReplicatedAudioPacket(QSharedPointer<ReceivedMessage> mess
|
|||
|
||||
auto replicatedMessage = QSharedPointer<ReceivedMessage>::create(audioData, rewrittenType,
|
||||
versionForPacketType(rewrittenType),
|
||||
message->getSenderSockAddr(), nodeID);
|
||||
message->getSenderSockAddr(), Node::NULL_LOCAL_ID);
|
||||
|
||||
getOrCreateClientData(replicatedNode.data())->queuePacket(replicatedMessage, replicatedNode);
|
||||
}
|
||||
|
|
|
@ -376,6 +376,11 @@ void AudioMixerSlave::addStream(AudioMixerClientData& listenerNodeData, const QU
|
|||
return;
|
||||
}
|
||||
|
||||
if (streamToAdd.getType() == PositionalAudioStream::Injector) {
|
||||
// apply per-avatar gain to positional audio injectors, which wouldn't otherwise be affected by PAL sliders
|
||||
hrtf.setGainAdjustment(listenerNodeData.hrtfForStream(sourceNodeID, QUuid()).getGainAdjustment());
|
||||
}
|
||||
|
||||
hrtf.render(_bufferSamples, _mixSamples, HRTF_DATASET_INDEX, azimuth, distance, gain,
|
||||
AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL);
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ SharedNodePointer addOrUpdateReplicatedNode(const QUuid& nodeID, const HifiSockA
|
|||
auto replicatedNode = DependencyManager::get<NodeList>()->addOrUpdateNode(nodeID, NodeType::Agent,
|
||||
senderSockAddr,
|
||||
senderSockAddr,
|
||||
true, true);
|
||||
Node::NULL_LOCAL_ID, true, true);
|
||||
|
||||
replicatedNode->setLastHeardMicrostamp(usecTimestampNow());
|
||||
|
||||
|
@ -112,8 +112,8 @@ void AvatarMixer::handleReplicatedPacket(QSharedPointer<ReceivedMessage> message
|
|||
void AvatarMixer::handleReplicatedBulkAvatarPacket(QSharedPointer<ReceivedMessage> message) {
|
||||
while (message->getBytesLeftToRead()) {
|
||||
// first, grab the node ID for this replicated avatar
|
||||
// Node ID is now part of user data, since ReplicatedBulkAvatarPacket is non-sourced.
|
||||
auto nodeID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
// make sure we have an upstream replicated node that matches
|
||||
auto replicatedNode = addOrUpdateReplicatedNode(nodeID, message->getSenderSockAddr());
|
||||
|
||||
|
@ -127,7 +127,7 @@ void AvatarMixer::handleReplicatedBulkAvatarPacket(QSharedPointer<ReceivedMessag
|
|||
// construct a "fake" avatar data received message from the byte array and packet list information
|
||||
auto replicatedMessage = QSharedPointer<ReceivedMessage>::create(avatarByteArray, PacketType::AvatarData,
|
||||
versionForPacketType(PacketType::AvatarData),
|
||||
message->getSenderSockAddr(), nodeID);
|
||||
message->getSenderSockAddr(), Node::NULL_LOCAL_ID);
|
||||
|
||||
// queue up the replicated avatar data with the client data for the replicated node
|
||||
auto start = usecTimestampNow();
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <openssl/err.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <random>
|
||||
|
||||
#include <AccountManager.h>
|
||||
#include <Assignment.h>
|
||||
|
@ -26,7 +27,7 @@ using SharedAssignmentPointer = QSharedPointer<Assignment>;
|
|||
DomainGatekeeper::DomainGatekeeper(DomainServer* server) :
|
||||
_server(server)
|
||||
{
|
||||
|
||||
initLocalIDManagement();
|
||||
}
|
||||
|
||||
void DomainGatekeeper::addPendingAssignedNode(const QUuid& nodeUUID, const QUuid& assignmentUUID,
|
||||
|
@ -451,11 +452,12 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
|
|||
return SharedNodePointer();
|
||||
}
|
||||
|
||||
QUuid hintNodeID;
|
||||
QUuid existingNodeID;
|
||||
|
||||
// in case this is a node that's failing to connect
|
||||
// double check we don't have the same node whose sockets match exactly already in the list
|
||||
limitedNodeList->eachNodeBreakable([&](const SharedNodePointer& node){
|
||||
|
||||
if (node->getPublicSocket() == nodeConnection.publicSockAddr && node->getLocalSocket() == nodeConnection.localSockAddr) {
|
||||
// we have a node that already has these exact sockets - this can occur if a node
|
||||
// is failing to connect to the domain
|
||||
|
@ -465,15 +467,20 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
|
|||
auto existingNodeData = static_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||
|
||||
if (existingNodeData->getUsername() == username) {
|
||||
hintNodeID = node->getUUID();
|
||||
qDebug() << "Deleting existing connection from same sockaddr: " << node->getUUID();
|
||||
existingNodeID = node->getUUID();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!existingNodeID.isNull()) {
|
||||
limitedNodeList->killNodeWithUUID(existingNodeID);
|
||||
}
|
||||
|
||||
// add the connecting node (or re-use the matched one from eachNodeBreakable above)
|
||||
SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection, hintNodeID);
|
||||
SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection);
|
||||
|
||||
// set the edit rights for this user
|
||||
newNode->setPermissions(userPerms);
|
||||
|
@ -523,8 +530,10 @@ SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const Node
|
|||
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
|
||||
Node::LocalID newLocalID = findOrCreateLocalID(nodeID);
|
||||
SharedNodePointer newNode = limitedNodeList->addOrUpdateNode(nodeID, nodeConnection.nodeType,
|
||||
nodeConnection.publicSockAddr, nodeConnection.localSockAddr);
|
||||
nodeConnection.publicSockAddr, nodeConnection.localSockAddr,
|
||||
newLocalID);
|
||||
|
||||
// So that we can send messages to this node at will - we need to activate the correct socket on this node now
|
||||
newNode->activateMatchingOrNewSymmetricSocket(discoveredSocket);
|
||||
|
@ -1014,3 +1023,31 @@ void DomainGatekeeper::refreshGroupsCache() {
|
|||
_server->_settingsManager.debugDumpGroupsState();
|
||||
#endif
|
||||
}
|
||||
|
||||
void DomainGatekeeper::initLocalIDManagement() {
|
||||
std::uniform_int_distribution<quint16> sixteenBitRand;
|
||||
std::random_device randomDevice;
|
||||
std::default_random_engine engine { randomDevice() };
|
||||
_currentLocalID = sixteenBitRand(engine);
|
||||
// Ensure increment is odd.
|
||||
_idIncrement = sixteenBitRand(engine) | 1;
|
||||
}
|
||||
|
||||
Node::LocalID DomainGatekeeper::findOrCreateLocalID(const QUuid& uuid) {
|
||||
auto existingLocalIDIt = _uuidToLocalID.find(uuid);
|
||||
if (existingLocalIDIt != _uuidToLocalID.end()) {
|
||||
return existingLocalIDIt->second;
|
||||
}
|
||||
|
||||
assert(_localIDs.size() < std::numeric_limits<LocalIDs::value_type>::max() - 2);
|
||||
|
||||
Node::LocalID newLocalID;
|
||||
do {
|
||||
newLocalID = _currentLocalID;
|
||||
_currentLocalID += _idIncrement;
|
||||
} while (newLocalID == Node::NULL_LOCAL_ID || _localIDs.find(newLocalID) != _localIDs.end());
|
||||
|
||||
_uuidToLocalID.emplace(uuid, newLocalID);
|
||||
_localIDs.insert(newLocalID);
|
||||
return newLocalID;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#define hifi_DomainGatekeeper_h
|
||||
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
|
@ -41,6 +42,8 @@ public:
|
|||
|
||||
void removeICEPeer(const QUuid& peerUUID) { _icePeers.remove(peerUUID); }
|
||||
|
||||
Node::LocalID findOrCreateLocalID(const QUuid& uuid);
|
||||
|
||||
static void sendProtocolMismatchConnectionDenial(const HifiSockAddr& senderSockAddr);
|
||||
public slots:
|
||||
void processConnectRequestPacket(QSharedPointer<ReceivedMessage> message);
|
||||
|
@ -120,6 +123,16 @@ private:
|
|||
void getGroupMemberships(const QString& username);
|
||||
// void getIsGroupMember(const QString& username, const QUuid groupID);
|
||||
void getDomainOwnerFriendsList();
|
||||
|
||||
// Local ID management.
|
||||
void initLocalIDManagement();
|
||||
using UUIDToLocalID = std::unordered_map<QUuid, Node::LocalID> ;
|
||||
using LocalIDs = std::unordered_set<Node::LocalID>;
|
||||
LocalIDs _localIDs;
|
||||
UUIDToLocalID _uuidToLocalID;
|
||||
|
||||
Node::LocalID _currentLocalID;
|
||||
Node::LocalID _idIncrement;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -593,8 +593,8 @@ bool DomainServer::isPacketVerified(const udt::Packet& packet) {
|
|||
|
||||
if (!PacketTypeEnum::getNonSourcedPackets().contains(headerType)) {
|
||||
// this is a sourced packet - first check if we have a node that matches
|
||||
QUuid sourceID = NLPacket::sourceIDInHeader(packet);
|
||||
SharedNodePointer sourceNode = nodeList->nodeWithUUID(sourceID);
|
||||
Node::LocalID localSourceID = NLPacket::sourceIDInHeader(packet);
|
||||
SharedNodePointer sourceNode = nodeList->nodeWithLocalID(localSourceID);
|
||||
|
||||
if (sourceNode) {
|
||||
// unverified DS packets (due to a lack of connection secret between DS + node)
|
||||
|
@ -612,14 +612,12 @@ bool DomainServer::isPacketVerified(const udt::Packet& packet) {
|
|||
return nodeList->isPacketVerifiedWithSource(packet, sourceNode.data());
|
||||
} else {
|
||||
HIFI_FDEBUG("Packet of type" << headerType
|
||||
<< "received from unmatched IP for UUID" << uuidStringWithoutCurlyBraces(sourceID));
|
||||
|
||||
<< "received from unmatched IP for UUID" << uuidStringWithoutCurlyBraces(sourceNode->getUUID()));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
HIFI_FDEBUG("Packet of type" << headerType
|
||||
<< "received from unknown node with UUID" << uuidStringWithoutCurlyBraces(sourceID));
|
||||
|
||||
<< "received from unknown node with UUID" << uuidStringWithoutCurlyBraces(sourceNode->getUUID()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -683,6 +681,10 @@ void DomainServer::setupNodeListAndAssignments() {
|
|||
}
|
||||
}
|
||||
|
||||
// Create our own short session ID.
|
||||
Node::LocalID serverSessionLocalID = _gatekeeper.findOrCreateLocalID(nodeList->getSessionUUID());
|
||||
nodeList->setSessionLocalID(serverSessionLocalID);
|
||||
|
||||
if (isMetaverseDomain) {
|
||||
// see if we think we're a temp domain (we have an API key) or a full domain
|
||||
const auto& temporaryDomainKey = DependencyManager::get<AccountManager>()->getTemporaryDomainKey(getID());
|
||||
|
@ -1112,7 +1114,8 @@ void DomainServer::handleConnectedNode(SharedNodePointer newNode) {
|
|||
}
|
||||
|
||||
void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr &senderSockAddr) {
|
||||
const int NUM_DOMAIN_LIST_EXTENDED_HEADER_BYTES = NUM_BYTES_RFC4122_UUID + NUM_BYTES_RFC4122_UUID + 2;
|
||||
const int NUM_DOMAIN_LIST_EXTENDED_HEADER_BYTES = NUM_BYTES_RFC4122_UUID + NLPacket::NUM_BYTES_LOCALID +
|
||||
NUM_BYTES_RFC4122_UUID + NLPacket::NUM_BYTES_LOCALID + 4;
|
||||
|
||||
// setup the extended header for the domain list packets
|
||||
// this data is at the beginning of each of the domain list packets
|
||||
|
@ -1122,7 +1125,9 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
|
|||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
|
||||
extendedHeaderStream << limitedNodeList->getSessionUUID();
|
||||
extendedHeaderStream << limitedNodeList->getSessionLocalID();
|
||||
extendedHeaderStream << node->getUUID();
|
||||
extendedHeaderStream << node->getLocalID();
|
||||
extendedHeaderStream << node->getPermissions();
|
||||
|
||||
auto domainListPackets = NLPacketList::create(PacketType::DomainList, extendedHeader);
|
||||
|
@ -2841,7 +2846,7 @@ void DomainServer::updateReplicationNodes(ReplicationServerDirection direction)
|
|||
// manually add the replication node to our node list
|
||||
auto node = nodeList->addOrUpdateNode(QUuid::createUuid(), replicationServer.nodeType,
|
||||
replicationServer.sockAddr, replicationServer.sockAddr,
|
||||
false, direction == Upstream);
|
||||
Node::NULL_LOCAL_ID, false, direction == Upstream);
|
||||
node->setIsForcedNeverSilent(true);
|
||||
|
||||
qDebug() << "Adding" << (direction == Upstream ? "upstream" : "downstream")
|
||||
|
@ -2903,7 +2908,7 @@ void DomainServer::updateReplicatedNodes() {
|
|||
}
|
||||
|
||||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||
nodeList->eachMatchingNode([this](const SharedNodePointer& otherNode) -> bool {
|
||||
nodeList->eachMatchingNode([](const SharedNodePointer& otherNode) -> bool {
|
||||
return otherNode->getType() == NodeType::Agent;
|
||||
}, [this](const SharedNodePointer& otherNode) {
|
||||
auto shouldReplicate = shouldReplicateNode(*otherNode);
|
||||
|
@ -3141,13 +3146,12 @@ void DomainServer::processNodeDisconnectRequestPacket(QSharedPointer<ReceivedMes
|
|||
// This packet has been matched to a source node and they're asking not to be in the domain anymore
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
|
||||
const QUuid& nodeUUID = message->getSourceID();
|
||||
|
||||
qDebug() << "Received a disconnect request from node with UUID" << nodeUUID;
|
||||
auto localID = message->getSourceID();
|
||||
qDebug() << "Received a disconnect request from node with local ID" << localID;
|
||||
|
||||
// we want to check what type this node was before going to kill it so that we can avoid sending the RemovedNode
|
||||
// packet to nodes that don't care about this type
|
||||
auto nodeToKill = limitedNodeList->nodeWithUUID(nodeUUID);
|
||||
auto nodeToKill = limitedNodeList->nodeWithLocalID(localID);
|
||||
|
||||
if (nodeToKill) {
|
||||
handleKillNode(nodeToKill);
|
||||
|
@ -3415,7 +3419,7 @@ void DomainServer::handleDomainContentReplacementFromURLRequest(QSharedPointer<R
|
|||
}
|
||||
|
||||
void DomainServer::handleOctreeFileReplacementRequest(QSharedPointer<ReceivedMessage> message) {
|
||||
auto node = DependencyManager::get<NodeList>()->nodeWithUUID(message->getSourceID());
|
||||
auto node = DependencyManager::get<NodeList>()->nodeWithLocalID(message->getSourceID());
|
||||
if (node->getCanReplaceContent()) {
|
||||
handleOctreeFileReplacement(message->readAll());
|
||||
}
|
||||
|
|
|
@ -689,6 +689,34 @@ 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>
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Property</th><th>Type</th><th>Data</th><th>Description</th></tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td><code>CameraFirstPerson</code></td><td>number</td><td>number</td><td>The camera is in first-person mode.
|
||||
* </td></tr>
|
||||
* <tr><td><code>CameraThirdPerson</code></td><td>number</td><td>number</td><td>The camera is in third-person mode.
|
||||
* </td></tr>
|
||||
* <tr><td><code>CameraFSM</code></td><td>number</td><td>number</td><td>The camera is in full screen mirror mode.</td></tr>
|
||||
* <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>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>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef Controller.Hardware-Application
|
||||
*/
|
||||
|
||||
static const QString STATE_IN_HMD = "InHMD";
|
||||
static const QString STATE_CAMERA_FULL_SCREEN_MIRROR = "CameraFSM";
|
||||
static const QString STATE_CAMERA_FIRST_PERSON = "CameraFirstPerson";
|
||||
|
@ -1100,10 +1128,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
auto audioScriptingInterface = DependencyManager::get<AudioScriptingInterface>();
|
||||
auto myAvatarPosition = DependencyManager::get<AvatarManager>()->getMyAvatar()->getWorldPosition();
|
||||
float distance = glm::distance(myAvatarPosition, position);
|
||||
bool shouldMute = !audioClient->isMuted() && (distance < radius);
|
||||
|
||||
if (shouldMute) {
|
||||
audioClient->toggleMute();
|
||||
if (distance < radius) {
|
||||
audioClient->setMuted(true);
|
||||
audioScriptingInterface->environmentMuted();
|
||||
}
|
||||
});
|
||||
|
@ -1528,7 +1555,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
|
||||
if (state) {
|
||||
if (action == controller::toInt(controller::Action::TOGGLE_MUTE)) {
|
||||
DependencyManager::get<AudioClient>()->toggleMute();
|
||||
auto audioClient = DependencyManager::get<AudioClient>();
|
||||
audioClient->setMuted(!audioClient->isMuted());
|
||||
} else if (action == controller::toInt(controller::Action::CYCLE_CAMERA)) {
|
||||
cycleCamera();
|
||||
} else if (action == controller::toInt(controller::Action::CONTEXT_MENU)) {
|
||||
|
@ -2658,6 +2686,12 @@ void Application::initializeUi() {
|
|||
// Now that the menu is instantiated, ensure the display plugin menu is properly updated
|
||||
updateDisplayMode();
|
||||
flushMenuUpdates();
|
||||
|
||||
// The display plugins are created before the menu now, so we need to do this here to hide the menu bar
|
||||
// now that it exists
|
||||
if (_window && _window->isFullScreen()) {
|
||||
setFullscreen(nullptr, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -3472,7 +3506,8 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
|
||||
case Qt::Key_M:
|
||||
if (isMeta) {
|
||||
DependencyManager::get<AudioClient>()->toggleMute();
|
||||
auto audioClient = DependencyManager::get<AudioClient>();
|
||||
audioClient->setMuted(!audioClient->isMuted());
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -5133,7 +5168,7 @@ void Application::update(float deltaTime) {
|
|||
if (menu->isOptionChecked(MenuOption::AutoMuteAudio) && !audioClient->isMuted()) {
|
||||
if (_lastFaceTrackerUpdate > 0
|
||||
&& ((usecTimestampNow() - _lastFaceTrackerUpdate) > MUTE_MICROPHONE_AFTER_USECS)) {
|
||||
audioClient->toggleMute();
|
||||
audioClient->setMuted(true);
|
||||
_lastFaceTrackerUpdate = 0;
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -31,10 +31,41 @@ using namespace crashpad;
|
|||
static const std::string BACKTRACE_URL { CMAKE_BACKTRACE_URL };
|
||||
static const std::string BACKTRACE_TOKEN { CMAKE_BACKTRACE_TOKEN };
|
||||
|
||||
static std::wstring gIPCPipe;
|
||||
|
||||
extern QString qAppFileName();
|
||||
|
||||
// crashpad::AnnotationList* crashpadAnnotations { nullptr };
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
LONG WINAPI vectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo) {
|
||||
static const DWORD EXTERNAL_EXCEPTION_CODE{ 0xe06d7363 };
|
||||
static const DWORD HEAP_CORRUPTION_CODE{ 0xc0000374 };
|
||||
|
||||
auto exceptionCode = pExceptionInfo->ExceptionRecord->ExceptionCode;
|
||||
if (exceptionCode == EXTERNAL_EXCEPTION_CODE) {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
if (exceptionCode == HEAP_CORRUPTION_CODE) {
|
||||
qCritical() << "VectoredExceptionHandler: Heap corruption:" << QString::number(exceptionCode, 16);
|
||||
|
||||
CrashpadClient client;
|
||||
if (gIPCPipe.length()) {
|
||||
bool rc = client.SetHandlerIPCPipe(gIPCPipe);
|
||||
qCritical() << "SetHandlerIPCPipe = " << rc;
|
||||
} else {
|
||||
qCritical() << "No IPC Pipe was previously defined for crash handler.";
|
||||
}
|
||||
qCritical() << "Calling DumpAndCrash()";
|
||||
client.DumpAndCrash(pExceptionInfo);
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
bool startCrashHandler() {
|
||||
if (BACKTRACE_URL.empty() || BACKTRACE_TOKEN.empty()) {
|
||||
return false;
|
||||
|
@ -76,7 +107,12 @@ bool startCrashHandler() {
|
|||
// Enable automated uploads.
|
||||
database->GetSettings()->SetUploadsEnabled(true);
|
||||
|
||||
return client.StartHandler(handler, db, db, BACKTRACE_URL, annotations, arguments, true, true);
|
||||
bool result = client.StartHandler(handler, db, db, BACKTRACE_URL, annotations, arguments, true, true);
|
||||
gIPCPipe = client.GetHandlerIPCPipe();
|
||||
|
||||
AddVectoredExceptionHandler(0, vectoredExceptionHandler);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void setCrashAnnotation(std::string name, std::string value) {
|
||||
|
|
|
@ -750,32 +750,32 @@ Menu::Menu() {
|
|||
action = addActionToQMenuAndActionHash(crashMenu, MenuOption::CrashPureVirtualFunction);
|
||||
connect(action, &QAction::triggered, qApp, []() { crash::pureVirtualCall(); });
|
||||
action = addActionToQMenuAndActionHash(crashMenu, MenuOption::CrashPureVirtualFunctionThreaded);
|
||||
connect(action, &QAction::triggered, qApp, []() { std::thread([]() { crash::pureVirtualCall(); }); });
|
||||
connect(action, &QAction::triggered, qApp, []() { std::thread(crash::pureVirtualCall).join(); });
|
||||
|
||||
action = addActionToQMenuAndActionHash(crashMenu, MenuOption::CrashDoubleFree);
|
||||
connect(action, &QAction::triggered, qApp, []() { crash::doubleFree(); });
|
||||
action = addActionToQMenuAndActionHash(crashMenu, MenuOption::CrashDoubleFreeThreaded);
|
||||
connect(action, &QAction::triggered, qApp, []() { std::thread([]() { crash::doubleFree(); }); });
|
||||
connect(action, &QAction::triggered, qApp, []() { std::thread(crash::doubleFree).join(); });
|
||||
|
||||
action = addActionToQMenuAndActionHash(crashMenu, MenuOption::CrashAbort);
|
||||
connect(action, &QAction::triggered, qApp, []() { crash::doAbort(); });
|
||||
action = addActionToQMenuAndActionHash(crashMenu, MenuOption::CrashAbortThreaded);
|
||||
connect(action, &QAction::triggered, qApp, []() { std::thread([]() { crash::doAbort(); }); });
|
||||
connect(action, &QAction::triggered, qApp, []() { std::thread(crash::doAbort).join(); });
|
||||
|
||||
action = addActionToQMenuAndActionHash(crashMenu, MenuOption::CrashNullDereference);
|
||||
connect(action, &QAction::triggered, qApp, []() { crash::nullDeref(); });
|
||||
action = addActionToQMenuAndActionHash(crashMenu, MenuOption::CrashNullDereferenceThreaded);
|
||||
connect(action, &QAction::triggered, qApp, []() { std::thread([]() { crash::nullDeref(); }); });
|
||||
connect(action, &QAction::triggered, qApp, []() { std::thread(crash::nullDeref).join(); });
|
||||
|
||||
action = addActionToQMenuAndActionHash(crashMenu, MenuOption::CrashOutOfBoundsVectorAccess);
|
||||
connect(action, &QAction::triggered, qApp, []() { crash::outOfBoundsVectorCrash(); });
|
||||
action = addActionToQMenuAndActionHash(crashMenu, MenuOption::CrashOutOfBoundsVectorAccessThreaded);
|
||||
connect(action, &QAction::triggered, qApp, []() { std::thread([]() { crash::outOfBoundsVectorCrash(); }); });
|
||||
connect(action, &QAction::triggered, qApp, []() { std::thread(crash::outOfBoundsVectorCrash).join(); });
|
||||
|
||||
action = addActionToQMenuAndActionHash(crashMenu, MenuOption::CrashNewFault);
|
||||
connect(action, &QAction::triggered, qApp, []() { crash::newFault(); });
|
||||
action = addActionToQMenuAndActionHash(crashMenu, MenuOption::CrashNewFaultThreaded);
|
||||
connect(action, &QAction::triggered, qApp, []() { std::thread([]() { crash::newFault(); }); });
|
||||
connect(action, &QAction::triggered, qApp, []() { std::thread(crash::newFault).join(); });
|
||||
|
||||
// Developer > Log...
|
||||
addActionToQMenuAndActionHash(developerMenu, MenuOption::Log, Qt::CTRL | Qt::SHIFT | Qt::Key_L,
|
||||
|
|
|
@ -71,7 +71,7 @@ void OctreePacketProcessor::processPacket(QSharedPointer<ReceivedMessage> messag
|
|||
if (message->getVersion() != versionForPacketType(message->getType())) {
|
||||
static QMultiMap<QUuid, PacketType> versionDebugSuppressMap;
|
||||
|
||||
const QUuid& senderUUID = message->getSourceID();
|
||||
const QUuid& senderUUID = sendingNode->getUUID();
|
||||
if (!versionDebugSuppressMap.contains(senderUUID, packetType)) {
|
||||
|
||||
qDebug() << "Was stats packet? " << wasStatsPacket;
|
||||
|
|
|
@ -50,110 +50,162 @@ float Audio::loudnessToLevel(float loudness) {
|
|||
|
||||
Audio::Audio() : _devices(_contextIsHMD) {
|
||||
auto client = DependencyManager::get<AudioClient>().data();
|
||||
connect(client, &AudioClient::muteToggled, this, &Audio::onMutedChanged);
|
||||
connect(client, &AudioClient::noiseReductionChanged, this, &Audio::onNoiseReductionChanged);
|
||||
connect(client, &AudioClient::muteToggled, this, &Audio::setMuted);
|
||||
connect(client, &AudioClient::noiseReductionChanged, this, &Audio::enableNoiseReduction);
|
||||
connect(client, &AudioClient::inputLoudnessChanged, this, &Audio::onInputLoudnessChanged);
|
||||
connect(client, &AudioClient::inputVolumeChanged, this, &Audio::onInputVolumeChanged);
|
||||
connect(client, &AudioClient::inputVolumeChanged, this, &Audio::setInputVolume);
|
||||
connect(this, &Audio::contextChanged, &_devices, &AudioDevices::onContextChanged);
|
||||
enableNoiseReduction(enableNoiseReductionSetting.get());
|
||||
}
|
||||
|
||||
bool Audio::startRecording(const QString& filepath) {
|
||||
auto client = DependencyManager::get<AudioClient>().data();
|
||||
return client->startRecording(filepath);
|
||||
return resultWithWriteLock<bool>([&] {
|
||||
return client->startRecording(filepath);
|
||||
});
|
||||
}
|
||||
|
||||
bool Audio::getRecording() {
|
||||
auto client = DependencyManager::get<AudioClient>().data();
|
||||
return client->getRecording();
|
||||
return resultWithReadLock<bool>([&] {
|
||||
return client->getRecording();
|
||||
});
|
||||
}
|
||||
|
||||
void Audio::stopRecording() {
|
||||
auto client = DependencyManager::get<AudioClient>().data();
|
||||
client->stopRecording();
|
||||
withWriteLock([&] {
|
||||
client->stopRecording();
|
||||
});
|
||||
}
|
||||
|
||||
bool Audio::isMuted() const {
|
||||
return resultWithReadLock<bool>([&] {
|
||||
return _isMuted;
|
||||
});
|
||||
}
|
||||
|
||||
void Audio::setMuted(bool isMuted) {
|
||||
if (_isMuted != isMuted) {
|
||||
auto client = DependencyManager::get<AudioClient>().data();
|
||||
QMetaObject::invokeMethod(client, "toggleMute");
|
||||
bool changed = false;
|
||||
withWriteLock([&] {
|
||||
if (_isMuted != isMuted) {
|
||||
_isMuted = isMuted;
|
||||
auto client = DependencyManager::get<AudioClient>().data();
|
||||
QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false));
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
if (changed) {
|
||||
emit mutedChanged(isMuted);
|
||||
}
|
||||
}
|
||||
|
||||
void Audio::onMutedChanged() {
|
||||
bool isMuted = DependencyManager::get<AudioClient>()->isMuted();
|
||||
if (_isMuted != isMuted) {
|
||||
_isMuted = isMuted;
|
||||
emit mutedChanged(_isMuted);
|
||||
}
|
||||
bool Audio::noiseReductionEnabled() const {
|
||||
return resultWithReadLock<bool>([&] {
|
||||
return _enableNoiseReduction;
|
||||
});
|
||||
}
|
||||
|
||||
void Audio::enableNoiseReduction(bool enable) {
|
||||
if (_enableNoiseReduction != enable) {
|
||||
auto client = DependencyManager::get<AudioClient>().data();
|
||||
QMetaObject::invokeMethod(client, "setNoiseReduction", Q_ARG(bool, enable));
|
||||
enableNoiseReductionSetting.set(enable);
|
||||
bool changed = false;
|
||||
withWriteLock([&] {
|
||||
if (_enableNoiseReduction != enable) {
|
||||
_enableNoiseReduction = enable;
|
||||
auto client = DependencyManager::get<AudioClient>().data();
|
||||
QMetaObject::invokeMethod(client, "setNoiseReduction", Q_ARG(bool, enable), Q_ARG(bool, false));
|
||||
enableNoiseReductionSetting.set(enable);
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
if (changed) {
|
||||
emit noiseReductionChanged(enable);
|
||||
}
|
||||
}
|
||||
|
||||
void Audio::onNoiseReductionChanged() {
|
||||
bool noiseReductionEnabled = DependencyManager::get<AudioClient>()->isNoiseReductionEnabled();
|
||||
if (_enableNoiseReduction != noiseReductionEnabled) {
|
||||
_enableNoiseReduction = noiseReductionEnabled;
|
||||
emit noiseReductionChanged(_enableNoiseReduction);
|
||||
}
|
||||
float Audio::getInputVolume() const {
|
||||
return resultWithReadLock<bool>([&] {
|
||||
return _inputVolume;
|
||||
});
|
||||
}
|
||||
|
||||
void Audio::setInputVolume(float volume) {
|
||||
// getInputVolume will not reflect changes synchronously, so clamp beforehand
|
||||
volume = glm::clamp(volume, 0.0f, 1.0f);
|
||||
|
||||
if (_inputVolume != volume) {
|
||||
auto client = DependencyManager::get<AudioClient>().data();
|
||||
QMetaObject::invokeMethod(client, "setInputVolume", Q_ARG(float, volume));
|
||||
bool changed = false;
|
||||
withWriteLock([&] {
|
||||
if (_inputVolume != volume) {
|
||||
_inputVolume = volume;
|
||||
auto client = DependencyManager::get<AudioClient>().data();
|
||||
QMetaObject::invokeMethod(client, "setInputVolume", Q_ARG(float, volume), Q_ARG(bool, false));
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
if (changed) {
|
||||
emit inputVolumeChanged(volume);
|
||||
}
|
||||
}
|
||||
|
||||
void Audio::onInputVolumeChanged(float volume) {
|
||||
if (_inputVolume != volume) {
|
||||
_inputVolume = volume;
|
||||
emit inputVolumeChanged(_inputVolume);
|
||||
}
|
||||
float Audio::getInputLevel() const {
|
||||
return resultWithReadLock<bool>([&] {
|
||||
return _inputLevel;
|
||||
});
|
||||
}
|
||||
|
||||
void Audio::onInputLoudnessChanged(float loudness) {
|
||||
float level = loudnessToLevel(loudness);
|
||||
|
||||
if (_inputLevel != level) {
|
||||
_inputLevel = level;
|
||||
emit inputLevelChanged(_inputLevel);
|
||||
bool changed = false;
|
||||
withWriteLock([&] {
|
||||
if (_inputLevel != level) {
|
||||
_inputLevel = level;
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
if (changed) {
|
||||
emit inputLevelChanged(level);
|
||||
}
|
||||
}
|
||||
|
||||
QString Audio::getContext() const {
|
||||
return _contextIsHMD ? Audio::HMD : Audio::DESKTOP;
|
||||
return resultWithReadLock<QString>([&] {
|
||||
return _contextIsHMD ? Audio::HMD : Audio::DESKTOP;
|
||||
});
|
||||
}
|
||||
|
||||
void Audio::onContextChanged() {
|
||||
bool changed = false;
|
||||
bool isHMD = qApp->isHMDMode();
|
||||
if (_contextIsHMD != isHMD) {
|
||||
_contextIsHMD = isHMD;
|
||||
emit contextChanged(getContext());
|
||||
withWriteLock([&] {
|
||||
if (_contextIsHMD != isHMD) {
|
||||
_contextIsHMD = isHMD;
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
if (changed) {
|
||||
emit contextChanged(isHMD ? Audio::HMD : Audio::DESKTOP);
|
||||
}
|
||||
}
|
||||
|
||||
void Audio::setReverb(bool enable) {
|
||||
DependencyManager::get<AudioClient>()->setReverb(enable);
|
||||
withWriteLock([&] {
|
||||
DependencyManager::get<AudioClient>()->setReverb(enable);
|
||||
});
|
||||
}
|
||||
|
||||
void Audio::setReverbOptions(const AudioEffectOptions* options) {
|
||||
DependencyManager::get<AudioClient>()->setReverbOptions(options);
|
||||
withWriteLock([&] {
|
||||
DependencyManager::get<AudioClient>()->setReverbOptions(options);
|
||||
});
|
||||
}
|
||||
|
||||
void Audio::setInputDevice(const QAudioDeviceInfo& device, bool isHMD) {
|
||||
_devices.chooseInputDevice(device, isHMD);
|
||||
withWriteLock([&] {
|
||||
_devices.chooseInputDevice(device, isHMD);
|
||||
});
|
||||
}
|
||||
|
||||
void Audio::setOutputDevice(const QAudioDeviceInfo& device, bool isHMD) {
|
||||
_devices.chooseOutputDevice(device, isHMD);
|
||||
withWriteLock([&] {
|
||||
_devices.chooseOutputDevice(device, isHMD);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -17,10 +17,11 @@
|
|||
#include "AudioEffectOptions.h"
|
||||
#include "SettingHandle.h"
|
||||
#include "AudioFileWav.h"
|
||||
#include <shared/ReadWriteLockable.h>
|
||||
|
||||
namespace scripting {
|
||||
|
||||
class Audio : public AudioScriptingInterface {
|
||||
class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
|
||||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
||||
|
@ -40,16 +41,13 @@ public:
|
|||
|
||||
virtual ~Audio() {}
|
||||
|
||||
bool isMuted() const { return _isMuted; }
|
||||
bool noiseReductionEnabled() const { return _enableNoiseReduction; }
|
||||
float getInputVolume() const { return _inputVolume; }
|
||||
float getInputLevel() const { return _inputLevel; }
|
||||
bool isMuted() const;
|
||||
bool noiseReductionEnabled() const;
|
||||
float getInputVolume() const;
|
||||
float getInputLevel() const;
|
||||
QString getContext() const;
|
||||
|
||||
void setMuted(bool muted);
|
||||
void enableNoiseReduction(bool enable);
|
||||
void showMicMeter(bool show);
|
||||
void setInputVolume(float volume);
|
||||
|
||||
Q_INVOKABLE void setInputDevice(const QAudioDeviceInfo& device, bool isHMD);
|
||||
Q_INVOKABLE void setOutputDevice(const QAudioDeviceInfo& device, bool isHMD);
|
||||
|
@ -72,9 +70,9 @@ public slots:
|
|||
void onContextChanged();
|
||||
|
||||
private slots:
|
||||
void onMutedChanged();
|
||||
void onNoiseReductionChanged();
|
||||
void onInputVolumeChanged(float volume);
|
||||
void setMuted(bool muted);
|
||||
void enableNoiseReduction(bool enable);
|
||||
void setInputVolume(float volume);
|
||||
void onInputLoudnessChanged(float loudness);
|
||||
|
||||
protected:
|
||||
|
|
|
@ -188,7 +188,7 @@ void AudioDeviceList::onDeviceChanged(const QAudioDeviceInfo& device, bool isHMD
|
|||
|
||||
for (auto i = 0; i < _devices.size(); ++i) {
|
||||
std::shared_ptr<AudioDevice> device = _devices[i];
|
||||
bool &isSelected = isHMD ? device->selectedHMD : device->selectedDesktop;
|
||||
bool& isSelected = isHMD ? device->selectedHMD : device->selectedDesktop;
|
||||
if (isSelected && device->info != selectedDevice) {
|
||||
isSelected = false;
|
||||
} else if (device->info == selectedDevice) {
|
||||
|
@ -259,7 +259,7 @@ void AudioDeviceList::onDevicesChanged(const QList<QAudioDeviceInfo>& devices) {
|
|||
|
||||
foreach(const QAudioDeviceInfo& deviceInfo, devices) {
|
||||
for (bool isHMD : {false, true}) {
|
||||
auto &backupSelectedDeviceName = isHMD ? _backupSelectedHMDDeviceName : _backupSelectedDesktopDeviceName;
|
||||
auto& backupSelectedDeviceName = isHMD ? _backupSelectedHMDDeviceName : _backupSelectedDesktopDeviceName;
|
||||
if (deviceInfo.deviceName() == backupSelectedDeviceName) {
|
||||
QAudioDeviceInfo& selectedDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice;
|
||||
selectedDevice = deviceInfo;
|
||||
|
@ -278,7 +278,7 @@ void AudioDeviceList::onDevicesChanged(const QList<QAudioDeviceInfo>& devices) {
|
|||
|
||||
for (bool isHMD : {false, true}) {
|
||||
QAudioDeviceInfo& selectedDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice;
|
||||
bool &isSelected = isHMD ? device.selectedHMD : device.selectedDesktop;
|
||||
bool& isSelected = isHMD ? device.selectedHMD : device.selectedDesktop;
|
||||
|
||||
if (!selectedDevice.isNull()) {
|
||||
isSelected = (device.info == selectedDevice);
|
||||
|
|
|
@ -24,11 +24,154 @@
|
|||
#include <WheelEvent.h>
|
||||
class ScriptEngine;
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* The Controller API provides facilities to interact with computer and controller hardware.
|
||||
*
|
||||
* <h5>Functions:</h5>
|
||||
*
|
||||
* <p>Properties</p>
|
||||
* <ul>
|
||||
* <li>{@link Controller.getActions|getActions}</li>
|
||||
* <li>{@link Controller.getHardware|getHardware}</li>
|
||||
* <li>{@link Controller.getStandard|getStandard}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Mappings</p>
|
||||
* <ul>
|
||||
* <li>{@link Controller.disableMapping|disableMapping}</li>
|
||||
* <li>{@link Controller.enableMapping|enableMapping}</li>
|
||||
* <li>{@link Controller.loadMapping|loadMapping}</li>
|
||||
* <li>{@link Controller.newMapping|newMapping}</li>
|
||||
* <li>{@link Controller.parseMapping|parseMapping}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Input, Hardware, and Action Reflection</p>
|
||||
* <ul>
|
||||
* <li>{@link Controller.findAction|findAction}</li>
|
||||
* <li>{@link Controller.findDevice|findDevice}</li>
|
||||
* <li>{@link Controller.getActionNames|getActionNames}</li>
|
||||
* <li>{@link Controller.getAllActions|getAllActions}</li>
|
||||
* <li>{@link Controller.getAvailableInputs|getAvailableInputs}</li>
|
||||
* <li>{@link Controller.getDeviceName|getDeviceName}</li>
|
||||
* <li>{@link Controller.getDeviceNames|getDeviceNames}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Input, Hardware, and Action Events</p>
|
||||
* <ul>
|
||||
* <li>{@link Controller.actionEvent|actionEvent}</li>
|
||||
* <li>{@link Controller.hardwareChanged|hardwareChanged}</li>
|
||||
* <li>{@link Controller.inputEvent|inputEvent}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Mouse, Keyboard, and Touch Events</p>
|
||||
* <ul>
|
||||
* <li>{@link Controller.keyPressEvent|keyPressEvent}</li>
|
||||
* <li>{@link Controller.keyReleaseEvent|keyReleaseEvent}</li>
|
||||
* <li>{@link Controller.mouseDoublePressEvent|mouseDoublePressEvent}</li>
|
||||
* <li>{@link Controller.mouseMoveEvent|mouseMoveEvent}</li>
|
||||
* <li>{@link Controller.mousePressEvent|mousePressEvent}</li>
|
||||
* <li>{@link Controller.mouseReleaseEvent|mouseReleaseEvent}</li>
|
||||
* <li>{@link Controller.touchBeginEvent|touchBeginEvent}</li>
|
||||
* <li>{@link Controller.touchEndEvent|touchEndEvent}</li>
|
||||
* <li>{@link Controller.touchUpdateEvent|touchUpdateEvent}</li>
|
||||
* <li>{@link Controller.wheelEvent|wheelEvent}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Control Capturing</p>
|
||||
* <ul>
|
||||
* <li>{@link Controller.captureMouseEvents|captureMouseEvents}</li>
|
||||
* <li>{@link Controller.captureTouchEvents|captureTouchEvents}</li>
|
||||
* <li>{@link Controller.captureWheelEvents|captureWheelEvents}</li>
|
||||
* <li>{@link Controller.releaseMouseEvents|releaseMouseEvents}</li>
|
||||
* <li>{@link Controller.releaseTouchEvents|releaseTouchEvents}</li>
|
||||
* <li>{@link Controller.releaseWheelEvents|releaseWheelEvents}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Action Capturing</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.releaseActionEvents|releaseActionEvents}</li>
|
||||
* <li>{@link Controller.releaseEntityClickEvents|releaseEntityClickEvents}</li>
|
||||
* <li>{@link Controller.releaseJoystick|releaseJoystick}</li>
|
||||
* <li>{@link Controller.releaseKeyEvents|releaseKeyEvents}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Controller and Action Values</p>
|
||||
* <ul>
|
||||
* <li>{@link Controller.getValue|getValue}</li>
|
||||
* <li>{@link Controller.getAxisValue|getAxisValue}</li>
|
||||
* <li>{@link Controller.getPoseValue|getgetPoseValue}</li>
|
||||
* <li>{@link Controller.getButtonValue|getButtonValue} for a particular device</li>
|
||||
* <li>{@link Controller.getAxisValue(0)|getAxisValue} for a particular device</li>
|
||||
* <li>{@link Controller.getPoseValue(0)|getPoseValue} for a particular device</li>
|
||||
* <li>{@link Controller.getActionValue|getActionValue}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Haptics</p>
|
||||
* <ul>
|
||||
* <li>{@link Controller.triggerHapticPulse|triggerHapticPulse}</li>
|
||||
* <li>{@link Controller.triggerHapticPulseOnDevice|triggerHapticPulseOnDevice}</li>
|
||||
* <li>{@link Controller.triggerShortHapticPulse|triggerShortHapticPulse}</li>
|
||||
* <li>{@link Controller.triggerShortHapticPulseOnDevice|triggerShortHapticPulseOnDevice}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Display Information</p>
|
||||
* <ul>
|
||||
* <li>{@link Controller.getViewportDimensions|getViewportDimensions}</li>
|
||||
* <li>{@link Controller.getRecommendedHUDRect|getRecommendedHUDRect}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Virtual Game Pad</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>
|
||||
* <ul>
|
||||
* <li>{@link Controller.startInputRecording|startInputRecording}</li>
|
||||
* <li>{@link Controller.stopInputRecording|stopInputRecording}</li>
|
||||
* <li>{@link Controller.saveInputRecording|saveInputRecording}</li>
|
||||
* <li>{@link Controller.getInputRecorderSaveDirectory|getInputRecorderSaveDirectory}</li>
|
||||
* <li>{@link Controller.loadInputRecording|loadInputRecording}</li>
|
||||
* <li>{@link Controller.startInputPlayback|startInputPlayback}</li>
|
||||
* <li>{@link Controller.stopInputPlayback|stopInputPlayback}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @namespace Controller
|
||||
*
|
||||
* @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 />
|
||||
* 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">
|
||||
* keyboardMouse.json</a> and
|
||||
* <a href="https://github.com/highfidelity/hifi/blob/master/interface/resources/controllers/standard.json">
|
||||
* standard.json</a>, respectively.
|
||||
*
|
||||
* @property {Controller.Hardware} Hardware - Standard and hardware-specific controller and computer outputs, plus predefined
|
||||
* actions on Interface and the user's avatar. The outputs can be mapped to <code>Actions</code> or functions in a
|
||||
* {@link RouteObject} mapping. Additionally, hardware-specific controller outputs can be mapped to <code>Standard</code>
|
||||
* 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 />
|
||||
* 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>.
|
||||
*/
|
||||
|
||||
/// handles scripting of input controller commands from JS
|
||||
class ControllerScriptingInterface : public controller::ScriptingInterface {
|
||||
Q_OBJECT
|
||||
|
||||
|
||||
public:
|
||||
virtual ~ControllerScriptingInterface() {}
|
||||
|
||||
|
@ -53,40 +196,214 @@ public:
|
|||
|
||||
public slots:
|
||||
|
||||
/**jsdoc
|
||||
* Disable 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>.
|
||||
* @example <caption>Disable left and right strafing.</caption>
|
||||
* var STRAFE_LEFT = { "key": 16777234, isShifted: true };
|
||||
* var STRAFE_RIGHT = { "key": 16777236, isShifted: true };
|
||||
*
|
||||
* Controller.captureKeyEvents(STRAFE_LEFT);
|
||||
* Controller.captureKeyEvents(STRAFE_RIGHT);
|
||||
*
|
||||
* Script.scriptEnding.connect(function () {
|
||||
* Controller.releaseKeyEvents(STRAFE_LEFT);
|
||||
* Controller.releaseKeyEvents(STRAFE_RIGHT);
|
||||
* });
|
||||
*/
|
||||
virtual void captureKeyEvents(const KeyEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Re-enable 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
|
||||
* specified. The <code>text</code> property is ignored. The other properties default to <code>false</code>.
|
||||
*/
|
||||
virtual void releaseKeyEvents(const KeyEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Disable default Interface actions for a joystick.
|
||||
* @function Controller.captureJoystick
|
||||
* @param {number} joystickID - The integer ID of the joystick.
|
||||
* @deprecated This function no longer has any effect.
|
||||
*/
|
||||
virtual void captureJoystick(int joystickIndex);
|
||||
|
||||
/**jsdoc
|
||||
* Re-enable 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.
|
||||
* @deprecated This function no longer has any effect.
|
||||
*/
|
||||
virtual void releaseJoystick(int joystickIndex);
|
||||
|
||||
/**jsdoc
|
||||
* Disable {@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) {
|
||||
* print("Clicked on entity: " + entityID);
|
||||
* });
|
||||
*
|
||||
* Script.setTimeout(function () {
|
||||
* Controller.captureEntityClickEvents();
|
||||
* }, 5000);
|
||||
*
|
||||
* Script.setTimeout(function () {
|
||||
* Controller.releaseEntityClickEvents();
|
||||
* }, 10000);
|
||||
*/
|
||||
virtual void captureEntityClickEvents();
|
||||
|
||||
/**jsdoc
|
||||
* Re-enable {@link Entities.mousePressOnEntity} and {@link Entities.mouseDoublePressOnEntity} events on entities that were
|
||||
* disabled using {@link Controller.captureEntityClickEvents|captureEntityClickEvents}.
|
||||
* @function Controller.releaseEntityClickEvents
|
||||
*/
|
||||
virtual void releaseEntityClickEvents();
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* Get 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
|
||||
* mode.
|
||||
* @function Controller.getRecommendedHUDRect
|
||||
* @returns {Rect} The recommended area in which to position UI.
|
||||
*/
|
||||
virtual QVariant getRecommendedHUDRect() const;
|
||||
|
||||
/**jsdoc
|
||||
* Enables or disables the virtual game pad that is displayed on certain devices (e.g., Android).
|
||||
* @function Controller.setVPadEnabled
|
||||
* @param {boolean} enable - If <code>true</code> then the virtual game pad doesn't work, otherwise it does work provided
|
||||
* that it is not hidden by {@link Controller.setVPadHidden|setVPadHidden}.
|
||||
*
|
||||
*/
|
||||
virtual void setVPadEnabled(bool enable);
|
||||
|
||||
/**jsdoc
|
||||
* Shows or hides the virtual game pad that is displayed on certain devices (e.g., Android).
|
||||
* @function Controller.setVPadHidden
|
||||
* @param {boolean} hidden - If <code>true</code> then the virtual game pad is hidden, otherwise it is shown.
|
||||
*/
|
||||
virtual void setVPadHidden(bool hidden); // Call it when a window should hide it
|
||||
|
||||
/**jsdoc
|
||||
* Sets the amount of extra margin between the virtual game pad that is displayed on certain devices (e.g., Android) and
|
||||
* the bottom of the display.
|
||||
* @function Controller.setVPadExtraBottomMargin
|
||||
* @param {number} margin - Integer number of pixels in the extra margin.
|
||||
*/
|
||||
virtual void setVPadExtraBottomMargin(int margin);
|
||||
|
||||
signals:
|
||||
/**jsdoc
|
||||
* Triggered when a keyboard key is pressed.
|
||||
* @function Controller.keyPressEvent
|
||||
* @param {KeyEvent} event - Details of the key press.
|
||||
* @returns {Signal}
|
||||
* @example <caption>Report the KeyEvent details for each key press.</caption>
|
||||
* Controller.keyPressEvent.connect(function (event) {
|
||||
* print(JSON.stringify(event));
|
||||
* });
|
||||
*/
|
||||
void keyPressEvent(const KeyEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when a keyboard key is released from being pressed.
|
||||
* @function Controller.keyReleaseEvent
|
||||
* @param {KeyEvent} event - Details of the key release.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void keyReleaseEvent(const KeyEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when the mouse moves.
|
||||
* @function Controller.mouseMoveEvent
|
||||
* @param {MouseEvent} event - Details of the mouse movement.
|
||||
* @returns {Signal}
|
||||
* @example <caption>Report the MouseEvent details for each mouse move.</caption>
|
||||
* Controller.mouseMoveEvent.connect(function (event) {
|
||||
* print(JSON.stringify(event));
|
||||
* });
|
||||
*/
|
||||
void mouseMoveEvent(const MouseEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when a mouse button is pressed.
|
||||
* @function Controller.mousePressEvent
|
||||
* @param {MouseEvent} event - Details of the button press.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void mousePressEvent(const MouseEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when a mouse button is double-pressed.
|
||||
* @function Controller.mouseDoublePressEvent
|
||||
* @param {MouseEvent} event - Details of the button double-press.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void mouseDoublePressEvent(const MouseEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when a mouse button is released from being pressed.
|
||||
* @function Controller.mouseReleaseEvent
|
||||
* @param {MouseEvent} event - Details of the button release.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void mouseReleaseEvent(const MouseEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when a touch event starts in the Interface window on a touch-enabled display or device.
|
||||
* @function Controller.touchBeginEvent
|
||||
* @param {TouchEvent} event - Details of the touch begin.
|
||||
* @returns {Signal}
|
||||
* @example <caption>Report the TouchEvent details when a touch event starts.</caption>
|
||||
* Controller.touchBeginEvent.connect(function (event) {
|
||||
* print(JSON.stringify(event));
|
||||
* });
|
||||
*/
|
||||
void touchBeginEvent(const TouchEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when a touch event ends in the Interface window on a touch-enabled display or device.
|
||||
* @function Controller.touchEndEvent
|
||||
* @param {TouchEvent} event - Details of the touch end.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void touchEndEvent(const TouchEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when a touch event update occurs in the Interface window on a touch-enabled display or device.
|
||||
* @function Controller.touchUpdateEvent
|
||||
* @param {TouchEvent} event - Details of the touch update.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void touchUpdateEvent(const TouchEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when the mouse wheel is rotated.
|
||||
* @function Controller.wheelEvent
|
||||
* @param {WheelEvent} event - Details of the wheel movement.
|
||||
* @returns {Signal}
|
||||
* @example <caption>Report the WheelEvent details for each wheel rotation.</caption>
|
||||
* Controller.wheelEvent.connect(function (event) {
|
||||
* print(JSON.stringify(event));
|
||||
* });
|
||||
*/
|
||||
void wheelEvent(const WheelEvent& event);
|
||||
|
||||
private:
|
||||
QString sanatizeName(const QString& name); /// makes a name clean for inclusing in JavaScript
|
||||
|
||||
QMultiMap<int,KeyEvent> _capturedKeys;
|
||||
QSet<int> _capturedJoysticks;
|
||||
bool _captureEntityClicks;
|
||||
|
|
|
@ -531,8 +531,8 @@ private slots:
|
|||
signals:
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when you change the domain you're visiting. <strong>Warning:</strong> Is not emitted if you go to domain that
|
||||
* isn't running.
|
||||
* Triggered when you change the domain you're visiting. <strong>Warning:</strong> Is not emitted if you go to a domain
|
||||
* that isn't running.
|
||||
* @function Window.domainChanged
|
||||
* @param {string} domainURL - The domain's URL.
|
||||
* @returns {Signal}
|
||||
|
|
|
@ -757,7 +757,7 @@ void AudioClient::Gate::flush() {
|
|||
|
||||
void AudioClient::handleNoisyMutePacket(QSharedPointer<ReceivedMessage> message) {
|
||||
if (!_muted) {
|
||||
toggleMute();
|
||||
setMuted(true);
|
||||
|
||||
// have the audio scripting interface emit a signal to say we were muted by the mixer
|
||||
emit mutedByMixer();
|
||||
|
@ -1384,15 +1384,21 @@ void AudioClient::sendMuteEnvironmentPacket() {
|
|||
}
|
||||
}
|
||||
|
||||
void AudioClient::toggleMute() {
|
||||
_muted = !_muted;
|
||||
emit muteToggled();
|
||||
void AudioClient::setMuted(bool muted, bool emitSignal) {
|
||||
if (_muted != muted) {
|
||||
_muted = muted;
|
||||
if (emitSignal) {
|
||||
emit muteToggled(_muted);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioClient::setNoiseReduction(bool enable) {
|
||||
void AudioClient::setNoiseReduction(bool enable, bool emitSignal) {
|
||||
if (_isNoiseGateEnabled != enable) {
|
||||
_isNoiseGateEnabled = enable;
|
||||
emit noiseReductionChanged();
|
||||
if (emitSignal) {
|
||||
emit noiseReductionChanged(_isNoiseGateEnabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2018,9 +2024,11 @@ void AudioClient::startThread() {
|
|||
moveToNewNamedThread(this, "Audio Thread", [this] { start(); });
|
||||
}
|
||||
|
||||
void AudioClient::setInputVolume(float volume) {
|
||||
void AudioClient::setInputVolume(float volume, bool emitSignal) {
|
||||
if (_audioInput && volume != (float)_audioInput->volume()) {
|
||||
_audioInput->setVolume(volume);
|
||||
emit inputVolumeChanged(_audioInput->volume());
|
||||
if (emitSignal) {
|
||||
emit inputVolumeChanged(_audioInput->volume());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -189,13 +189,13 @@ public slots:
|
|||
void reset();
|
||||
void audioMixerKilled();
|
||||
|
||||
void toggleMute();
|
||||
void setMuted(bool muted, bool emitSignal = true);
|
||||
bool isMuted() { return _muted; }
|
||||
|
||||
virtual bool setIsStereoInput(bool stereo) override;
|
||||
virtual bool isStereoInput() override { return _isStereoInput; }
|
||||
|
||||
void setNoiseReduction(bool isNoiseGateEnabled);
|
||||
void setNoiseReduction(bool isNoiseGateEnabled, bool emitSignal = true);
|
||||
bool isNoiseReductionEnabled() const { return _isNoiseGateEnabled; }
|
||||
|
||||
bool getLocalEcho() { return _shouldEchoLocally; }
|
||||
|
@ -218,7 +218,7 @@ public slots:
|
|||
bool switchAudioDevice(QAudio::Mode mode, const QString& deviceName);
|
||||
|
||||
float getInputVolume() const { return (_audioInput) ? (float)_audioInput->volume() : 0.0f; }
|
||||
void setInputVolume(float volume);
|
||||
void setInputVolume(float volume, bool emitSignal = true);
|
||||
void setReverb(bool reverb);
|
||||
void setReverbOptions(const AudioEffectOptions* options);
|
||||
|
||||
|
@ -229,8 +229,8 @@ public slots:
|
|||
|
||||
signals:
|
||||
void inputVolumeChanged(float volume);
|
||||
void muteToggled();
|
||||
void noiseReductionChanged();
|
||||
void muteToggled(bool muted);
|
||||
void noiseReductionChanged(bool noiseReductionEnabled);
|
||||
void mutedByMixer();
|
||||
void inputReceived(const QByteArray& inputSamples);
|
||||
void inputLoudnessChanged(float loudness);
|
||||
|
|
|
@ -120,8 +120,8 @@ int InboundAudioStream::parseData(ReceivedMessage& message) {
|
|||
// parse sequence number and track it
|
||||
quint16 sequence;
|
||||
message.readPrimitive(&sequence);
|
||||
SequenceNumberStats::ArrivalInfo arrivalInfo = _incomingSequenceNumberStats.sequenceNumberReceived(sequence,
|
||||
message.getSourceID());
|
||||
SequenceNumberStats::ArrivalInfo arrivalInfo =
|
||||
_incomingSequenceNumberStats.sequenceNumberReceived(sequence, message.getSourceID());
|
||||
QString codecInPacket = message.readString();
|
||||
|
||||
packetReceivedUpdateTimingStats();
|
||||
|
@ -186,7 +186,7 @@ int InboundAudioStream::parseData(ReceivedMessage& message) {
|
|||
_mismatchedAudioCodecCount = 0;
|
||||
|
||||
// inform others of the mismatch
|
||||
auto sendingNode = DependencyManager::get<NodeList>()->nodeWithUUID(message.getSourceID());
|
||||
auto sendingNode = DependencyManager::get<NodeList>()->nodeWithLocalID(message.getSourceID());
|
||||
if (sendingNode) {
|
||||
emit mismatchedAudioCodec(sendingNode, _selectedCodecName, codecInPacket);
|
||||
qDebug(audio) << "Codec mismatch threshold exceeded, SelectedAudioFormat(" << _selectedCodecName << " ) sent";
|
||||
|
|
|
@ -33,6 +33,282 @@ namespace controller {
|
|||
return std::make_shared<ActionEndpoint>(input);
|
||||
}
|
||||
|
||||
/**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>
|
||||
*
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Property</th><th>Type</th><th>Data</th><th>Description</th></tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td colSpan=4><strong>Avatar Movement</strong></td>
|
||||
* <tr><td><code>TranslateX</code></td><td>number</td><td>number</td><td>Move the user's avatar in the direction of its
|
||||
* x-axis, if the camera isn't in independent or mirror modes.</td></tr>
|
||||
* <tr><td><code>TranslateY</code></td><td>number</td><td>number</td><td>Move the user's avatar in the direction of its
|
||||
* y-axis, if the camera isn't in independent or mirror modes.</td></tr>
|
||||
* <tr><td><code>TranslateZ</code></td><td>number</td><td>number</td><td>Move the user's avatar in the direction of its
|
||||
* z-axis, if the camera isn't in independent or mirror modes.</td></tr>
|
||||
* <tr><td><code>Pitch</code></td><td>number</td><td>number</td><td>Rotate the user's avatar head and attached camera
|
||||
* about its negative x-axis (i.e., positive values pitch down), if the camera isn't in HMD, independent, or mirror
|
||||
* modes.</td></tr>
|
||||
* <tr><td><code>Yaw</code></td><td>number</td><td>number</td><td>Rotate the user's avatar about its y-axis, if the
|
||||
* camera isn't in independent or mirror modes.</td></tr>
|
||||
* <tr><td><code>Roll</code></td><td>number</td><td>number</td><td>No action.</td></tr>
|
||||
* <tr><td><code>StepTranslateX</code></td><td>number</td><td>number</td><td>No action.</td></tr>
|
||||
* <tr><td><code>StepTranslateY</code></td><td>number</td><td>number</td><td>No action.</td></tr>
|
||||
* <tr><td><code>StepTranslateZ</code></td><td>number</td><td>number</td><td>No action.</td></tr>
|
||||
* <tr><td><code>StepPitch</code></td><td>number</td><td>number</td><td>No action.</td></tr>
|
||||
* <tr><td><code>StepYaw</code></td><td>number</td><td>number</td><td>Rotate the user's avatar about its y-axis in a
|
||||
* step increment, if the camera isn't in independent or mirror modes.</td></tr>
|
||||
* <tr><td><code>StepRoll</code></td><td>number</td><td>number</td><td>No action.</td></tr>
|
||||
*
|
||||
* <tr><td colSpan=4><strong>Avatar Skeleton</strong></td>
|
||||
* <tr><td><code>Hips</code></td><td>number</td><td>{@link Pose}</td><td>Set the hips pose of the user's avatar.
|
||||
* </td></tr>
|
||||
* <tr><td><code>Spine2</code></td><td>number</td><td>{@link Pose}</td><td>Set the spine2 pose of the user's avatar.
|
||||
* </td></tr>
|
||||
* <tr><td><code>Head</code></td><td>number</td><td>{@link Pose}</td><td>Set the head pose of the user's avatar.
|
||||
* </td></tr>
|
||||
* <tr><td><code>LeftArm</code></td><td>number</td><td>{@link Pose}</td><td>Set the left arm pose of the user's avatar.
|
||||
* </td></tr>
|
||||
* <tr><td><code>RightArm</code></td><td>number</td><td>{@link Pose}</td><td>Set the right arm pose of the user's
|
||||
* avatar.</td></tr>
|
||||
* <tr><td><code>LeftHand</code></td><td>number</td><td>{@link Pose}</td><td>Set the left hand pose of the user's
|
||||
* avatar.</td></tr>
|
||||
* <tr><td><code>LeftHandThumb1</code></td><td>number</td><td>{@link Pose}</td><td>Set the left thumb 1 finger joint
|
||||
* pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>LeftHandThumb2</code></td><td>number</td><td>{@link Pose}</td><td>Set the left thumb 2 finger joint
|
||||
* pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>LeftHandThumb3</code></td><td>number</td><td>{@link Pose}</td><td>Set the left thumb 3 finger joint
|
||||
* pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>LeftHandThumb4</code></td><td>number</td><td>{@link Pose}</td><td>Set the left thumb 4 finger joint
|
||||
* pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>LeftHandIndex1</code></td><td>number</td><td>{@link Pose}</td><td>Set the left index 1 finger joint
|
||||
* pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>LeftHandIndex2</code></td><td>number</td><td>{@link Pose}</td><td>Set the left index 2 finger joint
|
||||
* pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>LeftHandIndex3</code></td><td>number</td><td>{@link Pose}</td><td>Set the left index 3 finger joint
|
||||
* pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>LeftHandIndex4</code></td><td>number</td><td>{@link Pose}</td><td>Set the left index 4 finger joint
|
||||
* pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>LeftHandMiddle1</code></td><td>number</td><td>{@link Pose}</td><td>Set the left middle 1 finger joint
|
||||
* pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>LeftHandMiddle2</code></td><td>number</td><td>{@link Pose}</td><td>Set the left middle 2 finger joint
|
||||
* pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>LeftHandMiddle3</code></td><td>number</td><td>{@link Pose}</td><td>Set the left middle 3 finger joint
|
||||
* pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>LeftHandMiddle4</code></td><td>number</td><td>{@link Pose}</td><td>Set the left middle 4 finger joint
|
||||
* pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>LeftHandRing1</code></td><td>number</td><td>{@link Pose}</td><td>Set the left ring 1 finger joint pose
|
||||
* of the user's avatar.</td></tr>
|
||||
* <tr><td><code>LeftHandRing2</code></td><td>number</td><td>{@link Pose}</td><td>Set the left ring 2 finger joint pose
|
||||
* of the user's avatar.</td></tr>
|
||||
* <tr><td><code>LeftHandRing3</code></td><td>number</td><td>{@link Pose}</td><td>Set the left ring 3 finger joint pose
|
||||
* of the user's avatar.</td></tr>
|
||||
* <tr><td><code>LeftHandRing4</code></td><td>number</td><td>{@link Pose}</td><td>Set the left ring 4 finger joint pose
|
||||
* of the user's avatar.</td></tr>
|
||||
* <tr><td><code>LeftHandPinky1</code></td><td>number</td><td>{@link Pose}</td><td>Set the left pinky 1 finger joint
|
||||
* pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>LeftHandPinky2</code></td><td>number</td><td>{@link Pose}</td><td>Set the left pinky 2 finger joint
|
||||
* pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>LeftHandPinky3</code></td><td>number</td><td>{@link Pose}</td><td>Set the left pinky 3 finger joint
|
||||
* pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>LeftHandPinky4</code></td><td>number</td><td>{@link Pose}</td><td>Set the left pinky 4 finger joint
|
||||
* pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>RightHand</code></td><td>number</td><td>{@link Pose}</td><td>Set the right hand of the user's avatar.
|
||||
* </td></tr>
|
||||
* <tr><td><code>RightHandThumb1</code></td><td>number</td><td>{@link Pose}</td><td>Set the right thumb 1 finger joint
|
||||
* pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>RightHandThumb2</code></td><td>number</td><td>{@link Pose}</td><td>Set the right thumb 2 finger joint
|
||||
* pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>RightHandThumb3</code></td><td>number</td><td>{@link Pose}</td><td>Set the right thumb 3 finger joint
|
||||
* pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>RightHandThumb4</code></td><td>number</td><td>{@link Pose}</td><td>Set the right thumb 4 finger joint
|
||||
* pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>RightHandIndex1</code></td><td>number</td><td>{@link Pose}</td><td>Set the right index 1 finger joint
|
||||
* pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>RightHandIndex2</code></td><td>number</td><td>{@link Pose}</td><td>Set the right index 2 finger joint
|
||||
* pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>RightHandIndex3</code></td><td>number</td><td>{@link Pose}</td><td>Set the right index 3 finger joint
|
||||
* pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>RightHandIndex4</code></td><td>number</td><td>{@link Pose}</td><td>Set the right index 4 finger joint
|
||||
* pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>RightHandMiddle1</code></td><td>number</td><td>{@link Pose}</td><td>Set the right middle 1 finger
|
||||
* joint pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>RightHandMiddle2</code></td><td>number</td><td>{@link Pose}</td><td>Set the right middle 2 finger
|
||||
* joint pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>RightHandMiddle3</code></td><td>number</td><td>{@link Pose}</td><td>Set the right middle 3 finger
|
||||
* joint pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>RightHandMiddle4</code></td><td>number</td><td>{@link Pose}</td><td>Set the right middle 4 finger
|
||||
* joint pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>RightHandRing1</code></td><td>number</td><td>{@link Pose}</td><td>Set the right ring 1 finger joint
|
||||
* pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>RightHandRing2</code></td><td>number</td><td>{@link Pose}</td><td>Set the right ring 2 finger joint
|
||||
* pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>RightHandRing3</code></td><td>number</td><td>{@link Pose}</td><td>Set the right ring 3 finger joint
|
||||
* pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>RightHandRing4</code></td><td>number</td><td>{@link Pose}</td><td>Set the right ring 4 finger joint
|
||||
* pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>RightHandPinky1</code></td><td>number</td><td>{@link Pose}</td><td>Set the right pinky 1 finger joint
|
||||
* pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>RightHandPinky2</code></td><td>number</td><td>{@link Pose}</td><td>Set the right pinky 2 finger joint
|
||||
* pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>RightHandPinky3</code></td><td>number</td><td>{@link Pose}</td><td>Set the right pinky 3 finger joint
|
||||
* pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>RightHandPinky4</code></td><td>number</td><td>{@link Pose}</td><td>Set the right pinky 4 finger joint
|
||||
* pose of the user's avatar.</td></tr>
|
||||
* <tr><td><code>LeftFoot</code></td><td>number</td><td>{@link Pose}</td><td>Set the left foot pose of the user's
|
||||
* avatar.</td></tr>
|
||||
* <tr><td><code>RightFoot</code></td><td>number</td><td>{@link Pose}</td><td>Set the right foot pose of the user's
|
||||
* avatar.</td></tr>
|
||||
*
|
||||
* <tr><td colSpan=4><strong><strong>Application</strong></td>
|
||||
* <tr><td><code>BoomIn</code></td><td>number</td><td>number</td><td>Zoom camera in from third person toward first
|
||||
* person view.</td></tr>
|
||||
* <tr><td><code>BoomOut</code></td><td>number</td><td>number</td><td>Zoom camera out from first person to third
|
||||
* 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>ToggleMute</code></td><td>number</td><td>number</td><td>Toggle the microphone mute.</td></tr>
|
||||
* <tr><td><code>ToggleOverlay</code></td><td>number</td><td>number</td><td>Toggle the display of overlays.</td></tr>
|
||||
* <tr><td><code>Sprint</code></td><td>number</td><td>number</td><td>Set avatar sprint mode.</td></tr>
|
||||
* <tr><td><code>ReticleClick</code></td><td>number</td><td>number</td><td>Set mouse-pressed.</td></tr>
|
||||
* <tr><td><code>ReticleX</code></td><td>number</td><td>number</td><td>Move the cursor left/right in the x direction.
|
||||
* </td></tr>
|
||||
* <tr><td><code>ReticleY</code></td><td>number</td><td>number</td><td>move the cursor up/down in the y direction.
|
||||
* </td></tr>
|
||||
* <tr><td><code>ReticleLeft</code></td><td>number</td><td>number</td><td>Move the cursor left.</td></tr>
|
||||
* <tr><td><code>ReticleRight</code></td><td>number</td><td>number</td><td>Move the cursor right.</td></tr>
|
||||
* <tr><td><code>ReticleUp</code></td><td>number</td><td>number</td><td>Move the cursor up.</td></tr>
|
||||
* <tr><td><code>ReticleDown</code></td><td>number</td><td>number</td><td>Move the cursor down.</td></tr>
|
||||
* <tr><td><code>UiNavLateral</code></td><td>number</td><td>number</td><td>Generate a keyboard left or right arrow key
|
||||
* event.</td></tr>
|
||||
* <tr><td><code>UiNavVertical</code></td><td>number</td><td>number</td><td>Generate a keyboard up or down arrow key
|
||||
* event.</td></tr>
|
||||
* <tr><td><code>UiNavGroup</code></td><td>number</td><td>number</td><td>Generate a keyboard tab or back-tab key event.
|
||||
* </td></tr>
|
||||
* <tr><td><code>UiNavSelect</code></td><td>number</td><td>number</td><td>Generate a keyboard Enter key event.
|
||||
* </td></tr>
|
||||
* <tr><td><code>UiNavBack</code></td><td>number</td><td>number</td><td>Generate a keyboard Esc key event.</td></tr>
|
||||
* <tr><td><code>LeftHandClick</code></td><td>number</td><td>number</td><td><strong>Deprecated: </strong> No action.
|
||||
* </td></tr>
|
||||
* <tr><td><code>RightHandClick</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> No action.
|
||||
* </td></tr>
|
||||
* <tr><td><code>Shift</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> No action.</td></tr>
|
||||
* <tr><td><code>PrimaryAction</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> No action.
|
||||
* </td></tr>
|
||||
* <tr><td><code>SecondaryAction</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> No action.
|
||||
* </td></tr>
|
||||
*
|
||||
* <tr><td colSpan=4><strong>Aliases</strong></td>
|
||||
* <tr><td><code>Backward</code></td><td>number</td><td>number</td><td>Alias for <code>TranslateZ</code> in the
|
||||
* positive direction.</td></tr>
|
||||
* <tr><td><code>Forward</code></td><td>number</td><td>number</td><td>Alias for <code>TranslateZ</code> in the negative
|
||||
* direction.</td></tr>
|
||||
* <tr><td><code>StrafeRight</code></td><td>number</td><td>number</td><td>Alias for <code>TranslateX</code> in the
|
||||
* positive direction.</td></tr>
|
||||
* <tr><td><code>StrafeLeft</code></td><td>number</td><td>number</td><td>Alias for <code>TranslateX</code> in the
|
||||
* negative direction.</td></tr>
|
||||
* <tr><td><code>Up</code></td><td>number</td><td>number</td><td>Alias for <code>TranslateY</code> in the positive
|
||||
* direction.</td></tr>
|
||||
* <tr><td><code>Down</code></td><td>number</td><td>number</td><td>Alias for <code>TranslateY</code> in the negative
|
||||
* direction.</td></tr>
|
||||
* <tr><td><code>PitchDown</code></td><td>number</td><td>number</td><td>Alias for <code>Pitch</code> in the positive
|
||||
* direction.</td></tr>
|
||||
* <tr><td><code>PitchUp</code></td><td>number</td><td>number</td><td>Alias for <code>Pitch</code> in the negative
|
||||
* direction.</td></tr>
|
||||
* <tr><td><code>YawLeft</code></td><td>number</td><td>number</td><td>Alias for <code>Yaw</code> in the positive
|
||||
* direction.</td></tr>
|
||||
* <tr><td><code>YawRight</code></td><td>number</td><td>number</td><td>Alias for <code>Yaw</code> in the negative
|
||||
* direction.</td></tr>
|
||||
*
|
||||
* <tr><td colSpan=4><strong>Deprecated Aliases</strong></td>
|
||||
* <tr><td><code>LEFT_HAND</code></td><td>number</td><td>{@link Pose}</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>LeftHand</code> instead.</td></tr>
|
||||
* <tr><td><code>RIGHT_HAND</code></td><td>number</td><td>{@link Pose}</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>RightHand</code> instead.</td></tr>
|
||||
* <tr><td><code>BOOM_IN</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>BoomIn</code> instead.</td></tr>
|
||||
* <tr><td><code>BOOM_OUT</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>BoomOut</code> instead.</td></tr>
|
||||
* <tr><td><code>CONTEXT_MENU</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>ContextMenu</code> instead.</td></tr>
|
||||
* <tr><td><code>TOGGLE_MUTE</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>ToggleMute</code> instead.</td></tr>
|
||||
* <tr><td><code>SPRINT</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>Sprint</code> instead.</td></tr>
|
||||
* <tr><td><code>LONGITUDINAL_BACKWARD</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>Backward</code> instead.</td></tr>
|
||||
* <tr><td><code>LONGITUDINAL_FORWARD</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>Forward</code> instead.</td></tr>
|
||||
* <tr><td><code>LATERAL_LEFT</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>StrafeLeft</code> instead.</td></tr>
|
||||
* <tr><td><code>LATERAL_RIGHT</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>StrafeRight</code> instead.</td></tr>
|
||||
* <tr><td><code>VERTICAL_UP</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>Up</code> instead.</td></tr>
|
||||
* <tr><td><code>VERTICAL_DOWN</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>Down</code> instead.</td></tr>
|
||||
* <tr><td><code>PITCH_DOWN</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>PitchDown</code> instead.</td></tr>
|
||||
* <tr><td><code>PITCH_UP</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>PitchUp</code> instead.</td></tr>
|
||||
* <tr><td><code>YAW_LEFT</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>YawLeft</code> instead.</td></tr>
|
||||
* <tr><td><code>YAW_RIGHT</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>YawRight</code> instead.</td></tr>
|
||||
* <tr><td><code>LEFT_HAND_CLICK</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>LeftHandClick</code> instead.</td></tr>
|
||||
* <tr><td><code>RIGHT_HAND_CLICK</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>RightHandClick</code> instead.</td></tr>
|
||||
* <tr><td><code>SHIFT</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>Shift</code> instead.</td></tr>
|
||||
* <tr><td><code>ACTION1</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>PrimaryAction</code> instead.</td></tr>
|
||||
* <tr><td><code>ACTION2</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>SecondaryAction</code> instead.</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><strong>Deprecated: </strong> No
|
||||
* action.</td></tr>
|
||||
* <tr><td><code>TrackedObject01</code></td><td>number</td><td>{@link Pose}</td><td><strong>Deprecated: </strong> No
|
||||
* action.</td></tr>
|
||||
* <tr><td><code>TrackedObject02</code></td><td>number</td><td>{@link Pose}</td><td><strong>Deprecated: </strong> No
|
||||
* action.</td></tr>
|
||||
* <tr><td><code>TrackedObject03</code></td><td>number</td><td>{@link Pose}</td><td><strong>Deprecated: </strong> No
|
||||
* action.</td></tr>
|
||||
* <tr><td><code>TrackedObject04</code></td><td>number</td><td>{@link Pose}</td><td><strong>Deprecated: </strong> No
|
||||
* action.</td></tr>
|
||||
* <tr><td><code>TrackedObject05</code></td><td>number</td><td>{@link Pose}</td><td><strong>Deprecated: </strong> No
|
||||
* action.</td></tr>
|
||||
* <tr><td><code>TrackedObject06</code></td><td>number</td><td>{@link Pose}</td><td><strong>Deprecated: </strong> No
|
||||
* action.</td></tr>
|
||||
* <tr><td><code>TrackedObject07</code></td><td>number</td><td>{@link Pose}</td><td><strong>Deprecated: </strong> No
|
||||
* action.</td></tr>
|
||||
* <tr><td><code>TrackedObject08</code></td><td>number</td><td>{@link Pose}</td><td><strong>Deprecated: </strong> No
|
||||
* action.</td></tr>
|
||||
* <tr><td><code>TrackedObject09</code></td><td>number</td><td>{@link Pose}</td><td><strong>Deprecated: </strong> No
|
||||
* action.</td></tr>
|
||||
* <tr><td><code>TrackedObject10</code></td><td>number</td><td>{@link Pose}</td><td><strong>Deprecated: </strong> No
|
||||
* action.</td></tr>
|
||||
* <tr><td><code>TrackedObject11</code></td><td>number</td><td>{@link Pose}</td><td><strong>Deprecated: </strong> No
|
||||
* action.</td></tr>
|
||||
* <tr><td><code>TrackedObject12</code></td><td>number</td><td>{@link Pose}</td><td><strong>Deprecated: </strong> No
|
||||
* action.</td></tr>
|
||||
* <tr><td><code>TrackedObject13</code></td><td>number</td><td>{@link Pose}</td><td><strong>Deprecated: </strong> No
|
||||
* action.</td></tr>
|
||||
* <tr><td><code>TrackedObject14</code></td><td>number</td><td>{@link Pose}</td><td><strong>Deprecated: </strong> No
|
||||
* action.</td></tr>
|
||||
* <tr><td><code>TrackedObject15</code></td><td>number</td><td>{@link Pose}</td><td><strong>Deprecated: </strong> No
|
||||
* action.</td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef Controller.Actions
|
||||
*/
|
||||
// Device functions
|
||||
Input::NamedVector ActionsDevice::getAvailableInputs() const {
|
||||
static Input::NamedVector availableInputs {
|
||||
|
|
|
@ -31,12 +31,67 @@ namespace controller {
|
|||
class Endpoint;
|
||||
using EndpointPointer = std::shared_ptr<Endpoint>;
|
||||
|
||||
/**jsdoc
|
||||
* <p>Some controller actions may be associated with one or both hands:</p>
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Value</th><th>Description</th></tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td><code>0</code></td><td>Left hand.</td></tr>
|
||||
* <tr><td><code>1</code></td><td>Right hand.</td></tr>
|
||||
* <tr><td><code>2</code></td><td>Both hands.</td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef {number} Controller.Hand
|
||||
*/
|
||||
enum Hand {
|
||||
LEFT = 0,
|
||||
RIGHT,
|
||||
BOTH
|
||||
};
|
||||
|
||||
/**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.
|
||||
*
|
||||
* <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
|
||||
* <a href="https://github.com/highfidelity/hifi/tree/master/interface/resources/controllers">
|
||||
* https://github.com/highfidelity/hifi/tree/master/interface/resources/controllers</a>.</p>
|
||||
*
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Property</th><th>Type</th><th>Description</th></tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td><code>Controller.Hardware.Actions</code></td><td>object</td><td>Synonym for {@link Controller.Actions}.</td></tr>
|
||||
* <tr><td><code>Controller.Hardware.Application</code></td><td>object</td><td>Interface state outputs. See
|
||||
* {@link Controller.Hardware-Application}.</td></tr>
|
||||
* <tr><td><code>Controller.Hardware.Keyboard</code></td><td>object</td><td>Keyboard, mouse, and touch pad outputs. See
|
||||
* {@link Controller.Hardware-Keyboard}.</td></tr>
|
||||
* <tr><td><code>Controller.Hardware.OculusTouch</code></td><td>object</td><td>Oculus Rift HMD outputs. See
|
||||
* {@link Controller.Hardware-OculusTouch}.</td></tr>
|
||||
* <tr><td><code>Controller.Hardware.Vive</code></td><td>object</td><td>Vive HMD outputs. See
|
||||
* {@link Controller.Hardware-Vive}.</td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef Controller.Hardware
|
||||
* @example <caption>List all the currently available <code>Controller.Hardware</code> properties.</caption>
|
||||
* function printProperties(string, item) {
|
||||
* print(string);
|
||||
* for (var key in item) {
|
||||
* if (item.hasOwnProperty(key)) {
|
||||
* printProperties(string + "." + key, item[key]);
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* printProperties("Controller.Hardware", Controller.Hardware);
|
||||
*/
|
||||
// NOTE: If something inherits from both InputDevice and InputPlugin, InputPlugin must go first.
|
||||
// e.g. class Example : public InputPlugin, public InputDevice
|
||||
// instead of class Example : public InputDevice, public InputPlugin
|
||||
|
|
|
@ -30,6 +30,15 @@ namespace controller {
|
|||
velocity == right.getVelocity() && angularVelocity == right.getAngularVelocity();
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* The pose of a joint or other item relative to the world or a parent.
|
||||
* @typedef {object} Pose
|
||||
* @property {Vec3} translation - Translation.
|
||||
* @property {Quat} rotation - Rotation.
|
||||
* @property {Vec3} velocity - Velocity in m/s.
|
||||
* @property {Vec3} angularVelocity - Angular velocity in rad/s.
|
||||
* @property {boolean} valid - <code>true</code> if the pose is valid, otherwise <code>false</code>.
|
||||
*/
|
||||
QScriptValue Pose::toScriptValue(QScriptEngine* engine, const Pose& pose) {
|
||||
QScriptValue obj = engine->newObject();
|
||||
obj.setProperty("translation", vec3toScriptValue(engine, pose.translation));
|
||||
|
@ -37,7 +46,6 @@ namespace controller {
|
|||
obj.setProperty("velocity", vec3toScriptValue(engine, pose.velocity));
|
||||
obj.setProperty("angularVelocity", vec3toScriptValue(engine, pose.angularVelocity));
|
||||
obj.setProperty("valid", pose.valid);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// AbstractControllerScriptingInterface.h
|
||||
// libraries/script-engine/src
|
||||
// ScriptingInterface.h
|
||||
// libraries/controllers/src/controllers
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 12/17/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
|
@ -61,6 +61,8 @@ namespace controller {
|
|||
/// handles scripting of input controller commands from JS
|
||||
class ScriptingInterface : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
|
||||
// These properties have JSDoc in ControllerScriptingInterface.h.
|
||||
Q_PROPERTY(QVariantMap Hardware READ getHardware CONSTANT FINAL)
|
||||
Q_PROPERTY(QVariantMap Actions READ getActions CONSTANT FINAL)
|
||||
Q_PROPERTY(QVariantMap Standard READ getStandard CONSTANT FINAL)
|
||||
|
@ -69,42 +71,401 @@ namespace controller {
|
|||
ScriptingInterface();
|
||||
virtual ~ScriptingInterface() {};
|
||||
|
||||
/**jsdoc
|
||||
* Get a list of all available actions.
|
||||
* @function Controller.getAllActions
|
||||
* @returns {Action[]} All available actions.
|
||||
* @deprecated This function no longer works.
|
||||
*/
|
||||
// FIXME: This function causes a JavaScript crash: https://highfidelity.manuscript.com/f/cases/edit/13921
|
||||
Q_INVOKABLE QVector<Action> getAllActions();
|
||||
|
||||
/**jsdoc
|
||||
* Get 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.
|
||||
* @deprecated This function no longer works.
|
||||
*/
|
||||
// FIXME: This function causes a JavaScript crash: https://highfidelity.manuscript.com/f/cases/edit/13922
|
||||
Q_INVOKABLE QVector<Input::NamedPair> getAvailableInputs(unsigned int device);
|
||||
|
||||
/**jsdoc
|
||||
* Find 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>.
|
||||
* @example <caption>Get the name of the Oculus Touch controller from its ID.</caption>
|
||||
* var deviceID = Controller.findDevice("OculusTouch");
|
||||
* print("Device ID = " + deviceID);
|
||||
*
|
||||
* var deviceName = Controller.getDeviceName(deviceID);
|
||||
* print("Device name = " + deviceName);
|
||||
*/
|
||||
Q_INVOKABLE QString getDeviceName(unsigned int device);
|
||||
|
||||
/**jsdoc
|
||||
* Get 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.
|
||||
* @example <caption>Periodically report the value of the "TranslateX" action.</caption>
|
||||
* var actionID = Controller.findAction("TranslateX");
|
||||
*
|
||||
* function reportValue() {
|
||||
* print(Controller.getActionValue(actionID));
|
||||
* }
|
||||
* reportTimer = Script.setInterval(reportValue, 1000);
|
||||
*/
|
||||
Q_INVOKABLE float getActionValue(int action);
|
||||
|
||||
/**jsdoc
|
||||
* Find 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>.
|
||||
* @example <caption>Get the ID of the Oculus Touch.</caption>
|
||||
* var deviceID = Controller.findDevice("OculusTouch");
|
||||
* print("Device ID = " + deviceID);
|
||||
*/
|
||||
Q_INVOKABLE int findDevice(QString name);
|
||||
|
||||
/**jsdoc
|
||||
* Get 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>
|
||||
* var deviceNames = Controller.getDeviceNames();
|
||||
* print(JSON.stringify(deviceNames));
|
||||
* // ["Standard","Keyboard","LeapMotion","OculusTouch","Application","Actions"] or similar.
|
||||
*/
|
||||
Q_INVOKABLE QVector<QString> getDeviceNames();
|
||||
|
||||
/**jsdoc
|
||||
* Find 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
|
||||
* the same as the value of the relevant {@link Controller.Actions} property.
|
||||
* @example <caption>Get the ID of the "TranslateY" action. Compare with the property value.</caption>
|
||||
* var actionID = Controller.findAction("TranslateY");
|
||||
* print("Action ID = " + actionID); // 1
|
||||
* print("Property value = " + Controller.Actions.TranslateY); // 537001728 or similar value.
|
||||
*/
|
||||
Q_INVOKABLE int findAction(QString actionName);
|
||||
|
||||
/**jsdoc
|
||||
* Get 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>
|
||||
* var actionNames = Controller.getActionNames();
|
||||
* print("Action names: " + JSON.stringify(actionNames));
|
||||
* // ["TranslateX","TranslateY","TranslateZ","Roll", ...
|
||||
*/
|
||||
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.
|
||||
* @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
|
||||
* <code>0</code>.
|
||||
* @example <caption>Report the Standard and Vive right trigger values.</caption>
|
||||
* var triggerValue = Controller.getValue(Controller.Standard.RT);
|
||||
* print("Trigger value: " + triggerValue);
|
||||
*
|
||||
* if (Controller.Hardware.Vive) {
|
||||
* triggerValue = Controller.getValue(Controller.Hardware.Vive.RT);
|
||||
* print("Vive trigger value: " + triggerValue);
|
||||
* } else {
|
||||
* print("No Vive present");
|
||||
* }
|
||||
*/
|
||||
Q_INVOKABLE float getValue(const int& source) const;
|
||||
Q_INVOKABLE float getButtonValue(StandardButtonChannel source, uint16_t device = 0) const;
|
||||
Q_INVOKABLE float getAxisValue(StandardAxisChannel source, uint16_t device = 0) const;
|
||||
|
||||
/**jsdoc
|
||||
* Get 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
|
||||
* <code>0</code>.
|
||||
*/
|
||||
// TODO: getAxisValue() should use const int& parameter? Or others shouldn't?
|
||||
Q_INVOKABLE float getAxisValue(int source) const;
|
||||
|
||||
/**jsdoc
|
||||
* Get 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
|
||||
* an invalid pose with <code>Pose.valid == false</code>.
|
||||
* @exammple <caption>Report the right hand's pose.</caption>
|
||||
* var pose = Controller.getPoseValue(Controller.Standard.RightHand);
|
||||
* print("Pose: " + JSON.stringify(pose));
|
||||
*/
|
||||
Q_INVOKABLE Pose getPoseValue(const int& source) const;
|
||||
|
||||
/**jsdoc
|
||||
* Get the value of a button on a particular device.
|
||||
* @function Controller.getButtonValue
|
||||
* @param {StandardButtonChannel} source - The button to get the value of.
|
||||
* @param {number} [device=0] - The ID of the hardware device to get the value from. The default value of
|
||||
* <code>0</code> corresponds to <code>Standard</code>.
|
||||
* @returns {number} The current value of the button if the parameters are valid, otherwise <code>0</code>.
|
||||
* @deprecated This function no longer works.
|
||||
*/
|
||||
// FIXME: This function causes a JavaScript crash: https://highfidelity.manuscript.com/f/cases/edit/14139
|
||||
Q_INVOKABLE float getButtonValue(StandardButtonChannel source, uint16_t device = 0) const;
|
||||
|
||||
/**jsdoc
|
||||
* Get the value of an axis control on a particular device.
|
||||
* @function Controller.getAxisValue
|
||||
* @variation 0
|
||||
* @param {StandardAxisChannel} source - The axis to get the value of.
|
||||
* @param {number} [device=0] - The ID of the hardware device to get the value from. The default value of
|
||||
* <code>0</code> corresponds to <code>Standard</code>.
|
||||
* @returns {number} The current value of the axis if the parameters are valid, otherwise <code>0</code>.
|
||||
* @deprecated This function no longer works.
|
||||
*/
|
||||
Q_INVOKABLE float getAxisValue(StandardAxisChannel source, uint16_t device = 0) const;
|
||||
|
||||
/**jsdoc
|
||||
* Get the value of an pose control on a particular device.
|
||||
* @function Controller.getPoseValue
|
||||
* @variation 0
|
||||
* @param {StandardPoseChannel} source - The pose to get the value of.
|
||||
* @param {number} [device=0] - The ID of the hardware device to get the value from. The default value of
|
||||
* <code>0</code> corresponds to <code>Standard</code>.
|
||||
* @returns {Pose} The current value of the controller pose output if the parameters are valid, otherwise an invalid
|
||||
* pose with <code>Pose.valid == false</code>.
|
||||
* @deprecated This function no longer works.
|
||||
*/
|
||||
Q_INVOKABLE Pose getPoseValue(StandardPoseChannel source, uint16_t device = 0) const;
|
||||
|
||||
/**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} duration - The duration of the haptic pulse, in milliseconds.
|
||||
* @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;
|
||||
* var RIGHT_HAND = 1;
|
||||
* Controller.triggerHapticPulse(HAPTIC_STRENGTH, HAPTIC_DURATION, RIGHT_HAND);
|
||||
*/
|
||||
Q_INVOKABLE bool triggerHapticPulse(float strength, float duration, controller::Hand hand = BOTH) const;
|
||||
Q_INVOKABLE bool triggerShortHapticPulse(float strength, controller::Hand hand = BOTH) const;
|
||||
Q_INVOKABLE bool triggerHapticPulseOnDevice(unsigned int device, float strength, float duration, controller::Hand hand = BOTH) const;
|
||||
Q_INVOKABLE bool triggerShortHapticPulseOnDevice(unsigned int device, float strength, controller::Hand hand = BOTH) const;
|
||||
|
||||
/**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.
|
||||
*/
|
||||
Q_INVOKABLE bool triggerShortHapticPulse(float strength, controller::Hand hand = BOTH) const;
|
||||
|
||||
/**jsdoc
|
||||
* 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} duration - The duration of the haptic pulse, in milliseconds.
|
||||
* @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");
|
||||
* var HAPTIC_DURATION = 10;
|
||||
* var RIGHT_HAND = 1;
|
||||
* Controller.triggerHapticPulseOnDevice(deviceID, HAPTIC_STRENGTH, HAPTIC_DURATION, RIGHT_HAND);
|
||||
*/
|
||||
Q_INVOKABLE bool triggerHapticPulseOnDevice(unsigned int device, float strength, float duration,
|
||||
controller::Hand hand = BOTH) const;
|
||||
|
||||
/**jsdoc
|
||||
* 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.
|
||||
*/
|
||||
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
|
||||
* 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
|
||||
* 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>
|
||||
* var MAPPING_NAME = "com.highfidelity.controllers.example.newMapping";
|
||||
* var mapping = Controller.newMapping(MAPPING_NAME);
|
||||
*
|
||||
* mapping.from(Controller.Standard.RT).to(Controller.Actions.TranslateY);
|
||||
* Controller.enableMapping(MAPPING_NAME);
|
||||
*
|
||||
* Script.scriptEnding.connect(function () {
|
||||
* Controller.disableMapping(MAPPING_NAME);
|
||||
* });
|
||||
*/
|
||||
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.
|
||||
* @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.
|
||||
*/
|
||||
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.
|
||||
* @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
|
||||
* {@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}.
|
||||
* @returns {MappingObject} A controller mapping object.
|
||||
* @example <caption>Create a simple mapping that makes the right trigger move your avatar up.</caption>
|
||||
* var mappingJSON = {
|
||||
* "name": "com.highfidelity.controllers.example.jsonMapping",
|
||||
* "channels": [
|
||||
* { "from": "Standard.RT", "to": "Actions.TranslateY" }
|
||||
* ]
|
||||
* };
|
||||
*
|
||||
* var mapping = Controller.parseMapping(JSON.stringify(mappingJSON));
|
||||
* mapping.enable();
|
||||
*
|
||||
* Script.scriptEnding.connect(function () {
|
||||
* mapping.disable();
|
||||
* });
|
||||
*/
|
||||
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
|
||||
* {@link Controller.enableMapping|enableMapping} to enable the mapping for it to take effect.
|
||||
* @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}
|
||||
* property, <code>Controller.Hardware</code>.
|
||||
* @function Controller.getHardware
|
||||
* @returns {Controller.Hardware} The {@link Controller.Hardware} property tree.
|
||||
*/
|
||||
Q_INVOKABLE const QVariantMap getHardware() { return _hardware; }
|
||||
Q_INVOKABLE const QVariantMap getActions() { return _actions; }
|
||||
|
||||
/**jsdoc
|
||||
* Get 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.
|
||||
*/
|
||||
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}
|
||||
* property, <code>Controller.Standard</code>.
|
||||
* @function Controller.getStandard
|
||||
* @returns {Controller.Standard} The {@link Controller.Standard} property tree.
|
||||
*/
|
||||
Q_INVOKABLE const QVariantMap getStandard() { return _standard; }
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* Start making a recording of currently active controllers.
|
||||
* @function Controller.startInputRecording
|
||||
* @example <caption>Make a controller recording.</caption>
|
||||
* // Delay start of recording for 2s.
|
||||
* Script.setTimeout(function () {
|
||||
* print("Start input recording");
|
||||
* Controller.startInputRecording();
|
||||
* }, 2000);
|
||||
*
|
||||
* // Make a 10s recording.
|
||||
* Script.setTimeout(function () {
|
||||
* print("Stop input recording");
|
||||
* Controller.stopInputRecording();
|
||||
* Controller.saveInputRecording();
|
||||
* print("Input recording saved in: " + Controller.getInputRecorderSaveDirectory());
|
||||
* }, 12000);
|
||||
*/
|
||||
Q_INVOKABLE void startInputRecording();
|
||||
|
||||
/**jsdoc
|
||||
* Stop 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
|
||||
* {@link Controller.startInputRecording|startInputRecording} and
|
||||
* {@link Controller.stopInputRecording|stopInputRecording}, or loaded by
|
||||
* {@link Controller.loadInputRecording|loadInputRecording}. Playback repeats in a loop until
|
||||
* {@link Controller.stopInputPlayback|stopInputPlayback} is called.
|
||||
* @function Controller.startInputPlayback
|
||||
* @example <caption>Play back a controller recording.</caption>
|
||||
* var file = Window.browse("Select Recording", Controller.getInputRecorderSaveDirectory());
|
||||
* if (file !== null) {
|
||||
* print("Play recording: " + file);
|
||||
* Controller.loadInputRecording("file:///" + file);
|
||||
* Controller.startInputPlayback();
|
||||
*
|
||||
* // Stop playback after 20s.
|
||||
* Script.setTimeout(function () {
|
||||
* print("Stop playing recording");
|
||||
* Controller.stopInputPlayback();
|
||||
* }, 20000);
|
||||
* }
|
||||
*/
|
||||
Q_INVOKABLE void startInputPlayback();
|
||||
|
||||
/**jsdoc
|
||||
* Stop 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
|
||||
* {@link Controller.startInputRecording|startInputRecording} and
|
||||
* {@link Controller.stopInputRecording|stopInputRecording}, or loaded by
|
||||
* {@link Controller.loadInputRecording|loadInputRecording}. It is saved in the directory returned by
|
||||
* {@link Controller.getInputRecorderSaveDirectory|getInputRecorderSaveDirectory}.
|
||||
* @function Controller.saveInputRecording
|
||||
*/
|
||||
Q_INVOKABLE void saveInputRecording();
|
||||
|
||||
/**jsdoc
|
||||
* Load 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.
|
||||
* @function Controller.getInputRecorderSaveDirectory
|
||||
* @returns {string} The directory in which input recordings are saved.
|
||||
*/
|
||||
Q_INVOKABLE QString getInputRecorderSaveDirectory();
|
||||
|
||||
bool isMouseCaptured() const { return _mouseCaptured; }
|
||||
|
@ -114,21 +475,156 @@ namespace controller {
|
|||
|
||||
public slots:
|
||||
|
||||
/**jsdoc
|
||||
* Disable 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>
|
||||
* var MAPPING_NAME = "com.highfidelity.controllers.example.newMapping";
|
||||
* var mapping = Controller.newMapping(MAPPING_NAME);
|
||||
* mapping.from(Controller.Hardware.Keyboard.MouseX).to(function (x) {
|
||||
* print("Mouse x = " + x);
|
||||
* });
|
||||
* mapping.from(Controller.Hardware.Keyboard.MouseY).to(function (y) {
|
||||
* print("Mouse y = " + y);
|
||||
* });
|
||||
* Controller.enableMapping(MAPPING_NAME);
|
||||
* Script.scriptEnding.connect(function () {
|
||||
* Controller.disableMapping(MAPPING_NAME);
|
||||
* });
|
||||
*
|
||||
* Script.setTimeout(function () {
|
||||
* Controller.captureMouseEvents();
|
||||
* }, 5000);
|
||||
*
|
||||
* Script.setTimeout(function () {
|
||||
* Controller.releaseMouseEvents();
|
||||
* }, 10000);
|
||||
*/
|
||||
virtual void captureMouseEvents() { _mouseCaptured = true; }
|
||||
|
||||
/**jsdoc
|
||||
* Enable 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
|
||||
*/
|
||||
virtual void releaseMouseEvents() { _mouseCaptured = false; }
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* Disable 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.
|
||||
* @function Controller.captureTouchEvents
|
||||
*/
|
||||
virtual void captureTouchEvents() { _touchCaptured = true; }
|
||||
|
||||
/**jsdoc
|
||||
* Enable 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
|
||||
* {@link Controller.captureTouchEvents|captureTouchEvents}.
|
||||
* @function Controller.releaseTouchEvents
|
||||
*/
|
||||
virtual void releaseTouchEvents() { _touchCaptured = false; }
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* Disable 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}
|
||||
* outputs that wer disabled using {@link Controller.captureWheelEvents|captureWheelEvents}.
|
||||
* @function Controller.releaseWheelEvents
|
||||
*/
|
||||
virtual void releaseWheelEvents() { _wheelCaptured = false; }
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* Disable 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 () {
|
||||
* Controller.captureActionEvents();
|
||||
* }, 5000);
|
||||
*
|
||||
* Script.setTimeout(function () {
|
||||
* Controller.releaseActionEvents();
|
||||
* }, 10000);
|
||||
*/
|
||||
virtual void captureActionEvents() { _actionsCaptured = true; }
|
||||
|
||||
/**jsdoc
|
||||
* Enable 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; }
|
||||
|
||||
signals:
|
||||
/**jsdoc
|
||||
* Triggered when an action occurs.
|
||||
* @function Controller.actionEvent
|
||||
* @param {number} actionID - The ID of the action, per {@link Controller.findAction|findAction}.
|
||||
* @param {number} value - The value associated with the action.
|
||||
* @returns {Signal}
|
||||
* @example <caption>Report action events as they occur.</caption>
|
||||
* var actionNamesForID = {};
|
||||
* var actionNames = Controller.getActionNames();
|
||||
* for (var i = 0, length = actionNames.length; i < length; i++) {
|
||||
* actionNamesForID[Controller.findAction(actionNames[i])] = actionNames[i];
|
||||
* }
|
||||
*
|
||||
* function onActionEvent(action, value) {
|
||||
* print("onActionEvent() : " + action + " ( " + actionNamesForID[action] + " ) ; " + value);
|
||||
* }
|
||||
*
|
||||
* Controller.actionEvent.connect(onActionEvent);
|
||||
*
|
||||
* Script.scriptEnding.connect(function () {
|
||||
* Controller.actionEvent.disconnect(onActionEvent);
|
||||
* });
|
||||
*/
|
||||
void actionEvent(int action, float state);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when there is a new controller input event.
|
||||
* @function Controller.inputEvent
|
||||
* @param {number} action - The input action, per {@link Controller.Standard}.
|
||||
* @param {number} value - The value associated with the input action.
|
||||
* @returns {Signal}
|
||||
* @example <caption>Report input events as they occur.</caption>
|
||||
* var inputNamesForID = {};
|
||||
* for (var property in Controller.Standard) {
|
||||
* inputNamesForID[Controller.Standard[property]] = "Controller.Standard." + property;
|
||||
* }
|
||||
*
|
||||
* function onInputEvent(input, value) {
|
||||
* print("onInputEvent() : " + input + " ( " + inputNamesForID[input] + " ) ; " + value);
|
||||
* }
|
||||
*
|
||||
* Controller.inputEvent.connect(onInputEvent);
|
||||
*
|
||||
* Script.scriptEnding.connect(function () {
|
||||
* Controller.inputEvent.disconnect(onInputEvent);
|
||||
* });
|
||||
*/
|
||||
void inputEvent(int action, float state);
|
||||
|
||||
/**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
|
||||
* connecting or disconnecting an Xbox controller will.
|
||||
* @function Controller.hardwareChanged
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void hardwareChanged();
|
||||
|
||||
private:
|
||||
|
@ -145,8 +641,6 @@ namespace controller {
|
|||
std::atomic<bool> _actionsCaptured { false };
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // hifi_AbstractControllerScriptingInterface_h
|
||||
|
|
|
@ -27,6 +27,212 @@ void StandardController::focusOutEvent() {
|
|||
_buttonPressedMap.clear();
|
||||
};
|
||||
|
||||
/**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>
|
||||
*
|
||||
* <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>A</code></td><td>number</td><td>number</td><td>"A" button pressed.</td></tr>
|
||||
* <tr><td><code>B</code></td><td>number</td><td>number</td><td>"B" button pressed.</td></tr>
|
||||
* <tr><td><code>X</code></td><td>number</td><td>number</td><td>"X" button pressed.</td></tr>
|
||||
* <tr><td><code>Y</code></td><td>number</td><td>number</td><td>"Y" button pressed.</td></tr>
|
||||
* <tr><td><code>DL</code></td><td>number</td><td>number</td><td>D-pad left pressed.</td></tr>
|
||||
* <tr><td><code>DR</code></td><td>number</td><td>number</td><td>D-pad right pressed.</td></tr>
|
||||
* <tr><td><code>DU</code></td><td>number</td><td>number</td><td>D-pad up pressed.</td></tr>
|
||||
* <tr><td><code>DD</code></td><td>number</td><td>number</td><td>D-pad down pressed.</td></tr>
|
||||
* <tr><td><code>Start</code></td><td>number</td><td>number</td><td>"Start" center button pressed.</td></tr>
|
||||
* <tr><td><code>Back</code></td><td>number</td><td>number</td><td>"Back" center button pressed.</td></tr>
|
||||
* <tr><td><code>LB</code></td><td>number</td><td>number</td><td>Left bumper button pressed.</td></tr>
|
||||
* <tr><td><code>RB</code></td><td>number</td><td>number</td><td>Right bumper button pressed.</td></tr>
|
||||
*
|
||||
* <tr><td colspan="4"><strong>Sticks</strong></td></tr>
|
||||
* <tr><td><code>LX</code></td><td>number</td><td>number</td><td>Left stick x-axis scale.</td></tr>
|
||||
* <tr><td><code>LY</code></td><td>number</td><td>number</td><td>Left stick y-axis scale.</td></tr>
|
||||
* <tr><td><code>RX</code></td><td>number</td><td>number</td><td>Right stick x-axis scale.</td></tr>
|
||||
* <tr><td><code>RY</code></td><td>number</td><td>number</td><td>Right stick y-axis scale.</td></tr>
|
||||
* <tr><td><code>LS</code></td><td>number</td><td>number</td><td>Left stick button pressed.</td></tr>
|
||||
* <tr><td><code>RS</code></td><td>number</td><td>number</td><td>Right stick button pressed.</td></tr>
|
||||
* <tr><td><code>LSTouch</code></td><td>number</td><td>number</td><td>Left stick is touched.</td></tr>
|
||||
* <tr><td><code>RSTouch</code></td><td>number</td><td>number</td><td>Right stick is touched.</td></tr>
|
||||
*
|
||||
* <tr><td colspan="4"><strong>Triggers</strong></td></tr>
|
||||
* <tr><td><code>LT</code></td><td>number</td><td>number</td><td>Left trigger scale.</td></tr>
|
||||
* <tr><td><code>RT</code></td><td>number</td><td>number</td><td>Right trigger scale.</td></tr>
|
||||
* <tr><td><code>LTClick</code></td><td>number</td><td>number</td><td>Left trigger click.</td></tr>
|
||||
* <tr><td><code>RTClick</code></td><td>number</td><td>number</td><td>Right trigger click.</td></tr>
|
||||
* <tr><td><code>LeftGrip</code></td><td>number</td><td>number</td><td>Left grip scale.</td></tr>
|
||||
* <tr><td><code>RightGrip</code></td><td>number</td><td>number</td><td>Right grip scale.</td></tr>
|
||||
* <tr><td><code>LeftGripTouch</code></td><td>number</td><td>number</td><td>Left grip is touched.</td></tr>
|
||||
* <tr><td><code>RightGripTouch</code></td><td>number</td><td>number</td><td>Right grip is touched.</td></tr>
|
||||
*
|
||||
* <tr><td colspan="4"><strong>Aliases, PlayStation Style Names</strong></td></tr>
|
||||
* <tr><td><code>Cross</code></td><td>number</td><td>number</td><td>Alias for <code>A</code>.</td></tr>
|
||||
* <tr><td><code>Circle</code></td><td>number</td><td>number</td><td>Alias for <code>B</code>.</td></tr>
|
||||
* <tr><td><code>Square</code></td><td>number</td><td>number</td><td>Alias for <code>X</code>.</td></tr>
|
||||
* <tr><td><code>Triangle</code></td><td>number</td><td>number</td><td>Alias for <code>Y</code>.</td></tr>
|
||||
* <tr><td><code>Left</code></td><td>number</td><td>number</td><td>Alias for <code>DL</code>.</td></tr>
|
||||
* <tr><td><code>Right</code></td><td>number</td><td>number</td><td>Alias for <code>DR</code>.</td></tr>
|
||||
* <tr><td><code>Up</code></td><td>number</td><td>number</td><td>Alias for <code>DU</code>.</td></tr>
|
||||
* <tr><td><code>Down</code></td><td>number</td><td>number</td><td>Alias for <code>DD</code>.</td></tr>
|
||||
* <tr><td><code>Select</code></td><td>number</td><td>number</td><td>Alias for <code>Back</code>.</td></tr>
|
||||
* <tr><td><code>L1</code></td><td>number</td><td>number</td><td>Alias for <code>LB</code>.</td></tr>
|
||||
* <tr><td><code>R1</code></td><td>number</td><td>number</td><td>Alias for <code>RB</code>.</td></tr>
|
||||
* <tr><td><code>L3</code></td><td>number</td><td>number</td><td>Alias for <code>LS</code>.</td></tr>
|
||||
* <tr><td><code>R3</code></td><td>number</td><td>number</td><td>Alias for <code>RS</code>.</td></tr>
|
||||
* <tr><td><code>L2</code></td><td>number</td><td>number</td><td>Alias for <code>LT</code>.</td></tr>
|
||||
* <tr><td><code>R2</code></td><td>number</td><td>number</td><td>Alias for <code>RT</code>.</td></tr>
|
||||
*
|
||||
* <tr><td colspan="4"><strong>Finger Abstractions</strong></td></tr>
|
||||
* <tr><td><code>LeftPrimaryThumb</code></td><td>number</td><td>number</td><td>Left primary thumb button pressed.</td></tr>
|
||||
* <tr><td><code>LeftSecondaryThumb</code></td><td>number</td><td>number</td><td>Left secondary thumb button pressed.
|
||||
* </td></tr>
|
||||
* <tr><td><code>RightPrimaryThumb</code></td><td>number</td><td>number</td><td>Right primary thumb button pressed.
|
||||
* </td></tr>
|
||||
* <tr><td><code>RightSecondaryThumb</code></td><td>number</td><td>number</td><td>Right secondary thumb button pressed.
|
||||
* </td></tr>
|
||||
* <tr><td><code>LeftPrimaryThumbTouch</code></td><td>number</td><td>number</td><td>Left thumb touching primary thumb
|
||||
* button.</td></tr>
|
||||
* <tr><td><code>LeftSecondaryThumbTouch</code></td><td>number</td><td>number</td><td>Left thumb touching secondary thumb
|
||||
* button.</td></tr>
|
||||
* <tr><td><code>LeftThumbUp</code></td><td>number</td><td>number</td><td>Left thumb not touching primary or secondary
|
||||
* thumb buttons.</td></tr>
|
||||
* <tr><td><code>RightPrimaryThumbTouch</code></td><td>number</td><td>number</td><td>Right thumb touching primary thumb
|
||||
* button.</td></tr>
|
||||
* <tr><td><code>RightSecondaryThumbTouch</code></td><td>number</td><td>number</td><td>Right thumb touching secondary thumb
|
||||
* 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>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>
|
||||
* <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
|
||||
* index finger control.</td></tr>
|
||||
* <tr><td><code>LeftSecondaryIndexTouch</code></td><td>number</td><td>number</td><td>Left index finger is touching
|
||||
* secondary index finger control.</td></tr>
|
||||
* <tr><td><code>LeftIndexPoint</code></td><td>number</td><td>number</td><td>Left index finger is pointing, not touching
|
||||
* primary or secondary index finger controls.</td></tr>
|
||||
* <tr><td><code>RightPrimaryIndexTouch</code></td><td>number</td><td>number</td><td>Right index finger is touching primary
|
||||
* index finger control.</td></tr>
|
||||
* <tr><td><code>RightSecondaryIndexTouch</code></td><td>number</td><td>number</td><td>Right index finger is touching
|
||||
* secondary index finger control.</td></tr>
|
||||
* <tr><td><code>RightIndexPoint</code></td><td>number</td><td>number</td><td>Right index finger is pointing, not touching
|
||||
* primary or secondary index finger controls.</td></tr>
|
||||
*
|
||||
* <tr><td colspan="4"><strong>Avatar Skeleton</strong></td></tr>
|
||||
* <tr><td><code>Hips</code></td><td>number</td><td>{@link Pose}</td><td>Hips pose.</td></tr>
|
||||
* <tr><td><code>Spine2</code></td><td>number</td><td>{@link Pose}</td><td>Spine2 pose.</td></tr>
|
||||
* <tr><td><code>Head</code></td><td>number</td><td>{@link Pose}</td><td>Head pose.</td></tr>
|
||||
* <tr><td><code>LeftArm</code></td><td>number</td><td>{@link Pose}</td><td>Left arm pose.</td></tr>
|
||||
* <tr><td><code>RightArm</code></td><td>number</td><td>{@link Pose}</td><td>Right arm pose</td></tr>
|
||||
* <tr><td><code>LeftHand</code></td><td>number</td><td>{@link Pose}</td><td>Left hand pose.</td></tr>
|
||||
* <tr><td><code>LeftHandThumb1</code></td><td>number</td><td>{@link Pose}</td><td>Left thumb 1 finger joint pose.</td></tr>
|
||||
* <tr><td><code>LeftHandThumb2</code></td><td>number</td><td>{@link Pose}</td><td>Left thumb 2 finger joint pose.</td></tr>
|
||||
* <tr><td><code>LeftHandThumb3</code></td><td>number</td><td>{@link Pose}</td><td>Left thumb 3 finger joint pose.</td></tr>
|
||||
* <tr><td><code>LeftHandThumb4</code></td><td>number</td><td>{@link Pose}</td><td>Left thumb 4 finger joint pose.</td></tr>
|
||||
* <tr><td><code>LeftHandIndex1</code></td><td>number</td><td>{@link Pose}</td><td>Left index 1 finger joint pose.</td></tr>
|
||||
* <tr><td><code>LeftHandIndex2</code></td><td>number</td><td>{@link Pose}</td><td>Left index 2 finger joint pose.</td></tr>
|
||||
* <tr><td><code>LeftHandIndex3</code></td><td>number</td><td>{@link Pose}</td><td>Left index 3 finger joint pose.</td></tr>
|
||||
* <tr><td><code>LeftHandIndex4</code></td><td>number</td><td>{@link Pose}</td><td>Left index 4 finger joint pose.</td></tr>
|
||||
* <tr><td><code>LeftHandMiddle1</code></td><td>number</td><td>{@link Pose}</td><td>Left middle 1 finger joint pose.
|
||||
* </td></tr>
|
||||
* <tr><td><code>LeftHandMiddle2</code></td><td>number</td><td>{@link Pose}</td><td>Left middle 2 finger joint pose.
|
||||
* </td></tr>
|
||||
* <tr><td><code>LeftHandMiddle3</code></td><td>number</td><td>{@link Pose}</td><td>Left middle 3 finger joint pose.
|
||||
* </td></tr>
|
||||
* <tr><td><code>LeftHandMiddle4</code></td><td>number</td><td>{@link Pose}</td><td>Left middle 4 finger joint pose.
|
||||
* </td></tr>
|
||||
* <tr><td><code>LeftHandRing1</code></td><td>number</td><td>{@link Pose}</td><td>Left ring 1 finger joint pose.</td></tr>
|
||||
* <tr><td><code>LeftHandRing2</code></td><td>number</td><td>{@link Pose}</td><td>Left ring 2 finger joint pose.</td></tr>
|
||||
* <tr><td><code>LeftHandRing3</code></td><td>number</td><td>{@link Pose}</td><td>Left ring 3 finger joint pose.</td></tr>
|
||||
* <tr><td><code>LeftHandRing4</code></td><td>number</td><td>{@link Pose}</td><td>Left ring 4 finger joint pose.</td></tr>
|
||||
* <tr><td><code>LeftHandPinky1</code></td><td>number</td><td>{@link Pose}</td><td>Left pinky 1 finger joint pose.</td></tr>
|
||||
* <tr><td><code>LeftHandPinky2</code></td><td>number</td><td>{@link Pose}</td><td>Left pinky 2 finger joint pose.</td></tr>
|
||||
* <tr><td><code>LeftHandPinky3</code></td><td>number</td><td>{@link Pose}</td><td>Left pinky 3 finger joint pose.</td></tr>
|
||||
* <tr><td><code>LeftHandPinky4</code></td><td>number</td><td>{@link Pose}</td><td>Left pinky 4 finger joint pose.</td></tr>
|
||||
* <tr><td><code>RightHand</code></td><td>number</td><td>{@link Pose}</td><td>Right hand pose.</td></tr>
|
||||
* <tr><td><code>RightHandThumb1</code></td><td>number</td><td>{@link Pose}</td><td>Right thumb 1 finger joint pose.
|
||||
* </td></tr>
|
||||
* <tr><td><code>RightHandThumb2</code></td><td>number</td><td>{@link Pose}</td><td>Right thumb 2 finger joint pose.
|
||||
* </td></tr>
|
||||
* <tr><td><code>RightHandThumb3</code></td><td>number</td><td>{@link Pose}</td><td>Right thumb 3 finger joint pose.
|
||||
* </td></tr>
|
||||
* <tr><td><code>RightHandThumb4</code></td><td>number</td><td>{@link Pose}</td><td>Right thumb 4 finger joint pose.
|
||||
* </td></tr>
|
||||
* <tr><td><code>RightHandIndex1</code></td><td>number</td><td>{@link Pose}</td><td>Right index 1 finger joint pose.
|
||||
* </td></tr>
|
||||
* <tr><td><code>RightHandIndex2</code></td><td>number</td><td>{@link Pose}</td><td>Right index 2 finger joint pose.
|
||||
* </td></tr>
|
||||
* <tr><td><code>RightHandIndex3</code></td><td>number</td><td>{@link Pose}</td><td>Right index 3 finger joint pose.
|
||||
* </td></tr>
|
||||
* <tr><td><code>RightHandIndex4</code></td><td>number</td><td>{@link Pose}</td><td>Right index 4 finger joint pose.
|
||||
* </td></tr>
|
||||
* <tr><td><code>RightHandMiddle1</code></td><td>number</td><td>{@link Pose}</td><td>Right middle 1 finger joint pose.
|
||||
* </td></tr>
|
||||
* <tr><td><code>RightHandMiddle2</code></td><td>number</td><td>{@link Pose}</td><td>Right middle 2 finger joint pose.
|
||||
* </td></tr>
|
||||
* <tr><td><code>RightHandMiddle3</code></td><td>number</td><td>{@link Pose}</td><td>Right middle 3 finger joint pose.
|
||||
* </td></tr>
|
||||
* <tr><td><code>RightHandMiddle4</code></td><td>number</td><td>{@link Pose}</td><td>Right middle 4 finger joint pose.
|
||||
* </td></tr>
|
||||
* <tr><td><code>RightHandRing1</code></td><td>number</td><td>{@link Pose}</td><td>Right ring 1 finger joint pose.</td></tr>
|
||||
* <tr><td><code>RightHandRing2</code></td><td>number</td><td>{@link Pose}</td><td>Right ring 2 finger joint pose.</td></tr>
|
||||
* <tr><td><code>RightHandRing3</code></td><td>number</td><td>{@link Pose}</td><td>Right ring 3 finger joint pose.</td></tr>
|
||||
* <tr><td><code>RightHandRing4</code></td><td>number</td><td>{@link Pose}</td><td>Right ring 4 finger joint pose.</td></tr>
|
||||
* <tr><td><code>RightHandPinky1</code></td><td>number</td><td>{@link Pose}</td><td>Right pinky 1 finger joint pose.
|
||||
* </td></tr>
|
||||
* <tr><td><code>RightHandPinky2</code></td><td>number</td><td>{@link Pose}</td><td>Right pinky 2 finger joint pose.
|
||||
* </td></tr>
|
||||
* <tr><td><code>RightHandPinky3</code></td><td>number</td><td>{@link Pose}</td><td>Right pinky 3 finger joint pose.
|
||||
* </td></tr>
|
||||
* <tr><td><code>RightHandPinky4</code></td><td>number</td><td>{@link Pose}</td><td>Right pinky 4 finger joint pose.
|
||||
* </td></tr>
|
||||
* <tr><td><code>LeftFoot</code></td><td>number</td><td>{@link Pose}</td><td>Left foot pose.</td></tr>
|
||||
* <tr><td><code>RightFoot</code></td><td>number</td><td>{@link Pose}</td><td>Right foot pose.</td></tr>
|
||||
*
|
||||
* <tr><td colspan="4"><strong>Trackers</strong></td></tr>
|
||||
* <tr><td><code>TrackedObject00</code></td><td>number</td><td>{@link Pose}</td><td>Tracker 0 pose.</td></tr>
|
||||
* <tr><td><code>TrackedObject01</code></td><td>number</td><td>{@link Pose}</td><td>Tracker 1 pose.</td></tr>
|
||||
* <tr><td><code>TrackedObject02</code></td><td>number</td><td>{@link Pose}</td><td>Tracker 2 pose.</td></tr>
|
||||
* <tr><td><code>TrackedObject03</code></td><td>number</td><td>{@link Pose}</td><td>Tracker 3 pose.</td></tr>
|
||||
* <tr><td><code>TrackedObject04</code></td><td>number</td><td>{@link Pose}</td><td>Tracker 4 pose.</td></tr>
|
||||
* <tr><td><code>TrackedObject05</code></td><td>number</td><td>{@link Pose}</td><td>Tracker 5 pose.</td></tr>
|
||||
* <tr><td><code>TrackedObject06</code></td><td>number</td><td>{@link Pose}</td><td>Tracker 6 pose.</td></tr>
|
||||
* <tr><td><code>TrackedObject07</code></td><td>number</td><td>{@link Pose}</td><td>Tracker 7 pose.</td></tr>
|
||||
* <tr><td><code>TrackedObject08</code></td><td>number</td><td>{@link Pose}</td><td>Tracker 8 pose.</td></tr>
|
||||
* <tr><td><code>TrackedObject09</code></td><td>number</td><td>{@link Pose}</td><td>Tracker 9 pose.</td></tr>
|
||||
* <tr><td><code>TrackedObject10</code></td><td>number</td><td>{@link Pose}</td><td>Tracker 10 pose.</td></tr>
|
||||
* <tr><td><code>TrackedObject11</code></td><td>number</td><td>{@link Pose}</td><td>Tracker 11 pose.</td></tr>
|
||||
* <tr><td><code>TrackedObject12</code></td><td>number</td><td>{@link Pose}</td><td>Tracker 12 pose.</td></tr>
|
||||
* <tr><td><code>TrackedObject13</code></td><td>number</td><td>{@link Pose}</td><td>Tracker 13 pose.</td></tr>
|
||||
* <tr><td><code>TrackedObject14</code></td><td>number</td><td>{@link Pose}</td><td>Tracker 14 pose.</td></tr>
|
||||
* <tr><td><code>TrackedObject15</code></td><td>number</td><td>{@link Pose}</td><td>Tracker 15 pose.</td></tr>
|
||||
*
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef Controller.Standard
|
||||
*/
|
||||
Input::NamedVector StandardController::getAvailableInputs() const {
|
||||
static Input::NamedVector availableInputs {
|
||||
// Buttons
|
||||
|
|
|
@ -24,6 +24,101 @@ namespace controller {
|
|||
class ScriptingInterface;
|
||||
class UserInputMapper;
|
||||
|
||||
/**jsdoc
|
||||
* <p>A {@link Controller} mapping object that can contain a set of routes that map:</p>
|
||||
* <ul>
|
||||
* <li>{@link Controller.Standard} outputs to {@link Controller.Actions} actions or script functions.</li>
|
||||
* <li>{@link Controller.Hardware} outputs to {@link Controller.Standard} outputs, {@link Controller.Actions} actions, or
|
||||
* script functions.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Create by one of the following methods:</p>
|
||||
* <ul>
|
||||
* <li>Use {@link Controller.newMapping} to create the mapping object, add routes using {@link MappingObject#from|from} or
|
||||
* {@link MappingObject#makeAxis|makeAxis}, and map the routes to actions or functions using {@link RouteObject}
|
||||
* methods.</li>
|
||||
* <li>Use {@link Controller.parseMapping} or {@link Controller.loadMapping} to load a {@link Controller.MappingJSON}.</li>
|
||||
* </ul>
|
||||
* <p>Enable the mapping using {@link MappingObject#enable|enable} or {@link Controller.enableMapping} for it to take effect.
|
||||
*
|
||||
* <p>Mappings and their routes are applied according to the following rules:</p>
|
||||
* <ul>
|
||||
* <li>One read per output: after a controller output has been read, it can't be read again. Exception: You can use
|
||||
* {@link RouteObject#peek} to read a value without marking that output as having been read.</li>
|
||||
* <li>Existing mapping routes take precedence over new mapping routes: within a mapping, if a route is added for a control
|
||||
* output that already has a route the new route is ignored.</li>
|
||||
* <li>New mappings override previous mappings: each output is processed using the route in the most recently enabled
|
||||
* mapping that contains that output.</li>
|
||||
* </p>
|
||||
*
|
||||
* @class MappingObject
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* A {@link MappingObject} can be specified in JSON format. A simple example is provided below. Full examples — the
|
||||
* default mappings provided in Interface — can be found at
|
||||
* <a href="https://github.com/highfidelity/hifi/tree/master/interface/resources/controllers">
|
||||
* https://github.com/highfidelity/hifi/tree/master/interface/resources/controllers</a>.
|
||||
* @typedef {object} Controller.MappingJSON
|
||||
* @property {string} name - The name of the mapping.
|
||||
* @property {Controller.MappingJSONRoute[]} channels - An array of routes.
|
||||
* @example <caption>A simple mapping JSON that makes the right trigger move your avatar up after a dead zone.</caption>
|
||||
* {
|
||||
* "name": "com.highfidelity.controllers.example.jsonMapping",
|
||||
* "channels": [
|
||||
* {
|
||||
* "from": "Standard.RT",
|
||||
* "filters": { "type": "deadZone", "min": 0.05 },
|
||||
* "to": "Actions.TranslateY"
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
*/
|
||||
|
||||
/**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|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
|
||||
* <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
|
||||
* <code>"Controller."</code> can be omitted.
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* An axis pair in a {@link Controller.MappingJSONRoute}.
|
||||
* @typedef {object} Controller.MappingJSONAxis
|
||||
* @property {string[][]} makeAxis - A two-member array of single-member arrays of {@link Controller.Hardware} property names.
|
||||
* The leading <code>"Controller.Hardware."</code> can be omitted from the property names.
|
||||
* @example <caption>An axis using the keyboard's left and right keys.</caption>
|
||||
* { "makeAxis" : [
|
||||
* ["Keyboard.Left"],
|
||||
* ["Keyboard.Right"]
|
||||
* ]
|
||||
* }
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* A filter in a {@link Controller.MappingJSONRoute}.
|
||||
* @typedef {object} Controller.MappingJSONFilter
|
||||
* @property {string} type - The name of the filter, being the name of the one of the {@link RouteObject}'s filter methods.
|
||||
* @property {string} [?] - If the filter method has a first parameter, the property name is the name of that parameter and the
|
||||
* property value is the value to use.
|
||||
* @property {string} [?] - If the filter method has a second parameter, the property name is the name of that parameter and
|
||||
* the property value is the value to use.
|
||||
* @example <caption>A hysteresis filter.</caption>
|
||||
* {
|
||||
* "type": "hysteresis",
|
||||
* "min": 0.85,
|
||||
* "max": 0.9
|
||||
* }
|
||||
*/
|
||||
|
||||
// TODO migrate functionality to a MappingBuilder class and make the proxy defer to that
|
||||
// (for easier use in both C++ and JS)
|
||||
class MappingBuilderProxy : public QObject {
|
||||
|
@ -32,13 +127,76 @@ public:
|
|||
MappingBuilderProxy(UserInputMapper& parent, Mapping::Pointer mapping)
|
||||
: _parent(parent), _mapping(mapping) { }
|
||||
|
||||
/**jsdoc
|
||||
* Create 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
|
||||
* @param {Controller.Standard|Controller.Hardware|function} source - The controller output or function that is the source
|
||||
* of the route data. If a function, it must return a number or a {@link Pose} value as the route data.
|
||||
* @returns {RouteObject} A route ready for mapping to an action or function using {@link RouteObject} methods.
|
||||
*/
|
||||
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
|
||||
* 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
|
||||
* @param {Controller.Hardware} source1 - The first, negative-direction controller output.
|
||||
* @param {Controller.Hardware} source2 - The second, positive-direction controller output.
|
||||
* @returns {RouteObject} A route ready for mapping to an action or function using {@link RouteObject} methods. The data
|
||||
* value passed to the route is the combined value of <code>source2 - source1</code>.
|
||||
*/
|
||||
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
|
||||
* function.
|
||||
* @function MappingObject#from
|
||||
* @param {Controller.Standard|Controller.Hardware|function} source - The controller output or function that is the source
|
||||
* of the route data. If a function, it must return a number or a {@link Pose} value as the route data.
|
||||
* @returns {RouteObject} A route ready for mapping to an action or function using {@link RouteObject} methods.
|
||||
*/
|
||||
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
|
||||
* 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.
|
||||
* @param {Controller.Hardware} source2 - The second, positive-direction controller output.
|
||||
* @returns {RouteObject} A route ready for mapping to an action or function using {@link RouteObject} methods. The data
|
||||
* value passed to the route is the combined value of <code>source2 - source1</code>.
|
||||
* @example <caption>Make the Oculus Touch triggers move your avatar up and down.</caption>
|
||||
* var MAPPING_NAME = "com.highfidelity.controllers.example.newMapping";
|
||||
* var mapping = Controller.newMapping(MAPPING_NAME);
|
||||
* mapping
|
||||
* .makeAxis(Controller.Hardware.OculusTouch.LT, Controller.Hardware.OculusTouch.RT)
|
||||
* .to(Controller.Actions.Up);
|
||||
* Controller.enableMapping(MAPPING_NAME);
|
||||
*
|
||||
* Script.scriptEnding.connect(function () {
|
||||
* Controller.disableMapping(MAPPING_NAME);
|
||||
* });
|
||||
*/
|
||||
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 />
|
||||
* 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.
|
||||
* @returns {MappingObject} The mapping object, so that further routes can be added.
|
||||
*/
|
||||
Q_INVOKABLE QObject* enable(bool enable = true);
|
||||
|
||||
/**jsdoc
|
||||
* Disable 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.
|
||||
*/
|
||||
Q_INVOKABLE QObject* disable() { return enable(false); }
|
||||
|
||||
protected:
|
||||
|
|
|
@ -25,6 +25,18 @@ namespace controller {
|
|||
|
||||
class ScriptingInterface;
|
||||
|
||||
/**jsdoc
|
||||
* <p>A route in a {@link MappingObject} used by the {@link Controller} API.</p>
|
||||
*
|
||||
* <p>Create a route using {@link MappingObject} methods and apply this object's methods to process it, terminating with
|
||||
* {@link RouteObject#to} to apply it to a <code>Standard</code> control, action, or script function.</p>
|
||||
*
|
||||
* <p>Some methods apply to routes with number data, some apply routes with {@link Pose} data, and some apply to both route
|
||||
* types.<p>
|
||||
*
|
||||
* @class RouteObject
|
||||
*/
|
||||
|
||||
// TODO migrate functionality to a RouteBuilder class and make the proxy defer to that
|
||||
// (for easier use in both C++ and JS)
|
||||
class RouteBuilderProxy : public QObject {
|
||||
|
@ -33,27 +45,404 @@ class RouteBuilderProxy : public QObject {
|
|||
RouteBuilderProxy(UserInputMapper& parent, Mapping::Pointer mapping, Route::Pointer route)
|
||||
: _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
|
||||
* 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
|
||||
* @param {Controller.Standard|Controller.Actions|function} destination - The standard control, action, or JavaScript
|
||||
* function that the route output is mapped to. For a function, the parameter can be either the name of the function or
|
||||
* an in-line function definition.
|
||||
*/
|
||||
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
|
||||
* 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.
|
||||
* @function RouteObject#whenQml
|
||||
* @param {condition|condition[]} expression - <p>A <code>condition</code> may be a:</p>
|
||||
* <ul>
|
||||
* <li>A boolean or numeric {@link Controller.Hardware} property, which is evaluated as a boolean.</li>
|
||||
* <li><code>!</code> followed by a {@link Controller.Hardware} property, indicating the logical NOT should be
|
||||
* used.</li>
|
||||
* <li>A script function returning a boolean value. This can be either the name of the function or an in-line
|
||||
* definition.</li>
|
||||
* </ul>
|
||||
* <p>If an array of conditions is provided, their values are ANDed together.</p>
|
||||
* @returns {RouteObject} The <code>RouteObject</code> with the condition added.
|
||||
*/
|
||||
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
|
||||
* sent to the specified destination.
|
||||
* @function RouteObject#to
|
||||
* @param {Controller.Standard|Controller.Actions|function} destination - The standard control, action, or JavaScript
|
||||
* function that the route output is mapped to. For a function, the parameter can be either the name of the function or
|
||||
* an in-line function definition.
|
||||
*
|
||||
* @example <caption>Make the right trigger move your avatar up.</caption>
|
||||
* var MAPPING_NAME = "com.highfidelity.controllers.example.newMapping";
|
||||
* var mapping = Controller.newMapping(MAPPING_NAME);
|
||||
*
|
||||
* mapping.from(Controller.Standard.RT).to(Controller.Actions.TranslateY);
|
||||
* Controller.enableMapping(MAPPING_NAME);
|
||||
*
|
||||
* Script.scriptEnding.connect(function () {
|
||||
* Controller.disableMapping(MAPPING_NAME);
|
||||
* });
|
||||
*
|
||||
* @example <caption>Make the right trigger call a function.</caption>
|
||||
* function onRightTrigger(value) {
|
||||
* print("Trigger value: " + value);
|
||||
* }
|
||||
*
|
||||
* var MAPPING_NAME = "com.highfidelity.controllers.example.newMapping";
|
||||
* var mapping = Controller.newMapping(MAPPING_NAME);
|
||||
*
|
||||
* mapping.from(Controller.Standard.RT).to(onRightTrigger);
|
||||
* Controller.enableMapping(MAPPING_NAME);
|
||||
*
|
||||
* Script.scriptEnding.connect(function () {
|
||||
* Controller.disableMapping(MAPPING_NAME);
|
||||
* });
|
||||
*/
|
||||
Q_INVOKABLE void to(const QScriptValue& destination);
|
||||
|
||||
/**jsdoc
|
||||
* Enable and disabling 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.
|
||||
* @returns {RouteObject} The <code>RouteObject</code> with debug output enabled or disabled.
|
||||
* @example <caption>Write debug information to the program log for a right trigger mapping.</caption>
|
||||
* var MAPPING_NAME = "com.highfidelity.controllers.example.newMapping";
|
||||
* var mapping = Controller.newMapping(MAPPING_NAME);
|
||||
*
|
||||
* mapping.from(Controller.Standard.RT).debug().to(function (value) {
|
||||
* print("Value: " + value);
|
||||
* });
|
||||
*
|
||||
* // Information similar to the following is written each frame:
|
||||
* [DEBUG] [hifi.controllers] Beginning mapping frame
|
||||
* [DEBUG] [hifi.controllers] Processing device routes
|
||||
* [DEBUG] [hifi.controllers] Processing standard routes
|
||||
* [DEBUG] [hifi.controllers] Applying route ""
|
||||
* [DEBUG] [hifi.controllers] Value was 5.96046e-07
|
||||
* [DEBUG] [hifi.controllers] Filtered value was 5.96046e-07
|
||||
*
|
||||
* Controller.enableMapping(MAPPING_NAME);
|
||||
*
|
||||
* Script.scriptEnding.connect(function () {
|
||||
* Controller.disableMapping(MAPPING_NAME);
|
||||
* });
|
||||
*/
|
||||
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
|
||||
* 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
|
||||
* controller source as having been read.
|
||||
* @returns {RouteObject} The <code>RouteObject</code> with the peek feature enabled.
|
||||
*/
|
||||
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
|
||||
* 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
|
||||
* @param {condition|condition[]} expression - <p>A <code>condition</code> may be a:</p>
|
||||
* <ul>
|
||||
* <li>A numeric {@link Controller.Hardware} property, which is evaluated as a boolean.</li>
|
||||
* <li><code>!</code> followed by a {@link Controller.Hardware} property to use the logical NOT of the property
|
||||
* value.</li>
|
||||
* <li>A script function returning a boolean value. This can be either the name of the function or an in-line
|
||||
* definition.</li>
|
||||
* </ul>
|
||||
* <p>If an array of conditions is provided, their values are ANDed together.</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";
|
||||
* var mapping = Controller.newMapping(MAPPING_NAME);
|
||||
*
|
||||
* // Processed only if in HMD mode.
|
||||
* mapping.from(Controller.Standard.RT)
|
||||
* .when(Controller.Hardware.Application.InHMD)
|
||||
* .to(function () { print("Trigger pressed in HMD mode."); });
|
||||
*
|
||||
* // Processed only if previous route not processed.
|
||||
* mapping.from(Controller.Standard.RT)
|
||||
* .to(function () { print("Trigger pressed in desktop mode."); });
|
||||
*
|
||||
* Controller.enableMapping(MAPPING_NAME);
|
||||
*
|
||||
* Script.scriptEnding.connect(function () {
|
||||
* Controller.disableMapping(MAPPING_NAME);
|
||||
* });
|
||||
*/
|
||||
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
|
||||
* route.
|
||||
* @function RouteObject#clamp
|
||||
* @param {number} min - The minimum value to pass through.
|
||||
* @param {number} max - The maximum value to pass through.
|
||||
* @returns {RouteObject} The route object with the clamp filter added.
|
||||
* @example <caption>Clamp right trigger values to between 0.3 and 0.7.</caption>
|
||||
* var MAPPING_NAME = "com.highfidelity.controllers.example.newMapping";
|
||||
* var mapping = Controller.newMapping(MAPPING_NAME);
|
||||
* mapping.from(Controller.Standard.RT).clamp(0.3, 0.7).to(function (value) {
|
||||
* print("Value: " + value);
|
||||
* });
|
||||
* Controller.enableMapping(MAPPING_NAME);
|
||||
*
|
||||
* Script.scriptEnding.connect(function () {
|
||||
* Controller.disableMapping(MAPPING_NAME);
|
||||
* });
|
||||
*/
|
||||
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
|
||||
* 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
|
||||
* @param {number} min - When the input value drops below this value the output value changes to <code>0</code>.
|
||||
* @param {number} max - When the input value rises above this value the output value changes to <code>1</code>.
|
||||
* @returns {RouteObject} The <code>RouteObject</code> with the filter applied.
|
||||
* @example <caption>Round the right joystick forward/back values to 0 or 1 with hysteresis.</caption>
|
||||
* var MAPPING_NAME = "com.highfidelity.controllers.example.newMapping";
|
||||
* var mapping = Controller.newMapping(MAPPING_NAME);
|
||||
* mapping.from(Controller.Standard.RY).peek().to(function (value) {
|
||||
* print("Raw value: " + value); // 0.0 - 1.0.
|
||||
* });
|
||||
* mapping.from(Controller.Standard.RY).hysteresis(0.3, 0.7).to(function (value) {
|
||||
* print("Hysteresis value: " + value); // 0 or 1.
|
||||
* });
|
||||
* Controller.enableMapping(MAPPING_NAME);
|
||||
*
|
||||
* Script.scriptEnding.connect(function () {
|
||||
* Controller.disableMapping(MAPPING_NAME);
|
||||
* });
|
||||
*/
|
||||
Q_INVOKABLE QObject* hysteresis(float min, float max);
|
||||
|
||||
/**jsdoc
|
||||
* Filter 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.
|
||||
* @example <caption>Send right trigger values every half second.</caption>
|
||||
* var MAPPING_NAME = "com.highfidelity.controllers.example.newMapping";
|
||||
* var mapping = Controller.newMapping(MAPPING_NAME);
|
||||
* mapping.from(Controller.Standard.RT).pulse(0.5).to(function (value) {
|
||||
* print("Value: " + value);
|
||||
* });
|
||||
* Controller.enableMapping(MAPPING_NAME);
|
||||
*
|
||||
* Script.scriptEnding.connect(function () {
|
||||
* Controller.disableMapping(MAPPING_NAME);
|
||||
* });
|
||||
*/
|
||||
Q_INVOKABLE QObject* pulse(float interval);
|
||||
|
||||
/**jsdoc
|
||||
* Filter 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.
|
||||
* @example <caption>Scale the value of the right joystick forward/back values by 10.</caption>
|
||||
* var MAPPING_NAME = "com.highfidelity.controllers.example.newMapping";
|
||||
* var mapping = Controller.newMapping(MAPPING_NAME);
|
||||
* mapping.from(Controller.Standard.LY).to(function (value) {
|
||||
* print("L value: " + value); // -1.0 to 1.0 values.
|
||||
* });
|
||||
* mapping.from(Controller.Standard.RY).scale(10.0).to(function (value) {
|
||||
* print("R value: " + value); // -10.0 to -10.0 values.
|
||||
* });
|
||||
* Controller.enableMapping(MAPPING_NAME);
|
||||
*
|
||||
* Script.scriptEnding.connect(function () {
|
||||
* Controller.disableMapping(MAPPING_NAME);
|
||||
* });
|
||||
*/
|
||||
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
|
||||
* <code>-0.5</code>.
|
||||
* @function RouteObject#invert
|
||||
* @returns {RouteObject} The <code>RouteObject</code> with the filter applied.
|
||||
* @example <caption>Invert the value of the right joystick forward/back values.</caption>
|
||||
* var MAPPING_NAME = "com.highfidelity.controllers.example.newMapping";
|
||||
* var mapping = Controller.newMapping(MAPPING_NAME);
|
||||
* mapping.from(Controller.Standard.LY).to(function (value) {
|
||||
* print("L value: " + value); // -1.0 to 1.0 values, forward to back.
|
||||
* });
|
||||
* mapping.from(Controller.Standard.RY).invert().to(function (value) {
|
||||
* print("R value: " + value); // 1.0 to -1.0 values, forward to back.
|
||||
* });
|
||||
* Controller.enableMapping(MAPPING_NAME);
|
||||
*
|
||||
* Script.scriptEnding.connect(function () {
|
||||
* Controller.disableMapping(MAPPING_NAME);
|
||||
* });
|
||||
*/
|
||||
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
|
||||
* 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
|
||||
* @param {number} min - The minimum input value at which to start sending output. For negative input values, the
|
||||
* negative of this value is used.
|
||||
* @returns {RouteObject} The <code>RouteObject</code> with the filter applied.
|
||||
* @example <caption>Apply a dead-zone to the right joystick forward/back values.</caption>
|
||||
* var MAPPING_NAME = "com.highfidelity.controllers.example.newMapping";
|
||||
* var mapping = Controller.newMapping(MAPPING_NAME);
|
||||
* mapping.from(Controller.Standard.RY).deadZone(0.2).to(function (value) {
|
||||
* print("Value: " + value); // 0.0 - 1.0 values once outside the dead-zone.
|
||||
* });
|
||||
* Controller.enableMapping(MAPPING_NAME);
|
||||
*
|
||||
* Script.scriptEnding.connect(function () {
|
||||
* Controller.disableMapping(MAPPING_NAME);
|
||||
* });
|
||||
*/
|
||||
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>.
|
||||
* 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
|
||||
* @returns {RouteObject} The <code>RouteObject</code> with the filter applied.
|
||||
* @example <caption>Round the right joystick forward/back values to <code>-1</code>, <code>0</code>, or
|
||||
* <code>1</code>.</caption>
|
||||
* var MAPPING_NAME = "com.highfidelity.controllers.example.newMapping";
|
||||
* var mapping = Controller.newMapping(MAPPING_NAME);
|
||||
* mapping.from(Controller.Standard.RY).constrainToInteger().to(function (value) {
|
||||
* print("Value: " + value); // -1, 0, or 1
|
||||
* });
|
||||
* Controller.enableMapping(MAPPING_NAME);
|
||||
*
|
||||
* Script.scriptEnding.connect(function () {
|
||||
* Controller.disableMapping(MAPPING_NAME);
|
||||
* });
|
||||
*/
|
||||
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
|
||||
* 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.
|
||||
* @example <caption>Round the right joystick forward/back values to <code>0</code> or <code>1</code>.</caption>
|
||||
* var MAPPING_NAME = "com.highfidelity.controllers.example.newMapping";
|
||||
* var mapping = Controller.newMapping(MAPPING_NAME);
|
||||
* mapping.from(Controller.Standard.RY).constrainToPositiveInteger().to(function (value) {
|
||||
* print("Value: " + value); // 0, or 1
|
||||
* });
|
||||
* Controller.enableMapping(MAPPING_NAME);
|
||||
*
|
||||
* Script.scriptEnding.connect(function () {
|
||||
* Controller.disableMapping(MAPPING_NAME);
|
||||
* });
|
||||
*/
|
||||
Q_INVOKABLE QObject* constrainToPositiveInteger();
|
||||
|
||||
/**jsdoc
|
||||
* Filter {@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.
|
||||
*/
|
||||
// No JSDoc example because filter not currently used.
|
||||
Q_INVOKABLE QObject* translate(glm::vec3 translate);
|
||||
|
||||
/**jsdoc
|
||||
* Filter {@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.
|
||||
*/
|
||||
// No JSDoc example because filter not currently used.
|
||||
Q_INVOKABLE QObject* transform(glm::mat4 transform);
|
||||
|
||||
/**jsdoc
|
||||
* Filter {@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.
|
||||
*/
|
||||
// No JSDoc example because filter not currently used.
|
||||
Q_INVOKABLE QObject* postTransform(glm::mat4 transform);
|
||||
|
||||
/**jsdoc
|
||||
* Filter {@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.
|
||||
*/
|
||||
// No JSDoc example because filter not currently used.
|
||||
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
|
||||
* 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.
|
||||
* @function RouteObject#lowVelocity
|
||||
* @param {number} rotationConstant - The rotational velocity, in rad/s, at which the filter value is wholly the latest
|
||||
* controller value.
|
||||
* @param {number} translationConstant - The linear velocity, in m/s, at which the filter value is wholly the latest
|
||||
* controller value.
|
||||
* @returns {RouteObject} The <code>RouteObject</code> smoothed by low velocity filtering.
|
||||
*/
|
||||
// No JSDoc example because filter not currently used.
|
||||
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
|
||||
* 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.
|
||||
* @function RouteObject#exponentialSmoothing
|
||||
* @param {number} rotationConstant - Rotation filter constant, <code>0.0–1.0</code>.
|
||||
* @param {number} translationConstant - Translation filter constant, <code>0.0–1.0</code>.
|
||||
* @returns {RouteObject} The <code>RouteObject</code> smoothed by an exponential filter.
|
||||
*/
|
||||
// No JSDoc example because filter used only in Vive.json.
|
||||
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
|
||||
* are changed to <code>0.0</code>.
|
||||
* @function RouteObject#logicalNot
|
||||
* @returns {RouteObject} The <code>RouteObject</code> with the filter applied.
|
||||
* @example <caption>Logical NOT of LSTouch value.</caption>
|
||||
* var MAPPING_NAME = "com.highfidelity.controllers.example.newMapping";
|
||||
* var mapping = Controller.newMapping(MAPPING_NAME);
|
||||
*
|
||||
* mapping.from(Controller.Standard.RSTouch).peek().to(function (value) {
|
||||
* print("RSTouch: " + value);
|
||||
* });
|
||||
* mapping.from(Controller.Standard.RSTouch).logicalNot().to(function (value) {
|
||||
* print("localNot of RSTouch: " + value);
|
||||
* });
|
||||
* Controller.enableMapping(MAPPING_NAME);
|
||||
*
|
||||
* Script.scriptEnding.connect(function () {
|
||||
* Controller.disableMapping(MAPPING_NAME);
|
||||
* });
|
||||
*/
|
||||
Q_INVOKABLE QObject* logicalNot();
|
||||
|
||||
private:
|
||||
|
|
|
@ -278,7 +278,7 @@ bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) {
|
|||
// FIXME, the max FPS could be better managed by being dynamic (based on the number of current surfaces
|
||||
// and the current rendering load)
|
||||
_webSurface->setMaxFps(DEFAULT_MAX_FPS);
|
||||
QObject::connect(_webSurface.data(), &OffscreenQmlSurface::rootContextCreated, [this](QQmlContext* surfaceContext) {
|
||||
QObject::connect(_webSurface.data(), &OffscreenQmlSurface::rootContextCreated, [](QQmlContext* surfaceContext) {
|
||||
// FIXME - Keyboard HMD only: Possibly add "HMDinfo" object to context for WebView.qml.
|
||||
surfaceContext->setContextProperty("desktop", QVariant());
|
||||
// Let us interact with the keyboard
|
||||
|
|
|
@ -1649,7 +1649,7 @@ signals:
|
|||
|
||||
/**jsdoc
|
||||
* Triggered when a mouse button is double-clicked while the mouse cursor is on an entity.
|
||||
* @function Entities.mousePressOnEntity
|
||||
* @function Entities.mouseDoublePressOnEntity
|
||||
* @param {Uuid} entityID - The ID of the entity that was double-pressed.
|
||||
* @param {PointerEvent} event - Details of the event.
|
||||
* @returns {Signal}
|
||||
|
|
|
@ -2285,7 +2285,10 @@ bool EntityTree::sendEntitiesOperation(const OctreeElementPointer& element, void
|
|||
if (args->otherTree) {
|
||||
args->otherTree->withWriteLock([&] {
|
||||
EntityItemPointer entity = args->otherTree->addEntity(newID, properties);
|
||||
entity->deserializeActions();
|
||||
if (entity) {
|
||||
entity->deserializeActions();
|
||||
}
|
||||
// else: there was an error adding this entity
|
||||
});
|
||||
}
|
||||
return newID;
|
||||
|
|
|
@ -452,7 +452,7 @@ protected:
|
|||
// Backend dependant compilation of the shader
|
||||
virtual GLShader* compileBackendProgram(const Shader& program, const Shader::CompilationHandler& handler);
|
||||
virtual GLShader* compileBackendShader(const Shader& shader, const Shader::CompilationHandler& handler);
|
||||
virtual std::string getBackendShaderHeader() const;
|
||||
virtual std::string getBackendShaderHeader() const = 0;
|
||||
virtual void makeProgramBindings(ShaderObject& shaderObject);
|
||||
class ElementResource {
|
||||
public:
|
||||
|
|
|
@ -12,27 +12,6 @@
|
|||
using namespace gpu;
|
||||
using namespace gpu::gl;
|
||||
|
||||
// GLSL version
|
||||
std::string GLBackend::getBackendShaderHeader() const {
|
||||
|
||||
#if defined(USE_GLES)
|
||||
static const std::string header(
|
||||
R"SHADER(#version 310 es
|
||||
#extension GL_EXT_texture_buffer : enable
|
||||
precision lowp float; // check precision 2
|
||||
precision lowp samplerBuffer;
|
||||
precision lowp sampler2DShadow;
|
||||
)SHADER");
|
||||
#else
|
||||
static const std::string header(
|
||||
R"SHADER(#version 410 core
|
||||
)SHADER");
|
||||
#endif
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
|
||||
// Shader domain
|
||||
static const size_t NUM_SHADER_DOMAINS = 3;
|
||||
static_assert(Shader::Type::NUM_DOMAINS == NUM_SHADER_DOMAINS, "GL shader domains must equal defined GPU shader domains");
|
||||
|
|
|
@ -14,7 +14,13 @@ using namespace gpu::gl41;
|
|||
|
||||
// GLSL version
|
||||
std::string GL41Backend::getBackendShaderHeader() const {
|
||||
return std::string("#version 410 core\n#define GPU_GL410 1");
|
||||
static const std::string header(
|
||||
R"SHADER(#version 410 core
|
||||
#define GPU_GL410
|
||||
#define PRECISIONQ
|
||||
#define BITFIELD int
|
||||
)SHADER");
|
||||
return header;
|
||||
}
|
||||
|
||||
int GL41Backend::makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) {
|
||||
|
|
|
@ -15,15 +15,17 @@ using namespace gpu::gl45;
|
|||
|
||||
// GLSL version
|
||||
std::string GL45Backend::getBackendShaderHeader() const {
|
||||
const char header[] =
|
||||
R"GLSL(#version 450 core
|
||||
#define GPU_GL450
|
||||
)GLSL"
|
||||
static const std::string header(
|
||||
R"SHADER(#version 450 core
|
||||
#define GPU_GL450
|
||||
#define PRECISIONQ
|
||||
#define BITFIELD int
|
||||
)SHADER"
|
||||
#ifdef GPU_SSBO_TRANSFORM_OBJECT
|
||||
R"GLSL(#define GPU_SSBO_TRANSFORM_OBJECT 1)GLSL"
|
||||
R"SHADER(#define GPU_SSBO_TRANSFORM_OBJECT)SHADER"
|
||||
#endif
|
||||
;
|
||||
return std::string(header);
|
||||
);
|
||||
return header;
|
||||
}
|
||||
|
||||
int GL45Backend::makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) {
|
||||
|
|
|
@ -14,7 +14,16 @@ using namespace gpu::gles;
|
|||
|
||||
// GLSL version
|
||||
std::string GLESBackend::getBackendShaderHeader() const {
|
||||
return Parent::getBackendShaderHeader();
|
||||
static const std::string header(
|
||||
R"SHADER(#version 310 es
|
||||
#extension GL_EXT_texture_buffer : enable
|
||||
precision lowp float; // check precision 2
|
||||
precision lowp samplerBuffer;
|
||||
precision lowp sampler2DShadow;
|
||||
#define PRECISIONQ highp
|
||||
#define BITFIELD highp int
|
||||
)SHADER");
|
||||
return header;
|
||||
}
|
||||
|
||||
int GLESBackend::makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) {
|
||||
|
|
|
@ -29,16 +29,9 @@ vec3 getLightColor(Light l) { return lightIrradiance_getColor(l.irradiance); }
|
|||
float getLightIntensity(Light l) { return lightIrradiance_getIntensity(l.irradiance); }
|
||||
vec3 getLightIrradiance(Light l) { return lightIrradiance_getIrradiance(l.irradiance); }
|
||||
|
||||
// AMbient lighting needs extra info provided from a different Buffer
|
||||
// Ambient lighting needs extra info provided from a different Buffer
|
||||
<@include graphics/SphericalHarmonics.shared.slh@>
|
||||
// Light Ambient
|
||||
#ifndef PRECISIONQ
|
||||
#ifdef GL_ES
|
||||
#define PRECISIONQ highp
|
||||
#else
|
||||
#define PRECISIONQ
|
||||
#endif
|
||||
#endif
|
||||
|
||||
struct LightAmbient {
|
||||
PRECISIONQ vec4 _ambient;
|
||||
|
|
|
@ -12,13 +12,7 @@
|
|||
|
||||
|
||||
#define LightIrradianceConstRef LightIrradiance
|
||||
#ifndef PRECISIONQ
|
||||
#ifdef GL_ES
|
||||
#define PRECISIONQ highp
|
||||
#else
|
||||
#define PRECISIONQ
|
||||
#endif
|
||||
#endif
|
||||
|
||||
struct LightIrradiance {
|
||||
PRECISIONQ vec4 colorIntensity;
|
||||
// falloffRadius, cutoffRadius, falloffSpot, spare
|
||||
|
|
|
@ -15,14 +15,6 @@
|
|||
|
||||
#define LightVolumeConstRef LightVolume
|
||||
|
||||
#ifndef PRECISIONQ
|
||||
#ifdef GL_ES
|
||||
#define PRECISIONQ highp
|
||||
#else
|
||||
#define PRECISIONQ
|
||||
#endif
|
||||
#endif
|
||||
|
||||
struct LightVolume {
|
||||
PRECISIONQ vec4 positionRadius;
|
||||
PRECISIONQ vec4 directionSpotCos;
|
||||
|
|
|
@ -13,14 +13,7 @@
|
|||
|
||||
// The material values (at least the material key) must be precisely bitwise accurate
|
||||
// to what is provided by the uniform buffer, or the material key has the wrong bits
|
||||
#ifdef GL_ES
|
||||
precision highp float;
|
||||
#define BITFIELD highp int
|
||||
#define PRECISIONQ highp
|
||||
#else
|
||||
#define BITFIELD int
|
||||
#define PRECISIONQ
|
||||
#endif
|
||||
|
||||
struct Material {
|
||||
PRECISIONQ vec4 _emissiveOpacity;
|
||||
PRECISIONQ vec4 _albedoRoughness;
|
||||
|
|
|
@ -15,14 +15,6 @@
|
|||
|
||||
#define SphericalHarmonicsConstRef SphericalHarmonics
|
||||
|
||||
#ifndef PRECISIONQ
|
||||
#ifdef GL_ES
|
||||
#define PRECISIONQ highp
|
||||
#else
|
||||
#define PRECISIONQ
|
||||
#endif
|
||||
#endif
|
||||
|
||||
struct SphericalHarmonics {
|
||||
PRECISIONQ vec4 L00;
|
||||
PRECISIONQ vec4 L1m1;
|
||||
|
|
|
@ -216,6 +216,72 @@ controller::Input KeyboardMouseDevice::InputDevice::makeInput(KeyboardMouseDevic
|
|||
return controller::Input(_deviceID, button, controller::ChannelType::BUTTON);
|
||||
}
|
||||
|
||||
/**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>
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Property</th><th>Type</th><td>Data</th><th>Description</th></tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td><code>0</code> – <code>9</code></td><td>number</td><td>number</td><td>A "0" – "1" key on the
|
||||
* keyboard or keypad is pressed.</td></tr>
|
||||
* <tr><td><code>A</code> – <code>Z</code></td><td>number</td><td>number</td><td>A "A" – "Z" key on the
|
||||
* keyboard is pressed.</td></tr>
|
||||
* <tr><td><code>Space</code></td><td>number</td><td>number</td><td>The space bar on the keyboard is pressed.</td></tr>
|
||||
* <tr><td><code>Tab</code></td><td>number</td><td>number</td><td>The tab key on the keyboard is pressed.</td></tr>
|
||||
* <tr><td><code>Shift</code></td><td>number</td><td>number</td><td>The shift key on the keyboard is pressed.</td></tr>
|
||||
* <tr><td><code>Control</code></td><td>number</td><td>number</td><td>The control key on the keyboard is pressed. (The
|
||||
* "Command" key on OSX.)</td></tr>
|
||||
* <tr><td><code>Left</code></td><td>number</td><td>number</td><td>The left arrow key on the keyboard or keypad is pressed.
|
||||
* </td></tr>
|
||||
* <tr><td><code>Right</code></td><td>number</td><td>number</td><td>The right arrow key on the keyboard or keypad is
|
||||
* pressed.</td></tr>
|
||||
* <tr><td><code>Up</code></td><td>number</td><td>number</td><td>The up arrow key on the keyboard or keypad is pressed.
|
||||
* </td></tr>
|
||||
* <tr><td><code>Down</code></td><td>number</td><td>number</td><td>The down arrow key on the keyboard or keypad is pressed.
|
||||
* </td></tr>
|
||||
* <tr><td><code>PgUp</code></td><td>number</td><td>number</td><td>The page up key on the keyboard or keypad is pressed.
|
||||
* </td></tr>
|
||||
* <tr><td><code>PgDown</code></td><td>number</td><td>number</td><td>The page down key on the keyboard or keypad is pressed.
|
||||
* </td></tr>
|
||||
* <tr><td><code>LeftMouseButton</code></td><td>number</td><td>number</td><td>The left mouse button pressed.</td></tr>
|
||||
* <tr><td><code>MiddleMouseButton</code></td><td>number</td><td>number</td><td>The middle mouse button pressed.</td></tr>
|
||||
* <tr><td><code>RightMouseButton</code></td><td>number</td><td>number</td><td>The right mouse button pressed.</td></tr>
|
||||
* <tr><td><code>LeftMouseClicked</code></td><td>number</td><td>number</td><td>The left mouse button clicked.</td></tr>
|
||||
* <tr><td><code>MiddleMouseClicked</code></td><td>number</td><td>number</td><td>The middle mouse button clicked.</td></tr>
|
||||
* <tr><td><code>RightMouseClicked</code></td><td>number</td><td>number</td><td>The right mouse button clicked.</td></tr>
|
||||
* <tr><td><code>MouseMoveRight</code></td><td>number</td><td>number</td><td>The mouse moved right.</td></tr>
|
||||
* <tr><td><code>MouseMoveLeft</code></td><td>number</td><td>number</td><td>The mouse moved left.</td></tr>
|
||||
* <tr><td><code>MouseMoveUp</code></td><td>number</td><td>number</td><td>The mouse moved up.</td></tr>
|
||||
* <tr><td><code>MouseMoveDown</code></td><td>number</td><td>number</td><td>The mouse moved down.</td></tr>
|
||||
* <tr><td><code>MouseX</code></td><td>number</td><td>number</td><td>The mouse x-coordinate changed. The data value is its
|
||||
* 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
|
||||
* 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>
|
||||
* <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>
|
||||
* <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
|
||||
* moved left. The data value is how far the average position of all touch points moved.</td></tr>
|
||||
* <tr><td><code>TouchpadUp</code></td><td>number</td><td>number</td><td>The average touch on a touch-enabled device
|
||||
* moved up. The data value is how far the average position of all touch points moved.</td></tr>
|
||||
* <tr><td><code>TouchpadDown</code></td><td>number</td><td>number</td><td>The average touch on a touch-enabled device
|
||||
* moved down. The data value is how far the average position of all touch points moved.</td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef 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;
|
||||
static QVector<Input::NamedPair> availableInputs;
|
||||
|
|
|
@ -34,15 +34,16 @@ const QString GET_PLACE = "/api/v1/places/%1";
|
|||
*
|
||||
* @namespace location
|
||||
* @property {Uuid} domainID - A UUID uniquely identifying the domain you're visiting. Is {@link Uuid|Uuid.NULL} if you're not
|
||||
* connected to the domain.
|
||||
* connected to the domain or are in a serverless domain.
|
||||
* <em>Read-only.</em>
|
||||
* @property {Uuid} domainId - Synonym for <code>domainId</code>. <em>Read-only.</em> <strong>Deprecated:</strong> This property
|
||||
* is deprecated and will soon be removed.
|
||||
* @property {string} hostname - The name of the domain for your current metaverse address (e.g., <code>"AvatarIsland"</code>,
|
||||
* <code>localhost</code>, or an IP address).
|
||||
* <code>localhost</code>, or an IP address). Is blank if you're in a serverless domain.
|
||||
* <em>Read-only.</em>
|
||||
* @property {string} href - Your current metaverse address (e.g., <code>"hifi://avatarisland/15,-10,26/0,0,0,1"</code>)
|
||||
* regardless of whether or not you're connected to the domain.
|
||||
* regardless of whether or not you're connected to the domain. Starts with <code>"file:///"</code> if you're in a
|
||||
* serverless domain.
|
||||
* <em>Read-only.</em>
|
||||
* @property {boolean} isConnected - <code>true</code> if you're connected to the domain in your current <code>href</code>
|
||||
* metaverse address, otherwise <code>false</code>.
|
||||
|
|
|
@ -45,6 +45,9 @@ public:
|
|||
const QUuid& getUUID() const { return _uuid; }
|
||||
void setUUID(const QUuid& uuid);
|
||||
|
||||
Node::LocalID getLocalID() const { return _localID; }
|
||||
void setLocalID(Node::LocalID localID) { _localID = localID; }
|
||||
|
||||
QString getHostname() const { return _domainURL.host(); }
|
||||
|
||||
const QHostAddress& getIP() const { return _sockAddr.getAddress(); }
|
||||
|
@ -185,6 +188,7 @@ private:
|
|||
void hardReset();
|
||||
|
||||
QUuid _uuid;
|
||||
Node::LocalID _localID;
|
||||
QUrl _domainURL;
|
||||
HifiSockAddr _sockAddr;
|
||||
QUuid _assignmentUUID;
|
||||
|
|
94
libraries/networking/src/HMACAuth.cpp
Normal file
94
libraries/networking/src/HMACAuth.cpp
Normal file
|
@ -0,0 +1,94 @@
|
|||
//
|
||||
// HMACAuth.cpp
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Simon Walton on 3/19/2018.
|
||||
// Copyright 2018 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 <openssl/opensslv.h>
|
||||
#include <openssl/hmac.h>
|
||||
|
||||
#include "HMACAuth.h"
|
||||
|
||||
#include <QUuid>
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000
|
||||
HMACAuth::HMACAuth(AuthMethod authMethod)
|
||||
: _hmacContext(HMAC_CTX_new())
|
||||
, _authMethod(authMethod) { }
|
||||
|
||||
HMACAuth::~HMACAuth()
|
||||
{
|
||||
HMAC_CTX_free(_hmacContext);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
HMACAuth::HMACAuth(AuthMethod authMethod)
|
||||
: _hmacContext(new HMAC_CTX())
|
||||
, _authMethod(authMethod) {
|
||||
HMAC_CTX_init(_hmacContext);
|
||||
}
|
||||
|
||||
HMACAuth::~HMACAuth() {
|
||||
HMAC_CTX_cleanup(_hmacContext);
|
||||
delete _hmacContext;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool HMACAuth::setKey(const char* keyValue, int keyLen) {
|
||||
const EVP_MD* sslStruct = nullptr;
|
||||
|
||||
switch (_authMethod) {
|
||||
case MD5:
|
||||
sslStruct = EVP_md5();
|
||||
break;
|
||||
|
||||
case SHA1:
|
||||
sslStruct = EVP_sha1();
|
||||
break;
|
||||
|
||||
case SHA224:
|
||||
sslStruct = EVP_sha224();
|
||||
break;
|
||||
|
||||
case SHA256:
|
||||
sslStruct = EVP_sha256();
|
||||
break;
|
||||
|
||||
case RIPEMD160:
|
||||
sslStruct = EVP_ripemd160();
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
QMutexLocker lock(&_lock);
|
||||
return (bool) HMAC_Init_ex(_hmacContext, keyValue, keyLen, sslStruct, nullptr);
|
||||
}
|
||||
|
||||
bool HMACAuth::setKey(const QUuid& uidKey) {
|
||||
const QByteArray rfcBytes(uidKey.toRfc4122());
|
||||
return setKey(rfcBytes.constData(), rfcBytes.length());
|
||||
}
|
||||
|
||||
bool HMACAuth::addData(const char* data, int dataLen) {
|
||||
QMutexLocker lock(&_lock);
|
||||
return (bool) HMAC_Update(_hmacContext, reinterpret_cast<const unsigned char*>(data), dataLen);
|
||||
}
|
||||
|
||||
HMACAuth::HMACHash HMACAuth::result() {
|
||||
HMACHash hashValue(EVP_MAX_MD_SIZE);
|
||||
unsigned int hashLen;
|
||||
QMutexLocker lock(&_lock);
|
||||
HMAC_Final(_hmacContext, &hashValue[0], &hashLen);
|
||||
hashValue.resize((size_t) hashLen);
|
||||
// Clear state for possible reuse.
|
||||
HMAC_Init_ex(_hmacContext, nullptr, 0, nullptr, nullptr);
|
||||
return hashValue;
|
||||
}
|
40
libraries/networking/src/HMACAuth.h
Normal file
40
libraries/networking/src/HMACAuth.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
//
|
||||
// HMACAuth.h
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Simon Walton on 3/19/2018.
|
||||
// Copyright 2018 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_HMACAuth_h
|
||||
#define hifi_HMACAuth_h
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <QtCore/QMutex>
|
||||
|
||||
class QUuid;
|
||||
|
||||
class HMACAuth {
|
||||
public:
|
||||
enum AuthMethod { MD5, SHA1, SHA224, SHA256, RIPEMD160 };
|
||||
using HMACHash = std::vector<unsigned char>;
|
||||
|
||||
explicit HMACAuth(AuthMethod authMethod = MD5);
|
||||
~HMACAuth();
|
||||
|
||||
bool setKey(const char* keyValue, int keyLen);
|
||||
bool setKey(const QUuid& uidKey);
|
||||
bool addData(const char* data, int dataLen);
|
||||
HMACHash result();
|
||||
|
||||
private:
|
||||
QMutex _lock;
|
||||
struct hmac_ctx_st* _hmacContext;
|
||||
AuthMethod _authMethod;
|
||||
};
|
||||
|
||||
#endif // hifi_HMACAuth_h
|
|
@ -36,6 +36,7 @@
|
|||
#include "HifiSockAddr.h"
|
||||
#include "NetworkLogging.h"
|
||||
#include "udt/Packet.h"
|
||||
#include "HMACAuth.h"
|
||||
|
||||
static Setting::Handle<quint16> LIMITED_NODELIST_LOCAL_PORT("LimitedNodeList.LocalPort", 0);
|
||||
|
||||
|
@ -131,6 +132,16 @@ void LimitedNodeList::setSessionUUID(const QUuid& sessionUUID) {
|
|||
}
|
||||
}
|
||||
|
||||
Node::LocalID LimitedNodeList::getSessionLocalID() const {
|
||||
QReadLocker readLock { &_sessionUUIDLock };
|
||||
return _sessionLocalID;
|
||||
}
|
||||
|
||||
void LimitedNodeList::setSessionLocalID(Node::LocalID sessionLocalID) {
|
||||
QWriteLocker lock { &_sessionUUIDLock };
|
||||
_sessionLocalID = sessionLocalID;
|
||||
}
|
||||
|
||||
void LimitedNodeList::setPermissions(const NodePermissions& newPermissions) {
|
||||
NodePermissions originalPermissions = _permissions;
|
||||
|
||||
|
@ -229,13 +240,16 @@ bool LimitedNodeList::packetVersionMatch(const udt::Packet& packet) {
|
|||
senderString = QString("%1:%2").arg(senderSockAddr.getAddress().toString()).arg(senderSockAddr.getPort());
|
||||
}
|
||||
} else {
|
||||
sourceID = NLPacket::sourceIDInHeader(packet);
|
||||
SharedNodePointer sourceNode = nodeWithLocalID(NLPacket::sourceIDInHeader(packet));
|
||||
if (sourceNode) {
|
||||
sourceID = sourceNode->getUUID();
|
||||
|
||||
hasBeenOutput = sourcedVersionDebugSuppressMap.contains(sourceID, headerType);
|
||||
hasBeenOutput = sourcedVersionDebugSuppressMap.contains(sourceID, headerType);
|
||||
|
||||
if (!hasBeenOutput) {
|
||||
sourcedVersionDebugSuppressMap.insert(sourceID, headerType);
|
||||
senderString = uuidStringWithoutCurlyBraces(sourceID.toString());
|
||||
if (!hasBeenOutput) {
|
||||
sourcedVersionDebugSuppressMap.insert(sourceID, headerType);
|
||||
senderString = uuidStringWithoutCurlyBraces(sourceID.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -288,17 +302,20 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe
|
|||
return true;
|
||||
}
|
||||
} else {
|
||||
QUuid sourceID = NLPacket::sourceIDInHeader(packet);
|
||||
|
||||
NLPacket::LocalID sourceLocalID = Node::NULL_LOCAL_ID;
|
||||
// check if we were passed a sourceNode hint or if we need to look it up
|
||||
if (!sourceNode) {
|
||||
// figure out which node this is from
|
||||
SharedNodePointer matchingNode = nodeWithUUID(sourceID);
|
||||
sourceLocalID = NLPacket::sourceIDInHeader(packet);
|
||||
|
||||
SharedNodePointer matchingNode = nodeWithLocalID(sourceLocalID);
|
||||
sourceNode = matchingNode.data();
|
||||
}
|
||||
|
||||
QUuid sourceID = sourceNode ? sourceNode->getUUID() : QUuid();
|
||||
|
||||
if (!sourceNode &&
|
||||
sourceID == getDomainUUID() &&
|
||||
sourceLocalID == getDomainLocalID() &&
|
||||
packet.getSenderSockAddr() == getDomainSockAddr() &&
|
||||
PacketTypeEnum::getDomainSourcedPackets().contains(headerType)) {
|
||||
// This is a packet sourced by the domain server
|
||||
|
@ -314,7 +331,7 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe
|
|||
if (verifiedPacket && !ignoreVerification) {
|
||||
|
||||
QByteArray packetHeaderHash = NLPacket::verificationHashInHeader(packet);
|
||||
QByteArray expectedHash = NLPacket::hashForPacketAndSecret(packet, sourceNode->getConnectionSecret());
|
||||
QByteArray expectedHash = NLPacket::hashForPacketAndHMAC(packet, sourceNode->getAuthenticateHash());
|
||||
|
||||
// check if the md5 hash in the header matches the hash we would expect
|
||||
if (packetHeaderHash != expectedHash) {
|
||||
|
@ -354,15 +371,15 @@ void LimitedNodeList::collectPacketStats(const NLPacket& packet) {
|
|||
_numCollectedBytes += packet.getDataSize();
|
||||
}
|
||||
|
||||
void LimitedNodeList::fillPacketHeader(const NLPacket& packet, const QUuid& connectionSecret) {
|
||||
void LimitedNodeList::fillPacketHeader(const NLPacket& packet, HMACAuth* hmacAuth) {
|
||||
if (!PacketTypeEnum::getNonSourcedPackets().contains(packet.getType())) {
|
||||
packet.writeSourceID(getSessionUUID());
|
||||
packet.writeSourceID(getSessionLocalID());
|
||||
}
|
||||
|
||||
if (!connectionSecret.isNull()
|
||||
if (hmacAuth
|
||||
&& !PacketTypeEnum::getNonSourcedPackets().contains(packet.getType())
|
||||
&& !PacketTypeEnum::getNonVerifiedPackets().contains(packet.getType())) {
|
||||
packet.writeVerificationHashGivenSecret(connectionSecret);
|
||||
packet.writeVerificationHash(*hmacAuth);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -378,17 +395,17 @@ qint64 LimitedNodeList::sendUnreliablePacket(const NLPacket& packet, const Node&
|
|||
emit dataSent(destinationNode.getType(), packet.getDataSize());
|
||||
destinationNode.recordBytesSent(packet.getDataSize());
|
||||
|
||||
return sendUnreliablePacket(packet, *destinationNode.getActiveSocket(), destinationNode.getConnectionSecret());
|
||||
return sendUnreliablePacket(packet, *destinationNode.getActiveSocket(), &destinationNode.getAuthenticateHash());
|
||||
}
|
||||
|
||||
qint64 LimitedNodeList::sendUnreliablePacket(const NLPacket& packet, const HifiSockAddr& sockAddr,
|
||||
const QUuid& connectionSecret) {
|
||||
HMACAuth* hmacAuth) {
|
||||
Q_ASSERT(!packet.isPartOfMessage());
|
||||
Q_ASSERT_X(!packet.isReliable(), "LimitedNodeList::sendUnreliablePacket",
|
||||
"Trying to send a reliable packet unreliably.");
|
||||
|
||||
collectPacketStats(packet);
|
||||
fillPacketHeader(packet, connectionSecret);
|
||||
fillPacketHeader(packet, hmacAuth);
|
||||
|
||||
return _nodeSocket.writePacket(packet, sockAddr);
|
||||
}
|
||||
|
@ -401,7 +418,7 @@ qint64 LimitedNodeList::sendPacket(std::unique_ptr<NLPacket> packet, const Node&
|
|||
emit dataSent(destinationNode.getType(), packet->getDataSize());
|
||||
destinationNode.recordBytesSent(packet->getDataSize());
|
||||
|
||||
return sendPacket(std::move(packet), *activeSocket, destinationNode.getConnectionSecret());
|
||||
return sendPacket(std::move(packet), *activeSocket, &destinationNode.getAuthenticateHash());
|
||||
} else {
|
||||
qCDebug(networking) << "LimitedNodeList::sendPacket called without active socket for node" << destinationNode << "- not sending";
|
||||
return ERROR_SENDING_PACKET_BYTES;
|
||||
|
@ -409,18 +426,18 @@ qint64 LimitedNodeList::sendPacket(std::unique_ptr<NLPacket> packet, const Node&
|
|||
}
|
||||
|
||||
qint64 LimitedNodeList::sendPacket(std::unique_ptr<NLPacket> packet, const HifiSockAddr& sockAddr,
|
||||
const QUuid& connectionSecret) {
|
||||
HMACAuth* hmacAuth) {
|
||||
Q_ASSERT(!packet->isPartOfMessage());
|
||||
if (packet->isReliable()) {
|
||||
collectPacketStats(*packet);
|
||||
fillPacketHeader(*packet, connectionSecret);
|
||||
fillPacketHeader(*packet, hmacAuth);
|
||||
|
||||
auto size = packet->getDataSize();
|
||||
_nodeSocket.writePacket(std::move(packet), sockAddr);
|
||||
|
||||
return size;
|
||||
} else {
|
||||
return sendUnreliablePacket(*packet, sockAddr, connectionSecret);
|
||||
return sendUnreliablePacket(*packet, sockAddr, hmacAuth);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -429,13 +446,14 @@ qint64 LimitedNodeList::sendUnreliableUnorderedPacketList(NLPacketList& packetLi
|
|||
|
||||
if (activeSocket) {
|
||||
qint64 bytesSent = 0;
|
||||
auto connectionSecret = destinationNode.getConnectionSecret();
|
||||
auto& connectionHash = destinationNode.getAuthenticateHash();
|
||||
|
||||
// close the last packet in the list
|
||||
packetList.closeCurrentPacket();
|
||||
|
||||
while (!packetList._packets.empty()) {
|
||||
bytesSent += sendPacket(packetList.takeFront<NLPacket>(), *activeSocket, connectionSecret);
|
||||
bytesSent += sendPacket(packetList.takeFront<NLPacket>(), *activeSocket,
|
||||
&connectionHash);
|
||||
}
|
||||
|
||||
emit dataSent(destinationNode.getType(), bytesSent);
|
||||
|
@ -448,14 +466,14 @@ qint64 LimitedNodeList::sendUnreliableUnorderedPacketList(NLPacketList& packetLi
|
|||
}
|
||||
|
||||
qint64 LimitedNodeList::sendUnreliableUnorderedPacketList(NLPacketList& packetList, const HifiSockAddr& sockAddr,
|
||||
const QUuid& connectionSecret) {
|
||||
HMACAuth* hmacAuth) {
|
||||
qint64 bytesSent = 0;
|
||||
|
||||
// close the last packet in the list
|
||||
packetList.closeCurrentPacket();
|
||||
|
||||
while (!packetList._packets.empty()) {
|
||||
bytesSent += sendPacket(packetList.takeFront<NLPacket>(), sockAddr, connectionSecret);
|
||||
bytesSent += sendPacket(packetList.takeFront<NLPacket>(), sockAddr, hmacAuth);
|
||||
}
|
||||
|
||||
return bytesSent;
|
||||
|
@ -483,7 +501,7 @@ qint64 LimitedNodeList::sendPacketList(std::unique_ptr<NLPacketList> packetList,
|
|||
for (std::unique_ptr<udt::Packet>& packet : packetList->_packets) {
|
||||
NLPacket* nlPacket = static_cast<NLPacket*>(packet.get());
|
||||
collectPacketStats(*nlPacket);
|
||||
fillPacketHeader(*nlPacket, destinationNode.getConnectionSecret());
|
||||
fillPacketHeader(*nlPacket, &destinationNode.getAuthenticateHash());
|
||||
}
|
||||
|
||||
return _nodeSocket.writePacketList(std::move(packetList), *activeSocket);
|
||||
|
@ -506,7 +524,7 @@ qint64 LimitedNodeList::sendPacket(std::unique_ptr<NLPacket> packet, const Node&
|
|||
auto& destinationSockAddr = (overridenSockAddr.isNull()) ? *destinationNode.getActiveSocket()
|
||||
: overridenSockAddr;
|
||||
|
||||
return sendPacket(std::move(packet), destinationSockAddr, destinationNode.getConnectionSecret());
|
||||
return sendPacket(std::move(packet), destinationSockAddr, &destinationNode.getAuthenticateHash());
|
||||
}
|
||||
|
||||
int LimitedNodeList::updateNodeWithDataFromPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
|
||||
|
@ -539,6 +557,13 @@ SharedNodePointer LimitedNodeList::nodeWithUUID(const QUuid& nodeUUID) {
|
|||
return it == _nodeHash.cend() ? SharedNodePointer() : it->second;
|
||||
}
|
||||
|
||||
SharedNodePointer LimitedNodeList::nodeWithLocalID(Node::LocalID localID) const {
|
||||
QReadLocker readLocker(&_nodeMutex);
|
||||
|
||||
LocalIDMapping::const_iterator idIter = _localIDMap.find(localID);
|
||||
return idIter == _localIDMap.cend() ? nullptr : idIter->second;
|
||||
}
|
||||
|
||||
void LimitedNodeList::eraseAllNodes() {
|
||||
QSet<SharedNodePointer> killedNodes;
|
||||
|
||||
|
@ -547,6 +572,8 @@ void LimitedNodeList::eraseAllNodes() {
|
|||
// and then remove them from the hash
|
||||
QWriteLocker writeLocker(&_nodeMutex);
|
||||
|
||||
_localIDMap.clear();
|
||||
|
||||
if (_nodeHash.size() > 0) {
|
||||
qCDebug(networking) << "LimitedNodeList::eraseAllNodes() removing all nodes from NodeList.";
|
||||
|
||||
|
@ -569,9 +596,10 @@ void LimitedNodeList::reset() {
|
|||
|
||||
// we need to make sure any socket connections are gone so wait on that here
|
||||
_nodeSocket.clearConnections();
|
||||
_connectionIDs.clear();
|
||||
}
|
||||
|
||||
bool LimitedNodeList::killNodeWithUUID(const QUuid& nodeUUID) {
|
||||
bool LimitedNodeList::killNodeWithUUID(const QUuid& nodeUUID, ConnectionID newConnectionID) {
|
||||
QReadLocker readLocker(&_nodeMutex);
|
||||
|
||||
NodeHash::iterator it = _nodeHash.find(nodeUUID);
|
||||
|
@ -585,7 +613,7 @@ bool LimitedNodeList::killNodeWithUUID(const QUuid& nodeUUID) {
|
|||
_nodeHash.unsafe_erase(it);
|
||||
}
|
||||
|
||||
handleNodeKill(matchingNode);
|
||||
handleNodeKill(matchingNode, newConnectionID);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -600,7 +628,7 @@ void LimitedNodeList::processKillNode(ReceivedMessage& message) {
|
|||
killNodeWithUUID(nodeUUID);
|
||||
}
|
||||
|
||||
void LimitedNodeList::handleNodeKill(const SharedNodePointer& node) {
|
||||
void LimitedNodeList::handleNodeKill(const SharedNodePointer& node, ConnectionID nextConnectionID) {
|
||||
qCDebug(networking) << "Killed" << *node;
|
||||
node->stopPingTimer();
|
||||
emit nodeKilled(node);
|
||||
|
@ -608,11 +636,20 @@ void LimitedNodeList::handleNodeKill(const SharedNodePointer& node) {
|
|||
if (auto activeSocket = node->getActiveSocket()) {
|
||||
_nodeSocket.cleanupConnection(*activeSocket);
|
||||
}
|
||||
|
||||
auto it = _connectionIDs.find(node->getUUID());
|
||||
if (it != _connectionIDs.end()) {
|
||||
if (nextConnectionID == NULL_CONNECTION_ID) {
|
||||
it->second++;
|
||||
} else {
|
||||
it->second = nextConnectionID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t nodeType,
|
||||
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket,
|
||||
bool isReplicated, bool isUpstream,
|
||||
Node::LocalID localID, bool isReplicated, bool isUpstream,
|
||||
const QUuid& connectionSecret, const NodePermissions& permissions) {
|
||||
QReadLocker readLocker(&_nodeMutex);
|
||||
NodeHash::const_iterator it = _nodeHash.find(uuid);
|
||||
|
@ -626,15 +663,22 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t
|
|||
matchingNode->setConnectionSecret(connectionSecret);
|
||||
matchingNode->setIsReplicated(isReplicated);
|
||||
matchingNode->setIsUpstream(isUpstream || NodeType::isUpstream(nodeType));
|
||||
matchingNode->setLocalID(localID);
|
||||
|
||||
return matchingNode;
|
||||
} else {
|
||||
auto it = _connectionIDs.find(uuid);
|
||||
if (it == _connectionIDs.end()) {
|
||||
_connectionIDs[uuid] = INITIAL_CONNECTION_ID;
|
||||
}
|
||||
|
||||
// we didn't have this node, so add them
|
||||
Node* newNode = new Node(uuid, nodeType, publicSocket, localSocket);
|
||||
newNode->setIsReplicated(isReplicated);
|
||||
newNode->setIsUpstream(isUpstream || NodeType::isUpstream(nodeType));
|
||||
newNode->setConnectionSecret(connectionSecret);
|
||||
newNode->setPermissions(permissions);
|
||||
newNode->setLocalID(localID);
|
||||
|
||||
// move the newly constructed node to the LNL thread
|
||||
newNode->moveToThread(thread());
|
||||
|
@ -660,6 +704,7 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t
|
|||
|
||||
auto oldSoloNode = previousSoloIt->second;
|
||||
|
||||
_localIDMap.unsafe_erase(oldSoloNode->getLocalID());
|
||||
_nodeHash.unsafe_erase(previousSoloIt);
|
||||
handleNodeKill(oldSoloNode);
|
||||
|
||||
|
@ -675,6 +720,7 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t
|
|||
#else
|
||||
_nodeHash.emplace(newNode->getUUID(), newNodePointer);
|
||||
#endif
|
||||
_localIDMap.emplace(localID, newNodePointer);
|
||||
readLocker.unlock();
|
||||
|
||||
qCDebug(networking) << "Added" << *newNode;
|
||||
|
@ -703,13 +749,13 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t
|
|||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<NLPacket> LimitedNodeList::constructPingPacket(PingType_t pingType) {
|
||||
int packetSize = sizeof(PingType_t) + sizeof(quint64);
|
||||
std::unique_ptr<NLPacket> LimitedNodeList::constructPingPacket(const QUuid& nodeId, PingType_t pingType) {
|
||||
int packetSize = sizeof(PingType_t) + sizeof(quint64) + sizeof(int64_t);
|
||||
|
||||
auto pingPacket = NLPacket::create(PacketType::Ping, packetSize);
|
||||
|
||||
pingPacket->writePrimitive(pingType);
|
||||
pingPacket->writePrimitive(usecTimestampNow());
|
||||
pingPacket->writePrimitive(_connectionIDs[nodeId]);
|
||||
|
||||
return pingPacket;
|
||||
}
|
||||
|
@ -802,6 +848,7 @@ void LimitedNodeList::removeSilentNodes() {
|
|||
if (!node->isForcedNeverSilent()
|
||||
&& (usecTimestampNow() - node->getLastHeardMicrostamp()) > (NODE_SILENCE_THRESHOLD_MSECS * USECS_PER_MSEC)) {
|
||||
// call the NodeHash erase to get rid of this node
|
||||
_localIDMap.unsafe_erase(node->getLocalID());
|
||||
it = _nodeHash.unsafe_erase(it);
|
||||
|
||||
killedNodes.insert(node);
|
||||
|
|
|
@ -66,6 +66,10 @@ const QHostAddress DEFAULT_ASSIGNMENT_CLIENT_MONITOR_HOSTNAME = QHostAddress::Lo
|
|||
|
||||
const QString USERNAME_UUID_REPLACEMENT_STATS_KEY = "$username";
|
||||
|
||||
using ConnectionID = int64_t;
|
||||
const ConnectionID NULL_CONNECTION_ID { -1 };
|
||||
const ConnectionID INITIAL_CONNECTION_ID { 0 };
|
||||
|
||||
typedef std::pair<QUuid, SharedNodePointer> UUIDNodePair;
|
||||
typedef tbb::concurrent_unordered_map<QUuid, SharedNodePointer, UUIDHasher> NodeHash;
|
||||
|
||||
|
@ -106,6 +110,8 @@ public:
|
|||
Q_ENUM(ConnectionStep);
|
||||
QUuid getSessionUUID() const;
|
||||
void setSessionUUID(const QUuid& sessionUUID);
|
||||
Node::LocalID getSessionLocalID() const;
|
||||
void setSessionLocalID(Node::LocalID localID);
|
||||
|
||||
void setPermissions(const NodePermissions& newPermissions);
|
||||
bool isAllowedEditor() const { return _permissions.can(NodePermissions::Permission::canAdjustLocks); }
|
||||
|
@ -126,24 +132,23 @@ public:
|
|||
|
||||
virtual bool isDomainServer() const { return true; }
|
||||
virtual QUuid getDomainUUID() const { assert(false); return QUuid(); }
|
||||
virtual Node::LocalID getDomainLocalID() const { assert(false); return Node::NULL_LOCAL_ID; }
|
||||
virtual HifiSockAddr getDomainSockAddr() const { assert(false); return HifiSockAddr(); }
|
||||
|
||||
// use sendUnreliablePacket to send an unrelaible packet (that you do not need to move)
|
||||
// use sendUnreliablePacket to send an unreliable packet (that you do not need to move)
|
||||
// either to a node (via its active socket) or to a manual sockaddr
|
||||
qint64 sendUnreliablePacket(const NLPacket& packet, const Node& destinationNode);
|
||||
qint64 sendUnreliablePacket(const NLPacket& packet, const HifiSockAddr& sockAddr,
|
||||
const QUuid& connectionSecret = QUuid());
|
||||
qint64 sendUnreliablePacket(const NLPacket& packet, const HifiSockAddr& sockAddr, HMACAuth* hmacAuth = nullptr);
|
||||
|
||||
// use sendPacket to send a moved unreliable or reliable NL packet to a node's active socket or manual sockaddr
|
||||
qint64 sendPacket(std::unique_ptr<NLPacket> packet, const Node& destinationNode);
|
||||
qint64 sendPacket(std::unique_ptr<NLPacket> packet, const HifiSockAddr& sockAddr,
|
||||
const QUuid& connectionSecret = QUuid());
|
||||
qint64 sendPacket(std::unique_ptr<NLPacket> packet, const HifiSockAddr& sockAddr, HMACAuth* hmacAuth = nullptr);
|
||||
|
||||
// use sendUnreliableUnorderedPacketList to unreliably send separate packets from the packet list
|
||||
// either to a node's active socket or to a manual sockaddr
|
||||
qint64 sendUnreliableUnorderedPacketList(NLPacketList& packetList, const Node& destinationNode);
|
||||
qint64 sendUnreliableUnorderedPacketList(NLPacketList& packetList, const HifiSockAddr& sockAddr,
|
||||
const QUuid& connectionSecret = QUuid());
|
||||
HMACAuth* hmacAuth = nullptr);
|
||||
|
||||
// use sendPacketList to send reliable packet lists (ordered or unordered) to a node's active socket
|
||||
// or to a manual sock addr
|
||||
|
@ -155,11 +160,12 @@ public:
|
|||
size_t size() const { QReadLocker readLock(&_nodeMutex); return _nodeHash.size(); }
|
||||
|
||||
SharedNodePointer nodeWithUUID(const QUuid& nodeUUID);
|
||||
SharedNodePointer nodeWithLocalID(Node::LocalID localID) const;
|
||||
|
||||
SharedNodePointer addOrUpdateNode(const QUuid& uuid, NodeType_t nodeType,
|
||||
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket,
|
||||
bool isReplicated = false, bool isUpstream = false,
|
||||
const QUuid& connectionSecret = QUuid(),
|
||||
Node::LocalID localID = Node::NULL_LOCAL_ID, bool isReplicated = false,
|
||||
bool isUpstream = false, const QUuid& connectionSecret = QUuid(),
|
||||
const NodePermissions& permissions = DEFAULT_AGENT_PERMISSIONS);
|
||||
|
||||
static bool parseSTUNResponse(udt::BasePacket* packet, QHostAddress& newPublicAddress, uint16_t& newPublicPort);
|
||||
|
@ -180,7 +186,7 @@ public:
|
|||
void getPacketStats(float& packetsInPerSecond, float& bytesInPerSecond, float& packetsOutPerSecond, float& bytesOutPerSecond);
|
||||
void resetPacketStats();
|
||||
|
||||
std::unique_ptr<NLPacket> constructPingPacket(PingType_t pingType = PingType::Agnostic);
|
||||
std::unique_ptr<NLPacket> constructPingPacket(const QUuid& nodeId, PingType_t pingType = PingType::Agnostic);
|
||||
std::unique_ptr<NLPacket> constructPingReplyPacket(ReceivedMessage& message);
|
||||
|
||||
static std::unique_ptr<NLPacket> constructICEPingPacket(PingType_t pingType, const QUuid& iceID);
|
||||
|
@ -319,7 +325,7 @@ public slots:
|
|||
void startSTUNPublicSocketUpdate();
|
||||
virtual void sendSTUNRequest();
|
||||
|
||||
bool killNodeWithUUID(const QUuid& nodeUUID);
|
||||
bool killNodeWithUUID(const QUuid& nodeUUID, ConnectionID newConnectionID = NULL_CONNECTION_ID);
|
||||
|
||||
signals:
|
||||
void dataSent(quint8 channelType, int bytes);
|
||||
|
@ -364,14 +370,14 @@ protected:
|
|||
qint64 writePacket(const NLPacket& packet, const HifiSockAddr& destinationSockAddr,
|
||||
const QUuid& connectionSecret = QUuid());
|
||||
void collectPacketStats(const NLPacket& packet);
|
||||
void fillPacketHeader(const NLPacket& packet, const QUuid& connectionSecret = QUuid());
|
||||
void fillPacketHeader(const NLPacket& packet, HMACAuth* hmacAuth = nullptr);
|
||||
|
||||
void setLocalSocket(const HifiSockAddr& sockAddr);
|
||||
|
||||
bool packetSourceAndHashMatchAndTrackBandwidth(const udt::Packet& packet, Node* sourceNode = nullptr);
|
||||
void processSTUNResponse(std::unique_ptr<udt::BasePacket> packet);
|
||||
|
||||
void handleNodeKill(const SharedNodePointer& node);
|
||||
void handleNodeKill(const SharedNodePointer& node, ConnectionID newConnectionID = NULL_CONNECTION_ID);
|
||||
|
||||
void stopInitialSTUNUpdate(bool success);
|
||||
|
||||
|
@ -418,6 +424,7 @@ protected:
|
|||
}
|
||||
}
|
||||
|
||||
std::unordered_map<QUuid, ConnectionID> _connectionIDs;
|
||||
|
||||
private slots:
|
||||
void flagTimeForConnectionStep(ConnectionStep connectionStep, quint64 timestamp);
|
||||
|
@ -427,6 +434,9 @@ private slots:
|
|||
private:
|
||||
mutable QReadWriteLock _sessionUUIDLock;
|
||||
QUuid _sessionUUID;
|
||||
using LocalIDMapping = tbb::concurrent_unordered_map<Node::LocalID, SharedNodePointer>;
|
||||
LocalIDMapping _localIDMap;
|
||||
Node::LocalID _sessionLocalID { 0 };
|
||||
};
|
||||
|
||||
#endif // hifi_LimitedNodeList_h
|
||||
|
|
|
@ -11,10 +11,12 @@
|
|||
|
||||
#include "NLPacket.h"
|
||||
|
||||
#include "HMACAuth.h"
|
||||
|
||||
int NLPacket::localHeaderSize(PacketType type) {
|
||||
bool nonSourced = PacketTypeEnum::getNonSourcedPackets().contains(type);
|
||||
bool nonVerified = PacketTypeEnum::getNonVerifiedPackets().contains(type);
|
||||
qint64 optionalSize = (nonSourced ? 0 : NUM_BYTES_RFC4122_UUID) + ((nonSourced || nonVerified) ? 0 : NUM_BYTES_MD5_HASH);
|
||||
qint64 optionalSize = (nonSourced ? 0 : NUM_BYTES_LOCALID) + ((nonSourced || nonVerified) ? 0 : NUM_BYTES_MD5_HASH);
|
||||
return sizeof(PacketType) + sizeof(PacketVersion) + optionalSize;
|
||||
}
|
||||
int NLPacket::totalHeaderSize(PacketType type, bool isPartOfMessage) {
|
||||
|
@ -139,28 +141,25 @@ PacketVersion NLPacket::versionInHeader(const udt::Packet& packet) {
|
|||
return *reinterpret_cast<const PacketVersion*>(packet.getData() + headerOffset + sizeof(PacketType));
|
||||
}
|
||||
|
||||
QUuid NLPacket::sourceIDInHeader(const udt::Packet& packet) {
|
||||
NLPacket::LocalID NLPacket::sourceIDInHeader(const udt::Packet& packet) {
|
||||
int offset = Packet::totalHeaderSize(packet.isPartOfMessage()) + sizeof(PacketType) + sizeof(PacketVersion);
|
||||
return QUuid::fromRfc4122(QByteArray::fromRawData(packet.getData() + offset, NUM_BYTES_RFC4122_UUID));
|
||||
return *reinterpret_cast<const LocalID*>(packet.getData() + offset);
|
||||
}
|
||||
|
||||
QByteArray NLPacket::verificationHashInHeader(const udt::Packet& packet) {
|
||||
int offset = Packet::totalHeaderSize(packet.isPartOfMessage()) + sizeof(PacketType) + sizeof(PacketVersion) + NUM_BYTES_RFC4122_UUID;
|
||||
int offset = Packet::totalHeaderSize(packet.isPartOfMessage()) + sizeof(PacketType) +
|
||||
sizeof(PacketVersion) + NUM_BYTES_LOCALID;
|
||||
return QByteArray(packet.getData() + offset, NUM_BYTES_MD5_HASH);
|
||||
}
|
||||
|
||||
QByteArray NLPacket::hashForPacketAndSecret(const udt::Packet& packet, const QUuid& connectionSecret) {
|
||||
QCryptographicHash hash(QCryptographicHash::Md5);
|
||||
|
||||
QByteArray NLPacket::hashForPacketAndHMAC(const udt::Packet& packet, HMACAuth& hash) {
|
||||
int offset = Packet::totalHeaderSize(packet.isPartOfMessage()) + sizeof(PacketType) + sizeof(PacketVersion)
|
||||
+ NUM_BYTES_RFC4122_UUID + NUM_BYTES_MD5_HASH;
|
||||
+ NUM_BYTES_LOCALID + NUM_BYTES_MD5_HASH;
|
||||
|
||||
// add the packet payload and the connection UUID
|
||||
hash.addData(packet.getData() + offset, packet.getDataSize() - offset);
|
||||
hash.addData(connectionSecret.toRfc4122());
|
||||
|
||||
// return the hash
|
||||
return hash.result();
|
||||
auto hashResult { hash.result() };
|
||||
return QByteArray((const char*) hashResult.data(), (int) hashResult.size());
|
||||
}
|
||||
|
||||
void NLPacket::writeTypeAndVersion() {
|
||||
|
@ -203,22 +202,24 @@ void NLPacket::readSourceID() {
|
|||
}
|
||||
}
|
||||
|
||||
void NLPacket::writeSourceID(const QUuid& sourceID) const {
|
||||
void NLPacket::writeSourceID(LocalID sourceID) const {
|
||||
Q_ASSERT(!PacketTypeEnum::getNonSourcedPackets().contains(_type));
|
||||
|
||||
auto offset = Packet::totalHeaderSize(isPartOfMessage()) + sizeof(PacketType) + sizeof(PacketVersion);
|
||||
memcpy(_packet.get() + offset, sourceID.toRfc4122().constData(), NUM_BYTES_RFC4122_UUID);
|
||||
|
||||
memcpy(_packet.get() + offset, &sourceID, sizeof(sourceID));
|
||||
|
||||
_sourceID = sourceID;
|
||||
}
|
||||
|
||||
void NLPacket::writeVerificationHashGivenSecret(const QUuid& connectionSecret) const {
|
||||
void NLPacket::writeVerificationHash(HMACAuth& hmacAuth) const {
|
||||
Q_ASSERT(!PacketTypeEnum::getNonSourcedPackets().contains(_type) &&
|
||||
!PacketTypeEnum::getNonVerifiedPackets().contains(_type));
|
||||
|
||||
auto offset = Packet::totalHeaderSize(isPartOfMessage()) + sizeof(PacketType) + sizeof(PacketVersion)
|
||||
+ NUM_BYTES_RFC4122_UUID;
|
||||
QByteArray verificationHash = hashForPacketAndSecret(*this, connectionSecret);
|
||||
+ NUM_BYTES_LOCALID;
|
||||
|
||||
QByteArray verificationHash = hashForPacketAndHMAC(*this, hmacAuth);
|
||||
|
||||
memcpy(_packet.get() + offset, verificationHash.data(), verificationHash.size());
|
||||
}
|
||||
|
|
|
@ -18,35 +18,35 @@
|
|||
|
||||
#include "udt/Packet.h"
|
||||
|
||||
class HMACAuth;
|
||||
|
||||
class NLPacket : public udt::Packet {
|
||||
Q_OBJECT
|
||||
public:
|
||||
//
|
||||
// | BYTE | BYTE | BYTE | BYTE |
|
||||
// NLPacket format:
|
||||
//
|
||||
// | BYTE | BYTE | BYTE | BYTE |
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Packet Type | Version | |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
|
||||
// | Packet Type | Version | Local Node ID - sourced only |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | |
|
||||
// | Node UUID - 16 bytes |
|
||||
// | (ONLY FOR SOURCED PACKETS) |
|
||||
// | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | | |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
|
||||
// | MD5 Verification - 16 bytes |
|
||||
// | (ONLY FOR VERIFIED PACKETS) |
|
||||
// | |
|
||||
// | MD5 Verification - 16 bytes |
|
||||
// | (ONLY FOR VERIFIED PACKETS) |
|
||||
// | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | | |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
|
||||
// NLPacket Header Format
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
//
|
||||
|
||||
using LocalID = NetworkLocalID;
|
||||
static const LocalID NULL_LOCAL_ID = 0;
|
||||
|
||||
static const int NUM_BYTES_LOCALID = sizeof(LocalID);
|
||||
// this is used by the Octree classes - must be known at compile time
|
||||
static const int MAX_PACKET_HEADER_SIZE =
|
||||
sizeof(udt::Packet::SequenceNumberAndBitField) + sizeof(udt::Packet::MessageNumberAndBitField) +
|
||||
sizeof(PacketType) + sizeof(PacketVersion) + NUM_BYTES_RFC4122_UUID + NUM_BYTES_MD5_HASH;
|
||||
sizeof(PacketType) + sizeof(PacketVersion) + NUM_BYTES_LOCALID + NUM_BYTES_MD5_HASH;
|
||||
|
||||
static std::unique_ptr<NLPacket> create(PacketType type, qint64 size = -1,
|
||||
bool isReliable = false, bool isPartOfMessage = false, PacketVersion version = 0);
|
||||
|
@ -69,9 +69,9 @@ public:
|
|||
static PacketType typeInHeader(const udt::Packet& packet);
|
||||
static PacketVersion versionInHeader(const udt::Packet& packet);
|
||||
|
||||
static QUuid sourceIDInHeader(const udt::Packet& packet);
|
||||
static LocalID sourceIDInHeader(const udt::Packet& packet);
|
||||
static QByteArray verificationHashInHeader(const udt::Packet& packet);
|
||||
static QByteArray hashForPacketAndSecret(const udt::Packet& packet, const QUuid& connectionSecret);
|
||||
static QByteArray hashForPacketAndHMAC(const udt::Packet& packet, HMACAuth& hash);
|
||||
|
||||
PacketType getType() const { return _type; }
|
||||
void setType(PacketType type);
|
||||
|
@ -79,10 +79,10 @@ public:
|
|||
PacketVersion getVersion() const { return _version; }
|
||||
void setVersion(PacketVersion version);
|
||||
|
||||
const QUuid& getSourceID() const { return _sourceID; }
|
||||
LocalID getSourceID() const { return _sourceID; }
|
||||
|
||||
void writeSourceID(const QUuid& sourceID) const;
|
||||
void writeVerificationHashGivenSecret(const QUuid& connectionSecret) const;
|
||||
void writeSourceID(LocalID sourceID) const;
|
||||
void writeVerificationHash(HMACAuth& hmacAuth) const;
|
||||
|
||||
protected:
|
||||
|
||||
|
@ -106,7 +106,7 @@ protected:
|
|||
|
||||
PacketType _type;
|
||||
PacketVersion _version;
|
||||
mutable QUuid _sourceID;
|
||||
mutable LocalID _sourceID;
|
||||
};
|
||||
|
||||
#endif // hifi_NLPacket_h
|
||||
|
|
|
@ -22,7 +22,7 @@ public:
|
|||
bool isReliable = false, bool isOrdered = false);
|
||||
|
||||
PacketVersion getVersion() const { return _packetVersion; }
|
||||
const QUuid& getSourceID() const { return _sourceID; }
|
||||
NLPacket::LocalID getSourceID() const { return _sourceID; }
|
||||
|
||||
qint64 getMaxSegmentSize() const override { return NLPacket::maxPayloadSize(_packetType, _isOrdered); }
|
||||
|
||||
|
@ -37,7 +37,7 @@ private:
|
|||
|
||||
|
||||
PacketVersion _packetVersion;
|
||||
QUuid _sourceID;
|
||||
NLPacket::LocalID _sourceID;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(QSharedPointer<NLPacketList>)
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <Trace.h>
|
||||
#include "NodeType.h"
|
||||
|
||||
const NetworkPeer::LocalID NetworkPeer::NULL_LOCAL_ID;
|
||||
|
||||
NetworkPeer::NetworkPeer(QObject* parent) :
|
||||
QObject(parent),
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <QtCore/QTimer>
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
#include "UUID.h"
|
||||
#include "HifiSockAddr.h"
|
||||
|
||||
const QString ICE_SERVER_HOSTNAME = "localhost";
|
||||
|
@ -39,6 +40,12 @@ public:
|
|||
const QUuid& getUUID() const { return _uuid; }
|
||||
void setUUID(const QUuid& uuid) { _uuid = uuid; }
|
||||
|
||||
using LocalID = NetworkLocalID;
|
||||
static const LocalID NULL_LOCAL_ID = 0;
|
||||
|
||||
LocalID getLocalID() const { return _localID; }
|
||||
void setLocalID(LocalID localID) { _localID = localID; }
|
||||
|
||||
void softReset();
|
||||
void reset();
|
||||
|
||||
|
@ -99,6 +106,7 @@ protected:
|
|||
void setActiveSocket(HifiSockAddr* discoveredSocket);
|
||||
|
||||
QUuid _uuid;
|
||||
LocalID _localID { 0 };
|
||||
|
||||
HifiSockAddr _publicSocket;
|
||||
HifiSockAddr _localSocket;
|
||||
|
|
|
@ -86,10 +86,10 @@ NodeType_t NodeType::fromString(QString type) {
|
|||
|
||||
|
||||
Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket,
|
||||
const HifiSockAddr& localSocket, QObject* parent) :
|
||||
const HifiSockAddr& localSocket, QObject* parent) :
|
||||
NetworkPeer(uuid, publicSocket, localSocket, parent),
|
||||
_type(type),
|
||||
_pingMs(-1), // "Uninitialized"
|
||||
_authenticateHash(new HMACAuth), _pingMs(-1), // "Uninitialized"
|
||||
_clockSkewUsec(0),
|
||||
_mutex(),
|
||||
_clockSkewMovingPercentile(30, 0.8f) // moving 80th percentile of 30 samples
|
||||
|
@ -108,6 +108,7 @@ void Node::setType(char type) {
|
|||
_symmetricSocket.setObjectName(typeString);
|
||||
}
|
||||
|
||||
|
||||
void Node::updateClockSkewUsec(qint64 clockSkewSample) {
|
||||
_clockSkewMovingPercentile.updatePercentile(clockSkewSample);
|
||||
_clockSkewUsec = (quint64)_clockSkewMovingPercentile.getValueAtPercentile();
|
||||
|
@ -168,6 +169,7 @@ QDataStream& operator<<(QDataStream& out, const Node& node) {
|
|||
out << node._localSocket;
|
||||
out << node._permissions;
|
||||
out << node._isReplicated;
|
||||
out << node._localID;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
@ -178,6 +180,7 @@ QDataStream& operator>>(QDataStream& in, Node& node) {
|
|||
in >> node._localSocket;
|
||||
in >> node._permissions;
|
||||
in >> node._isReplicated;
|
||||
in >> node._localID;
|
||||
return in;
|
||||
}
|
||||
|
||||
|
@ -188,7 +191,16 @@ QDebug operator<<(QDebug debug, const Node& node) {
|
|||
} else {
|
||||
debug.nospace() << " (" << node.getType() << ")";
|
||||
}
|
||||
debug << " " << node.getUUID().toString().toLocal8Bit().constData() << " ";
|
||||
debug << " " << node.getUUID().toString().toLocal8Bit().constData() << "(" << node.getLocalID() << ") ";
|
||||
debug.nospace() << node.getPublicSocket() << "/" << node.getLocalSocket();
|
||||
return debug.nospace();
|
||||
}
|
||||
|
||||
void Node::setConnectionSecret(const QUuid& connectionSecret) {
|
||||
if (_connectionSecret == connectionSecret) {
|
||||
return;
|
||||
}
|
||||
|
||||
_connectionSecret = connectionSecret;
|
||||
_authenticateHash->setKey(_connectionSecret);
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "SimpleMovingAverage.h"
|
||||
#include "MovingPercentile.h"
|
||||
#include "NodePermissions.h"
|
||||
#include "HMACAuth.h"
|
||||
|
||||
class Node : public NetworkPeer {
|
||||
Q_OBJECT
|
||||
|
@ -55,7 +56,8 @@ public:
|
|||
void setIsUpstream(bool isUpstream) { _isUpstream = isUpstream; }
|
||||
|
||||
const QUuid& getConnectionSecret() const { return _connectionSecret; }
|
||||
void setConnectionSecret(const QUuid& connectionSecret) { _connectionSecret = connectionSecret; }
|
||||
void setConnectionSecret(const QUuid& connectionSecret);
|
||||
HMACAuth& getAuthenticateHash() const { return *_authenticateHash; }
|
||||
|
||||
NodeData* getLinkedData() const { return _linkedData.get(); }
|
||||
void setLinkedData(std::unique_ptr<NodeData> linkedData) { _linkedData = std::move(linkedData); }
|
||||
|
@ -97,6 +99,7 @@ private:
|
|||
NodeType_t _type;
|
||||
|
||||
QUuid _connectionSecret;
|
||||
std::unique_ptr<HMACAuth> _authenticateHash;
|
||||
std::unique_ptr<NodeData> _linkedData;
|
||||
bool _isReplicated { false };
|
||||
int _pingMs;
|
||||
|
|
|
@ -214,6 +214,20 @@ void NodeList::processPingPacket(QSharedPointer<ReceivedMessage> message, Shared
|
|||
sendingNode->setSymmetricSocket(senderSockAddr);
|
||||
}
|
||||
}
|
||||
|
||||
int64_t connectionID;
|
||||
|
||||
message->readPrimitive(&connectionID);
|
||||
|
||||
auto it = _connectionIDs.find(sendingNode->getUUID());
|
||||
if (it != _connectionIDs.end()) {
|
||||
if (connectionID > it->second) {
|
||||
qDebug() << "Received a ping packet with a larger connection id (" << connectionID << ">" << it->second << ") from "
|
||||
<< sendingNode->getUUID();
|
||||
killNodeWithUUID(sendingNode->getUUID(), connectionID);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void NodeList::processPingReplyPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
|
||||
|
@ -610,13 +624,21 @@ void NodeList::processDomainServerList(QSharedPointer<ReceivedMessage> message)
|
|||
return;
|
||||
}
|
||||
|
||||
// pull our owner UUID from the packet, it's always the first thing
|
||||
Node::LocalID domainLocalID;
|
||||
packetStream >> domainLocalID;
|
||||
|
||||
// pull our owner (ie. session) UUID from the packet, it's always the first thing
|
||||
// The short (16 bit) ID comes next.
|
||||
QUuid newUUID;
|
||||
Node::LocalID newLocalID;
|
||||
packetStream >> newUUID;
|
||||
packetStream >> newLocalID;
|
||||
setSessionLocalID(newLocalID);
|
||||
setSessionUUID(newUUID);
|
||||
|
||||
// if this was the first domain-server list from this domain, we've now connected
|
||||
if (!_domainHandler.isConnected()) {
|
||||
_domainHandler.setLocalID(newLocalID);
|
||||
_domainHandler.setUUID(domainUUID);
|
||||
_domainHandler.setIsConnected(true);
|
||||
|
||||
|
@ -654,12 +676,14 @@ void NodeList::processDomainServerRemovedNode(QSharedPointer<ReceivedMessage> me
|
|||
void NodeList::parseNodeFromPacketStream(QDataStream& packetStream) {
|
||||
// setup variables to read into from QDataStream
|
||||
qint8 nodeType;
|
||||
QUuid nodeUUID, connectionUUID;
|
||||
QUuid nodeUUID, connectionSecretUUID;
|
||||
HifiSockAddr nodePublicSocket, nodeLocalSocket;
|
||||
NodePermissions permissions;
|
||||
bool isReplicated;
|
||||
Node::LocalID sessionLocalID;
|
||||
|
||||
packetStream >> nodeType >> nodeUUID >> nodePublicSocket >> nodeLocalSocket >> permissions >> isReplicated;
|
||||
packetStream >> nodeType >> nodeUUID >> nodePublicSocket >> nodeLocalSocket >> permissions
|
||||
>> isReplicated >> sessionLocalID;
|
||||
|
||||
// if the public socket address is 0 then it's reachable at the same IP
|
||||
// as the domain server
|
||||
|
@ -667,10 +691,10 @@ void NodeList::parseNodeFromPacketStream(QDataStream& packetStream) {
|
|||
nodePublicSocket.setAddress(_domainHandler.getIP());
|
||||
}
|
||||
|
||||
packetStream >> connectionUUID;
|
||||
packetStream >> connectionSecretUUID;
|
||||
|
||||
SharedNodePointer node = addOrUpdateNode(nodeUUID, nodeType, nodePublicSocket,
|
||||
nodeLocalSocket, isReplicated, false, connectionUUID, permissions);
|
||||
SharedNodePointer node = addOrUpdateNode(nodeUUID, nodeType, nodePublicSocket, nodeLocalSocket,
|
||||
sessionLocalID, isReplicated, false, connectionSecretUUID, permissions);
|
||||
|
||||
// nodes that are downstream or upstream of our own type are kept alive when we hear about them from the domain server
|
||||
// and always have their public socket as their active socket
|
||||
|
@ -705,16 +729,18 @@ void NodeList::pingPunchForInactiveNode(const SharedNodePointer& node) {
|
|||
if (node->getConnectionAttempts() > 0 && node->getConnectionAttempts() % NUM_DEBUG_CONNECTION_ATTEMPTS == 0) {
|
||||
qCDebug(networking) << "No response to UDP hole punch pings for node" << node->getUUID() << "in last second.";
|
||||
}
|
||||
|
||||
auto nodeID = node->getUUID();
|
||||
|
||||
// send the ping packet to the local and public sockets for this node
|
||||
auto localPingPacket = constructPingPacket(PingType::Local);
|
||||
auto localPingPacket = constructPingPacket(nodeID, PingType::Local);
|
||||
sendPacket(std::move(localPingPacket), *node, node->getLocalSocket());
|
||||
|
||||
auto publicPingPacket = constructPingPacket(PingType::Public);
|
||||
auto publicPingPacket = constructPingPacket(nodeID, PingType::Public);
|
||||
sendPacket(std::move(publicPingPacket), *node, node->getPublicSocket());
|
||||
|
||||
if (!node->getSymmetricSocket().isNull()) {
|
||||
auto symmetricPingPacket = constructPingPacket(PingType::Symmetric);
|
||||
auto symmetricPingPacket = constructPingPacket(nodeID, PingType::Symmetric);
|
||||
sendPacket(std::move(symmetricPingPacket), *node, node->getSymmetricSocket());
|
||||
}
|
||||
|
||||
|
@ -784,7 +810,7 @@ void NodeList::sendKeepAlivePings() {
|
|||
auto type = node->getType();
|
||||
return !node->isUpstream() && _nodeTypesOfInterest.contains(type) && !NodeType::isDownstream(type);
|
||||
}, [&](const SharedNodePointer& node) {
|
||||
sendPacket(constructPingPacket(), *node);
|
||||
sendPacket(constructPingPacket(node->getUUID()), *node);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -94,6 +94,7 @@ public:
|
|||
|
||||
virtual bool isDomainServer() const override { return false; }
|
||||
virtual QUuid getDomainUUID() const override { return _domainHandler.getUUID(); }
|
||||
virtual Node::LocalID getDomainLocalID() const override { return _domainHandler.getLocalID(); }
|
||||
virtual HifiSockAddr getDomainSockAddr() const override { return _domainHandler.getSockAddr(); }
|
||||
|
||||
public slots:
|
||||
|
|
|
@ -261,10 +261,9 @@ void PacketReceiver::handleVerifiedMessage(QSharedPointer<ReceivedMessage> recei
|
|||
|
||||
SharedNodePointer matchingNode;
|
||||
|
||||
if (!receivedMessage->getSourceID().isNull()) {
|
||||
matchingNode = nodeList->nodeWithUUID(receivedMessage->getSourceID());
|
||||
if (receivedMessage->getSourceID() != Node::NULL_LOCAL_ID) {
|
||||
matchingNode = nodeList->nodeWithLocalID(receivedMessage->getSourceID());
|
||||
}
|
||||
|
||||
QMutexLocker packetListenerLocker(&_packetListenerLock);
|
||||
|
||||
auto it = _messageListenerMap.find(receivedMessage->getType());
|
||||
|
|
|
@ -43,7 +43,7 @@ ReceivedMessage::ReceivedMessage(NLPacket& packet)
|
|||
}
|
||||
|
||||
ReceivedMessage::ReceivedMessage(QByteArray byteArray, PacketType packetType, PacketVersion packetVersion,
|
||||
const HifiSockAddr& senderSockAddr, QUuid sourceID) :
|
||||
const HifiSockAddr& senderSockAddr, NLPacket::LocalID sourceID) :
|
||||
_data(byteArray),
|
||||
_headData(_data.mid(0, HEAD_DATA_SIZE)),
|
||||
_numPackets(1),
|
||||
|
|
|
@ -25,7 +25,7 @@ public:
|
|||
ReceivedMessage(const NLPacketList& packetList);
|
||||
ReceivedMessage(NLPacket& packet);
|
||||
ReceivedMessage(QByteArray byteArray, PacketType packetType, PacketVersion packetVersion,
|
||||
const HifiSockAddr& senderSockAddr, QUuid sourceID = QUuid());
|
||||
const HifiSockAddr& senderSockAddr, NLPacket::LocalID sourceID = NLPacket::NULL_LOCAL_ID);
|
||||
|
||||
QByteArray getMessage() const { return _data; }
|
||||
const char* getRawMessage() const { return _data.constData(); }
|
||||
|
@ -40,7 +40,7 @@ public:
|
|||
bool failed() const { return _failed; }
|
||||
bool isComplete() const { return _isComplete; }
|
||||
|
||||
const QUuid& getSourceID() const { return _sourceID; }
|
||||
NLPacket::LocalID getSourceID() const { return _sourceID; }
|
||||
const HifiSockAddr& getSenderSockAddr() { return _senderSockAddr; }
|
||||
|
||||
qint64 getPosition() const { return _position; }
|
||||
|
@ -93,7 +93,7 @@ private:
|
|||
std::atomic<qint64> _position { 0 };
|
||||
std::atomic<qint64> _numPackets { 0 };
|
||||
|
||||
QUuid _sourceID;
|
||||
NLPacket::LocalID _sourceID { NLPacket::NULL_LOCAL_ID };
|
||||
PacketType _packetType;
|
||||
PacketVersion _packetVersion;
|
||||
HifiSockAddr _senderSockAddr;
|
||||
|
|
|
@ -25,7 +25,7 @@ SequenceNumberStats::SequenceNumberStats(int statsHistoryLength, bool canDetectO
|
|||
: _lastReceivedSequence(0),
|
||||
_missingSet(),
|
||||
_stats(),
|
||||
_lastSenderUUID(),
|
||||
_lastSenderID(NULL_LOCAL_ID),
|
||||
_statsHistory(statsHistoryLength),
|
||||
_lastUnreasonableSequence(0),
|
||||
_consecutiveUnreasonableOnTime(0)
|
||||
|
@ -35,7 +35,7 @@ SequenceNumberStats::SequenceNumberStats(int statsHistoryLength, bool canDetectO
|
|||
void SequenceNumberStats::reset() {
|
||||
_missingSet.clear();
|
||||
_stats = PacketStreamStats();
|
||||
_lastSenderUUID = QUuid();
|
||||
_lastSenderID = NULL_LOCAL_ID;
|
||||
_statsHistory.clear();
|
||||
_lastUnreasonableSequence = 0;
|
||||
_consecutiveUnreasonableOnTime = 0;
|
||||
|
@ -43,18 +43,18 @@ void SequenceNumberStats::reset() {
|
|||
|
||||
static const int UINT16_RANGE = std::numeric_limits<uint16_t>::max() + 1;
|
||||
|
||||
SequenceNumberStats::ArrivalInfo SequenceNumberStats::sequenceNumberReceived(quint16 incoming, QUuid senderUUID, const bool wantExtraDebugging) {
|
||||
SequenceNumberStats::ArrivalInfo SequenceNumberStats::sequenceNumberReceived(quint16 incoming, NetworkLocalID senderID, const bool wantExtraDebugging) {
|
||||
|
||||
SequenceNumberStats::ArrivalInfo arrivalInfo;
|
||||
|
||||
// if the sender node has changed, reset all stats
|
||||
if (senderUUID != _lastSenderUUID) {
|
||||
if (senderID != _lastSenderID) {
|
||||
if (_stats._received > 0) {
|
||||
qCDebug(networking) << "sequence number stats was reset due to new sender node";
|
||||
qCDebug(networking) << "previous:" << _lastSenderUUID << "current:" << senderUUID;
|
||||
qCDebug(networking) << "previous:" << _lastSenderID << "current:" << senderID;
|
||||
reset();
|
||||
}
|
||||
_lastSenderUUID = senderUUID;
|
||||
_lastSenderID = senderID;
|
||||
}
|
||||
|
||||
// determine our expected sequence number... handle rollover appropriately
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
#include "SharedUtil.h"
|
||||
#include "RingBufferHistory.h"
|
||||
#include <quuid.h>
|
||||
#include "UUID.h"
|
||||
|
||||
const int MAX_REASONABLE_SEQUENCE_GAP = 1000;
|
||||
|
||||
|
@ -73,7 +73,7 @@ public:
|
|||
SequenceNumberStats(int statsHistoryLength = 0, bool canDetectOutOfSync = true);
|
||||
|
||||
void reset();
|
||||
ArrivalInfo sequenceNumberReceived(quint16 incoming, QUuid senderUUID = QUuid(), const bool wantExtraDebugging = false);
|
||||
ArrivalInfo sequenceNumberReceived(quint16 incoming, NetworkLocalID senderID = NULL_LOCAL_ID, const bool wantExtraDebugging = false);
|
||||
void pruneMissingSet(const bool wantExtraDebugging = false);
|
||||
void pushStatsToHistory() { _statsHistory.insert(_stats); }
|
||||
|
||||
|
@ -100,7 +100,8 @@ private:
|
|||
|
||||
PacketStreamStats _stats;
|
||||
|
||||
QUuid _lastSenderUUID;
|
||||
NetworkLocalID _lastSenderID;
|
||||
static const NetworkLocalID NULL_LOCAL_ID = (NetworkLocalID) 0;
|
||||
|
||||
RingBufferHistory<PacketStreamStats> _statsHistory;
|
||||
|
||||
|
|
|
@ -24,6 +24,8 @@ int packetTypeMetaTypeId = qRegisterMetaType<PacketType>();
|
|||
|
||||
PacketVersion versionForPacketType(PacketType packetType) {
|
||||
switch (packetType) {
|
||||
case PacketType::StunResponse:
|
||||
return 17;
|
||||
case PacketType::DomainList:
|
||||
return static_cast<PacketVersion>(DomainListVersion::GetMachineFingerprintFromUUIDSupport);
|
||||
case PacketType::EntityAdd:
|
||||
|
@ -40,8 +42,21 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
return static_cast<PacketVersion>(AvatarMixerPacketVersion::FBXReaderNodeReparenting);
|
||||
case PacketType::MessagesData:
|
||||
return static_cast<PacketVersion>(MessageDataVersion::TextOrBinaryData);
|
||||
// ICE packets
|
||||
case PacketType::ICEServerPeerInformation:
|
||||
return 17;
|
||||
case PacketType::ICEServerHeartbeatACK:
|
||||
return 17;
|
||||
case PacketType::ICEServerQuery:
|
||||
return 17;
|
||||
case PacketType::ICEServerHeartbeat:
|
||||
return 18; // ICE Server Heartbeat signing
|
||||
case PacketType::ICEPing:
|
||||
return static_cast<PacketVersion>(IcePingVersion::SendICEPeerID);
|
||||
case PacketType::ICEPingReply:
|
||||
return 17;
|
||||
case PacketType::ICEServerHeartbeatDenied:
|
||||
return 17;
|
||||
case PacketType::AssetMappingOperation:
|
||||
case PacketType::AssetMappingOperationReply:
|
||||
return static_cast<PacketVersion>(AssetServerPacketVersion::RedirectedMappings);
|
||||
|
@ -71,12 +86,12 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
case PacketType::MicrophoneAudioWithEcho:
|
||||
case PacketType::AudioStreamStats:
|
||||
return static_cast<PacketVersion>(AudioVersion::HighDynamicRangeVolume);
|
||||
case PacketType::ICEPing:
|
||||
return static_cast<PacketVersion>(IcePingVersion::SendICEPeerID);
|
||||
case PacketType::DomainSettings:
|
||||
return 18; // replace min_avatar_scale and max_avatar_scale with min_avatar_height and max_avatar_height
|
||||
case PacketType::Ping:
|
||||
return static_cast<PacketVersion>(PingVersion::IncludeConnectionID);
|
||||
default:
|
||||
return 17;
|
||||
return 19;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -323,4 +323,8 @@ enum class IcePingVersion : PacketVersion {
|
|||
SendICEPeerID = 18
|
||||
};
|
||||
|
||||
enum class PingVersion : PacketVersion {
|
||||
IncludeConnectionID = 18
|
||||
};
|
||||
|
||||
#endif // hifi_PacketHeaders_h
|
||||
|
|
|
@ -96,7 +96,7 @@ void OctreeProcessor::processDatagram(ReceivedMessage& message, SharedNodePointe
|
|||
quint64 totalUncompress = 0;
|
||||
quint64 totalReadBitsteam = 0;
|
||||
|
||||
const QUuid& sourceUUID = message.getSourceID();
|
||||
const QUuid& sourceUUID = sourceNode->getUUID();
|
||||
|
||||
int subsection = 1;
|
||||
|
||||
|
|
|
@ -13,14 +13,6 @@
|
|||
|
||||
<@func declareLightingModel()@>
|
||||
|
||||
#ifndef PRECISIONQ
|
||||
#ifdef GL_ES
|
||||
#define PRECISIONQ highp
|
||||
#else
|
||||
#define PRECISIONQ
|
||||
#endif
|
||||
#endif
|
||||
|
||||
struct LightingModel {
|
||||
PRECISIONQ vec4 _UnlitEmissiveLightmapBackground;
|
||||
PRECISIONQ vec4 _ScatteringDiffuseSpecularAlbedo;
|
||||
|
|
|
@ -36,8 +36,8 @@ void main(void) {
|
|||
} else if (frag.mode == FRAG_MODE_LIGHTMAPPED) {
|
||||
discard;
|
||||
} else {
|
||||
vec4 midNormalCurvature;
|
||||
vec4 lowNormalCurvature;
|
||||
vec4 midNormalCurvature = vec4(0);
|
||||
vec4 lowNormalCurvature = vec4(0);
|
||||
if (frag.mode == FRAG_MODE_SCATTERING) {
|
||||
unpackMidLowNormalCurvature(_texCoord0, midNormalCurvature, lowNormalCurvature);
|
||||
}
|
||||
|
|
|
@ -37,8 +37,8 @@ void main(void) {
|
|||
} else if (frag.mode == FRAG_MODE_LIGHTMAPPED) {
|
||||
discard;
|
||||
} else {
|
||||
vec4 midNormalCurvature;
|
||||
vec4 lowNormalCurvature;
|
||||
vec4 midNormalCurvature = vec4(0);
|
||||
vec4 lowNormalCurvature = vec4(0);
|
||||
if (frag.mode == FRAG_MODE_SCATTERING) {
|
||||
unpackMidLowNormalCurvature(_texCoord0, midNormalCurvature, lowNormalCurvature);
|
||||
}
|
||||
|
|
|
@ -33,8 +33,8 @@ void main(void) {
|
|||
} else if (frag.mode == FRAG_MODE_LIGHTMAPPED) {
|
||||
discard;
|
||||
} else {
|
||||
vec4 midNormalCurvature;
|
||||
vec4 lowNormalCurvature;
|
||||
vec4 midNormalCurvature = vec4(0);
|
||||
vec4 lowNormalCurvature = vec4(0);
|
||||
if (frag.mode == FRAG_MODE_SCATTERING) {
|
||||
unpackMidLowNormalCurvature(_texCoord0, midNormalCurvature, lowNormalCurvature);
|
||||
}
|
||||
|
|
|
@ -38,8 +38,8 @@ void main(void) {
|
|||
} else if (frag.mode == FRAG_MODE_LIGHTMAPPED) {
|
||||
discard;
|
||||
} else {
|
||||
vec4 midNormalCurvature;
|
||||
vec4 lowNormalCurvature;
|
||||
vec4 midNormalCurvature = vec4(0);
|
||||
vec4 lowNormalCurvature = vec4(0);
|
||||
if (frag.mode == FRAG_MODE_SCATTERING) {
|
||||
unpackMidLowNormalCurvature(_texCoord0, midNormalCurvature, lowNormalCurvature);
|
||||
}
|
||||
|
|
|
@ -22,10 +22,10 @@
|
|||
<@include MaterialTextures.slh@>
|
||||
<$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, METALLIC, EMISSIVE, OCCLUSION)$>
|
||||
|
||||
in vec4 _position;
|
||||
in vec4 _positionES;
|
||||
in vec2 _texCoord0;
|
||||
in vec2 _texCoord1;
|
||||
in vec3 _normal;
|
||||
in vec3 _normalWS;
|
||||
in vec3 _color;
|
||||
|
||||
layout(location = 0) out vec4 _fragColor0;
|
||||
|
@ -54,8 +54,8 @@ void main(void) {
|
|||
<$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>;
|
||||
vec3 fresnel = getFresnelF0(metallic, albedo);
|
||||
|
||||
vec3 fragPosition = _position.xyz;
|
||||
vec3 fragNormal = normalize(_normal);
|
||||
vec3 fragPosition = _positionES.xyz;
|
||||
vec3 fragNormal = normalize(_normalWS);
|
||||
|
||||
TransformCamera cam = getTransformCamera();
|
||||
|
||||
|
|
|
@ -26,8 +26,8 @@
|
|||
|
||||
in vec2 _texCoord0;
|
||||
in vec2 _texCoord1;
|
||||
in vec4 _position;
|
||||
in vec3 _normal;
|
||||
in vec4 _positionES;
|
||||
in vec3 _normalWS;
|
||||
in vec3 _color;
|
||||
in float _alpha;
|
||||
|
||||
|
@ -56,8 +56,8 @@ void main(void) {
|
|||
vec3 emissive = getMaterialEmissive(mat);
|
||||
<$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>;
|
||||
|
||||
vec3 fragPosition = _position.xyz;
|
||||
vec3 fragNormal = normalize(_normal);
|
||||
vec3 fragPosition = _positionES.xyz;
|
||||
vec3 fragNormal = normalize(_normalWS);
|
||||
|
||||
TransformCamera cam = getTransformCamera();
|
||||
|
||||
|
|
|
@ -19,9 +19,7 @@
|
|||
<$declareMaterialTextures(ALBEDO)$>
|
||||
|
||||
in vec2 _texCoord0;
|
||||
in vec3 _normal;
|
||||
in vec3 _color;
|
||||
in float _alpha;
|
||||
|
||||
layout(location = 0) out vec4 _fragColor0;
|
||||
|
||||
|
|
|
@ -17,12 +17,9 @@
|
|||
<$declareEvalSkyboxGlobalColor()$>
|
||||
|
||||
// the interpolated normal
|
||||
in vec3 _normal;
|
||||
in vec3 _modelNormal;
|
||||
in vec3 _normalWS;
|
||||
in vec4 _color;
|
||||
in vec2 _texCoord0;
|
||||
in vec4 _position;
|
||||
in vec4 _eyePosition;
|
||||
in vec4 _positionES;
|
||||
|
||||
layout(location = 0) out vec4 _fragColor0;
|
||||
|
||||
|
@ -33,7 +30,7 @@ layout(location = 0) out vec4 _fragColor0;
|
|||
|
||||
#line 2030
|
||||
void main(void) {
|
||||
vec3 normal = normalize(_normal.xyz);
|
||||
vec3 normal = normalize(_normalWS.xyz);
|
||||
vec3 diffuse = _color.rgb;
|
||||
vec3 specular = DEFAULT_SPECULAR;
|
||||
float shininess = DEFAULT_SHININESS;
|
||||
|
@ -53,7 +50,7 @@ void main(void) {
|
|||
#endif
|
||||
|
||||
TransformCamera cam = getTransformCamera();
|
||||
vec3 fragPosition = _eyePosition.xyz;
|
||||
vec3 fragPosition = _positionES.xyz;
|
||||
|
||||
if (emissiveAmount > 0.0) {
|
||||
_fragColor0 = vec4(evalSkyboxGlobalColor(
|
||||
|
|
|
@ -23,10 +23,10 @@
|
|||
uniform sampler2D originalTexture;
|
||||
|
||||
// the interpolated normal
|
||||
in vec3 _normal;
|
||||
in vec3 _normalWS;
|
||||
in vec4 _color;
|
||||
in vec2 _texCoord0;
|
||||
in vec4 _eyePosition;
|
||||
in vec4 _positionES;
|
||||
|
||||
layout(location = 0) out vec4 _fragColor0;
|
||||
|
||||
|
@ -35,14 +35,14 @@ void main(void) {
|
|||
float colorAlpha = _color.a * texel.a;
|
||||
|
||||
TransformCamera cam = getTransformCamera();
|
||||
vec3 fragPosition = _eyePosition.xyz;
|
||||
vec3 fragPosition = _positionES.xyz;
|
||||
|
||||
_fragColor0 = vec4(evalSkyboxGlobalColor(
|
||||
cam._viewInverse,
|
||||
1.0,
|
||||
DEFAULT_OCCLUSION,
|
||||
fragPosition,
|
||||
normalize(_normal),
|
||||
normalize(_normalWS),
|
||||
_color.rgb * texel.rgb,
|
||||
DEFAULT_FRESNEL,
|
||||
DEFAULT_METALLIC,
|
||||
|
|
|
@ -23,10 +23,10 @@
|
|||
uniform sampler2D originalTexture;
|
||||
|
||||
// the interpolated normal
|
||||
in vec3 _normal;
|
||||
in vec3 _normalWS;
|
||||
in vec4 _color;
|
||||
in vec2 _texCoord0;
|
||||
in vec4 _eyePosition;
|
||||
in vec4 _positionES;
|
||||
|
||||
layout(location = 0) out vec4 _fragColor0;
|
||||
|
||||
|
@ -35,14 +35,14 @@ void main(void) {
|
|||
float colorAlpha = _color.a * texel.a;
|
||||
|
||||
TransformCamera cam = getTransformCamera();
|
||||
vec3 fragPosition = _eyePosition.xyz;
|
||||
vec3 fragPosition = _positionES.xyz;
|
||||
|
||||
_fragColor0 = vec4(evalGlobalLightingAlphaBlendedWithHaze(
|
||||
cam._viewInverse,
|
||||
1.0,
|
||||
DEFAULT_OCCLUSION,
|
||||
fragPosition,
|
||||
normalize(_normal),
|
||||
normalize(_normalWS),
|
||||
_color.rgb * texel.rgb,
|
||||
DEFAULT_FRESNEL,
|
||||
DEFAULT_METALLIC,
|
||||
|
|
|
@ -17,11 +17,9 @@
|
|||
<$declareEvalGlobalLightingAlphaBlended()$>
|
||||
|
||||
// the interpolated normal
|
||||
in vec3 _normal;
|
||||
in vec3 _modelNormal;
|
||||
in vec3 _normalWS;
|
||||
in vec4 _color;
|
||||
in vec2 _texCoord0;
|
||||
in vec4 _eyePosition;
|
||||
in vec4 _positionES;
|
||||
|
||||
layout(location = 0) out vec4 _fragColor0;
|
||||
|
||||
|
@ -32,7 +30,7 @@ layout(location = 0) out vec4 _fragColor0;
|
|||
|
||||
#line 2030
|
||||
void main(void) {
|
||||
vec3 normal = normalize(_normal.xyz);
|
||||
vec3 normal = normalize(_normalWS.xyz);
|
||||
vec3 diffuse = _color.rgb;
|
||||
vec3 specular = DEFAULT_SPECULAR;
|
||||
float shininess = DEFAULT_SHININESS;
|
||||
|
@ -52,7 +50,7 @@ void main(void) {
|
|||
#endif
|
||||
|
||||
TransformCamera cam = getTransformCamera();
|
||||
vec3 fragPosition = _eyePosition.xyz;
|
||||
vec3 fragPosition = _positionES.xyz;
|
||||
|
||||
if (emissiveAmount > 0.0) {
|
||||
_fragColor0 = vec4(evalGlobalLightingAlphaBlendedWithHaze(
|
||||
|
|
|
@ -62,6 +62,7 @@ void main(void) {
|
|||
// Frag to eye vec
|
||||
vec4 fragEyeVector = invViewMat * vec4(-frag.position.xyz, 0.0);
|
||||
vec3 fragEyeDir = normalize(fragEyeVector.xyz);
|
||||
_fragColor = vec4(0, 0, 0, 1);
|
||||
|
||||
int numLightTouching = 0;
|
||||
int lightClusterOffset = cluster.z;
|
||||
|
|
|
@ -45,8 +45,8 @@ void main(void) {
|
|||
discard;
|
||||
}
|
||||
|
||||
vec4 midNormalCurvature;
|
||||
vec4 lowNormalCurvature;
|
||||
vec4 midNormalCurvature = vec4(0);
|
||||
vec4 lowNormalCurvature = vec4(0);
|
||||
if (frag.mode == FRAG_MODE_SCATTERING) {
|
||||
unpackMidLowNormalCurvature(texCoord, midNormalCurvature, lowNormalCurvature);
|
||||
}
|
||||
|
|
|
@ -19,10 +19,9 @@
|
|||
<@include MaterialTextures.slh@>
|
||||
<$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, METALLIC, EMISSIVE, OCCLUSION, SCATTERING)$>
|
||||
|
||||
in vec4 _position;
|
||||
in vec2 _texCoord0;
|
||||
in vec2 _texCoord1;
|
||||
in vec3 _normal;
|
||||
in vec3 _normalWS;
|
||||
in vec3 _color;
|
||||
|
||||
|
||||
|
@ -53,7 +52,7 @@ void main(void) {
|
|||
<$evalMaterialScattering(scatteringTex, scattering, matKey, scattering)$>;
|
||||
|
||||
packDeferredFragment(
|
||||
normalize(_normal),
|
||||
normalize(_normalWS),
|
||||
opacity,
|
||||
albedo,
|
||||
roughness,
|
||||
|
|
|
@ -23,8 +23,8 @@ out vec3 _color;
|
|||
out float _alpha;
|
||||
out vec2 _texCoord0;
|
||||
out vec2 _texCoord1;
|
||||
out vec4 _position;
|
||||
out vec3 _normal;
|
||||
out vec4 _positionES;
|
||||
out vec3 _normalWS;
|
||||
|
||||
void main(void) {
|
||||
_color = color_sRGBToLinear(inColor.xyz);
|
||||
|
@ -37,6 +37,6 @@ void main(void) {
|
|||
// standard transform
|
||||
TransformCamera cam = getTransformCamera();
|
||||
TransformObject obj = getTransformObject();
|
||||
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _position, gl_Position)$>
|
||||
<$transformModelToWorldDir(cam, obj, inNormal.xyz, _normal)$>
|
||||
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _positionES, gl_Position)$>
|
||||
<$transformModelToWorldDir(cam, obj, inNormal.xyz, _normalWS)$>
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
<@include Fade.slh@>
|
||||
<$declareFadeFragment()$>
|
||||
|
||||
in vec4 _positionES;
|
||||
in vec2 _texCoord0;
|
||||
in vec2 _texCoord1;
|
||||
in vec3 _normalWS;
|
||||
|
|
|
@ -20,10 +20,9 @@
|
|||
<$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, METALLIC)$>
|
||||
<$declareMaterialLightmap()$>
|
||||
|
||||
in vec4 _position;
|
||||
in vec2 _texCoord0;
|
||||
in vec2 _texCoord1;
|
||||
in vec3 _normal;
|
||||
in vec3 _normalWS;
|
||||
in vec3 _color;
|
||||
|
||||
void main(void) {
|
||||
|
@ -33,7 +32,7 @@ void main(void) {
|
|||
<$fetchMaterialTexturesCoord1(matKey, _texCoord1, _SCRIBE_NULL, lightmapVal)$>
|
||||
|
||||
packDeferredFragmentLightmap(
|
||||
normalize(_normal),
|
||||
normalize(_normalWS),
|
||||
evalOpaqueFinalAlpha(getMaterialOpacity(mat), albedo.a),
|
||||
getMaterialAlbedo(mat) * albedo.rgb * _color,
|
||||
getMaterialRoughness(mat) * roughness,
|
||||
|
|
|
@ -20,10 +20,10 @@
|
|||
<@include MaterialTextures.slh@>
|
||||
<$declareMaterialTexMapArrayBuffer()$>
|
||||
|
||||
out vec4 _position;
|
||||
out vec4 _positionES;
|
||||
out vec2 _texCoord0;
|
||||
out vec2 _texCoord1;
|
||||
out vec3 _normal;
|
||||
out vec3 _normalWS;
|
||||
out vec3 _color;
|
||||
|
||||
void main(void) {
|
||||
|
@ -38,7 +38,6 @@ void main(void) {
|
|||
// standard transform
|
||||
TransformCamera cam = getTransformCamera();
|
||||
TransformObject obj = getTransformObject();
|
||||
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _position, gl_Position)$>
|
||||
<$transformModelToWorldDir(cam, obj, inNormal.xyz, _normal)$>
|
||||
}
|
||||
|
||||
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _positionES, gl_Position)$>
|
||||
<$transformModelToWorldDir(cam, obj, inNormal.xyz, _normalWS)$>
|
||||
}
|
|
@ -23,19 +23,18 @@
|
|||
<@include Fade.slh@>
|
||||
<$declareFadeFragment()$>
|
||||
|
||||
in vec4 _position;
|
||||
in vec2 _texCoord0;
|
||||
in vec2 _texCoord1;
|
||||
in vec3 _normal;
|
||||
in vec3 _normalWS;
|
||||
in vec3 _color;
|
||||
in vec4 _worldPosition;
|
||||
in vec4 _positionWS;
|
||||
|
||||
void main(void) {
|
||||
vec3 fadeEmissive;
|
||||
FadeObjectParams fadeParams;
|
||||
|
||||
<$fetchFadeObjectParams(fadeParams)$>
|
||||
applyFade(fadeParams, _worldPosition.xyz, fadeEmissive);
|
||||
applyFade(fadeParams, _positionWS.xyz, fadeEmissive);
|
||||
|
||||
Material mat = getMaterial();
|
||||
BITFIELD matKey = getMaterialKey(mat);
|
||||
|
@ -43,7 +42,7 @@ void main(void) {
|
|||
<$fetchMaterialTexturesCoord1(matKey, _texCoord1, _SCRIBE_NULL, lightmapVal)$>
|
||||
|
||||
packDeferredFragmentLightmap(
|
||||
normalize(_normal),
|
||||
normalize(_normalWS),
|
||||
evalOpaqueFinalAlpha(getMaterialOpacity(mat), albedo.a),
|
||||
getMaterialAlbedo(mat) * albedo.rgb * _color,
|
||||
getMaterialRoughness(mat) * roughness,
|
||||
|
|
|
@ -20,12 +20,12 @@
|
|||
<@include MaterialTextures.slh@>
|
||||
<$declareMaterialTexMapArrayBuffer()$>
|
||||
|
||||
out vec4 _position;
|
||||
out vec4 _positionES;
|
||||
out vec2 _texCoord0;
|
||||
out vec2 _texCoord1;
|
||||
out vec3 _normal;
|
||||
out vec3 _normalWS;
|
||||
out vec3 _color;
|
||||
out vec4 _worldPosition;
|
||||
out vec4 _positionWS;
|
||||
|
||||
void main(void) {
|
||||
// pass along the color in linear space
|
||||
|
@ -39,8 +39,8 @@ void main(void) {
|
|||
// standard transform
|
||||
TransformCamera cam = getTransformCamera();
|
||||
TransformObject obj = getTransformObject();
|
||||
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _position, gl_Position)$>
|
||||
<$transformModelToWorldPos(obj, inPosition, _worldPosition)$>
|
||||
<$transformModelToWorldDir(cam, obj, inNormal.xyz, _normal)$>
|
||||
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _positionES, gl_Position)$>
|
||||
<$transformModelToWorldPos(obj, inPosition, _positionWS)$>
|
||||
<$transformModelToWorldDir(cam, obj, inNormal.xyz, _normalWS)$>
|
||||
}
|
||||
|
||||
|
|
|
@ -20,11 +20,11 @@
|
|||
<$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, METALLIC)$>
|
||||
<$declareMaterialLightmap()$>
|
||||
|
||||
in vec4 _position;
|
||||
in vec4 _positionES;
|
||||
in vec2 _texCoord0;
|
||||
in vec2 _texCoord1;
|
||||
in vec3 _normal;
|
||||
in vec3 _tangent;
|
||||
in vec3 _normalWS;
|
||||
in vec3 _tangentWS;
|
||||
in vec3 _color;
|
||||
|
||||
void main(void) {
|
||||
|
@ -34,7 +34,7 @@ void main(void) {
|
|||
<$fetchMaterialTexturesCoord1(matKey, _texCoord1, _SCRIBE_NULL, lightmapVal)$>
|
||||
|
||||
vec3 fragNormal;
|
||||
<$evalMaterialNormalLOD(_position, normalTexel, _normal, _tangent, fragNormal)$>
|
||||
<$evalMaterialNormalLOD(_positionES, normalTexel, _normalWS, _tangentWS, fragNormal)$>
|
||||
|
||||
packDeferredFragmentLightmap(
|
||||
normalize(fragNormal.xyz),
|
||||
|
|
|
@ -20,11 +20,11 @@
|
|||
<@include MaterialTextures.slh@>
|
||||
<$declareMaterialTexMapArrayBuffer()$>
|
||||
|
||||
out vec4 _position;
|
||||
out vec4 _positionES;
|
||||
out vec2 _texCoord0;
|
||||
out vec2 _texCoord1;
|
||||
out vec3 _normal;
|
||||
out vec3 _tangent;
|
||||
out vec3 _normalWS;
|
||||
out vec3 _tangentWS;
|
||||
out vec3 _color;
|
||||
|
||||
void main(void) {
|
||||
|
@ -38,7 +38,7 @@ void main(void) {
|
|||
// standard transform
|
||||
TransformCamera cam = getTransformCamera();
|
||||
TransformObject obj = getTransformObject();
|
||||
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _position, gl_Position)$>
|
||||
<$transformModelToWorldDir(cam, obj, inNormal.xyz, _normal)$>
|
||||
<$transformModelToWorldDir(cam, obj, inTangent.xyz, _tangent)$>
|
||||
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _positionES, gl_Position)$>
|
||||
<$transformModelToWorldDir(cam, obj, inNormal.xyz, _normalWS)$>
|
||||
<$transformModelToWorldDir(cam, obj, inTangent.xyz, _tangentWS)$>
|
||||
}
|
||||
|
|
|
@ -23,20 +23,20 @@
|
|||
<@include Fade.slh@>
|
||||
<$declareFadeFragment()$>
|
||||
|
||||
in vec4 _position;
|
||||
in vec4 _positionES;
|
||||
in vec2 _texCoord0;
|
||||
in vec2 _texCoord1;
|
||||
in vec3 _normal;
|
||||
in vec3 _tangent;
|
||||
in vec3 _normalWS;
|
||||
in vec3 _tangentWS;
|
||||
in vec3 _color;
|
||||
in vec4 _worldPosition;
|
||||
in vec4 _positionWS;
|
||||
|
||||
void main(void) {
|
||||
vec3 fadeEmissive;
|
||||
FadeObjectParams fadeParams;
|
||||
|
||||
<$fetchFadeObjectParams(fadeParams)$>
|
||||
applyFade(fadeParams, _worldPosition.xyz, fadeEmissive);
|
||||
applyFade(fadeParams, _positionWS.xyz, fadeEmissive);
|
||||
|
||||
Material mat = getMaterial();
|
||||
BITFIELD matKey = getMaterialKey(mat);
|
||||
|
@ -44,7 +44,7 @@ void main(void) {
|
|||
<$fetchMaterialTexturesCoord1(matKey, _texCoord1, _SCRIBE_NULL, lightmapVal)$>
|
||||
|
||||
vec3 fragNormal;
|
||||
<$evalMaterialNormalLOD(_position, normalTexel, _normal, _tangent, fragNormal)$>
|
||||
<$evalMaterialNormalLOD(_positionES, normalTexel, _normalWS, _tangentWS, fragNormal)$>
|
||||
|
||||
packDeferredFragmentLightmap(
|
||||
normalize(fragNormal.xyz),
|
||||
|
|
|
@ -20,13 +20,13 @@
|
|||
<@include MaterialTextures.slh@>
|
||||
<$declareMaterialTexMapArrayBuffer()$>
|
||||
|
||||
out vec4 _position;
|
||||
out vec4 _positionES;
|
||||
out vec2 _texCoord0;
|
||||
out vec2 _texCoord1;
|
||||
out vec3 _normal;
|
||||
out vec3 _tangent;
|
||||
out vec3 _normalWS;
|
||||
out vec3 _tangentWS;
|
||||
out vec3 _color;
|
||||
out vec4 _worldPosition;
|
||||
out vec4 _positionWS;
|
||||
|
||||
void main(void) {
|
||||
// pass along the color in linear space
|
||||
|
@ -39,8 +39,8 @@ void main(void) {
|
|||
// standard transform
|
||||
TransformCamera cam = getTransformCamera();
|
||||
TransformObject obj = getTransformObject();
|
||||
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _position, gl_Position)$>
|
||||
<$transformModelToWorldPos(obj, inPosition, _worldPosition)$>
|
||||
<$transformModelToWorldDir(cam, obj, inNormal.xyz, _normal)$>
|
||||
<$transformModelToWorldDir(cam, obj, inTangent.xyz, _tangent)$>
|
||||
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _positionES, gl_Position)$>
|
||||
<$transformModelToWorldPos(obj, inPosition, _positionWS)$>
|
||||
<$transformModelToWorldDir(cam, obj, inNormal.xyz, _normalWS)$>
|
||||
<$transformModelToWorldDir(cam, obj, inTangent.xyz, _tangentWS)$>
|
||||
}
|
||||
|
|
|
@ -17,13 +17,13 @@
|
|||
|
||||
layout(location = 0) out vec4 _fragColor;
|
||||
|
||||
in vec4 _worldPosition;
|
||||
in vec4 _positionWS;
|
||||
|
||||
void main(void) {
|
||||
FadeObjectParams fadeParams;
|
||||
|
||||
<$fetchFadeObjectParams(fadeParams)$>
|
||||
applyFadeClip(fadeParams, _worldPosition.xyz);
|
||||
applyFadeClip(fadeParams, _positionWS.xyz);
|
||||
|
||||
// pass-through to set z-buffer
|
||||
_fragColor = vec4(1.0, 1.0, 1.0, 0.0);
|
||||
|
|
|
@ -18,12 +18,12 @@
|
|||
|
||||
<$declareStandardTransform()$>
|
||||
|
||||
out vec4 _worldPosition;
|
||||
out vec4 _positionWS;
|
||||
|
||||
void main(void) {
|
||||
// standard transform
|
||||
TransformCamera cam = getTransformCamera();
|
||||
TransformObject obj = getTransformObject();
|
||||
<$transformModelToClipPos(cam, obj, inPosition, gl_Position)$>
|
||||
<$transformModelToWorldPos(obj, inPosition, _worldPosition)$>
|
||||
<$transformModelToWorldPos(obj, inPosition, _positionWS)$>
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
<$declareMaterialTextures(ALBEDO)$>
|
||||
|
||||
in vec2 _texCoord0;
|
||||
in vec3 _normal;
|
||||
in vec3 _normalWS;
|
||||
in vec3 _color;
|
||||
in float _alpha;
|
||||
|
||||
|
@ -39,7 +39,7 @@ void main(void) {
|
|||
albedo *= _color;
|
||||
|
||||
packDeferredFragmentUnlit(
|
||||
normalize(_normal),
|
||||
normalize(_normalWS),
|
||||
opacity,
|
||||
albedo * isUnlitEnabled());
|
||||
}
|
||||
|
|
|
@ -34,8 +34,6 @@ vec4 evalGlobalColor(float shadowAttenuation, vec3 position, vec3 normal, vec3 a
|
|||
LightAmbient ambient = getLightAmbient();
|
||||
|
||||
TransformCamera cam = getTransformCamera();
|
||||
vec3 fragNormal;
|
||||
<$transformEyeToWorldDir(cam, normal, fragNormal)$>
|
||||
vec3 fragEyeVectorView = normalize(-position);
|
||||
vec3 fragEyeDir;
|
||||
<$transformEyeToWorldDir(cam, fragEyeVectorView, fragEyeDir)$>
|
||||
|
@ -57,8 +55,8 @@ vec4 evalGlobalColor(float shadowAttenuation, vec3 position, vec3 normal, vec3 a
|
|||
uniform sampler2D originalTexture;
|
||||
|
||||
in vec2 _texCoord0;
|
||||
in vec4 _position;
|
||||
in vec3 _normal;
|
||||
in vec4 _positionES;
|
||||
in vec3 _normalWS;
|
||||
in vec3 _color;
|
||||
in float _alpha;
|
||||
|
||||
|
@ -67,8 +65,8 @@ out vec4 _fragColor;
|
|||
void main(void) {
|
||||
vec4 albedo = texture(originalTexture, _texCoord0);
|
||||
|
||||
vec3 fragPosition = _position.xyz;
|
||||
vec3 fragNormal = normalize(_normal);
|
||||
vec3 fragPosition = _positionES.xyz;
|
||||
vec3 fragNormal = normalize(_normalWS);
|
||||
vec3 fragAlbedo = albedo.rgb * _color;
|
||||
float fragMetallic = 0.0;
|
||||
vec3 fragSpecular = vec3(0.1);
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
out vec3 _color;
|
||||
out float _alpha;
|
||||
out vec2 _texCoord0;
|
||||
out vec4 _position;
|
||||
out vec3 _normal;
|
||||
out vec4 _positionES;
|
||||
out vec3 _normalWS;
|
||||
|
||||
void main(void) {
|
||||
_color = color_sRGBToLinear(inColor.xyz);
|
||||
|
@ -31,6 +31,6 @@ void main(void) {
|
|||
// standard transform
|
||||
TransformCamera cam = getTransformCamera();
|
||||
TransformObject obj = getTransformObject();
|
||||
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _position, gl_Position)$>
|
||||
<$transformModelToWorldDir(cam, obj, inNormal.xyz, _normal)$>
|
||||
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _positionES, gl_Position)$>
|
||||
<$transformModelToWorldDir(cam, obj, inNormal.xyz, _normalWS)$>
|
||||
}
|
||||
|
|
|
@ -24,8 +24,8 @@
|
|||
|
||||
in vec2 _texCoord0;
|
||||
in vec2 _texCoord1;
|
||||
in vec4 _position;
|
||||
in vec3 _normal;
|
||||
in vec4 _positionES;
|
||||
in vec3 _normalWS;
|
||||
in vec3 _color;
|
||||
in float _alpha;
|
||||
|
||||
|
@ -55,19 +55,16 @@ void main(void) {
|
|||
<$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>;
|
||||
|
||||
|
||||
vec3 fragPosition = _position.xyz;
|
||||
//vec3 fragNormal = normalize(_normal);
|
||||
vec3 fragPosition = _positionES.xyz;
|
||||
|
||||
TransformCamera cam = getTransformCamera();
|
||||
vec3 fragNormal;
|
||||
<$transformEyeToWorldDir(cam, _normal, fragNormal)$>;
|
||||
|
||||
vec4 color = vec4(evalSkyboxGlobalColor(
|
||||
cam._viewInverse,
|
||||
1.0,
|
||||
occlusionTex,
|
||||
fragPosition,
|
||||
fragNormal,
|
||||
normalize(_normalWS),
|
||||
albedo,
|
||||
fresnel,
|
||||
metallic,
|
||||
|
|
|
@ -23,10 +23,9 @@
|
|||
|
||||
in vec2 _texCoord0;
|
||||
in vec2 _texCoord1;
|
||||
in vec4 _position;
|
||||
in vec3 _normal;
|
||||
in vec4 _positionES;
|
||||
in vec3 _normalWS;
|
||||
in vec3 _color;
|
||||
in float _alpha;
|
||||
|
||||
out vec4 _fragColor;
|
||||
|
||||
|
@ -52,19 +51,15 @@ void main(void) {
|
|||
vec3 emissive = getMaterialEmissive(mat);
|
||||
<$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>;
|
||||
|
||||
|
||||
vec3 fragPosition = _position.xyz;
|
||||
|
||||
vec3 fragPosition = _positionES.xyz;
|
||||
TransformCamera cam = getTransformCamera();
|
||||
vec3 fragNormal;
|
||||
<$transformEyeToWorldDir(cam, _normal, fragNormal)$>
|
||||
|
||||
vec4 color = vec4(evalGlobalLightingAlphaBlendedWithHaze(
|
||||
cam._viewInverse,
|
||||
1.0,
|
||||
occlusionTex,
|
||||
fragPosition,
|
||||
fragNormal,
|
||||
normalize(_normalWS),
|
||||
albedo,
|
||||
fresnel,
|
||||
metallic,
|
||||
|
|
|
@ -18,9 +18,7 @@
|
|||
<$declareMaterialTextures(ALBEDO)$>
|
||||
|
||||
in vec2 _texCoord0;
|
||||
in vec3 _normal;
|
||||
in vec3 _color;
|
||||
in float _alpha;
|
||||
|
||||
out vec4 _fragColor;
|
||||
|
||||
|
|
|
@ -18,9 +18,7 @@
|
|||
<$declareMaterialTextures(ALBEDO)$>
|
||||
|
||||
in vec2 _texCoord0;
|
||||
in vec3 _normal;
|
||||
in vec3 _color;
|
||||
in float _alpha;
|
||||
|
||||
out vec4 _fragColor;
|
||||
|
||||
|
|
|
@ -34,8 +34,6 @@ vec4 evalGlobalColor(float shadowAttenuation, vec3 position, vec3 normal, vec3 a
|
|||
LightAmbient ambient = getLightAmbient();
|
||||
|
||||
TransformCamera cam = getTransformCamera();
|
||||
vec3 fragNormal;
|
||||
<$transformEyeToWorldDir(cam, normal, fragNormal)$>
|
||||
vec3 fragEyeVectorView = normalize(-position);
|
||||
vec3 fragEyeDir;
|
||||
<$transformEyeToWorldDir(cam, fragEyeVectorView, fragEyeDir)$>
|
||||
|
@ -57,8 +55,8 @@ vec4 evalGlobalColor(float shadowAttenuation, vec3 position, vec3 normal, vec3 a
|
|||
uniform sampler2D originalTexture;
|
||||
|
||||
in vec2 _texCoord0;
|
||||
in vec4 _position;
|
||||
in vec3 _normal;
|
||||
in vec4 _positionES;
|
||||
in vec3 _normalWS;
|
||||
in vec3 _color;
|
||||
in float _alpha;
|
||||
|
||||
|
@ -67,8 +65,8 @@ out vec4 _fragColor;
|
|||
void main(void) {
|
||||
vec4 albedo = texture(originalTexture, _texCoord0);
|
||||
|
||||
vec3 fragPosition = _position.xyz;
|
||||
vec3 fragNormal = normalize(_normal);
|
||||
vec3 fragPosition = _positionES.xyz;
|
||||
vec3 fragNormal = normalize(_normalWS);
|
||||
vec3 fragAlbedo = albedo.rgb * _color;
|
||||
float fragMetallic = 0.0;
|
||||
vec3 fragSpecular = vec3(0.1);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue