diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index a1a2b7c1b5..ce724d7368 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -82,6 +82,9 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri } _assignmentServerSocket = HifiSockAddr(_assignmentServerHostname, assignmentServerPort, true); + if (_assignmentServerSocket.isNull()) { + qCCritical(assignment_client) << "PAGE: Couldn't resolve domain server address" << _assignmentServerHostname; + } _assignmentServerSocket.setObjectName("AssignmentServer"); nodeList->setAssignmentServerSocket(_assignmentServerSocket); @@ -183,16 +186,21 @@ void AssignmentClient::sendAssignmentRequest() { // we want to check again for the local domain-server port in case the DS has restarted quint16 localAssignmentServerPort; if (nodeList->getLocalServerPortFromSharedMemory(DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY, localAssignmentServerPort)) { - if (localAssignmentServerPort != _assignmentServerSocket.getPort()) { - qCDebug(assignment_client) << "Port for local assignment server read from shared memory is" - << localAssignmentServerPort; + if (localAssignmentServerPort == 0) { + qCWarning(assignment_client) << "ALERT: Server port from shared memory is 0"; + } else { + if (localAssignmentServerPort != _assignmentServerSocket.getPort()) { + qCDebug(assignment_client) << "Port for local assignment server read from shared memory is" + << localAssignmentServerPort; - _assignmentServerSocket.setPort(localAssignmentServerPort); - nodeList->setAssignmentServerSocket(_assignmentServerSocket); + _assignmentServerSocket.setPort(localAssignmentServerPort); + nodeList->setAssignmentServerSocket(_assignmentServerSocket); + } } } else { - qCWarning(assignment_client) << "Failed to read local assignment server port from shared memory" - << "- will send assignment request to previous assignment server socket."; + qCWarning(assignment_client) << "ALERT: Failed to read local assignment server port from shared memory (" + << DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY + << ")- will send assignment request to previous assignment server socket."; } } @@ -250,7 +258,7 @@ void AssignmentClient::handleCreateAssignmentPacket(QSharedPointerstarted() workerThread->start(); } else { - qCWarning(assignment_client) << "Received an assignment that could not be unpacked. Re-requesting."; + qCWarning(assignment_client) << "ALERT: Received an assignment that could not be unpacked. Re-requesting."; } } diff --git a/assignment-client/src/AssignmentClientMonitor.cpp b/assignment-client/src/AssignmentClientMonitor.cpp index 6601be849f..4c7f71a7aa 100644 --- a/assignment-client/src/AssignmentClientMonitor.cpp +++ b/assignment-client/src/AssignmentClientMonitor.cpp @@ -33,7 +33,7 @@ const QString ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME = "assignment-client-monitor const int WAIT_FOR_CHILD_MSECS = 1000; #ifdef Q_OS_WIN -HANDLE PROCESS_GROUP = createProcessGroup(); +void* PROCESS_GROUP = createProcessGroup(); #endif AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmentClientForks, diff --git a/assignment-client/src/AssignmentFactory.cpp b/assignment-client/src/AssignmentFactory.cpp index 405039d833..ceab285763 100644 --- a/assignment-client/src/AssignmentFactory.cpp +++ b/assignment-client/src/AssignmentFactory.cpp @@ -24,7 +24,9 @@ ThreadedAssignment* AssignmentFactory::unpackAssignment(ReceivedMessage& message) { quint8 packedType; - message.peekPrimitive(&packedType); + if (message.peekPrimitive(&packedType) != sizeof(packedType)) { + return nullptr; + } Assignment::Type unpackedType = (Assignment::Type) packedType; diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 46ca51219d..522f0bf163 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -265,7 +265,7 @@ static const int AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND = 45; void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { quint64 start = usecTimestampNow(); - if (node->getType() == NodeType::Agent && node->getLinkedData() && node->getActiveSocket() && !node->isUpstream()) { + if ((node->getType() == NodeType::Agent || node->getType() == NodeType::EntityScriptServer) && node->getLinkedData() && node->getActiveSocket() && !node->isUpstream()) { broadcastAvatarDataToAgent(node); } else if (node->getType() == NodeType::DownstreamAvatarMixer) { broadcastAvatarDataToDownstreamMixer(node); @@ -448,13 +448,6 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) // or that somehow we haven't sent if (lastSeqToReceiver == lastSeqFromSender && lastSeqToReceiver != 0) { ++numAvatarsHeldBack; - - // BUGZ-781 verbose debugging: - auto usecLastTimeSent = destinationNodeData->getLastOtherAvatarEncodeTime(sourceAvatarNodeData->getNodeLocalID()); - if (usecLastTimeSent != 0 && startIgnoreCalculation - usecLastTimeSent > 10 * USECS_PER_SECOND) { - qCDebug(avatars) << "Not sent avatar" << *sourceAvatarNode << "to Node" << *destinationNode << "in > 10 s"; - } - sendAvatar = false; } else if (lastSeqFromSender == 0) { // We have have not yet received any data about this avatar. Ignore it for now diff --git a/assignment-client/src/entities/EntityTreeHeadlessViewer.h b/assignment-client/src/entities/EntityTreeHeadlessViewer.h index f4d5911821..a8503510e0 100644 --- a/assignment-client/src/entities/EntityTreeHeadlessViewer.h +++ b/assignment-client/src/entities/EntityTreeHeadlessViewer.h @@ -24,6 +24,9 @@ class EntitySimulation; /**jsdoc + * The EntityViewer API provides a headless viewer for assignment client scripts, so that they can "see" entities + * in order for them to be available in the {@link Entities} API. + * * @namespace EntityViewer * * @hifi-assignment-client diff --git a/assignment-client/src/octree/OctreeHeadlessViewer.h b/assignment-client/src/octree/OctreeHeadlessViewer.h index 67a81b1d2a..2debf2cb84 100644 --- a/assignment-client/src/octree/OctreeHeadlessViewer.h +++ b/assignment-client/src/octree/OctreeHeadlessViewer.h @@ -28,6 +28,7 @@ public: public slots: /**jsdoc + * Updates the entities currently in view. * @function EntityViewer.queryOctree */ void queryOctree(); @@ -36,26 +37,30 @@ public slots: // setters for camera attributes /**jsdoc + * Sets the position of the view frustum. * @function EntityViewer.setPosition - * @param {Vec3} position + * @param {Vec3} position - The position of the view frustum. */ void setPosition(const glm::vec3& position) { _hasViewFrustum = true; _viewFrustum.setPosition(position); } /**jsdoc + * Sets the orientation of the view frustum. * @function EntityViewer.setOrientation - * @param {Quat} orientation + * @param {Quat} orientation - The orientation of the view frustum. */ void setOrientation(const glm::quat& orientation) { _hasViewFrustum = true; _viewFrustum.setOrientation(orientation); } /**jsdoc + * Sets the radius of the center "keyhole" in the view frustum. * @function EntityViewer.setCenterRadius - * @param {number} radius + * @param {number} radius - The radius of the center "keyhole" in the view frustum. */ void setCenterRadius(float radius) { _hasViewFrustum = true; _viewFrustum.setCenterRadius(radius); } /**jsdoc + * Sets the radius of the center "keyhole" in the view frustum. * @function EntityViewer.setKeyholeRadius - * @param {number} radius + * @param {number} radius - The radius of the center "keyhole" in the view frustum. * @deprecated This function is deprecated and will be removed. Use {@link EntityViewer.setCenterRadius|setCenterRadius} * instead. */ @@ -66,33 +71,38 @@ public slots: /**jsdoc * @function EntityViewer.setVoxelSizeScale - * @param {number} sizeScale + * @param {number} sizeScale - The voxel size scale. + * @deprecated This function is deprecated and will be removed. */ void setVoxelSizeScale(float sizeScale) { _octreeQuery.setOctreeSizeScale(sizeScale) ; } /**jsdoc * @function EntityViewer.setBoundaryLevelAdjust - * @param {number} boundaryLevelAdjust + * @param {number} boundaryLevelAdjust - The boundary level adjust factor. + * @deprecated This function is deprecated and will be removed. */ void setBoundaryLevelAdjust(int boundaryLevelAdjust) { _octreeQuery.setBoundaryLevelAdjust(boundaryLevelAdjust); } /**jsdoc + * Sets the maximum number of entity packets to receive from the domain server per second. * @function EntityViewer.setMaxPacketsPerSecond - * @param {number} maxPacketsPerSecond + * @param {number} maxPacketsPerSecond - The maximum number of entity packets to receive per second. */ void setMaxPacketsPerSecond(int maxPacketsPerSecond) { _octreeQuery.setMaxQueryPacketsPerSecond(maxPacketsPerSecond); } // getters for camera attributes /**jsdoc + * Gets the position of the view frustum. * @function EntityViewer.getPosition - * @returns {Vec3} + * @returns {Vec3} The position of the view frustum. */ const glm::vec3& getPosition() const { return _viewFrustum.getPosition(); } /**jsdoc + * Gets the orientation of the view frustum. * @function EntityViewer.getOrientation - * @returns {Quat} + * @returns {Quat} The orientation of the view frustum. */ const glm::quat& getOrientation() const { return _viewFrustum.getOrientation(); } @@ -101,26 +111,30 @@ public slots: /**jsdoc * @function EntityViewer.getVoxelSizeScale - * @returns {number} + * @returns {number} The voxel size scale. + * @deprecated This function is deprecated and will be removed. */ float getVoxelSizeScale() const { return _octreeQuery.getOctreeSizeScale(); } /**jsdoc * @function EntityViewer.getBoundaryLevelAdjust - * @returns {number} + * @returns {number} The boundary level adjust factor. + * @deprecated This function is deprecated and will be removed. */ int getBoundaryLevelAdjust() const { return _octreeQuery.getBoundaryLevelAdjust(); } /**jsdoc + * Gets the maximum number of entity packets to receive from the domain server per second. * @function EntityViewer.getMaxPacketsPerSecond - * @returns {number} + * @returns {number} The maximum number of entity packets to receive per second. */ int getMaxPacketsPerSecond() const { return _octreeQuery.getMaxQueryPacketsPerSecond(); } /**jsdoc + * Gets the number of nodes in the octree. * @function EntityViewer.getOctreeElementsCount - * @returns {number} + * @returns {number} The number of nodes in the octree. */ unsigned getOctreeElementsCount() const { return _tree->getOctreeElementsCount(); } diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index ea002a27c6..7c3d491470 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -86,8 +86,6 @@ EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssig this, "handleOctreePacket"); packetReceiver.registerListener(PacketType::SelectedAudioFormat, this, "handleSelectedAudioFormat"); - auto avatarHashMap = DependencyManager::set(); - packetReceiver.registerListener(PacketType::ReloadEntityServerScript, this, "handleReloadEntityServerScriptPacket"); packetReceiver.registerListener(PacketType::EntityScriptGetStatus, this, "handleEntityScriptGetStatusPacket"); packetReceiver.registerListener(PacketType::EntityServerScriptLog, this, "handleEntityServerScriptLogPacket"); @@ -255,6 +253,7 @@ void EntityScriptServer::handleEntityScriptCallMethodPacket(QSharedPointer(ScriptEngine::ENTITY_SERVER_SCRIPT); DependencyManager::set(); + DependencyManager::set(); // make sure we request our script once the agent connects to the domain auto nodeList = DependencyManager::get(); @@ -448,6 +447,7 @@ void EntityScriptServer::resetEntitiesScriptEngine() { newEngine->globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue); newEngine->registerGlobalObject("SoundCache", DependencyManager::get().data()); + newEngine->registerGlobalObject("AvatarList", DependencyManager::get().data()); // connect this script engines printedMessage signal to the global ScriptEngines these various messages auto scriptEngines = DependencyManager::get().data(); @@ -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/cmake/externals/wasapi/CMakeLists.txt b/cmake/externals/wasapi/CMakeLists.txt index 9e22e28f58..18d93bde40 100644 --- a/cmake/externals/wasapi/CMakeLists.txt +++ b/cmake/externals/wasapi/CMakeLists.txt @@ -6,8 +6,8 @@ if (WIN32) include(ExternalProject) ExternalProject_Add( ${EXTERNAL_NAME} - URL https://public.highfidelity.com/dependencies/qtaudio_wasapi11.zip - URL_MD5 d0eb8489455e7f79d59155535a2c8861 + URL https://public.highfidelity.com/dependencies/qtaudio_wasapi12.zip + URL_MD5 9e2eef41165f85344808f754b48bf08d CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" diff --git a/cmake/ports/bullet3/bullet-git-fix-build-clang-8.patch b/cmake/ports/bullet3/bullet-git-fix-build-clang-8.patch new file mode 100644 index 0000000000..bdf106b38e --- /dev/null +++ b/cmake/ports/bullet3/bullet-git-fix-build-clang-8.patch @@ -0,0 +1,36 @@ +From 7638b7c5a659dceb4e580ae87d4d60b00847ef94 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Emil=20Nord=C3=A9n?= +Date: Sat, 4 May 2019 08:38:53 +0200 +Subject: [PATCH] fixed build on latest version of clang + +--- + src/Bullet3Common/b3Vector3.h | 2 +- + src/LinearMath/btVector3.h | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/Bullet3Common/b3Vector3.h b/src/Bullet3Common/b3Vector3.h +index 56e6c13311..a70d68d6e1 100644 +--- a/src/Bullet3Common/b3Vector3.h ++++ b/src/Bullet3Common/b3Vector3.h +@@ -36,7 +36,7 @@ subject to the following restrictions: + #pragma warning(disable : 4556) // value of intrinsic immediate argument '4294967239' is out of range '0 - 255' + #endif + +-#define B3_SHUFFLE(x, y, z, w) ((w) << 6 | (z) << 4 | (y) << 2 | (x)) ++#define B3_SHUFFLE(x, y, z, w) (((w) << 6 | (z) << 4 | (y) << 2 | (x)) & 0xff) + //#define b3_pshufd_ps( _a, _mask ) (__m128) _mm_shuffle_epi32((__m128i)(_a), (_mask) ) + #define b3_pshufd_ps(_a, _mask) _mm_shuffle_ps((_a), (_a), (_mask)) + #define b3_splat3_ps(_a, _i) b3_pshufd_ps((_a), B3_SHUFFLE(_i, _i, _i, 3)) +diff --git a/src/LinearMath/btVector3.h b/src/LinearMath/btVector3.h +index 61fd8d1e46..d65ed9808d 100644 +--- a/src/LinearMath/btVector3.h ++++ b/src/LinearMath/btVector3.h +@@ -36,7 +36,7 @@ subject to the following restrictions: + #pragma warning(disable : 4556) // value of intrinsic immediate argument '4294967239' is out of range '0 - 255' + #endif + +-#define BT_SHUFFLE(x, y, z, w) ((w) << 6 | (z) << 4 | (y) << 2 | (x)) ++#define BT_SHUFFLE(x, y, z, w) (((w) << 6 | (z) << 4 | (y) << 2 | (x)) & 0xff) + //#define bt_pshufd_ps( _a, _mask ) (__m128) _mm_shuffle_epi32((__m128i)(_a), (_mask) ) + #define bt_pshufd_ps(_a, _mask) _mm_shuffle_ps((_a), (_a), (_mask)) + #define bt_splat3_ps(_a, _i) bt_pshufd_ps((_a), BT_SHUFFLE(_i, _i, _i, 3)) diff --git a/cmake/ports/bullet3/portfile.cmake b/cmake/ports/bullet3/portfile.cmake index 32713f5a46..cda39ed349 100644 --- a/cmake/ports/bullet3/portfile.cmake +++ b/cmake/ports/bullet3/portfile.cmake @@ -27,6 +27,7 @@ vcpkg_from_github( REF ab8f16961e19a86ee20c6a1d61f662392524cc77 SHA512 927742db29867517283d45e475f0c534a9a57e165cae221f26e08e88057253a1682ac9919b2dc547b9cf388ba0b931b175623461d44f28c9184796ba90b1ed55 HEAD_REF master + PATCHES "bullet-git-fix-build-clang-8.patch" ) vcpkg_configure_cmake( diff --git a/cmake/ports/webrtc/portfile.cmake b/cmake/ports/webrtc/portfile.cmake index 3f2fb7a6ab..b5b46723bd 100644 --- a/cmake/ports/webrtc/portfile.cmake +++ b/cmake/ports/webrtc/portfile.cmake @@ -14,9 +14,9 @@ elseif (WIN32) elseif (APPLE) vcpkg_download_distfile( WEBRTC_SOURCE_ARCHIVE - URLS https://hifi-public.s3.amazonaws.com/seth/webrtc-20190626-osx.tar.gz - SHA512 fc70cec1b5ee87395137b7090f424e2fc2300fc17d744d5ffa1cf7aa0e0f1a069a9d72ba1ad2fb4a640ebeb6c218bda24351ba0083e1ff96c4a4b5032648a9d2 - FILENAME webrtc-20190626-osx.tar.gz + URLS https://hifi-public.s3.amazonaws.com/seth/webrtc-m78-osx.tar.gz + SHA512 8b547da921cc96f5c22b4253a1c9e707971bb627898fbdb6b238ef1318c7d2512e878344885c936d4bd6a66005cc5b63dfc3fa5ddd14f17f378dcaa17b5e25df + FILENAME webrtc-m78-osx.tar.gz ) else () # else Linux desktop 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/hifi_vcpkg.py b/hifi_vcpkg.py index 821d9ae0b7..7bb261faa0 100644 --- a/hifi_vcpkg.py +++ b/hifi_vcpkg.py @@ -134,7 +134,7 @@ endif() downloadVcpkg = True if not downloadVcpkg and not os.path.isfile(self.exe): - print("Missing executable, boostrapping") + print("Missing executable, boot-strapping") downloadVcpkg = True # Make sure we have a vcpkg executable 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..670c520b65 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": 20, + "interpTarget": 20, "interpType": "evaluateBoth", "priority": 0.33, "resume": true, @@ -2159,8 +2489,8 @@ { "easingType": "easeInOutQuad", "id": "talk02", - "interpDuration": 15, - "interpTarget": 15, + "interpDuration": 20, + "interpTarget": 20, "interpType": "evaluateBoth", "priority": 0.33, "resume": true, @@ -2170,8 +2500,8 @@ { "easingType": "easeInOutQuad", "id": "talk03", - "interpDuration": 15, - "interpTarget": 15, + "interpDuration": 20, + "interpTarget": 20, "interpType": "evaluateBoth", "priority": 0.33, "resume": true, @@ -2181,8 +2511,8 @@ { "easingType": "easeInOutQuad", "id": "talk04", - "interpDuration": 15, - "interpTarget": 15, + "interpDuration": 20, + "interpTarget": 20, "interpType": "evaluateBoth", "priority": 0.33, "resume": true, @@ -2192,8 +2522,8 @@ { "easingType": "easeInOutQuad", "id": "talk_armsdown", - "interpDuration": 15, - "interpTarget": 15, + "interpDuration": 20, + "interpTarget": 20, "interpType": "evaluateBoth", "priority": 0.33, "resume": true, @@ -2203,8 +2533,8 @@ { "easingType": "easeInOutQuad", "id": "talk_lefthand", - "interpDuration": 15, - "interpTarget": 15, + "interpDuration": 20, + "interpTarget": 20, "interpType": "evaluateBoth", "priority": 0.33, "resume": true, @@ -2214,8 +2544,8 @@ { "easingType": "easeInOutQuad", "id": "talk_righthand", - "interpDuration": 15, - "interpTarget": 15, + "interpDuration": 20, + "interpTarget": 20, "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": [ { @@ -4406,32 +4736,6 @@ }, { "children": [ - { - "children": [ - ], - "data": { - "endFrame": 30, - "loopFlag": true, - "startFrame": 1, - "timeScale": 1, - "url": "qrc:///avatar/animations/side_step_short_left.fbx" - }, - "id": "strafeLeftShortStep_c", - "type": "clip" - }, - { - "children": [ - ], - "data": { - "endFrame": 20, - "loopFlag": true, - "startFrame": 1, - "timeScale": 1, - "url": "qrc:///avatar/animations/side_step_left.fbx" - }, - "id": "strafeLeftStep_c", - "type": "clip" - }, { "children": [ ], @@ -4489,8 +4793,6 @@ "alpha": 0, "alphaVar": "moveLateralAlpha", "characteristicSpeeds": [ - 0.1, - 0.5, 1, 2.55, 3.35, @@ -4504,34 +4806,6 @@ }, { "children": [ - { - "children": [ - ], - "data": { - "endFrame": 30, - "loopFlag": true, - "mirrorFlag": true, - "startFrame": 1, - "timeScale": 1, - "url": "qrc:///avatar/animations/side_step_short_left.fbx" - }, - "id": "strafeRightShortStep_c", - "type": "clip" - }, - { - "children": [ - ], - "data": { - "endFrame": 20, - "loopFlag": true, - "mirrorFlag": true, - "startFrame": 1, - "timeScale": 1, - "url": "qrc:///avatar/animations/side_step_left.fbx" - }, - "id": "strafeRightStep_c", - "type": "clip" - }, { "children": [ ], @@ -4593,8 +4867,6 @@ "alpha": 0, "alphaVar": "moveLateralAlpha", "characteristicSpeeds": [ - 0.1, - 0.5, 1, 2.55, 3.4, @@ -4912,62 +5184,6 @@ "interpTarget": 6, "interpType": "evaluateBoth", "transitions": [ - { - "state": "idle", - "var": "isNotMoving" - }, - { - "state": "WALKFWD", - "var": "isMovingForward" - }, - { - "state": "WALKBWD", - "var": "isMovingBackward" - }, - { - "state": "STRAFERIGHT", - "var": "isMovingRight" - }, - { - "state": "STRAFELEFT", - "var": "isMovingLeft" - }, - { - "state": "turnRight", - "var": "isTurningRight" - }, - { - "state": "turnLeft", - "var": "isTurningLeft" - }, - { - "state": "fly", - "var": "isFlying" - }, - { - "state": "takeoffStand", - "var": "isTakeoffStand" - }, - { - "state": "TAKEOFFRUN", - "var": "isTakeoffRun" - }, - { - "state": "inAirStand", - "var": "isInAirStand" - }, - { - "state": "INAIRRUN", - "var": "isInAirRun" - }, - { - "state": "strafeRightHmd", - "var": "isMovingRightHmd" - }, - { - "state": "strafeLeftHmd", - "var": "isMovingLeftHmd" - }, { "state": "idle", "var": "isNotSeated" @@ -4977,25 +5193,25 @@ { "easingType": "easeInOutQuad", "id": "idle", - "interpDuration": 20, + "interpDuration": 15, "interpTarget": 20, "interpType": "evaluateBoth", "transitions": [ { "state": "WALKFWD", - "var": "isMovingForward" + "var": "isInputForward" }, { "state": "WALKBWD", - "var": "isMovingBackward" + "var": "isInputBackward" }, { "state": "STRAFERIGHT", - "var": "isMovingRight" + "var": "isInputRight" }, { "state": "STRAFELEFT", - "var": "isMovingLeft" + "var": "isInputLeft" }, { "state": "turnRight", @@ -5050,19 +5266,19 @@ }, { "state": "idle", - "var": "isNotMoving" + "var": "isNotInput" }, { "state": "WALKBWD", - "var": "isMovingBackward" + "var": "isInputBackward" }, { "state": "STRAFERIGHT", - "var": "isMovingRight" + "var": "isInputRight" }, { "state": "STRAFELEFT", - "var": "isMovingLeft" + "var": "isInputLeft" }, { "state": "turnRight", @@ -5109,8 +5325,8 @@ { "easingType": "easeInOutQuad", "id": "idleSettle", - "interpDuration": 15, - "interpTarget": 15, + "interpDuration": 13, + "interpTarget": 14, "interpType": "snapshotPrev", "transitions": [ { @@ -5139,19 +5355,19 @@ }, { "state": "WALKFWD", - "var": "isMovingForward" + "var": "isInputForward" }, { "state": "WALKBWD", - "var": "isMovingBackward" + "var": "isInputBackward" }, { "state": "STRAFERIGHT", - "var": "isMovingRight" + "var": "isInputRight" }, { "state": "STRAFELEFT", - "var": "isMovingLeft" + "var": "isInputLeft" }, { "state": "strafeRightHmd", @@ -5203,19 +5419,19 @@ "transitions": [ { "state": "idleSettle", - "var": "isNotMoving" + "var": "isNotInputSlow" }, { "state": "WALKBWD", - "var": "isMovingBackward" + "var": "isInputBackward" }, { "state": "STRAFERIGHT", - "var": "isMovingRight" + "var": "isInputRight" }, { "state": "STRAFELEFT", - "var": "isMovingLeft" + "var": "isInputLeft" }, { "state": "turnRight", @@ -5267,19 +5483,19 @@ "transitions": [ { "state": "idleSettle", - "var": "isNotMoving" + "var": "isNotInputSlow" }, { "state": "WALKFWD", - "var": "isMovingForward" + "var": "isInputForward" }, { "state": "STRAFERIGHT", - "var": "isMovingRight" + "var": "isInputRight" }, { "state": "STRAFELEFT", - "var": "isMovingLeft" + "var": "isInputLeft" }, { "state": "turnRight", @@ -5331,15 +5547,15 @@ "transitions": [ { "state": "idleSettle", - "var": "isNotMoving" + "var": "isNotInputSlow" }, { "state": "WALKFWD", - "var": "isMovingForward" + "var": "isInputForward" }, { "state": "WALKBWD", - "var": "isMovingBackward" + "var": "isInputBackward" }, { "state": "STRAFELEFT", @@ -5395,15 +5611,15 @@ "transitions": [ { "state": "idleSettle", - "var": "isNotMoving" + "var": "isNotInputSlow" }, { "state": "WALKFWD", - "var": "isMovingForward" + "var": "isInputForward" }, { "state": "WALKBWD", - "var": "isMovingBackward" + "var": "isInputBackward" }, { "state": "STRAFERIGHT", @@ -5464,19 +5680,19 @@ }, { "state": "WALKFWD", - "var": "isMovingForward" + "var": "isInputForward" }, { "state": "WALKBWD", - "var": "isMovingBackward" + "var": "isInputBackward" }, { "state": "STRAFERIGHT", - "var": "isMovingRight" + "var": "isInputRight" }, { "state": "STRAFELEFT", - "var": "isMovingLeft" + "var": "isInputLeft" }, { "state": "turnLeft", @@ -5529,19 +5745,19 @@ }, { "state": "WALKFWD", - "var": "isMovingForward" + "var": "isInputForward" }, { "state": "WALKBWD", - "var": "isMovingBackward" + "var": "isInputBackward" }, { "state": "STRAFERIGHT", - "var": "isMovingRight" + "var": "isInputRight" }, { "state": "STRAFELEFT", - "var": "isMovingLeft" + "var": "isInputLeft" }, { "state": "turnRight", @@ -5589,15 +5805,15 @@ "transitions": [ { "state": "idleSettle", - "var": "isNotMoving" + "var": "isNotInput" }, { "state": "WALKFWD", - "var": "isMovingForward" + "var": "isInputForward" }, { "state": "WALKBWD", - "var": "isMovingBackward" + "var": "isInputBackward" }, { "state": "strafeLeftHmd", @@ -5605,11 +5821,11 @@ }, { "state": "STRAFERIGHT", - "var": "isMovingRight" + "var": "isInputRight" }, { "state": "STRAFELEFT", - "var": "isMovingLeft" + "var": "isInputLeft" }, { "state": "turnRight", @@ -5653,15 +5869,15 @@ "transitions": [ { "state": "idleSettle", - "var": "isNotMoving" + "var": "isNotInput" }, { "state": "WALKFWD", - "var": "isMovingForward" + "var": "isInputForward" }, { "state": "WALKBWD", - "var": "isMovingBackward" + "var": "isInputBackward" }, { "state": "strafeRightHmd", @@ -5669,11 +5885,11 @@ }, { "state": "STRAFERIGHT", - "var": "isMovingRight" + "var": "isInputRight" }, { "state": "STRAFELEFT", - "var": "isMovingLeft" + "var": "isInputLeft" }, { "state": "turnRight", @@ -5798,19 +6014,19 @@ "transitions": [ { "state": "WALKFWD", - "var": "isMovingForward" + "var": "isInputForward" }, { "state": "WALKBWD", - "var": "isMovingBackward" + "var": "isInputBackward" }, { "state": "STRAFERIGHT", - "var": "isMovingRight" + "var": "isInputRight" }, { "state": "STRAFELEFT", - "var": "isMovingLeft" + "var": "isInputLeft" }, { "state": "turnRight", diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index eb07c9a6dd..d6ecc540c2 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -78,6 +78,15 @@ "to": "Actions.Yaw" }, + { "from": { "makeAxis" : [ + ["Keyboard.Left"], + ["Keyboard.Right"] + ] + }, + "when": ["Application.CameraFirstPersonLookat", "!Keyboard.Shift"], + "to": "Actions.Yaw" + }, + { "from": { "makeAxis" : [ ["Keyboard.Left"], ["Keyboard.Right"] @@ -113,7 +122,16 @@ "when": ["Application.CameraFirstPerson", "!Keyboard.Control"], "to": "Actions.Yaw" }, - + + { "from": { "makeAxis" : [ + ["Keyboard.A"], + ["Keyboard.D"] + ] + }, + "when": ["Application.CameraFirstPersonLookat", "!Keyboard.Control"], + "to": "Actions.Yaw" + }, + { "from": { "makeAxis" : [ ["Keyboard.A"], ["Keyboard.D"] @@ -149,6 +167,15 @@ "when": "Application.CameraFirstPerson", "to": "Actions.Yaw" }, + + { "from": { "makeAxis" : [ + ["Keyboard.TouchpadLeft"], + ["Keyboard.TouchpadRight"] + ] + }, + "when": "Application.CameraFirstPersonLookat", + "to": "Actions.Yaw" + }, { "from": { "makeAxis" : [ ["Keyboard.TouchpadLeft"], @@ -222,10 +249,12 @@ { "from": "Keyboard.Left", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" }, { "from": "Keyboard.Right", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" }, { "from": "Keyboard.Up", "when": "Application.CameraFirstPerson", "to": "Actions.LONGITUDINAL_FORWARD" }, + { "from": "Keyboard.Up", "when": "Application.CameraFirstPersonLookat", "to": "Actions.LONGITUDINAL_FORWARD" }, { "from": "Keyboard.Up", "when": "Application.CameraThirdPerson", "to": "Actions.LONGITUDINAL_FORWARD" }, { "from": "Keyboard.Up", "when": "Application.CameraLookAt", "to": "Actions.LONGITUDINAL_FORWARD" }, { "from": "Keyboard.Up", "when": "Application.CameraSelfie", "to": "Actions.LONGITUDINAL_BACKWARD" }, { "from": "Keyboard.Down", "when": "Application.CameraFirstPerson", "to": "Actions.LONGITUDINAL_BACKWARD" }, + { "from": "Keyboard.Down", "when": "Application.CameraFirstPersonLookat", "to": "Actions.LONGITUDINAL_BACKWARD" }, { "from": "Keyboard.Down", "when": "Application.CameraThirdPerson", "to": "Actions.LONGITUDINAL_BACKWARD" }, { "from": "Keyboard.Down", "when": "Application.CameraLookAt", "to": "Actions.LONGITUDINAL_BACKWARD" }, { "from": "Keyboard.Down", "when": "Application.CameraSelfie", "to": "Actions.LONGITUDINAL_FORWARD" }, diff --git a/interface/resources/qml/Web3DSurface.qml b/interface/resources/qml/Web3DSurface.qml index ff574ceaa5..3340226761 100644 --- a/interface/resources/qml/Web3DSurface.qml +++ b/interface/resources/qml/Web3DSurface.qml @@ -33,6 +33,12 @@ Item { property var item: null function load(url, scriptUrl) { + // Ensure we reset any existing item to "about:blank" to ensure web audio stops: DEV-2375 + if (root.item != null) { + root.item.url = "about:blank" + root.item.destroy() + root.item = null + } QmlSurface.load("./controls/WebView.qml", root, function(newItem) { root.item = newItem root.item.url = url diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index 03fbbb178e..85e5211649 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -580,8 +580,9 @@ Rectangle { sendToScript(msg); } else if (msg.method === "showInvalidatedLightbox") { lightboxPopup.titleText = "Item Invalidated"; - lightboxPopup.bodyText = 'Your item is marked "invalidated" because this item has been suspended ' + - "from the Marketplace due to a claim against its author."; + lightboxPopup.bodyText = 'This item has been invalidated and is no longer available.
' + + 'If you have questions, please contact marketplace@highfidelity.com.
' + + 'Thank you!'; lightboxPopup.button1text = "CLOSE"; lightboxPopup.button1method = function() { lightboxPopup.visible = false; diff --git a/interface/resources/qml/hifi/simplifiedUI/avatarApp/AvatarApp.qml b/interface/resources/qml/hifi/simplifiedUI/avatarApp/AvatarApp.qml index d52dd3f3d7..7dbadc59f4 100644 --- a/interface/resources/qml/hifi/simplifiedUI/avatarApp/AvatarApp.qml +++ b/interface/resources/qml/hifi/simplifiedUI/avatarApp/AvatarApp.qml @@ -9,6 +9,7 @@ // import QtQuick 2.10 +import QtQuick.Layouts 1.3 import "../simplifiedConstants" as SimplifiedConstants import "../simplifiedControls" as SimplifiedControls import "./components" as AvatarAppComponents @@ -79,7 +80,11 @@ Rectangle { errorText.text = "There was a problem while retrieving your inventory. " + "Please try closing and re-opening the Avatar app.\n\nInventory status: " + result.status + "\nMessage: " + result.message; } else if (result.data && result.data.assets && result.data.assets.length === 0 && avatarAppInventoryModel.count === 0) { - errorText.text = "You have not created any avatars yet! Create an avatar with the Avatar Creator, then close and re-open the Avatar App." + emptyInventoryContainer.visible = true; + } + + if (Settings.getValue("simplifiedUI/debugFTUE", 0) === 4) { + emptyInventoryContainer.visible = true; } avatarAppInventoryModel.handlePage(result.status !== "success" && result.message, result); @@ -140,8 +145,95 @@ Rectangle { anchors.rightMargin: 24 } + + Item { + id: emptyInventoryContainer + visible: false + anchors.top: displayNameHeader.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + + Flickable { + id: emptyInventoryFlickable + anchors.fill: parent + contentWidth: parent.width + contentHeight: emptyInventoryLayout.height + clip: true + + ColumnLayout { + id: emptyInventoryLayout + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: 26 + anchors.right: parent.right + anchors.rightMargin: 26 + spacing: 0 + + HifiStylesUit.GraphikSemiBold { + text: "Stand out from the crowd!" + Layout.preferredWidth: parent.width + Layout.preferredHeight: paintedHeight + Layout.topMargin: 16 + size: 28 + color: simplifiedUI.colors.text.white + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.Wrap + } + + HifiStylesUit.GraphikRegular { + text: "Create your custom avatar." + Layout.preferredWidth: parent.width + Layout.preferredHeight: paintedHeight + Layout.topMargin: 2 + size: 18 + wrapMode: Text.Wrap + color: simplifiedUI.colors.text.white + horizontalAlignment: Text.AlignHCenter + } + + Image { + id: avatarImage; + source: "images/avatarProfilePic.png" + Layout.preferredWidth: parent.width + Layout.preferredHeight: 450 + Layout.alignment: Qt.AlignHCenter + mipmap: true + fillMode: Image.PreserveAspectFit + } + + Image { + source: "images/qrCode.jpg" + Layout.preferredWidth: 190 + Layout.preferredHeight: 190 + Layout.alignment: Qt.AlignHCenter + Layout.topMargin: -160 + mipmap: true + fillMode: Image.PreserveAspectFit + } + + HifiStylesUit.GraphikSemiBold { + text: "Scan for Mobile App" + Layout.preferredWidth: parent.width + Layout.preferredHeight: paintedHeight + Layout.topMargin: 12 + size: 28 + color: simplifiedUI.colors.text.white + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.Wrap + } + } + } + + SimplifiedControls.VerticalScrollBar { + parent: emptyInventoryFlickable + } + } + + Item { id: avatarInfoTextContainer + visible: !emptyInventoryContainer.visible width: parent.implicitWidth height: childrenRect.height anchors.top: displayNameHeader.bottom @@ -164,7 +256,7 @@ Rectangle { id: yourAvatarsSubtitle text: "These are the avatars that you've created and uploaded via the Avatar Creator." width: parent.width - wrapMode: Text.WordWrap + wrapMode: Text.Wrap anchors.top: yourAvatarsTitle.bottom anchors.topMargin: 6 verticalAlignment: TextInput.AlignVCenter @@ -208,9 +300,10 @@ Rectangle { anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom + visible: !emptyInventoryContainer.visible AnimatedImage { - visible: !inventoryContentsList.visible && !errorText.visible + visible: !(inventoryContentsList.visible || errorText.visible) anchors.centerIn: parent width: 72 height: width @@ -271,6 +364,8 @@ Rectangle { return; } } + + root.avatarPreviewUrl = "../../images/defaultAvatar.svg"; } function fromScript(message) { diff --git a/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/avatarProfilePic.png b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/avatarProfilePic.png new file mode 100644 index 0000000000..b59ebb3085 Binary files /dev/null and b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/avatarProfilePic.png differ diff --git a/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/hero.png b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/hero.png new file mode 100644 index 0000000000..15c358e024 Binary files /dev/null and b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/hero.png differ diff --git a/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/qrCode.jpg b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/qrCode.jpg new file mode 100644 index 0000000000..0674781c45 Binary files /dev/null and b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/qrCode.jpg differ diff --git a/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Blue.png b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Blue.png new file mode 100644 index 0000000000..210d37acb6 Binary files /dev/null and b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Blue.png differ diff --git a/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Cyan.png b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Cyan.png new file mode 100644 index 0000000000..726e1c8a69 Binary files /dev/null and b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Cyan.png differ diff --git a/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Green.png b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Green.png new file mode 100644 index 0000000000..6db55816dc Binary files /dev/null and b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Green.png differ diff --git a/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Magenta.png b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Magenta.png new file mode 100644 index 0000000000..f01622dcd0 Binary files /dev/null and b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Magenta.png differ diff --git a/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Red.png b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Red.png new file mode 100644 index 0000000000..1e308d7f1f Binary files /dev/null and b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Red.png differ diff --git a/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Yellow.png b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Yellow.png new file mode 100644 index 0000000000..b123f358c0 Binary files /dev/null and b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Yellow.png differ diff --git a/interface/resources/qml/hifi/simplifiedUI/settingsApp/general/General.qml b/interface/resources/qml/hifi/simplifiedUI/settingsApp/general/General.qml index 7a98849b95..6a59816af8 100644 --- a/interface/resources/qml/hifi/simplifiedUI/settingsApp/general/General.qml +++ b/interface/resources/qml/hifi/simplifiedUI/settingsApp/general/General.qml @@ -225,9 +225,9 @@ Flickable { SimplifiedControls.RadioButton { id: firstPerson text: "First Person View" - checked: Camera.mode === "first person" + checked: Camera.mode === "first person look at" onClicked: { - Camera.mode = "first person" + Camera.mode = "first person look at" } } @@ -254,7 +254,7 @@ Flickable { target: Camera onModeUpdated: { - if (Camera.mode === "first person") { + if (Camera.mode === "first person look at") { firstPerson.checked = true } else if (Camera.mode === "look at") { thirdPerson.checked = true diff --git a/interface/resources/qml/hifi/simplifiedUI/topBar/SimplifiedTopBar.qml b/interface/resources/qml/hifi/simplifiedUI/topBar/SimplifiedTopBar.qml index c92da7e6e5..65a5eb0c80 100644 --- a/interface/resources/qml/hifi/simplifiedUI/topBar/SimplifiedTopBar.qml +++ b/interface/resources/qml/hifi/simplifiedUI/topBar/SimplifiedTopBar.qml @@ -54,8 +54,8 @@ Rectangle { if ((MyAvatar.skeletonModelURL.indexOf("defaultAvatar") > -1 || MyAvatar.skeletonModelURL.indexOf("fst") === -1) && topBarInventoryModel.count > 0) { - Settings.setValue("simplifiedUI/alreadyAutoSelectedAvatar", true); - MyAvatar.useFullAvatarURL = topBarInventoryModel.get(0).download_url; + Settings.setValue("simplifiedUI/alreadyAutoSelectedAvatarFromInventory", true); + MyAvatar.useFullAvatarURL(topBarInventoryModel.get(0).download_url); } } } @@ -71,7 +71,7 @@ Rectangle { if (isLoggedIn) { Commerce.getWalletStatus(); } else { - // Show some error to the user + // Show some error to the user in the UI? } } @@ -113,12 +113,68 @@ Rectangle { topBarInventoryModel.getNextPage(); } else { inventoryFullyReceived = true; + var scriptExecutionCount = Settings.getValue("simplifiedUI/SUIScriptExecutionCount"); + var currentAvatarURL = MyAvatar.skeletonModelURL; + var currentAvatarURLContainsDefaultAvatar = currentAvatarURL.indexOf("defaultAvatar") > -1; + var currentAvatarURLContainsFST = currentAvatarURL.indexOf("fst") > -1; + var currentAvatarURLContainsSimplifiedAvatar = currentAvatarURL.indexOf("simplifiedAvatar") > -1; + var alreadyAutoSelectedAvatarFromInventory = Settings.getValue("simplifiedUI/alreadyAutoSelectedAvatarFromInventory", false); + var userHasValidAvatarInInventory = topBarInventoryModel.count > 0 && + topBarInventoryModel.get(0).download_url.indexOf(".fst") > -1; + var simplifiedAvatarPrefix = "https://content.highfidelity.com/Experiences/Releases/simplifiedUI/simplifiedFTUE/avatars/simplifiedAvatar_"; + var simplifiedAvatarColors = ["Blue", "Cyan", "Green", "Magenta", "Red"]; + var simplifiedAvatarSuffix = "/avatar.fst"; - // If we have an avatar in our inventory AND we haven't already auto-selected an avatar... - if ((!Settings.getValue("simplifiedUI/alreadyAutoSelectedAvatar", false) || - MyAvatar.skeletonModelURL.indexOf("defaultAvatar") > -1 || MyAvatar.skeletonModelURL.indexOf("fst") === -1) && topBarInventoryModel.count > 0) { - Settings.setValue("simplifiedUI/alreadyAutoSelectedAvatar", true); - MyAvatar.skeletonModelURL = topBarInventoryModel.get(0).download_url; + // Use `Settings.setValue("simplifiedUI/debugFTUE", 0);` to turn off FTUE Debug Mode. + // Use `Settings.setValue("simplifiedUI/debugFTUE", 1);` to debug FTUE Screen 1. + // Use `Settings.setValue("simplifiedUI/debugFTUE", 2);` to debug FTUE Screen 2. + // Use `Settings.setValue("simplifiedUI/debugFTUE", 3);` to debug FTUE Screen 3. + // Use `Settings.setValue("simplifiedUI/debugFTUE", 4);` to force the UI to show what would happen if the user had an empty Inventory. + + var debugFTUE = Settings.getValue("simplifiedUI/debugFTUE", 0); + if (debugFTUE === 1 || debugFTUE === 2) { + scriptExecutionCount = 1; + currentAvatarURLContainsDefaultAvatar = true; + if (debugFTUE === 1) { + userHasValidAvatarInInventory = false; + currentAvatarURLContainsSimplifiedAvatar = false; + } + } else if (debugFTUE === 3) { + scriptExecutionCount = 2; + currentAvatarURLContainsDefaultAvatar = false; + currentAvatarURLContainsSimplifiedAvatar = true; + } + + // If we have never auto-selected and the user is still using a default avatar or if the current avatar is not valid (fst), or if + // the current avatar is the old default (Woody), use top avatar from inventory or one of the new defaults. + + // If the current avatar URL is invalid, OR the user is using the "default avatar" (Woody)... + if (!currentAvatarURLContainsFST || currentAvatarURLContainsDefaultAvatar) { + // If the user has a valid avatar in their inventory... + if (userHasValidAvatarInInventory) { + // ...use the first avatar in the user's inventory. + MyAvatar.useFullAvatarURL(topBarInventoryModel.get(0).download_url); + Settings.setValue("simplifiedUI/alreadyAutoSelectedAvatarFromInventory", true); + // Else if the user isn't wearing a "Simplified Avatar" + } else if (!currentAvatarURLContainsSimplifiedAvatar) { + // ...assign to the user a new "Simplified Avatar" (i.e. a simple avatar of random color) + var avatarColor = simplifiedAvatarColors[Math.floor(Math.random() * simplifiedAvatarColors.length)]; + var simplifiedAvatarModelURL = simplifiedAvatarPrefix + avatarColor + simplifiedAvatarSuffix; + MyAvatar.useFullAvatarURL(simplifiedAvatarModelURL); + currentAvatarURLContainsSimplifiedAvatar = true; + } + } + + if (scriptExecutionCount === 1) { + sendToScript({ + "source": "SimplifiedTopBar.qml", + "method": "displayInitialLaunchWindow" + }); + } else if (scriptExecutionCount === 2 && currentAvatarURLContainsSimplifiedAvatar) { + sendToScript({ + "source": "SimplifiedTopBar.qml", + "method": "displaySecondLaunchWindow" + }); } } } @@ -384,6 +440,7 @@ Rectangle { placeholderTextColor: "#8E8E8E" font.pixelSize: 14 placeholderText: width - leftPadding - rightPadding < goToTextFieldMetrics.width ? shortPlaceholderText : longPlaceholderText + blankPlaceholderTextOnFocus: false clip: true selectByMouse: true autoScroll: true @@ -555,7 +612,7 @@ Rectangle { } - function updatePreviewUrl() { + function updatePreviewUrl() { var previewUrl = ""; var downloadUrl = ""; for (var i = 0; i < topBarInventoryModel.count; ++i) { @@ -569,6 +626,8 @@ Rectangle { return; } } + + avatarButtonImage.source = "../images/defaultAvatar.svg"; } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b4a37519a6..994c9c7fd3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -252,6 +252,7 @@ #if defined(Q_OS_WIN) #include +#include // On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU // FIXME seems to be broken. @@ -532,6 +533,11 @@ bool isDomainURL(QUrl url) { } #ifdef Q_OS_WIN +static const UINT UWM_IDENTIFY_INSTANCES = + RegisterWindowMessage("UWM_IDENTIFY_INSTANCES_{8AB82783-B74A-4258-955B-8188C22AA0D6}_" + qgetenv("USERNAME")); +static const UINT UWM_SHOW_APPLICATION = + RegisterWindowMessage("UWM_SHOW_APPLICATION_{71123FD6-3DA8-4DC1-9C27-8A12A6250CBA}_" + qgetenv("USERNAME")); + class MyNativeEventFilter : public QAbstractNativeEventFilter { public: static MyNativeEventFilter& getInstance() { @@ -712,6 +718,7 @@ private: static const QString STATE_IN_HMD = "InHMD"; static const QString STATE_CAMERA_FULL_SCREEN_MIRROR = "CameraFSM"; static const QString STATE_CAMERA_FIRST_PERSON = "CameraFirstPerson"; +static const QString STATE_CAMERA_FIRST_PERSON_LOOK_AT = "CameraFirstPersonLookat"; static const QString STATE_CAMERA_THIRD_PERSON = "CameraThirdPerson"; static const QString STATE_CAMERA_ENTITY = "CameraEntity"; static const QString STATE_CAMERA_INDEPENDENT = "CameraIndependent"; @@ -933,7 +940,8 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { DependencyManager::set(); DependencyManager::set(); controller::StateController::setStateVariables({ { STATE_IN_HMD, STATE_CAMERA_FULL_SCREEN_MIRROR, - STATE_CAMERA_FIRST_PERSON, STATE_CAMERA_THIRD_PERSON, STATE_CAMERA_ENTITY, STATE_CAMERA_INDEPENDENT, STATE_CAMERA_LOOK_AT, STATE_CAMERA_SELFIE, + STATE_CAMERA_FIRST_PERSON, STATE_CAMERA_FIRST_PERSON_LOOK_AT, STATE_CAMERA_THIRD_PERSON, + STATE_CAMERA_ENTITY, STATE_CAMERA_INDEPENDENT, STATE_CAMERA_LOOK_AT, STATE_CAMERA_SELFIE, STATE_SNAP_TURN, STATE_ADVANCED_MOVEMENT_CONTROLS, STATE_GROUNDED, STATE_NAV_FOCUSED, STATE_PLATFORM_WINDOWS, STATE_PLATFORM_MAC, STATE_PLATFORM_ANDROID, STATE_LEFT_HAND_DOMINANT, STATE_RIGHT_HAND_DOMINANT, STATE_STRAFE_ENABLED } }); DependencyManager::set(); @@ -968,6 +976,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { QObject::connect(PlatformHelper::instance(), &PlatformHelper::systemWillWake, [] { QMetaObject::invokeMethod(DependencyManager::get().data(), "noteAwakening", Qt::QueuedConnection); + QMetaObject::invokeMethod(DependencyManager::get().data(), "noteAwakening", Qt::QueuedConnection); }); @@ -1880,6 +1889,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo _applicationStateDevice->setInputVariant(STATE_CAMERA_FIRST_PERSON, []() -> float { return qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON ? 1 : 0; }); + _applicationStateDevice->setInputVariant(STATE_CAMERA_FIRST_PERSON_LOOK_AT, []() -> float { + return qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON_LOOK_AT ? 1 : 0; + }); _applicationStateDevice->setInputVariant(STATE_CAMERA_THIRD_PERSON, []() -> float { return qApp->getCamera().getMode() == CAMERA_MODE_THIRD_PERSON ? 1 : 0; }); @@ -1989,7 +2001,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo settingsTimer->start(); }, QThread::LowestPriority); - if (Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson)) { + if (Menu::getInstance()->isOptionChecked(MenuOption::FirstPersonLookAt)) { getMyAvatar()->setBoomLength(MyAvatar::ZOOM_MIN); // So that camera doesn't auto-switch to third person. } @@ -2952,13 +2964,29 @@ Application::~Application() { qInstallMessageHandler(LogHandler::verboseMessageHandler); #ifdef Q_OS_MAC + // 10/16/2019 - Disabling this call. This causes known crashes (A), and it is not + // fully understood whether it might cause other unknown crashes (B). + // + // (A) Although we try to shutdown the ScriptEngine threads in onAboutToQuit, there is + // currently no guarantee that they have stopped. Waiting on them to stop has so far appeared to + // never return on Mac, causing the application to hang on shutdown. Because ScriptEngines + // may still be running, they may end up receiving events that are triggered from this processEvents call, + // and then try to access resources that are no longer available at this point in time. + // If the ScriptEngine threads were fully destroyed before getting here, this would + // not be an issue. + // + // (B) It seems likely that a bunch of potential event handlers are dependent on Application + // and other common dependencies to be available and not destroyed or in the middle of being + // destroyed. + + // Clear the event queue before application is totally destructed. // This will drain the messasge queue of pending "deleteLaters" queued up // during shutdown of the script engines. // We do this here because there is a possiblty that [NSApplication terminate:] // will be called during processEvents which will invoke all static destructors. // We want to postpone this utill the last possible moment. - QCoreApplication::processEvents(); + //QCoreApplication::processEvents(); #endif } @@ -3604,16 +3632,18 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { // Using the latter will cause the camera to wobble with idle animations, // or with changes from the face tracker CameraMode mode = _myCamera.getMode(); - if (mode == CAMERA_MODE_FIRST_PERSON) { + if (mode == CAMERA_MODE_FIRST_PERSON || mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT) { _thirdPersonHMDCameraBoomValid= false; if (isHMDMode()) { mat4 camMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); _myCamera.setPosition(extractTranslation(camMat)); _myCamera.setOrientation(glmExtractRotation(camMat)); - } - else { + } else if (mode == CAMERA_MODE_FIRST_PERSON) { _myCamera.setPosition(myAvatar->getDefaultEyePosition()); _myCamera.setOrientation(myAvatar->getMyHead()->getHeadOrientation()); + } else { + _myCamera.setPosition(myAvatar->getCameraEyesPosition(deltaTime)); + _myCamera.setOrientation(myAvatar->getLookAtRotation()); } } else if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE) { if (isHMDMode()) { @@ -3647,9 +3677,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 +3707,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 +3728,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()); } @@ -4403,7 +4431,7 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_1: { Menu* menu = Menu::getInstance(); - menu->triggerOption(MenuOption::FirstPerson); + menu->triggerOption(MenuOption::FirstPersonLookAt); break; } case Qt::Key_2: { @@ -4960,7 +4988,7 @@ extern "C" { CCHAR NumberOfProcessors; }; - NTSYSCALLAPI NTSTATUS NTAPI NtQuerySystemInformation( + NTSYSCALLAPI LONG NTAPI NtQuerySystemInformation( _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, _Out_writes_bytes_opt_(SystemInformationLength) PVOID SystemInformation, _In_ ULONG SystemInformationLength, @@ -4969,12 +4997,12 @@ extern "C" { } template -NTSTATUS NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS SystemInformationClass, T& t) { +LONG NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS SystemInformationClass, T& t) { return NtQuerySystemInformation(SystemInformationClass, &t, (ULONG)sizeof(T), nullptr); } template -NTSTATUS NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS SystemInformationClass, std::vector& t) { +LONG NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS SystemInformationClass, std::vector& t) { return NtQuerySystemInformation(SystemInformationClass, t.data(), (ULONG)(sizeof(T) * t.size()), nullptr); } @@ -5488,7 +5516,7 @@ void Application::loadSettings() { isFirstPerson = menu->isOptionChecked(MenuOption::FirstPersonHMD); } else { // if HMD is not active, only use first person if the menu option is checked - isFirstPerson = menu->isOptionChecked(MenuOption::FirstPerson); + isFirstPerson = menu->isOptionChecked(MenuOption::FirstPersonLookAt); } } } @@ -5503,9 +5531,9 @@ void Application::loadSettings() { // finish initializing the camera, based on everything we checked above. Third person camera will be used if no settings // dictated that we should be in first person - Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, isFirstPerson); + Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPersonLookAt, isFirstPerson); Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !isFirstPerson); - _myCamera.setMode((isFirstPerson) ? CAMERA_MODE_FIRST_PERSON : CAMERA_MODE_LOOK_AT); + _myCamera.setMode((isFirstPerson) ? CAMERA_MODE_FIRST_PERSON_LOOK_AT : CAMERA_MODE_LOOK_AT); cameraMenuChanged(); auto inputs = pluginManager->getInputPlugins(); @@ -5669,7 +5697,7 @@ void Application::pauseUntilLoginDetermined() { menu->getMenu("Developer")->setVisible(false); } _previousCameraMode = _myCamera.getMode(); - _myCamera.setMode(CAMERA_MODE_FIRST_PERSON); + _myCamera.setMode(CAMERA_MODE_FIRST_PERSON_LOOK_AT); cameraModeChanged(); // disconnect domain handler. @@ -5858,11 +5886,11 @@ void Application::cycleCamera() { if (menu->isOptionChecked(MenuOption::FullscreenMirror)) { menu->setIsOptionChecked(MenuOption::FullscreenMirror, false); - menu->setIsOptionChecked(MenuOption::FirstPerson, true); + menu->setIsOptionChecked(MenuOption::FirstPersonLookAt, true); - } else if (menu->isOptionChecked(MenuOption::FirstPerson)) { + } else if (menu->isOptionChecked(MenuOption::FirstPersonLookAt)) { - menu->setIsOptionChecked(MenuOption::FirstPerson, false); + menu->setIsOptionChecked(MenuOption::FirstPersonLookAt, false); menu->setIsOptionChecked(MenuOption::LookAtCamera, true); } else if (menu->isOptionChecked(MenuOption::LookAtCamera)) { @@ -5881,8 +5909,8 @@ void Application::cycleCamera() { void Application::cameraModeChanged() { switch (_myCamera.getMode()) { - case CAMERA_MODE_FIRST_PERSON: - Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, true); + case CAMERA_MODE_FIRST_PERSON_LOOK_AT: + Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPersonLookAt, true); break; case CAMERA_MODE_LOOK_AT: Menu::getInstance()->setIsOptionChecked(MenuOption::LookAtCamera, true); @@ -5902,12 +5930,12 @@ void Application::changeViewAsNeeded(float boomLength) { // This is called when the boom length has changed bool boomLengthGreaterThanMinimum = (boomLength > MyAvatar::ZOOM_MIN); - if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON && boomLengthGreaterThanMinimum) { - Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, false); + if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON_LOOK_AT && boomLengthGreaterThanMinimum) { + Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPersonLookAt, false); Menu::getInstance()->setIsOptionChecked(MenuOption::LookAtCamera, true); cameraMenuChanged(); } else if (_myCamera.getMode() == CAMERA_MODE_LOOK_AT && !boomLengthGreaterThanMinimum) { - Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, true); + Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPersonLookAt, true); Menu::getInstance()->setIsOptionChecked(MenuOption::LookAtCamera, false); cameraMenuChanged(); } @@ -5915,9 +5943,9 @@ void Application::changeViewAsNeeded(float boomLength) { void Application::cameraMenuChanged() { auto menu = Menu::getInstance(); - if (menu->isOptionChecked(MenuOption::FirstPerson)) { - if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON) { - _myCamera.setMode(CAMERA_MODE_FIRST_PERSON); + if (menu->isOptionChecked(MenuOption::FirstPersonLookAt)) { + if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON_LOOK_AT) { + _myCamera.setMode(CAMERA_MODE_FIRST_PERSON_LOOK_AT); getMyAvatar()->setBoomLength(MyAvatar::ZOOM_MIN); } } else if (menu->isOptionChecked(MenuOption::LookAtCamera)) { @@ -9018,7 +9046,7 @@ void Application::setDisplayPlugin(DisplayPluginPointer newDisplayPlugin) { } if (isHmd && menu->isOptionChecked(MenuOption::FirstPersonHMD)) { - menu->setIsOptionChecked(MenuOption::FirstPerson, true); + menu->setIsOptionChecked(MenuOption::FirstPersonLookAt, true); cameraMenuChanged(); } diff --git a/interface/src/Application.h b/interface/src/Application.h index af2348d1e9..e3334d12d6 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -91,12 +91,6 @@ namespace controller { class StateController; } -#ifdef Q_OS_WIN -static const UINT UWM_IDENTIFY_INSTANCES = - RegisterWindowMessage("UWM_IDENTIFY_INSTANCES_{8AB82783-B74A-4258-955B-8188C22AA0D6}_" + qgetenv("USERNAME")); -static const UINT UWM_SHOW_APPLICATION = - RegisterWindowMessage("UWM_SHOW_APPLICATION_{71123FD6-3DA8-4DC1-9C27-8A12A6250CBA}_" + qgetenv("USERNAME")); -#endif static const QString RUNNING_MARKER_FILENAME = "Interface.running"; static const QString SCRIPTS_SWITCH = "scripts"; diff --git a/interface/src/FancyCamera.h b/interface/src/FancyCamera.h index 0cfe147138..f7be71e053 100644 --- a/interface/src/FancyCamera.h +++ b/interface/src/FancyCamera.h @@ -20,7 +20,7 @@ class FancyCamera : public Camera { /**jsdoc * The Camera API provides access to the "camera" that defines your view in desktop and HMD display modes. - * The High Fidelity camera has axes x = right, y = up, -z = forward. + * The High Fidelity camera has axes x = right, y = up, -z = forward. * * @namespace Camera * diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 89fec3c812..5a3bb36ca2 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -172,7 +172,7 @@ Menu::Menu() { // View > First Person auto firstPersonAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash( - viewMenu, MenuOption::FirstPerson, 0, + viewMenu, MenuOption::FirstPersonLookAt, 0, true, qApp, SLOT(cameraMenuChanged()))); firstPersonAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup)); @@ -640,11 +640,6 @@ Menu::Menu() { } #endif - - // Developer >> Tests >>> - MenuWrapper* testMenu = developerMenu->addMenu("Tests"); - addActionToQMenuAndActionHash(testMenu, MenuOption::RunClientScriptTests, 0, dialogsManager.data(), SLOT(showTestingResults())); - // Developer > Timing >>> MenuWrapper* timingMenu = developerMenu->addMenu("Timing"); MenuWrapper* perfTimerMenu = timingMenu->addMenu("Performance Timer"); @@ -829,7 +824,7 @@ Menu::Menu() { // Help > Release Notes action = addActionToQMenuAndActionHash(helpMenu, "Release Notes"); connect(action, &QAction::triggered, qApp, [] { - QDesktopServices::openUrl(QUrl("http://steamcommunity.com/games/390540/announcements/")); + QDesktopServices::openUrl(QUrl("https://docs.highfidelity.com/release-notes.html")); }); // Help > Report a Bug! diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 0ba1159052..4a2a97b168 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -111,7 +111,8 @@ namespace MenuOption { const QString ExpandSimulationTiming = "Expand /simulation"; const QString ExpandPhysicsTiming = "Expand /physics"; const QString ExpandUpdateTiming = "Expand /update"; - const QString FirstPerson = "First Person"; + const QString FirstPerson = "First Person Legacy"; + const QString FirstPersonLookAt = "First Person"; const QString FirstPersonHMD = "Enter First Person Mode in HMD"; const QString FivePointCalibration = "5 Point Calibration"; const QString FixGaze = "Fix Gaze (no saccade)"; @@ -178,7 +179,6 @@ namespace MenuOption { const QString ResetAvatarSize = "Reset Avatar Size"; const QString ResetSensors = "Reset Sensors"; const QString RunningScripts = "Running Scripts..."; - const QString RunClientScriptTests = "Run Client Script Tests"; const QString RunTimingTests = "Run Timing Tests"; const QString ScriptedMotorControl = "Enable Scripted Motor Control"; const QString ShowTrackedObjects = "Show Tracked Objects"; diff --git a/interface/src/SpeechRecognizer.h b/interface/src/SpeechRecognizer.h index 7e2acdb8ac..ae35f63a35 100644 --- a/interface/src/SpeechRecognizer.h +++ b/interface/src/SpeechRecognizer.h @@ -23,6 +23,10 @@ #include /**jsdoc + * The SpeechRecognizer API provides facilities to recognize voice commands. + *

Speech recognition is enabled or disabled via the Developer > Scripting > Enable Speech Control API menu item or + * the {@link SpeechRecognizer.setEnabled} method.

+ * * @namespace SpeechRecognizer * * @hifi-interface @@ -40,36 +44,86 @@ public: public slots: /**jsdoc + * Enables or disables speech recognition. * @function SpeechRecognizer.setEnabled - * @param {boolean} enabled + * @param {boolean} enabled - true to enable speech recognition, false to disable. */ void setEnabled(bool enabled); /**jsdoc + * Adds a voice command to the speech recognizer. * @function SpeechRecognizer.addCommand - * @param {string} command + * @param {string} command - The voice command to recognize. */ void addCommand(const QString& command); /**jsdoc + * Removes a voice command from the speech recognizer. * @function SpeechRecognizer.removeCommand - * @param {string} command + * @param {string} command - The voice command to stop recognizing. */ void removeCommand(const QString& command); signals: /**jsdoc + * Triggered when a voice command has been recognized. * @function SpeechRecognizer.commandRecognized - * @param {string} command + * @param {string} command - The voice command recognized. * @returns {Signal} + * @example Turn your avatar upon voice command. + * var TURN_LEFT = "turn left"; + * var TURN_RIGHT = "turn right"; + * var TURN_RATE = 0.5; + * var TURN_DURATION = 1000; // ms + * var turnRate = 0; + * + * function getTurnRate() { + * return turnRate; + * } + * + * var MAPPING_NAME = "com.highfidelity.controllers.example.speechRecognizer"; + * var mapping = Controller.newMapping(MAPPING_NAME); + * + * mapping.from(getTurnRate).to(Controller.Actions.Yaw); + * Controller.enableMapping(MAPPING_NAME); + * + * function onCommandRecognized(command) { + * print("Speech command: " + command); + * switch (command) { + * case TURN_LEFT: + * turnRate = -TURN_RATE; + * break; + * case TURN_RIGHT: + * turnRate = TURN_RATE; + * break; + * } + * Script.setTimeout(function () { + * turnRate = 0; + * }, TURN_DURATION); + * } + * + * SpeechRecognizer.addCommand(TURN_LEFT); + * SpeechRecognizer.addCommand(TURN_RIGHT); + * SpeechRecognizer.commandRecognized.connect(onCommandRecognized); + * + * Script.scriptEnding.connect(function () { + * Controller.disableMapping(MAPPING_NAME); + * SpeechRecognizer.removeCommand(TURN_LEFT); + * SpeechRecognizer.removeCommand(TURN_RIGHT); + * }); */ void commandRecognized(const QString& command); /**jsdoc + * Triggered when speech recognition is enabled or disabled. * @function SpeechRecognizer.enabledUpdated - * @param {boolean} enabled + * @param {boolean} enabled - true if speech recognition is enabled, false if it is disabled. * @returns {Signal} + * @example Report when speech recognition is enabled or disabled. + * SpeechRecognizer.enabledUpdated.connect(function (enabled) { + * print("Speech recognition: " + (enabled ? "enabled" : "disabled")); + * }); */ void enabledUpdated(bool enabled); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 54785933c9..6e0bfab69b 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -152,7 +152,7 @@ static int triggerReactionNameToIndex(const QString& reactionName) { } static int beginEndReactionNameToIndex(const QString& reactionName) { - assert(NUM_AVATAR_BEGIN_END_REACTIONS == TRIGGER_REACTION_NAMES.size()); + assert(NUM_AVATAR_BEGIN_END_REACTIONS == BEGIN_END_REACTION_NAMES.size()); return BEGIN_END_REACTION_NAMES.indexOf(reactionName); } @@ -958,7 +958,8 @@ 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_LOOK_AT || mode == CAMERA_MODE_FIRST_PERSON || + mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE) { if (!_pointAtActive || !_isPointTargetValid) { updateHeadLookAt(deltaTime); } else { @@ -2175,10 +2176,10 @@ static float lookAtCostFunction(const glm::vec3& myForward, const glm::vec3& myP } void MyAvatar::computeMyLookAtTarget(const AvatarHash& hash) { - glm::vec3 myForward = getHead()->getFinalOrientationInWorldFrame() * IDENTITY_FORWARD; + glm::vec3 myForward = _lookAtYaw * IDENTITY_FORWARD; glm::vec3 myPosition = getHead()->getEyePosition(); CameraMode mode = qApp->getCamera().getMode(); - if (mode == CAMERA_MODE_FIRST_PERSON) { + if (mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT || mode == CAMERA_MODE_FIRST_PERSON) { myPosition = qApp->getCamera().getPosition(); } @@ -2553,7 +2554,7 @@ void MyAvatar::clearWornAvatarEntities() { } /**jsdoc - * Information about an avatar entity. + *

Information about an avatar entity.

* * * @@ -2718,7 +2719,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 (!qApp->isHMDMode() && (mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE)) { motorRotation = getLookAtRotation(); } else { motorRotation = getMyHead()->getHeadOrientation(); @@ -3398,7 +3400,8 @@ bool MyAvatar::cameraInsideHead(const glm::vec3& cameraPosition) const { bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const { bool defaultMode = renderArgs->_renderMode == RenderArgs::DEFAULT_RENDER_MODE; - bool firstPerson = qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON; + bool firstPerson = qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON_LOOK_AT || + qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON; bool overrideAnim = _skeletonModel ? _skeletonModel->getRig().isPlayingOverrideAnimation() : false; bool insideHead = cameraInsideHead(renderArgs->getViewFrustum().getPosition()); return !defaultMode || (!firstPerson && !insideHead) || (overrideAnim && !insideHead); @@ -3442,8 +3445,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_LOOK_AT || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE); + bool smoothCameraYaw = computeLookAt && mode != CAMERA_MODE_FIRST_PERSON_LOOK_AT; + 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 +3477,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; } @@ -3557,18 +3562,22 @@ void MyAvatar::updateOrientation(float deltaTime) { if (faceForward || _shouldTurnToFaceCamera) { const float REORIENT_FORWARD_BLEND = 0.25f; const float REORIENT_TURN_BLEND = 0.03f; - const float DIAGONAL_TURN_BLEND = 0.02f; + const float DIAGONAL_TURN_BLEND = 0.1f; float blend = (_shouldTurnToFaceCamera ? REORIENT_TURN_BLEND : REORIENT_FORWARD_BLEND) * timeScale; if (blend > 1.0f) { 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_LOOK_AT ? 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_LOOK_AT) { + blend = 1.0f; + } } setWorldOrientation(glm::slerp(getWorldOrientation(), faceRotation, blend)); } else if (isRotatingWhileSeated) { @@ -3630,20 +3639,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_LOOK_AT) { + limitAngle = glm::sin(glm::radians(90.0f - FIRST_PERSON_TRIGGER_REORIENT_ANGLE)); + triggerAngle = limitAngle; + } + float reorientAngle = mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT ? 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 +3685,22 @@ void MyAvatar::updateOrientation(float deltaTime) { _lookAtCameraTarget = targetPoint; } _headLookAtActive = true; + const float FIRST_PERSON_RECENTER_SECONDS = 15.0f; + if (mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT) { + 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 +3773,9 @@ 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_LOOK_AT || mode == CAMERA_MODE_SELFIE) && + zSpeed != 0.0f && xSpeed != 0.0f && !isFlying()){ direction = (zSpeed * forward); } @@ -4351,7 +4390,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 +5439,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_LOOK_AT || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE; result = headFollowsCamera ? qApp->getCamera().getOrientation() : getHead()->getFinalOrientationInWorldFrame(); break; } @@ -6768,6 +6808,72 @@ 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; +} + +glm::vec3 MyAvatar::getCameraEyesPosition(float deltaTime) { + glm::vec3 defaultEyesPosition = getLookAtPivotPoint(); + if (isFlying()) { + return defaultEyesPosition; + } + glm::vec3 avatarFrontVector = getWorldOrientation() * Vectors::FRONT; + glm::vec3 avatarUpVector = getWorldOrientation() * Vectors::UP; + // Compute the offset between the default and real eye positions. + glm::vec3 defaultEyesToEyesVector = getHead()->getEyePosition() - defaultEyesPosition; + float FRONT_OFFSET_IDLE_MULTIPLIER = 2.5f; + float FRONT_OFFSET_JUMP_MULTIPLIER = 1.5f; + float frontOffset = FRONT_OFFSET_IDLE_MULTIPLIER * glm::length(defaultEyesPosition - getDefaultEyePosition()); + + // Looking down will aproximate move the camera forward to meet the real eye position + float mixAlpha = glm::dot(_lookAtPitch * Vectors::FRONT, -avatarUpVector); + bool isLanding = false; + // When jumping the camera should follow the real eye on the Y coordenate + float upOffset = 0.0f; + if (isJumping() || _characterController.getState() == CharacterController::State::Takeoff) { + upOffset = glm::dot(defaultEyesToEyesVector, avatarUpVector); + frontOffset = glm::dot(defaultEyesToEyesVector, avatarFrontVector) * FRONT_OFFSET_JUMP_MULTIPLIER; + mixAlpha = 1.0f; + _landingAfterJumpTime = 0.0f; + } else { + // Limit the range effect from 45 to 0 degrees + // between the front camera and the down vectors + const float HEAD_OFFSET_RANGE_IN_DEGREES = 45.0f; + const float HEAD_OFFSET_RANGE_OUT_DEGREES = 0.0f; + float rangeIn = glm::cos(glm::radians(HEAD_OFFSET_RANGE_IN_DEGREES)); + float rangeOut = glm::cos(glm::radians(HEAD_OFFSET_RANGE_OUT_DEGREES)); + mixAlpha = mixAlpha < rangeIn ? 0.0f : (mixAlpha - rangeIn) / (rangeOut - rangeIn); + const float WAIT_TO_LAND_TIME = 1.0f; + if (_landingAfterJumpTime < WAIT_TO_LAND_TIME) { + _landingAfterJumpTime += deltaTime; + isLanding = true; + } + } + const float FPS = 60.0f; + float timeScale = deltaTime * FPS; + frontOffset = frontOffset < 0.0f ? 0.0f : mixAlpha * frontOffset; + glm::vec3 cameraOffset = upOffset * Vectors::UP + frontOffset * Vectors::FRONT; + const float JUMPING_TAU = 0.1f; + const float NO_JUMP_TAU = 0.3f; + const float LANDING_TAU = 0.05f; + float tau = NO_JUMP_TAU; + if (isJumping()) { + tau = JUMPING_TAU; + } else if (isLanding) { + tau = LANDING_TAU; + } + _cameraEyesOffset = _cameraEyesOffset + (cameraOffset - _cameraEyesOffset) * min(1.0f, tau * timeScale); + glm::vec3 estimatedCameraPosition = defaultEyesPosition + getWorldOrientation() * _cameraEyesOffset; + return estimatedCameraPosition; +} + +bool MyAvatar::isJumping() { + return (_characterController.getState() == CharacterController::State::InAir || + _characterController.getState() == CharacterController::State::Takeoff) && !isFlying(); +} + bool MyAvatar::setPointAt(const glm::vec3& pointAtTarget) { if (QThread::currentThread() != thread()) { bool result = false; @@ -6794,3 +6900,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..081fd00d5b 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -170,10 +170,12 @@ class MyAvatar : public Avatar { * collision. It can be a mono or stereo 16-bit WAV file running at either 24kHz or 48kHz. The latter is down-sampled * by the audio mixer, so all audio effectively plays back at a 24khz. 48kHz RAW files are also supported. * @property {number} audioListenerMode=0 - Specifies the listening position when hearing spatialized audio. Must be one - * of the following property values:
- * Myavatar.audioListenerModeHead
- * Myavatar.audioListenerModeCamera
- * Myavatar.audioListenerModeCustom + * of the following property values: + *
    + *
  • MyAvatar.audioListenerModeHead
  • + *
  • MyAvatar.audioListenerModeCamera
  • + *
  • MyAvatar.audioListenerModeCustom
  • + *
* @property {number} audioListenerModeHead=0 - The audio listening position is at the avatar's head. Read-only. * @property {number} audioListenerModeCamera=1 - The audio listening position is at the camera. Read-only. * @property {number} audioListenerModeCustom=2 - The audio listening position is at a the position specified by set by the @@ -182,8 +184,8 @@ class MyAvatar : public Avatar { * property value is audioListenerModeCustom. * @property {Quat} customListenOrientation=Quat.IDENTITY - The listening orientation used when the * audioListenerMode property value is audioListenerModeCustom. - * @property {boolean} hasScriptedBlendshapes=false - true to transmit blendshapes over the network.
- * Note: Currently doesn't work. Use {@link MyAvatar.setForceFaceTrackerConnected} instead. + * @property {boolean} hasScriptedBlendshapes=false - true to transmit blendshapes over the network. + *

Note: Currently doesn't work. Use {@link MyAvatar.setForceFaceTrackerConnected} instead.

* @property {boolean} hasProceduralBlinkFaceMovement=true - true if procedural blinking is turned on. * @property {boolean} hasProceduralEyeFaceMovement=true - true if procedural eye movement is turned on. * @property {boolean} hasAudioEnabledFaceMovement=true - true to move the mouth blendshapes with voice audio @@ -241,8 +243,8 @@ class MyAvatar : public Avatar { * @property {boolean} useAdvancedMovementControls - Returns and sets the value of the Interface setting, Settings > * Controls > Walking. Note: Setting the value has no effect unless Interface is restarted. * @property {boolean} showPlayArea - Returns and sets the value of the Interface setting, Settings > Controls > Show room - * boundaries while teleporting.
- * Note: Setting the value has no effect unless Interface is restarted. + * boundaries while teleporting. + *

Note: Setting the value has no effect unless Interface is restarted.

* * @property {number} yawSpeed=75 - The mouse X sensitivity value in Settings > General. Read-only. * @property {number} pitchSpeed=50 - The mouse Y sensitivity value in Settings > General. Read-only. @@ -272,11 +274,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. @@ -490,9 +493,10 @@ public: * - * + * the avatar to sit on the floor when the user sits on the floor.

+ *

Note: Experimental.

* *
PropertyTypeDescription
2AutoInterface detects when the user is standing or seated in the real world. * Avatar leaning is disabled when the user is sitting (i.e., avatar always recenters), and avatar leaning is enabled * when the user is standing (i.e., avatar leans, then if leans too far it recenters).
3DisableHMDLeanBoth avatar leaning and recentering are disabled regardless of + *
3DisableHMDLean

Both avatar leaning and recentering are disabled regardless of * what the user is doing in the real world and no matter what their avatar is doing in the virtual world. Enables - * the avatar to sit on the floor when the user sits on the floor.
Note: Experimental.

* @typedef {number} MyAvatar.SitStandModelType @@ -781,7 +785,7 @@ public: * additional properties specified when adding the different handlers.

*

A handler may change a value from animStateDictionaryIn or add different values in the * animStateDictionaryOut returned. Any property values set in animStateDictionaryOut will - * override those of the internal animation machinery. * @function MyAvatar.addAnimationStateHandler * @param {function} handler - The animation state handler function to add. * @param {Array|null} propertiesList - The list of {@link MyAvatar.AnimStateDictionary|AnimStateDictionary} @@ -1890,6 +1894,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 +1918,9 @@ public: void debugDrawPose(controller::Action action, const char* channelName, float size); bool getIsJointOverridden(int jointIndex) const; + glm::vec3 getLookAtPivotPoint(); + glm::vec3 getCameraEyesPosition(float deltaTime); + bool isJumping(); public slots: @@ -2663,6 +2678,7 @@ private: bool _shouldTurnToFaceCamera { false }; bool _scriptControlsHeadLookAt { false }; float _scriptHeadControlTimer { 0.0f }; + float _firstPersonSteadyHeadTimer { 0.0f }; bool _pointAtActive { false }; bool _isPointTargetValid { true }; @@ -2959,6 +2975,9 @@ private: // used to prevent character from jumping after endSit is called. bool _endSitKeyPressComplete { false }; + + glm::vec3 _cameraEyesOffset; + float _landingAfterJumpTime { 0.0f }; }; QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 38065c8095..8d92767321 100755 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -315,6 +315,10 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { const float TALKING_TIME_THRESHOLD = 0.75f; params.isTalking = head->getTimeWithoutTalking() <= TALKING_TIME_THRESHOLD; + //pass X and Z input key floats (-1 to 1) to rig + params.inputX = myAvatar->getDriveKey(MyAvatar::TRANSLATE_X); + params.inputZ = myAvatar->getDriveKey(MyAvatar::TRANSLATE_Z); + myAvatar->updateRigControllerParameters(params); _rig.updateFromControllerParameters(params, deltaTime); diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 9af1d07309..5ca4d18a45 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -36,6 +36,7 @@ #include "Profile.h" #ifdef Q_OS_WIN +#include extern "C" { typedef int(__stdcall * CHECKMINSPECPROC) (); } diff --git a/interface/src/raypick/PickScriptingInterface.cpp b/interface/src/raypick/PickScriptingInterface.cpp index ddc1fe376c..3a7b67a8e9 100644 --- a/interface/src/raypick/PickScriptingInterface.cpp +++ b/interface/src/raypick/PickScriptingInterface.cpp @@ -78,17 +78,17 @@ PickFilter getPickFilter(unsigned int filter) { * @property {boolean} [enabled=false] - true if this pick should start enabled, false if it should * start disabled. Disabled picks do not update their pick results. * @property {FilterFlags} [filter=0] - The filter for this pick to use. Construct using {@link Picks} FilterFlags property - * values (e.g., Picks.PICK_DOMAIN_ENTTITIES) combined with | (bitwise OR) operators. + * values (e.g., Picks.PICK_DOMAIN_ENTITIES) combined with | (bitwise OR) operators. * @property {number} [maxDistance=0.0] - The maximum distance at which this pick will intersect. A value of 0.0 * means no maximum. * @property {Uuid} [parentID] - The ID of the parent: an avatar, an entity, or another pick. * @property {number} [parentJointIndex=0] - The joint of the parent to parent to, for example, an avatar joint. - * A value of 0 means no joint.
- * Used only if parentID is specified. + * A value of 0 means no joint. + *

Used only if parentID is specified.

* @property {string} [joint] - "Mouse" parents the pick to the mouse; "Avatar" parents the pick to * the user's avatar head; a joint name parents to the joint in the user's avatar; otherwise, the pick is "static", not - * parented to anything.
- * Used only if parentID is not specified. + * parented to anything. + *

Used only if parentID is not specified.

* @property {Vec3} [position=Vec3.ZERO] - The offset of the ray origin from its parent if parented, otherwise the ray origin * in world coordinates. * @property {Vec3} [posOffset] - Synonym for position. @@ -164,7 +164,7 @@ std::shared_ptr PickScriptingInterface::buildRayPick(const QVariantMa * @property {boolean} [enabled=false] - true if this pick should start enabled, false if it should * start disabled. Disabled picks do not update their pick results. * @property {number} [filter=0] - The filter for this pick to use. Construct using {@link Picks} FilterFlags property - * values (e.g., Picks.PICK_DOMAIN_ENTTITIES) combined with | (bitwise OR) operators. + * values (e.g., Picks.PICK_DOMAIN_ENTITIES) combined with | (bitwise OR) operators. *

Note: Stylus picks do not intersect avatars or the HUD.

* @property {number} [maxDistance=0.0] - The maximum distance at which this pick will intersect. A value of 0.0 * means no maximum. @@ -212,17 +212,17 @@ std::shared_ptr PickScriptingInterface::buildStylusPick(const QVarian * @property {boolean} [enabled=false] - true if this pick should start enabled, false if it should * start disabled. Disabled picks do not update their pick results. * @property {number} [filter=0] - The filter for this pick to use. Construct using {@link Picks} FilterFlags property - * values (e.g., Picks.PICK_DOMAIN_ENTTITIES) combined with | (bitwise OR) operators. + * values (e.g., Picks.PICK_DOMAIN_ENTITIES) combined with | (bitwise OR) operators. * @property {number} [maxDistance=0.0] - The maximum distance at which this pick will intersect. A value of 0.0 * means no maximum. * @property {Uuid} [parentID] - The ID of the parent: an avatar, an entity, or another pick. * @property {number} [parentJointIndex=0] - The joint of the parent to parent to, for example, an avatar joint. - * A value of 0 means no joint.
- * Used only if parentID is specified. + * A value of 0 means no joint. + *

Used only if parentID is specified.

* @property {string} [joint] - "Mouse" parents the pick to the mouse; "Avatar" parents the pick to * the user's avatar head; a joint name parents to the joint in the user's avatar; otherwise, the pick is "static", not * parented to anything. - * Used only if parentID is not specified. + *

Used only if parentID is not specified.

* @property {Vec3} [position=Vec3.ZERO] - The offset of the parabola origin from its parent if parented, otherwise the * parabola origin in world coordinates. * @property {Vec3} [posOffset] - Synonym for position. @@ -321,18 +321,18 @@ std::shared_ptr PickScriptingInterface::buildParabolaPick(const QVari * @property {boolean} [enabled=false] - true if this pick should start enabled, false if it should * start disabled. Disabled picks do not update their pick results. * @property {FilterFlags} [filter=0] - The filter for this pick to use. Construct using {@link Picks} FilterFlags property - * values (e.g., Picks.PICK_DOMAIN_ENTTITIES) combined with | (bitwise OR) operators. + * values (e.g., Picks.PICK_DOMAIN_ENTITIES) combined with | (bitwise OR) operators. *

Note: Collision picks do not intersect the HUD.

* @property {number} [maxDistance=0.0] - The maximum distance at which this pick will intersect. A value of 0.0 * means no maximum. * @property {Uuid} [parentID] - The ID of the parent: an avatar, an entity, or another pick. * @property {number} [parentJointIndex=0] - The joint of the parent to parent to, for example, an avatar joint. - * A value of 0 means no joint.
- * Used only if parentID is specified. + * A value of 0 means no joint. + *

Used only if parentID is specified.

* @property {string} [joint] - "Mouse" parents the pick to the mouse; "Avatar" parents the pick to * the user's avatar head; a joint name parents to the joint in the user's avatar; otherwise, the pick is "static", not - * parented to anything.
- * Used only if parentID is not specified. + * parented to anything. + *

Used only if parentID is not specified.

* @property {boolean} [scaleWithParent=true] - true to scale the pick's dimensions and threshold according to the * scale of the parent. * @@ -344,9 +344,11 @@ std::shared_ptr PickScriptingInterface::buildParabolaPick(const QVari * the collision region. The depth is in world coordinates but scales with the parent if defined. * @property {CollisionMask} [collisionGroup=8] - The type of objects the collision region collides as. Objects whose collision * masks overlap with the region's collision group are considered to be colliding with the region. - * @property {PickType} pickType - The type of pick when getting these properties from {@link Picks.getPickProperties} or {@link Picks.getPickScriptParameters}. A collision pick's type is {@link PickType.Collision}. - * @property {Vec3} baseScale - Returned from {@link Picks.getPickProperties} when the pick has a parent with varying scale (usually an avatar or an entity). - * Its value is the original scale of the parent at the moment the pick was created, and is used to rescale the pick, and/or the pointer which owns this pick, if any. + * @property {PickType} pickType - The type of pick when getting these properties from {@link Picks.getPickProperties} or + * {@link Picks.getPickScriptParameters}. A collision pick's type is {@link PickType.Collision}. + * @property {Vec3} baseScale - Returned from {@link Picks.getPickProperties} when the pick has a parent with varying scale + * (usually an avatar or an entity). Its value is the original scale of the parent at the moment the pick was created, and + * is used to rescale the pick, and/or the pointer which owns this pick, if any. */ std::shared_ptr PickScriptingInterface::buildCollisionPick(const QVariantMap& propMap) { bool enabled = false; diff --git a/interface/src/raypick/PickScriptingInterface.h b/interface/src/raypick/PickScriptingInterface.h index 72b28bbc47..e26b91b9a2 100644 --- a/interface/src/raypick/PickScriptingInterface.h +++ b/interface/src/raypick/PickScriptingInterface.h @@ -287,7 +287,7 @@ public slots: /**jsdoc * @function Picks.PICK_ENTITIES * @deprecated This function is deprecated and will be removed. Use the Picks.PICK_DOMAIN_ENTITIES | - * Picks.PICK_AVATAR_ENTITIES properties expression instead. + * Picks.PICK_AVATAR_ENTITIES properties expression instead. * @returns {number} */ static constexpr unsigned int PICK_ENTITIES() { return PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES); } diff --git a/interface/src/raypick/PointerScriptingInterface.h b/interface/src/raypick/PointerScriptingInterface.h index 0eed8b4741..2d2f3f6dae 100644 --- a/interface/src/raypick/PointerScriptingInterface.h +++ b/interface/src/raypick/PointerScriptingInterface.h @@ -41,7 +41,7 @@ public: * @property {Controller.Standard|Controller.Actions|function} action - The controller output or function that triggers the * events on the entity or overlay. If a function, it must return a number >= 1.0 to start the action * and < 1.0 to terminate the action. - * @property {string} button - Which button to trigger. + * @property {string} button - Which button to trigger: *
    *
  • "Primary", "Secondary", and "Tertiary" cause {@link Entities} and * {@link Overlays} mouse pointer events. Other button names also cause mouse events but the button diff --git a/interface/src/scripting/AccountServicesScriptingInterface.h b/interface/src/scripting/AccountServicesScriptingInterface.h index b188b4e63b..3ad637d0e6 100644 --- a/interface/src/scripting/AccountServicesScriptingInterface.h +++ b/interface/src/scripting/AccountServicesScriptingInterface.h @@ -50,12 +50,14 @@ class AccountServicesScriptingInterface : public QObject { * "Unknown user". Read-only. * @property {boolean} loggedIn - true if the user is logged in, otherwise false. * Read-only. - * @property {string} findableBy - The user's visibility to other users:
    - * "none" - user appears offline.
    - * "friends" - user is visible only to friends.
    - * "connections" - user is visible to friends and connections.
    - * "all" - user is visible to everyone. - * @property {string} metaverseServerURL - The metaverse server that the user is authenticated against when logged in + * @property {string} findableBy - The user's visibility to other users: + *
      + *
    • "none" — user appears offline.
    • + *
    • "friends" — user is visible only to friends.
    • + *
    • "connections" — user is visible to friends and connections.
    • + *
    • "all" — user is visible to everyone.
    • + *
    + * @property {string} metaverseServerURL - The metaverse server that the user is authenticated against when logged in * — typically "https://metaverse.highfidelity.com". Read-only. */ @@ -160,11 +162,13 @@ signals: /**jsdoc * Triggered when the user's visibility to others changes. * @function AccountServices.findableByChanged - * @param {string} findableBy - The user's visibility to other people:
    - * "none" - user appears offline.
    - * "friends" - user is visible only to friends.
    - * "connections" - user is visible to friends and connections.
    - * "all" - user is visible to everyone. + * @param {string} findableBy - The user's visibility to other people: + *
      + *
    • "none" — user appears offline.
    • + *
    • "friends" — user is visible only to friends.
    • + *
    • "connections" — user is visible to friends and connections.
    • + *
    • "all" — user is visible to everyone.
    • + *
    * @returns {Signal} * @example Report when your visiblity changes. * AccountServices.findableByChanged.connect(function (findableBy) { diff --git a/interface/src/scripting/ControllerScriptingInterface.h b/interface/src/scripting/ControllerScriptingInterface.h index 4fb631463e..f65791335d 100644 --- a/interface/src/scripting/ControllerScriptingInterface.h +++ b/interface/src/scripting/ControllerScriptingInterface.h @@ -168,7 +168,6 @@ class ScriptEngine; * startFarTrigger
    continueFarTrigger
    stopFarTrigger * These methods are called when a user is more than 0.3m away from the entity, the entity is triggerable, and the * user starts, continues, or stops squeezing the trigger. - * * A light switch that can be toggled on and off from a distance. * * @@ -217,25 +216,25 @@ class ScriptEngine; * * @property {Controller.Actions} Actions - Predefined actions on Interface and the user's avatar. These can be used as end * points in a {@link RouteObject} mapping. A synonym for Controller.Hardware.Actions. - * Read-only.

    - * Default mappings are provided from the Controller.Hardware.Keyboard and Controller.Standard to - * actions in + * Read-only. + *

    Default mappings are provided from the Controller.Hardware.Keyboard and Controller.Standard + * to actions in * * keyboardMouse.json and * - * standard.json, respectively. + * standard.json, 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 Actions or functions in a - * {@link RouteObject} mapping. Additionally, hardware-specific controller outputs can be mapped to Standard - * controller outputs. Read-only. + * {@link RouteObject} mapping. Additionally, hardware-specific controller outputs can be mapped to + * Controller.Standard controller outputs. Read-only. * * @property {Controller.Standard} Standard - Standard controller outputs that can be mapped to Actions or - * functions in a {@link RouteObject} mapping. Read-only.

    - * Each hardware device has a mapping from its outputs to Controller.Standard items, specified in a JSON file. + * functions in a {@link RouteObject} mapping. Read-only. + *

    Each hardware device has a mapping from its outputs to Controller.Standard items, specified in a JSON file. * For example, * leapmotion.json and - * vive.json. + * vive.json.

    */ /// handles scripting of input controller commands from JS diff --git a/interface/src/scripting/DesktopScriptingInterface.cpp b/interface/src/scripting/DesktopScriptingInterface.cpp index 874b3fa42d..ae4af48cd6 100644 --- a/interface/src/scripting/DesktopScriptingInterface.cpp +++ b/interface/src/scripting/DesktopScriptingInterface.cpp @@ -31,10 +31,10 @@ * @property {InteractiveWindow.DockArea} RIGHT - Dock to the right edge of the Interface window. */ /**jsdoc - * A docking location of an InteractiveWindow. + *

    A docking location of an InteractiveWindow.

    * * - * + * * * * @@ -52,6 +52,23 @@ static const QVariantMap DOCK_AREA { { "RIGHT", DockArea::RIGHT } }; +/**jsdoc + * The possible "relative position anchors" of an InteractiveWindow. Used when defining the `relativePosition` property of an `InteractiveWindow`. + * @typedef {object} InteractiveWindow.RelativePositionAnchors + * @property {InteractiveWindow.RelativePositionAnchor} NO_ANCHOR - Specifies that the position of the `InteractiveWindow` will not be relative to any part of the Interface window. + * @property {InteractiveWindow.RelativePositionAnchor} TOP_LEFT - Specifies that the `relativePosition` of the `InteractiveWindow` will be offset from the top left of the Interface window. + * @property {InteractiveWindow.RelativePositionAnchor} TOP_RIGHT - Specifies that the `relativePosition` of the `InteractiveWindow` will be offset from the top right of the Interface window. + * @property {InteractiveWindow.RelativePositionAnchor} BOTTOM_RIGHT - Specifies that the `relativePosition` of the `InteractiveWindow` will be offset from the bottom right of the Interface window. + * @property {InteractiveWindow.RelativePositionAnchor} BOTTOM_LEFT - Specifies that the `relativePosition` of the `InteractiveWindow` will be offset from the bottom left of the Interface window. + */ +static const QVariantMap RELATIVE_POSITION_ANCHOR { + { "NO_ANCHOR", RelativePositionAnchor::NO_ANCHOR }, + { "TOP_LEFT", RelativePositionAnchor::TOP_LEFT }, + { "TOP_RIGHT", RelativePositionAnchor::TOP_RIGHT }, + { "BOTTOM_RIGHT", RelativePositionAnchor::BOTTOM_RIGHT }, + { "BOTTOM_LEFT", RelativePositionAnchor::BOTTOM_LEFT } +}; + DesktopScriptingInterface::DesktopScriptingInterface(QObject* parent, bool restricted) : QObject(parent), _restricted(restricted) { } @@ -73,10 +90,10 @@ int DesktopScriptingInterface::getHeight() { * own separate window. */ /**jsdoc - * A display mode for an InteractiveWindow. + *

    A display mode for an InteractiveWindow.

    *
    ValueName

    Description
    ValueNameDescription
    0TOPDock to the top edge of the Interface window.
    * - * + * * * * * * - * + * * * * @@ -1674,11 +1673,11 @@ QVector Overlays::findOverlays(const glm::vec3& center, float radius) { * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from * pulseMin to pulseMax, then pulseMax to pulseMin in one period. * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise * used.) * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise * used.) * @@ -1692,7 +1691,7 @@ QVector Overlays::findOverlays(const glm::vec3& center, float radius) { * parentID set, otherwise the same value as rotation. Synonym: localOrientation. * @property {boolean} isSolid=false - true if the overlay is rendered as a solid, false if it is * rendered as a wire frame. - * Synonyms: solid, isFilled, and filled. + * Synonyms: solid, isFilled, and filled. * Antonyms: isWire and wire. * @property {boolean} ignorePickIntersection=false - true if {@link Picks} ignore the overlay, false * if they don't. @@ -1761,17 +1760,17 @@ QVector Overlays::findOverlays(const glm::vec3& center, float radius) { * @property {Color} color=255,255,255 - The color of the overlay text. Synonym: textColor. * @property {number} alpha=0.7 - The opacity of the overlay text, 0.01.0. *

    Currently not used; use textAlpha instead.

    - * + * @comment CURRENTLY BROKEN * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from * pulseMin to pulseMax, then pulseMax to pulseMin in one period. * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise * used.) * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise * used.) * @@ -1811,11 +1810,11 @@ QVector Overlays::findOverlays(const glm::vec3& center, float radius) { * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from * pulseMin to pulseMax, then pulseMax to pulseMin in one period. * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise * used.) * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise * used.) * @@ -1857,11 +1856,11 @@ QVector Overlays::findOverlays(const glm::vec3& center, float radius) { * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from * pulseMin to pulseMax, then pulseMax to pulseMin in one period. * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise * used.) * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise * used.) * @@ -1916,41 +1915,45 @@ QVector Overlays::findOverlays(const glm::vec3& center, float radius) { * HUD surface. * @property {boolean} grabbable=false - true if the overlay can be grabbed, false if it can't be. * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. + *

    Currently doesn't work.

    + * @comment CURRENTLY BROKEN * @property {number} parentJointIndex=65535 - Integer value specifying the joint of the entity or avatar that the entity is * parented to if parentID is set. Use 65535 or -1 to parent to the parent's position and orientation rather * than a joint. * * @property {Uuid} endParentID=null - The avatar, entity, or overlay that the end point of the line is parented to. *

    Currently doesn't work.

    - * + * @comment CURRENTLY BROKEN * @property {number} endParentJointIndex=65535 - Integer value specifying the skeleton joint that the end point of the line is * attached to if parentID is an avatar skeleton. A value of 65535 means "no joint". *

    Currently doesn't work.

    - * + * @comment CURRENTLY BROKEN * @property {Vec3} start - The start point of the line. Synonyms: startPoint and p1. - *

    If parentID is set, use localStart to set the local position of the start point.

    - * + *

    Note: If parentID is set, use localStart to set the local position of the + * start point.

    * @property {Vec3} end - The end point of the line. Synonyms: endPoint and p2. - *

    If parentID is set, use localEnd to set the local position of the end point.

    - * + *

    Note: If parentID is set, use localEnd to set the local position of the + * end point.

    * @property {Vec3} localStart - The local position of the overlay relative to its parent if the overlay has a * parentID set, otherwise the same value as start. - * + *

    Currently doesn't work.

    + * @comment CURRENTLY BROKEN * @property {Vec3} localEnd - The local position of the overlay relative to its parent if the overlay has a * endParentID set, otherwise the same value as end. - * + *

    Currently doesn't work.

    + * @comment CURRENTLY BROKEN * @property {number} length - The length of the line, in meters. This can be set after creating a line with start and end * points. *

    Currently doesn't work.

    - * + * @comment CURRENTLY BROKEN * @property {number} glow=0 - If glow > 0, the line is rendered with a glow. * @property {number} lineWidth=0.02 - Width of the line, in meters. *

    You can set this property's value but currently cannot retrieve its value. Use the strokeWidths - * property to retrieve its value instead.

    + * property to retrieve its value instead.

    */ /**jsdoc @@ -1967,11 +1970,11 @@ QVector Overlays::findOverlays(const glm::vec3& center, float radius) { * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from * pulseMin to pulseMax, then pulseMax to pulseMin in one period. * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise * used.) * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise * used.) * @@ -2010,11 +2013,11 @@ QVector Overlays::findOverlays(const glm::vec3& center, float radius) { * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from * pulseMin to pulseMax, then pulseMax to pulseMin in one period. * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise * used.) * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise * used.) * @@ -2029,7 +2032,7 @@ QVector Overlays::findOverlays(const glm::vec3& center, float radius) { * parentID set, otherwise the same value as rotation. Synonym: localOrientation. * @property {boolean} isSolid=false - true if the overlay is rendered as a solid, false if it is * rendered as a wire frame. - * Synonyms: solid
    , isFilled, and filled. + * Synonyms: solid, isFilled, and filled. * Antonyms: isWire and wire. * @property {boolean} ignorePickIntersection=false - true if {@link Picks} ignore the overlay, false * if they don't. 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/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index 24acdcf20a..7f201f94e7 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -55,6 +55,8 @@ public: float getFrame() const { return _frame; } void loadURL(const QString& url); + AnimBlendType getBlendType() const { return _blendType; }; + protected: virtual void setCurrentFrameInternal(float frame) override; diff --git a/libraries/animation/src/AnimInverseKinematics.h b/libraries/animation/src/AnimInverseKinematics.h index 8d3f898e67..bcad7867cc 100644 --- a/libraries/animation/src/AnimInverseKinematics.h +++ b/libraries/animation/src/AnimInverseKinematics.h @@ -63,7 +63,7 @@ public: *

    Specifies the initial conditions of the IK solver.

    *
    ValueName

    Description
    ValueNameDescription
    0VIRTUALThe window is displayed inside Interface: in the desktop window in @@ -99,6 +116,10 @@ QVariantMap DesktopScriptingInterface::getDockArea() { return DOCK_AREA; } +QVariantMap DesktopScriptingInterface::getRelativePositionAnchor() { + return RELATIVE_POSITION_ANCHOR; +} + void DesktopScriptingInterface::setHUDAlpha(float alpha) { qApp->getApplicationCompositor().setAlpha(alpha); } diff --git a/interface/src/scripting/DesktopScriptingInterface.h b/interface/src/scripting/DesktopScriptingInterface.h index e562a32543..c25f382891 100644 --- a/interface/src/scripting/DesktopScriptingInterface.h +++ b/interface/src/scripting/DesktopScriptingInterface.h @@ -42,6 +42,9 @@ * @property {InteractiveWindow.DockAreas} DockArea - The possible docking locations of an {@link InteractiveWindow}: top, * bottom, left, or right of the Interface window. * Read-only. + * @property {InteractiveWindow.RelativePositionAnchors} RelativePositionAnchor - The possible "relative position anchors" for an {@link InteractiveWindow}: top left, + * top right, bottom right, or bottom left of the Interface window. + * Read-only. */ class DesktopScriptingInterface : public QObject, public Dependency { Q_OBJECT @@ -50,6 +53,7 @@ class DesktopScriptingInterface : public QObject, public Dependency { Q_PROPERTY(QVariantMap PresentationMode READ getPresentationMode CONSTANT FINAL) Q_PROPERTY(QVariantMap DockArea READ getDockArea CONSTANT FINAL) + Q_PROPERTY(QVariantMap RelativePositionAnchor READ getRelativePositionAnchor CONSTANT FINAL) Q_PROPERTY(int ALWAYS_ON_TOP READ flagAlwaysOnTop CONSTANT FINAL) Q_PROPERTY(int CLOSE_BUTTON_HIDES READ flagCloseButtonHides CONSTANT FINAL) @@ -106,7 +110,7 @@ private: Q_INVOKABLE InteractiveWindowPointer createWindowOnThread(const QString& sourceUrl, const QVariantMap& properties, QThread* targetThread); static QVariantMap getDockArea(); - + static QVariantMap getRelativePositionAnchor(); Q_INVOKABLE static QVariantMap getPresentationMode(); const bool _restricted; }; diff --git a/interface/src/scripting/MenuScriptingInterface.h b/interface/src/scripting/MenuScriptingInterface.h index 6c2634b1de..110df8dd06 100644 --- a/interface/src/scripting/MenuScriptingInterface.h +++ b/interface/src/scripting/MenuScriptingInterface.h @@ -24,7 +24,8 @@ class MenuItemProperties; *

    Groupings

    * *

    A "grouping" provides a way to group a set of menus or menu items together so that they can all be set visible or invisible - * as a group.

    There is currently only one available group: "Developer". This grouping can be toggled in the + * as a group.

    + *

    There is currently only one available group: "Developer". This grouping can be toggled in the * "Settings" menu.

    *

    If a menu item doesn't belong to a group, it is always displayed.

    * diff --git a/interface/src/scripting/RenderScriptingInterface.cpp b/interface/src/scripting/RenderScriptingInterface.cpp index f23dc598a9..56e9a93377 100644 --- a/interface/src/scripting/RenderScriptingInterface.cpp +++ b/interface/src/scripting/RenderScriptingInterface.cpp @@ -82,11 +82,18 @@ void RenderScriptingInterface::forceShadowsEnabled(bool enabled) { _shadowsEnabled = (enabled); _shadowsEnabledSetting.set(enabled); - auto lightingModelConfig = qApp->getRenderEngine()->getConfiguration()->getConfig("RenderMainView.LightingModel"); + auto renderConfig = qApp->getRenderEngine()->getConfiguration(); + assert(renderConfig); + auto lightingModelConfig = renderConfig->getConfig("RenderMainView.LightingModel"); if (lightingModelConfig) { Menu::getInstance()->setIsOptionChecked(MenuOption::Shadows, enabled); lightingModelConfig->setShadow(enabled); } + auto secondaryLightingModelConfig = renderConfig->getConfig("RenderSecondView.LightingModel"); + if (secondaryLightingModelConfig) { + Menu::getInstance()->setIsOptionChecked(MenuOption::Shadows, enabled); + secondaryLightingModelConfig->setShadow(enabled); + } }); } diff --git a/interface/src/scripting/SelectionScriptingInterface.cpp b/interface/src/scripting/SelectionScriptingInterface.cpp index 5856188868..d2147ac5cc 100644 --- a/interface/src/scripting/SelectionScriptingInterface.cpp +++ b/interface/src/scripting/SelectionScriptingInterface.cpp @@ -44,7 +44,7 @@ SelectionScriptingInterface::SelectionScriptingInterface() { } /**jsdoc - * The type of a specific item in a selection list. + *

    The type of a specific item in a selection list.

    * * * diff --git a/interface/src/scripting/WalletScriptingInterface.h b/interface/src/scripting/WalletScriptingInterface.h index 3ef9c7953a..e9535ceb4e 100644 --- a/interface/src/scripting/WalletScriptingInterface.h +++ b/interface/src/scripting/WalletScriptingInterface.h @@ -42,7 +42,7 @@ public: * @property {WalletScriptingInterface.WalletStatus} walletStatus - The status of the user's wallet. Read-only. * @property {boolean} limitedCommerce - true if Interface is running in limited commerce mode. In limited commerce * mode, certain Interface functionalities are disabled, e.g., users can't buy items that are not free from the Marketplace. - * The Oculus Store version of Interface runs in limited commerce mode. Read-only. + * The Oculus Store and Steam versions of Interface run in limited commerce mode. Read-only. */ class WalletScriptingInterface : public QObject, public Dependency { Q_OBJECT @@ -73,9 +73,9 @@ public: /**jsdoc * Check that a certified avatar entity is owned by the avatar whose entity it is. The result of the check is provided via * the {@link WalletScriptingInterface.ownershipVerificationSuccess|ownershipVerificationSuccess} and - * {@link WalletScriptingInterface.ownershipVerificationFailed|ownershipVerificationFailed} signals.
    - * Warning: Neither of these signals are triggered if the entity is not an avatar entity or is not - * certified. + * {@link WalletScriptingInterface.ownershipVerificationFailed|ownershipVerificationFailed} signals. + *

    Warning: Neither of these signals are triggered if the entity is not an avatar entity or is not + * certified.

    * @function WalletScriptingInterface.proveAvatarEntityOwnershipVerification * @param {Uuid} entityID - The avatar entity's ID. * @example diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 6207b22cb8..341b012c2d 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -326,10 +326,10 @@ public slots: * full resolution is used (window dimensions in desktop mode; HMD display dimensions in HMD mode), otherwise one of the * dimensions is adjusted in order to match the aspect ratio. * @param {string} [filename=""] - If a filename is not provided, the image is saved as "hifi-snap-by-<user - * name>-on-YYYY-MM-DD_HH-MM-SS".
    - * Still images are saved in JPEG or PNG format according to the extension provided — ".jpg", + * name>-on-YYYY-MM-DD_HH-MM-SS". + *

    Still images are saved in JPEG or PNG format according to the extension provided — ".jpg", * ".jpeg", or ".png" — or if not provided then in JPEG format with an extension of - * ".jpg". Animated images are saved in GIF format. + * ".jpg". Animated images are saved in GIF format.

    * * @example * function onStillSnapshotTaken(path, notify) { @@ -365,10 +365,10 @@ public slots: * @param {boolean} [notify=true] - This value is passed on through the {@link Window.stillSnapshotTaken|stillSnapshotTaken} * signal. * @param {string} [filename=""] - If a filename is not provided, the image is saved as "hifi-snap-by-<user - * name>-on-YYYY-MM-DD_HH-MM-SS".
    - * Images are saved in JPEG or PNG format according to the extension provided — ".jpg", + * name>-on-YYYY-MM-DD_HH-MM-SS". + *

    Images are saved in JPEG or PNG format according to the extension provided — ".jpg", * ".jpeg", or ".png" — or if not provided then in JPEG format with an extension of - * ".jpg". + * ".jpg".

    */ void takeSecondaryCameraSnapshot(const bool& notify = true, const QString& filename = QString()); @@ -384,10 +384,10 @@ public slots: * @param {boolean} [notify=true] - This value is passed on through the {@link Window.stillSnapshotTaken|stillSnapshotTaken} * signal. * @param {string} [filename=""] - If a filename is not provided, the image is saved as "hifi-snap-by-<user - * name>-on-YYYY-MM-DD_HH-MM-SS".
    - * Images are saved in JPEG or PNG format according to the extension provided — ".jpg", + * name>-on-YYYY-MM-DD_HH-MM-SS". + *

    Images are saved in JPEG or PNG format according to the extension provided — ".jpg", * ".jpeg", or ".png" — or if not provided then in JPEG format with an extension of - * ".jpg". + * ".jpg".

    */ void takeSecondaryCamera360Snapshot(const glm::vec3& cameraPosition, const bool& cubemapOutputFormat = false, const bool& notify = true, const QString& filename = QString()); @@ -515,13 +515,13 @@ public slots: /**jsdoc * Opens a URL in the Interface window or other application, depending on the URL's scheme. The following schemes are - * supported:
    + * supported: *
      *
    • hifi: Navigate to the URL in Interface.
    • *
    • hifiapp: Open a system app in Interface.
    • *
    - * Other schemes will either be handled by the OS (e.g. http, https, or mailto) or - * will display a dialog asking the user to confirm that they want to try to open the URL. + *

    Other schemes will either be handled by the OS (e.g. http, https, or mailto) or + * will display a dialog asking the user to confirm that they want to try to open the URL.

    * @function Window.openUrl * @param {string} url - The URL to open. */ @@ -623,8 +623,8 @@ private slots: signals: /**jsdoc - * Triggered when you change the domain you're visiting. Warning: Is not emitted if you go to a domain - * that isn't running. + * Triggered when you change the domain you're visiting. + *

    Warning: 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} diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index e34b82e0a1..0a655de5e5 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -186,15 +186,6 @@ void DialogsManager::setAddressBarVisible(bool addressBarVisible) { emit addressBarShown(_addressBarVisible); } -void DialogsManager::showTestingResults() { - if (!_testingDialog) { - _testingDialog = new TestingDialog(qApp->getWindow()); - connect(_testingDialog, SIGNAL(closed()), _testingDialog, SLOT(deleteLater())); - } - _testingDialog->show(); - _testingDialog->raise(); -} - void DialogsManager::showDomainConnectionDialog() { // if the dialog already exists we delete it so the connection data is refreshed if (_domainConnectionDialog) { diff --git a/interface/src/ui/DialogsManager.h b/interface/src/ui/DialogsManager.h index b11264444b..949c86c240 100644 --- a/interface/src/ui/DialogsManager.h +++ b/interface/src/ui/DialogsManager.h @@ -53,7 +53,6 @@ public slots: void lodTools(); void hmdTools(bool showTools); void showDomainConnectionDialog(); - void showTestingResults(); void toggleAddressBar(); // Application Update diff --git a/interface/src/ui/InteractiveWindow.cpp b/interface/src/ui/InteractiveWindow.cpp index e63c392a47..6cc26e2409 100644 --- a/interface/src/ui/InteractiveWindow.cpp +++ b/interface/src/ui/InteractiveWindow.cpp @@ -39,6 +39,9 @@ static const char* const ADDITIONAL_FLAGS_PROPERTY = "additionalFlags"; static const char* const OVERRIDE_FLAGS_PROPERTY = "overrideFlags"; static const char* const SOURCE_PROPERTY = "source"; static const char* const TITLE_PROPERTY = "title"; +static const char* const RELATIVE_POSITION_ANCHOR_PROPERTY = "relativePositionAnchor"; +static const char* const RELATIVE_POSITION_PROPERTY = "relativePosition"; +static const char* const IS_FULL_SCREEN_WINDOW = "isFullScreenWindow"; static const char* const POSITION_PROPERTY = "position"; static const char* const INTERACTIVE_WINDOW_POSITION_PROPERTY = "interactiveWindowPosition"; static const char* const SIZE_PROPERTY = "size"; @@ -112,6 +115,15 @@ void InteractiveWindow::forwardKeyReleaseEvent(int key, int modifiers) { QCoreApplication::postEvent(QCoreApplication::instance(), event); } +void InteractiveWindow::onMainWindowGeometryChanged(QRect geometry) { + // This handler is only connected `if (_isFullScreenWindow || _relativePositionAnchor != RelativePositionAnchor::NONE)`. + if (_isFullScreenWindow) { + repositionAndResizeFullScreenWindow(); + } else if (_relativePositionAnchor != RelativePositionAnchor::NO_ANCHOR) { + setPositionUsingRelativePositionAndAnchor(geometry); + } +} + void InteractiveWindow::emitMainWindowResizeEvent() { emit qApp->getWindow()->windowGeometryChanged(qApp->getWindow()->geometry()); } @@ -184,22 +196,32 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap */ if (nativeWindowInfo.contains(DOCK_AREA_PROPERTY)) { DockArea dockedArea = (DockArea) nativeWindowInfo[DOCK_AREA_PROPERTY].toInt(); + int tempWidth = 0; + int tempHeight = 0; switch (dockedArea) { case DockArea::TOP: dockArea = Qt::TopDockWidgetArea; - _dockWidget->setFixedHeight(windowSize.height()); + tempHeight = windowSize.height(); + _dockWidget->setFixedHeight(tempHeight); + qApp->getWindow()->setDockedWidgetRelativePositionOffset(QSize(0, -tempHeight)); break; case DockArea::BOTTOM: dockArea = Qt::BottomDockWidgetArea; - _dockWidget->setFixedHeight(windowSize.height()); + tempHeight = windowSize.height(); + _dockWidget->setFixedHeight(tempHeight); + qApp->getWindow()->setDockedWidgetRelativePositionOffset(QSize(0, tempHeight)); break; case DockArea::LEFT: dockArea = Qt::LeftDockWidgetArea; - _dockWidget->setFixedWidth(windowSize.width()); + tempWidth = windowSize.width(); + _dockWidget->setFixedWidth(tempWidth); + qApp->getWindow()->setDockedWidgetRelativePositionOffset(QSize(-tempWidth, 0)); break; case DockArea::RIGHT: dockArea = Qt::RightDockWidgetArea; - _dockWidget->setFixedWidth(windowSize.width()); + tempWidth = windowSize.width(); + _dockWidget->setFixedWidth(tempWidth); + qApp->getWindow()->setDockedWidgetRelativePositionOffset(QSize(tempWidth, 0)); break; default: @@ -255,6 +277,9 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap if (properties.contains(TITLE_PROPERTY)) { object->setProperty(TITLE_PROPERTY, properties[TITLE_PROPERTY].toString()); } + if (properties.contains(VISIBLE_PROPERTY)) { + object->setProperty(VISIBLE_PROPERTY, properties[INTERACTIVE_WINDOW_VISIBLE_PROPERTY].toBool()); + } if (properties.contains(SIZE_PROPERTY)) { const auto size = vec2FromVariant(properties[SIZE_PROPERTY]); object->setProperty(INTERACTIVE_WINDOW_SIZE_PROPERTY, QSize(size.x, size.y)); @@ -263,8 +288,21 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap const auto position = vec2FromVariant(properties[POSITION_PROPERTY]); object->setProperty(INTERACTIVE_WINDOW_POSITION_PROPERTY, QPointF(position.x, position.y)); } - if (properties.contains(VISIBLE_PROPERTY)) { - object->setProperty(VISIBLE_PROPERTY, properties[INTERACTIVE_WINDOW_VISIBLE_PROPERTY].toBool()); + if (properties.contains(RELATIVE_POSITION_ANCHOR_PROPERTY)) { + _relativePositionAnchor = static_cast(properties[RELATIVE_POSITION_ANCHOR_PROPERTY].toInt()); + } + if (properties.contains(RELATIVE_POSITION_PROPERTY)) { + _relativePosition = vec2FromVariant(properties[RELATIVE_POSITION_PROPERTY]); + setPositionUsingRelativePositionAndAnchor(qApp->getWindow()->geometry()); + } + if (properties.contains(IS_FULL_SCREEN_WINDOW)) { + _isFullScreenWindow = properties[IS_FULL_SCREEN_WINDOW].toBool(); + } + + if (_isFullScreenWindow) { + QRect geo = qApp->getWindow()->geometry(); + object->setProperty(INTERACTIVE_WINDOW_POSITION_PROPERTY, QPointF(geo.x(), geo.y())); + object->setProperty(INTERACTIVE_WINDOW_SIZE_PROPERTY, QSize(geo.width(), geo.height())); } // The qmlToScript method handles the thread-safety of this call. Because the QVariant argument @@ -288,6 +326,10 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap connect(object, SIGNAL(interactiveWindowVisibleChanged()), this, SLOT(parentNativeWindowToMainWindow()), Qt::QueuedConnection); connect(object, SIGNAL(presentationModeChanged()), this, SLOT(parentNativeWindowToMainWindow()), Qt::QueuedConnection); #endif + + if (_isFullScreenWindow || _relativePositionAnchor != RelativePositionAnchor::NO_ANCHOR) { + connect(qApp->getWindow(), &MainWindow::windowGeometryChanged, this, &InteractiveWindow::onMainWindowGeometryChanged, Qt::QueuedConnection); + } QUrl sourceURL{ sourceUrl }; // If the passed URL doesn't correspond to a known scheme, assume it's a local file path @@ -414,6 +456,71 @@ void InteractiveWindow::setPosition(const glm::vec2& position) { } } +RelativePositionAnchor InteractiveWindow::getRelativePositionAnchor() const { + return _relativePositionAnchor; +} + +void InteractiveWindow::setRelativePositionAnchor(const RelativePositionAnchor& relativePositionAnchor) { + _relativePositionAnchor = relativePositionAnchor; + setPositionUsingRelativePositionAndAnchor(qApp->getWindow()->geometry()); +} + +glm::vec2 InteractiveWindow::getRelativePosition() const { + return _relativePosition; +} + +void InteractiveWindow::setRelativePosition(const glm::vec2& relativePosition) { + _relativePosition = relativePosition; + setPositionUsingRelativePositionAndAnchor(qApp->getWindow()->geometry()); +} + +void InteractiveWindow::setPositionUsingRelativePositionAndAnchor(const QRect& mainWindowGeometry) { + RelativePositionAnchor relativePositionAnchor = getRelativePositionAnchor(); + glm::vec2 relativePosition = getRelativePosition(); + + glm::vec2 newPosition; + + switch (relativePositionAnchor) { + case RelativePositionAnchor::TOP_LEFT: + newPosition.x = mainWindowGeometry.x() + relativePosition.x; + newPosition.y = mainWindowGeometry.y() + relativePosition.y; + break; + case RelativePositionAnchor::TOP_RIGHT: + newPosition.x = mainWindowGeometry.x() + mainWindowGeometry.width() - relativePosition.x; + newPosition.y = mainWindowGeometry.y() + relativePosition.y; + break; + case RelativePositionAnchor::BOTTOM_RIGHT: + newPosition.x = mainWindowGeometry.x() + mainWindowGeometry.width() - relativePosition.x; + newPosition.y = mainWindowGeometry.y() + mainWindowGeometry.height() - relativePosition.y; + break; + case RelativePositionAnchor::BOTTOM_LEFT: + newPosition.x = mainWindowGeometry.x() + relativePosition.x; + newPosition.y = mainWindowGeometry.y() + mainWindowGeometry.height() - relativePosition.y; + break; + case RelativePositionAnchor::NO_ANCHOR: + // No-op. + break; + } + + // Make sure we include the dimensions of the docked widget! + QSize dockedWidgetRelativePositionOffset = qApp->getWindow()->getDockedWidgetRelativePositionOffset(); + newPosition.x = newPosition.x + dockedWidgetRelativePositionOffset.width(); + newPosition.y = newPosition.y + dockedWidgetRelativePositionOffset.height(); + + if (_qmlWindowProxy) { + QMetaObject::invokeMethod(_qmlWindowProxy.get(), "writeProperty", Q_ARG(QString, INTERACTIVE_WINDOW_POSITION_PROPERTY), + Q_ARG(QVariant, QPointF(newPosition.x, newPosition.y))); + } + setPosition(newPosition); +} + +void InteractiveWindow::repositionAndResizeFullScreenWindow() { + QRect windowGeometry = qApp->getWindow()->geometry(); + + setPosition(glm::vec2(windowGeometry.x(), windowGeometry.y())); + setSize(glm::vec2(windowGeometry.width(), windowGeometry.height())); +} + glm::vec2 InteractiveWindow::getSize() const { if (!_qmlWindowProxy) { return {}; diff --git a/interface/src/ui/InteractiveWindow.h b/interface/src/ui/InteractiveWindow.h index 70077a27b0..fb10aac444 100644 --- a/interface/src/ui/InteractiveWindow.h +++ b/interface/src/ui/InteractiveWindow.h @@ -56,8 +56,8 @@ namespace InteractiveWindowEnums { Q_NAMESPACE /**jsdoc - * A set of flags controlling InteractiveWindow behavior. The value is constructed by using the - * | (bitwise OR) operator on the individual flag values.
    + *

    A set of flags controlling InteractiveWindow behavior. The value is constructed by using the + * | (bitwise OR) operator on the individual flag values.

    *
    ValueDescription
    Check the ownership of all nearby certified avatar entities.Using the snapshot function and signals.
    * * @@ -89,6 +89,15 @@ namespace InteractiveWindowEnums { RIGHT }; Q_ENUM_NS(DockArea); + + enum RelativePositionAnchor { + NO_ANCHOR, + TOP_LEFT, + TOP_RIGHT, + BOTTOM_RIGHT, + BOTTOM_LEFT + }; + Q_ENUM_NS(RelativePositionAnchor); } using namespace InteractiveWindowEnums; @@ -121,6 +130,8 @@ class InteractiveWindow : public QObject { Q_PROPERTY(QString title READ getTitle WRITE setTitle) Q_PROPERTY(glm::vec2 position READ getPosition WRITE setPosition) + Q_PROPERTY(RelativePositionAnchor relativePositionAnchor READ getRelativePositionAnchor WRITE setRelativePositionAnchor) + Q_PROPERTY(glm::vec2 relativePosition READ getRelativePosition WRITE setRelativePosition) Q_PROPERTY(glm::vec2 size READ getSize WRITE setSize) Q_PROPERTY(bool visible READ isVisible WRITE setVisible) Q_PROPERTY(int presentationMode READ getPresentationMode WRITE setPresentationMode) @@ -136,6 +147,21 @@ private: Q_INVOKABLE glm::vec2 getPosition() const; Q_INVOKABLE void setPosition(const glm::vec2& position); + + RelativePositionAnchor _relativePositionAnchor{ RelativePositionAnchor::NO_ANCHOR }; + Q_INVOKABLE RelativePositionAnchor getRelativePositionAnchor() const; + Q_INVOKABLE void setRelativePositionAnchor(const RelativePositionAnchor& position); + + // This "relative position" is relative to the "relative position anchor" and excludes the window frame. + // This position will ALWAYS include the geometry of a docked widget, if one is present. + glm::vec2 _relativePosition{ 0.0f, 0.0f }; + Q_INVOKABLE glm::vec2 getRelativePosition() const; + Q_INVOKABLE void setRelativePosition(const glm::vec2& position); + + Q_INVOKABLE void setPositionUsingRelativePositionAndAnchor(const QRect& mainWindowGeometry); + + bool _isFullScreenWindow{ false }; + Q_INVOKABLE void repositionAndResizeFullScreenWindow(); Q_INVOKABLE glm::vec2 getSize() const; Q_INVOKABLE void setSize(const glm::vec2& size); @@ -320,6 +346,7 @@ protected slots: void forwardKeyPressEvent(int key, int modifiers); void forwardKeyReleaseEvent(int key, int modifiers); void emitMainWindowResizeEvent(); + void onMainWindowGeometryChanged(QRect geometry); private: std::shared_ptr _qmlWindowProxy; diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 100711d69b..5e43c5df8d 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -1346,49 +1346,48 @@ QVector Overlays::findOverlays(const glm::vec3& center, float radius) { * * * - * + * * * - * + * * * - * + * * * - * + * * * - * + * * * - * + * * * - * + * * * - * + * * * - * + * * * - * + * * * - * + * * * *
    Flag NameValueDescription
    Some text.{@link Overlays.OverlayProperties-Text|OverlayProperties-Text}
    "cube"3DA cube. A "shape" overlay can also be used to create a cube.
    - * Deprecated. - *

    A cube. A "shape" overlay can also be used to create a cube.

    + *

    Deprecated.

    {@link Overlays.OverlayProperties-Cube|OverlayProperties-Cube}
    "sphere"3DA sphere. A "shape" overlay can also be used to create a sphere.
    - * Deprecated.

    A sphere. A "shape" overlay can also be used to create a sphere.

    + *

    Deprecated.

    {@link Overlays.OverlayProperties-Sphere|OverlayProperties-Sphere}
    "shape"3DA geometric shape, such as a cube, sphere, or cylinder.
    - * Deprecated.

    A geometric shape, such as a cube, sphere, or cylinder.

    + *

    Deprecated.

    {@link Overlays.OverlayProperties-Shape|OverlayProperties-Shape}
    "model"3DA model.
    - * Deprecated.

    A model.

    + *

    Deprecated.

    {@link Overlays.OverlayProperties-Model|OverlayProperties-Model}
    "image3d"3DAn image. Synonym: "billboard".
    - * Deprecated.

    An image. Synonym: "billboard".

    + *

    Deprecated.

    {@link Overlays.OverlayProperties-Image3D|OverlayProperties-Image3D}
    "rectangle3d"3DA rectangle.
    - * Deprecated.

    A rectangle.

    + *

    Deprecated.

    {@link Overlays.OverlayProperties-Rectangle3D|OverlayProperties-Rectangle3D}
    "text3d"3DSome text.
    - * Deprecated.

    Some text.

    + *

    Deprecated.

    {@link Overlays.OverlayProperties-Text3D|OverlayProperties-Text3D}
    "web3d"3DWeb content.
    - * Deprecated.

    Web content.

    + *

    Deprecated.

    {@link Overlays.OverlayProperties-Web3D|OverlayProperties-Web3D}
    "line3d"3DA line.
    - * Deprecated.

    A line.

    + *

    Deprecated.

    {@link Overlays.OverlayProperties-Line3D|OverlayProperties-Line3D}
    "grid"3DA grid of lines in a plane.
    - * Deprecated.

    A grid of lines in a plane.

    + *

    Deprecated.

    {@link Overlays.OverlayProperties-Grid|OverlayProperties-Grid}
    "circle3d"3DA circle.
    - * Deprecated.

    A circle.

    + *

    Deprecated.

    {@link Overlays.OverlayProperties-Circle3D|OverlayProperties-Circle3D}
    @@ -1410,17 +1409,17 @@ QVector Overlays::findOverlays(const glm::vec3& center, float radius) { * @see {@link Overlays.OverlayProperties-Rectangle|OverlayProperties-Rectangle} * @see {@link Overlays.OverlayProperties-Image|OverlayProperties-Image} * @see {@link Overlays.OverlayProperties-Text|OverlayProperties-Text} - * @see {@link Overlays.OverlayProperties-Cube|OverlayProperties-Cube} Deprecated. - * @see {@link Overlays.OverlayProperties-Sphere|OverlayProperties-Sphere} Deprecated. - * @see {@link Overlays.OverlayProperties-Shape|OverlayProperties-Shape} Deprecated. - * @see {@link Overlays.OverlayProperties-Model|OverlayProperties-Model} Deprecated. - * @see {@link Overlays.OverlayProperties-Rectangle3D|OverlayProperties-Rectangle3D} Deprecated. - * @see {@link Overlays.OverlayProperties-Image3D|OverlayProperties-Image3D} Deprecated. - * @see {@link Overlays.OverlayProperties-Text3D|OverlayProperties-Text3D} Deprecated. - * @see {@link Overlays.OverlayProperties-Web3D|OverlayProperties-Web3D} Deprecated. - * @see {@link Overlays.OverlayProperties-Line3D|OverlayProperties-Line3D} Deprecated. - * @see {@link Overlays.OverlayProperties-Grid|OverlayProperties-Grid} Deprecated. - * @see {@link Overlays.OverlayProperties-Circle3D|OverlayProperties-Circle3D} Deprecated. + * @see {@link Overlays.OverlayProperties-Cube|OverlayProperties-Cube} — Deprecated. + * @see {@link Overlays.OverlayProperties-Sphere|OverlayProperties-Sphere} — Deprecated. + * @see {@link Overlays.OverlayProperties-Shape|OverlayProperties-Shape} — Deprecated. + * @see {@link Overlays.OverlayProperties-Model|OverlayProperties-Model} — Deprecated. + * @see {@link Overlays.OverlayProperties-Rectangle3D|OverlayProperties-Rectangle3D} — Deprecated. + * @see {@link Overlays.OverlayProperties-Image3D|OverlayProperties-Image3D} — Deprecated. + * @see {@link Overlays.OverlayProperties-Text3D|OverlayProperties-Text3D} — Deprecated. + * @see {@link Overlays.OverlayProperties-Web3D|OverlayProperties-Web3D} — Deprecated. + * @see {@link Overlays.OverlayProperties-Line3D|OverlayProperties-Line3D} — Deprecated. + * @see {@link Overlays.OverlayProperties-Grid|OverlayProperties-Grid} — Deprecated. + * @see {@link Overlays.OverlayProperties-Circle3D|OverlayProperties-Circle3D} — Deprecated. */ /**jsdoc @@ -1502,11 +1501,11 @@ QVector Overlays::findOverlays(const glm::vec3& center, float radius) { * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from * pulseMin to pulseMax, then pulseMax to pulseMin in one period. * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise * used.) * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise * used.) * @@ -1520,7 +1519,7 @@ QVector Overlays::findOverlays(const glm::vec3& center, float radius) { * parentID set, otherwise the same value as rotation. Synonym: localOrientation. * @property {boolean} isSolid=false - true if the overlay is rendered as a solid, false if it is * rendered as a wire frame. - * Synonyms: solid, isFilled, and filled. + * Synonyms: solid, isFilled, and filled. * Antonyms: isWire and wire. * @property {boolean} ignorePickIntersection=false - true if {@link Picks} ignore the overlay, false * if they don't. @@ -1550,11 +1549,11 @@ QVector Overlays::findOverlays(const glm::vec3& center, float radius) { * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from * pulseMin to pulseMax, then pulseMax to pulseMin in one period. * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise * used.) * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise * used.) * @@ -1568,7 +1567,7 @@ QVector Overlays::findOverlays(const glm::vec3& center, float radius) { * parentID set, otherwise the same value as rotation. Synonym: localOrientation. * @property {boolean} isSolid=false - true if the overlay is rendered as a solid, false if it is * rendered as a wire frame. - * Synonyms: solid, isFilled, and filled. + * Synonyms: solid, isFilled, and filled. * Antonyms: isWire and wire. * @property {boolean} ignorePickIntersection=false - true if {@link Picks} ignore the overlay, false * if they don't. @@ -1599,11 +1598,11 @@ QVector Overlays::findOverlays(const glm::vec3& center, float radius) { * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from * pulseMin to pulseMax, then pulseMax to pulseMin in one period. * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise * used.) * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise * used.) * @@ -1617,7 +1616,7 @@ QVector Overlays::findOverlays(const glm::vec3& center, float radius) { * parentID set, otherwise the same value as rotation. Synonym: localOrientation. * @property {boolean} isSolid=false - true if the overlay is rendered as a solid, false if it is * rendered as a wire frame. - * Synonyms: solid, isFilled, and filled. + * Synonyms: solid, isFilled, and filled. * Antonyms: isWire and wire. * @property {boolean} ignorePickIntersection=false - true if {@link Picks} ignore the overlay, false * if they don't. @@ -1641,7 +1640,7 @@ QVector Overlays::findOverlays(const glm::vec3& center, float radius) { *
    ValueDimensionsDescription
    "Circle"2DA circle oriented in 3D.
    "Circle"2DA circle oriented in 3D.
    "Cone"3D
    "Cube"3D
    "Cylinder"3D
    * - * + * * * * - * + * * diff --git a/tools/jsdoc/hifi-jsdoc-template/tmpl/params.tmpl b/tools/jsdoc/hifi-jsdoc-template/tmpl/params.tmpl index e994e42bfa..dd9f5f7ee7 100644 --- a/tools/jsdoc/hifi-jsdoc-template/tmpl/params.tmpl +++ b/tools/jsdoc/hifi-jsdoc-template/tmpl/params.tmpl @@ -120,10 +120,15 @@ - + diff --git a/tools/jsdoc/hifi-jsdoc-template/tmpl/properties.tmpl b/tools/jsdoc/hifi-jsdoc-template/tmpl/properties.tmpl index 5c83a7d587..31717ed63b 100644 --- a/tools/jsdoc/hifi-jsdoc-template/tmpl/properties.tmpl +++ b/tools/jsdoc/hifi-jsdoc-template/tmpl/properties.tmpl @@ -87,7 +87,9 @@
    ValueName

    Description
    ValueNameDescription
    0RelaxToUnderPosesThis is a blend: it is 15/16 PreviousSolution @@ -73,12 +73,14 @@ public: *
    1RelaxToLimitCenterPosesThis is a blend: it is 15/16 * PreviousSolution and 1/16 LimitCenterPoses. This should converge quickly because it is * close to the previous solution, but still provides the benefits of avoiding limb locking.
    2PreviousSolutionThe IK system will begin to solve from the same position and - * orientations for each joint that was the result from the previous frame.
    - * Pros: As the end effectors typically do not move much from frame to frame, this is likely to converge quickly - * to a valid solution.
    - * Cons: If the previous solution resulted in an awkward or uncomfortable posture, the next frame will also be - * awkward and uncomfortable. It can also result in locked elbows and knees.
    2PreviousSolution + *

    The IK system will begin to solve from the same position and orientations for each joint that was the result + * from the previous frame.

    + *

    Pros: As the end effectors typically do not move much from frame to frame, this is likely to converge quickly + * to a valid solution.

    + *

    Cons: If the previous solution resulted in an awkward or uncomfortable posture, the next frame will also be + * awkward and uncomfortable. It can also result in locked elbows and knees.

    + *
    3UnderPosesThe IK occurs at one of the top-most layers. It has access to the * full posture that was computed via canned animations and blends. We call this animated set of poses the "under * pose". The under poses are what would be visible if IK was completely disabled. Using the under poses as the diff --git a/libraries/animation/src/AnimOverlay.h b/libraries/animation/src/AnimOverlay.h index 1ad4e100db..d5e114a2d2 100644 --- a/libraries/animation/src/AnimOverlay.h +++ b/libraries/animation/src/AnimOverlay.h @@ -28,7 +28,7 @@ public: *

    Specifies sets of joints.

    * * - * + * * * * diff --git a/libraries/animation/src/IKTarget.h b/libraries/animation/src/IKTarget.h index 331acedd4e..a56f5578b3 100644 --- a/libraries/animation/src/IKTarget.h +++ b/libraries/animation/src/IKTarget.h @@ -20,14 +20,14 @@ public: *

    An IK target type.

    *
    ValueName

    Description
    ValueNameDescription
    0FullBodyBoneSetAll joints.
    * - * + * * * * * - * * diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index d63c2d6a9b..fac4e04ce9 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -95,7 +95,7 @@ static const QString MAIN_STATE_MACHINE_RIGHT_HAND_POSITION("mainStateMachineRig *

    Warning: These properties are subject to change. *

    ValueName

    Description
    ValueNameDescription
    0RotationAndPositionAttempt to reach the rotation and position end * effector.
    1RotationOnlyAttempt to reach the end effector rotation only.
    2HmdHeadA special mode of IK that would attempt to prevent unnecessary - * bending of the spine.
    + *
    2HmdHead + *

    A special mode of IK that would attempt to prevent unnecessary bending of the spine.

    *

    Deprecated: This target type is deprecated and will be removed.

    3HipsRelativeRotationAndPositionAttempt to reach a rotation and position end * effector that is not in absolute rig coordinates but is offset by the avatar hips translation.
    * - * + * * * * * var entityID = Entities.addEntity({ * type: "Sphere", @@ -1217,10 +1213,10 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * dimensions / voxelVolumesize. * @property {string} voxelData="ABAAEAAQAAAAHgAAEAB42u3BAQ0AAADCoPdPbQ8HFAAAAPBuEAAAAQ==" - Base-64 encoded compressed dump of * the PolyVox data. This property is typically not used in scripts directly; rather, functions that manipulate a PolyVox - * entity update it.
    - * The size of this property increases with the size and complexity of the PolyVox entity, with the size depending on how + * entity update it. + *

    The size of this property increases with the size and complexity of the PolyVox entity, with the size depending on how * the particular entity's voxels compress. Because this property value has to fit within a High Fidelity datagram packet, - * there is a limit to the size and complexity of a PolyVox entity; edits which would result in an overflow are rejected. + * there is a limit to the size and complexity of a PolyVox entity; edits which would result in an overflow are rejected.

    * @property {Entities.PolyVoxSurfaceStyle} voxelSurfaceStyle=2 - The style of rendering the voxels' surface and how * neighboring PolyVox entities are joined. * @property {string} xTextureURL="" - The URL of the texture to map to surfaces perpendicular to the entity's local x-axis. @@ -1309,7 +1305,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @property {number} bottomMargin=0.0 - The bottom margin, in meters. * @property {boolean} unlit=false - true if the entity is unaffected by lighting, false if it is lit * by the key light and local lights. - * @property {string} font="" - The font to render the text with. It can be one of the following: "Courier""Courier", * "Inconsolata", "Roboto", "Timeless", or a path to a .sdff file. * @property {Entities.TextEffect} textEffect="none" - The effect that is applied to the text. * @property {Color} textEffectColor=255,255,255 - The color of the effect. diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 7cfdc8a68d..3305d9ba00 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -666,7 +666,7 @@ QScriptValue EntityScriptingInterface::getMultipleEntityProperties(QScriptContex const int ARGUMENT_EXTENDED_DESIRED_PROPERTIES = 1; auto entityScriptingInterface = DependencyManager::get(); - const auto entityIDs = qScriptValueToValue>(context->argument(ARGUMENT_ENTITY_IDS)); + const auto entityIDs = qscriptvalue_cast>(context->argument(ARGUMENT_ENTITY_IDS)); return entityScriptingInterface->getMultipleEntityPropertiesInternal(engine, entityIDs, context->argument(ARGUMENT_EXTENDED_DESIRED_PROPERTIES)); } @@ -716,7 +716,7 @@ QScriptValue EntityScriptingInterface::getMultipleEntityPropertiesInternal(QScri psuedoPropertyFlags.set(EntityPsuedoPropertyFlag::FlagsActive); } - EntityPropertyFlags desiredProperties = qScriptValueToValue(extendedDesiredProperties); + EntityPropertyFlags desiredProperties = qscriptvalue_cast(extendedDesiredProperties); bool needsScriptSemantics = desiredProperties.getHasProperty(PROP_POSITION) || desiredProperties.getHasProperty(PROP_ROTATION) || desiredProperties.getHasProperty(PROP_LOCAL_POSITION) || diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 52470e56c4..fb1ec56503 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -106,8 +106,9 @@ public: * "domain" entities, travel to different domains with a user as "avatar" entities, or be visible only to an individual user as * "local" entities (a.k.a. "overlays"). * - *

    Note: For Interface scripts, the entities available to scripts are those that Interface has displayed and so knows - * about.

    + *

    Note: For Interface, avatar, and client entity scripts, the entities available to scripts are those that Interface has + * displayed and so knows about. For assignment client scripts, the entities available are those that are "seen" by the + * {@link EntityViewer}. For entity server scripts, all entities are available.

    * *

    Entity Methods

    * @@ -728,7 +729,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
    @@ -1870,7 +1872,7 @@ public slots: /**jsdoc * Called when a {@link Entities.getMeshes} call is complete. * @callback Entities~getMeshesCallback - * @param {MeshProxy[]} meshes - If success< is true, a {@link MeshProxy} per mesh in the + * @param {MeshProxy[]} meshes - If success is true, a {@link MeshProxy} per mesh in the * Model or PolyVox entity; otherwise undefined. * @param {boolean} success - true if the {@link Entities.getMeshes} call was successful, false * otherwise. The call may be unsuccessful if the requested entity could not be found. diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h index 4106eb8054..7f72c3f761 100644 --- a/libraries/entities/src/EntityTypes.h +++ b/libraries/entities/src/EntityTypes.h @@ -46,18 +46,18 @@ public: * See also, the "Box" and "Sphere" entity types. * * * * * * * diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp index dca9e9fefa..da21c7995e 100755 --- a/libraries/fbx/src/GLTFSerializer.cpp +++ b/libraries/fbx/src/GLTFSerializer.cpp @@ -1859,7 +1859,7 @@ bool GLTFSerializer::readArray(const hifi::ByteArray& bin, int byteOffset, int c break; default: qWarning(modelformat) << "Unknown accessorType: " << accessorType; - blobstream.unsetDevice(); + blobstream.setDevice(nullptr); return false; } for (int i = 0; i < count; ++i) { @@ -1869,13 +1869,13 @@ bool GLTFSerializer::readArray(const hifi::ByteArray& bin, int byteOffset, int c blobstream >> value; outarray.push_back(value); } else { - blobstream.unsetDevice(); + blobstream.setDevice(nullptr); return false; } } } - blobstream.unsetDevice(); + blobstream.setDevice(nullptr); return true; } template diff --git a/libraries/gpu/src/gpu/FrameIO.cpp b/libraries/gpu/src/gpu/FrameIO.cpp new file mode 100644 index 0000000000..a0f21df881 --- /dev/null +++ b/libraries/gpu/src/gpu/FrameIO.cpp @@ -0,0 +1,136 @@ +// +// Created by Bradley Austin Davis on 2019/10/03 +// Copyright 2013-2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "FrameIO.h" +#include +#include + +using namespace gpu::hfb; + +static bool skip(const uint8_t*& ptr, size_t& remaining, uint32_t size) { + if (remaining < size) { + return false; + } + ptr += size; + remaining -= size; + return true; +} + +template +static bool read(const uint8_t*& ptr, size_t& remaining, T& output) { + uint32_t readSize = (uint32_t)sizeof(T); + if (remaining < readSize) { + return false; + } + memcpy(&output, ptr, readSize); + return skip(ptr, remaining, readSize); +} + +Descriptor::Descriptor(const StoragePointer& storage) : storage(storage) { + const auto* const start = storage->data(); + const auto* ptr = storage->data(); + auto remaining = storage->size(); + + try { + // Can't parse files more than 4GB + if (remaining > UINT32_MAX) { + throw std::runtime_error("File too large"); + } + + if (!read(ptr, remaining, header)) { + throw std::runtime_error("Couldn't read binary header"); + } + + if (header.length != storage->size()) { + throw std::runtime_error("Header/Actual size mismatch"); + } + + while (remaining != 0) { + chunks.emplace_back(); + auto& chunk = chunks.back(); + ChunkHeader& chunkHeader = chunk; + if (!read(ptr, remaining, chunkHeader)) { + throw std::runtime_error("Coulnd't read chunk header"); + } + chunk.offset = (uint32_t)(ptr - start); + if (chunk.end() > storage->size()) { + throw std::runtime_error("Chunk too large for file"); + } + if (!skip(ptr, remaining, chunk.length)) { + throw std::runtime_error("Skip chunk data failed"); + } + } + } catch (const std::runtime_error&) { + // LOG somnething + header.magic = 0; + } +} + +size_t Chunk::end() const { + size_t result = offset; + result += length; + return result; +} + +StoragePointer Descriptor::getChunk(uint32_t chunkIndex) const { + if (chunkIndex >= chunks.size()) { + return {}; + } + const auto& chunk = chunks[chunkIndex]; + if (chunk.end() > storage->size()) { + return {}; + } + return storage->createView(chunk.length, chunk.offset); +} + +static void writeUint(uint8_t*& dest, uint32_t value) { + memcpy(dest, &value, sizeof(uint32_t)); + dest += sizeof(uint32_t); +} + +template +static void writeChunk(uint8_t*& dest, uint32_t chunkType, const T& chunkData) { + uint32_t chunkSize = static_cast(chunkData.size()); + writeUint(dest, chunkSize); + writeUint(dest, chunkType); + memcpy(dest, chunkData.data(), chunkSize); + dest += chunkSize; +} + +void gpu::hfb::writeFrame(const std::string& filename, + const std::string& json, + const Buffer& binaryBuffer, + const StorageBuilders& ktxBuilders) { + uint32_t strLen = (uint32_t)json.size(); + uint32_t size = gpu::hfb::HEADER_SIZE + gpu::hfb::CHUNK_HEADER_SIZE + strLen; + size += gpu::hfb::CHUNK_HEADER_SIZE + (uint32_t)binaryBuffer.size(); + for (const auto& builder : ktxBuilders) { + auto storage = builder(); + if (storage) { + size += gpu::hfb::CHUNK_HEADER_SIZE + (uint32_t)storage->size(); + } + } + + auto outputConst = storage::FileStorage::create(filename.c_str(), size, nullptr); + auto output = std::const_pointer_cast(outputConst); + auto ptr = output->mutableData(); + writeUint(ptr, gpu::hfb::MAGIC); + writeUint(ptr, gpu::hfb::VERSION); + writeUint(ptr, size); + writeChunk(ptr, gpu::hfb::CHUNK_TYPE_JSON, json); + writeChunk(ptr, gpu::hfb::CHUNK_TYPE_BIN, binaryBuffer); + for (const auto& builder : ktxBuilders) { + static StoragePointer EMPTY_STORAGE{ std::make_shared(0, nullptr) }; + auto storage = builder(); + if (!storage) { + storage = EMPTY_STORAGE; + } + writeChunk(ptr, gpu::hfb::CHUNK_TYPE_KTX, *storage); + } + assert((ptr - output->data()) == size); +} diff --git a/libraries/gpu/src/gpu/FrameIO.h b/libraries/gpu/src/gpu/FrameIO.h index 4de4cf5fe7..cf653cc4ae 100644 --- a/libraries/gpu/src/gpu/FrameIO.h +++ b/libraries/gpu/src/gpu/FrameIO.h @@ -12,17 +12,72 @@ #include "Forward.h" #include "Format.h" +#include + #include namespace gpu { -using TextureCapturer = std::function; -using TextureLoader = std::function; +using TextureCapturer = std::function; +//using TextureLoader = std::function; void writeFrame(const std::string& filename, const FramePointer& frame, const TextureCapturer& capturer = nullptr); -FramePointer readFrame(const std::string& filename, uint32_t externalTexture, const TextureLoader& loader = nullptr); +FramePointer readFrame(const std::string& filename, uint32_t externalTexture); -using IndexOptimizer = std::function; -void optimizeFrame(const std::string& filename, const IndexOptimizer& optimizer); +namespace hfb { + +using Storage = storage::Storage; +using StoragePointer = storage::Pointer; +using StorageBuilders = storage::Builders; + +constexpr const char* const EXTENSION{ ".hfb" }; +constexpr uint32_t HEADER_SIZE{ sizeof(uint32_t) * 3 }; +constexpr uint32_t CHUNK_HEADER_SIZE = sizeof(uint32_t) * 2; +constexpr uint32_t MAGIC{ 0x49464948 }; +constexpr uint32_t VERSION{ 0x01 }; +constexpr uint32_t CHUNK_TYPE_JSON{ 0x4E4F534A }; +constexpr uint32_t CHUNK_TYPE_KTX{ 0x0058544b }; +constexpr uint32_t CHUNK_TYPE_BIN{ 0x004E4942 }; +constexpr uint32_t CHUNK_TYPE_PNG{ 0x00474E50 }; + +using Buffer = std::vector; + +struct Header { + uint32_t magic{ 0 }; + uint32_t version{ 0 }; + uint32_t length{ 0 }; +}; + +struct ChunkHeader { + uint32_t length{ 0 }; + uint32_t type{ 0 }; +}; + +struct Chunk : public ChunkHeader { + uint32_t offset{ 0 }; + + size_t end() const; +}; + +using Chunks = std::vector; + +struct Descriptor { + using Pointer = std::shared_ptr; + + Header header; + Chunks chunks; + StoragePointer storage; + + Descriptor(const StoragePointer& storage); + operator bool() const { return header.magic == MAGIC; } + StoragePointer getChunk(uint32_t chunk) const; +}; + +void writeFrame(const std::string& filename, + const std::string& json, + const Buffer& binaryBuffer, + const StorageBuilders& pngBuffers); + +} // namespace hfb } // namespace gpu diff --git a/libraries/gpu/src/gpu/FrameIOKeys.h b/libraries/gpu/src/gpu/FrameIOKeys.h index 5d5f8a5bd9..1a98d0decd 100644 --- a/libraries/gpu/src/gpu/FrameIOKeys.h +++ b/libraries/gpu/src/gpu/FrameIOKeys.h @@ -11,128 +11,130 @@ namespace gpu { namespace keys { -static const char* binary = "binary"; -static const char* L00 = "L00"; -static const char* L1m1 = "L1m1"; -static const char* L10 = "L10"; -static const char* L11 = "L11"; -static const char* L2m2 = "L2m2"; -static const char* L2m1 = "L2m1"; -static const char* L20 = "L20"; -static const char* L21 = "L21"; -static const char* L22 = "L22"; -static const char* eyeProjections = "eyeProjections"; -static const char* eyeViews = "eyeViews"; -static const char* alphaToCoverageEnable = "alphaToCoverageEnable"; -static const char* antialisedLineEnable = "antialisedLineEnable"; -static const char* attributes = "attributes"; -static const char* batches = "batches"; -static const char* blendFunction = "blendFunction"; -static const char* borderColor = "borderColor"; -static const char* bufferMask = "bufferMask"; -static const char* buffers = "buffers"; -static const char* capturedTextures = "capturedTextures"; -static const char* channel = "channel"; -static const char* colorAttachments = "colorAttachments"; -static const char* colorWriteMask = "colorWriteMask"; -static const char* commands = "commands"; -static const char* comparisonFunction = "comparisonFunction"; -static const char* cullMode = "cullMode"; -static const char* data = "data"; -static const char* depth = "depth"; -static const char* depthBias = "depthBias"; -static const char* depthBiasSlopeScale = "depthBiasSlopeScale"; -static const char* depthClampEnable = "depthClampEnable"; -static const char* depthStencilAttachment = "depthStencilAttachment"; -static const char* depthTest = "depthTest"; -static const char* drawCallInfos = "drawCallInfos"; -static const char* drawcallUniform = "drawcallUniform"; -static const char* drawcallUniformReset = "drawcallUniformReset"; -static const char* element = "element"; -static const char* fillMode = "fillMode"; -static const char* filter = "filter"; -static const char* formats = "formats"; -static const char* frameIndex = "frameIndex"; -static const char* framebuffer = "framebuffer"; -static const char* framebuffers = "framebuffers"; -static const char* frequency = "frequency"; -static const char* frontFaceClockwise = "frontFaceClockwise"; -static const char* height = "height"; -static const char* id = "id"; -static const char* ktxFile = "ktxFile"; -static const char* layers = "layers"; -static const char* maxAnisotropy = "maxAnisotropy"; -static const char* maxMip = "maxMip"; -static const char* minMip = "minMip"; -static const char* mipOffset = "mipOffset"; -static const char* mips = "mips"; -static const char* multisampleEnable = "multisampleEnable"; -static const char* name = "name"; -static const char* namedData = "namedData"; -static const char* names = "names"; -static const char* objects = "objects"; -static const char* offset = "offset"; -static const char* pipelines = "pipelines"; -static const char* pose = "pose"; -static const char* profileRanges = "profileRanges"; -static const char* program = "program"; -static const char* programs = "programs"; -static const char* projectionJitter = "projectionJitter"; -static const char* queries = "queries"; -static const char* sampleCount = "sampleCount"; -static const char* sampleMask = "sampleMask"; -static const char* sampler = "sampler"; -static const char* samples = "samples"; -static const char* scissorEnable = "scissorEnable"; -static const char* shaders = "shaders"; -static const char* size = "size"; -static const char* skybox = "skybox"; -static const char* slot = "slot"; -static const char* source = "source"; -static const char* state = "state"; -static const char* stencilActivation = "stencilActivation"; -static const char* stencilTestBack = "stencilTestBack"; -static const char* stencilTestFront = "stencilTestFront"; -static const char* stereo = "stereo"; -static const char* subresource = "subresource"; -static const char* swapchains = "swapchains"; -static const char* texelFormat = "texelFormat"; -static const char* texture = "texture"; -static const char* textureTables = "textureTables"; -static const char* textures = "textures"; -static const char* transforms = "transforms"; -static const char* type = "type"; -static const char* usageType = "usageType"; -static const char* view = "view"; -static const char* width = "width"; -static const char* wrapModeU = "wrapModeU"; -static const char* wrapModeV = "wrapModeV"; -static const char* wrapModeW = "wrapModeW"; +constexpr const char* binary = "binary"; +constexpr const char* L00 = "L00"; +constexpr const char* L1m1 = "L1m1"; +constexpr const char* L10 = "L10"; +constexpr const char* L11 = "L11"; +constexpr const char* L2m2 = "L2m2"; +constexpr const char* L2m1 = "L2m1"; +constexpr const char* L20 = "L20"; +constexpr const char* L21 = "L21"; +constexpr const char* L22 = "L22"; + +constexpr const char* eyeProjections = "eyeProjections"; +constexpr const char* eyeViews = "eyeViews"; +constexpr const char* alphaToCoverageEnable = "alphaToCoverageEnable"; +constexpr const char* antialisedLineEnable = "antialisedLineEnable"; +constexpr const char* attributes = "attributes"; +constexpr const char* batches = "batches"; +constexpr const char* blendFunction = "blendFunction"; +constexpr const char* borderColor = "borderColor"; +constexpr const char* bufferMask = "bufferMask"; +constexpr const char* buffers = "buffers"; +constexpr const char* capturedTextures = "capturedTextures"; +constexpr const char* channel = "channel"; +constexpr const char* chunk = "chunk"; +constexpr const char* colorAttachments = "colorAttachments"; +constexpr const char* colorWriteMask = "colorWriteMask"; +constexpr const char* commands = "commands"; +constexpr const char* comparisonFunction = "comparisonFunction"; +constexpr const char* cullMode = "cullMode"; +constexpr const char* data = "data"; +constexpr const char* depth = "depth"; +constexpr const char* depthBias = "depthBias"; +constexpr const char* depthBiasSlopeScale = "depthBiasSlopeScale"; +constexpr const char* depthClampEnable = "depthClampEnable"; +constexpr const char* depthStencilAttachment = "depthStencilAttachment"; +constexpr const char* depthTest = "depthTest"; +constexpr const char* drawCallInfos = "drawCallInfos"; +constexpr const char* drawcallUniform = "drawcallUniform"; +constexpr const char* drawcallUniformReset = "drawcallUniformReset"; +constexpr const char* element = "element"; +constexpr const char* fillMode = "fillMode"; +constexpr const char* filter = "filter"; +constexpr const char* formats = "formats"; +constexpr const char* frameIndex = "frameIndex"; +constexpr const char* framebuffer = "framebuffer"; +constexpr const char* framebuffers = "framebuffers"; +constexpr const char* frequency = "frequency"; +constexpr const char* frontFaceClockwise = "frontFaceClockwise"; +constexpr const char* height = "height"; +constexpr const char* id = "id"; +constexpr const char* ktxFile = "ktxFile"; +constexpr const char* layers = "layers"; +constexpr const char* maxAnisotropy = "maxAnisotropy"; +constexpr const char* maxMip = "maxMip"; +constexpr const char* minMip = "minMip"; +constexpr const char* mipOffset = "mipOffset"; +constexpr const char* mips = "mips"; +constexpr const char* multisampleEnable = "multisampleEnable"; +constexpr const char* name = "name"; +constexpr const char* namedData = "namedData"; +constexpr const char* names = "names"; +constexpr const char* objects = "objects"; +constexpr const char* offset = "offset"; +constexpr const char* pipelines = "pipelines"; +constexpr const char* pose = "pose"; +constexpr const char* profileRanges = "profileRanges"; +constexpr const char* program = "program"; +constexpr const char* programs = "programs"; +constexpr const char* projectionJitter = "projectionJitter"; +constexpr const char* queries = "queries"; +constexpr const char* sampleCount = "sampleCount"; +constexpr const char* sampleMask = "sampleMask"; +constexpr const char* sampler = "sampler"; +constexpr const char* samples = "samples"; +constexpr const char* scissorEnable = "scissorEnable"; +constexpr const char* shaders = "shaders"; +constexpr const char* size = "size"; +constexpr const char* skybox = "skybox"; +constexpr const char* slot = "slot"; +constexpr const char* source = "source"; +constexpr const char* state = "state"; +constexpr const char* stencilActivation = "stencilActivation"; +constexpr const char* stencilTestBack = "stencilTestBack"; +constexpr const char* stencilTestFront = "stencilTestFront"; +constexpr const char* stereo = "stereo"; +constexpr const char* subresource = "subresource"; +constexpr const char* swapchains = "swapchains"; +constexpr const char* texelFormat = "texelFormat"; +constexpr const char* texture = "texture"; +constexpr const char* textureTables = "textureTables"; +constexpr const char* textures = "textures"; +constexpr const char* transforms = "transforms"; +constexpr const char* type = "type"; +constexpr const char* usageType = "usageType"; +constexpr const char* view = "view"; +constexpr const char* width = "width"; +constexpr const char* wrapModeU = "wrapModeU"; +constexpr const char* wrapModeV = "wrapModeV"; +constexpr const char* wrapModeW = "wrapModeW"; -static const char* backWriteMask = "backWriteMask"; -static const char* frontWriteMask = "frontWriteMask"; -static const char* reference = "reference"; -static const char* readMask = "readMask"; -static const char* failOp = "failOp"; -static const char* depthFailOp = "depthFailOp"; -static const char* passOp = "passOp"; -static const char* enabled = "enabled"; -static const char* blend = "blend"; -static const char* flags = "flags"; -static const char* writeMask = "writeMask"; -static const char* function = "function"; -static const char* sourceColor = "sourceColor"; -static const char* sourceAlpha = "sourceAlpha"; -static const char* destColor = "destColor"; -static const char* destAlpha = "destAlpha"; -static const char* opColor = "opColor"; -static const char* opAlpha = "opAlpha"; -static const char* enable = "enable"; -static const char* contextDisable = "contextDisable"; +constexpr const char* backWriteMask = "backWriteMask"; +constexpr const char* frontWriteMask = "frontWriteMask"; +constexpr const char* reference = "reference"; +constexpr const char* readMask = "readMask"; +constexpr const char* failOp = "failOp"; +constexpr const char* depthFailOp = "depthFailOp"; +constexpr const char* passOp = "passOp"; +constexpr const char* enabled = "enabled"; +constexpr const char* blend = "blend"; +constexpr const char* flags = "flags"; +constexpr const char* writeMask = "writeMask"; +constexpr const char* function = "function"; +constexpr const char* sourceColor = "sourceColor"; +constexpr const char* sourceAlpha = "sourceAlpha"; +constexpr const char* destColor = "destColor"; +constexpr const char* destAlpha = "destAlpha"; +constexpr const char* opColor = "opColor"; +constexpr const char* opAlpha = "opAlpha"; +constexpr const char* enable = "enable"; +constexpr const char* contextDisable = "contextDisable"; -static const char* COMMAND_NAMES[] = { +constexpr const char* COMMAND_NAMES[] = { "draw", "drawIndexed", "drawInstanced", diff --git a/libraries/gpu/src/gpu/FrameReader.cpp b/libraries/gpu/src/gpu/FrameReader.cpp index 740ec2b26f..d636c6aaca 100644 --- a/libraries/gpu/src/gpu/FrameReader.cpp +++ b/libraries/gpu/src/gpu/FrameReader.cpp @@ -10,9 +10,7 @@ #include #include -#include -#include - +#include #include #include "Frame.h" #include "Batch.h" @@ -22,24 +20,18 @@ namespace gpu { using json = nlohmann::json; +using StoragePointer = storage::StoragePointer; +using FileStorage = storage::FileStorage; class Deserializer { public: - static std::string getBaseName(const std::string& filename) { - static const std::string ext{ ".json" }; - if (std::string::npos != filename.rfind(ext)) { - return filename.substr(0, filename.size() - ext.size()); - } - return filename; - } - static std::string getBaseDir(const std::string& filename) { std::string result; if (0 == filename.find("assets:")) { auto lastSlash = filename.rfind('/'); result = filename.substr(0, lastSlash + 1); } else { - result = QFileInfo(filename.c_str()).absoluteDir().canonicalPath().toStdString(); + result = FileUtils::getParentPath(filename.c_str()).toStdString(); if (*result.rbegin() != '/') { result += '/'; } @@ -47,15 +39,17 @@ public: return result; } - Deserializer(const std::string& filename, uint32_t externalTexture, const TextureLoader& loader) : - basename(getBaseName(filename)), basedir(getBaseDir(filename)), externalTexture(externalTexture), textureLoader(loader) { + Deserializer(const std::string& filename_, uint32_t externalTexture = 0) : + filename(filename_), basedir(getBaseDir(filename_)), mappedFile(std::make_shared(filename.c_str())), + externalTexture(externalTexture) { + descriptor = std::make_shared(mappedFile); } - const std::string basename; + const std::string filename; const std::string basedir; - std::string binaryFile; + const StoragePointer mappedFile; const uint32_t externalTexture; - TextureLoader textureLoader; + hfb::Descriptor::Pointer descriptor; std::vector shaders; std::vector programs; std::vector textures; @@ -69,10 +63,13 @@ public: std::vector queries; json frameNode; FramePointer readFrame(); - void optimizeFrame(const IndexOptimizer& optimizer); FramePointer deserializeFrame(); + std::string getStringChunk(size_t chunkIndex) { + auto storage = descriptor->getChunk((uint32_t)chunkIndex); + return std::string{ (const char*)storage->data(), storage->size() }; + } void readBuffers(const json& node); @@ -148,12 +145,11 @@ public: } template - static bool readBatchCacheTransformed(typename Batch::Cache::Vector& dest, - const json& node, - const std::string& name, - std::function f = [](const json& node) -> TT { - return node.get(); - }) { + static bool readBatchCacheTransformed( + typename Batch::Cache::Vector& dest, + const json& node, + const std::string& name, + std::function f = [](const json& node) -> TT { return node.get(); }) { if (node.count(name)) { const auto& arrayNode = node[name]; for (const auto& entry : arrayNode) { @@ -230,12 +226,8 @@ public: static void readCommand(const json& node, Batch& batch); }; -FramePointer readFrame(const std::string& filename, uint32_t externalTexture, const TextureLoader& loader) { - return Deserializer(filename, externalTexture, loader).readFrame(); -} - -void optimizeFrame(const std::string& filename, const IndexOptimizer& optimizer) { - return Deserializer(filename, 0, {}).optimizeFrame(optimizer); +FramePointer readFrame(const std::string& filename, uint32_t externalTexture) { + return Deserializer(filename, externalTexture).readFrame(); } } // namespace gpu @@ -243,9 +235,9 @@ void optimizeFrame(const std::string& filename, const IndexOptimizer& optimizer) using namespace gpu; void Deserializer::readBuffers(const json& buffersNode) { - storage::FileStorage mappedFile(binaryFile.c_str()); - const auto mappedSize = mappedFile.size(); - const auto* mapped = mappedFile.data(); + const auto& binaryChunk = descriptor->chunks[1]; + const auto* mapped = mappedFile->data() + binaryChunk.offset; + const auto mappedSize = binaryChunk.length; size_t bufferCount = buffersNode.size(); buffers.reserve(buffersNode.size()); size_t offset = 0; @@ -311,6 +303,8 @@ Sampler Deserializer::readSampler(const json& node) { return result; } +constexpr uint32_t INVALID_CHUNK_INDEX{ (uint32_t)-1 }; + TexturePointer Deserializer::readTexture(const json& node, uint32_t external) { if (node.is_null()) { return nullptr; @@ -319,8 +313,17 @@ TexturePointer Deserializer::readTexture(const json& node, uint32_t external) { std::string source; readOptional(source, node, keys::source); + uint32_t chunkIndex = INVALID_CHUNK_INDEX; + readOptional(chunkIndex, node, keys::chunk); + std::string ktxFile; readOptional(ktxFile, node, keys::ktxFile); + if (!ktxFile.empty()) { + if (!FileUtils::exists(ktxFile.c_str())) { + qDebug() << "Warning" << ktxFile.c_str() << " not found, ignoring"; + ktxFile = {}; + } + } Element ktxTexelFormat, ktxMipFormat; if (!ktxFile.empty()) { // If we get a texture that starts with ":" we need to re-route it to the resources directory @@ -330,8 +333,8 @@ TexturePointer Deserializer::readTexture(const json& node, uint32_t external) { frameReaderPath.replace("libraries/gpu/src/gpu/framereader.cpp", "interface/resources", Qt::CaseInsensitive); ktxFile.replace(0, 1, frameReaderPath.toStdString()); } - if (QFileInfo(ktxFile.c_str()).isRelative()) { - ktxFile = basedir + ktxFile; + if (FileUtils::isRelative(ktxFile.c_str())) { + ktxFile = basedir + "/" + ktxFile; } ktx::StoragePointer ktxStorage{ new storage::FileStorage(ktxFile.c_str()) }; auto ktxObject = ktx::KTX::create(ktxStorage); @@ -364,12 +367,14 @@ TexturePointer Deserializer::readTexture(const json& node, uint32_t external) { auto& texture = *result; readOptional(texture._source, node, keys::source); - if (!ktxFile.empty()) { - if (QFileInfo(ktxFile.c_str()).isRelative()) { - ktxFile = basedir + "/" + ktxFile; - } + + if (chunkIndex != INVALID_CHUNK_INDEX) { + auto ktxChunk = descriptor->getChunk(chunkIndex); + texture.setKtxBacking(ktxChunk); + } else if (!ktxFile.empty()) { + texture.setSource(ktxFile); texture.setKtxBacking(ktxFile); - } + } return result; } @@ -405,11 +410,11 @@ ShaderPointer Deserializer::readShader(const json& node) { // FIXME support procedural shaders Shader::Type type = node[keys::type]; std::string name = node[keys::name]; - // Using the serialized ID is bad, because it's generated at - // cmake time, and can change across platforms or when + // Using the serialized ID is bad, because it's generated at + // cmake time, and can change across platforms or when // shaders are added or removed // uint32_t id = node[keys::id]; - + uint32_t id = shadersIdsByName[name]; ShaderPointer result; switch (type) { @@ -555,11 +560,15 @@ StatePointer readState(const json& node) { State::Data data; Deserializer::readOptionalTransformed(data.flags, node, keys::flags, &readStateFlags); - Deserializer::readOptionalTransformed(data.blendFunction, node, keys::blendFunction, &readBlendFunction); + Deserializer::readOptionalTransformed(data.blendFunction, node, keys::blendFunction, + &readBlendFunction); Deserializer::readOptionalTransformed(data.depthTest, node, keys::depthTest, &readDepthTest); - Deserializer::readOptionalTransformed(data.stencilActivation, node, keys::stencilActivation, &readStencilActivation); - Deserializer::readOptionalTransformed(data.stencilTestFront, node, keys::stencilTestFront, &readStencilTest); - Deserializer::readOptionalTransformed(data.stencilTestBack, node, keys::stencilTestBack, &readStencilTest); + Deserializer::readOptionalTransformed(data.stencilActivation, node, keys::stencilActivation, + &readStencilActivation); + Deserializer::readOptionalTransformed(data.stencilTestFront, node, keys::stencilTestFront, + &readStencilTest); + Deserializer::readOptionalTransformed(data.stencilTestBack, node, keys::stencilTestBack, + &readStencilTest); Deserializer::readOptional(data.colorWriteMask, node, keys::colorWriteMask); Deserializer::readOptional(data.cullMode, node, keys::cullMode); Deserializer::readOptional(data.depthBias, node, keys::depthBias); @@ -799,25 +808,15 @@ StereoState readStereoState(const json& node) { FramePointer Deserializer::deserializeFrame() { - { - std::string filename{ basename + ".json" }; - storage::FileStorage mappedFile(filename.c_str()); - frameNode = json::parse(std::string((const char*)mappedFile.data(), mappedFile.size())); + if (!descriptor.operator bool()) { + return {}; } + frameNode = json::parse(getStringChunk(0)); + FramePointer result = std::make_shared(); auto& frame = *result; - if (frameNode[keys::binary].is_string()) { - binaryFile = frameNode[keys::binary]; - if (QFileInfo(binaryFile.c_str()).isRelative()) { - binaryFile = basedir + "/" + binaryFile; - } - } else { - binaryFile = basename + ".bin"; - } - - if (frameNode.count(keys::buffers)) { readBuffers(frameNode[keys::buffers]); } @@ -830,19 +829,7 @@ FramePointer Deserializer::deserializeFrame() { formats = readArray(frameNode, keys::formats, [](const json& node) { return readFormat(node); }); - auto textureReader = [this](const json& node) { return readTexture(node, externalTexture); }; - textures = readArray(frameNode, keys::textures, textureReader); - if (textureLoader) { - std::vector capturedTextures = readNumericVector(frameNode[keys::capturedTextures]); - for (const auto& index : capturedTextures) { - const auto& texturePointer = textures[index]; - uint16 layers = std::max(texturePointer->getNumSlices(), 1); - for (uint16 layer = 0; layer < layers; ++layer) { - std::string filename = basename + "." + std::to_string(index) + "." + std::to_string(layer) + ".png"; - textureLoader(filename, texturePointer, layer); - } - } - } + textures = readArray(frameNode, keys::textures, [this](const json& node) { return readTexture(node, externalTexture); }); // Must come after textures auto textureTableReader = [this](const json& node) { return readTextureTable(node); }; @@ -868,87 +855,22 @@ FramePointer Deserializer::deserializeFrame() { } } + for (uint32_t i = 0; i < textures.size(); ++i) { + const auto& texturePtr = textures[i]; + if (!texturePtr) { + continue; + } + const auto& texture = *texturePtr; + if (texture.getUsageType() == gpu::TextureUsageType::RESOURCE && texture.source().empty()) { + qDebug() << "Empty source "; + } + } + return result; } - - FramePointer Deserializer::readFrame() { auto result = deserializeFrame(); result->finish(); return result; } - -void Deserializer::optimizeFrame(const IndexOptimizer& optimizer) { - auto result = deserializeFrame(); - auto& frame = *result; - - - // optimize the index buffers? - struct CurrentIndexBuffer { - Offset offset{ 0 }; - BufferPointer buffer; - Type type{ gpu::Type::INT32 }; - Primitive primitve{ Primitive::TRIANGLES }; - uint32_t numIndices{ 0 }; - uint32_t startIndex{ 0 }; - }; - - std::vector captured; - for (auto& batch : frame.batches) { - - CurrentIndexBuffer currentIndexBuffer; - batch->forEachCommand([&](Batch::Command cmd, const Batch::Param* params){ - switch(cmd) { - case Batch::Command::COMMAND_setIndexBuffer: - currentIndexBuffer.offset = params[0]._size; - currentIndexBuffer.buffer = batch->_buffers.get(params[1]._int); - currentIndexBuffer.type = (Type)params[2]._int; - break; - - case Batch::Command::COMMAND_drawIndexed: - currentIndexBuffer.startIndex = params[0]._int; - currentIndexBuffer.numIndices = params[1]._int; - currentIndexBuffer.primitve = (Primitive)params[2]._int; - captured.emplace_back(currentIndexBuffer); - break; - - case Batch::Command::COMMAND_drawIndexedInstanced: - currentIndexBuffer.startIndex = params[1]._int; - currentIndexBuffer.numIndices = params[2]._int; - currentIndexBuffer.primitve = (Primitive)params[3]._int; - captured.emplace_back(currentIndexBuffer); - break; - - default: - break; - } - }); - } - - - std::string optimizedBinaryFile = basename + "_optimized.bin"; - QFile(binaryFile.c_str()).copy(optimizedBinaryFile.c_str()); - { - storage::FileStorage mappedFile(optimizedBinaryFile.c_str()); - std::set uniqueBuffers; - for (const auto& capturedIndexData : captured) { - if (uniqueBuffers.count(capturedIndexData.buffer)) { - continue; - } - uniqueBuffers.insert(capturedIndexData.buffer); - auto bufferOffset = bufferOffsets[capturedIndexData.buffer]; - auto& buffer = *capturedIndexData.buffer; - const auto& count = capturedIndexData.numIndices; - auto indices = (uint32_t*)buffer.editData(); - optimizer(capturedIndexData.primitve, count / 3, count, indices); - memcpy(mappedFile.mutableData() + bufferOffset, indices, sizeof(uint32_t) * count); - } - } - frameNode[keys::binary] = optimizedBinaryFile; - { - std::string frameJson = frameNode.dump(); - std::string filename = basename + "_optimized.json"; - storage::FileStorage::create(filename.c_str(), frameJson.size(), (const uint8_t*)frameJson.data()); - } -} diff --git a/libraries/gpu/src/gpu/FrameWriter.cpp b/libraries/gpu/src/gpu/FrameWriter.cpp index f348827385..761f37a620 100644 --- a/libraries/gpu/src/gpu/FrameWriter.cpp +++ b/libraries/gpu/src/gpu/FrameWriter.cpp @@ -20,7 +20,7 @@ using json = nlohmann::json; class Serializer { public: - const std::string basename; + const std::string filename; const TextureCapturer textureCapturer; std::unordered_map shaderMap; std::unordered_map programMap; @@ -32,8 +32,11 @@ public: std::unordered_map framebufferMap; std::unordered_map swapchainMap; std::unordered_map queryMap; + std::unordered_set captureTextures; + hfb::Buffer binaryBuffer; + hfb::StorageBuilders ktxBuilders; - Serializer(const std::string& basename, const TextureCapturer& capturer) : basename(basename), textureCapturer(capturer) {} + Serializer(const std::string& basename, const TextureCapturer& capturer) : filename(basename + hfb::EXTENSION), textureCapturer(capturer) {} template static uint32_t getGlobalIndex(const T& value, std::unordered_map& map) { @@ -129,7 +132,7 @@ public: json writeProgram(const ShaderPointer& program); json writeNamedBatchData(const Batch::NamedBatchData& namedData); - json writeCapturableTextures(const Frame& frame); + void findCapturableTextures(const Frame& frame); void writeBinaryBlob(); static std::string toBase64(const std::vector& v); static json writeIrradiance(const SHPointer& irradiance); @@ -146,7 +149,7 @@ public: static json writeTransform(const Transform& t) { return writeMat4(t.getMatrix()); } static json writeCommand(size_t index, const Batch& batch); static json writeSampler(const Sampler& sampler); - static json writeTexture(const TexturePointer& texture); + json writeTexture(const TexturePointer& texture); static json writeFormat(const Stream::FormatPointer& format); static json writeQuery(const QueryPointer& query); static json writeShader(const ShaderPointer& shader); @@ -389,9 +392,17 @@ json Serializer::writeTexture(const TexturePointer& texturePointer) { const auto* storage = texture._storage.get(); const auto* ktxStorage = dynamic_cast(storage); if (ktxStorage) { - result[keys::ktxFile] = ktxStorage->_filename; - } else { - // TODO serialize the backing storage + result[keys::chunk] = 2 + ktxBuilders.size(); + auto filename = ktxStorage->_filename; + ktxBuilders.push_back([=] { + return std::make_shared(filename.c_str()); + }); + } else if (textureCapturer && captureTextures.count(texturePointer) != 0) { + result[keys::chunk] = 2 + ktxBuilders.size(); + auto storage = textureCapturer(texturePointer); + ktxBuilders.push_back([=] { + return storage; + }); } } return result; @@ -673,14 +684,8 @@ json Serializer::writeQuery(const QueryPointer& queryPointer) { return result; } -json Serializer::writeCapturableTextures(const Frame& frame) { - if (!textureCapturer) { - return json::array(); - } - +void Serializer::findCapturableTextures(const Frame& frame) { std::unordered_set writtenRenderbuffers; - std::unordered_set captureTextures; - auto maybeCaptureTexture = [&](const TexturePointer& texture) { // Not a valid texture if (!texture) { @@ -755,20 +760,6 @@ json Serializer::writeCapturableTextures(const Frame& frame) { } } } - - json result = json::array(); - for (const auto& texture : captureTextures) { - if (textureCapturer) { - auto index = textureMap[texture]; - auto layers = std::max(texture->getNumSlices(), 1); - for (uint16 layer = 0; layer < layers; ++layer) { - std::string textureFilename = basename + "." + std::to_string(index) + "." + std::to_string(layer) + ".png"; - textureCapturer(textureFilename, texture, layer); - } - result.push_back(index); - } - } - return result; } void Serializer::writeFrame(const Frame& frame) { @@ -780,7 +771,7 @@ void Serializer::writeFrame(const Frame& frame) { } frameNode[keys::stereo] = writeStereoState(frame.stereoState); - frameNode[keys::capturedTextures] = writeCapturableTextures(frame); + findCapturableTextures(frame); frameNode[keys::frameIndex] = frame.frameIndex; frameNode[keys::view] = writeMat4(frame.view); frameNode[keys::pose] = writeMat4(frame.pose); @@ -797,35 +788,21 @@ void Serializer::writeFrame(const Frame& frame) { // Serialize textures and buffers last, since the maps they use can be populated by some of the above code // Serialize textures - serializeMap(frameNode, keys::textures, textureMap, writeTexture); + serializeMap(frameNode, keys::textures, textureMap, std::bind(&Serializer::writeTexture, this, _1)); // Serialize buffers serializeMap(frameNode, keys::buffers, bufferMap, writeBuffer); - { - std::string frameJson = frameNode.dump(); - std::string filename = basename + ".json"; - storage::FileStorage::create(filename.c_str(), frameJson.size(), (const uint8_t*)frameJson.data()); - } - writeBinaryBlob(); - frameNode[keys::binary] = basename + ".bin"; + + hfb::writeFrame(filename, frameNode.dump(), binaryBuffer, ktxBuilders); } void Serializer::writeBinaryBlob() { const auto buffers = mapToVector(bufferMap); auto accumulator = [](size_t total, const BufferPointer& buffer) { return total + (buffer ? buffer->getSize() : 0); }; size_t totalSize = std::accumulate(buffers.begin(), buffers.end(), (size_t)0, accumulator); - - const auto blobFilename = basename + ".bin"; - QFile file(blobFilename.c_str()); - if (!file.open(QFile::ReadWrite | QIODevice::Truncate)) { - throw std::runtime_error("Unable to open file for writing"); - } - if (!file.resize(totalSize)) { - throw std::runtime_error("Unable to resize file"); - } - - auto mapped = file.map(0, totalSize); + binaryBuffer.resize(totalSize); + auto mapped = binaryBuffer.data(); size_t offset = 0; for (const auto& bufferPointer : buffers) { @@ -838,7 +815,4 @@ void Serializer::writeBinaryBlob() { memcpy(mapped + offset, bufferData, bufferSize); offset += bufferSize; } - if (!file.unmap(mapped)) { - throw std::runtime_error("Unable to unmap file"); - } } diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 5e2485941d..debedf02a5 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -343,6 +343,7 @@ public: class KtxStorage : public Storage { public: + KtxStorage(const storage::StoragePointer& storage); KtxStorage(const std::string& filename); KtxStorage(const cache::FilePointer& file); PixelsPointer getMipFace(uint16 level, uint8 face = 0) const override; @@ -366,6 +367,7 @@ public: static std::vector, std::shared_ptr>> _cachedKtxFiles; static std::mutex _cachedKtxFilesMutex; + storage::StoragePointer _storage; std::string _filename; cache::FilePointer _cacheEntry; std::atomic _minMipLevelAvailable; @@ -543,6 +545,7 @@ public: Size getStoredSize() const; void setStorage(std::unique_ptr& newStorage); + void setKtxBacking(const storage::StoragePointer& storage); void setKtxBacking(const std::string& filename); void setKtxBacking(const cache::FilePointer& cacheEntry); diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index f471baf2c7..a5cea3e60e 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -159,7 +159,31 @@ struct IrradianceKTXPayload { }; const std::string IrradianceKTXPayload::KEY{ "hifi.irradianceSH" }; -KtxStorage::KtxStorage(const cache::FilePointer& cacheEntry) : KtxStorage(cacheEntry->getFilepath()) { +KtxStorage::KtxStorage(const storage::StoragePointer& storage) : _storage(storage) { + auto ktxPointer = ktx::KTX::create(storage); + _ktxDescriptor.reset(new ktx::KTXDescriptor(ktxPointer->toDescriptor())); + if (_ktxDescriptor->images.size() < _ktxDescriptor->header.numberOfMipmapLevels) { + qWarning() << "Bad images found in ktx"; + } + + _offsetToMinMipKV = _ktxDescriptor->getValueOffsetForKey(ktx::HIFI_MIN_POPULATED_MIP_KEY); + if (_offsetToMinMipKV) { + auto data = storage->data() + ktx::KTX_HEADER_SIZE + _offsetToMinMipKV; + _minMipLevelAvailable = *data; + } else { + // Assume all mip levels are available + _minMipLevelAvailable = 0; + } + + // now that we know the ktx, let's get the header info to configure this Texture::Storage: + Format mipFormat = Format::COLOR_BGRA_32; + Format texelFormat = Format::COLOR_SRGBA_32; + if (Texture::evalTextureFormat(_ktxDescriptor->header, mipFormat, texelFormat)) { + _format = mipFormat; + } +} + +KtxStorage::KtxStorage(const cache::FilePointer& cacheEntry) : KtxStorage(cacheEntry->getFilepath()) { _cacheEntry = cacheEntry; } @@ -228,28 +252,31 @@ void KtxStorage::releaseOpenKtxFiles() { PixelsPointer KtxStorage::getMipFace(uint16 level, uint8 face) const { auto faceOffset = _ktxDescriptor->getMipFaceTexelsOffset(level, face); auto faceSize = _ktxDescriptor->getMipFaceTexelsSize(level, face); + storage::StoragePointer storageView; if (faceSize != 0 && faceOffset != 0) { - std::lock_guard lock(*_cacheFileMutex); - auto file = maybeOpenFile(); - if (file) { - auto storageView = file->createView(faceSize, faceOffset); - if (storageView) { - return storageView->toMemoryStorage(); - } else { - qWarning() << "Failed to get a valid storageView for faceSize=" << faceSize << " faceOffset=" << faceOffset << "out of valid file " << QString::fromStdString(_filename); - } + if (_storage) { + storageView = _storage->createView(faceSize, faceOffset); } else { - qWarning() << "Failed to get a valid file out of maybeOpenFile " << QString::fromStdString(_filename); + std::lock_guard lock(*_cacheFileMutex); + auto file = maybeOpenFile(); + if (file) { + storageView = file->createView(faceSize, faceOffset); + } else { + qWarning() << "Failed to get a valid file out of maybeOpenFile " << QString::fromStdString(_filename); + } } } - return nullptr; + if (!storageView) { + qWarning() << "Failed to get a valid storageView for faceSize=" << faceSize << " faceOffset=" << faceOffset + << "out of valid file " << QString::fromStdString(_filename); + } + return storageView->toMemoryStorage(); } Size KtxStorage::getMipFaceSize(uint16 level, uint8 face) const { return _ktxDescriptor->getMipFaceTexelsSize(level, face); } - bool KtxStorage::isMipAvailable(uint16 level, uint8 face) const { return level >= _minMipLevelAvailable; } @@ -271,7 +298,7 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor auto& imageDesc = _ktxDescriptor->images[level]; if (storage->size() != imageDesc._imageSize) { qWarning() << "Invalid image size: " << storage->size() << ", expected: " << imageDesc._imageSize - << ", level: " << level << ", filename: " << QString::fromStdString(_filename); + << ", level: " << level << ", filename: " << QString::fromStdString(_filename); return; } @@ -311,8 +338,7 @@ void KtxStorage::assignMipFaceData(uint16 level, uint8 face, const storage::Stor throw std::runtime_error("Invalid call"); } -bool validKtx(const std::string& filename) { - ktx::StoragePointer storage { new storage::FileStorage(filename.c_str()) }; +bool validKtx(const storage::StoragePointer& storage) { auto ktxPointer = ktx::KTX::create(storage); if (!ktxPointer) { return false; @@ -320,6 +346,21 @@ bool validKtx(const std::string& filename) { return true; } +bool validKtx(const std::string& filename) { + ktx::StoragePointer storage{ new storage::FileStorage(filename.c_str()) }; + return validKtx(storage); +} + +void Texture::setKtxBacking(const storage::StoragePointer& storage) { + // Check the KTX file for validity before using it as backing storage + if (!validKtx(storage)) { + return; + } + + auto newBacking = std::unique_ptr(new KtxStorage(storage)); + setStorage(newBacking); +} + void Texture::setKtxBacking(const std::string& filename) { // Check the KTX file for validity before using it as backing storage if (!validKtx(filename)) { @@ -355,7 +396,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { // Set Dimensions uint32_t numFaces = 1; switch (texture.getType()) { - case TEX_1D: { + case TEX_1D: { if (texture.isArray()) { header.set1DArray(texture.getWidth(), texture.getNumSlices()); } else { @@ -363,7 +404,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { } break; } - case TEX_2D: { + case TEX_2D: { if (texture.isArray()) { header.set2DArray(texture.getWidth(), texture.getHeight(), texture.getNumSlices()); } else { @@ -371,7 +412,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { } break; } - case TEX_3D: { + case TEX_3D: { if (texture.isArray()) { header.set3DArray(texture.getWidth(), texture.getHeight(), texture.getDepth(), texture.getNumSlices()); } else { @@ -379,7 +420,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { } break; } - case TEX_CUBE: { + case TEX_CUBE: { if (texture.isArray()) { header.setCubeArray(texture.getWidth(), texture.getHeight(), texture.getNumSlices()); } else { @@ -388,8 +429,8 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { numFaces = Texture::CUBE_FACE_COUNT; break; } - default: - return nullptr; + default: + return nullptr; } // Number level of mips coming diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index 2ef83e42d8..eb84521f5c 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -20,7 +20,7 @@ size_t Image::getByteCount() const { if (_format == Format_RGBAF) { return sizeof(FloatPixels::value_type) * _floatData.size(); } else { - return _packedData.byteCount(); + return _packedData.sizeInBytes(); } } diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index 0a6c76e456..b1746951bb 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -225,7 +225,7 @@ controller::Input KeyboardMouseDevice::InputDevice::makeInput(KeyboardMouseDevic * *
    NameType

    Description
    NameTypeDescription
    userAnimNonebooleantrue when no user overrideAnimation is @@ -545,7 +545,8 @@ QStringList Rig::getAnimationRoles() const { auto clipNode = std::dynamic_pointer_cast(node); if (clipNode) { // filter out the userAnims, they are for internal use only. - if (!clipNode->getID().startsWith("userAnim")) { + // also don't return additive blend node clips as valid roles. + if (!clipNode->getID().startsWith("userAnim") && clipNode->getBlendType() == AnimBlendType_Normal) { list.append(node->getID()); } } @@ -1432,6 +1433,69 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos } _lastEnableInverseKinematics = _enableInverseKinematics; + + //stategraph vars based on input + const float INPUT_DEADZONE_THRESHOLD = 0.05f; + const float SLOW_SPEED_THRESHOLD = 1.5f; + + if (fabsf(_previousControllerParameters.inputX) <= INPUT_DEADZONE_THRESHOLD && + fabsf(_previousControllerParameters.inputZ) <= INPUT_DEADZONE_THRESHOLD) { + // no WASD input + if (fabsf(forwardSpeed) <= SLOW_SPEED_THRESHOLD && fabsf(lateralSpeed) <= SLOW_SPEED_THRESHOLD) { + _animVars.set("isInputForward", false); + _animVars.set("isInputBackward", false); + _animVars.set("isInputRight", false); + _animVars.set("isInputLeft", false); + _animVars.set("isNotInput", true); + _animVars.set("isNotInputSlow", true); + + } else { + _animVars.set("isInputForward", false); + _animVars.set("isInputBackward", false); + _animVars.set("isInputRight", false); + _animVars.set("isInputLeft", false); + _animVars.set("isNotInput", true); + _animVars.set("isNotInputSlow", false); + } + } else if (fabsf(_previousControllerParameters.inputZ) >= fabsf(_previousControllerParameters.inputX)) { + if (_previousControllerParameters.inputZ > 0.0f) { + // forward + _animVars.set("isInputForward", true); + _animVars.set("isInputBackward", false); + _animVars.set("isInputRight", false); + _animVars.set("isInputLeft", false); + _animVars.set("isNotInput", false); + _animVars.set("isNotInputSlow", false); + } else { + // backward + _animVars.set("isInputForward", false); + _animVars.set("isInputBackward", true); + _animVars.set("isInputRight", false); + _animVars.set("isInputLeft", false); + _animVars.set("isNotInput", false); + _animVars.set("isNotInputSlow", false); + } + } else { + if (_previousControllerParameters.inputX > 0.0f) { + // right + _animVars.set("isInputForward", false); + _animVars.set("isInputBackward", false); + _animVars.set("isInputRight", true); + _animVars.set("isInputLeft", false); + _animVars.set("isNotInput", false); + _animVars.set("isNotInputSlow", false); + } else { + // left + _animVars.set("isInputForward", false); + _animVars.set("isInputBackward", false); + _animVars.set("isInputRight", false); + _animVars.set("isInputLeft", true); + _animVars.set("isNotInput", false); + _animVars.set("isNotInputSlow", false); + } + } + + } _lastForward = forward; _lastPosition = worldPosition; @@ -2160,6 +2224,7 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo } } + _headEnabled = params.primaryControllerFlags[PrimaryControllerType_Head] & (uint8_t)ControllerFlags::Enabled; bool leftHandEnabled = params.primaryControllerFlags[PrimaryControllerType_LeftHand] & (uint8_t)ControllerFlags::Enabled; bool rightHandEnabled = params.primaryControllerFlags[PrimaryControllerType_RightHand] & (uint8_t)ControllerFlags::Enabled; diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 98431e1dca..8570ae4441 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -88,6 +88,8 @@ public: AnimPose secondaryControllerPoses[NumSecondaryControllerTypes]; // rig space uint8_t secondaryControllerFlags[NumSecondaryControllerTypes]; bool isTalking; + float inputX = 0.0f; + float inputZ = 0.0f; bool reactionEnabledFlags[NUM_AVATAR_BEGIN_END_REACTIONS]; bool reactionTriggers[NUM_AVATAR_TRIGGER_REACTIONS]; HFMJointShapeInfo hipsShapeInfo; diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 5e1f285a6c..d29045c99b 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1998,6 +1998,12 @@ void AudioClient::outputNotify() { } } +void AudioClient::noteAwakening() { + qCDebug(audioclient) << "Restarting the audio devices."; + switchInputToAudioDevice(_inputDeviceInfo); + switchOutputToAudioDevice(_outputDeviceInfo); +} + bool AudioClient::switchOutputToAudioDevice(const HifiAudioDeviceInfo outputDeviceInfo, bool isShutdownRequest) { Q_ASSERT_X(QThread::currentThread() == thread(), Q_FUNC_INFO, "Function invoked on wrong thread"); diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 28a4f23968..b4ddb1018e 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -255,6 +255,7 @@ public slots: void setOutputGain(float gain) { _outputGain = gain; }; void outputNotify(); + void noteAwakening(); void loadSettings(); void saveSettings(); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 75a7693de8..bea9f979b8 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -545,7 +545,7 @@ void Avatar::relayJointDataToChildren() { } /**jsdoc - * An avatar has different types of data simulated at different rates, in Hz. + *

    An avatar has different types of data simulated at different rates, in Hz.

    * * * @@ -854,7 +854,8 @@ void Avatar::render(RenderArgs* renderArgs) { float distanceToTarget = glm::length(toTarget); const float DISPLAYNAME_DISTANCE = 20.0f; updateDisplayNameAlpha(distanceToTarget < DISPLAYNAME_DISTANCE); - if (!isMyAvatar() || renderArgs->_cameraMode != (int8_t)CAMERA_MODE_FIRST_PERSON) { + if (!isMyAvatar() || !(renderArgs->_cameraMode == (int8_t)CAMERA_MODE_FIRST_PERSON_LOOK_AT + || renderArgs->_cameraMode == (int8_t)CAMERA_MODE_FIRST_PERSON)) { auto& frustum = renderArgs->getViewFrustum(); auto textPosition = getDisplayNamePosition(); if (frustum.pointIntersectsFrustum(textPosition)) { diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index b969449d5e..610f34ed45 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -470,7 +470,7 @@ public: /**jsdoc * Sets the joint of the entity or avatar that the avatar is parented to. * @function MyAvatar.setParentJointIndex - * @param {number} parentJointIndex - he joint of the entity or avatar that the avatar should be parented to. Use + * @param {number} parentJointIndex - The joint of the entity or avatar that the avatar should be parented to. Use * 65535 or -1 to parent to the entity or avatar's position and orientation rather than a * joint. */ 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/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index a91154ff15..710bfb8d2a 100755 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1445,7 +1445,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { } /**jsdoc - * The avatar mixer data comprises different types of data, with the data rates of each being tracked in kbps. + *

    The avatar mixer data comprises different types of data, with the data rates of each being tracked in kbps.

    * *
    * @@ -1550,7 +1550,7 @@ float AvatarData::getDataRate(const QString& rateName) const { } /**jsdoc - * The avatar mixer data comprises different types of data updated at different rates, in Hz. + *

    The avatar mixer data comprises different types of data updated at different rates, in Hz.

    * *
    * diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 59a2e2a53e..df0783ef4b 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -1277,9 +1277,9 @@ public: * @param {number} [scale=1.0] - The scale to apply to the model. * @param {boolean} [isSoft=false] - If the model has a skeleton, set this to true so that the bones of the * attached model's skeleton are rotated to fit the avatar's current pose. isSoft is used, for example, - * to have clothing that moves with the avatar.
    - * If true, the translation, rotation, and scale parameters are - * ignored. + * to have clothing that moves with the avatar. + *

    If true, the translation, rotation, and scale parameters are + * ignored.

    * @param {boolean} [allowDuplicates=false] - If true then more than one copy of any particular model may be * attached to the same joint; if false then the same model cannot be attached to the same joint. * @param {boolean} [useSaved=true] - Not used. diff --git a/libraries/controllers/src/controllers/Actions.cpp b/libraries/controllers/src/controllers/Actions.cpp index 40011f2682..b6b96216b5 100644 --- a/libraries/controllers/src/controllers/Actions.cpp +++ b/libraries/controllers/src/controllers/Actions.cpp @@ -44,7 +44,7 @@ namespace controller { * * * - * + * * * * * - * + * * * * - * + * * * * - * + * * * * - * + * * * * - * + * * * * - * + * * * * * - * + * * *
    PropertyTypeDataDescription
    Avatar Movement
    Avatar Movement
    TranslateXnumbernumberMove the user's avatar in the direction of its * x-axis, if the camera isn't in independent or mirror modes.
    TranslateYnumbernumberMove the user's avatar in the direction of its @@ -71,7 +71,7 @@ namespace controller { * step increment, if the camera isn't in independent or mirror modes.
    StepRollnumbernumberNo action.
    Avatar Skeleton
    Avatar Skeleton
    Hipsnumber{@link Pose}Set the hips pose of the user's avatar. *
    Spine2number{@link Pose}Set the spine2 pose of the user's avatar. @@ -171,7 +171,7 @@ namespace controller { *
    RightFootnumber{@link Pose}Set the right foot pose of the user's * avatar.
    Application
    Application
    BoomInnumbernumberZoom camera in from third person toward first * person view.
    BoomOutnumbernumberZoom camera out from first person to third @@ -212,7 +212,7 @@ namespace controller { *
    SecondaryActionnumbernumberDeprecated: This * action is deprecated and will be removed. It takes no action.
    Aliases
    Aliases
    BackwardnumbernumberAlias for TranslateZ in the * positive direction.
    ForwardnumbernumberAlias for TranslateZ in the negative @@ -234,7 +234,7 @@ namespace controller { *
    YawRightnumbernumberAlias for Yaw in the negative * direction.
    Deprecated Aliases
    Deprecated Aliases
    LEFT_HANDnumber{@link Pose}Deprecated: This * action is deprecated and will be removed. Use LeftHand instead.
    RIGHT_HANDnumber{@link Pose}Deprecated: This @@ -282,7 +282,7 @@ namespace controller { *
    ACTION2numbernumberDeprecated: This * action is deprecated and will be removed. Use SecondaryAction instead.
    Deprecated Trackers
    Deprecated Trackers
    TrackedObject00number{@link Pose}Deprecated: * This action is deprecated and will be removed. It takes no action.
    TrackedObject01number{@link Pose}Deprecated: @@ -335,8 +335,8 @@ namespace controller { makeAxisPair(Action::STEP_PITCH, "StepPitch"), makeAxisPair(Action::STEP_ROLL, "StepRoll"), makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateX"), - makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateY"), - makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateZ"), + makeAxisPair(Action::STEP_TRANSLATE_Y, "StepTranslateY"), + makeAxisPair(Action::STEP_TRANSLATE_Z, "StepTranslateZ"), makePosePair(Action::LEFT_HAND, "LeftHand"), makePosePair(Action::RIGHT_HAND, "RightHand"), diff --git a/libraries/controllers/src/controllers/StandardController.cpp b/libraries/controllers/src/controllers/StandardController.cpp index dbc92cc7e5..ae592485dc 100644 --- a/libraries/controllers/src/controllers/StandardController.cpp +++ b/libraries/controllers/src/controllers/StandardController.cpp @@ -33,7 +33,7 @@ void StandardController::focusOutEvent() { * identifying each output. Read-only.

    *

    These outputs can be mapped to actions or functions in a {@link RouteObject} mapping. The data value provided by each * control is either a number or a {@link Pose}. Numbers are typically normalized to 0.0 or 1.0 for - * button states, the range 0.0 – 1.0 for unidirectional scales, and the range + * button states, the range 0.01.0 for unidirectional scales, and the range * -1.01.0 for bidirectional scales.

    *

    Each hardware device has a mapping from its outputs to a subset of Controller.Standard items, specified in a * JSON file. For example, @@ -118,8 +118,7 @@ void StandardController::focusOutEvent() { * button.

    RightThumbUpnumbernumberRight thumb not touching primary or secondary * thumb buttons.
    LeftPrimaryIndexnumbernumberLeft primary index control - * pressed.
    LeftPrimaryIndexnumbernumberLeft primary index control pressed.
    LeftSecondaryIndexnumbernumberLeft secondary index control pressed. *
    RightPrimaryIndexnumbernumberRight primary index control pressed. diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h index f0a823a3de..5a8fd3083d 100644 --- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h @@ -86,7 +86,7 @@ class UserInputMapper; * @typedef {object} Controller.MappingJSONRoute * @property {string|Controller.MappingJSONAxis} from - The name of a {@link Controller.Hardware} property or an axis made from * them. If a property name, the leading "Controller.Hardware." can be omitted. - * @property {boolean} [peek=false] - If true, then peeking is enabled per {@link RouteObject#peek}. + * @property {boolean} [peek=false] - If true, then peeking is enabled per {@link RouteObject#peek}. * @property {boolean} [debug=false] - If true, 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 a property name with a ! to do a logical NOT. The leading @@ -135,8 +135,8 @@ public: /**jsdoc * Creates a new {@link RouteObject} from a controller output, ready to be mapped to a standard control, action, or - * function.
    - * This is a QML-specific version of {@link MappingObject#from|from}: use this version in QML files. + * function. + *

    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. @@ -146,8 +146,8 @@ public: /**jsdoc * Creates a new {@link RouteObject} from two numeric {@link Controller.Hardware} outputs, one applied in the negative - * direction and the other in the positive direction, ready to be mapped to a standard control, action, or function.
    - * This is a QML-specific version of {@link MappingObject#makeAxis|makeAxis}: use this version in QML files. + * direction and the other in the positive direction, ready to be mapped to a standard control, action, or function. + *

    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. @@ -189,8 +189,8 @@ public: Q_INVOKABLE QObject* makeAxis(const QScriptValue& source1, const QScriptValue& source2); /**jsdoc - * Enables or disables the mapping. When enabled, the routes in the mapping take effect.
    - * Synonymous with {@link Controller.enableMapping}. + * Enables or disables the mapping. When enabled, the routes in the mapping take effect. + *

    Synonymous with {@link Controller.enableMapping}.

    * @function MappingObject#enable * @param {boolean} enable=true - If true then the mapping is enabled, otherwise it is disabled. * @returns {MappingObject} The mapping object, so that further routes can be added. @@ -198,8 +198,8 @@ public: Q_INVOKABLE QObject* enable(bool enable = true); /**jsdoc - * Disables the mapping. When disabled, the routes in the mapping have no effect.
    - * Synonymous with {@link Controller.disableMapping}. + * Disables the mapping. When disabled, the routes in the mapping have no effect. + *

    Synonymous with {@link Controller.disableMapping}.

    * @function MappingObject#disable * @returns {MappingObject} The mapping object, so that further routes can be added. */ diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h index e7ff04d72c..f1b36cfec5 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h @@ -52,8 +52,8 @@ class RouteBuilderProxy : public QObject { /**jsdoc * Terminates the route with a standard control, an action, or a script function. The output value from the route is - * sent to the specified destination.
    - * This is a QML-specific version of {@link MappingObject#to|to}: use this version in QML files. + * sent to the specified destination. + *

    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 @@ -64,8 +64,8 @@ class RouteBuilderProxy : public QObject { /**jsdoc * Processes the route only if a condition is satisfied. The condition is evaluated before the route input is read, and * the input is read only if the condition is true. Thus, if the condition is not met then subsequent - * routes using the same input are processed.
    - * This is a QML-specific version of {@link MappingObject#to|to}: use this version in QML files. + * routes using the same input are processed. + *

    This is a QML-specific version of {@link MappingObject#when|when}: use this version in QML files.

    * @function RouteObject#whenQml * @param {condition|condition[]} expression -

    A condition may be a:

    *
      diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp index d3cb602e5b..a1138b3018 100644 --- a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp +++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp @@ -15,10 +15,10 @@ #include #include -#include -#include +#include +#include #include -#include +#include #include #include @@ -177,9 +177,35 @@ QPointF CompositorHelper::getMouseEventPosition(QMouseEvent* event) { return event->localPos(); } +static bool isWindowActive() { + for (const auto& window : QGuiApplication::topLevelWindows()) { + if (window->isActive()) { + return true; + } + } + return false; +} + bool CompositorHelper::shouldCaptureMouse() const { + if (!_allowMouseCapture) { + return false; + } + + if (!isHMD()) { + return false; + } + + + if (!isWindowActive()) { + return false; + } + + if (ui::Menu::isSomeSubmenuShown()) { + return false; + } + // if we're in HMD mode, and some window of ours is active, but we're not currently showing a popup menu - return _allowMouseCapture && isHMD() && QApplication::activeWindow() && !ui::Menu::isSomeSubmenuShown(); + return true; } void CompositorHelper::setAllowMouseCapture(bool capture) { @@ -206,9 +232,9 @@ void CompositorHelper::handleLeaveEvent() { mainWidgetFrame.moveTopLeft(topLeftScreen); } QRect uncoveredRect = mainWidgetFrame; - foreach(QWidget* widget, QApplication::topLevelWidgets()) { - if (widget->isWindow() && widget->isVisible() && widget != mainWidget) { - QRect widgetFrame = widget->frameGeometry(); + for(QWindow* window : QGuiApplication::topLevelWindows()) { + if (window->isVisible() && window != mainWidget->windowHandle()) { + QRect widgetFrame = window->frameGeometry(); if (widgetFrame.intersects(uncoveredRect)) { QRect intersection = uncoveredRect & widgetFrame; if (intersection.top() > uncoveredRect.top()) { @@ -292,7 +318,7 @@ glm::vec2 CompositorHelper::getReticleMaximumPosition() const { if (isHMD()) { result = VIRTUAL_SCREEN_SIZE; } else { - QRect rec = QApplication::desktop()->screenGeometry(); + QRect rec = QGuiApplication::primaryScreen()->geometry(); result = glm::vec2(rec.right(), rec.bottom()); } return result; @@ -308,8 +334,8 @@ void CompositorHelper::sendFakeMouseEvent() { // in HMD mode we need to fake our mouse moves... QPoint globalPos(_reticlePositionInHMD.x, _reticlePositionInHMD.y); auto button = Qt::NoButton; - auto buttons = QApplication::mouseButtons(); - auto modifiers = QApplication::keyboardModifiers(); + auto buttons = QGuiApplication::mouseButtons(); + auto modifiers = QGuiApplication::keyboardModifiers(); QMouseEvent event(QEvent::MouseMove, globalPos, button, buttons, modifiers); _fakeMouseEvent = true; qApp->sendEvent(_renderingWidget, &event); diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 74d36101db..f601b99779 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -36,6 +37,7 @@ #include #include #include +#include #include #include @@ -371,7 +373,7 @@ void OpenGLDisplayPlugin::customizeContext() { auto usage = gpu::Texture::Usage::Builder().withColor().withAlpha(); cursorData.texture->setUsage(usage.build()); cursorData.texture->setStoredMipFormat(gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); - cursorData.texture->assignStoredMip(0, image.byteCount(), image.constBits()); + cursorData.texture->assignStoredMip(0, image.sizeInBytes(), image.constBits()); cursorData.texture->setAutoGenerateMips(true); } } @@ -475,30 +477,48 @@ void OpenGLDisplayPlugin::submitFrame(const gpu::FramePointer& newFrame) { }); } +ktx::StoragePointer textureToKtx(const gpu::Texture& texture) { + ktx::Header header; + { + auto gpuDims = texture.getDimensions(); + header.pixelWidth = gpuDims.x; + header.pixelHeight = gpuDims.y; + header.pixelDepth = 0; + } + + { + auto gltexelformat = gpu::gl::GLTexelFormat::evalGLTexelFormat(texture.getStoredMipFormat()); + header.glInternalFormat = gltexelformat.internalFormat; + header.glFormat = gltexelformat.format; + header.glBaseInternalFormat = gltexelformat.format; + header.glType = gltexelformat.type; + header.glTypeSize = 1; + header.numberOfMipmapLevels = 1 + texture.getMaxMip(); + } + + auto memKtx = ktx::KTX::createBare(header); + auto storage = memKtx->_storage; + uint32_t faceCount = std::max(header.numberOfFaces, 1u); + uint32_t mipCount = std::max(header.numberOfMipmapLevels, 1u); + for (uint32_t mip = 0; mip < mipCount; ++mip) { + for (uint32_t face = 0; face < faceCount; ++face) { + const auto& image = memKtx->_images[mip]; + auto& faceBytes = const_cast(image._faceBytes[face]); + if (texture.isStoredMipFaceAvailable(mip, face)) { + auto storedImage = texture.accessStoredMipFace(mip, face); + auto storedSize = storedImage->size(); + memcpy(faceBytes, storedImage->data(), storedSize); + } + } + } + return storage; +} + void OpenGLDisplayPlugin::captureFrame(const std::string& filename) const { withOtherThreadContext([&] { using namespace gpu; - auto glBackend = const_cast(*this).getGLBackend(); - FramebufferPointer framebuffer{ Framebuffer::create("captureFramebuffer") }; - TextureCapturer captureLambda = [&](const std::string& filename, const gpu::TexturePointer& texture, uint16 layer) { - QImage image; - if (texture->getUsageType() == TextureUsageType::STRICT_RESOURCE) { - image = QImage{ 1, 1, QImage::Format_ARGB32 }; - auto storedImage = texture->accessStoredMipFace(0, 0); - memcpy(image.bits(), storedImage->data(), image.sizeInBytes()); - //if (texture == textureCache->getWhiteTexture()) { - //} else if (texture == textureCache->getBlackTexture()) { - //} else if (texture == textureCache->getBlueTexture()) { - //} else if (texture == textureCache->getGrayTexture()) { - } else { - ivec4 rect = { 0, 0, texture->getWidth(), texture->getHeight() }; - framebuffer->setRenderBuffer(0, texture, layer); - glBackend->syncGPUObject(*framebuffer); - - image = QImage{ rect.z, rect.w, QImage::Format_ARGB32 }; - glBackend->downloadFramebuffer(framebuffer, rect, image); - } - QImageWriter(filename.c_str()).write(image); + TextureCapturer captureLambda = [&](const gpu::TexturePointer& texture)->storage::StoragePointer { + return textureToKtx(*texture); }; if (_currentFrame) { diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index 722ab341f7..0c8b661980 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -290,7 +290,7 @@ void HmdDisplayPlugin::internalPresent() { _previewTexture->setSource("HMD Preview Texture"); _previewTexture->setUsage(gpu::Texture::Usage::Builder().withColor().build()); _previewTexture->setStoredMipFormat(gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); - _previewTexture->assignStoredMip(0, image.byteCount(), image.constBits()); + _previewTexture->assignStoredMip(0, image.sizeInBytes(), image.constBits()); _previewTexture->setAutoGenerateMips(true); auto viewport = getViewportForSourceSize(uvec2(_previewTexture->getDimensions())); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 3837be5c9c..ab3f4c5243 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); @@ -153,47 +168,52 @@ void EntityTreeRenderer::resetEntitiesScriptEngine() { entityScriptingInterface->setEntitiesScriptEngine(entitiesScriptEngineProvider); // Connect mouse events to entity script callbacks - connect(entityScriptingInterface.data(), &EntityScriptingInterface::mousePressOnEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { - _entitiesScriptEngine->callEntityScriptMethod(entityID, "mousePressOnEntity", event); - }); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseDoublePressOnEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { - _entitiesScriptEngine->callEntityScriptMethod(entityID, "mouseDoublePressOnEntity", event); - }); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseMoveOnEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { - _entitiesScriptEngine->callEntityScriptMethod(entityID, "mouseMoveOnEntity", event); - // FIXME: this is a duplicate of mouseMoveOnEntity, but it seems like some scripts might use this naming - _entitiesScriptEngine->callEntityScriptMethod(entityID, "mouseMoveEvent", event); - }); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseReleaseOnEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { - _entitiesScriptEngine->callEntityScriptMethod(entityID, "mouseReleaseOnEntity", event); - }); + if (!_mouseAndPreloadSignalHandlersConnected) { + + connect(entityScriptingInterface.data(), &EntityScriptingInterface::mousePressOnEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { + _entitiesScriptEngine->callEntityScriptMethod(entityID, "mousePressOnEntity", event); + }); + connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseDoublePressOnEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { + _entitiesScriptEngine->callEntityScriptMethod(entityID, "mouseDoublePressOnEntity", event); + }); + connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseMoveOnEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { + _entitiesScriptEngine->callEntityScriptMethod(entityID, "mouseMoveOnEntity", event); + // FIXME: this is a duplicate of mouseMoveOnEntity, but it seems like some scripts might use this naming + _entitiesScriptEngine->callEntityScriptMethod(entityID, "mouseMoveEvent", event); + }); + connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseReleaseOnEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { + _entitiesScriptEngine->callEntityScriptMethod(entityID, "mouseReleaseOnEntity", event); + }); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickDownOnEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { - _entitiesScriptEngine->callEntityScriptMethod(entityID, "clickDownOnEntity", event); - }); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::holdingClickOnEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { - _entitiesScriptEngine->callEntityScriptMethod(entityID, "holdingClickOnEntity", event); - }); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickReleaseOnEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { - _entitiesScriptEngine->callEntityScriptMethod(entityID, "clickReleaseOnEntity", event); - }); + connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickDownOnEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { + _entitiesScriptEngine->callEntityScriptMethod(entityID, "clickDownOnEntity", event); + }); + connect(entityScriptingInterface.data(), &EntityScriptingInterface::holdingClickOnEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { + _entitiesScriptEngine->callEntityScriptMethod(entityID, "holdingClickOnEntity", event); + }); + connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickReleaseOnEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { + _entitiesScriptEngine->callEntityScriptMethod(entityID, "clickReleaseOnEntity", event); + }); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { - _entitiesScriptEngine->callEntityScriptMethod(entityID, "hoverEnterEntity", event); - }); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverOverEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { - _entitiesScriptEngine->callEntityScriptMethod(entityID, "hoverOverEntity", event); - }); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { - _entitiesScriptEngine->callEntityScriptMethod(entityID, "hoverLeaveEntity", event); - }); + connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { + _entitiesScriptEngine->callEntityScriptMethod(entityID, "hoverEnterEntity", event); + }); + connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverOverEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { + _entitiesScriptEngine->callEntityScriptMethod(entityID, "hoverOverEntity", event); + }); + connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { + _entitiesScriptEngine->callEntityScriptMethod(entityID, "hoverLeaveEntity", event); + }); - connect(_entitiesScriptEngine.data(), &ScriptEngine::entityScriptPreloadFinished, [&](const EntityItemID& entityID) { - EntityItemPointer entity = getTree()->findEntityByID(entityID); - if (entity) { - entity->setScriptHasFinishedPreload(true); - } - }); + connect(_entitiesScriptEngine.data(), &ScriptEngine::entityScriptPreloadFinished, [&](const EntityItemID& entityID) { + EntityItemPointer entity = getTree()->findEntityByID(entityID); + if (entity) { + entity->setScriptHasFinishedPreload(true); + } + }); + + _mouseAndPreloadSignalHandlersConnected = true; + } } void EntityTreeRenderer::stopDomainAndNonOwnedEntities() { @@ -800,15 +820,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-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index b9fda690dd..6dbaedc123 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -210,6 +210,8 @@ private: std::function _getPrevRayPickResultOperator; std::function _setPrecisionPickingOperator; + bool _mouseAndPreloadSignalHandlersConnected { false }; + class LayeredZone { public: LayeredZone(std::shared_ptr zone) : zone(zone), id(zone->getID()), volume(zone->getVolumeEstimate()) {} diff --git a/libraries/entities/src/EntityDynamicInterface.cpp b/libraries/entities/src/EntityDynamicInterface.cpp index 69d771db22..9c73217bef 100644 --- a/libraries/entities/src/EntityDynamicInterface.cpp +++ b/libraries/entities/src/EntityDynamicInterface.cpp @@ -130,8 +130,8 @@ variables. These argument variables are used by the code which is run when bull *
    "ball-socket"Object constraintConnects two entities with a ball and socket joint.{@link Entities.ActionArguments-BallSocket}
    "spring"Synonym for "tractor". - * Deprecated.
    "spring" Synonym for "tractor". + *

    Deprecated.

     
    * @typedef {string} Entities.ActionType diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 5437ceaab8..2b8f2b4c14 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 @@ -944,7 +940,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @property {number} materialMappingRot=0 - How much to rotate the material within the parent's UV-space, in degrees. * @property {boolean} materialRepeat=true - true if the material repeats, false if it doesn't. If * false, fragments outside of texCoord 0 – 1 will be discarded. Works in both "uv" and - * "projected" modes. + * "projected" modes. * @example
    Color a sphere using a Material entity.Report the number of entities in view.
    {@link Entities.EntityProperties-Shape|EntityProperties-Shape}
    "Box"A rectangular prism. This is a synonym of "Shape" for the case - * where the entity's shape property value is "Cube".
    - * If an entity is created with its type + * where the entity's shape property value is "Cube". + *

    If an entity is created with its type * set to "Box" it will always be created with a shape property value of * "Cube". If an entity of type Shape or Sphere has its shape set - * to "Cube" then its type will be reported as "Box". + * to "Cube" then its type will be reported as "Box".

    {@link Entities.EntityProperties-Box|EntityProperties-Box}
    "Sphere"A sphere. This is a synonym of "Shape" for the case - * where the entity's shape property value is "Sphere".
    - * If an entity is created with its type + * where the entity's shape property value is "Sphere". + *

    If an entity is created with its type * set to "Sphere" it will always be created with a shape property value of * "Sphere". If an entity of type Box or Shape has its shape set - * to "Sphere" then its type will be reported as "Sphere". + * to "Sphere" then its type will be reported as "Sphere".

    {@link Entities.EntityProperties-Sphere|EntityProperties-Sphere}
    "Model"A mesh model from a glTF, FBX, or OBJ file.{@link Entities.EntityProperties-Model|EntityProperties-Model}
    * - * + * * * * * + * is the number of units rotated (typically 1.0). + *

    Warning: The mouse wheel in an ordinary mouse generates left/right wheel events instead of + * up/down.

    + * *
    + * is the number of units rotated (typically 1.0). + *

    Warning: The mouse wheel in an ordinary mouse generates left/right wheel events instead of + * up/down.

    + * *
    * * // Receiving script. * var channelName = "com.highfidelity.example.messages-example"; @@ -105,10 +105,10 @@ public: * @param {object} data - The data to send. The data is handled as a byte stream, for example, as may be provided via a * JavaScript Int8Array object. * @param {boolean} [localOnly=false] - If false then the message is sent to all Interface, client entity, - * server entity, and assignment client scripts in the domain.
    - * If true then: if sent from an Interface or client entity script it is received by all Interface and + * server entity, and assignment client scripts in the domain. + *

    If true then: if sent from an Interface or client entity script it is received by all Interface and * client entity scripts; if sent from a server entity script it is received by all entity server scripts; and if sent - * from an assignment client script it is received only by that same assignment client script. + * from an assignment client script it is received only by that same assignment client script.

    * @example
    * // Receiving script. * var channelName = "com.highfidelity.example.messages-example"; diff --git a/libraries/networking/src/udt/Socket.cpp b/libraries/networking/src/udt/Socket.cpp index 2d99f3649b..017855d628 100644 --- a/libraries/networking/src/udt/Socket.cpp +++ b/libraries/networking/src/udt/Socket.cpp @@ -31,6 +31,14 @@ using namespace udt; +#ifdef WIN32 +#include +#include +#else +#include +#endif + + Socket::Socket(QObject* parent, bool shouldChangeSocketOptions) : QObject(parent), _udpSocket(parent), diff --git a/libraries/plugins/src/plugins/SteamClientPlugin.h b/libraries/plugins/src/plugins/SteamClientPlugin.h index 2124d16b5e..07e320f8eb 100644 --- a/libraries/plugins/src/plugins/SteamClientPlugin.h +++ b/libraries/plugins/src/plugins/SteamClientPlugin.h @@ -41,13 +41,16 @@ public: }; /**jsdoc + * The Steam API provides facilities for working with the Steam version of Interface. + * * @namespace Steam * * @hifi-interface * @hifi-client-entity * @hifi-avatar * - * @property {boolean} running - Read-only. + * @property {boolean} running - true if Interface is running under Steam, false if it isn't. + * Read-only. */ class SteamScriptingInterface : public QObject { @@ -61,13 +64,22 @@ public: public slots: /**jsdoc + * Gets whether Interface is running under Steam. * @function Steam.isRunning - * @returns {boolean} + * @returns {boolean} true if Interface is running under Steam, false if it isn't. */ bool isRunning() const { return _plugin && _plugin->isRunning(); } /**jsdoc + * Opens Steam's "Choose Friends to invite" dialog if Interface is running under Steam. * @function Steam.openInviteOverlay + * @example + * if (Steam.running) { + * print("Invite Steam friends to joint you..."); + * Steam.openInviteOverlay(); + * } else { + * print("Interface isn't running under Steam."); + * } */ void openInviteOverlay() const { if (_plugin) { _plugin->openInviteOverlay(); } } diff --git a/libraries/render-utils/src/text/Font.cpp b/libraries/render-utils/src/text/Font.cpp index f9ca9d4cae..5cb5709252 100644 --- a/libraries/render-utils/src/text/Font.cpp +++ b/libraries/render-utils/src/text/Font.cpp @@ -258,7 +258,7 @@ void Font::read(QIODevice& in) { _texture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_POINT_MAG_LINEAR)); _texture->setStoredMipFormat(formatMip); - _texture->assignStoredMip(0, image.byteCount(), image.constBits()); + _texture->assignStoredMip(0, image.sizeInBytes(), image.constBits()); } void Font::setupGPU() { 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/FileScriptingInterface.h b/libraries/script-engine/src/FileScriptingInterface.h index 859f343ec5..ce93921403 100644 --- a/libraries/script-engine/src/FileScriptingInterface.h +++ b/libraries/script-engine/src/FileScriptingInterface.h @@ -17,6 +17,8 @@ #include /**jsdoc + * The File API provides some facilities for working with the file system. + * * @namespace File * * @hifi-interface @@ -35,37 +37,69 @@ public: public slots: /**jsdoc + * Extracts a filename from a URL, where the filename is specified in the query part of the URL as filename=. * @function File.convertUrlToPath - * @param {string} url - * @returns {string} + * @param {string} url - The URL to extract the filename from. + * @returns {string} The filename specified in the URL; an empty string if no filename is specified. + * @example + * var url = "http://domain.tld/path/page.html?filename=file.ext"; + * print("File name: " + File.convertUrlToPath(url)); // file.ext */ QString convertUrlToPath(QUrl url); /**jsdoc + * Unzips a file in the local file system to a new, unique temporary directory. * @function File.runUnzip - * @param {string} path - * @param {string} url - * @param {boolean} autoAdd - * @param {boolean} isZip - * @param {boolean} isBlocks + * @param {string} path - The path of the zip file in the local file system. May have a leading "file:///". + * Need not have a ".zip" extension if it is in a temporary directory (as created by + * {@link File.getTempDir|getTempDir}). + * @param {string} url - Not used. + * @param {boolean} autoAdd - Not used by user scripts. The value is simply passed through to the + * {@link File.unzipResult|unzipResult} signal. + * @param {boolean} isZip - Set to true if path has a ".zip" extension, + * false if it doesn't (but should still be treated as a zip file). + * @param {boolean} isBlocks - Not used by user scripts. The value is simply passed through to the + * {@link File.unzipResult|unzipResult} signal. + * @example + * File.unzipResult.connect(function (zipFile, unzipFiles, autoAdd, isZip, isBlocks) { + * print("File.unzipResult()"); + * print("- zipFile: " + zipFile); + * print("- unzipFiles(" + unzipFiles.length + "): " + unzipFiles); + * print("- autoAdd: " + autoAdd); + * print("- isZip: " + isZip); + * print("- isBlocks: " + isBlocks); + * }); + * + * var zipFile = Window.browse("Select a Zip File", "", "*.zip"); + * if (zipFile) { + * File.runUnzip(zipFile, "", false, true, false); + * } else { + * print("Zip file not selected."); + * } */ void runUnzip(QString path, QUrl url, bool autoAdd, bool isZip, bool isBlocks); /**jsdoc + * Creates a new, unique directory for temporary use. * @function File.getTempDir - * @returns {string} + * @returns {string} The path of the newly created temporary directory. + * @example + * print("New temporary directory: " + File.getTempDir()); */ QString getTempDir(); signals: /**jsdoc + * Triggered when {@link File.runUnzip|runUnzip} completes. * @function File.unzipResult - * @param {string} zipFile - * @param {string} unzipFile - * @param {boolean} autoAdd - * @param {boolean} isZip - * @param {boolean} isBlocks + * @param {string} zipFile - The file that was unzipped. + * @param {string[]} unzipFiles - The paths of the unzipped files in a newly created temporary directory. Includes entries + * for any subdirectories created. An empty array if the zipFile could not be unzipped. + * @param {boolean} autoAdd - The value that {@link File.runUnzip|runUnzip} was called with. + * @param {boolean} isZip - true if {@link File.runUnzip|runUnzip} was called with isZip == true, + * unless there is no FBX or OBJ file in the unzipped file(s) in which case the value is false. + * @param {boolean} isBlocks - The value that {@link File.runUnzip|runUnzip} was called with. * @returns {Signal} */ void unzipResult(QString zipFile, QStringList unzipFile, bool autoAdd, bool isZip, bool isBlocks); 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 + * 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 + * 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 + * 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 + * 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 + * 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 + * 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 + * 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 + * 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 + * 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 + * 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
    + * 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 + * 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/libraries/script-engine/src/Quat.h b/libraries/script-engine/src/Quat.h index 0a5e58ac26..44abe62b24 100644 --- a/libraries/script-engine/src/Quat.h +++ b/libraries/script-engine/src/Quat.h @@ -223,7 +223,7 @@ public slots: /**jsdoc * Gets the "front" direction that the camera would face if its orientation was set to the quaternion value. * This is a synonym for {@link Quat(0).getForward|Quat.getForward}. - * The High Fidelity camera has axes x = right, y = up, -z = forward. + * The High Fidelity camera has axes x = right, y = up, -z = forward. * @function Quat(0).getFront * @param {Quat} orientation - A quaternion representing an orientation. * @returns {Vec3} The negative z-axis rotated by orientation. @@ -233,7 +233,7 @@ public slots: /**jsdoc * Gets the "forward" direction that the camera would face if its orientation was set to the quaternion value. * This is a synonym for {@link Quat(0).getFront|Quat.getFront}. - * The High Fidelity camera has axes x = right, y = up, -z = forward. + * The High Fidelity camera has axes x = right, y = up, -z = forward. * @function Quat(0).getForward * @param {Quat} orientation - A quaternion representing an orientation. * @returns {Vec3} The negative z-axis rotated by orientation. @@ -245,7 +245,7 @@ public slots: /**jsdoc * Gets the "right" direction that the camera would have if its orientation was set to the quaternion value. - * The High Fidelity camera has axes x = right, y = up, -z = forward. + * The High Fidelity camera has axes x = right, y = up, -z = forward. * @function Quat(0).getRight * @param {Quat} orientation - A quaternion representing an orientation. * @returns {Vec3} The x-axis rotated by orientation. @@ -254,7 +254,7 @@ public slots: /**jsdoc * Gets the "up" direction that the camera would have if its orientation was set to the quaternion value. - * The High Fidelity camera has axes x = right, y = up, -z = forward. + * The High Fidelity camera has axes x = right, y = up, -z = forward. * @function Quat(0).getUp * @param {Quat} orientation - A quaternion representing an orientation. * @returns {Vec3} The y-axis rotated by orientation. @@ -405,8 +405,9 @@ public slots: void print(const QString& label, const glm::quat& q, bool asDegrees = false); /**jsdoc - * Tests whether two quaternions are equal. Note: The quaternions must be exactly equal in order for - * true to be returned; it is often better to use {@link Quat(0).dot|Quat.dot} and test for closeness to +/-1. + * Tests whether two quaternions are equal. + *

    Note: The quaternions must be exactly equal in order for true to be returned; it is + * often better to use {@link Quat(0).dot|Quat.dot} and test for closeness to +/-1.

    * @function Quat(0).equal * @param {Quat} q1 - The first quaternion. * @param {Quat} q2 - The second quaternion. diff --git a/libraries/script-engine/src/RecordingScriptingInterface.cpp b/libraries/script-engine/src/RecordingScriptingInterface.cpp index f95edb482b..cbcf94662e 100644 --- a/libraries/script-engine/src/RecordingScriptingInterface.cpp +++ b/libraries/script-engine/src/RecordingScriptingInterface.cpp @@ -64,6 +64,11 @@ void RecordingScriptingInterface::playClip(NetworkClipLoaderPointer clipLoader, } void RecordingScriptingInterface::loadRecording(const QString& url, QScriptValue callback) { + if (QThread::currentThread() != thread()) { + BLOCKING_INVOKE_METHOD(this, "loadRecording", Q_ARG(const QString&, url), Q_ARG(QScriptValue, callback)); + return; + } + auto clipLoader = DependencyManager::get()->getClipLoader(url); if (clipLoader->isLoaded()) { @@ -117,6 +122,11 @@ void RecordingScriptingInterface::startPlaying() { } void RecordingScriptingInterface::setPlayerVolume(float volume) { + if (QThread::currentThread() != thread()) { + BLOCKING_INVOKE_METHOD(this, "setPlayerVolume", Q_ARG(float, volume)); + return; + } + _player->setVolume(std::min(std::max(volume, 0.0f), 1.0f)); } @@ -137,6 +147,11 @@ void RecordingScriptingInterface::setPlayFromCurrentLocation(bool playFromCurren } void RecordingScriptingInterface::setPlayerLoop(bool loop) { + if (QThread::currentThread() != thread()) { + BLOCKING_INVOKE_METHOD(this, "setPlayerLoop", Q_ARG(bool, loop)); + return; + } + _player->loop(loop); } @@ -195,6 +210,16 @@ void RecordingScriptingInterface::startRecording() { } void RecordingScriptingInterface::stopRecording() { + if (!_recorder->isRecording()) { + qCWarning(scriptengine) << "Recorder is not running"; + return; + } + + if (QThread::currentThread() != thread()) { + BLOCKING_INVOKE_METHOD(this, "stopRecording"); + return; + } + _recorder->stop(); _lastClip = _recorder->getClip(); _lastClip->seek(0); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 8337d1911a..4cf4a9fc42 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -1027,8 +1027,8 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& }; /**jsdoc - * The name of an entity event. When the entity event occurs, any function that has been registered for that event via - * {@link Script.addEventHandler} is called with parameters per the entity event. + *

    The name of an entity event. When the entity event occurs, any function that has been registered for that event via + * {@link Script.addEventHandler} is called with parameters per the entity event.

    *
    PropertyTypeDataDescription
    PropertyTypeDataDescription
    09numbernumberA "0" – "1" key on the @@ -275,13 +275,15 @@ controller::Input KeyboardMouseDevice::InputDevice::makeInput(KeyboardMouseDevic *
    MouseWheelLeftnumbernumberThe mouse wheel rotated left. The data value * is the number of units rotated (typically 1.0).
    MouseWheelUpnumbernumberThe mouse wheel rotated up. The data value - * is the number of units rotated (typically 1.0).
    - * Warning: The mouse wheel in an ordinary mouse generates left/right wheel events instead of - * up/down.
    MouseWheelDownnumbernumberThe mouse wheel rotated down. The data value - * is the number of units rotated (typically 1.0).
    - * Warning: The mouse wheel in an ordinary mouse generates left/right wheel events instead of - * up/down.
    TouchpadRightnumbernumberThe average touch on a touch-enabled device * moved right. The data value is how far the average position of all touch points moved.
    TouchpadLeftnumbernumberThe average touch on a touch-enabled device diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index d755a482e3..0165113ec5 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -96,7 +96,7 @@ namespace ktx { using GLBaseInternalFormat = khronos::gl::texture::BaseInternalFormat; using Storage = storage::Storage; - using StoragePointer = std::shared_ptr; + using StoragePointer = std::shared_ptr; struct ImageDescriptor; using ImageDescriptors = std::vector; diff --git a/libraries/midi/src/Midi.h b/libraries/midi/src/Midi.h index dd2b5fd678..5b7c82d6da 100644 --- a/libraries/midi/src/Midi.h +++ b/libraries/midi/src/Midi.h @@ -23,7 +23,7 @@ /**jsdoc * The Midi API provides the ability to connect Interface with musical instruments and other external or virtual * devices via the MIDI protocol. For further information and examples, see the tutorial: - * Use MIDI to Control Your Environment.

    + * Use MIDI to Control Your Environment. * *

    Note: Only works on Windows.

    * diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index 7e76df7622..5473f1a010 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -808,6 +808,8 @@ void AccountManager::requestAccountSettings() { return; } + qCDebug(networking) << "Requesting the Account Settings from the Metaverse API"; + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QUrl lockerURL = _authURL; @@ -834,6 +836,9 @@ void AccountManager::requestAccountSettingsFinished() { if (rootObject.contains("status") && rootObject["status"].toString() == "success") { if (rootObject.contains("data") && rootObject["data"].isObject()) { _settings.unpack(rootObject["data"].toObject()); + _lastSuccessfulSyncTimestamp = _settings.lastChangeTimestamp(); + + qCDebug(networking) << "Received the Account Settings from the Metaverse API"; emit accountSettingsLoaded(); } else { @@ -874,6 +879,8 @@ void AccountManager::postAccountSettings() { return; } + qCDebug(networking) << "Account Settings have changed, pushing them to the Metaverse API"; + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QUrl lockerURL = _authURL; @@ -1035,7 +1042,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/AddressManager.h b/libraries/networking/src/AddressManager.h index 8187c23f71..31bda960ec 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -188,11 +188,11 @@ public slots: /**jsdoc * Takes you to a specified metaverse address. * @function location.handleLookupString - * @param {string} address - The address to go to: a "hifi://" address, an IP address (e.g., - * "127.0.0.1" or "localhost"), a domain name, a named path on a domain (starts with - * "/"), a position or position and orientation, or a user (starts with "@"). + * @param {string} address - The address to go to: a "hifi://" address, an IP address (e.g., + * "127.0.0.1" or "localhost"), a domain name, a named path on a domain (starts with + * "/"), a position or position and orientation, or a user (starts with "@"). * @param {boolean} [fromSuggestions=false] - Set to true if the address is obtained from the "Goto" dialog. - * Helps ensure that user's location history is correctly maintained. + * Helps ensure that user's location history is correctly maintained. */ void handleLookupString(const QString& lookupString, bool fromSuggestions = false); @@ -259,7 +259,7 @@ public slots: /**jsdoc * Checks if going back to the previous location is possible. * @function location.canGoBack - * @returns true if going back is possible, false if it isn't. + * @returns {boolean} true if going back is possible, false if it isn't. */ bool canGoBack() const; diff --git a/libraries/networking/src/HifiSockAddr.cpp b/libraries/networking/src/HifiSockAddr.cpp index a1bfcdd275..086dd08489 100644 --- a/libraries/networking/src/HifiSockAddr.cpp +++ b/libraries/networking/src/HifiSockAddr.cpp @@ -17,6 +17,13 @@ #include "NetworkLogging.h" +#ifdef WIN32 +#include +#include +#else +#include +#endif + int hifiSockAddrMetaTypeId = qRegisterMetaType(); HifiSockAddr::HifiSockAddr() : diff --git a/libraries/networking/src/HifiSockAddr.h b/libraries/networking/src/HifiSockAddr.h index 3c753f0434..dcf7f9a6a9 100644 --- a/libraries/networking/src/HifiSockAddr.h +++ b/libraries/networking/src/HifiSockAddr.h @@ -15,12 +15,7 @@ #include #include -#ifdef WIN32 -#include -#include -#else -#include -#endif +struct sockaddr; #include diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index d92db8a5d4..d867b49b30 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -38,6 +38,12 @@ #include "udt/Packet.h" #include "HMACAuth.h" +#if defined(Q_OS_WIN) +#include +#else +#include +#endif + static Setting::Handle LIMITED_NODELIST_LOCAL_PORT("LimitedNodeList.LocalPort", 0); using namespace std::chrono_literals; @@ -52,7 +58,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 +1161,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 +1177,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 +1219,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 +1250,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 +1335,8 @@ 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:" + << sharedPortMem->errorString(); } } @@ -1333,7 +1344,8 @@ void LimitedNodeList::putLocalPortIntoSharedMemory(const QString key, QObject* p bool LimitedNodeList::getLocalServerPortFromSharedMemory(const QString key, quint16& localPort) { QSharedMemory sharedMem(key); if (!sharedMem.attach(QSharedMemory::ReadOnly)) { - qCWarning(networking) << "Could not attach to shared memory at key" << key; + qCWarning(networking) << "Could not attach to shared memory at key" << key + << ":" << sharedMem.errorString(); return false; } else { sharedMem.lock(); diff --git a/libraries/networking/src/MessagesClient.h b/libraries/networking/src/MessagesClient.h index b97c6d8360..7f2714e5ea 100644 --- a/libraries/networking/src/MessagesClient.h +++ b/libraries/networking/src/MessagesClient.h @@ -57,10 +57,10 @@ public: * @param {string} channel - The channel to send the message on. * @param {string} message - The message to send. * @param {boolean} [localOnly=false] - If false then the message is sent to all Interface, client entity, - * server entity, and assignment client scripts in the domain.
    - * If true then: if sent from an Interface or client entity script it is received by all Interface and + * server entity, and assignment client scripts in the domain. + *

    If true then: if sent from an Interface or client entity script it is received by all Interface and * client entity scripts; if sent from a server entity script it is received by all entity server scripts; and if sent - * from an assignment client script it is received only by that same assignment client script. + * from an assignment client script it is received only by that same assignment client script.

    * @example
    Send and receive a message.Send and receive data.Invite Steam friends to join you in High Fidelity.Extract a filename from a URL.Select and unzip a file.Create a temporary directory.Create a matrix with rotation and translation.Create a matrix with scale, rotation, and translation.Create a matrix from columns.Create a matrix from an array.Extract the translation from a matrix.Extract the rotation from a matrix.Extract the scale from a matrix.Transform a point.Transform a vector.A matrix multiplied with its inverse is the unit matrix.Demonstrate that the "forward" direction is the negative z-axis.Two ways of printing a label and matrix value.
    * * diff --git a/libraries/shared/src/MovingPercentile.cpp b/libraries/shared/src/MovingPercentile.cpp index 5bcdbb5e80..427f7f2078 100644 --- a/libraries/shared/src/MovingPercentile.cpp +++ b/libraries/shared/src/MovingPercentile.cpp @@ -46,13 +46,13 @@ void MovingPercentile::updatePercentile(qint64 sample) { // swap new sample with neighbors in _samplesSorted until it's in sorted order // try swapping up first, then down. element will only be swapped one direction. while (newSampleIndex < _samplesSorted.size() - 1 && sample > _samplesSorted[newSampleIndex + 1]) { - _samplesSorted.swap(newSampleIndex, newSampleIndex + 1); - _sampleIds.swap(newSampleIndex, newSampleIndex + 1); + std::swap(_samplesSorted[newSampleIndex], _samplesSorted[newSampleIndex + 1]); + std::swap(_sampleIds[newSampleIndex], _sampleIds[newSampleIndex + 1]); newSampleIndex++; } while (newSampleIndex > 0 && sample < _samplesSorted[newSampleIndex - 1]) { - _samplesSorted.swap(newSampleIndex, newSampleIndex - 1); - _sampleIds.swap(newSampleIndex, newSampleIndex - 1); + std::swap(_samplesSorted[newSampleIndex], _samplesSorted[newSampleIndex - 1]); + std::swap(_sampleIds[newSampleIndex], _sampleIds[newSampleIndex - 1]); newSampleIndex--; } diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 39def1cab9..f14be72a71 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #ifdef Q_OS_WIN @@ -793,15 +794,7 @@ void printSystemInformation() { qCDebug(shared).noquote() << "\tKernel Type: " << QSysInfo::kernelType(); qCDebug(shared).noquote() << "\tKernel Version: " << QSysInfo::kernelVersion(); - auto macVersion = QSysInfo::macVersion(); - if (macVersion != QSysInfo::MV_None) { - qCDebug(shared) << "\tMac Version: " << macVersion; - } - - auto windowsVersion = QSysInfo::windowsVersion(); - if (windowsVersion != QSysInfo::WV_None) { - qCDebug(shared) << "\tWindows Version: " << windowsVersion; - } + qCDebug(shared) << "\tOS Version: " << QOperatingSystemVersion::current(); #ifdef Q_OS_WIN SYSTEM_INFO si; diff --git a/libraries/shared/src/shared/Camera.cpp b/libraries/shared/src/shared/Camera.cpp index b39b8b9169..6727f89d29 100644 --- a/libraries/shared/src/shared/Camera.cpp +++ b/libraries/shared/src/shared/Camera.cpp @@ -25,8 +25,14 @@ * * * - * + * + * + * + * + * + * * * * @@ -73,6 +79,8 @@ CameraMode stringToMode(const QString& mode) { return CAMERA_MODE_THIRD_PERSON; } else if (mode == "first person") { return CAMERA_MODE_FIRST_PERSON; + } else if (mode == "first person look at") { + return CAMERA_MODE_FIRST_PERSON_LOOK_AT; } else if (mode == "mirror") { return CAMERA_MODE_MIRROR; } else if (mode == "independent") { @@ -92,6 +100,8 @@ QString modeToString(CameraMode mode) { return "third person"; } else if (mode == CAMERA_MODE_FIRST_PERSON) { return "first person"; + } else if (mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT) { + return "first person look at"; } else if (mode == CAMERA_MODE_MIRROR) { return "mirror"; } else if (mode == CAMERA_MODE_INDEPENDENT) { diff --git a/libraries/shared/src/shared/Camera.h b/libraries/shared/src/shared/Camera.h index eecad34838..48cd814d86 100644 --- a/libraries/shared/src/shared/Camera.h +++ b/libraries/shared/src/shared/Camera.h @@ -19,6 +19,7 @@ enum CameraMode { CAMERA_MODE_NULL = -1, CAMERA_MODE_THIRD_PERSON, + CAMERA_MODE_FIRST_PERSON_LOOK_AT, CAMERA_MODE_FIRST_PERSON, CAMERA_MODE_MIRROR, CAMERA_MODE_INDEPENDENT, diff --git a/libraries/shared/src/shared/FileUtils.cpp b/libraries/shared/src/shared/FileUtils.cpp index f2a4925351..164af091de 100644 --- a/libraries/shared/src/shared/FileUtils.cpp +++ b/libraries/shared/src/shared/FileUtils.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include @@ -176,3 +177,15 @@ bool FileUtils::canCreateFile(const QString& fullPath) { } return true; } + +QString FileUtils::getParentPath(const QString& fullPath) { + return QFileInfo(fullPath).absoluteDir().canonicalPath(); +} + +bool FileUtils::exists(const QString& fileName) { + return QFileInfo(fileName).exists(); +} + +bool FileUtils::isRelative(const QString& fileName) { + return QFileInfo(fileName).isRelative(); +} diff --git a/libraries/shared/src/shared/FileUtils.h b/libraries/shared/src/shared/FileUtils.h index 2f5e11f005..d4ff819e75 100644 --- a/libraries/shared/src/shared/FileUtils.h +++ b/libraries/shared/src/shared/FileUtils.h @@ -12,20 +12,23 @@ #ifndef hifi_FileUtils_h #define hifi_FileUtils_h -#include -#include +#include + class FileUtils { public: static const QStringList& getFileSelectors(); static QString selectFile(const QString& fileName); static void locateFile(const QString& fileName); + static bool exists(const QString& fileName); + static bool isRelative(const QString& fileName); static QString standardPath(QString subfolder); static QString readFile(const QString& filename); static QStringList readLines(const QString& filename, QString::SplitBehavior splitBehavior = QString::KeepEmptyParts); static QString replaceDateTimeTokens(const QString& path); static QString computeDocumentPath(const QString& path); static bool canCreateFile(const QString& fullPath); + static QString getParentPath(const QString& fullPath); }; #endif // hifi_FileUtils_h diff --git a/libraries/shared/src/shared/Storage.h b/libraries/shared/src/shared/Storage.h index 0e5032bb62..6a2cecf8b9 100644 --- a/libraries/shared/src/shared/Storage.h +++ b/libraries/shared/src/shared/Storage.h @@ -10,15 +10,22 @@ #ifndef hifi_Storage_h #define hifi_Storage_h -#include +#include #include #include -#include -#include +#include + +#include +#include namespace storage { class Storage; - using StoragePointer = std::shared_ptr; + using Pointer = std::shared_ptr; + using StoragePointer = Pointer; + // A function that can construct storage, useful for creating a list of + // things that can become storage without requiring that they all be instantiated at once + using Builder = std::function; + using Builders = std::vector; // Abstract class to represent memory that stored _somewhere_ (in system memory or in a file, for example) class Storage : public std::enable_shared_from_this { diff --git a/libraries/task/src/task/Config.h b/libraries/task/src/task/Config.h index 71d48c9a18..7e6be9b53e 100644 --- a/libraries/task/src/task/Config.h +++ b/libraries/task/src/task/Config.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include "SettingHandle.h" @@ -189,7 +190,7 @@ public: * @returns {object[]} */ Q_INVOKABLE QObjectList getSubConfigs() const { - auto list = findChildren(QRegExp(".*"), Qt::FindDirectChildrenOnly); + auto list = findChildren(QRegularExpression(".*"), Qt::FindDirectChildrenOnly); QObjectList returned; for (int i = 0; i < list.size(); i++) { returned.push_back(list[i]); diff --git a/libraries/ui/src/InfoView.cpp b/libraries/ui/src/InfoView.cpp index 650d43831c..478401c6f8 100644 --- a/libraries/ui/src/InfoView.cpp +++ b/libraries/ui/src/InfoView.cpp @@ -56,10 +56,10 @@ void InfoView::show(const QString& path, bool firstOrChangedOnly, QString urlQue const QString lastVersion = infoVersion.get(); const QString version = fetchVersion(url); // If we have version information stored - if (lastVersion != QString::null) { + if (!lastVersion.isNull()) { // Check to see the document version. If it's valid and matches // the stored version, we're done, so exit - if (version == QString::null || version == lastVersion) { + if (version.isNull() || version == lastVersion) { return; } } diff --git a/libraries/ui/src/MainWindow.cpp b/libraries/ui/src/MainWindow.cpp index 124e25675a..ffa9bacbaa 100644 --- a/libraries/ui/src/MainWindow.cpp +++ b/libraries/ui/src/MainWindow.cpp @@ -11,8 +11,8 @@ #include "MainWindow.h" -#include -#include +#include +#include #include #include #include @@ -22,8 +22,8 @@ #include #include #include -#include -#include +#include +#include #include "ui/Logging.h" #include "DockWidget.h" @@ -60,7 +60,7 @@ QWindow* MainWindow::findMainWindow() { void MainWindow::restoreGeometry() { // Did not use setGeometry() on purpose, // see http://doc.qt.io/qt-5/qsettings.html#restoring-the-state-of-a-gui-application - QRect windowGeometry = qApp->desktop()->availableGeometry(); + QRect windowGeometry = QGuiApplication::primaryScreen()->availableGeometry(); #if defined(Q_OS_MAC) windowGeometry.setSize((windowGeometry.size() * 0.5f)); #endif diff --git a/libraries/ui/src/MainWindow.h b/libraries/ui/src/MainWindow.h index 543f8ce9af..b37ac2ec1b 100644 --- a/libraries/ui/src/MainWindow.h +++ b/libraries/ui/src/MainWindow.h @@ -24,6 +24,10 @@ public: ~MainWindow(); static QWindow* findMainWindow(); + + // This offset is used for positioning children window relative to the main window. + void setDockedWidgetRelativePositionOffset(const QSize& newOffset) { _dockedWidgetRelativePositionOffset.setWidth(newOffset.width()); _dockedWidgetRelativePositionOffset.setHeight(newOffset.height()); } + QSize getDockedWidgetRelativePositionOffset() { return _dockedWidgetRelativePositionOffset; } public slots: void restoreGeometry(); void saveGeometry(); @@ -46,6 +50,7 @@ protected: private: Setting::Handle _windowGeometry; Setting::Handle _windowState; + QSize _dockedWidgetRelativePositionOffset{ 0, 0 }; }; #endif /* defined(__hifi__MainWindow__) */ diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 75b08ba44f..040bb750d0 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -330,7 +330,6 @@ QObject* TabletScriptingInterface::getFlags() { // static const char* TABLET_HOME_SOURCE_URL = "hifi/tablet/TabletHome.qml"; -static const char* WEB_VIEW_SOURCE_URL = "hifi/tablet/TabletWebView.qml"; static const char* VRMENU_SOURCE_URL = "hifi/tablet/TabletMenu.qml"; class TabletRootWindow : public QmlWindowClass { @@ -827,58 +826,24 @@ void TabletProxy::loadWebScreenOnTop(const QVariant& url) { void TabletProxy::loadWebScreenOnTop(const QVariant& url, const QString& injectJavaScriptUrl) { bool localSafeContext = hifi::scripting::isLocalAccessSafeThread(); if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "loadHTMLSourceImpl", Q_ARG(QVariant, url), Q_ARG(QString, injectJavaScriptUrl), Q_ARG(bool, localSafeContext)); + QMetaObject::invokeMethod(this, "loadHTMLSourceOnTopImpl", Q_ARG(QString, url.toString()), Q_ARG(QString, injectJavaScriptUrl), Q_ARG(bool, false), Q_ARG(bool, localSafeContext)); return; } - loadHTMLSourceImpl(url, injectJavaScriptUrl, localSafeContext); -} - - - -void TabletProxy::loadHTMLSourceImpl(const QVariant& url, const QString& injectJavaScriptUrl, bool localSafeContext) { - if (QThread::currentThread() != thread()) { - qCWarning(uiLogging) << __FUNCTION__ << "may not be called directly by scripts"; - return; - - } - - - QObject* root = nullptr; - if (!_toolbarMode && _qmlTabletRoot) { - root = _qmlTabletRoot; - } else if (_toolbarMode && _desktopWindow) { - root = _desktopWindow->asQuickItem(); - } - - if (root) { - if (localSafeContext) { - hifi::scripting::setLocalAccessSafeThread(true); - } - QMetaObject::invokeMethod(root, "loadQMLOnTop", Q_ARG(const QVariant&, QVariant(WEB_VIEW_SOURCE_URL))); - QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true))); - if (_toolbarMode && _desktopWindow) { - QMetaObject::invokeMethod(root, "setResizable", Q_ARG(const QVariant&, QVariant(false))); - } - QMetaObject::invokeMethod(root, "loadWebOnTop", Q_ARG(const QVariant&, QVariant(url)), Q_ARG(const QVariant&, QVariant(injectJavaScriptUrl))); - hifi::scripting::setLocalAccessSafeThread(false); - } - _state = State::Web; + loadHTMLSourceOnTopImpl(url.toString(), injectJavaScriptUrl, false, localSafeContext); } void TabletProxy::gotoWebScreen(const QString& url, const QString& injectedJavaScriptUrl, bool loadOtherBase) { bool localSafeContext = hifi::scripting::isLocalAccessSafeThread(); if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "loadHTMLSourceImpl", Q_ARG(QString, url), Q_ARG(QString, injectedJavaScriptUrl), Q_ARG(bool, loadOtherBase), Q_ARG(bool, localSafeContext)); + QMetaObject::invokeMethod(this, "loadHTMLSourceOnTopImpl", Q_ARG(QString, url), Q_ARG(QString, injectedJavaScriptUrl), Q_ARG(bool, loadOtherBase), Q_ARG(bool, localSafeContext)); return; } - - loadHTMLSourceImpl(url, injectedJavaScriptUrl, loadOtherBase, localSafeContext); + loadHTMLSourceOnTopImpl(url, injectedJavaScriptUrl, loadOtherBase, localSafeContext); } -void TabletProxy::loadHTMLSourceImpl(const QString& url, const QString& injectedJavaScriptUrl, bool loadOtherBase, bool localSafeContext) { - +void TabletProxy::loadHTMLSourceOnTopImpl(const QString& url, const QString& injectedJavaScriptUrl, bool loadOtherBase, bool localSafeContext) { QObject* root = nullptr; if (!_toolbarMode && _qmlTabletRoot) { root = _qmlTabletRoot; @@ -911,7 +876,6 @@ void TabletProxy::loadHTMLSourceImpl(const QString& url, const QString& injected _initialWebPathParams.first = injectedJavaScriptUrl; _initialWebPathParams.second = loadOtherBase; _initialScreen = true; - } } diff --git a/libraries/ui/src/ui/TabletScriptingInterface.h b/libraries/ui/src/ui/TabletScriptingInterface.h index 58596d6d92..88d4ebe267 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.h +++ b/libraries/ui/src/ui/TabletScriptingInterface.h @@ -69,7 +69,7 @@ class TabletScriptingInterface : public QObject, public Dependency { public: /**jsdoc - * Standard tablet sounds. + *

    Standard tablet sounds.

    *
    Event NameEntity Event
    First Person"first person"The camera is positioned such that you have the same view as your avatar. The camera moves and rotates with your - * avatar.Legacy first person camera mode. The camera is positioned such that you have the same view as your avatar. + * The camera moves and rotates with your avatar.
    First Person Look At"first person look at"Default first person camera mode. The camera is positioned such that you have the same view as your avatar. + * The camera moves and rotates with your avatar's head.
    Third Person
    * * @@ -273,7 +273,9 @@ public: Q_INVOKABLE void gotoHomeScreen(); /**jsdoc - * Opens a web page or app on the tablet. + * Opens a web app or page in addition to any current app. In tablet mode, the app or page is displayed over the top of the + * current app; in toolbar mode, the app is opened in a new window that replaces any current window open. If in tablet + * mode, the app or page can be closed using {@link TabletProxy#returnToPreviousApp}. * @function TabletProxy#gotoWebScreen * @param {string} url - The URL of the web page or app. * @param {string} [injectedJavaScriptUrl=""] - The URL of JavaScript to inject into the web page. @@ -294,29 +296,31 @@ public: Q_INVOKABLE void loadQMLSource(const QVariant& path, bool resizable = false); /**jsdoc - * Internal function, do not call from scripts * @function TabletProxy#loadQMLSourceImpl + * @deprecated This function is deprecated and will be removed. */ + // Internal function, do not call from scripts. Q_INVOKABLE void loadQMLSourceImpl(const QVariant& path, bool resizable, bool localSafeContext); - /**jsdoc - * Internal function, do not call from scripts - * @function TabletProxy#loadHTMLSourceImpl + /**jsdoc + * @function TabletProxy#loadHTMLSourceOnTopImpl + * @deprecated This function is deprecated and will be removed. */ - Q_INVOKABLE void loadHTMLSourceImpl(const QVariant& url, const QString& injectJavaScriptUrl, bool localSafeContext); + // Internal function, do not call from scripts. + Q_INVOKABLE void loadHTMLSourceOnTopImpl(const QString& url, const QString& injectedJavaScriptUrl, bool loadOtherBase, bool localSafeContext); - /**jsdoc - * Internal function, do not call from scripts - * @function TabletProxy#loadHTMLSourceImpl - */ - Q_INVOKABLE void loadHTMLSourceImpl(const QString& url, const QString& injectedJavaScriptUrl, bool loadOtherBase, bool localSafeContext); - - /**jsdoc - * Internal function, do not call from scripts + /**jsdoc * @function TabletProxy#returnToPreviousAppImpl + * @deprecated This function is deprecated and will be removed. */ + // Internal function, do not call from scripts. Q_INVOKABLE void returnToPreviousAppImpl(bool localSafeContext); + /**jsdoc + *@function TabletProxy#loadQMLOnTopImpl + * @deprecated This function is deprecated and will be removed. + */ + // Internal function, do not call from scripts. Q_INVOKABLE void loadQMLOnTopImpl(const QVariant& path, bool localSafeContext); // FIXME: This currently relies on a script initializing the tablet (hence the bool denoting success); @@ -355,8 +359,8 @@ public: /**jsdoc * Opens a web app or page in addition to any current app. In tablet mode, the app or page is displayed over the top of the - * current app; in toolbar mode, the app is opened in a new window. If in tablet mode, the app or page can be closed using - * {@link TabletProxy#returnToPreviousApp}. + * current app; in toolbar mode, the app is opened in a new window that replaces any current window open. If in tablet + * mode, the app or page can be closed using {@link TabletProxy#returnToPreviousApp}. * @function TabletProxy#loadWebScreenOnTop * @param {string} path - The URL of the web page or HTML app. * @param {string} [injectedJavaScriptURL=""] - The URL of JavaScript to inject into the web page. diff --git a/libraries/ui/src/ui/ToolbarScriptingInterface.h b/libraries/ui/src/ui/ToolbarScriptingInterface.h index 952d3cce95..3d38aa296b 100644 --- a/libraries/ui/src/ui/ToolbarScriptingInterface.h +++ b/libraries/ui/src/ui/ToolbarScriptingInterface.h @@ -92,15 +92,19 @@ public: ToolbarProxy(QObject* qmlObject, QObject* parent = nullptr); /**jsdoc + * Currently doesn't work. * @function ToolbarProxy#addButton - * @param {object} properties - * @returns {ToolbarButtonProxy} + * @param {object} properties - Button properties + * @returns {object} The button added. + * @deprecated This method is deprecated and will be removed. */ Q_INVOKABLE ToolbarButtonProxy* addButton(const QVariant& properties); /**jsdoc + * Currently doesn't work. * @function ToolbarProxy#removeButton - * @param {string} name + * @param {string} name - Button name. + * @deprecated This method is deprecated and will be removed. */ Q_INVOKABLE void removeButton(const QVariant& name); diff --git a/plugins/oculus/src/OculusControllerManager.cpp b/plugins/oculus/src/OculusControllerManager.cpp index 14830f3f04..c1431fa4b2 100644 --- a/plugins/oculus/src/OculusControllerManager.cpp +++ b/plugins/oculus/src/OculusControllerManager.cpp @@ -50,13 +50,13 @@ QString OculusControllerManager::configurationLayout() { void OculusControllerManager::setConfigurationSettings(const QJsonObject configurationSettings) { if (configurationSettings.contains("trackControllersInOculusHome")) { - _touch->_trackControllersInOculusHome.set(configurationSettings["trackControllersInOculusHome"].toBool()); + _trackControllersInOculusHome.set(configurationSettings["trackControllersInOculusHome"].toBool()); } } QJsonObject OculusControllerManager::configurationSettings() { QJsonObject configurationSettings; - configurationSettings["trackControllersInOculusHome"] = _touch->_trackControllersInOculusHome.get(); + configurationSettings["trackControllersInOculusHome"] = _trackControllersInOculusHome.get(); return configurationSettings; } @@ -232,7 +232,7 @@ void OculusControllerManager::TouchDevice::update(float deltaTime, quint64 currentTime = usecTimestampNow(); static const auto REQUIRED_HAND_STATUS = ovrStatus_OrientationTracked | ovrStatus_PositionTracked; bool hasInputFocus = ovr::hasInputFocus(); - bool trackControllersInOculusHome = _trackControllersInOculusHome.get(); + bool trackControllersInOculusHome = _parent._trackControllersInOculusHome.get(); auto tracking = ovr::getTrackingState(); // ovr_GetTrackingState(_parent._session, 0, false); ovr::for_each_hand([&](ovrHandType hand) { ++numTrackedControllers; diff --git a/plugins/oculus/src/OculusControllerManager.h b/plugins/oculus/src/OculusControllerManager.h index ea32eace61..7d1d176a56 100644 --- a/plugins/oculus/src/OculusControllerManager.h +++ b/plugins/oculus/src/OculusControllerManager.h @@ -98,7 +98,6 @@ private: float _leftHapticStrength { 0.0f }; float _rightHapticDuration { 0.0f }; float _rightHapticStrength { 0.0f }; - Setting::Handle _trackControllersInOculusHome { "trackControllersInOculusHome", false }; mutable std::recursive_mutex _lock; std::map _lostTracking; std::map _regainTrackingDeadline; @@ -109,6 +108,7 @@ private: void checkForConnectedDevices(); + Setting::Handle _trackControllersInOculusHome { "trackControllersInOculusHome", false }; ovrInputState _remoteInputState {}; ovrInputState _touchInputState {}; RemoteDevice::Pointer _remote; diff --git a/scripts/+android_interface/defaultScripts.js b/scripts/+android_interface/defaultScripts.js index 8b3082d81a..a02f98b193 100644 --- a/scripts/+android_interface/defaultScripts.js +++ b/scripts/+android_interface/defaultScripts.js @@ -127,3 +127,8 @@ Script.scriptEnding.connect(function() { }); Menu.menuItemEvent.connect(menuItemEvent); + +var ANDROID_UI_AUTO_LOD_ADJUST = false; +var ANDROID_UI_LOD_ANGLE_DEG = 0.248; +LODManager.automaticLODAdjust = ANDROID_UI_AUTO_LOD_ADJUST; +LODManager.lodAngleDeg = ANDROID_UI_LOD_ANGLE_DEG; diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index 6f0dd40493..7f78d2477f 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -71,23 +71,63 @@ if (Menu.menuExists(MENU_CATEGORY) && !Menu.menuItemExists(MENU_CATEGORY, MENU_I } function loadSeparateDefaults() { + var currentlyRunningScripts = ScriptDiscoveryService.getRunning(); + for (var i in DEFAULT_SCRIPTS_SEPARATE) { - Script.load(DEFAULT_SCRIPTS_SEPARATE[i]); + var shouldLoadCurrentDefaultScript = true; + + for (var j = 0; j < currentlyRunningScripts.length; j++) { + var currentRunningScriptObject = currentlyRunningScripts[j]; + var currentDefaultScriptName = DEFAULT_SCRIPTS_SEPARATE[i].substr((DEFAULT_SCRIPTS_SEPARATE[i].lastIndexOf("/") + 1), DEFAULT_SCRIPTS_SEPARATE[i].length); + if (currentDefaultScriptName === currentRunningScriptObject.name) { + shouldLoadCurrentDefaultScript = false; + } + } + + if (shouldLoadCurrentDefaultScript) { + Script.load(DEFAULT_SCRIPTS_SEPARATE[i]); + } } } function runDefaultsTogether() { - for (var i in DEFAULT_SCRIPTS_COMBINED) { - Script.include(DEFAULT_SCRIPTS_COMBINED[i]); + var currentlyRunningScripts = ScriptDiscoveryService.getRunning(); + + for (var i = 0; i < DEFAULT_SCRIPTS_COMBINED.length; i++) { + var shouldIncludeCurrentDefaultScript = true; + + for (var j = 0; j < currentlyRunningScripts.length; j++) { + var currentRunningScriptObject = currentlyRunningScripts[j]; + var currentDefaultScriptName = DEFAULT_SCRIPTS_COMBINED[i].substr((DEFAULT_SCRIPTS_COMBINED[i].lastIndexOf("/") + 1), DEFAULT_SCRIPTS_COMBINED[i].length); + if (currentDefaultScriptName === currentRunningScriptObject.name) { + shouldIncludeCurrentDefaultScript = false; + } + } + + if (shouldIncludeCurrentDefaultScript) { + Script.include(DEFAULT_SCRIPTS_COMBINED[i]); + } } - loadSeparateDefaults(); } function runDefaultsSeparately() { + var currentlyRunningScripts = ScriptDiscoveryService.getRunning(); + for (var i in DEFAULT_SCRIPTS_COMBINED) { - Script.load(DEFAULT_SCRIPTS_COMBINED[i]); + var shouldLoadCurrentDefaultScript = true; + + for (var j = 0; j < currentlyRunningScripts.length; j++) { + var currentRunningScriptObject = currentlyRunningScripts[j]; + var currentDefaultScriptName = DEFAULT_SCRIPTS_COMBINED[i].substr((DEFAULT_SCRIPTS_COMBINED[i].lastIndexOf("/") + 1), DEFAULT_SCRIPTS_COMBINED[i].length); + if (currentDefaultScriptName === currentRunningScriptObject.name) { + shouldLoadCurrentDefaultScript = false; + } + } + + if (shouldLoadCurrentDefaultScript) { + Script.load(DEFAULT_SCRIPTS_COMBINED[i]); + } } - loadSeparateDefaults(); } // start all scripts @@ -99,6 +139,7 @@ if (Menu.isOptionChecked(MENU_ITEM)) { // include all default scripts into this ScriptEngine runDefaultsTogether(); } +loadSeparateDefaults(); function menuItemEvent(menuItem) { if (menuItem === MENU_ITEM) { 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/developer/tests/toolbarTest.js b/scripts/developer/tests/toolbarTest.js index 89609e610d..9e82f814ac 100644 --- a/scripts/developer/tests/toolbarTest.js +++ b/scripts/developer/tests/toolbarTest.js @@ -1,117 +1,38 @@ -var isActive = false; +(function () { -var toolBar = (function() { - var that = {}, - toolBar, - activeButton, - newModelButton, - newShapeButton, - newLightButton, - newTextButton, - newWebButton, - newZoneButton, - newParticleButton, - newMaterialButton - - var toolIconUrl = Script.resolvePath("../../system/assets/images/tools/"); - - function initialize() { - print("Toolbars: " + Toolbars); - toolBar = Toolbars.getToolbar("highfidelity.edit.toolbar"); - print("Toolbar: " + toolBar); - activeButton = toolBar.addButton({ - objectName: "activeButton", - imageURL: toolIconUrl + "edit-01.svg", - visible: true, - alpha: 0.9, - }); - - print("Button " + activeButton); - print("Button signal " + activeButton.clicked); - activeButton.clicked.connect(function(){ - print("Clicked on button " + isActive); - that.setActive(!isActive); - }); - - newModelButton = toolBar.addButton({ - objectName: "newModelButton", - imageURL: toolIconUrl + "model-01.svg", - alpha: 0.9, - visible: false - }); - - newShapeButton = toolBar.addButton({ - objectName: "newShapeButton", - imageURL: toolIconUrl + "cube-01.svg", - alpha: 0.9, - visible: false - }); - - newLightButton = toolBar.addButton({ - objectName: "newLightButton", - imageURL: toolIconUrl + "light-01.svg", - alpha: 0.9, - visible: false - }); - - newTextButton = toolBar.addButton({ - objectName: "newTextButton", - imageURL: toolIconUrl + "text-01.svg", - alpha: 0.9, - visible: false - }); - - newWebButton = toolBar.addButton({ - objectName: "newWebButton", - imageURL: toolIconUrl + "web-01.svg", - alpha: 0.9, - visible: false - }); - - newZoneButton = toolBar.addButton({ - objectName: "newZoneButton", - imageURL: toolIconUrl + "zone-01.svg", - alpha: 0.9, - visible: false - }); - - newParticleButton = toolBar.addButton({ - objectName: "newParticleButton", - imageURL: toolIconUrl + "particle-01.svg", - alpha: 0.9, - visible: false - }); - - newMaterialButton = toolBar.addButton({ - objectName: "newMaterialButton", - imageURL: toolIconUrl + "material-01.svg", - alpha: 0.9, - visible: false - }); - - that.setActive(false); - newModelButton.clicked(); + // Get the system toolbar. + var toolbar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); + if (!toolbar) { + print("ERROR: Couldn't get system toolbar."); + return; } - that.setActive = function(active) { - if (active != isActive) { - isActive = active; - that.showTools(isActive); - } - }; + Script.setTimeout(function () { + // Report the system toolbar visibility. + var isToolbarVisible = toolbar.readProperty("visible"); + print("Toolbar visible: " + isToolbarVisible); - // Sets visibility of tool buttons, excluding the power button - that.showTools = function(doShow) { - newModelButton.writeProperty('visible', doShow); - newShapeButton.writeProperty('visible', doShow); - newLightButton.writeProperty('visible', doShow); - newTextButton.writeProperty('visible', doShow); - newWebButton.writeProperty('visible', doShow); - newZoneButton.writeProperty('visible', doShow); - newParticleButton.writeProperty('visible', doShow); - newMaterialButton.writeProperty('visible', doShow); - }; + // Briefly toggle the system toolbar visibility. + print("Toggle toolbar"); + toolbar.writeProperty("visible", !isToolbarVisible); + Script.setTimeout(function () { + print("Toggle toolbar"); + toolbar.writeProperty("visible", isToolbarVisible); + }, 2000); + }, 2000); + + Script.setTimeout(function () { + // Report the system toolbar visibility alternative method. + isToolbarVisible = toolbar.readProperties(["visible"]).visible; + print("Toolbar visible: " + isToolbarVisible); + + // Briefly toggle the system toolbar visibility. + print("Toggle toolbar"); + toolbar.writeProperties({ visible: !isToolbarVisible }); + Script.setTimeout(function () { + print("Toggle toolbar"); + toolbar.writeProperties({ visible: isToolbarVisible }); + }, 2000); + }, 6000); - initialize(); - return that; }()); diff --git a/scripts/developer/utilities/lib/prop/PropColor.qml b/scripts/developer/utilities/lib/prop/PropColor.qml index e69de29bb2..bda19f44d6 100644 --- a/scripts/developer/utilities/lib/prop/PropColor.qml +++ b/scripts/developer/utilities/lib/prop/PropColor.qml @@ -0,0 +1,43 @@ +// +// PropItem.qml +// +// Created by Sam Gateau on 3/2/2019 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.7 + + +PropItem { + Global { id: global } + id: root + + // Scalar Prop + // property bool integral: false + // property var numDigits: 2 + + Rectangle { + id: valueLabel + + anchors.left: root.splitter.right + anchors.right: root.right + anchors.verticalCenter: root.verticalCenter + // horizontalAlignment: global.valueTextAlign + height: global.slimHeight + + function getColor() { + var c = root.valueVarGetter(); + return Qt.rgba(c.red, c.green, c.blue, 1.0); + } + + // background: Rectangle { + color: { return getColor() } + border.color: global.colorBorderLight + border.width: global.valueBorderWidth + radius: global.valueBorderRadius + // } + } +} diff --git a/scripts/developer/utilities/render/materialInspector.qml b/scripts/developer/utilities/render/materialInspector.qml index d4dad203cd..31d5651dd9 100644 --- a/scripts/developer/utilities/render/materialInspector.qml +++ b/scripts/developer/utilities/render/materialInspector.qml @@ -13,12 +13,19 @@ import QtQuick.Layouts 1.3 import stylesUit 1.0 import controlsUit 1.0 as HifiControls +import "../lib/prop" as Prop Rectangle { HifiConstants { id: hifi;} color: Qt.rgba(hifi.colors.baseGray.r, hifi.colors.baseGray.g, hifi.colors.baseGray.b, 0.8); id: root; + property var theMaterial: {} + property var theMaterialAttributes: {} + property var hasMaterial: false + + property var isReadOnly: true + function fromScript(message) { switch (message.method) { case "setObjectInfo": @@ -26,40 +33,213 @@ Rectangle { break; case "setMaterialJSON": materialJSONText.text = message.params.materialJSONText; + + theMaterial = JSON.parse(message.params.materialJSONText) + theMaterialAttributes = theMaterial.materials + hasMaterial = (theMaterial !== undefined) break; } } - - Rectangle { - id: entityIDContainer - height: 52 - width: root.width - color: Qt.rgba(root.color.r * 0.7, root.color.g * 0.7, root.color.b * 0.7, 0.8); - TextEdit { - id: entityIDInfo - text: "Type: Unknown\nID: None\nMesh Part: Unknown" - font.pointSize: 9 - color: "#FFFFFF" - readOnly: true - selectByMouse: true - } - } - - Original.ScrollView { - anchors.top: entityIDContainer.bottom - height: root.height - entityIDContainer.height - width: root.width - clip: true - Original.ScrollBar.horizontal.policy: Original.ScrollBar.AlwaysOff - TextEdit { - id: materialJSONText - text: "Click an object to get material JSON" + + Column { + + anchors.left: parent.left + anchors.right: parent.right + + Rectangle { + id: entityIDContainer + height: 52 width: root.width - font.pointSize: 10 - color: "#FFFFFF" - readOnly: true - selectByMouse: true - wrapMode: Text.WordWrap + color: Qt.rgba(root.color.r * 0.7, root.color.g * 0.7, root.color.b * 0.7, 0.8); + TextEdit { + id: entityIDInfo + text: "Type: Unknown\nID: None\nMesh Part: Unknown" + font.pointSize: 9 + color: "#FFFFFF" + readOnly: true + selectByMouse: true + } + } + + Prop.PropString { + visible: hasMaterial && ("name" in theMaterialAttributes) + label: "name" + object: theMaterialAttributes + property: "name" + readOnly: isReadOnly + } + Prop.PropString { + visible: hasMaterial && ("model" in theMaterialAttributes) + label: "model" + object: theMaterialAttributes + property: "model" + readOnly: isReadOnly + } + + Prop.PropColor { + visible: hasMaterial && ("albedo" in theMaterialAttributes) + label: "albedo" + object: theMaterialAttributes + property: "albedo" + readOnly: isReadOnly + } + Prop.PropString { + visible: hasMaterial && ("albedoMap" in theMaterialAttributes) + label: "albedoMap" + object: theMaterialAttributes + property: "albedoMap" + readOnly: isReadOnly + } + + Prop.PropScalar { + visible: hasMaterial && ("opacity" in theMaterialAttributes) + label: "opacity" + object: theMaterialAttributes + property: "opacity" + readOnly: isReadOnly + } + Prop.PropString { + visible: hasMaterial && ("opacityMap" in theMaterialAttributes) + label: "opacityMap" + object: theMaterialAttributes + property: "opacityMap" + readOnly: isReadOnly + } + Prop.PropString { + visible: hasMaterial && ("opacityMapMode" in theMaterialAttributes) + label: "opacityMapMode" + object: theMaterialAttributes + property: "opacityMapMode" + readOnly: isReadOnly + } + /*Prop.PropEnum { + visible: hasMaterial && ("opacityMapMode" in theMaterialAttributes) + label: "opacityMapMode" + object: theMaterialAttributes + property: "opacityMapMode" + readOnly: isReadOnly + enums: ["None", "Mask", "Blend"] + } */ + Prop.PropScalar { + visible: hasMaterial && ("opacityCutoff" in theMaterialAttributes) + label: "opacity Cutoff" + object: theMaterialAttributes + property: "opacityCutoff" + readOnly: isReadOnly + } + + Prop.PropString { + visible: hasMaterial && ("occlusionMap" in theMaterialAttributes) + label: "occlusionMap" + object: theMaterialAttributes + property: "occlusionMap" + readOnly: isReadOnly + } + Prop.PropString { + visible: hasMaterial && ("normalMap" in theMaterialAttributes) + label: "normalMap" + object: theMaterialAttributes + property: "normalMap" + readOnly: isReadOnly + } + Prop.PropString { + visible: hasMaterial && ("bumpMap" in theMaterialAttributes) + label: "normalMap from bumpMap" + object: theMaterialAttributes + property: "bumpMap" + readOnly: isReadOnly + } + + Prop.PropScalar { + visible: hasMaterial && ("roughness" in theMaterialAttributes) + label: "roughness" + object: theMaterialAttributes + property: "roughness" + readOnly: isReadOnly + } + Prop.PropScalar { + visible: hasMaterial && ("metallic" in theMaterialAttributes) + label: "metallic" + object: theMaterialAttributes + property: "metallic" + readOnly: isReadOnly + } + Prop.PropString { + visible: hasMaterial && ("roughnessMap" in theMaterialAttributes) + label: "roughnessMap" + object: theMaterialAttributes + property: "roughnessMap" + readOnly: isReadOnly + } + Prop.PropString { + visible: hasMaterial && ("glossMap" in theMaterialAttributes) + label: "roughnessMap from glossMap" + object: theMaterialAttributes + property: "glossMap" + readOnly: isReadOnly + } + Prop.PropString { + visible: hasMaterial && ("metallicMap" in theMaterialAttributes) + label: "metallicMap" + object: theMaterialAttributes + property: "metallicMap" + readOnly: isReadOnly + } + Prop.PropString { + visible: hasMaterial && ("specularMap" in theMaterialAttributes) + label: "metallicMap from specularMap" + object: theMaterialAttributes + property: "specularMap" + readOnly: isReadOnly + } + + Prop.PropScalar { + visible: hasMaterial && ("scattering" in theMaterialAttributes) + label: "scattering" + object: theMaterialAttributes + property: "scattering" + readOnly: isReadOnly + } + Prop.PropString { + visible: hasMaterial && ("scatteringMap" in theMaterialAttributes) + label: "scatteringMap" + object: theMaterialAttributes + property: "scatteringMap" + readOnly: isReadOnly + } + + + Prop.PropColor { + visible: hasMaterial && ("emissive" in theMaterialAttributes) + label: "emissive" + object: theMaterialAttributes + property: "emissive" + readOnly: isReadOnly + } + Prop.PropString { + visible: hasMaterial && ("emissiveMap" in theMaterialAttributes) + label: "emissiveMap" + object: theMaterialAttributes + property: "emissiveMap" + readOnly: isReadOnly + } + + Original.ScrollView { + // anchors.top: entityIDContainer.bottom + height: root.height - entityIDContainer.height + width: root.width + clip: true + Original.ScrollBar.horizontal.policy: Original.ScrollBar.AlwaysOff + TextEdit { + id: materialJSONText + text: "Click an object to get material JSON" + width: root.width + font.pointSize: 10 + color: "#FFFFFF" + readOnly: true + selectByMouse: true + wrapMode: Text.WordWrap + } } } } diff --git a/scripts/simplifiedUI/simplifiedEmote/simplifiedEmote.js b/scripts/simplifiedUI/simplifiedEmote/simplifiedEmote.js index 8633fe8870..d7d6279e10 100644 --- a/scripts/simplifiedUI/simplifiedEmote/simplifiedEmote.js +++ b/scripts/simplifiedUI/simplifiedEmote/simplifiedEmote.js @@ -444,17 +444,9 @@ function updateEmoteIndicatorIcon(iconURL) { } -function onGeometryChanged(rect) { - updateEmoteAppBarPosition(); -} - - function onWindowMinimizedChanged(isMinimized) { - if (isMinimized) { - handleEmoteIndicatorVisibleChanged(false); - } else if (!HMD.active) { - handleEmoteIndicatorVisibleChanged(true); - } + isWindowMinimized = isMinimized; + maybeChangeEmoteIndicatorVisibility(!isMinimized); } @@ -539,10 +531,11 @@ function showEmoteAppBar() { x: EMOTE_APP_BAR_WIDTH_PX, y: EMOTE_APP_BAR_HEIGHT_PX }, - position: { - x: Window.x + EMOTE_APP_BAR_LEFT_MARGIN, - y: Window.y + Window.innerHeight - EMOTE_APP_BAR_BOTTOM_MARGIN + relativePosition: { + x: EMOTE_APP_BAR_LEFT_MARGIN, + y: EMOTE_APP_BAR_BOTTOM_MARGIN }, + relativePositionAnchor: Desktop.RelativePositionAnchor.BOTTOM_LEFT, overrideFlags: EMOTE_APP_BAR_WINDOW_FLAGS }); @@ -550,10 +543,18 @@ function showEmoteAppBar() { } -function handleEmoteIndicatorVisibleChanged(shouldBeVisible) { - if (shouldBeVisible && !emoteAppBarWindow) { +// There is currently no property in the Window Scripting Interface to determine +// whether the Interface window is currently minimized. This feels like an oversight. +// We should add that functionality to the Window Scripting Interface, and remove `isWindowMinimized` below. +var isWindowMinimized = false; +function maybeChangeEmoteIndicatorVisibility(desiredVisibility) { + if (isWindowMinimized || HMD.active) { + desiredVisibility = false; + } + + if (desiredVisibility && !emoteAppBarWindow) { showEmoteAppBar(); - } else if (emoteAppBarWindow) { + } else if (!desiredVisibility && emoteAppBarWindow) { emoteAppBarWindow.fromQml.disconnect(onMessageFromEmoteAppBar); emoteAppBarWindow.close(); emoteAppBarWindow = false; @@ -561,23 +562,25 @@ function handleEmoteIndicatorVisibleChanged(shouldBeVisible) { } +function handleFTUEScreensVisibilityChanged(ftueScreenVisible) { + maybeChangeEmoteIndicatorVisibility(!ftueScreenVisible); +} + + function onDisplayModeChanged(isHMDMode) { reactionsBegun.forEach(function(react) { endReactionWrapper(react); }); - if (isHMDMode) { - handleEmoteIndicatorVisibleChanged(false); - } else { - handleEmoteIndicatorVisibleChanged(true); - } + maybeChangeEmoteIndicatorVisibility(!isHMDMode); } -var emojiAPI = Script.require("./emojiApp/simplifiedEmoji.js?" + Date.now()); +var emojiAPI = Script.require("./emojiApp/simplifiedEmoji.js"); var keyPressSignalsConnected = false; var emojiCodeMap; var customEmojiCodeMap; +var _this; function setup() { deleteOldReticles(); @@ -605,16 +608,25 @@ function setup() { }, {}); Window.minimizedChanged.connect(onWindowMinimizedChanged); - Window.geometryChanged.connect(onGeometryChanged); HMD.displayModeChanged.connect(onDisplayModeChanged); getSounds(); - handleEmoteIndicatorVisibleChanged(true); + maybeChangeEmoteIndicatorVisibility(true); Controller.keyPressEvent.connect(keyPressHandler); Controller.keyReleaseEvent.connect(keyReleaseHandler); keyPressSignalsConnected = true; Script.scriptEnding.connect(unload); + + function Emote() { + _this = this; + } + + Emote.prototype = { + handleFTUEScreensVisibilityChanged: handleFTUEScreensVisibilityChanged + }; + + return new Emote(); } @@ -638,7 +650,6 @@ function unload() { maybeDeleteRemoteIndicatorTimeout(); Window.minimizedChanged.disconnect(onWindowMinimizedChanged); - Window.geometryChanged.disconnect(onGeometryChanged); HMD.displayModeChanged.disconnect(onDisplayModeChanged); if (keyPressSignalsConnected) { @@ -671,7 +682,6 @@ function unload() { // #region EMOJI_UTILITY -var EMOJI_52_BASE_URL = "../../resources/images/emojis/52px/"; function selectedEmoji(code) { emojiAPI.addEmoji(code); // this URL needs to be relative to SimplifiedEmoteIndicator.qml @@ -786,4 +796,6 @@ function toggleEmojiApp() { // END EMOJI // ************************************* -setup(); \ No newline at end of file +var emote = setup(); + +module.exports = emote; \ No newline at end of file diff --git a/scripts/simplifiedUI/simplifiedEmote/ui/qml/images/emote_Icon.svg b/scripts/simplifiedUI/simplifiedEmote/ui/qml/images/emote_Icon.svg index 99c88cc20c..967ced7ae6 100644 --- a/scripts/simplifiedUI/simplifiedEmote/ui/qml/images/emote_Icon.svg +++ b/scripts/simplifiedUI/simplifiedEmote/ui/qml/images/emote_Icon.svg @@ -1,15 +1,40 @@ - - - + + + + + + + + + +]> + + + + + + + + + + + + + + diff --git a/scripts/simplifiedUI/ui/simplifiedFTUE/InitialLaunchWindow.qml b/scripts/simplifiedUI/ui/simplifiedFTUE/InitialLaunchWindow.qml new file mode 100644 index 0000000000..1938586edb --- /dev/null +++ b/scripts/simplifiedUI/ui/simplifiedFTUE/InitialLaunchWindow.qml @@ -0,0 +1,350 @@ +// +// InitialLaunchWindow.qml +// +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import QtGraphicalEffects 1.0 +import QtQuick.Layouts 1.3 +import stylesUit 1.0 as HifiStylesUit +import TabletScriptingInterface 1.0 +import hifi.simplifiedUI.simplifiedConstants 1.0 as SimplifiedConstants +import hifi.simplifiedUI.simplifiedControls 1.0 as SimplifiedControls + +Rectangle { + id: root + color: simplifiedUI.colors.white + anchors.fill: parent + property bool landscapeOrientation: root.width > root.height + + SimplifiedConstants.SimplifiedConstants { + id: simplifiedUI + } + + Component.onCompleted: { + var debugFTUE = Settings.getValue("simplifiedUI/debugFTUE", 0); + + if ((debugFTUE !== 1 && + (Settings.getValue("simplifiedUI/alreadyAutoSelectedAvatarFromInventory", false) || + Settings.getValue("simplifiedUI/closedAvatarPageOfInitialLaunchWindow", false))) || + debugFTUE === 2) { + tempAvatarPageContainer.visible = false; + controlsContainer.visible = true; + } + } + + Item { + id: tempAvatarPageContainer + anchors.fill: parent + + Item { + id: contentContainer + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: firstPageBottomBarContainer.top + + Image { + id: avatarImage + anchors.verticalCenter: parent.verticalCenter + height: Math.max(parent.height - 48, 350) + anchors.left: parent.left + anchors.leftMargin: 12 + source: resourceDirectoryUrl + "qml/hifi/simplifiedUI/avatarApp/images/" + + MyAvatar.skeletonModelURL.substring(MyAvatar.skeletonModelURL.indexOf("simplifiedAvatar"), MyAvatar.skeletonModelURL.lastIndexOf("/")) + ".png" + mipmap: true + fillMode: Image.PreserveAspectFit + } + + Flickable { + id: textContainer + clip: true + anchors.top: parent.top + anchors.topMargin: 128 + anchors.bottom: qrAndInstructionsContainer.top + anchors.bottomMargin: 32 + anchors.left: avatarImage.right + anchors.leftMargin: 48 + anchors.right: parent.right + contentWidth: width + contentHeight: contentItem.childrenRect.height + interactive: contentHeight > height + + HifiStylesUit.RalewayBold { + id: headerText + text: "We know this isn't you..." + color: simplifiedUI.colors.text.black + size: 48 + height: paintedHeight + wrapMode: Text.Wrap + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: 16 + } + + HifiStylesUit.RalewayRegular { + id: descriptionText + anchors.top: headerText.bottom + anchors.topMargin: 10 + anchors.left: parent.left + width: Math.min(700, parent.width) - headerText.anchors.rightMargin + height: paintedHeight + text: "...but we've given you this temporary avatar to use " + + "for today. If you see this avatar in-world, walk up and " + + "say hello to other new users!

    " + + "We want you to be you so we've built " + + 'Virtual You, an Avatar Creator ' + + "App. Creating an avatar is as easy as taking a selfie and picking your " + + "outfits! Available now on iOS and Android." + color: simplifiedUI.colors.text.black + size: 22 + wrapMode: Text.Wrap + + onLinkActivated: { + Qt.openUrlExternally(link); + } + } + } + + Item { + id: qrAndInstructionsContainer + anchors.left: avatarImage.right + anchors.leftMargin: 48 + anchors.right: parent.right + anchors.rightMargin: 16 + anchors.bottom: parent.bottom + height: 130 + + Image { + id: avatarAppQRCodeImage + source: resourceDirectoryUrl + "qml/hifi/simplifiedUI/avatarApp/images/qrCode.jpg" + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: parent.left + width: 130 + mipmap: true + fillMode: Image.PreserveAspectFit + } + + HifiStylesUit.RalewayBold { + id: instructionText + anchors.top: avatarAppQRCodeImage.top + anchors.bottom: avatarAppQRCodeImage.bottom + anchors.left: avatarAppQRCodeImage.right + anchors.leftMargin: 30 + anchors.right: parent.right + text: "Use your mobile phone to scan this QR code." + color: simplifiedUI.colors.text.black + size: 22 + wrapMode: Text.Wrap + } + } + + SimplifiedControls.VerticalScrollBar { + parent: textContainer + visible: parent.contentHeight > parent.height + size: parent.height / parent.contentHeight + } + } + + Item { + id: firstPageBottomBarContainer + anchors.left: parent.left + anchors.leftMargin: 32 + anchors.right: parent.right + anchors.rightMargin: 32 + anchors.bottom: parent.bottom + height: continueLink.height + 48 + + HifiStylesUit.RalewayBold { + id: continueLink + anchors.centerIn: parent + text: "Continue >" + width: parent.width + height: paintedHeight + color: simplifiedUI.colors.text.lightBlue + opacity: continueMouseArea.containsMouse ? 1.0 : 0.7 + size: 36 + wrapMode: Text.Wrap + horizontalAlignment: Text.AlignHCenter + + MouseArea { + id: continueMouseArea + hoverEnabled: true + anchors.fill: parent + + onClicked: { + Tablet.playSound(TabletEnums.ButtonClick); + tempAvatarPageContainer.visible = false; + Settings.setValue("simplifiedUI/closedAvatarPageOfInitialLaunchWindow", true); + controlsContainer.visible = true; + } + } + } + } + } + + Item { + id: controlsContainer + visible: false + anchors.fill: parent + + HifiStylesUit.RalewayRegular { + id: controlsDescriptionText + text: "Use these avatar controls to
    interact with and move around in your new HQ." + anchors.top: parent.top + anchors.topMargin: 48 + anchors.left: parent.left + anchors.leftMargin: 32 + anchors.right: parent.right + anchors.rightMargin: 32 + horizontalAlignment: Text.AlignHCenter + height: paintedHeight + color: simplifiedUI.colors.text.black + size: 36 + wrapMode: Text.Wrap + } + + Item { + anchors.top: controlsDescriptionText.bottom + anchors.topMargin: 16 + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: bottomBarContainer.top + + GridView { + id: controlsGrid + property int maxColumns: 2 + property int idealCellWidth: 361 + anchors.fill: parent + clip: true + cellWidth: width / Math.min(Math.floor(width / idealCellWidth), maxColumns) + cellHeight: 225 + model: ListModel { + ListElement { + imageHeight: 198 + imageSource: "images/walkingControls.png" + } + ListElement { + imageHeight: 193 + imageSource: "images/mouseControls.png" + } + ListElement { + imageHeight: 146 + imageSource: "images/runJumpControls.png" + } + ListElement { + imageHeight: 96 + imageSource: "images/cameraControls.png" + } + } + delegate: Rectangle { + height: GridView.view.cellHeight + width: GridView.view.cellWidth + Image { + anchors.centerIn: parent + width: parent.GridView.view.idealCellWidth + height: model.imageHeight + source: model.imageSource + fillMode: Image.PreserveAspectFit + } + } + } + + SimplifiedControls.VerticalScrollBar { + parent: controlsGrid + anchors.topMargin: 96 + anchors.bottomMargin: anchors.topMargin + } + } + + Item { + id: bottomBarContainer + anchors.left: parent.left + anchors.leftMargin: 32 + anchors.right: parent.right + anchors.rightMargin: 32 + anchors.bottom: parent.bottom + height: iHaveAGoodGrip.height + learnMoreLink.height + 48 + + HifiStylesUit.RalewayBold { + id: iHaveAGoodGrip + anchors.centerIn: parent + text: "I've got a good grip on the controls." + width: parent.width + height: paintedHeight + color: simplifiedUI.colors.text.lightBlue + opacity: goodGripMouseArea.containsMouse ? 1.0 : 0.7 + size: 36 + wrapMode: Text.Wrap + horizontalAlignment: Text.AlignHCenter + + MouseArea { + id: goodGripMouseArea + hoverEnabled: true + anchors.fill: parent + + onClicked: { + Tablet.playSound(TabletEnums.ButtonClick); + sendToScript({ + "source": "InitialLaunchWindow.qml", + "method": "closeInitialLaunchWindow" + }); + } + } + } + + HifiStylesUit.RalewayBold { + id: learnMoreLink + anchors.left: parent.left + anchors.leftMargin: 16 + anchors.top: iHaveAGoodGrip.bottom + anchors.topMargin: 8 + text: "Learn more about our controls." + width: paintedWidth + height: paintedHeight + color: simplifiedUI.colors.text.lightBlue + opacity: learnMoreAboutControlsMouseArea.containsMouse ? 1.0 : 0.7 + size: 14 + wrapMode: Text.Wrap + + MouseArea { + id: learnMoreAboutControlsMouseArea + hoverEnabled: true + anchors.fill: parent + + onClicked: { + Tablet.playSound(TabletEnums.ButtonClick); + Qt.openUrlExternally("https://www.highfidelity.com/knowledge/get-around"); + } + } + } + } + } + + Image { + id: topLeftAccentImage + width: 400 + height: 180 + anchors.left: parent.left + anchors.top: parent.top + source: "images/defaultTopLeft.png" + } + + Image { + id: bottomRightAccentImage + width: 80 + height: 250 + anchors.right: parent.right + anchors.bottom: parent.bottom + source: "images/defaultBottomRight.png" + } + + signal sendToScript(var message); +} diff --git a/scripts/simplifiedUI/ui/simplifiedFTUE/SecondLaunchWindow.qml b/scripts/simplifiedUI/ui/simplifiedFTUE/SecondLaunchWindow.qml new file mode 100644 index 0000000000..2a796465ae --- /dev/null +++ b/scripts/simplifiedUI/ui/simplifiedFTUE/SecondLaunchWindow.qml @@ -0,0 +1,186 @@ +// +// SecondLaunchWindow.qml +// +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import QtGraphicalEffects 1.0 +import QtQuick.Layouts 1.3 +import stylesUit 1.0 as HifiStylesUit +import TabletScriptingInterface 1.0 +import hifi.simplifiedUI.simplifiedConstants 1.0 as SimplifiedConstants +import hifi.simplifiedUI.simplifiedControls 1.0 as SimplifiedControls + +Rectangle { + id: root + color: simplifiedUI.colors.white + anchors.fill: parent + + SimplifiedConstants.SimplifiedConstants { + id: simplifiedUI + } + + Item { + id: contentContainer + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: continueLink.top + + Image { + id: avatarImage + anchors.verticalCenter: parent.verticalCenter + height: Math.max(parent.height - 48, 350) + anchors.left: parent.left + anchors.leftMargin: 12 + source: resourceDirectoryUrl + "qml/hifi/simplifiedUI/avatarApp/images/hero.png" + mipmap: true + fillMode: Image.PreserveAspectFit + } + + Item { + anchors.top: parent.top + anchors.topMargin: 196 + anchors.bottom: parent.bottom + anchors.bottomMargin: 32 + anchors.left: avatarImage.right + anchors.leftMargin: 48 + anchors.right: parent.right + + Flickable { + id: textContainer + clip: true + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: parent.left + width: Math.min(700, parent.width) + contentWidth: width + contentHeight: contentItem.childrenRect.height + interactive: contentHeight > height + + HifiStylesUit.RalewayBold { + id: headerText + text: "Stand out from the crowd!" + color: simplifiedUI.colors.text.black + size: 48 + height: paintedHeight + wrapMode: Text.Wrap + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: 16 + } + + HifiStylesUit.RalewayRegular { + id: descriptionText + anchors.top: headerText.bottom + anchors.topMargin: 10 + anchors.left: parent.left + width: parent.width - headerText.anchors.rightMargin + height: paintedHeight + text: "You can create and upload custom avatars from our Avatar Creator App. " + + "It's as easy as taking a selfie.
    Available now on iOS and Android Platforms." + color: simplifiedUI.colors.text.black + size: 22 + wrapMode: Text.Wrap + } + + Item { + id: qrAndInstructionsContainer + anchors.top: descriptionText.bottom + anchors.topMargin: 24 + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: 16 + height: avatarAppQRCodeImage.height + + Image { + id: avatarAppQRCodeImage + source: resourceDirectoryUrl + "qml/hifi/simplifiedUI/avatarApp/images/qrCode.jpg" + anchors.top: parent.top + anchors.left: parent.left + width: 130 + height: width + mipmap: true + fillMode: Image.PreserveAspectFit + } + + HifiStylesUit.RalewayBold { + id: instructionText + anchors.top: avatarAppQRCodeImage.top + anchors.bottom: avatarAppQRCodeImage.bottom + anchors.left: avatarAppQRCodeImage.right + anchors.leftMargin: 30 + anchors.right: parent.right + text: "Use your mobile phone to scan this QR code." + color: simplifiedUI.colors.text.black + size: 22 + wrapMode: Text.Wrap + } + } + } + } + + SimplifiedControls.VerticalScrollBar { + parent: textContainer + visible: parent.contentHeight > parent.height + size: parent.height / parent.contentHeight + } + } + + + HifiStylesUit.RalewayBold { + id: continueLink + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.leftMargin: 16 + anchors.right: parent.right + anchors.rightMargin: 16 + height: 96 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + text: "No thanks, I'll keep using my default avatar." + color: simplifiedUI.colors.text.lightBlue + opacity: continueMouseArea.containsMouse ? 1.0 : 0.7 + size: 24 + + MouseArea { + id: continueMouseArea + hoverEnabled: true + anchors.fill: parent + + onClicked: { + Tablet.playSound(TabletEnums.ButtonClick); + sendToScript({ + "source": "SecondLaunchWindow.qml", + "method": "closeSecondLaunchWindow" + }); + } + } + } + + Image { + id: topLeftAccentImage + width: 130 + height: 320 + anchors.left: parent.left + anchors.top: parent.top + source: "images/standOutTopLeft.png" + } + + Image { + id: bottomRightAccentImage + width: 250 + height: 80 + anchors.right: parent.right + anchors.bottom: parent.bottom + source: "images/standOutBottomRight.png" + } + + signal sendToScript(var message); +} diff --git a/scripts/simplifiedUI/ui/simplifiedFTUE/images/cameraControls.png b/scripts/simplifiedUI/ui/simplifiedFTUE/images/cameraControls.png new file mode 100644 index 0000000000..e54e26a3ba Binary files /dev/null and b/scripts/simplifiedUI/ui/simplifiedFTUE/images/cameraControls.png differ diff --git a/scripts/simplifiedUI/ui/simplifiedFTUE/images/defaultBottomRight.png b/scripts/simplifiedUI/ui/simplifiedFTUE/images/defaultBottomRight.png new file mode 100644 index 0000000000..1668347aa4 Binary files /dev/null and b/scripts/simplifiedUI/ui/simplifiedFTUE/images/defaultBottomRight.png differ diff --git a/scripts/simplifiedUI/ui/simplifiedFTUE/images/defaultTopLeft.png b/scripts/simplifiedUI/ui/simplifiedFTUE/images/defaultTopLeft.png new file mode 100644 index 0000000000..c863569d7f Binary files /dev/null and b/scripts/simplifiedUI/ui/simplifiedFTUE/images/defaultTopLeft.png differ diff --git a/scripts/simplifiedUI/ui/simplifiedFTUE/images/mouseControls.png b/scripts/simplifiedUI/ui/simplifiedFTUE/images/mouseControls.png new file mode 100644 index 0000000000..6354b1aeae Binary files /dev/null and b/scripts/simplifiedUI/ui/simplifiedFTUE/images/mouseControls.png differ diff --git a/scripts/simplifiedUI/ui/simplifiedFTUE/images/runJumpControls.png b/scripts/simplifiedUI/ui/simplifiedFTUE/images/runJumpControls.png new file mode 100644 index 0000000000..af0492475d Binary files /dev/null and b/scripts/simplifiedUI/ui/simplifiedFTUE/images/runJumpControls.png differ diff --git a/scripts/simplifiedUI/ui/simplifiedFTUE/images/standOutBottomRight.png b/scripts/simplifiedUI/ui/simplifiedFTUE/images/standOutBottomRight.png new file mode 100644 index 0000000000..8b9983bb88 Binary files /dev/null and b/scripts/simplifiedUI/ui/simplifiedFTUE/images/standOutBottomRight.png differ diff --git a/scripts/simplifiedUI/ui/simplifiedFTUE/images/standOutTopLeft.png b/scripts/simplifiedUI/ui/simplifiedFTUE/images/standOutTopLeft.png new file mode 100644 index 0000000000..30cb623f42 Binary files /dev/null and b/scripts/simplifiedUI/ui/simplifiedFTUE/images/standOutTopLeft.png differ diff --git a/scripts/simplifiedUI/ui/simplifiedFTUE/images/walkingControls.png b/scripts/simplifiedUI/ui/simplifiedFTUE/images/walkingControls.png new file mode 100644 index 0000000000..bedc6991bb Binary files /dev/null and b/scripts/simplifiedUI/ui/simplifiedFTUE/images/walkingControls.png differ diff --git a/scripts/simplifiedUI/ui/simplifiedNametag/resources/modules/nameTagListManager.js b/scripts/simplifiedUI/ui/simplifiedNametag/resources/modules/nameTagListManager.js index a38ba129db..c073a06589 100644 --- a/scripts/simplifiedUI/ui/simplifiedNametag/resources/modules/nameTagListManager.js +++ b/scripts/simplifiedUI/ui/simplifiedNametag/resources/modules/nameTagListManager.js @@ -22,6 +22,31 @@ var SECONDS_IN_MINUTE = 60; // Delete after 5 minutes in case a nametag is hanging around in on mode var ALWAYS_ON_MAX_LIFETIME_IN_SECONDS = 5 * SECONDS_IN_MINUTE; +// ************************************* +// START STARTUP/SHUTDOWN +// ************************************* +// #region STARTUP/SHUTDOWN + + +// Connect the camera mode updated signal on startup +function startup() { + Camera.modeUpdated.connect(handleCameraModeChanged); + cameraModeUpdatedSignalConnected = true; + + Script.scriptEnding.connect(shutdown); +} + +startup(); + +function shutdown() { + maybeDisconnectCameraModeUpdatedSignal(); +} + + +// ************************************* +// END STARTUP/SHUTDOWN +// ************************************* + // ************************************* // START UTILTY // ************************************* @@ -197,6 +222,27 @@ function toggleInterval() { } +// Disconnect the camera mode updated signal if we have one connected for the selfie mode +var cameraModeUpdatedSignalConnected = false; +function maybeDisconnectCameraModeUpdatedSignal() { + if (cameraModeUpdatedSignalConnected) { + Camera.modeUpdated.disconnect(handleCameraModeChanged); + cameraModeUpdatedSignalConnected = false; + } +} + + +// Turn on the nametag for yourself if you are in selfie mode, other wise delete it +function handleCameraModeChanged(mode) { + if (mode === "selfie") { + if (avatarNametagMode === "alwaysOn") { + add(MyAvatar.sessionUUID); + } + } else { + maybeRemove(MyAvatar.sessionUUID); + } +} + // Handle checking to see if we should add or delete nametags in persistent mode var alwaysOnAvatarDistanceCheck = false; var DISTANCE_CHECK_INTERVAL_MS = 1000; @@ -215,6 +261,10 @@ function handleAlwaysOnMode(shouldTurnOnAlwaysOnMode) { }); maybeClearAlwaysOnAvatarDistanceCheck(); alwaysOnAvatarDistanceCheck = Script.setInterval(maybeAddOrRemoveIntervalCheck, DISTANCE_CHECK_INTERVAL_MS); + + if (Camera.mode === "selfie") { + add(MyAvatar.sessionUUID); + } } } diff --git a/scripts/simplifiedUI/ui/simplifiedUI.js b/scripts/simplifiedUI/ui/simplifiedUI.js index 1154e386ea..3025b938cb 100644 --- a/scripts/simplifiedUI/ui/simplifiedUI.js +++ b/scripts/simplifiedUI/ui/simplifiedUI.js @@ -14,7 +14,6 @@ // START CONFIG OPTIONS -var DOCKED_QML_SUPPORTED = true; var TOOLBAR_NAME = "com.highfidelity.interface.toolbar.system"; var DEFAULT_SCRIPTS_PATH_PREFIX = ScriptDiscoveryService.defaultScriptsPath + "/"; // END CONFIG OPTIONS @@ -160,6 +159,7 @@ var SETTINGS_APP_WINDOW_FLAGS = 0x00000001 | // Qt::Window 0x08000000 | // Qt::WindowCloseButtonHint 0x00008000 | // Qt::WindowMaximizeButtonHint 0x00004000; // Qt::WindowMinimizeButtonHint +var SETTINGS_APP_RIGHT_MARGIN = 48; var settingsAppWindow = false; function toggleSettingsApp() { if (settingsAppWindow) { @@ -179,7 +179,7 @@ function toggleSettingsApp() { y: SETTINGS_APP_HEIGHT_PX }, position: { - x: Math.max(Window.x + POPOUT_SAFE_MARGIN_X, Window.x + Window.innerWidth / 2 - SETTINGS_APP_WIDTH_PX / 2), + x: Window.x + Window.innerWidth - SETTINGS_APP_WIDTH_PX - SETTINGS_APP_RIGHT_MARGIN, y: Math.max(Window.y + POPOUT_SAFE_MARGIN_Y, Window.y + Window.innerHeight / 2 - SETTINGS_APP_HEIGHT_PX / 2) }, overrideFlags: SETTINGS_APP_WINDOW_FLAGS @@ -283,28 +283,21 @@ function maybeDeleteOutputDeviceMutedOverlay() { var outputDeviceMutedOverlay = false; var OUTPUT_DEVICE_MUTED_OVERLAY_DEFAULT_DIMS_PX = 300; -var OUTPUT_DEVICE_MUTED_MARGIN_BOTTOM_PX = 20; -var OUTPUT_DEVICE_MUTED_MARGIN_LEFT_RIGHT_PX = 20; +var OUTPUT_DEVICE_MUTED_OVERLAY_DEFAULT_MARGINS_PX = 20; +var OUTPUT_DEVICE_MUTED_DIMS_RATIO_TO_WINDOW_SIZE = 0.8; function updateOutputDeviceMutedOverlay(isMuted) { if (isMuted) { var props = { imageURL: Script.resolvePath("images/outputDeviceMuted.svg"), alpha: 0.5 }; + var overlayDims = OUTPUT_DEVICE_MUTED_OVERLAY_DEFAULT_DIMS_PX; - props.x = Window.innerWidth / 2 - overlayDims / 2; - props.y = Window.innerHeight / 2 - overlayDims / 2; + var overlayWithMarginsDims = overlayDims + 2 * OUTPUT_DEVICE_MUTED_OVERLAY_DEFAULT_MARGINS_PX; - var outputDeviceMutedOverlayBottomY = props.y + overlayDims; - 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); - } - - if (overlayDims + OUTPUT_DEVICE_MUTED_MARGIN_LEFT_RIGHT_PX > Window.innerWidth) { - overlayDims = Math.min(Window.innerWidth - OUTPUT_DEVICE_MUTED_MARGIN_LEFT_RIGHT_PX, overlayDims); - } else { - overlayDims = Math.min(OUTPUT_DEVICE_MUTED_OVERLAY_DEFAULT_DIMS_PX, overlayDims); + if (overlayWithMarginsDims > Window.innerHeight || overlayWithMarginsDims > Window.innerWidth) { + var minWindowDims = Math.min(Window.innerHeight, Window.innerWidth); + overlayDims = Math.round(minWindowDims * OUTPUT_DEVICE_MUTED_DIMS_RATIO_TO_WINDOW_SIZE); } props.width = overlayDims; @@ -358,6 +351,124 @@ function setOutputMuted(outputMuted) { } } +var TOP_BAR_HEIGHT_PX = 48; +var INITIAL_LAUNCH_QML_PATH = Script.resolvePath("./simplifiedFTUE/InitialLaunchWindow.qml"); +var INITIAL_LAUNCH_WINDOW_TITLE = "Initial Launch"; +var INITIAL_LAUNCH_PRESENTATION_MODE = Desktop.PresentationMode.NATIVE; +var INITIAL_WINDOW_FLAGS = 0x00000001 | // Qt::Window +0x00000008 | // Qt::Popup +0x00000002 | // Qt::Tool +0x00000800 | // Qt::FramelessWindowHint +0x40000000; // Qt::NoDropShadowWindowHint +var initialLaunchWindow = false; +function displayInitialLaunchWindow() { + if (initialLaunchWindow) { + return; + } + + simplifiedEmote.handleFTUEScreensVisibilityChanged(true); + + initialLaunchWindow = Desktop.createWindow(INITIAL_LAUNCH_QML_PATH, { + title: INITIAL_LAUNCH_WINDOW_TITLE, + presentationMode: INITIAL_LAUNCH_PRESENTATION_MODE, + isFullScreenWindow: true, + overrideFlags: INITIAL_WINDOW_FLAGS + }); + + initialLaunchWindow.fromQml.connect(onMessageFromInitialLaunchWindow); + + Window.location = "file:///~/serverless/tutorial.json"; +} + +var SECOND_LAUNCH_QML_PATH = Script.resolvePath("simplifiedFTUE/SecondLaunchWindow.qml"); +var SECOND_LAUNCH_WINDOW_TITLE = "Second Launch"; +var SECOND_LAUNCH_PRESENTATION_MODE = Desktop.PresentationMode.NATIVE; +var SECOND_WINDOW_FLAGS = 0x00000001 | // Qt::Window +0x00000008 | // Qt::Popup +0x00000002 | // Qt::Tool +0x00000800 | // Qt::FramelessWindowHint +0x40000000; // Qt::NoDropShadowWindowHint +var secondLaunchWindow = false; +function displaySecondLaunchWindow() { + if (secondLaunchWindow) { + return; + } + + simplifiedEmote.handleFTUEScreensVisibilityChanged(true); + + secondLaunchWindow = Desktop.createWindow(SECOND_LAUNCH_QML_PATH, { + title: SECOND_LAUNCH_WINDOW_TITLE, + presentationMode: SECOND_LAUNCH_PRESENTATION_MODE, + isFullScreenWindow: true, + overrideFlags: SECOND_WINDOW_FLAGS + }); + + secondLaunchWindow.fromQml.connect(onMessageFromSecondLaunchWindow); + + Window.location = "file:///~/serverless/tutorial.json"; +} + +function closeInitialLaunchWindow() { + if (initialLaunchWindow) { + initialLaunchWindow.fromQml.disconnect(onMessageFromInitialLaunchWindow); + initialLaunchWindow.close(); + initialLaunchWindow = null; + } + + simplifiedEmote.handleFTUEScreensVisibilityChanged(false); +} + +function closeSecondLaunchWindow() { + if (secondLaunchWindow) { + secondLaunchWindow.fromQml.disconnect(onMessageFromSecondLaunchWindow); + secondLaunchWindow.close(); + secondLaunchWindow = null; + } + + simplifiedEmote.handleFTUEScreensVisibilityChanged(false); +} + +var INITIAL_LAUNCH_WINDOW_MESSAGE_SOURCE = "InitialLaunchWindow.qml"; +function onMessageFromInitialLaunchWindow(message) { + if (message.source !== INITIAL_LAUNCH_WINDOW_MESSAGE_SOURCE) { + return; + } + + switch (message.method) { + case "closeInitialLaunchWindow": + closeInitialLaunchWindow(); + var homeLocation = LocationBookmarks.getAddress("hqhome"); + if (homeLocation) { + Window.location = homeLocation; + } + break; + + default: + console.log("Unrecognized message from " + INITIAL_LAUNCH_WINDOW_MESSAGE_SOURCE + ": " + JSON.stringify(message)); + break; + } +} + +var SECOND_LAUNCH_WINDOW_MESSAGE_SOURCE = "SecondLaunchWindow.qml"; +function onMessageFromSecondLaunchWindow(message) { + if (message.source !== SECOND_LAUNCH_WINDOW_MESSAGE_SOURCE) { + return; + } + + switch (message.method) { + case "closeSecondLaunchWindow": + closeSecondLaunchWindow(); + var homeLocation = LocationBookmarks.getAddress("hqhome"); + if (homeLocation) { + Window.location = homeLocation; + } + break; + + default: + console.log("Unrecognized message from " + SECOND_LAUNCH_WINDOW_MESSAGE_SOURCE + ": " + JSON.stringify(message)); + break; + } +} var WAIT_FOR_TOP_BAR_MS = 1000; function sendLocalStatusToQml() { @@ -403,6 +514,14 @@ function onMessageFromTopBar(message) { si.toggleStatus(); break; + case "displayInitialLaunchWindow": + displayInitialLaunchWindow(); + break; + + case "displaySecondLaunchWindow": + displaySecondLaunchWindow(); + break; + default: console.log("Unrecognized message from " + TOP_BAR_MESSAGE_SOURCE + ": " + JSON.stringify(message)); break; @@ -431,7 +550,6 @@ var TOP_BAR_QML_PATH = Script.resourcesPath() + "qml/hifi/simplifiedUI/topBar/Si var TOP_BAR_WINDOW_TITLE = "Simplified Top Bar"; var TOP_BAR_PRESENTATION_MODE = Desktop.PresentationMode.NATIVE; var TOP_BAR_WIDTH_PX = Window.innerWidth; -var TOP_BAR_HEIGHT_PX = 48; var topBarWindow = false; function loadSimplifiedTopBar() { var windowProps = { @@ -442,16 +560,9 @@ function loadSimplifiedTopBar() { y: TOP_BAR_HEIGHT_PX } }; - if (DOCKED_QML_SUPPORTED) { - windowProps.presentationWindowInfo = { - dockArea: Desktop.DockArea.TOP - }; - } else { - windowProps.position = { - x: Window.x, - y: Window.y - }; - } + windowProps.presentationWindowInfo = { + dockArea: Desktop.DockArea.TOP + }; topBarWindow = Desktop.createWindow(TOP_BAR_QML_PATH, windowProps); topBarWindow.fromQml.connect(onMessageFromTopBar); @@ -516,32 +627,53 @@ function onHMDInputDeviceMutedChanged(isMuted) { function onGeometryChanged(rect) { updateInputDeviceMutedOverlay(Audio.muted); updateOutputDeviceMutedOverlay(isOutputMuted()); - if (topBarWindow && !DOCKED_QML_SUPPORTED) { - topBarWindow.size = { - "x": rect.width, - "y": TOP_BAR_HEIGHT_PX - }; - topBarWindow.position = { - "x": rect.x, - "y": rect.y - }; +} + +var initialLaunchWindowIsMinimized = false; +var secondLaunchWindowIsMinimized = false; +function onWindowMinimizedChanged(isMinimized) { + if (isMinimized) { + handleInitialLaunchWindowVisibleChanged(false); + handleSecondLaunchWindowVisibleChanged(false); + } else if (!HMD.active) { + handleInitialLaunchWindowVisibleChanged(true); + handleSecondLaunchWindowVisibleChanged(true); } } -function onWindowMinimizedChanged() { - // prerequisite placeholder for Reduce Friction of Customer Acquisition sub task: https://highfidelity.atlassian.net/browse/DEV-585 - print("WINDOW MINIMIZED CHANGED SIGNAL"); +function handleInitialLaunchWindowVisibleChanged(shouldBeVisible) { + if (shouldBeVisible && !initialLaunchWindow && initialLaunchWindowIsMinimized) { + displayInitialLaunchWindow(); + initialLaunchWindowIsMinimized = false; + } else if (!shouldBeVisible && initialLaunchWindow) { + closeInitialLaunchWindow(); + initialLaunchWindowIsMinimized = true; + } +} + +function handleSecondLaunchWindowVisibleChanged(shouldBeVisible) { + if (shouldBeVisible && !secondLaunchWindow && secondLaunchWindowIsMinimized) { + displaySecondLaunchWindow(); + secondLaunchWindowIsMinimized = false; + } else if (!shouldBeVisible && secondLaunchWindow) { + closeSecondLaunchWindow(); + secondLaunchWindowIsMinimized = true; + } } function onDisplayModeChanged(isHMDMode) { if (isHMDMode) { - Camera.setModeString("first person"); + Camera.setModeString("first person look at"); } if (isHMDMode) { onHMDInputDeviceMutedChanged(Audio.mutedHMD); + handleInitialLaunchWindowVisibleChanged(false); + handleSecondLaunchWindowVisibleChanged(false); } else { onDesktopInputDeviceMutedChanged(Audio.mutedDesktop); + handleInitialLaunchWindowVisibleChanged(true); + handleSecondLaunchWindowVisibleChanged(true); } } @@ -584,9 +716,9 @@ function restoreLODSettings() { } -var nametag = Script.require("./simplifiedNametag/simplifiedNametag.js?" + Date.now()); -var si = Script.require("./simplifiedStatusIndicator/simplifiedStatusIndicator.js?" + Date.now()); -var emote = Script.require("../simplifiedEmote/simplifiedEmote.js?" + Date.now()); +var nametag = Script.require("./simplifiedNametag/simplifiedNametag.js"); +var si = Script.require("./simplifiedStatusIndicator/simplifiedStatusIndicator.js"); +var simplifiedEmote = Script.require("../simplifiedEmote/simplifiedEmote.js"); var oldShowAudioTools; var oldShowBubbleTools; var keepExistingUIAndScriptsSetting = Settings.getValue("simplifiedUI/keepExistingUIAndScripts", false); @@ -653,6 +785,14 @@ function shutdown() { settingsAppWindow.close(); } + if (initialLaunchWindow) { + closeInitialLaunchWindow(); + } + + if (secondLaunchWindow) { + closeSecondLaunchWindow(); + } + maybeDeleteInputDeviceMutedOverlay(); maybeDeleteOutputDeviceMutedOverlay(); diff --git a/scripts/system/away.js b/scripts/system/away.js index 6293c0c452..da24e0c3f8 100644 --- a/scripts/system/away.js +++ b/scripts/system/away.js @@ -50,11 +50,11 @@ var OVERLAY_DATA_HMD = { }; var AWAY_INTRO = { - url: "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/kneel.fbx", + url: "https://hifi-content.s3.amazonaws.com/doug/animation/fbx/afk_texting.fbx", playbackRate: 30.0, - loopFlag: false, - startFrame: 0.0, - endFrame: 83.0 + loopFlag: true, + startFrame: 1.0, + endFrame: 489.0 }; // MAIN CONTROL diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index cf365a4119..f0d3ec0c03 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -595,6 +595,10 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Controller.disableMapping(MAPPING_NAME); _this.pointerManager.removePointers(); Pointers.removePointer(this.mouseRayPointer); + Overlays.mouseReleaseOnOverlay.disconnect(mouseReleaseOnOverlay); + Overlays.mousePressOnOverlay.disconnect(mousePress); + Entities.mousePressOnEntity.disconnect(mousePress); + Messages.messageReceived.disconnect(controllerDispatcher.handleMessage); }; } diff --git a/scripts/system/create/assets/data/createAppTooltips.json b/scripts/system/create/assets/data/createAppTooltips.json index cda6fc9be0..24f443f901 100644 --- a/scripts/system/create/assets/data/createAppTooltips.json +++ b/scripts/system/create/assets/data/createAppTooltips.json @@ -76,7 +76,7 @@ "tooltip": "The angle in deg at which light emits. Starts in the entity's -z direction, and rotates around its x axis." }, "keyLight.castShadows": { - "tooltip": "If enabled, shadows are cast. The entity or avatar casting the shadow must also have Cast Shadows enabled." + "tooltip": "If enabled, shadows are cast. The entity or avatar casting the shadow must also have Cast Shadows enabled. Note: Shadows are rendered only on high-profiled computers. This setting will have no effect on computers profiled to medium or low graphics." }, "keyLight.shadowBias": { "tooltip": "The bias of the shadows cast by the light. Use this to fine-tune your shadows to your scene to prevent shadow acne and peter panning." @@ -500,7 +500,7 @@ "tooltip": "If enabled, grabbed entities will follow the movements of your hand controller instead of your avatar's hand." }, "canCastShadow": { - "tooltip": "If enabled, this geometry of this entity casts shadows when a shadow-casting light source shines on it." + "tooltip": "If enabled, the geometry of this entity casts shadows when a shadow-casting light source shines on it. Note: Shadows are rendered only on high-profiled computers. This setting will have no effect on computers profiled to medium or low graphics.." }, "ignorePickIntersection": { "tooltip": "If enabled, this entity will not be considered for ray picks, and will also not occlude other entities when picking." diff --git a/scripts/system/firstPersonHMD.js b/scripts/system/firstPersonHMD.js index 5fdee1b7b5..5e81d53f6b 100644 --- a/scripts/system/firstPersonHMD.js +++ b/scripts/system/firstPersonHMD.js @@ -16,7 +16,7 @@ // Automatically enter first person mode when entering HMD mode HMD.displayModeChanged.connect(function(isHMDMode) { if (isHMDMode) { - Camera.setModeString("first person"); + Camera.setModeString("first person look at"); } }); diff --git a/scripts/system/interstitialPage.js b/scripts/system/interstitialPage.js index 8ecc982dab..2d225fd2a6 100644 --- a/scripts/system/interstitialPage.js +++ b/scripts/system/interstitialPage.js @@ -272,7 +272,7 @@ currentProgress = 0.0; connectionToDomainFailed = false; previousCameraMode = Camera.mode; - Camera.mode = "first person"; + Camera.mode = "first person look at"; updateProgressBar(0.0); scaleInterstitialPage(MyAvatar.sensorToWorldScale); timer = Script.setTimeout(update, 2000); diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index 0ee5259ffa..a92fbf1065 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -52,8 +52,8 @@ function calcSpawnInfo(hand, landscape) { var LEFT_HAND = Controller.Standard.LeftHand; var sensorToWorldScale = MyAvatar.sensorToWorldScale; - var headPos = (HMD.active && Camera.mode === "first person") ? HMD.position : Camera.position; - var headRot = Quat.cancelOutRollAndPitch((HMD.active && Camera.mode === "first person") ? + var headPos = (HMD.active && (Camera.mode === "first person" || Camera.mode === "first person look at")) ? HMD.position : Camera.position; + var headRot = Quat.cancelOutRollAndPitch((HMD.active && (Camera.mode === "first person" || Camera.mode === "first person look at")) ? HMD.orientation : Camera.orientation); var right = Quat.getRight(headRot); diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index 91c8b1edcf..f5b5ecf0a1 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -53,7 +53,7 @@ function handJointName(hand) { var jointName; if (hand === LEFT_HAND) { - if (Camera.mode === "first person") { + if (Camera.mode === "first person" || Camera.mode === "first person look at") { jointName = "_CONTROLLER_LEFTHAND"; } else if (Camera.mode === "third person") { jointName = "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"; @@ -61,7 +61,7 @@ jointName = "LeftHand"; } } else { - if (Camera.mode === "first person") { + if (Camera.mode === "first person" || Camera.mode === "first person look at") { jointName = "_CONTROLLER_RIGHTHAND"; } else if (Camera.mode === "third person") { jointName = "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND"; diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index fd74786a5e..4942ecbd63 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -19,41 +19,31 @@ function(check_test name) endfunction() if (BUILD_TOOLS) + set(ALL_TOOLS + udt-test + vhacd-util + gpu-frame-player + ice-client + ktx-tool + ac-client + skeleton-dump + atp-client + oven + ) + # Allow different tools for stable builds if (STABLE_BUILD) - set(ALL_TOOLS - udt-test - vhacd-util - frame-optimizer - gpu-frame-player - ice-client - ktx-tool - ac-client - skeleton-dump - atp-client - oven - ) else() - set(ALL_TOOLS - udt-test - vhacd-util - frame-optimizer - gpu-frame-player - ice-client - ktx-tool - ac-client - skeleton-dump - atp-client - oven - nitpick - ) + list(APPEND ALL_TOOLS nitpick) endif() foreach(TOOL ${ALL_TOOLS}) check_test(${TOOL}) if (${BUILD_TOOL_RESULT}) add_subdirectory(${TOOL}) - set_target_properties(${TOOL} PROPERTIES FOLDER "Tools") + if (TARGET ${TOOL}) + set_target_properties(${TOOL} PROPERTIES FOLDER "Tools") + endif() endif() endforeach() endif() diff --git a/tools/ci-scripts/postbuild.py b/tools/ci-scripts/postbuild.py index f04217ce24..f229855b21 100644 --- a/tools/ci-scripts/postbuild.py +++ b/tools/ci-scripts/postbuild.py @@ -17,14 +17,12 @@ WIPE_PATHS = [] if sys.platform == "win32": WIPE_PATHS = [ - 'jsdoc', - 'resources/serverless' + 'jsdoc' ] elif sys.platform == "darwin": INTERFACE_BUILD_PATH = os.path.join(INTERFACE_BUILD_PATH, "Interface.app", "Contents", "Resources") WIPE_PATHS = [ - 'jsdoc', - 'serverless' + 'jsdoc' ] @@ -81,9 +79,6 @@ def fixupMacZip(filename): # ignore the nitpick app if newFilename.startswith('nitpick.app'): continue - # ignore the serverless content - if newFilename.startswith('interface.app/Contents/Resources/serverless'): - continue # if we made it here, include the file in the output buffer = inzip.read(entry.filename) entry.filename = newFilename diff --git a/tools/frame-optimizer/CMakeLists.txt b/tools/frame-optimizer/CMakeLists.txt deleted file mode 100644 index cc268c5baf..0000000000 --- a/tools/frame-optimizer/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -set(TARGET_NAME frame-optimizer) - -setup_memory_debugger() -setup_hifi_project(Gui Widgets) -link_hifi_libraries(shared ktx shaders gpu ) -package_libraries_for_deployment() diff --git a/tools/frame-optimizer/src/main.cpp b/tools/frame-optimizer/src/main.cpp deleted file mode 100644 index a4200c3d97..0000000000 --- a/tools/frame-optimizer/src/main.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// -// Created by Bradley Austin Davis on 2018/10/14 -// Copyright 2014 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 - -#ifdef Q_OS_WIN -#include -#endif - -#include -#include -#include - - -gpu::IndexOptimizer optimizer= [](gpu::Primitive primitive, uint32_t faceCount, uint32_t indexCount, uint32_t* indices ) { - // FIXME add a triangle index optimizer here -}; - - -void messageHandler(QtMsgType type, const QMessageLogContext &, const QString & message) { - auto messageStr = message.toStdString(); -#ifdef Q_OS_WIN - OutputDebugStringA(messageStr.c_str()); - OutputDebugStringA("\n"); -#endif - std::cerr << messageStr << std::endl; -} - -int main(int argc, char** argv) { - QCoreApplication app(argc, argv); - qInstallMessageHandler(messageHandler); - gpu::optimizeFrame("D:/Frames/20190112_1647.json", optimizer); - return 0; -} diff --git a/tools/gpu-frame-player/src/PlayerWindow.cpp b/tools/gpu-frame-player/src/PlayerWindow.cpp index e74caddd5e..8e7f730181 100644 --- a/tools/gpu-frame-player/src/PlayerWindow.cpp +++ b/tools/gpu-frame-player/src/PlayerWindow.cpp @@ -8,6 +8,8 @@ #include "PlayerWindow.h" +#include +#include #include #include #include @@ -55,7 +57,7 @@ void PlayerWindow::loadFrame() { } } - QString fileName = QFileDialog::getOpenFileName(nullptr, tr("Open File"), openDir, tr("GPU Frames (*.json)")); + QString fileName = QFileDialog::getOpenFileName(nullptr, tr("Open File"), openDir, tr("GPU Frames (*.hfb)")); if (fileName.isNull()) { return; } @@ -104,17 +106,8 @@ void PlayerWindow::resizeEvent(QResizeEvent* ev) { _renderThread.resize(ev->size()); } -void PlayerWindow::textureLoader(const std::string& filename, const gpu::TexturePointer& texture, uint16_t layer) { - QImage image; - QImageReader(filename.c_str()).read(&image); - if (layer > 0) { - return; - } - texture->assignStoredMip(0, image.byteCount(), image.constBits()); -} - void PlayerWindow::loadFrame(const QString& path) { - auto frame = gpu::readFrame(path.toStdString(), _renderThread._externalTexture, &PlayerWindow::textureLoader); + auto frame = gpu::readFrame(path.toStdString(), _renderThread._externalTexture); if (frame) { _renderThread.submitFrame(frame); if (!_renderThread.isThreaded()) { diff --git a/tools/gpu-frame-player/src/PlayerWindow.h b/tools/gpu-frame-player/src/PlayerWindow.h index 4dfbca0855..a519fd9339 100644 --- a/tools/gpu-frame-player/src/PlayerWindow.h +++ b/tools/gpu-frame-player/src/PlayerWindow.h @@ -28,7 +28,7 @@ protected: void loadFrame(const QString& path); private: - static void textureLoader(const std::string& filename, const gpu::TexturePointer& texture, uint16_t layer); + static void textureLoader(const std::vector& filename, const gpu::TexturePointer& texture, uint16_t layer); QSettings _settings; RenderThread _renderThread; }; diff --git a/tools/jsdoc/hifi-jsdoc-template/static/styles/jsdoc.css b/tools/jsdoc/hifi-jsdoc-template/static/styles/jsdoc.css index 2386f88586..dd67a4da20 100644 --- a/tools/jsdoc/hifi-jsdoc-template/static/styles/jsdoc.css +++ b/tools/jsdoc/hifi-jsdoc-template/static/styles/jsdoc.css @@ -96,7 +96,7 @@ h6 .paramHeading, .typeDef h3.propsHeading, h3.subHeading { - font-size: .9rem; + font-size: 1rem; font-family: "Proxima Nova"; font-weight: bold; border-bottom: solid 1px #ddd; @@ -108,9 +108,9 @@ h4.name padding-bottom: 0px; } -h5, .container-overview .subsection-title -{ - font-size: 13px; +h5, .container-overview .subsection-title { + font-size: 1rem; + font-family: "Proxima Nova"; font-weight: bold; margin-bottom: 5px; } @@ -143,6 +143,14 @@ td { border: solid #c7cccb 1px; } +td > p:first-child, td > ul:first-child { + margin-top: 0; +} + +td > p:last-child, td > ul:last-child { + margin-bottom: 0; +} + article table thead tr th, article table tbody tr td, article table tbody tr td p { font-size: .89rem; line-height: 20px; @@ -538,7 +546,7 @@ header { .prettyprint code { - font-size: 0.9em; + font-size: .95em; line-height: 18px; display: block; padding: 4px 12px; @@ -596,11 +604,6 @@ header { font-size: 100%; } -.params td.description > p:first-child, .props td.description > p:first-child { - margin-top: 0; - padding-top: 0; -} - span.param-type, .params td .param-type, .param-type dd { color: #606; font-family: Consolas, Monaco, 'Andale Mono', monospace diff --git a/tools/jsdoc/hifi-jsdoc-template/tmpl/container.tmpl b/tools/jsdoc/hifi-jsdoc-template/tmpl/container.tmpl index 5c149fa434..fccf5f7d31 100644 --- a/tools/jsdoc/hifi-jsdoc-template/tmpl/container.tmpl +++ b/tools/jsdoc/hifi-jsdoc-template/tmpl/container.tmpl @@ -63,11 +63,13 @@
    +
    - + +

    Description

    -

    +
    @@ -75,7 +77,8 @@

    Description

    -

    +
    +
    ValueDescription
    @@ -84,15 +87,15 @@ -

    +

    Parameters

    +
    -

    Example 1? 's':'' ?>

    @@ -101,7 +104,7 @@

    Description

    -

    +
    @@ -264,6 +267,7 @@ + + - - - - + diff --git a/tools/jsdoc/hifi-jsdoc-template/tmpl/description.tmpl b/tools/jsdoc/hifi-jsdoc-template/tmpl/description.tmpl new file mode 100644 index 0000000000..0e0ad27b37 --- /dev/null +++ b/tools/jsdoc/hifi-jsdoc-template/tmpl/description.tmpl @@ -0,0 +1,15 @@ +]*>', 'i'); + var descriptionIndex = description.search(descriptionRegExp); +?> + + +

    + +

    + + + + diff --git a/tools/jsdoc/hifi-jsdoc-template/tmpl/details.tmpl b/tools/jsdoc/hifi-jsdoc-template/tmpl/details.tmpl index 0661ee3e50..afb0e9464c 100644 --- a/tools/jsdoc/hifi-jsdoc-template/tmpl/details.tmpl +++ b/tools/jsdoc/hifi-jsdoc-template/tmpl/details.tmpl @@ -31,113 +31,113 @@ if (data.defaultvalue && (data.defaultvaluetype === 'object' || data.defaultvalu

    Version:

    -

    +

    Since:

    -

    +

    Inherited From:

    -

    • +
      • -

      +

    Overrides:

    -

    • +
      • -

      +

    Implementations:

    -

      +
      • -

      +

    Implements:

    -

      +
      • -

      +

    Mixes In:

    -

      +
      • -

      +

    Author:

    -

    +

    -

    +

    Copyright:

    -

    +

    License:

    -

    +

    Default Value:

    -

      - > -

    +
      +
    • >
    • +

    Source:

    -

    • +
      • , -

      +

    Tutorials:

    -

    +

    -

    +

    See:

    -

    +

    -

    +

    To Do:

    -

    +

    -

    +
    diff --git a/tools/jsdoc/hifi-jsdoc-template/tmpl/example.tmpl b/tools/jsdoc/hifi-jsdoc-template/tmpl/example.tmpl index e1b06d0a46..ea70a150c5 100644 --- a/tools/jsdoc/hifi-jsdoc-template/tmpl/example.tmpl +++ b/tools/jsdoc/hifi-jsdoc-template/tmpl/example.tmpl @@ -29,113 +29,113 @@ if (data.defaultvalue && (data.defaultvaluetype === 'object' || data.defaultvalu

    Version:

    -

    +

    Since:

    -

    +

    Inherited From:

    -

    • +
      • -

      +

    Overrides:

    -

    • +
      • -

      +

    Implementations:

    -

      +
      • -

      +

    Implements:

    -

      +
      • -

      +

    Mixes In:

    -

      +
      • -

      +

    Author:

    -

    +

    -

    +

    Copyright:

    -

    +

    License:

    -

    +

    Default Value:

    -

      - > -

    +
      +
    • >
    • +

    Source:

    -

    • +
      • , -

      +

    Tutorials:

    -

    +

    -

    +

    See:

    -

    +

    -

    +

    To Do:

    -

    +

    -

    +
    diff --git a/tools/jsdoc/hifi-jsdoc-template/tmpl/members.tmpl b/tools/jsdoc/hifi-jsdoc-template/tmpl/members.tmpl index eef64c1f3f..6a870b4cda 100644 --- a/tools/jsdoc/hifi-jsdoc-template/tmpl/members.tmpl +++ b/tools/jsdoc/hifi-jsdoc-template/tmpl/members.tmpl @@ -16,7 +16,7 @@ var self = this;
    -

    +

     

    diff --git a/tools/jsdoc/hifi-jsdoc-template/tmpl/method.tmpl b/tools/jsdoc/hifi-jsdoc-template/tmpl/method.tmpl index 8db1df8a77..1542ca1806 100644 --- a/tools/jsdoc/hifi-jsdoc-template/tmpl/method.tmpl +++ b/tools/jsdoc/hifi-jsdoc-template/tmpl/method.tmpl @@ -12,8 +12,9 @@ var self = this; Returns: - + + +
    Type: @@ -24,23 +25,23 @@ var self = this;
    -

    - - -

    + + + +

     

    -

    Throws:

    +

    Throws

    1) { ?>
    -

    + diff --git a/tools/jsdoc/hifi-jsdoc-template/tmpl/methodList.tmpl b/tools/jsdoc/hifi-jsdoc-template/tmpl/methodList.tmpl index 7d88cd5e51..982c231fd9 100644 --- a/tools/jsdoc/hifi-jsdoc-template/tmpl/methodList.tmpl +++ b/tools/jsdoc/hifi-jsdoc-template/tmpl/methodList.tmpl @@ -29,11 +29,9 @@ var self = this;
    - - - - + +
    -
    Properties
    - -
    + + + + +
    Properties
    + + +
    - + + +

    Default Value:

    diff --git a/tools/jsdoc/hifi-jsdoc-template/tmpl/signal.tmpl b/tools/jsdoc/hifi-jsdoc-template/tmpl/signal.tmpl index 00bf7122e1..76d6f78948 100644 --- a/tools/jsdoc/hifi-jsdoc-template/tmpl/signal.tmpl +++ b/tools/jsdoc/hifi-jsdoc-template/tmpl/signal.tmpl @@ -25,23 +25,23 @@ var self = this;
    -

    - - -

    + + + +

     

    -

    Throws:

    +

    Throws

    1) { ?>
    -

    + diff --git a/tools/jsdoc/hifi-jsdoc-template/tmpl/signalList.tmpl b/tools/jsdoc/hifi-jsdoc-template/tmpl/signalList.tmpl index c5fdefc7d8..98b0892122 100644 --- a/tools/jsdoc/hifi-jsdoc-template/tmpl/signalList.tmpl +++ b/tools/jsdoc/hifi-jsdoc-template/tmpl/signalList.tmpl @@ -13,12 +13,10 @@ var self = this;
    - - - - + + + -