diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index ea002a27c6..174cf73094 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -556,7 +556,51 @@ void EntityScriptServer::checkAndCallPreload(const EntityItemID& entityID, bool } void EntityScriptServer::sendStatsPacket() { + QJsonObject statsObject; + QJsonObject octreeStats; + octreeStats["elementCount"] = (double)OctreeElement::getNodeCount(); + octreeStats["internalElementCount"] = (double)OctreeElement::getInternalNodeCount(); + octreeStats["leafElementCount"] = (double)OctreeElement::getLeafNodeCount(); + statsObject["octree_stats"] = octreeStats; + + QJsonObject scriptEngineStats; + int numberRunningScripts = 0; + const auto scriptEngine = _entitiesScriptEngine; + if (scriptEngine) { + numberRunningScripts = scriptEngine->getNumRunningEntityScripts(); + } + scriptEngineStats["number_running_scripts"] = numberRunningScripts; + statsObject["script_engine_stats"] = scriptEngineStats; + + + auto nodeList = DependencyManager::get(); + QJsonObject nodesObject; + nodeList->eachNode([&](const SharedNodePointer& node) { + QJsonObject clientStats; + const QString uuidString(uuidStringWithoutCurlyBraces(node->getUUID())); + clientStats["node_type"] = NodeType::getNodeTypeName(node->getType()); + auto& nodeStats = node->getConnectionStats(); + + static const QString NODE_OUTBOUND_KBPS_STAT_KEY("outbound_kbit/s"); + static const QString NODE_INBOUND_KBPS_STAT_KEY("inbound_kbit/s"); + + // add the key to ask the domain-server for a username replacement, if it has it + clientStats[USERNAME_UUID_REPLACEMENT_STATS_KEY] = uuidString; + + clientStats[NODE_OUTBOUND_KBPS_STAT_KEY] = node->getOutboundKbps(); + clientStats[NODE_INBOUND_KBPS_STAT_KEY] = node->getInboundKbps(); + + using namespace std::chrono; + const float statsPeriod = duration(nodeStats.endTime - nodeStats.startTime).count(); + clientStats["unreliable_packet/s"] = (nodeStats.sentUnreliablePackets + nodeStats.receivedUnreliablePackets) / statsPeriod; + clientStats["reliable_packet/s"] = (nodeStats.sentPackets + nodeStats.receivedPackets) / statsPeriod; + + nodesObject[uuidString] = clientStats; + }); + + statsObject["nodes"] = nodesObject; + addPacketStatsAndSendStatsPacket(statsObject); } void EntityScriptServer::handleOctreePacket(QSharedPointer message, SharedNodePointer senderNode) { diff --git a/domain-server/resources/web/js/shared.js b/domain-server/resources/web/js/shared.js index abcb2cb9eb..f4053ebddc 100644 --- a/domain-server/resources/web/js/shared.js +++ b/domain-server/resources/web/js/shared.js @@ -136,6 +136,23 @@ function getCurrentDomainIDType() { return DOMAIN_ID_TYPE_UNKNOWN; } +function isCloudDomain() { + + if (!domainIDIsSet()) { + return false; + } + if (typeof DomainInfo === 'undefined') { + return false; + } + if (DomainInfo === null) { + return false; + } + if (typeof DomainInfo.cloud_domain !== "boolean") { + return false; + } + return DomainInfo.cloud_domain; +} + function showLoadingDialog(msg) { var message = '
'; message += ' ' + msg; diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js index 136d5b0ebc..e8c04fa2be 100644 --- a/domain-server/resources/web/settings/js/settings.js +++ b/domain-server/resources/web/settings/js/settings.js @@ -606,6 +606,9 @@ $(document).ready(function(){ var address = DomainInfo.network_address === null ? '' : DomainInfo.network_address; var port = DomainInfo.network_port === null ? '' : DomainInfo.network_port; var modal_body = "
"; + if (isCloudDomain()) { + modal_body += '
Changing the network settings may actually break your domain.
'; + } if (includeAddress) { modal_body += ""; modal_body += ""; @@ -867,6 +870,10 @@ $(document).ready(function(){ } } + if (getCurrentDomainIDType() === DOMAIN_ID_TYPE_TEMP) { + $(Settings.DOMAIN_ID_SELECTOR).siblings('span').append(" This is a temporary domain and will not be visible in your domain list."); + } + if (accessTokenIsSet()) { appendAddButtonToPlacesTable(); } diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index d9c0a0a6b3..ceb4679137 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -385,7 +385,7 @@ void DomainServer::parseCommandLine(int argc, char* argv[]) { } if (_iceServerAddr.isEmpty()) { - qCWarning(domain_server_ice) << "Could not parse an IP address and port combination from" << hostnamePortString; + qCWarning(domain_server_ice) << "ALERT: Could not parse an IP address and port combination from" << hostnamePortString; ::exit(0); } } @@ -876,7 +876,7 @@ void DomainServer::setupAutomaticNetworking() { nodeList->startSTUNPublicSocketUpdate(); } } else { - qDebug() << "Cannot enable domain-server automatic networking without a domain ID." + qCCritical(domain_server) << "PAGE: Cannot enable domain-server automatic networking without a domain ID." << "Please add an ID to your config file or via the web interface."; return; } @@ -1635,8 +1635,9 @@ void DomainServer::handleFailedICEServerAddressUpdate(QNetworkReply* requestRepl } else { const int ICE_SERVER_UPDATE_RETRY_MS = 2 * 1000; - qCWarning(domain_server_ice) << "Failed to update ice-server address (" << _iceServerSocket << ") with High Fidelity Metaverse - error was" - << requestReply->errorString(); + qCWarning(domain_server_ice) << "PAGE: Failed to update ice-server address (" << _iceServerSocket << + ") with Metaverse (" << requestReply->url() << ") (critical error for auto-networking) error:" << + requestReply->errorString(); qCWarning(domain_server_ice) << "\tRe-attempting in" << ICE_SERVER_UPDATE_RETRY_MS / 1000 << "seconds"; QTimer::singleShot(ICE_SERVER_UPDATE_RETRY_MS, this, SLOT(sendICEServerAddressToMetaverseAPI())); @@ -3450,8 +3451,9 @@ void DomainServer::randomizeICEServerAddress(bool shouldTriggerHostLookup) { // we ended up with an empty list since everything we've tried has failed // so clear the set of failed addresses and start going through them again - qCWarning(domain_server_ice) << "All current ice-server addresses have failed - re-attempting all current addresses for" - << _iceServerAddr; + qCWarning(domain_server_ice) << + "PAGE: All current ice-server addresses have failed - re-attempting all current addresses for" + << _iceServerAddr; _failedIceServerAddresses.clear(); candidateICEAddresses = _iceServerAddresses; diff --git a/interface/resources/avatar/animations/sitting_idle02.fbx b/interface/resources/avatar/animations/sitting_idle02.fbx index 8cf3d22fc6..8dd2d46dca 100644 Binary files a/interface/resources/avatar/animations/sitting_idle02.fbx and b/interface/resources/avatar/animations/sitting_idle02.fbx differ diff --git a/interface/resources/avatar/animations/sitting_idle04.fbx b/interface/resources/avatar/animations/sitting_idle04.fbx new file mode 100644 index 0000000000..7012481b2a Binary files /dev/null and b/interface/resources/avatar/animations/sitting_idle04.fbx differ diff --git a/interface/resources/avatar/animations/sitting_idle05.fbx b/interface/resources/avatar/animations/sitting_idle05.fbx new file mode 100644 index 0000000000..1b89d26f15 Binary files /dev/null and b/interface/resources/avatar/animations/sitting_idle05.fbx differ diff --git a/interface/resources/avatar/animations/sitting_idle_once_fidget.fbx b/interface/resources/avatar/animations/sitting_idle_once_fidget.fbx new file mode 100644 index 0000000000..a7e5dce013 Binary files /dev/null and b/interface/resources/avatar/animations/sitting_idle_once_fidget.fbx differ diff --git a/interface/resources/avatar/animations/sitting_idle_once_leanforward.fbx b/interface/resources/avatar/animations/sitting_idle_once_leanforward.fbx new file mode 100644 index 0000000000..75a4603335 Binary files /dev/null and b/interface/resources/avatar/animations/sitting_idle_once_leanforward.fbx differ diff --git a/interface/resources/avatar/animations/sitting_idle_once_lookaround.fbx b/interface/resources/avatar/animations/sitting_idle_once_lookaround.fbx new file mode 100644 index 0000000000..d991316a17 Binary files /dev/null and b/interface/resources/avatar/animations/sitting_idle_once_lookaround.fbx differ diff --git a/interface/resources/avatar/animations/sitting_idle_once_lookfidget.fbx b/interface/resources/avatar/animations/sitting_idle_once_lookfidget.fbx new file mode 100644 index 0000000000..b3ab378c26 Binary files /dev/null and b/interface/resources/avatar/animations/sitting_idle_once_lookfidget.fbx differ diff --git a/interface/resources/avatar/animations/sitting_idle_once_lookleftright.fbx b/interface/resources/avatar/animations/sitting_idle_once_lookleftright.fbx new file mode 100644 index 0000000000..57f06e13e2 Binary files /dev/null and b/interface/resources/avatar/animations/sitting_idle_once_lookleftright.fbx differ diff --git a/interface/resources/avatar/animations/sitting_idle_once_shakelegs.fbx b/interface/resources/avatar/animations/sitting_idle_once_shakelegs.fbx new file mode 100644 index 0000000000..a020e20044 Binary files /dev/null and b/interface/resources/avatar/animations/sitting_idle_once_shakelegs.fbx differ diff --git a/interface/resources/avatar/animations/sitting_idle_once_shifting.fbx b/interface/resources/avatar/animations/sitting_idle_once_shifting.fbx new file mode 100644 index 0000000000..ec5ba9a654 Binary files /dev/null and b/interface/resources/avatar/animations/sitting_idle_once_shifting.fbx differ diff --git a/interface/resources/avatar/animations/sitting_idle_once_shiftweight.fbx b/interface/resources/avatar/animations/sitting_idle_once_shiftweight.fbx new file mode 100644 index 0000000000..90d2bd220b Binary files /dev/null and b/interface/resources/avatar/animations/sitting_idle_once_shiftweight.fbx differ diff --git a/interface/resources/avatar/avatar-animation.json b/interface/resources/avatar/avatar-animation.json index fa3a020b23..2b8b3eb9b3 100644 --- a/interface/resources/avatar/avatar-animation.json +++ b/interface/resources/avatar/avatar-animation.json @@ -640,8 +640,8 @@ { "easingType": "easeInOutQuad", "id": "seatedTalk02", - "interpDuration": 15, - "interpTarget": 15, + "interpDuration": 30, + "interpTarget": 30, "interpType": "evaluateBoth", "priority": 1, "resume": true, @@ -651,8 +651,8 @@ { "easingType": "easeInOutQuad", "id": "seatedTalk03", - "interpDuration": 15, - "interpTarget": 15, + "interpDuration": 30, + "interpTarget": 30, "interpType": "evaluateBoth", "priority": 1, "resume": true, @@ -662,8 +662,8 @@ { "easingType": "easeInOutQuad", "id": "seatedTalk04", - "interpDuration": 15, - "interpTarget": 15, + "interpDuration": 30, + "interpTarget": 30, "interpType": "evaluateBoth", "priority": 1, "resume": true, @@ -680,84 +680,414 @@ "children": [ { "children": [ + { + "children": [ + ], + "data": { + "endFrame": 800, + "loopFlag": true, + "startFrame": 0, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle.fbx" + }, + "id": "seatedIdle01", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 800, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle02.fbx" + }, + "id": "seatedIdle02", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 800, + "loopFlag": true, + "startFrame": 0, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle03.fbx" + }, + "id": "seatedIdle03", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 800, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle04.fbx" + }, + "id": "seatedIdle04", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 332, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle05.fbx" + }, + "id": "seatedIdle05", + "type": "clip" + } ], "data": { - "endFrame": 800, - "loopFlag": true, - "startFrame": 0, + "currentState": "seatedIdle01", + "endFrame": 30, + "randomSwitchTimeMax": 40, + "randomSwitchTimeMin": 10, + "startFrame": 10, + "states": [ + { + "easingType": "easeInOutQuad", + "id": "seatedIdle01", + "interpDuration": 30, + "interpTarget": 30, + "interpType": "evaluateBoth", + "priority": 1, + "resume": true, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedIdle02", + "interpDuration": 30, + "interpTarget": 30, + "interpType": "evaluateBoth", + "priority": 1, + "resume": true, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedIdle03", + "interpDuration": 30, + "interpTarget": 30, + "interpType": "evaluateBoth", + "priority": 1, + "resume": true, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedIdle04", + "interpDuration": 30, + "interpTarget": 30, + "interpType": "evaluateBoth", + "priority": 1, + "resume": true, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedIdle05", + "interpDuration": 30, + "interpTarget": 30, + "interpType": "evaluateBoth", + "priority": 1, + "resume": true, + "transitions": [ + ] + } + ], "timeScale": 1, - "url": "qrc:///avatar/animations/sitting_idle.fbx" + "triggerRandomSwitch": "seatedIdleSwitch", + "triggerTimeMax": 10 }, - "id": "seatedIdle01", - "type": "clip" + "id": "masterSeatedIdle", + "type": "randomSwitchStateMachine" }, { "children": [ + { + "children": [ + ], + "data": { + "endFrame": 744, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle_once_shifting.fbx" + }, + "id": "seatedFidgetShifting", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 420, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle_once_lookfidget.fbx" + }, + "id": "seatedFidgetLookFidget", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 282, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle_once_shiftweight.fbx" + }, + "id": "seatedFidgetShiftWeight", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 428, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle_once_fidget.fbx" + }, + "id": "seatedFidgeting", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 324, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle_once_lookaround.fbx" + }, + "id": "seatedFidgetLookAround", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 120, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle_once_lookleftright.fbx" + }, + "id": "seatedFidgetLookLeftRight", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 178, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle_once_leanforward.fbx" + }, + "id": "seatedFidgetLeanForward", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 140, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle_once_shakelegs.fbx" + }, + "id": "seatedFidgetShakeLegs", + "type": "clip" + } ], "data": { - "endFrame": 800, - "loopFlag": true, - "startFrame": 0, - "timeScale": 1, - "url": "qrc:///avatar/animations/sitting_idle02.fbx" + "currentState": "seatedFidgetShifting", + "states": [ + { + "easingType": "easeInOutQuad", + "id": "seatedFidgetShifting", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedFidgetLookFidget", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedFidgetShiftWeight", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedFidgeting", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedFidgetLookAround", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedFidgetLookLeftRight", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedFidgetLeanForward", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedFidgetShakeLegs", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + } + ] }, - "id": "seatedIdle02", - "type": "clip" - }, - { - "children": [ - ], - "data": { - "endFrame": 800, - "loopFlag": true, - "startFrame": 0, - "timeScale": 1, - "url": "qrc:///avatar/animations/sitting_idle03.fbx" - }, - "id": "seatedIdle03", - "type": "clip" + "id": "seatedFidget", + "type": "randomSwitchStateMachine" } ], "data": { - "currentState": "seatedIdle01", + "currentState": "masterSeatedIdle", "randomSwitchTimeMax": 20, "randomSwitchTimeMin": 10, "states": [ { "easingType": "easeInOutQuad", - "id": "seatedIdle01", - "interpDuration": 15, - "interpTarget": 15, + "id": "masterSeatedIdle", + "interpDuration": 20, + "interpTarget": 20, "interpType": "evaluateBoth", "priority": 1, - "resume": true, + "resume": false, "transitions": [ + { + "randomSwitchState": "seatedFidget", + "var": "timeToSeatedFidget" + } ] }, { "easingType": "easeInOutQuad", - "id": "seatedIdle02", - "interpDuration": 15, - "interpTarget": 15, + "id": "seatedFidget", + "interpDuration": 30, + "interpTarget": 30, "interpType": "evaluateBoth", - "priority": 1, - "resume": true, - "transitions": [ - ] - }, - { - "easingType": "easeInOutQuad", - "id": "seatedIdle03", - "interpDuration": 15, - "interpTarget": 15, - "interpType": "evaluateBoth", - "priority": 1, - "resume": true, + "priority": -1, + "resume": false, "transitions": [ + { + "randomSwitchState": "masterSeatedIdle", + "var": "seatedFidgetShiftingOnDone" + }, + { + "randomSwitchState": "masterSeatedIdle", + "var": "seatedFidgetLookFidgetOnDone" + }, + { + "randomSwitchState": "masterSeatedIdle", + "var": "seatedFidgetShiftWeightOnDone" + }, + { + "randomSwitchState": "masterSeatedIdle", + "var": "seatedFidgetingOnDone" + }, + { + "randomSwitchState": "masterSeatedIdle", + "var": "seatedFidgetLookAroundOnDone" + }, + { + "randomSwitchState": "masterSeatedIdle", + "var": "seatedFidgetLookLeftRightOnDone" + }, + { + "randomSwitchState": "masterSeatedIdle", + "var": "seatedFidgetLeanForwardOnDone" + }, + { + "randomSwitchState": "masterSeatedIdle", + "var": "seatedFidgetShakeLegsOnDone" + } ] } ], - "triggerRandomSwitch": "seatedIdleSwitch" + "transitionVar": "timeToSeatedFidget", + "triggerRandomSwitch": "", + "triggerTimeMax": 45, + "triggerTimeMin": 10 }, "id": "seatedIdle", "type": "randomSwitchStateMachine" @@ -1544,9 +1874,9 @@ "type": "randomSwitchStateMachine" }, { - "children": [ + "children": [ { - "children": [ + "children": [ { "children": [ { @@ -1653,7 +1983,7 @@ "blendType": "addAbsolute" }, "id": "seatedReactionPointBase", - "type": "blendLinear" + "type": "blendLinear" }, { "children": [ @@ -2148,8 +2478,8 @@ { "easingType": "easeInOutQuad", "id": "talk", - "interpDuration": 15, - "interpTarget": 15, + "interpDuration": 1, + "interpTarget": 1, "interpType": "evaluateBoth", "priority": 0.33, "resume": true, @@ -2159,8 +2489,8 @@ { "easingType": "easeInOutQuad", "id": "talk02", - "interpDuration": 15, - "interpTarget": 15, + "interpDuration": 1, + "interpTarget": 1, "interpType": "evaluateBoth", "priority": 0.33, "resume": true, @@ -2170,8 +2500,8 @@ { "easingType": "easeInOutQuad", "id": "talk03", - "interpDuration": 15, - "interpTarget": 15, + "interpDuration": 1, + "interpTarget": 1, "interpType": "evaluateBoth", "priority": 0.33, "resume": true, @@ -2181,8 +2511,8 @@ { "easingType": "easeInOutQuad", "id": "talk04", - "interpDuration": 15, - "interpTarget": 15, + "interpDuration": 1, + "interpTarget": 1, "interpType": "evaluateBoth", "priority": 0.33, "resume": true, @@ -2192,8 +2522,8 @@ { "easingType": "easeInOutQuad", "id": "talk_armsdown", - "interpDuration": 15, - "interpTarget": 15, + "interpDuration": 1, + "interpTarget": 1, "interpType": "evaluateBoth", "priority": 0.33, "resume": true, @@ -2203,8 +2533,8 @@ { "easingType": "easeInOutQuad", "id": "talk_lefthand", - "interpDuration": 15, - "interpTarget": 15, + "interpDuration": 1, + "interpTarget": 1, "interpType": "evaluateBoth", "priority": 0.33, "resume": true, @@ -2214,8 +2544,8 @@ { "easingType": "easeInOutQuad", "id": "talk_righthand", - "interpDuration": 15, - "interpTarget": 15, + "interpDuration": 1, + "interpTarget": 1, "interpType": "evaluateBoth", "priority": 0.33, "resume": true, @@ -2773,8 +3103,8 @@ { "easingType": "easeInOutQuad", "id": "fidget", - "interpDuration": 15, - "interpTarget": 15, + "interpDuration": 20, + "interpTarget": 20, "interpType": "evaluateBoth", "priority": -1, "resume": false, @@ -3997,8 +4327,8 @@ { "easingType": "easeInOutQuad", "id": "idleTalkOverlay", - "interpDuration": 20, - "interpTarget": 20, + "interpDuration": 25, + "interpTarget": 25, "interpType": "evaluateBoth", "transitions": [ { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b4a37519a6..4766353eba 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3610,10 +3610,9 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { mat4 camMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); _myCamera.setPosition(extractTranslation(camMat)); _myCamera.setOrientation(glmExtractRotation(camMat)); - } - else { - _myCamera.setPosition(myAvatar->getDefaultEyePosition()); - _myCamera.setOrientation(myAvatar->getMyHead()->getHeadOrientation()); + } else { + _myCamera.setPosition(myAvatar->getLookAtPivotPoint()); + _myCamera.setOrientation(myAvatar->getLookAtRotation()); } } else if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE) { if (isHMDMode()) { @@ -3647,9 +3646,9 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { if (mode == CAMERA_MODE_SELFIE) { lookAtRotation = lookAtRotation * glm::angleAxis(PI, myAvatar->getWorldOrientation() * Vectors::UP); } - _myCamera.setPosition(myAvatar->getDefaultEyePosition() + _myCamera.setPosition(myAvatar->getLookAtPivotPoint() + lookAtRotation * boomOffset); - _myCamera.lookAt(myAvatar->getDefaultEyePosition()); + _myCamera.lookAt(myAvatar->getLookAtPivotPoint()); } } } else if (mode == CAMERA_MODE_MIRROR) { @@ -3677,8 +3676,7 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { + glm::vec3(0, _raiseMirror * myAvatar->getModelScale(), 0) + mirrorBodyOrientation * glm::vec3(0.0f, 0.0f, 1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror + mirrorBodyOrientation * hmdOffset); - } - else { + } else { auto userInputMapper = DependencyManager::get(); const float YAW_SPEED = TWO_PI / 5.0f; float deltaYaw = userInputMapper->getActionState(controller::Action::YAW) * YAW_SPEED * deltaTime; @@ -3699,8 +3697,7 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { _myCamera.setOrientation(cameraEntity->getWorldOrientation() * hmdRotation); glm::vec3 hmdOffset = extractTranslation(myAvatar->getHMDSensorMatrix()); _myCamera.setPosition(cameraEntity->getWorldPosition() + (hmdRotation * hmdOffset)); - } - else { + } else { _myCamera.setOrientation(cameraEntity->getWorldOrientation()); _myCamera.setPosition(cameraEntity->getWorldPosition()); } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 54785933c9..de6ae526b4 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -958,7 +958,7 @@ void MyAvatar::simulate(float deltaTime, bool inView) { head->setScale(getModelScale()); head->simulate(deltaTime); CameraMode mode = qApp->getCamera().getMode(); - if (_scriptControlsHeadLookAt || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE) { + if (_scriptControlsHeadLookAt || mode == CAMERA_MODE_FIRST_PERSON || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE) { if (!_pointAtActive || !_isPointTargetValid) { updateHeadLookAt(deltaTime); } else { @@ -2718,7 +2718,8 @@ void MyAvatar::updateMotors() { if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) { if (_characterController.getState() == CharacterController::State::Hover || _characterController.computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS) { - if (qApp->getCamera().getMode() == CAMERA_MODE_LOOK_AT || qApp->getCamera().getMode() == CAMERA_MODE_SELFIE) { + CameraMode mode = qApp->getCamera().getMode(); + if (mode == CAMERA_MODE_FIRST_PERSON || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE) { motorRotation = getLookAtRotation(); } else { motorRotation = getMyHead()->getHeadOrientation(); @@ -3442,8 +3443,10 @@ void MyAvatar::updateOrientation(float deltaTime) { // Smoothly rotate body with arrow keys float targetSpeed = getDriveKey(YAW) * _yawSpeed; CameraMode mode = qApp->getCamera().getMode(); - bool computeLookAt = (mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE) && isReadyForPhysics() && !qApp->isHMDMode(); - if (computeLookAt) { + bool computeLookAt = isReadyForPhysics() && !qApp->isHMDMode() && + (mode == CAMERA_MODE_FIRST_PERSON || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE); + bool smoothCameraYaw = computeLookAt && mode != CAMERA_MODE_FIRST_PERSON; + if (smoothCameraYaw) { // For "Look At" and "Selfie" camera modes we also smooth the yaw rotation from right-click mouse movement. float speedFromDeltaYaw = deltaTime > FLT_EPSILON ? getDriveKey(DELTA_YAW) / deltaTime : 0.0f; speedFromDeltaYaw *= _yawSpeed / YAW_SPEED_DEFAULT; @@ -3472,7 +3475,7 @@ void MyAvatar::updateOrientation(float deltaTime) { } } float totalBodyYaw = _bodyYawDelta * deltaTime; - if (!computeLookAt) { + if (!smoothCameraYaw) { // Rotate directly proportional to delta yaw and delta pitch from right-click mouse movement. totalBodyYaw += getDriveKey(DELTA_YAW) * _yawSpeed / YAW_SPEED_DEFAULT; } @@ -3563,12 +3566,16 @@ void MyAvatar::updateOrientation(float deltaTime) { blend = 1.0f; } glm::quat faceRotation = _lookAtYaw; - if (isMovingFwdBwd && isMovingSideways) { - // Reorient avatar to face camera diagonal - blend = DIAGONAL_TURN_BLEND; - float turnSign = getDriveKey(TRANSLATE_Z) < 0.0f ? -1.0f : 1.0f; - turnSign = getDriveKey(TRANSLATE_X) > 0.0f ? -turnSign : turnSign; - faceRotation = _lookAtYaw * glm::angleAxis(turnSign * 0.25f * PI, Vectors::UP); + if (isMovingFwdBwd) { + if (isMovingSideways) { + // Reorient avatar to face camera diagonal + blend = mode == CAMERA_MODE_FIRST_PERSON ? 1.0f : DIAGONAL_TURN_BLEND; + float turnSign = getDriveKey(TRANSLATE_Z) < 0.0f ? -1.0f : 1.0f; + turnSign = getDriveKey(TRANSLATE_X) > 0.0f ? -turnSign : turnSign; + faceRotation = _lookAtYaw * glm::angleAxis(turnSign * 0.25f * PI, Vectors::UP); + } else if (mode == CAMERA_MODE_FIRST_PERSON) { + blend = 1.0f; + } } setWorldOrientation(glm::slerp(getWorldOrientation(), faceRotation, blend)); } else if (isRotatingWhileSeated) { @@ -3630,20 +3637,32 @@ void MyAvatar::updateOrientation(float deltaTime) { glm::vec3 avatarVectorRight = getWorldOrientation() * Vectors::RIGHT; float leftRightDot = glm::dot(cameraYawVector, avatarVectorRight); - const float REORIENT_ANGLE = 65.0f; + const float DEFAULT_REORIENT_ANGLE = 65.0f; + const float FIRST_PERSON_REORIENT_ANGLE = 95.0f; const float TRIGGER_REORIENT_ANGLE = 45.0f; + const float FIRST_PERSON_TRIGGER_REORIENT_ANGLE = 65.0f; glm::vec3 ajustedYawVector = cameraYawVector; - if (frontBackDot < 0.0f) { - ajustedYawVector = (leftRightDot < 0.0f ? -avatarVectorRight : avatarVectorRight); - cameraVector = (ajustedYawVector * _lookAtPitch) * Vectors::FRONT; + float limitAngle = 0.0f; + float triggerAngle = -glm::sin(glm::radians(TRIGGER_REORIENT_ANGLE)); + if (mode == CAMERA_MODE_FIRST_PERSON) { + limitAngle = glm::sin(glm::radians(90.0f - FIRST_PERSON_TRIGGER_REORIENT_ANGLE)); + triggerAngle = limitAngle; + } + float reorientAngle = mode == CAMERA_MODE_FIRST_PERSON ? FIRST_PERSON_REORIENT_ANGLE : DEFAULT_REORIENT_ANGLE; + if (frontBackDot < limitAngle) { + if (frontBackDot < 0.0f) { + ajustedYawVector = (leftRightDot < 0.0f ? -avatarVectorRight : avatarVectorRight); + cameraVector = (ajustedYawVector * _lookAtPitch) * Vectors::FRONT; + } if (!isRotatingWhileSeated) { - if (frontBackDot < -glm::sin(glm::radians(TRIGGER_REORIENT_ANGLE))) { + if (frontBackDot < triggerAngle) { _shouldTurnToFaceCamera = true; + _firstPersonSteadyHeadTimer = 0.0f; } } else { setWorldOrientation(previousOrientation); } - } else if (frontBackDot > glm::sin(glm::radians(REORIENT_ANGLE))) { + } else if (frontBackDot > glm::sin(glm::radians(reorientAngle))) { _shouldTurnToFaceCamera = false; } @@ -3664,6 +3683,22 @@ void MyAvatar::updateOrientation(float deltaTime) { _lookAtCameraTarget = targetPoint; } _headLookAtActive = true; + const float FIRST_PERSON_RECENTER_SECONDS = 15.0f; + if (mode == CAMERA_MODE_FIRST_PERSON) { + if (getDriveKey(YAW) + getDriveKey(STEP_YAW) + getDriveKey(DELTA_YAW) == 0.0f) { + if (_firstPersonSteadyHeadTimer < FIRST_PERSON_RECENTER_SECONDS) { + if (_firstPersonSteadyHeadTimer > 0.0f) { + _firstPersonSteadyHeadTimer += deltaTime; + } + } else { + _shouldTurnToFaceCamera = true; + _firstPersonSteadyHeadTimer = 0.0f; + } + } else { + _firstPersonSteadyHeadTimer = deltaTime; + } + } + } else { head->setBaseYaw(0.0f); head->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime @@ -3736,7 +3771,8 @@ glm::vec3 MyAvatar::scaleMotorSpeed(const glm::vec3 forward, const glm::vec3 rig } else { // Desktop mode. direction = (zSpeed * forward) + (xSpeed * right); - if (qApp->getCamera().getMode() == CAMERA_MODE_LOOK_AT && zSpeed != 0.0f && xSpeed != 0.0f){ + CameraMode mode = qApp->getCamera().getMode(); + if ((mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_FIRST_PERSON || mode == CAMERA_MODE_SELFIE) && zSpeed != 0.0f && xSpeed != 0.0f){ direction = (zSpeed * forward); } @@ -4351,7 +4387,8 @@ bool MyAvatar::isFlying() { bool MyAvatar::isInAir() { // If Avatar is Hover, Falling, or Taking off, they are in Air. - return _characterController.getState() != CharacterController::State::Ground; + return _characterController.getState() != CharacterController::State::Ground && + _characterController.getState() != CharacterController::State::Seated; } bool MyAvatar::getFlyingEnabled() { @@ -5399,7 +5436,7 @@ glm::quat MyAvatar::getOrientationForAudio() { case AudioListenerMode::FROM_HEAD: { // Using the camera's orientation instead, when the current mode is controlling the avatar's head. CameraMode mode = qApp->getCamera().getMode(); - bool headFollowsCamera = mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE; + bool headFollowsCamera = mode == CAMERA_MODE_FIRST_PERSON || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE; result = headFollowsCamera ? qApp->getCamera().getOrientation() : getHead()->getFinalOrientationInWorldFrame(); break; } @@ -6768,6 +6805,12 @@ void MyAvatar::setHeadLookAt(const glm::vec3& lookAtTarget) { _lookAtScriptTarget = lookAtTarget; } +glm::vec3 MyAvatar::getLookAtPivotPoint() { + glm::vec3 avatarUp = getWorldOrientation() * Vectors::UP; + glm::vec3 yAxisEyePosition = getWorldPosition() + avatarUp * glm::dot(avatarUp, _skeletonModel->getDefaultEyeModelPosition()); + return yAxisEyePosition; +} + bool MyAvatar::setPointAt(const glm::vec3& pointAtTarget) { if (QThread::currentThread() != thread()) { bool result = false; @@ -6794,3 +6837,4 @@ void MyAvatar::resetPointAt() { POINT_BLEND_LINEAR_ALPHA_NAME, POINT_ALPHA_BLENDING); } } + diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 6b1344aad2..a7ba639461 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -272,11 +272,12 @@ class MyAvatar : public Avatar { * the value.

* @property {number} analogPlusSprintSpeed - The sprint (run) speed of your avatar for the "AnalogPlus" control scheme. * @property {MyAvatar.SitStandModelType} userRecenterModel - Controls avatar leaning and recentering behavior. - * @property {number} isInSittingState - true if your avatar is sitting (avatar leaning is disabled, - * recenntering is enabled), false if it is standing (avatar leaning is enabled, and avatar recenters if it - * leans too far). If userRecenterModel == 2 (i.e., auto) the property value automatically updates as the - * user sits or stands, unless isSitStandStateLocked == true. Setting the property value overrides the - * current siting / standing state, which is updated when the user next sits or stands unless + * @property {number} isInSittingState - true if the user wearing the HMD is determined to be sitting + * (avatar leaning is disabled, recenntering is enabled), false if the user wearing the HMD is + * determined to be standing (avatar leaning is enabled, and avatar recenters if it leans too far). + * If userRecenterModel == 2 (i.e., auto) the property value automatically updates as the user sits + * or stands, unless isSitStandStateLocked == true. Setting the property value overrides the current + * siting / standing state, which is updated when the user next sits or stands unless * isSitStandStateLocked == true. * @property {boolean} isSitStandStateLocked - true to lock the avatar sitting/standing state, i.e., use this * to disable automatically changing state. @@ -1890,6 +1891,14 @@ public: */ Q_INVOKABLE void endSit(const glm::vec3& position, const glm::quat& rotation); + /**jsdoc + * Gets whether the avatar is in a seated pose. The seated pose is set by calling the + * MyAvatar::beginSit method. + * @function MyAvatar.isSeated + * @returns {boolean} true if the avatar is in a seated pose. + */ + Q_INVOKABLE bool isSeated() { return _characterController.getSeated(); } + int getOverrideJointCount() const; bool getFlowActive() const; bool getNetworkGraphActive() const; @@ -1906,6 +1915,7 @@ public: void debugDrawPose(controller::Action action, const char* channelName, float size); bool getIsJointOverridden(int jointIndex) const; + glm::vec3 getLookAtPivotPoint(); public slots: @@ -2663,6 +2673,7 @@ private: bool _shouldTurnToFaceCamera { false }; bool _scriptControlsHeadLookAt { false }; float _scriptHeadControlTimer { 0.0f }; + float _firstPersonSteadyHeadTimer { 0.0f }; bool _pointAtActive { false }; bool _isPointTargetValid { true }; diff --git a/launchers/darwin/HQ Launcher.entitlements b/launchers/darwin/HQ Launcher.entitlements index ddfcefe3af..08d1c52df3 100644 --- a/launchers/darwin/HQ Launcher.entitlements +++ b/launchers/darwin/HQ Launcher.entitlements @@ -6,5 +6,7 @@ high-fidelity.hifi + com.apple.security.device.audio-input + diff --git a/libraries/avatars-renderer/src/avatars-renderer/Head.cpp b/libraries/avatars-renderer/src/avatars-renderer/Head.cpp index 445184f5f8..63d8e2981c 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Head.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Head.cpp @@ -92,22 +92,23 @@ void Head::simulate(float deltaTime) { } else if (_timeWithoutTalking - deltaTime < BLINK_AFTER_TALKING && _timeWithoutTalking >= BLINK_AFTER_TALKING) { forceBlink = true; } - if (_leftEyeBlinkVelocity == 0.0f && _rightEyeBlinkVelocity == 0.0f) { // no blinking when brows are raised; blink less with increasing loudness const float BASE_BLINK_RATE = 15.0f / 60.0f; const float ROOT_LOUDNESS_TO_BLINK_INTERVAL = 0.25f; if (forceBlink || (_browAudioLift < EPSILON && shouldDo(glm::max(1.0f, sqrt(fabs(_averageLoudness - _longTermAverageLoudness)) * ROOT_LOUDNESS_TO_BLINK_INTERVAL) / BASE_BLINK_RATE, deltaTime))) { - _leftEyeBlinkVelocity = BLINK_SPEED + randFloat() * BLINK_SPEED_VARIABILITY; - _rightEyeBlinkVelocity = BLINK_SPEED + randFloat() * BLINK_SPEED_VARIABILITY; + float randSpeedVariability = randFloat(); + float eyeBlinkVelocity = BLINK_SPEED + randSpeedVariability * BLINK_SPEED_VARIABILITY; + _leftEyeBlinkVelocity = eyeBlinkVelocity; + _rightEyeBlinkVelocity = eyeBlinkVelocity; if (randFloat() < 0.5f) { _leftEyeBlink = BLINK_START_VARIABILITY; - } else { _rightEyeBlink = BLINK_START_VARIABILITY; } } } else { + _leftEyeBlink = glm::clamp(_leftEyeBlink + _leftEyeBlinkVelocity * deltaTime, FULLY_OPEN, FULLY_CLOSED); _rightEyeBlink = glm::clamp(_rightEyeBlink + _rightEyeBlinkVelocity * deltaTime, FULLY_OPEN, FULLY_CLOSED); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 3837be5c9c..52738bb6cd 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -96,6 +96,21 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf connect(entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity, this, handlePointerEvent); connect(entityScriptingInterface, &EntityScriptingInterface::mouseMoveOnEntity, this, handlePointerEvent); connect(entityScriptingInterface, &EntityScriptingInterface::mouseReleaseOnEntity, this, handlePointerEvent); + // Handle mouse-clicking or laser-clicking on entities with the `href` property set + connect(entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity, this, [&](const QUuid& entityID, const PointerEvent& event) { + if (!EntityTree::areEntityClicksCaptured() && (event.getButtons() & PointerEvent::PrimaryButton)) { + auto entity = getEntity(entityID); + if (!entity) { + return; + } + auto properties = entity->getProperties(); + QString urlString = properties.getHref(); + QUrl url = QUrl(urlString, QUrl::StrictMode); + if (url.isValid() && !url.isEmpty()) { + DependencyManager::get()->handleLookupString(urlString); + } + } + }); connect(entityScriptingInterface, &EntityScriptingInterface::hoverEnterEntity, this, [&](const QUuid& entityID, const PointerEvent& event) { std::shared_ptr thisEntity; auto entity = getEntity(entityID); @@ -800,15 +815,6 @@ QUuid EntityTreeRenderer::mousePressEvent(QMouseEvent* event) { RayToEntityIntersectionResult rayPickResult = _getPrevRayPickResultOperator(_mouseRayPickID); EntityItemPointer entity; if (rayPickResult.intersects && (entity = getTree()->findEntityByID(rayPickResult.entityID))) { - if (!EntityTree::areEntityClicksCaptured() && event->button() == Qt::MouseButton::LeftButton) { - auto properties = entity->getProperties(); - QString urlString = properties.getHref(); - QUrl url = QUrl(urlString, QUrl::StrictMode); - if (url.isValid() && !url.isEmpty()) { - DependencyManager::get()->handleLookupString(urlString); - } - } - glm::vec2 pos2D = projectOntoEntityXYPlane(entity, ray, rayPickResult); PointerEvent pointerEvent(PointerEvent::Press, PointerManager::MOUSE_POINTER_ID, pos2D, rayPickResult.intersection, diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 5437ceaab8..5cb5ddbbfa 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -706,15 +706,11 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * * @property {Vec3} gravity=0,0,0 - The acceleration due to gravity in m/s2 that the entity should move with, in * world coordinates. Use a value of { x: 0, y: -9.8, z: 0 } to simulate Earth's gravity. Gravity is applied - * to an entity's motion only if its dynamic property is true. The gravity value is - * applied in addition to the acceleration value. + * to an entity's motion only if its dynamic property is true. *

If changing an entity's gravity from {@link Vec3(0)|Vec3.ZERO}, you need to give it a small * velocity in order to kick off physics simulation.

- * @property {Vec3} acceleration=0,0,0 - A general acceleration in m/s2 that the entity should move with, in world - * coordinates. The acceleration is applied to an entity's motion only if its dynamic property is - * true. The acceleration value is applied in addition to the gravity value. - *

If changing an entity's acceleration from {@link Vec3(0)|Vec3.ZERO}, you need to give it a small - * velocity in order to kick off physics simulation.

+ * @property {Vec3} acceleration - The current, measured acceleration of the entity, in m/s2. + *

Deprecated: This property is deprecated and will be removed.

* @property {number} restitution=0.5 - The "bounciness" of an entity when it collides, range 0.0 – * 0.99. The higher the value, the more bouncy. * @property {number} friction=0.5 - How much an entity slows down when it's moving against another, range 0.0 diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 52470e56c4..7c3fd2536d 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -728,7 +728,8 @@ public slots: * Finds all domain and avatar entities whose axis-aligned boxes intersect a search frustum. * @function Entities.findEntitiesInFrustum * @param {ViewFrustum} frustum - The frustum to search in. The position, orientation, - * projection, and centerRadius properties must be specified. + * projection, and centerRadius properties must be specified. The fieldOfView + * and aspectRatio properties are not used; these values are specified by the projection. * @returns {Uuid[]} An array of entity IDs whose axis-aligned boxes intersect the search frustum. The array is empty if no * entities could be found. * @example Report the number of entities in view. diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index 7e76df7622..5edbc5261d 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -1035,7 +1035,7 @@ void AccountManager::publicKeyUploadSucceeded(QNetworkReply* reply) { void AccountManager::publicKeyUploadFailed(QNetworkReply* reply) { // the public key upload has failed - qWarning() << "Public key upload failed from AccountManager" << reply->errorString(); + qCritical() << "PAGE: Public key upload failed from AccountManager to" << reply->url() << reply->errorString(); // we aren't waiting for a response any longer _isWaitingForKeypairResponse = false; diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index bd3dc7c177..50e2657622 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -234,14 +234,38 @@ const JSONCallbackParameters& AddressManager::apiCallbackParameters() { return callbackParams; } -bool AddressManager::handleUrl(const QUrl& lookupUrl, LookupTrigger trigger) { +bool AddressManager::handleUrl(const QUrl& lookupUrlIn, LookupTrigger trigger) { static QString URL_TYPE_USER = "user"; static QString URL_TYPE_DOMAIN_ID = "domain_id"; static QString URL_TYPE_PLACE = "place"; static QString URL_TYPE_NETWORK_ADDRESS = "network_address"; - if (lookupUrl.scheme() == URL_SCHEME_HIFI) { - qCDebug(networking) << "Trying to go to URL" << lookupUrl.toString(); + QUrl lookupUrl = lookupUrlIn; + + qCDebug(networking) << "Trying to go to URL" << lookupUrl.toString(); + + if (lookupUrl.scheme().isEmpty() && !lookupUrl.path().startsWith("/")) { + // 'urls' without schemes are taken as domain names, as opposed to + // simply a path portion of a url, so we need to set the scheme + lookupUrl.setScheme(URL_SCHEME_HIFI); + } + + static const QRegExp PORT_REGEX = QRegExp("\\d{1,5}(\\/.*)?"); + if(!lookupUrl.scheme().isEmpty() && lookupUrl.host().isEmpty() && PORT_REGEX.exactMatch(lookupUrl.path())) { + // this is in the form somewhere:, convert it to hifi://somewhere: + lookupUrl = QUrl(URL_SCHEME_HIFI + "://" + lookupUrl.toString()); + } + // it should be noted that url's in the form + // somewhere: are not valid, as that + // would indicate that the scheme is 'somewhere' + // use hifi://somewhere: instead + + if (lookupUrl.scheme() == URL_SCHEME_HIFI) { + if (lookupUrl.host().isEmpty()) { + // this was in the form hifi:/somewhere or hifi:somewhere. Fix it by making it hifi://somewhere + static const QRegExp HIFI_SCHEME_REGEX = QRegExp(URL_SCHEME_HIFI + ":\\/?", Qt::CaseInsensitive); + lookupUrl = QUrl(lookupUrl.toString().replace(HIFI_SCHEME_REGEX, URL_SCHEME_HIFI + "://")); + } DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::LookupAddress); @@ -379,25 +403,11 @@ bool isPossiblePlaceName(QString possiblePlaceName) { } void AddressManager::handleLookupString(const QString& lookupString, bool fromSuggestions) { - if (!lookupString.isEmpty()) { + + QString sanitizedString = lookupString.trimmed(); + if (!sanitizedString.isEmpty()) { // make this a valid hifi URL and handle it off to handleUrl - QString sanitizedString = lookupString.trimmed(); - QUrl lookupURL; - - if (!lookupString.startsWith('/')) { - // sometimes we need to handle lookupStrings like hifi:/somewhere - const QRegExp HIFI_SCHEME_REGEX = QRegExp(URL_SCHEME_HIFI + ":\\/{1,2}", Qt::CaseInsensitive); - sanitizedString = sanitizedString.remove(HIFI_SCHEME_REGEX); - - lookupURL = QUrl(sanitizedString); - if (lookupURL.scheme().isEmpty() || lookupURL.scheme().toLower() == LOCALHOST) { - lookupURL = QUrl("hifi://" + sanitizedString); - } - } else { - lookupURL = QUrl(sanitizedString); - } - - handleUrl(lookupURL, fromSuggestions ? Suggestions : UserInput); + handleUrl(sanitizedString, fromSuggestions ? Suggestions : UserInput); } } diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index d92db8a5d4..ecf2218e2f 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -52,7 +52,7 @@ LimitedNodeList::LimitedNodeList(int socketListenPort, int dtlsListenPort) : _nodeSocket.bind(QHostAddress::AnyIPv4, port); quint16 assignedPort = _nodeSocket.localPort(); if (socketListenPort != INVALID_PORT && socketListenPort != 0 && socketListenPort != assignedPort) { - qCCritical(networking) << "NodeList is unable to assign requested port of" << socketListenPort; + qCCritical(networking) << "PAGE: NodeList is unable to assign requested port of" << socketListenPort; } qCDebug(networking) << "NodeList socket is listening on" << assignedPort; @@ -1155,6 +1155,7 @@ void LimitedNodeList::startSTUNPublicSocketUpdate() { void LimitedNodeList::possiblyTimeoutSTUNAddressLookup() { if (_stunSockAddr.getAddress().isNull()) { // our stun address is still NULL, but we've been waiting for long enough - time to force a fail + qCCritical(networking) << "PAGE: Failed to lookup address of STUN server" << STUN_SERVER_HOSTNAME; stopInitialSTUNUpdate(false); } } @@ -1170,7 +1171,7 @@ void LimitedNodeList::stopInitialSTUNUpdate(bool success) { if (!success) { // if we're here this was the last failed STUN request // use our DS as our stun server - qCDebug(networking, "Failed to lookup public address via STUN server at %s:%hu.", + qCWarning(networking, "PAGE: Failed to lookup public address via STUN server at %s:%hu (likely a critical error for auto-networking).", STUN_SERVER_HOSTNAME, STUN_SERVER_PORT); qCDebug(networking) << "LimitedNodeList public socket will be set with local port and null QHostAddress."; @@ -1212,7 +1213,8 @@ void LimitedNodeList::updateLocalSocket() { QTcpSocket* localIPTestSocket = new QTcpSocket; connect(localIPTestSocket, &QTcpSocket::connected, this, &LimitedNodeList::connectedForLocalSocketTest); - connect(localIPTestSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(errorTestingLocalSocket())); + connect(localIPTestSocket, static_cast(&QTcpSocket::error), + this, &LimitedNodeList::errorTestingLocalSocket); // attempt to connect to our reliable host localIPTestSocket->connectToHost(RELIABLE_LOCAL_IP_CHECK_HOST, RELIABLE_LOCAL_IP_CHECK_PORT); @@ -1242,6 +1244,8 @@ void LimitedNodeList::errorTestingLocalSocket() { // then use our possibly updated guessed local address as fallback if (!_hasTCPCheckedLocalSocket) { setLocalSocket(HifiSockAddr { getGuessedLocalAddress(), _nodeSocket.localPort() }); + qCCritical(networking) << "PAGE: Can't connect to Google DNS service via TCP, falling back to guessed local address" + << getLocalSockAddr(); } localIPTestSocket->deleteLater(); @@ -1325,7 +1329,7 @@ void LimitedNodeList::putLocalPortIntoSharedMemory(const QString key, QObject* p qCDebug(networking) << "Wrote local listening port" << localPort << "to shared memory at key" << key; } else { - qWarning() << "Failed to create and attach to shared memory to share local port with assignment-client children."; + qWarning() << "ALERT: Failed to create and attach to shared memory to share local port with assignment-client children."; } } diff --git a/libraries/script-engine/src/AssetScriptingInterface.cpp b/libraries/script-engine/src/AssetScriptingInterface.cpp index 54b570e256..33be9de2ad 100644 --- a/libraries/script-engine/src/AssetScriptingInterface.cpp +++ b/libraries/script-engine/src/AssetScriptingInterface.cpp @@ -29,6 +29,8 @@ #include #include +#include + using Promise = MiniPromise::Promise; AssetScriptingInterface::AssetScriptingInterface(QObject* parent) : BaseAssetScriptingInterface(parent) { @@ -38,6 +40,25 @@ AssetScriptingInterface::AssetScriptingInterface(QObject* parent) : BaseAssetScr #define JS_VERIFY(cond, error) { if (!this->jsVerify(cond, error)) { return; } } +bool AssetScriptingInterface::initializeCache() { + if (!Parent::initializeCache()) { + if (assetClient()) { + std::promise cacheStatusResult; + Promise assetClientPromise(makePromise(__func__)); + assetClientPromise->moveToThread(qApp->thread()); // To ensure the finally() is processed. + + assetClient()->cacheInfoRequestAsync(assetClientPromise); + assetClientPromise->finally([&](QString, QVariantMap result) + { cacheStatusResult.set_value(!result.isEmpty()); }); + return cacheStatusResult.get_future().get(); + } else { + return false; + } + } else { + return true; + } +} + void AssetScriptingInterface::uploadData(QString data, QScriptValue callback) { auto handler = jsBindCallback(thisObject(), callback); QByteArray dataByteArray = data.toUtf8(); diff --git a/libraries/script-engine/src/AssetScriptingInterface.h b/libraries/script-engine/src/AssetScriptingInterface.h index 5da3c51a08..955adaa86c 100644 --- a/libraries/script-engine/src/AssetScriptingInterface.h +++ b/libraries/script-engine/src/AssetScriptingInterface.h @@ -356,7 +356,7 @@ public: * @function Assets.initializeCache * @returns {boolean} true if the cache is initialized, false if it isn't. */ - Q_INVOKABLE bool initializeCache() { return Parent::initializeCache(); } + Q_INVOKABLE bool initializeCache(); /**jsdoc * Checks whether the script can write to the cache. diff --git a/libraries/script-engine/src/Mat4.cpp b/libraries/script-engine/src/Mat4.cpp index 3e75d815c3..d4d73a46cc 100644 --- a/libraries/script-engine/src/Mat4.cpp +++ b/libraries/script-engine/src/Mat4.cpp @@ -70,15 +70,16 @@ glm::mat4 Mat4::inverse(const glm::mat4& m) const { } glm::vec3 Mat4::getForward(const glm::mat4& m) const { - return glm::vec3(-m[0][2], -m[1][2], -m[2][2]); + // -z is forward + return -glm::normalize(glm::vec3(m[2])); } glm::vec3 Mat4::getRight(const glm::mat4& m) const { - return glm::vec3(m[0][0], m[1][0], m[2][0]); + return glm::normalize(glm::vec3(m[0])); } glm::vec3 Mat4::getUp(const glm::mat4& m) const { - return glm::vec3(m[0][1], m[1][1], m[2][1]); + return glm::normalize(glm::vec3(m[1])); } void Mat4::print(const QString& label, const glm::mat4& m, bool transpose) const { diff --git a/libraries/script-engine/src/Mat4.h b/libraries/script-engine/src/Mat4.h index 0cdc70e79c..33fd889541 100644 --- a/libraries/script-engine/src/Mat4.h +++ b/libraries/script-engine/src/Mat4.h @@ -22,6 +22,10 @@ #include "RegisteredMetaTypes.h" /**jsdoc + * The Mat4 API provides facilities for generating and using 4 x 4 matrices. These matrices are typically used to + * represent transforms (scale, rotate, and translate) that convert one coordinate system into another, or perspective + * transforms that convert 3D points into screen coordinates. + * * @namespace Mat4 * @variation 0 * @@ -39,130 +43,279 @@ class Mat4 : public QObject, protected QScriptable { public slots: /**jsdoc + * Multiplies two matrices. * @function Mat4(0).multiply - * @param {Mat4} m1 - * @param {Mat4} m2 - * @returns {Mat4} + * @param {Mat4} m1 - The first matrix. + * @param {Mat4} m2 - The second matrix. + * @returns {Mat4} m1 multiplied with m2. */ glm::mat4 multiply(const glm::mat4& m1, const glm::mat4& m2) const; /**jsdoc + * Creates a matrix that represents a rotation and translation. * @function Mat4(0).createFromRotAndTrans - * @param {Quat} rot - * @param {Vec3} trans - * @returns {Mat4} + * @param {Quat} rot - The rotation. + * @param {Vec3} trans - The translation. + * @returns {Mat4} The matrix that represents the rotation and translation. + * @example Create a matrix with rotation and translation. + * var rot = Quat.fromPitchYawRollDegrees(30, 45, 60); + * var trans = { x: 10, y: 11, z: 12 }; + * var matrix = Mat4.createFromRotAndTrans(rot, trans); + * Mat4.print("Matrix:", matrix); + * // Matrix: dmat4x4((0.353553, 0.612372, -0.707107, 0.000000), + * // (-0.573223, 0.739199, 0.353553, 0.000000), + * // (0.739199, 0.280330, 0.612372, 0.000000), + * // (10.000000, 11.000000, 12.000000, 1.000000)) */ glm::mat4 createFromRotAndTrans(const glm::quat& rot, const glm::vec3& trans) const; /**jsdoc + * Creates a matrix that represents a scale, rotation, and translation. * @function Mat4(0).createFromScaleRotAndTrans - * @param {Vec3} scale - * @param {Quat} rot - * @param {Vec3} trans - * @returns {Mat4} + * @param {Vec3} scale - The scale. + * @param {Quat} rot - The rotation. + * @param {Vec3} trans - The translation. + * @returns {Mat4} The matrix that represents the scale, rotation, and translation. + * @example Create a matrix with scale, rotation, and translation. + * var scale = Vec3.multiply(2, Vec3.ONE); + * var rot = Quat.fromPitchYawRollDegrees(30, 45, 60); + * var trans = { x: 10, y: 11, z: 12 }; + * var matrix = Mat4.createFromScaleRotAndTrans(scale, rot, trans); + * Mat4.print("Matrix:", matrix); + * // Matrix: dmat4x4((0.707107, 1.224745, -1.414214, 0.000000), + * // (-1.146447, 1.478398, 0.707107, 0.000000), + * // (1.478398, 0.560660, 1.224745, 0.000000), + * // (10.000000, 11.000000, 12.000000, 1.000000)) */ glm::mat4 createFromScaleRotAndTrans(const glm::vec3& scale, const glm::quat& rot, const glm::vec3& trans) const; /**jsdoc + * Creates a matrix from columns of values. * @function Mat4(0).createFromColumns - * @param {Vec4} col0 - * @param {Vec4} col1 - * @param {Vec4} col2 - * @param {Vec4} col - * @returns {Mat4} + * @param {Vec4} col0 - Column 0 values. + * @param {Vec4} col1 - Column 1 values. + * @param {Vec4} col2 - Column 2 values. + * @param {Vec4} col3 - Column 3 valuse. + * @returns {Mat4} The matrix with the specified columns values. + * @example Create a matrix from columns. + * var col0 = { x: 0.707107, y: 1.224745, z: -1.414214, w: 0.0 }; + * var col1 = { x: -1.146447, y: 1.478398, z: 0.707107, w: 0.0 }; + * var col2 = { x: 1.478398, y: 0.560660, z: 1.224745, w: 0.0 }; + * var col3 = { x: 10.0, y: 11.0, z: 12.0, w: 1.0 }; + * var matrix = Mat4.createFromColumns(col0, col1, col2, col3); + * Mat4.print("Matrix:", matrix); + * //Matrix: dmat4x4((0.707107, 1.224745, -1.414214, 0.000000), + * // (-1.146447, 1.478398, 0.707107, 0.000000), + * // (1.478398, 0.560660, 1.224745, 0.000000), + * // (10.000000, 11.000000, 12.000000, 1.000000)) */ glm::mat4 createFromColumns(const glm::vec4& col0, const glm::vec4& col1, const glm::vec4& col2, const glm::vec4& col3) const; /**jsdoc + * Creates a matrix from an array of values. * @function Mat4(0).createFromArray - * @param {number[]} numbers - * @returns {Mat4} + * @param {number[]} arr - The array of values, starting with column 0. + * @returns {Mat4} The matrix with the specified values. + * @example Create a matrix from an array. + * var arr = [ + * 0.707107, 1.224745, -1.414214, 0.0, + * -1.146447, 1.478398, 0.707107, 0.0, + * 1.478398, 0.560660, 1.224745, 0.0, + * 10.0, 11.0, 12.0, 1.00 + * ]; + * var matrix = Mat4.createFromArray(arr); + * Mat4.print("Matrix:", matrix); + * //Matrix: dmat4x4((0.707107, 1.224745, -1.414214, 0.000000), + * // (-1.146447, 1.478398, 0.707107, 0.000000), + * // (1.478398, 0.560660, 1.224745, 0.000000), + * // (10.000000, 11.000000, 12.000000, 1.000000)) */ glm::mat4 createFromArray(const QVector& floats) const; /**jsdoc + * Extracts the translation from a matrix. * @function Mat4(0).extractTranslation - * @param {Mat4} m - * @returns {Vec3} + * @param {Mat4} m - The matrix. + * @returns {Vec3} The translation contained in the matrix. + * @example Extract the translation from a matrix. + * var scale = Vec3.multiply(2, Vec3.ONE); + * var rot = Quat.fromPitchYawRollDegrees(30, 45, 60); + * var trans = { x: 10, y: 11, z: 12 }; + * var matrix = Mat4.createFromScaleRotAndTrans(scale, rot, trans); + * + * trans = Mat4.extractTranslation(matrix); + * print("Translation: " + JSON.stringify(trans)); + * // Translation: {"x":10,"y":11,"z":12} */ glm::vec3 extractTranslation(const glm::mat4& m) const; /**jsdoc + * Extracts the rotation from a matrix. * @function Mat4(0).extractRotation - * @param {Mat4} m - * @returns {Vec3} + * @param {Mat4} m - The matrix. + * @returns {Quat} The rotation contained in the matrix. + * @example Extract the rotation from a matrix. + * var scale = Vec3.multiply(2, Vec3.ONE); + * var rot = Quat.fromPitchYawRollDegrees(30, 45, 60); + * var trans = { x: 10, y: 11, z: 12 }; + * var matrix = Mat4.createFromScaleRotAndTrans(scale, rot, trans); + * + * rot = Mat4.extractRotation(matrix); + * print("Rotation: " + JSON.stringify(Quat.safeEulerAngles(rot))); + * // Rotation: {"x":29.999998092651367,"y":45.00000762939453,"z":60.000003814697266} */ glm::quat extractRotation(const glm::mat4& m) const; /**jsdoc + * Extracts the scale from a matrix. * @function Mat4(0).extractScale - * @param {Mat4} m - * @returns {Vec3} + * @param {Mat4} m - The matrix. + * @returns {Vec3} The scale contained in the matrix. + * @example Extract the scale from a matrix. + * var scale = Vec3.multiply(2, Vec3.ONE); + * var rot = Quat.fromPitchYawRollDegrees(30, 45, 60); + * var trans = { x: 10, y: 11, z: 12 }; + * var matrix = Mat4.createFromScaleRotAndTrans(scale, rot, trans); + * + * scale = Mat4.extractScale(matrix); + * print("Scale: " + JSON.stringify(scale)); + * // Scale: {"x":1.9999998807907104,"y":1.9999998807907104,"z":1.9999998807907104} */ glm::vec3 extractScale(const glm::mat4& m) const; /**jsdoc + * Transforms a point into a new coordinate system: the point value is scaled, rotated, and translated. * @function Mat4(0).transformPoint - * @param {Mat4} m - * @param {Vec3} point - * @returns {Vec3} + * @param {Mat4} m - The transform to the new coordinate system. + * @param {Vec3} point - The point to transform. + * @returns {Vec3} The point in the new coordinate system. + * @example Transform a point. + * var scale = Vec3.multiply(2, Vec3.ONE); + * var rot = Quat.fromPitchYawRollDegrees(0, 45, 0); + * var trans = { x: 0, y: 10, z: 0 }; + * var matrix = Mat4.createFromScaleRotAndTrans(scale, rot, trans); + * + * var point = { x: 1, y: 1, z: 1 }; + * var transformedPoint = Mat4.transformPoint(matrix, point); + * print("Transformed point: " + JSON.stringify(transformedPoint)); + * // Transformed point: { "x": 2.8284270763397217, "y": 12, "z": -2.384185791015625e-7 } */ glm::vec3 transformPoint(const glm::mat4& m, const glm::vec3& point) const; /**jsdoc + * Transforms a vector into a new coordinate system: the vector is scaled and rotated. * @function Mat4(0).transformVector - * @param {Mat4} m - * @param {Vec3} vector - * @returns {Vec3} + * @param {Mat4} m - The transform to the new coordinate system. + * @param {Vec3} vector - The vector to transform. + * @returns {Vec3} The vector in the new coordinate system. + * @example Transform a vector. + * var scale = Vec3.multiply(2, Vec3.ONE); + * var rot = Quat.fromPitchYawRollDegrees(0, 45, 0); + * var trans = { x: 0, y: 10, z: 0 }; + * var matrix = Mat4.createFromScaleRotAndTrans(scale, rot, trans); + * + * var vector = { x: 1, y: 1, z: 1 }; + * var transformedVector = Mat4.transformVector(matrix, vector); + * print("Transformed vector: " + JSON.stringify(transformedVector)); + * // Transformed vector: { "x": 2.8284270763397217, "y": 2, "z": -2.384185791015625e-7 } */ glm::vec3 transformVector(const glm::mat4& m, const glm::vec3& vector) const; /**jsdoc + * Calculates the inverse of a matrix. * @function Mat4(0).inverse - * @param {Mat4} m - * @returns {Mat4} + * @param {Mat4} m - The matrix. + * @returns {Mat4} The inverse of the matrix. + * @example A matrix multiplied with its inverse is the unit matrix. + * var scale = Vec3.multiply(2, Vec3.ONE); + * var rot = Quat.fromPitchYawRollDegrees(30, 45, 60); + * var trans = { x: 10, y: 11, z: 12 }; + * var matrix = Mat4.createFromScaleRotAndTrans(scale, rot, trans); + * var inverse = Mat4.inverse(matrix); + * var multiplied = Mat4.multiply(matrix, inverse); + * Mat4.print("Multiplied:", multiplied); + * //Multiplied: dmat4x4((1.000000, 0.000000, 0.000000, 0.000000), + * // (0.000000, 1.000000, -0.000000, 0.000000), + * // (0.000000, 0.000000, 1.000000, 0.000000), + * // (0.000000, 0.000000, 0.000001, 1.000000)) */ glm::mat4 inverse(const glm::mat4& m) const; /**jsdoc + * Gets the "forward" direction that the camera would face if its orientation was set to the rotation contained in a + * matrix. The High Fidelity camera has axes x = right, y = up, -z = forward. + *

Synonym for {@link Mat4(0).getForward|getForward}.

* @function Mat4(0).getFront - * @param {Mat4} m - * @returns {Vec3} + * @param {Mat4} m - The matrix. + * @returns {Vec3} The negative z-axis rotated by orientation. */ // redundant, calls getForward which better describes the returned vector as a direction glm::vec3 getFront(const glm::mat4& m) const { return getForward(m); } /**jsdoc + * Gets the "forward" direction that the camera would face if its orientation was set to the rotation contained in a + * matrix. The High Fidelity camera has axes x = right, y = up, -z = forward. * @function Mat4(0).getForward - * @param {Mat4} m - * @returns {Vec3} + * @param {Mat4} m - The matrix. + * @returns {Vec3} The negative z-axis rotated by the rotation in the matrix. + * @example Demonstrate that the "forward" direction is the negative z-axis. + * var rot = Quat.IDENTITY; + * var trans = Vec3.ZERO; + * var matrix = Mat4.createFromRotAndTrans(rot, trans); + * var forward = Mat4.getForward(matrix); + * print("Forward: " + JSON.stringify(forward)); + * // Forward: {"x":0,"y":0,"z":-1} */ glm::vec3 getForward(const glm::mat4& m) const; /**jsdoc + * Gets the "right" direction that the camera would have if its orientation was set to the rotation contained in a matrix. + * The High Fidelity camera has axes x = right, y = up, -z = forward. * @function Mat4(0).getRight - * @param {Mat4} m - * @returns {Vec3} + * @param {Mat4} m - The matrix. + * @returns {Vec3} The x-axis rotated by the rotation in the matrix. */ glm::vec3 getRight(const glm::mat4& m) const; /**jsdoc + * Gets the "up" direction that the camera would have if its orientation was set to the rotation contained in a matrix. The + * High Fidelity camera has axes x = right, y = up, -z = forward. * @function Mat4(0).getUp - * @param {Mat4} m - * @returns {Vec3} + * @param {Mat4} m - The matrix. + * @returns {Vec3} The y-axis rotated by the rotation in the matrix. */ glm::vec3 getUp(const glm::mat4& m) const; + /**jsdoc + * Prints a matrix to the program log as a label followed by the matrix's values. * @function Mat4(0).print - * @param {string} label - * @param {Mat4} m - * @param {boolean} [transpose=false] + * @param {string} label - The label to print. + * @param {Mat4} m - The matrix to print. + * @param {boolean} [transpose=false] - true to transpose the matrix before printing (so that it prints the + * matrix's rows), false to not transpose the matrix (so that it prints the matrix's columns). + * @example Two ways of printing a label and matrix value. + * var scale = Vec3.multiply(2, Vec3.ONE); + * var rot = Quat.fromPitchYawRollDegrees(30, 45, 60); + * var trans = { x: 10, y: 11, z: 12 }; + * var matrix = Mat4.createFromScaleRotAndTrans(scale, rot, trans); + * + * Mat4.print("Matrix:", matrix); + * // Matrix: dmat4x4((0.707107, 1.224745, -1.414214, 0.000000), + * // (-1.146447, 1.478398, 0.707107, 0.000000), + * // (1.478398, 0.560660, 1.224745, 0.000000), + * // (10.000000, 11.000000, 12.000000, 1.000000)) + * + * print("Matrix: " + JSON.stringify(matrix)); + * // Matrix: {"r0c0":0.7071067094802856,"r1c0":1.2247446775436401,"r2c0":-1.4142136573791504,"r3c0":0, + * // "r0c1": -1.1464465856552124, "r1c1": 1.4783978462219238, "r2c1": 0.7071066498756409, "r3c1": 0, + * // "r0c2": 1.4783978462219238, "r1c2": 0.5606603026390076, "r2c2": 1.2247447967529297, "r3c2": 0, + * // "r0c3": 10, "r1c3": 11, "r2c3": 12, "r3c3": 1} */ void print(const QString& label, const glm::mat4& m, bool transpose = false) const; }; diff --git a/scripts/developer/tests/mat4test.js b/scripts/developer/tests/mat4test.js index 4e835ec82f..2abcf0dd19 100644 --- a/scripts/developer/tests/mat4test.js +++ b/scripts/developer/tests/mat4test.js @@ -9,14 +9,25 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var IDENTITY = {r0c0: 1, r0c1: 0, r0c2: 0, r0c3: 0, - r1c0: 0, r1c1: 1, r1c2: 0, r1c3: 0, - r2c0: 0, r2c1: 0, r2c2: 1, r2c3: 0, - r3c0: 0, r3c1: 0, r3c2: 0, r3c3: 1}; +var X = {x: 1, y: 0, z: 0}; +var Y = {x: 0, y: 1, z: 0}; +var Z = {x: 0, y: 0, z: 1}; + +var IDENTITY = { + r0c0: 1, r0c1: 0, r0c2: 0, r0c3: 0, + r1c0: 0, r1c1: 1, r1c2: 0, r1c3: 0, + r2c0: 0, r2c1: 0, r2c2: 1, r2c3: 0, + r3c0: 0, r3c1: 0, r3c2: 0, r3c3: 1 +}; var ROT_ZERO = {x: 0, y: 0, z: 0, w: 1}; var ROT_Y_180 = {x: 0, y: 1, z: 0, w: 0}; +var DEG_45 = Math.PI / 4; +var ROT_X_45 = Quat.angleAxis(DEG_45, X); +var ROT_Y_45 = Quat.angleAxis(DEG_45, Y); +var ROT_Z_45 = Quat.angleAxis(DEG_45, Z); + var ONE = {x: 1, y: 1, z: 1}; var ZERO = {x: 0, y: 0, z: 0}; var ONE_TWO_THREE = {x: 1, y: 2, z: 3}; @@ -24,6 +35,7 @@ var ONE_HALF = {x: 0.5, y: 0.5, z: 0.5}; var EPSILON = 0.000001; + function mat4FuzzyEqual(a, b) { var r, c; for (r = 0; r < 4; r++) { @@ -141,12 +153,45 @@ function testInverse() { assert(mat4FuzzyEqual(IDENTITY, Mat4.multiply(test2, Mat4.inverse(test2)))); } -function testForward() { - var test0 = IDENTITY; - assert(mat4FuzzyEqual({x: 0, y: 0, z: -1}, Mat4.getForward(test0))); +function columnsFromQuat(q) { + var axes = [Vec3.multiplyQbyV(q, X), Vec3.multiplyQbyV(q, Y), Vec3.multiplyQbyV(q, Z)]; + axes[0].w = 0; + axes[1].w = 0; + axes[2].w = 0; + axes[3] = {x: 0, y: 0, z: 0, w: 1}; + return axes; +} - var test1 = Mat4.createFromScaleRotAndTrans(ONE_HALF, ROT_Y_180, ONE_TWO_THREE); - assert(mat4FuzzyEqual({x: 0, y: 0, z: 1}, Mat4.getForward(test1))); +function matrixFromColumns(cols) { + return Mat4.createFromColumns(cols[0], cols[1], cols[2], cols[3]); +} + +function testMatForwardRightUpFromQuat(q) { + var cols = columnsFromQuat(q); + var mat = matrixFromColumns(cols); + + assert(vec3FuzzyEqual(Mat4.getForward(mat), Vec3.multiply(cols[2], -1))); + assert(vec3FuzzyEqual(Mat4.getForward(mat), Quat.getForward(q))); + + assert(vec3FuzzyEqual(Mat4.getRight(mat), cols[0])); + assert(vec3FuzzyEqual(Mat4.getRight(mat), Quat.getRight(q))); + + assert(vec3FuzzyEqual(Mat4.getUp(mat), cols[1])); + assert(vec3FuzzyEqual(Mat4.getUp(mat), Quat.getUp(q))); +} + +function testForwardRightUp() { + + // test several variations of rotations + testMatForwardRightUpFromQuat(ROT_X_45); + testMatForwardRightUpFromQuat(ROT_Y_45); + testMatForwardRightUpFromQuat(ROT_Z_45); + testMatForwardRightUpFromQuat(Quat.multiply(ROT_X_45, ROT_Y_45)); + testMatForwardRightUpFromQuat(Quat.multiply(ROT_Y_45, ROT_X_45)); + testMatForwardRightUpFromQuat(Quat.multiply(ROT_X_45, ROT_Z_45)); + testMatForwardRightUpFromQuat(Quat.multiply(ROT_Z_45, ROT_X_45)); + testMatForwardRightUpFromQuat(Quat.multiply(ROT_X_45, ROT_Z_45)); + testMatForwardRightUpFromQuat(Quat.multiply(ROT_Z_45, ROT_X_45)); } function testMat4() { @@ -157,7 +202,7 @@ function testMat4() { testTransformPoint(); testTransformVector(); testInverse(); - testForward(); + testForwardRightUp(); print("MAT4 TEST complete! (" + (testCount - failureCount) + "/" + testCount + ") tests passed!"); } diff --git a/scripts/simplifiedUI/ui/simplifiedUI.js b/scripts/simplifiedUI/ui/simplifiedUI.js index f1f1dd6dd3..1154e386ea 100644 --- a/scripts/simplifiedUI/ui/simplifiedUI.js +++ b/scripts/simplifiedUI/ui/simplifiedUI.js @@ -296,7 +296,7 @@ function updateOutputDeviceMutedOverlay(isMuted) { props.y = Window.innerHeight / 2 - overlayDims / 2; var outputDeviceMutedOverlayBottomY = props.y + overlayDims; - var inputDeviceMutedOverlayTopY = getInputDeviceMutedOverlayTopY(); + var inputDeviceMutedOverlayTopY = INPUT_DEVICE_MUTED_MARGIN_TOP_PX; if (outputDeviceMutedOverlayBottomY + OUTPUT_DEVICE_MUTED_MARGIN_BOTTOM_PX > inputDeviceMutedOverlayTopY) { overlayDims = 2 * (inputDeviceMutedOverlayTopY - Window.innerHeight / 2 - OUTPUT_DEVICE_MUTED_MARGIN_BOTTOM_PX); } @@ -473,15 +473,11 @@ function maybeDeleteInputDeviceMutedOverlay() { } -function getInputDeviceMutedOverlayTopY() { - return (Window.innerHeight - INPUT_DEVICE_MUTED_OVERLAY_DEFAULT_Y_PX - INPUT_DEVICE_MUTED_MARGIN_BOTTOM_PX); -} - - var inputDeviceMutedOverlay = false; -var INPUT_DEVICE_MUTED_OVERLAY_DEFAULT_X_PX = 353; -var INPUT_DEVICE_MUTED_OVERLAY_DEFAULT_Y_PX = 95; -var INPUT_DEVICE_MUTED_MARGIN_BOTTOM_PX = 20 + TOP_BAR_HEIGHT_PX; +var INPUT_DEVICE_MUTED_OVERLAY_DEFAULT_X_PX = 237; +var INPUT_DEVICE_MUTED_OVERLAY_DEFAULT_Y_PX = 64; +var INPUT_DEVICE_MUTED_MARGIN_LEFT_PX = 20; +var INPUT_DEVICE_MUTED_MARGIN_TOP_PX = 20; function updateInputDeviceMutedOverlay(isMuted) { if (isMuted) { var props = { @@ -490,8 +486,8 @@ function updateInputDeviceMutedOverlay(isMuted) { }; props.width = INPUT_DEVICE_MUTED_OVERLAY_DEFAULT_X_PX; props.height = INPUT_DEVICE_MUTED_OVERLAY_DEFAULT_Y_PX; - props.x = Window.innerWidth / 2 - INPUT_DEVICE_MUTED_OVERLAY_DEFAULT_X_PX / 2; - props.y = getInputDeviceMutedOverlayTopY(); + props.x = INPUT_DEVICE_MUTED_MARGIN_LEFT_PX; + props.y = INPUT_DEVICE_MUTED_MARGIN_TOP_PX; if (inputDeviceMutedOverlay) { Overlays.editOverlay(inputDeviceMutedOverlay, props); } else {