diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index dc20763953..c9fd769822 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -19,6 +19,4 @@ endif (UNIX) include_application_version() -setup_memory_debugger() - copy_dlls_beside_windows_executable() diff --git a/assignment-client/src/AssignmentActionFactory.cpp b/assignment-client/src/AssignmentActionFactory.cpp index 7c404cbd97..f99e712b72 100644 --- a/assignment-client/src/AssignmentActionFactory.cpp +++ b/assignment-client/src/AssignmentActionFactory.cpp @@ -13,10 +13,9 @@ EntityActionPointer assignmentActionFactory(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity) { - return (EntityActionPointer) new AssignmentAction(type, id, ownerEntity); + return EntityActionPointer(new AssignmentAction(type, id, ownerEntity)); } - EntityActionPointer AssignmentActionFactory::factory(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity, diff --git a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp index 1d9adab28b..a023b9a7fd 100644 --- a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp +++ b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp @@ -131,9 +131,6 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer packet qDebug() << " numBytesPacketHeader=" << NLPacket::totalHeaderSize(packetType); qDebug() << " sizeof(sequence)=" << sizeof(sequence); qDebug() << " sizeof(sentAt)=" << sizeof(sentAt); - } - - if (debugProcessPacket) { qDebug() << " atByte (in payload)=" << packet->pos(); qDebug() << " payload size=" << packet->getPayloadSize(); diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 062fe39dbd..a0350622db 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -643,7 +643,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url quint64 averageLoggingTime = _tree->getAverageLoggingTime(); - float averageElementsPerPacket = totalPacketsProcessed == 0 ? 0 : totalElementsProcessed / totalPacketsProcessed; + float averageElementsPerPacket = totalPacketsProcessed == 0 ? 0 : (float)totalElementsProcessed / totalPacketsProcessed; statsString += QString(" Current Inbound Packets Queue: %1 packets\r\n") .arg(locale.toString((uint)currentPacketsInQueue).rightJustified(COLUMN_WIDTH, ' ')); @@ -695,7 +695,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url totalElementsProcessed = senderStats.getTotalElementsProcessed(); totalPacketsProcessed = senderStats.getTotalPacketsProcessed(); - averageElementsPerPacket = totalPacketsProcessed == 0 ? 0 : totalElementsProcessed / totalPacketsProcessed; + averageElementsPerPacket = totalPacketsProcessed == 0 ? 0 : (float)totalElementsProcessed / totalPacketsProcessed; statsString += QString(" Total Inbound Packets: %1 packets\r\n") .arg(locale.toString((uint)totalPacketsProcessed).rightJustified(COLUMN_WIDTH, ' ')); @@ -1075,9 +1075,7 @@ void OctreeServer::run() { // now set up PersistThread _persistThread = new OctreePersistThread(_tree, _persistFilename, _persistInterval, _wantBackup, _settings, _debugTimestampNow, _persistAsFileType); - if (_persistThread) { - _persistThread->initialize(true); - } + _persistThread->initialize(true); } HifiSockAddr senderSockAddr; diff --git a/cmake/macros/LinkHifiLibraries.cmake b/cmake/macros/LinkHifiLibraries.cmake index ef7467e170..d0a12f3bea 100644 --- a/cmake/macros/LinkHifiLibraries.cmake +++ b/cmake/macros/LinkHifiLibraries.cmake @@ -33,4 +33,7 @@ macro(LINK_HIFI_LIBRARIES) list(APPEND ${TARGET_NAME}_DEPENDENCY_INCLUDES ${LINKED_TARGET_DEPENDENCY_INCLUDES}) endif() endforeach() + + setup_memory_debugger() + endmacro(LINK_HIFI_LIBRARIES) \ No newline at end of file diff --git a/cmake/macros/SetupHifiLibrary.cmake b/cmake/macros/SetupHifiLibrary.cmake index e38e224084..aba8d500a5 100644 --- a/cmake/macros/SetupHifiLibrary.cmake +++ b/cmake/macros/SetupHifiLibrary.cmake @@ -15,6 +15,8 @@ macro(SETUP_HIFI_LIBRARY) file(GLOB_RECURSE LIB_SRCS "src/*.h" "src/*.cpp" "src/*.c") list(APPEND ${TARGET_NAME}_SRCS ${LIB_SRCS}) + setup_memory_debugger() + # create a library and set the property so it can be referenced later if (${${TARGET_NAME}_SHARED}) add_library(${TARGET_NAME} SHARED ${LIB_SRCS} ${AUTOMTC_SRC} ${AUTOSCRIBE_SHADER_LIB_SRC} ${QT_RESOURCES_FILE}) diff --git a/domain-server/CMakeLists.txt b/domain-server/CMakeLists.txt index d2f30b6c25..e4fa1d874d 100644 --- a/domain-server/CMakeLists.txt +++ b/domain-server/CMakeLists.txt @@ -1,7 +1,5 @@ set(TARGET_NAME domain-server) -setup_memory_debugger() - if (UPPER_CMAKE_BUILD_TYPE MATCHES DEBUG AND NOT WIN32) set(_SHOULD_SYMLINK_RESOURCES TRUE) else () diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp new file mode 100644 index 0000000000..0b0e0fedd3 --- /dev/null +++ b/domain-server/src/DomainGatekeeper.cpp @@ -0,0 +1,611 @@ +// +// DomainGatekeeper.cpp +// domain-server/src +// +// Created by Stephen Birarda on 2015-08-24. +// Copyright 2015 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 "DomainGatekeeper.h" + +#include +#include +#include + +#include +#include +#include + +#include "DomainServer.h" +#include "DomainServerNodeData.h" + +using SharedAssignmentPointer = QSharedPointer; + +DomainGatekeeper::DomainGatekeeper(DomainServer* server) : + _server(server) +{ + +} + +void DomainGatekeeper::addPendingAssignedNode(const QUuid& nodeUUID, const QUuid& assignmentUUID, + const QUuid& walletUUID, const QString& nodeVersion) { + _pendingAssignedNodes.emplace(std::piecewise_construct, + std::forward_as_tuple(nodeUUID), + std::forward_as_tuple(assignmentUUID, walletUUID, nodeVersion)); +} + +QUuid DomainGatekeeper::assignmentUUIDForPendingAssignment(const QUuid& tempUUID) { + auto it = _pendingAssignedNodes.find(tempUUID); + + if (it != _pendingAssignedNodes.end()) { + return it->second.getAssignmentUUID(); + } else { + return QUuid(); + } +} + +const NodeSet STATICALLY_ASSIGNED_NODES = NodeSet() << NodeType::AudioMixer + << NodeType::AvatarMixer << NodeType::EntityServer; + +void DomainGatekeeper::processConnectRequestPacket(QSharedPointer packet) { + if (packet->getPayloadSize() == 0) { + return; + } + + QDataStream packetStream(packet.data()); + + // read a NodeConnectionData object from the packet so we can pass around this data while we're inspecting it + NodeConnectionData nodeConnection = NodeConnectionData::fromDataStream(packetStream, packet->getSenderSockAddr()); + + if (nodeConnection.localSockAddr.isNull() || nodeConnection.publicSockAddr.isNull()) { + qDebug() << "Unexpected data received for node local socket or public socket. Will not allow connection."; + return; + } + + // check if this connect request matches an assignment in the queue + auto pendingAssignment = _pendingAssignedNodes.find(nodeConnection.connectUUID); + + SharedNodePointer node; + + if (pendingAssignment != _pendingAssignedNodes.end()) { + node = processAssignmentConnectRequest(nodeConnection, pendingAssignment->second); + } else if (!STATICALLY_ASSIGNED_NODES.contains(nodeConnection.nodeType)) { + QString username; + QByteArray usernameSignature; + + if (packet->bytesLeftToRead() > 0) { + // read username from packet + packetStream >> username; + + if (packet->bytesLeftToRead() > 0) { + // read user signature from packet + packetStream >> usernameSignature; + } + } + + node = processAgentConnectRequest(nodeConnection, username, usernameSignature); + } + + if (node) { + // set the sending sock addr and node interest set on this node + DomainServerNodeData* nodeData = reinterpret_cast(node->getLinkedData()); + nodeData->setSendingSockAddr(packet->getSenderSockAddr()); + nodeData->setNodeInterestSet(nodeConnection.interestList.toSet()); + + // signal that we just connected a node so the DomainServer can get it a list + // and broadcast its presence right away + emit connectedNode(node); + } else { + qDebug() << "Refusing connection from node at" << packet->getSenderSockAddr(); + } +} + +SharedNodePointer DomainGatekeeper::processAssignmentConnectRequest(const NodeConnectionData& nodeConnection, + const PendingAssignedNodeData& pendingAssignment) { + + // make sure this matches an assignment the DS told us we sent out + auto it = _pendingAssignedNodes.find(nodeConnection.connectUUID); + + SharedAssignmentPointer matchingQueuedAssignment = SharedAssignmentPointer(); + + if (it != _pendingAssignedNodes.end()) { + // find the matching queued static assignment in DS queue + matchingQueuedAssignment = _server->dequeueMatchingAssignment(it->second.getAssignmentUUID(), nodeConnection.nodeType); + + if (matchingQueuedAssignment) { + qDebug() << "Assignment deployed with" << uuidStringWithoutCurlyBraces(nodeConnection.connectUUID) + << "matches unfulfilled assignment" + << uuidStringWithoutCurlyBraces(matchingQueuedAssignment->getUUID()); + } else { + // this is a node connecting to fulfill an assignment that doesn't exist + // don't reply back to them so they cycle back and re-request an assignment + qDebug() << "No match for assignment deployed with" << uuidStringWithoutCurlyBraces(nodeConnection.connectUUID); + return SharedNodePointer(); + } + } else { + qDebug() << "No assignment was deployed with UUID" << uuidStringWithoutCurlyBraces(nodeConnection.connectUUID); + return SharedNodePointer(); + } + + // add the new node + SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection); + + DomainServerNodeData* nodeData = reinterpret_cast(newNode->getLinkedData()); + + // set assignment related data on the linked data for this node + nodeData->setAssignmentUUID(matchingQueuedAssignment->getUUID()); + nodeData->setWalletUUID(it->second.getWalletUUID()); + nodeData->setNodeVersion(it->second.getNodeVersion()); + + // cleanup the PendingAssignedNodeData for this assignment now that it's connecting + _pendingAssignedNodes.erase(it); + + // always allow assignment clients to create and destroy entities + newNode->setCanAdjustLocks(true); + newNode->setCanRez(true); + + return newNode; +} + +const QString MAXIMUM_USER_CAPACITY = "security.maximum_user_capacity"; +const QString ALLOWED_EDITORS_SETTINGS_KEYPATH = "security.allowed_editors"; +const QString EDITORS_ARE_REZZERS_KEYPATH = "security.editors_are_rezzers"; + +SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnectionData& nodeConnection, + const QString& username, + const QByteArray& usernameSignature) { + + auto limitedNodeList = DependencyManager::get(); + + bool isRestrictingAccess = + _server->_settingsManager.valueOrDefaultValueForKeyPath(RESTRICTED_ACCESS_SETTINGS_KEYPATH).toBool(); + + // check if this user is on our local machine - if this is true they are always allowed to connect + QHostAddress senderHostAddress = nodeConnection.senderSockAddr.getAddress(); + bool isLocalUser = + (senderHostAddress == limitedNodeList->getLocalSockAddr().getAddress() || senderHostAddress == QHostAddress::LocalHost); + + // if we're using restricted access and this user is not local make sure we got a user signature + if (isRestrictingAccess && !isLocalUser) { + if (!username.isEmpty()) { + if (usernameSignature.isEmpty()) { + // if user didn't include usernameSignature in connect request, send a connectionToken packet + sendConnectionTokenPacket(username, nodeConnection.senderSockAddr); + + // ask for their public key right now to make sure we have it + requestUserPublicKey(username); + + return SharedNodePointer(); + } + } + } + + bool verifiedUsername = false; + + // if we do not have a local user we need to subject them to our verification and capacity checks + if (!isLocalUser) { + + // check if we need to look at the username signature + if (isRestrictingAccess) { + if (isVerifiedAllowedUser(username, usernameSignature, nodeConnection.senderSockAddr)) { + // we verified the user via their username and signature - set the verifiedUsername + // so we don't re-decrypt their sig if we're trying to exempt them from max capacity check (due to + // being in the allowed editors list) + verifiedUsername = true; + } else { + // failed to verify user - return a null shared ptr + return SharedNodePointer(); + } + } + + if (!isWithinMaxCapacity(username, usernameSignature, verifiedUsername, nodeConnection.senderSockAddr)) { + // we can't allow this user to connect because we are at max capacity (and they either aren't an allowed editor + // or couldn't be verified as one) + return SharedNodePointer(); + } + } + + // if this user is in the editors list (or if the editors list is empty) set the user's node's canAdjustLocks to true + const QVariant* allowedEditorsVariant = + valueForKeyPath(_server->_settingsManager.getSettingsMap(), ALLOWED_EDITORS_SETTINGS_KEYPATH); + QStringList allowedEditors = allowedEditorsVariant ? allowedEditorsVariant->toStringList() : QStringList(); + + // if the allowed editors list is empty then everyone can adjust locks + bool canAdjustLocks = allowedEditors.empty(); + + if (allowedEditors.contains(username)) { + // we have a non-empty allowed editors list - check if this user is verified to be in it + if (!verifiedUsername) { + if (!verifyUserSignature(username, usernameSignature, HifiSockAddr())) { + // failed to verify a user that is in the allowed editors list + + // TODO: fix public key refresh in interface/metaverse and force this check + qDebug() << "Could not verify user" << username << "as allowed editor. In the interim this user" + << "will be given edit rights to avoid a thrasing of public key requests and connect requests."; + } + + canAdjustLocks = true; + } else { + // already verified this user and they are in the allowed editors list + canAdjustLocks = true; + } + } + + // check if only editors should be able to rez entities + const QVariant* editorsAreRezzersVariant = + valueForKeyPath(_server->_settingsManager.getSettingsMap(), EDITORS_ARE_REZZERS_KEYPATH); + + bool onlyEditorsAreRezzers = false; + if (editorsAreRezzersVariant) { + onlyEditorsAreRezzers = editorsAreRezzersVariant->toBool(); + } + + bool canRez = true; + if (onlyEditorsAreRezzers) { + canRez = canAdjustLocks; + } + + // add the new node + SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection); + + // set the edit rights for this user + newNode->setCanAdjustLocks(canAdjustLocks); + newNode->setCanRez(canRez); + + // grab the linked data for our new node so we can set the username + DomainServerNodeData* nodeData = reinterpret_cast(newNode->getLinkedData()); + + // if we have a username from the connect request, set it on the DomainServerNodeData + nodeData->setUsername(username); + + // also add an interpolation to JSONBreakableMarshal so that servers can get username in stats + JSONBreakableMarshal::addInterpolationForKey(USERNAME_UUID_REPLACEMENT_STATS_KEY, + uuidStringWithoutCurlyBraces(newNode->getUUID()), username); + + return newNode; +} + +SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection) { + HifiSockAddr discoveredSocket = nodeConnection.senderSockAddr; + SharedNetworkPeer connectedPeer = _icePeers.value(nodeConnection.connectUUID); + + QUuid nodeUUID; + + if (connectedPeer) { + // this user negotiated a connection with us via ICE, so re-use their ICE client ID + nodeUUID = nodeConnection.connectUUID; + + if (connectedPeer->getActiveSocket()) { + // set their discovered socket to whatever the activated socket on the network peer object was + discoveredSocket = *connectedPeer->getActiveSocket(); + } + } else { + // we got a connectUUID we didn't recognize, just add the node with a new UUID + nodeUUID = QUuid::createUuid(); + } + + auto limitedNodeList = DependencyManager::get(); + + SharedNodePointer newNode = limitedNodeList->addOrUpdateNode(nodeUUID, nodeConnection.nodeType, + nodeConnection.publicSockAddr, nodeConnection.localSockAddr); + + // So that we can send messages to this node at will - we need to activate the correct socket on this node now + newNode->activateMatchingOrNewSymmetricSocket(discoveredSocket); + + return newNode; +} + +bool DomainGatekeeper::verifyUserSignature(const QString& username, + const QByteArray& usernameSignature, + const HifiSockAddr& senderSockAddr) { + + // it's possible this user can be allowed to connect, but we need to check their username signature + QByteArray publicKeyArray = _userPublicKeys.value(username); + + const QUuid& connectionToken = _connectionTokenHash.value(username.toLower()); + + if (!publicKeyArray.isEmpty() && !connectionToken.isNull()) { + // if we do have a public key for the user, check for a signature match + + const unsigned char* publicKeyData = reinterpret_cast(publicKeyArray.constData()); + + // first load up the public key into an RSA struct + RSA* rsaPublicKey = d2i_RSA_PUBKEY(NULL, &publicKeyData, publicKeyArray.size()); + + QByteArray lowercaseUsername = username.toLower().toUtf8(); + QByteArray usernameWithToken = QCryptographicHash::hash(lowercaseUsername.append(connectionToken.toRfc4122()), + QCryptographicHash::Sha256); + + if (rsaPublicKey) { + QByteArray decryptedArray(RSA_size(rsaPublicKey), 0); + int decryptResult = RSA_verify(NID_sha256, + reinterpret_cast(usernameWithToken.constData()), + usernameWithToken.size(), + reinterpret_cast(usernameSignature.constData()), + usernameSignature.size(), + rsaPublicKey); + + if (decryptResult == 1) { + qDebug() << "Username signature matches for" << username << "- allowing connection."; + + // free up the public key and remove connection token before we return + RSA_free(rsaPublicKey); + _connectionTokenHash.remove(username); + + return true; + + } else { + if (!senderSockAddr.isNull()) { + qDebug() << "Error decrypting username signature for " << username << "- denying connection."; + sendConnectionDeniedPacket("Error decrypting username signature.", senderSockAddr); + } + + // free up the public key, we don't need it anymore + RSA_free(rsaPublicKey); + } + + } else { + + // we can't let this user in since we couldn't convert their public key to an RSA key we could use + if (!senderSockAddr.isNull()) { + qDebug() << "Couldn't convert data to RSA key for" << username << "- denying connection."; + sendConnectionDeniedPacket("Couldn't convert data to RSA key.", senderSockAddr); + } + } + } else { + if (!senderSockAddr.isNull()) { + qDebug() << "Insufficient data to decrypt username signature - denying connection."; + sendConnectionDeniedPacket("Insufficient data", senderSockAddr); + } + } + + requestUserPublicKey(username); // no joy. maybe next time? + return false; +} + +bool DomainGatekeeper::isVerifiedAllowedUser(const QString& username, const QByteArray& usernameSignature, + const HifiSockAddr& senderSockAddr) { + + if (username.isEmpty()) { + qDebug() << "Connect request denied - no username provided."; + + sendConnectionDeniedPacket("No username provided", senderSockAddr); + + return false; + } + + QStringList allowedUsers = + _server->_settingsManager.valueOrDefaultValueForKeyPath(ALLOWED_USERS_SETTINGS_KEYPATH).toStringList(); + + if (allowedUsers.contains(username, Qt::CaseInsensitive)) { + if (!verifyUserSignature(username, usernameSignature, senderSockAddr)) { + return false; + } + } else { + qDebug() << "Connect request denied for user" << username << "- not in allowed users list."; + sendConnectionDeniedPacket("User not on whitelist.", senderSockAddr); + + return false; + } + + return true; +} + +bool DomainGatekeeper::isWithinMaxCapacity(const QString& username, const QByteArray& usernameSignature, + bool& verifiedUsername, + const HifiSockAddr& senderSockAddr) { + // find out what our maximum capacity is + const QVariant* maximumUserCapacityVariant = valueForKeyPath(_server->_settingsManager.getSettingsMap(), MAXIMUM_USER_CAPACITY); + unsigned int maximumUserCapacity = maximumUserCapacityVariant ? maximumUserCapacityVariant->toUInt() : 0; + + if (maximumUserCapacity > 0) { + unsigned int connectedUsers = _server->countConnectedUsers(); + + if (connectedUsers >= maximumUserCapacity) { + // too many users, deny the new connection unless this user is an allowed editor + + const QVariant* allowedEditorsVariant = + valueForKeyPath(_server->_settingsManager.getSettingsMap(), ALLOWED_EDITORS_SETTINGS_KEYPATH); + + QStringList allowedEditors = allowedEditorsVariant ? allowedEditorsVariant->toStringList() : QStringList(); + if (allowedEditors.contains(username)) { + if (verifiedUsername || verifyUserSignature(username, usernameSignature, senderSockAddr)) { + verifiedUsername = true; + qDebug() << "Above maximum capacity -" << connectedUsers << "/" << maximumUserCapacity << + "but user" << username << "is in allowed editors list so will be allowed to connect."; + return true; + } + } + + // deny connection from this user + qDebug() << connectedUsers << "/" << maximumUserCapacity << "users connected, denying new connection."; + sendConnectionDeniedPacket("Too many connected users.", senderSockAddr); + + return false; + } + + qDebug() << connectedUsers << "/" << maximumUserCapacity << "users connected, allowing new connection."; + } + + return true; +} + + +void DomainGatekeeper::preloadAllowedUserPublicKeys() { + const QVariant* allowedUsersVariant = valueForKeyPath(_server->_settingsManager.getSettingsMap(), ALLOWED_USERS_SETTINGS_KEYPATH); + QStringList allowedUsers = allowedUsersVariant ? allowedUsersVariant->toStringList() : QStringList(); + + if (allowedUsers.size() > 0) { + // in the future we may need to limit how many requests here - for now assume that lists of allowed users are not + // going to create > 100 requests + foreach(const QString& username, allowedUsers) { + requestUserPublicKey(username); + } + } +} + +void DomainGatekeeper::requestUserPublicKey(const QString& username) { + // even if we have a public key for them right now, request a new one in case it has just changed + JSONCallbackParameters callbackParams; + callbackParams.jsonCallbackReceiver = this; + callbackParams.jsonCallbackMethod = "publicKeyJSONCallback"; + + const QString USER_PUBLIC_KEY_PATH = "api/v1/users/%1/public_key"; + + qDebug() << "Requesting public key for user" << username; + + AccountManager::getInstance().sendRequest(USER_PUBLIC_KEY_PATH.arg(username), + AccountManagerAuth::None, + QNetworkAccessManager::GetOperation, callbackParams); +} + +void DomainGatekeeper::publicKeyJSONCallback(QNetworkReply& requestReply) { + QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object(); + + if (jsonObject["status"].toString() == "success") { + // figure out which user this is for + + const QString PUBLIC_KEY_URL_REGEX_STRING = "api\\/v1\\/users\\/([A-Za-z0-9_\\.]+)\\/public_key"; + QRegExp usernameRegex(PUBLIC_KEY_URL_REGEX_STRING); + + if (usernameRegex.indexIn(requestReply.url().toString()) != -1) { + QString username = usernameRegex.cap(1); + + qDebug() << "Storing a public key for user" << username; + + // pull the public key as a QByteArray from this response + const QString JSON_DATA_KEY = "data"; + const QString JSON_PUBLIC_KEY_KEY = "public_key"; + + _userPublicKeys[username] = + QByteArray::fromBase64(jsonObject[JSON_DATA_KEY].toObject()[JSON_PUBLIC_KEY_KEY].toString().toUtf8()); + } + } +} + +void DomainGatekeeper::sendConnectionDeniedPacket(const QString& reason, const HifiSockAddr& senderSockAddr) { + // this is an agent and we've decided we won't let them connect - send them a packet to deny connection + QByteArray utfString = reason.toUtf8(); + quint16 payloadSize = utfString.size(); + + // setup the DomainConnectionDenied packet + auto connectionDeniedPacket = NLPacket::create(PacketType::DomainConnectionDenied, payloadSize + sizeof(payloadSize)); + + // pack in the reason the connection was denied (the client displays this) + if (payloadSize > 0) { + connectionDeniedPacket->writePrimitive(payloadSize); + connectionDeniedPacket->write(utfString); + } + + // send the packet off + DependencyManager::get()->sendPacket(std::move(connectionDeniedPacket), senderSockAddr); +} + +void DomainGatekeeper::sendConnectionTokenPacket(const QString& username, const HifiSockAddr& senderSockAddr) { + // get the existing connection token or create a new one + QUuid& connectionToken = _connectionTokenHash[username.toLower()]; + + if (connectionToken.isNull()) { + connectionToken = QUuid::createUuid(); + } + + // setup a static connection token packet + static auto connectionTokenPacket = NLPacket::create(PacketType::DomainServerConnectionToken, NUM_BYTES_RFC4122_UUID); + + // reset the packet before each time we send + connectionTokenPacket->reset(); + + // write the connection token + connectionTokenPacket->write(connectionToken.toRfc4122()); + + // send off the packet unreliably + DependencyManager::get()->sendUnreliablePacket(*connectionTokenPacket, senderSockAddr); +} + +const int NUM_PEER_PINGS_BEFORE_DELETE = 2000 / UDP_PUNCH_PING_INTERVAL_MS; + +void DomainGatekeeper::pingPunchForConnectingPeer(const SharedNetworkPeer& peer) { + + if (peer->getConnectionAttempts() >= NUM_PEER_PINGS_BEFORE_DELETE) { + // we've reached the maximum number of ping attempts + qDebug() << "Maximum number of ping attempts reached for peer with ID" << peer->getUUID(); + qDebug() << "Removing from list of connecting peers."; + + _icePeers.remove(peer->getUUID()); + } else { + auto limitedNodeList = DependencyManager::get(); + + // send the ping packet to the local and public sockets for this node + auto localPingPacket = limitedNodeList->constructICEPingPacket(PingType::Local, limitedNodeList->getSessionUUID()); + limitedNodeList->sendPacket(std::move(localPingPacket), peer->getLocalSocket()); + + auto publicPingPacket = limitedNodeList->constructICEPingPacket(PingType::Public, limitedNodeList->getSessionUUID()); + limitedNodeList->sendPacket(std::move(publicPingPacket), peer->getPublicSocket()); + + peer->incrementConnectionAttempts(); + } +} + +void DomainGatekeeper::handlePeerPingTimeout() { + NetworkPeer* senderPeer = qobject_cast(sender()); + + if (senderPeer) { + SharedNetworkPeer sharedPeer = _icePeers.value(senderPeer->getUUID()); + + if (sharedPeer && !sharedPeer->getActiveSocket()) { + pingPunchForConnectingPeer(sharedPeer); + } + } +} + +void DomainGatekeeper::processICEPeerInformationPacket(QSharedPointer packet) { + // loop through the packet and pull out network peers + // any peer we don't have we add to the hash, otherwise we update + QDataStream iceResponseStream(packet.data()); + + NetworkPeer* receivedPeer = new NetworkPeer; + iceResponseStream >> *receivedPeer; + + if (!_icePeers.contains(receivedPeer->getUUID())) { + qDebug() << "New peer requesting ICE connection being added to hash -" << *receivedPeer; + SharedNetworkPeer newPeer = SharedNetworkPeer(receivedPeer); + _icePeers[receivedPeer->getUUID()] = newPeer; + + // make sure we know when we should ping this peer + connect(newPeer.data(), &NetworkPeer::pingTimerTimeout, this, &DomainGatekeeper::handlePeerPingTimeout); + + // immediately ping the new peer, and start a timer to continue pinging it until we connect to it + newPeer->startPingTimer(); + + qDebug() << "Sending ping packets to establish connectivity with ICE peer with ID" + << newPeer->getUUID(); + + pingPunchForConnectingPeer(newPeer); + } else { + delete receivedPeer; + } +} + +void DomainGatekeeper::processICEPingPacket(QSharedPointer packet) { + auto limitedNodeList = DependencyManager::get(); + auto pingReplyPacket = limitedNodeList->constructICEPingReplyPacket(*packet, limitedNodeList->getSessionUUID()); + + limitedNodeList->sendPacket(std::move(pingReplyPacket), packet->getSenderSockAddr()); +} + +void DomainGatekeeper::processICEPingReplyPacket(QSharedPointer packet) { + QDataStream packetStream(packet.data()); + + QUuid nodeUUID; + packetStream >> nodeUUID; + + SharedNetworkPeer sendingPeer = _icePeers.value(nodeUUID); + + if (sendingPeer) { + // we had this NetworkPeer in our connecting list - add the right sock addr to our connected list + sendingPeer->activateMatchingOrNewSymmetricSocket(packet->getSenderSockAddr()); + } +} diff --git a/domain-server/src/DomainGatekeeper.h b/domain-server/src/DomainGatekeeper.h new file mode 100644 index 0000000000..857b4419da --- /dev/null +++ b/domain-server/src/DomainGatekeeper.h @@ -0,0 +1,93 @@ +// +// DomainGatekeeper.h +// domain-server/src +// +// Created by Stephen Birarda on 2015-08-24. +// Copyright 2015 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 +// + +#pragma once + +#ifndef hifi_DomainGatekeeper_h +#define hifi_DomainGatekeeper_h + +#include + +#include +#include + +#include +#include +#include + +#include "NodeConnectionData.h" +#include "PendingAssignedNodeData.h" + +class DomainServer; + +class DomainGatekeeper : public QObject { + Q_OBJECT +public: + DomainGatekeeper(DomainServer* server); + + void addPendingAssignedNode(const QUuid& nodeUUID, const QUuid& assignmentUUID, + const QUuid& walletUUID, const QString& nodeVersion); + QUuid assignmentUUIDForPendingAssignment(const QUuid& tempUUID); + + void preloadAllowedUserPublicKeys(); + + void removeICEPeer(const QUuid& peerUUID) { _icePeers.remove(peerUUID); } +public slots: + void processConnectRequestPacket(QSharedPointer packet); + void processICEPingPacket(QSharedPointer packet); + void processICEPingReplyPacket(QSharedPointer packet); + void processICEPeerInformationPacket(QSharedPointer packet); + + void publicKeyJSONCallback(QNetworkReply& requestReply); + +signals: + void connectedNode(SharedNodePointer node); + +private slots: + void handlePeerPingTimeout(); +private: + SharedNodePointer processAssignmentConnectRequest(const NodeConnectionData& nodeConnection, + const PendingAssignedNodeData& pendingAssignment); + SharedNodePointer processAgentConnectRequest(const NodeConnectionData& nodeConnection, + const QString& username, + const QByteArray& usernameSignature); + SharedNodePointer addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection); + + bool verifyUserSignature(const QString& username, const QByteArray& usernameSignature, + const HifiSockAddr& senderSockAddr); + bool isVerifiedAllowedUser(const QString& username, const QByteArray& usernameSignature, + const HifiSockAddr& senderSockAddr); + bool isWithinMaxCapacity(const QString& username, const QByteArray& usernameSignature, + bool& verifiedUsername, + const HifiSockAddr& senderSockAddr); + + bool shouldAllowConnectionFromNode(const QString& username, const QByteArray& usernameSignature, + const HifiSockAddr& senderSockAddr); + + void sendConnectionTokenPacket(const QString& username, const HifiSockAddr& senderSockAddr); + void sendConnectionDeniedPacket(const QString& reason, const HifiSockAddr& senderSockAddr); + + void pingPunchForConnectingPeer(const SharedNetworkPeer& peer); + + void requestUserPublicKey(const QString& username); + + DomainServer* _server; + + std::unordered_map _pendingAssignedNodes; + + QHash _icePeers; + + QHash _connectionTokenHash; + QHash _userPublicKeys; +}; + + +#endif // hifi_DomainGatekeeper_h diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 52b20d721f..4999674c4f 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -9,9 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include -#include -#include +#include "DomainServer.h" #include #include @@ -38,24 +36,19 @@ #include #include "DomainServerNodeData.h" - -#include "DomainServer.h" +#include "NodeConnectionData.h" int const DomainServer::EXIT_CODE_REBOOT = 234923; const QString ICE_SERVER_DEFAULT_HOSTNAME = "ice.highfidelity.io"; -const QString MAXIMUM_USER_CAPACITY = "security.maximum_user_capacity"; -const QString ALLOWED_EDITORS_SETTINGS_KEYPATH = "security.allowed_editors"; -const QString EDITORS_ARE_REZZERS_KEYPATH = "security.editors_are_rezzers"; - DomainServer::DomainServer(int argc, char* argv[]) : QCoreApplication(argc, argv), + _gatekeeper(this), _httpManager(DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this), _httpsManager(NULL), _allAssignments(), _unfulfilledAssignments(), - _pendingAssignedNodes(), _isUsingDTLS(false), _oauthProviderURL(), _oauthClientID(), @@ -94,6 +87,9 @@ DomainServer::DomainServer(int argc, char* argv[]) : qRegisterMetaType("DomainServerWebSessionData"); qRegisterMetaTypeStreamOperators("DomainServerWebSessionData"); + + // make sure we hear about newly connected nodes from our gatekeeper + connect(&_gatekeeper, &DomainGatekeeper::connectedNode, this, &DomainServer::handleConnectedNode); if (optionallyReadX509KeyAndCertificate() && optionallySetupOAuth() && optionallySetupAssignmentPayment()) { // we either read a certificate and private key or were not passed one @@ -108,7 +104,7 @@ DomainServer::DomainServer(int argc, char* argv[]) : setupAutomaticNetworking(); // preload some user public keys so they can connect on first request - preloadAllowedUserPublicKeys(); + _gatekeeper.preloadAllowedUserPublicKeys(); } } @@ -289,16 +285,22 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) { // register as the packet receiver for the types we want PacketReceiver& packetReceiver = nodeList->getPacketReceiver(); packetReceiver.registerListener(PacketType::RequestAssignment, this, "processRequestAssignmentPacket"); - packetReceiver.registerListener(PacketType::DomainConnectRequest, this, "processConnectRequestPacket"); + packetReceiver.registerListener(PacketType::DomainConnectRequest, &_gatekeeper, "processConnectRequestPacket"); packetReceiver.registerListener(PacketType::DomainListRequest, this, "processListRequestPacket"); packetReceiver.registerListener(PacketType::DomainServerPathQuery, this, "processPathQueryPacket"); packetReceiver.registerListener(PacketType::NodeJsonStats, this, "processNodeJSONStatsPacket"); +<<<<<<< HEAD packetReceiver.registerListener(PacketType::ICEPing, this, "processICEPingPacket"); packetReceiver.registerListener(PacketType::ICEPingReply, this, "processICEPingReplyPacket"); packetReceiver.registerListener(PacketType::ICEServerPeerInformation, this, "processICEPeerInformationPacket"); // NodeList won't be available to the settings manager when it is created, so call registerListener here packetReceiver.registerListener(PacketType::DomainSettingsRequest, &_settingsManager, "processSettingsRequestPacket"); +======= + packetReceiver.registerListener(PacketType::ICEPing, &_gatekeeper, "processICEPingPacket"); + packetReceiver.registerListener(PacketType::ICEPingReply, &_gatekeeper, "processICEPingReplyPacket"); + packetReceiver.registerListener(PacketType::ICEServerPeerInformation, &_gatekeeper, "processICEPeerInformationPacket"); +>>>>>>> d0db56a4a6902e9d1ee706ec02152e94688f4e5c // add whatever static assignments that have been parsed to the queue addStaticAssignmentsToQueue(); @@ -582,6 +584,7 @@ void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet packet) } +======= +>>>>>>> d0db56a4a6902e9d1ee706ec02152e94688f4e5c void DomainServer::processListRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode) { - NodeType_t throwawayNodeType; - HifiSockAddr nodePublicAddress, nodeLocalAddress; - QDataStream packetStream(packet.data()); + NodeConnectionData nodeRequestData = NodeConnectionData::fromDataStream(packetStream, packet->getSenderSockAddr(), false); - parseNodeData(packetStream, throwawayNodeType, nodePublicAddress, nodeLocalAddress, packet->getSenderSockAddr()); + // update this node's sockets in case they have changed + sendingNode->setPublicSocket(nodeRequestData.publicSockAddr); + sendingNode->setLocalSocket(nodeRequestData.localSockAddr); + + // update the NodeInterestSet in case there have been any changes + DomainServerNodeData* nodeData = reinterpret_cast(sendingNode->getLinkedData()); + nodeData->setNodeInterestSet(nodeRequestData.interestList.toSet()); - sendingNode->setPublicSocket(nodePublicAddress); - sendingNode->setLocalSocket(nodeLocalAddress); - - QList nodeInterestList; - packetStream >> nodeInterestList; - - sendDomainListToNode(sendingNode, packet->getSenderSockAddr(), nodeInterestList.toSet()); + sendDomainListToNode(sendingNode, packet->getSenderSockAddr()); } unsigned int DomainServer::countConnectedUsers() { @@ -806,156 +809,6 @@ unsigned int DomainServer::countConnectedUsers() { return result; } - -bool DomainServer::verifyUserSignature(const QString& username, - const QByteArray& usernameSignature, - QString& reasonReturn) { - - // it's possible this user can be allowed to connect, but we need to check their username signature - QByteArray publicKeyArray = _userPublicKeys.value(username); - - const QUuid& connectionToken = _connectionTokenHash.value(username.toLower()); - - if (!publicKeyArray.isEmpty() && !connectionToken.isNull()) { - // if we do have a public key for the user, check for a signature match - - const unsigned char* publicKeyData = reinterpret_cast(publicKeyArray.constData()); - - // first load up the public key into an RSA struct - RSA* rsaPublicKey = d2i_RSA_PUBKEY(NULL, &publicKeyData, publicKeyArray.size()); - - QByteArray lowercaseUsername = username.toLower().toUtf8(); - QByteArray usernameWithToken = QCryptographicHash::hash(lowercaseUsername.append(connectionToken.toRfc4122()), - QCryptographicHash::Sha256); - - if (rsaPublicKey) { - QByteArray decryptedArray(RSA_size(rsaPublicKey), 0); - int decryptResult = RSA_verify(NID_sha256, - reinterpret_cast(usernameWithToken.constData()), - usernameWithToken.size(), - reinterpret_cast(usernameSignature.constData()), - usernameSignature.size(), - rsaPublicKey); - - if (decryptResult == 1) { - qDebug() << "Username signature matches for" << username << "- allowing connection."; - - // free up the public key and remove connection token before we return - RSA_free(rsaPublicKey); - _connectionTokenHash.remove(username); - - return true; - - } else { - qDebug() << "Error decrypting username signature for " << username << "- denying connection."; - reasonReturn = "Error decrypting username signature."; - // free up the public key, we don't need it anymore - RSA_free(rsaPublicKey); - } - - } else { - - // we can't let this user in since we couldn't convert their public key to an RSA key we could use - qDebug() << "Couldn't convert data to RSA key for" << username << "- denying connection."; - reasonReturn = "Couldn't convert data to RSA key."; - } - } else { - qDebug() << "Insufficient data to decrypt username signature - denying connection."; - reasonReturn = "Insufficient data"; - } - - requestUserPublicKey(username); // no joy. maybe next time? - return false; -} - - -bool DomainServer::shouldAllowConnectionFromNode(const QString& username, - const QByteArray& usernameSignature, - const HifiSockAddr& senderSockAddr, - QString& reasonReturn) { - - //TODO: improve flow so these bools aren't declared twice - bool isRestrictingAccess = - _settingsManager.valueOrDefaultValueForKeyPath(RESTRICTED_ACCESS_SETTINGS_KEYPATH).toBool(); - bool isLocalUser = (senderSockAddr.getAddress() == DependencyManager::get()->getLocalSockAddr().getAddress() || senderSockAddr.getAddress() == QHostAddress::LocalHost); - - if (isRestrictingAccess && !isLocalUser) { - QStringList allowedUsers = - _settingsManager.valueOrDefaultValueForKeyPath(ALLOWED_USERS_SETTINGS_KEYPATH).toStringList(); - - if (allowedUsers.contains(username, Qt::CaseInsensitive)) { - if (username.isEmpty()) { - qDebug() << "Connect request denied - no username provided."; - reasonReturn = "No username provided"; - return false; - } - if (!verifyUserSignature(username, usernameSignature, reasonReturn)) { - return false; - } - } else { - qDebug() << "Connect request denied for user" << username << "not in allowed users list."; - reasonReturn = "User not on whitelist."; - - return false; - } - } - - // either we aren't restricting users, or this user is in the allowed list - // if this user is in the editors list, exempt them from the max-capacity check - const QVariant* allowedEditorsVariant = - valueForKeyPath(_settingsManager.getSettingsMap(), ALLOWED_EDITORS_SETTINGS_KEYPATH); - QStringList allowedEditors = allowedEditorsVariant ? allowedEditorsVariant->toStringList() : QStringList(); - if (allowedEditors.contains(username)) { - if (verifyUserSignature(username, usernameSignature, reasonReturn)) { - return true; - } - } - - // if we haven't reached max-capacity, let them in. - const QVariant* maximumUserCapacityVariant = valueForKeyPath(_settingsManager.getSettingsMap(), MAXIMUM_USER_CAPACITY); - unsigned int maximumUserCapacity = maximumUserCapacityVariant ? maximumUserCapacityVariant->toUInt() : 0; - if (maximumUserCapacity > 0) { - unsigned int connectedUsers = countConnectedUsers(); - if (connectedUsers >= maximumUserCapacity) { - // too many users, deny the new connection. - qDebug() << connectedUsers << "/" << maximumUserCapacity << "users connected, denying new connection."; - reasonReturn = "Too many connected users."; - return false; - } - qDebug() << connectedUsers << "/" << maximumUserCapacity << "users connected, perhaps allowing new connection."; - } - - return true; -} - -void DomainServer::preloadAllowedUserPublicKeys() { - const QVariant* allowedUsersVariant = valueForKeyPath(_settingsManager.getSettingsMap(), ALLOWED_USERS_SETTINGS_KEYPATH); - QStringList allowedUsers = allowedUsersVariant ? allowedUsersVariant->toStringList() : QStringList(); - - if (allowedUsers.size() > 0) { - // in the future we may need to limit how many requests here - for now assume that lists of allowed users are not - // going to create > 100 requests - foreach(const QString& username, allowedUsers) { - requestUserPublicKey(username); - } - } -} - -void DomainServer::requestUserPublicKey(const QString& username) { - // even if we have a public key for them right now, request a new one in case it has just changed - JSONCallbackParameters callbackParams; - callbackParams.jsonCallbackReceiver = this; - callbackParams.jsonCallbackMethod = "publicKeyJSONCallback"; - - const QString USER_PUBLIC_KEY_PATH = "api/v1/users/%1/public_key"; - - qDebug() << "Requesting public key for user" << username; - - AccountManager::getInstance().sendRequest(USER_PUBLIC_KEY_PATH.arg(username), - AccountManagerAuth::None, - QNetworkAccessManager::GetOperation, callbackParams); -} - QUrl DomainServer::oauthRedirectURL() { return QString("https://%1:%2/oauth").arg(_hostname).arg(_httpsManager->serverPort()); } @@ -990,30 +843,18 @@ QUrl DomainServer::oauthAuthorizationURL(const QUuid& stateUUID) { return authorizationURL; } -int DomainServer::parseNodeData(QDataStream& packetStream, NodeType_t& nodeType, - HifiSockAddr& publicSockAddr, HifiSockAddr& localSockAddr, - const HifiSockAddr& senderSockAddr) { - packetStream >> nodeType; - packetStream >> publicSockAddr >> localSockAddr; +void DomainServer::handleConnectedNode(SharedNodePointer newNode) { - if (publicSockAddr.getAddress().isNull()) { - // this node wants to use us its STUN server - // so set the node public address to whatever we perceive the public address to be - - // if the sender is on our box then leave its public address to 0 so that - // other users attempt to reach it on the same address they have for the domain-server - if (senderSockAddr.getAddress().isLoopback()) { - publicSockAddr.setAddress(QHostAddress()); - } else { - publicSockAddr.setAddress(senderSockAddr.getAddress()); - } - } - - return packetStream.device()->pos(); + DomainServerNodeData* nodeData = reinterpret_cast(newNode->getLinkedData()); + + // reply back to the user with a PacketType::DomainList + sendDomainListToNode(newNode, nodeData->getSendingSockAddr()); + + // send out this node to our other connected nodes + broadcastNewNode(newNode); } -void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr &senderSockAddr, - const NodeSet& nodeInterestSet) { +void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr &senderSockAddr) { const int NUM_DOMAIN_LIST_EXTENDED_HEADER_BYTES = NUM_BYTES_RFC4122_UUID + NUM_BYTES_RFC4122_UUID + 2; // setup the extended header for the domain list packets @@ -1036,7 +877,7 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif DomainServerNodeData* nodeData = reinterpret_cast(node->getLinkedData()); // store the nodeInterestSet on this DomainServerNodeData, in case it has changed - nodeData->setNodeInterestSet(nodeInterestSet); + auto& nodeInterestSet = nodeData->getNodeInterestSet(); if (nodeInterestSet.size() > 0) { @@ -1045,6 +886,7 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif // if this authenticated node has any interest types, send back those nodes as well limitedNodeList->eachNode([&](const SharedNodePointer& otherNode){ if (otherNode->getUUID() != node->getUUID() && nodeInterestSet.contains(otherNode->getType())) { + // since we're about to add a node to the packet we start a segment domainListPackets.startSegment(); @@ -1176,11 +1018,10 @@ void DomainServer::processRequestAssignmentPacket(QSharedPointer packe auto limitedNodeList = DependencyManager::get(); limitedNodeList->sendUnreliablePacket(*assignmentPacket, packet->getSenderSockAddr()); - // add the information for that deployed assignment to the hash of pending assigned nodes - PendingAssignedNodeData* pendingNodeData = new PendingAssignedNodeData(assignmentToDeploy->getUUID(), - requestAssignment.getWalletUUID(), - requestAssignment.getNodeVersion()); - _pendingAssignedNodes.insert(uniqueAssignment.getUUID(), pendingNodeData); + // give the information for that deployed assignment to the gatekeeper so it knows to that that node + // in when it comes back around + _gatekeeper.addPendingAssignedNode(uniqueAssignment.getUUID(), assignmentToDeploy->getUUID(), + requestAssignment.getWalletUUID(), requestAssignment.getNodeVersion()); } else { if (requestAssignment.getType() != Assignment::AgentType || noisyMessageTimer.elapsed() > NOISY_MESSAGE_INTERVAL_MSECS) { @@ -1260,30 +1101,6 @@ void DomainServer::sendPendingTransactionsToServer() { } } -void DomainServer::publicKeyJSONCallback(QNetworkReply& requestReply) { - QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object(); - - if (jsonObject["status"].toString() == "success") { - // figure out which user this is for - - const QString PUBLIC_KEY_URL_REGEX_STRING = "api\\/v1\\/users\\/([A-Za-z0-9_\\.]+)\\/public_key"; - QRegExp usernameRegex(PUBLIC_KEY_URL_REGEX_STRING); - - if (usernameRegex.indexIn(requestReply.url().toString()) != -1) { - QString username = usernameRegex.cap(1); - - qDebug() << "Storing a public key for user" << username; - - // pull the public key as a QByteArray from this response - const QString JSON_DATA_KEY = "data"; - const QString JSON_PUBLIC_KEY_KEY = "public_key"; - - _userPublicKeys[username] = - QByteArray::fromBase64(jsonObject[JSON_DATA_KEY].toObject()[JSON_PUBLIC_KEY_KEY].toString().toUtf8()); - } - } -} - void DomainServer::transactionJSONCallback(const QJsonObject& data) { // check if this was successful - if so we can remove it from our list of pending if (data.value("status").toString() == "success") { @@ -1380,91 +1197,6 @@ void DomainServer::sendHeartbeatToIceServer() { DependencyManager::get()->sendHeartbeatToIceServer(_iceServerSocket); } -const int NUM_PEER_PINGS_BEFORE_DELETE = 2000 / UDP_PUNCH_PING_INTERVAL_MS; - -void DomainServer::pingPunchForConnectingPeer(const SharedNetworkPeer& peer) { - - if (peer->getConnectionAttempts() > 0 && peer->getConnectionAttempts() % NUM_PEER_PINGS_BEFORE_DELETE == 0) { - // we've reached the maximum number of ping attempts - qDebug() << "Maximum number of ping attempts reached for peer with ID" << peer->getUUID(); - qDebug() << "Removing from list of connecting peers."; - - _icePeers.remove(peer->getUUID()); - } else { - auto limitedNodeList = DependencyManager::get(); - - // send the ping packet to the local and public sockets for this node - auto localPingPacket = limitedNodeList->constructICEPingPacket(PingType::Local, limitedNodeList->getSessionUUID()); - limitedNodeList->sendPacket(std::move(localPingPacket), peer->getLocalSocket()); - - auto publicPingPacket = limitedNodeList->constructICEPingPacket(PingType::Public, limitedNodeList->getSessionUUID()); - limitedNodeList->sendPacket(std::move(publicPingPacket), peer->getPublicSocket()); - - peer->incrementConnectionAttempts(); - } -} - -void DomainServer::handlePeerPingTimeout() { - NetworkPeer* senderPeer = qobject_cast(sender()); - - if (senderPeer) { - SharedNetworkPeer sharedPeer = _icePeers.value(senderPeer->getUUID()); - - if (sharedPeer && !sharedPeer->getActiveSocket()) { - pingPunchForConnectingPeer(sharedPeer); - } - } -} - -void DomainServer::processICEPeerInformationPacket(QSharedPointer packet) { - // loop through the packet and pull out network peers - // any peer we don't have we add to the hash, otherwise we update - QDataStream iceResponseStream(packet.data()); - - NetworkPeer* receivedPeer = new NetworkPeer; - iceResponseStream >> *receivedPeer; - - if (!_icePeers.contains(receivedPeer->getUUID())) { - qDebug() << "New peer requesting ICE connection being added to hash -" << *receivedPeer; - SharedNetworkPeer newPeer = SharedNetworkPeer(receivedPeer); - _icePeers[receivedPeer->getUUID()] = newPeer; - - // make sure we know when we should ping this peer - connect(newPeer.data(), &NetworkPeer::pingTimerTimeout, this, &DomainServer::handlePeerPingTimeout); - - // immediately ping the new peer, and start a timer to continue pinging it until we connect to it - newPeer->startPingTimer(); - - qDebug() << "Sending ping packets to establish connectivity with ICE peer with ID" - << newPeer->getUUID(); - - pingPunchForConnectingPeer(newPeer); - } else { - delete receivedPeer; - } -} - -void DomainServer::processICEPingPacket(QSharedPointer packet) { - auto limitedNodeList = DependencyManager::get(); - auto pingReplyPacket = limitedNodeList->constructICEPingReplyPacket(*packet, limitedNodeList->getSessionUUID()); - - limitedNodeList->sendPacket(std::move(pingReplyPacket), packet->getSenderSockAddr()); -} - -void DomainServer::processICEPingReplyPacket(QSharedPointer packet) { - QDataStream packetStream(packet.data()); - - QUuid nodeUUID; - packetStream >> nodeUUID; - - SharedNetworkPeer sendingPeer = _icePeers.value(nodeUUID); - - if (sendingPeer) { - // we had this NetworkPeer in our connecting list - add the right sock addr to our connected list - sendingPeer->activateMatchingOrNewSymmetricSocket(packet->getSenderSockAddr()); - } -} - void DomainServer::processNodeJSONStatsPacket(QSharedPointer packet, SharedNodePointer sendingNode) { auto nodeData = dynamic_cast(sendingNode->getLinkedData()); if (nodeData) { @@ -1576,9 +1308,9 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url SharedAssignmentPointer matchingAssignment = _allAssignments.value(matchingUUID); if (!matchingAssignment) { // check if we have a pending assignment that matches this temp UUID, and it is a scripted assignment - PendingAssignedNodeData* pendingData = _pendingAssignedNodes.value(matchingUUID); - if (pendingData) { - matchingAssignment = _allAssignments.value(pendingData->getAssignmentUUID()); + QUuid assignmentUUID = _gatekeeper.assignmentUUIDForPendingAssignment(matchingUUID); + if (!assignmentUUID.isNull()) { + matchingAssignment = _allAssignments.value(assignmentUUID); if (matchingAssignment && matchingAssignment->getType() == Assignment::AgentType) { // we have a matching assignment and it is for the right type, have the HTTP manager handle it @@ -1586,7 +1318,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url QUrl scriptURL = url; scriptURL.setPath(URI_ASSIGNMENT + "/scripts/" - + uuidStringWithoutCurlyBraces(pendingData->getAssignmentUUID())); + + uuidStringWithoutCurlyBraces(assignmentUUID)); // have the HTTPManager serve the appropriate script file return _httpManager.handleHTTPRequest(connection, scriptURL, true); @@ -2120,7 +1852,7 @@ void DomainServer::nodeAdded(SharedNodePointer node) { void DomainServer::nodeKilled(SharedNodePointer node) { // if this peer connected via ICE then remove them from our ICE peers hash - _icePeers.remove(node->getUUID()); + _gatekeeper.removeICEPeer(node->getUUID()); DomainServerNodeData* nodeData = reinterpret_cast(node->getLinkedData()); @@ -2148,7 +1880,7 @@ void DomainServer::nodeKilled(SharedNodePointer node) { } } -SharedAssignmentPointer DomainServer::matchingQueuedAssignmentForCheckIn(const QUuid& assignmentUUID, NodeType_t nodeType) { +SharedAssignmentPointer DomainServer::dequeueMatchingAssignment(const QUuid& assignmentUUID, NodeType_t nodeType) { QQueue::iterator i = _unfulfilledAssignments.begin(); while (i != _unfulfilledAssignments.end()) { @@ -2199,20 +1931,6 @@ SharedAssignmentPointer DomainServer::deployableAssignmentForRequest(const Assig return SharedAssignmentPointer(); } -void DomainServer::removeMatchingAssignmentFromQueue(const SharedAssignmentPointer& removableAssignment) { - QQueue::iterator potentialMatchingAssignment = _unfulfilledAssignments.begin(); - while (potentialMatchingAssignment != _unfulfilledAssignments.end()) { - if (potentialMatchingAssignment->data()->getUUID() == removableAssignment->getUUID()) { - _unfulfilledAssignments.erase(potentialMatchingAssignment); - - // we matched and removed an assignment, bail out - break; - } else { - ++potentialMatchingAssignment; - } - } -} - void DomainServer::addStaticAssignmentsToQueue() { // if the domain-server has just restarted, diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index c39633f62a..a66192b44e 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -25,6 +25,7 @@ #include #include +#include "DomainGatekeeper.h" #include "DomainServerSettingsManager.h" #include "DomainServerWebSessionData.h" #include "WalletTransaction.h" @@ -51,19 +52,14 @@ public slots: /// Called by NodeList to inform us a node has been killed void nodeKilled(SharedNodePointer node); - void publicKeyJSONCallback(QNetworkReply& requestReply); void transactionJSONCallback(const QJsonObject& data); void restart(); void processRequestAssignmentPacket(QSharedPointer packet); - void processConnectRequestPacket(QSharedPointer packet); void processListRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); void processNodeJSONStatsPacket(QSharedPointer packet, SharedNodePointer sendingNode); void processPathQueryPacket(QSharedPointer packet); - void processICEPingPacket(QSharedPointer packet); - void processICEPingReplyPacket(QSharedPointer packet); - void processICEPeerInformationPacket(QSharedPointer packet); private slots: void aboutToQuit(); @@ -75,7 +71,9 @@ private slots: void performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr); void sendHeartbeatToDataServer() { sendHeartbeatToDataServer(QString()); } void sendHeartbeatToIceServer(); - void handlePeerPingTimeout(); + + void handleConnectedNode(SharedNodePointer newNode); + private: void setupNodeListAndAssignments(const QUuid& sessionUUID = QUuid::createUuid()); bool optionallySetupOAuth(); @@ -88,20 +86,9 @@ private: void setupAutomaticNetworking(); void sendHeartbeatToDataServer(const QString& networkAddress); - void pingPunchForConnectingPeer(const SharedNetworkPeer& peer); - unsigned int countConnectedUsers(); - bool verifyUserSignature (const QString& username, const QByteArray& usernameSignature, QString& reasonReturn); - bool shouldAllowConnectionFromNode(const QString& username, const QByteArray& usernameSignature, - const HifiSockAddr& senderSockAddr, QString& reasonReturn); - void preloadAllowedUserPublicKeys(); - void requestUserPublicKey(const QString& username); - - int parseNodeData(QDataStream& packetStream, NodeType_t& nodeType, - HifiSockAddr& publicSockAddr, HifiSockAddr& localSockAddr, const HifiSockAddr& senderSockAddr); - void sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr& senderSockAddr, - const NodeSet& nodeInterestSet); + void sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr& senderSockAddr); QUuid connectionSecretForNodes(const SharedNodePointer& nodeA, const SharedNodePointer& nodeB); void broadcastNewNode(const SharedNodePointer& node); @@ -112,12 +99,11 @@ private: void populateDefaultStaticAssignmentsExcludingTypes(const QSet& excludedTypes); void populateStaticScriptedAssignmentsFromSettings(); - SharedAssignmentPointer matchingQueuedAssignmentForCheckIn(const QUuid& checkInUUID, NodeType_t nodeType); + SharedAssignmentPointer dequeueMatchingAssignment(const QUuid& checkInUUID, NodeType_t nodeType); SharedAssignmentPointer deployableAssignmentForRequest(const Assignment& requestAssignment); - void removeMatchingAssignmentFromQueue(const SharedAssignmentPointer& removableAssignment); void refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer& assignment); void addStaticAssignmentsToQueue(); - + QUrl oauthRedirectURL(); QUrl oauthAuthorizationURL(const QUuid& stateUUID = QUuid::createUuid()); @@ -132,13 +118,14 @@ private: QJsonObject jsonForSocket(const HifiSockAddr& socket); QJsonObject jsonObjectForNode(const SharedNodePointer& node); + + DomainGatekeeper _gatekeeper; HTTPManager _httpManager; HTTPSManager* _httpsManager; QHash _allAssignments; QQueue _unfulfilledAssignments; - QHash _pendingAssignedNodes; TransactionHash _pendingAssignmentCredits; bool _isUsingDTLS; @@ -150,18 +137,14 @@ private: QSet _webAuthenticationStateSet; QHash _cookieSessionHash; - - QHash _connectionTokenHash; - - QHash _userPublicKeys; - - QHash _icePeers; QString _automaticNetworkingSetting; DomainServerSettingsManager _settingsManager; HifiSockAddr _iceServerSocket; + + friend class DomainGatekeeper; }; diff --git a/domain-server/src/NodeConnectionData.cpp b/domain-server/src/NodeConnectionData.cpp new file mode 100644 index 0000000000..3c75e8faad --- /dev/null +++ b/domain-server/src/NodeConnectionData.cpp @@ -0,0 +1,42 @@ +// +// NodeConnectionData.cpp +// domain-server/src +// +// Created by Stephen Birarda on 2015-08-24. +// Copyright 2015 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 "NodeConnectionData.h" + +NodeConnectionData NodeConnectionData::fromDataStream(QDataStream& dataStream, const HifiSockAddr& senderSockAddr, + bool isConnectRequest) { + NodeConnectionData newHeader; + + if (isConnectRequest) { + dataStream >> newHeader.connectUUID; + } + + dataStream >> newHeader.nodeType + >> newHeader.publicSockAddr >> newHeader.localSockAddr + >> newHeader.interestList; + + newHeader.senderSockAddr = senderSockAddr; + + if (newHeader.publicSockAddr.getAddress().isNull()) { + // this node wants to use us its STUN server + // so set the node public address to whatever we perceive the public address to be + + // if the sender is on our box then leave its public address to 0 so that + // other users attempt to reach it on the same address they have for the domain-server + if (senderSockAddr.getAddress().isLoopback()) { + newHeader.publicSockAddr.setAddress(QHostAddress()); + } else { + newHeader.publicSockAddr.setAddress(senderSockAddr.getAddress()); + } + } + + return newHeader; +} diff --git a/domain-server/src/NodeConnectionData.h b/domain-server/src/NodeConnectionData.h new file mode 100644 index 0000000000..6b3b8eb7c1 --- /dev/null +++ b/domain-server/src/NodeConnectionData.h @@ -0,0 +1,33 @@ +// +// NodeConnectionData.h +// domain-server/src +// +// Created by Stephen Birarda on 2015-08-24. +// Copyright 2015 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 +// + +#pragma once + +#ifndef hifi_NodeConnectionData_h +#define hifi_NodeConnectionData_h + +#include + +class NodeConnectionData { +public: + static NodeConnectionData fromDataStream(QDataStream& dataStream, const HifiSockAddr& senderSockAddr, + bool isConnectRequest = true); + + QUuid connectUUID; + NodeType_t nodeType; + HifiSockAddr publicSockAddr; + HifiSockAddr localSockAddr; + HifiSockAddr senderSockAddr; + QList interestList; +}; + + +#endif // hifi_NodeConnectionData_h diff --git a/domain-server/src/PendingAssignedNodeData.cpp b/domain-server/src/PendingAssignedNodeData.cpp index 30310ac01f..1376e6aa0e 100644 --- a/domain-server/src/PendingAssignedNodeData.cpp +++ b/domain-server/src/PendingAssignedNodeData.cpp @@ -16,5 +16,5 @@ PendingAssignedNodeData::PendingAssignedNodeData(const QUuid& assignmentUUID, co _walletUUID(walletUUID), _nodeVersion(nodeVersion) { - -} \ No newline at end of file + +} diff --git a/domain-server/src/PendingAssignedNodeData.h b/domain-server/src/PendingAssignedNodeData.h index 2d546f573f..eb384a7a93 100644 --- a/domain-server/src/PendingAssignedNodeData.h +++ b/domain-server/src/PendingAssignedNodeData.h @@ -34,4 +34,4 @@ private: QString _nodeVersion; }; -#endif // hifi_PendingAssignedNodeData_h \ No newline at end of file +#endif // hifi_PendingAssignedNodeData_h diff --git a/examples/particles.js b/examples/particles.js index deb6228fff..fc1a936a72 100644 --- a/examples/particles.js +++ b/examples/particles.js @@ -39,10 +39,11 @@ this.entity = Entities.addEntity({ type: "ParticleEffect", animationSettings: animationSettings, position: spawnPoint, - textures: "http://www.hyperlogic.org/images/particle.png", - emitRate: emitRate, - emitStrength: emitStrength, - emitDirection: emitDirection, + dimensions: {x: 2, y: 2, z: 2}, + emitVelocity: {x: 0, y: 5, z: 0}, + velocitySpread: {x: 2, y: 0, z: 2}, + emitAcceleration: {x: 0, y: -9.8, z: 0}, + textures: "https://raw.githubusercontent.com/ericrius1/SantasLair/santa/assets/smokeparticle.png", color: color, lifespan: 1.0, visible: true, diff --git a/gvr-interface/CMakeLists.txt b/gvr-interface/CMakeLists.txt index c4880a80b6..2c1e87c8b4 100644 --- a/gvr-interface/CMakeLists.txt +++ b/gvr-interface/CMakeLists.txt @@ -88,6 +88,4 @@ if (ANDROID) endif (ANDROID) -setup_memory_debugger() - copy_dlls_beside_windows_executable() diff --git a/ice-server/CMakeLists.txt b/ice-server/CMakeLists.txt index d62192bcec..b056d79efd 100644 --- a/ice-server/CMakeLists.txt +++ b/ice-server/CMakeLists.txt @@ -1,7 +1,5 @@ set(TARGET_NAME ice-server) -setup_memory_debugger() - # setup the project and link required Qt modules setup_hifi_project(Network) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 0c531dfacb..d7ee9228f4 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -205,6 +205,4 @@ else (APPLE) endif() endif (APPLE) -setup_memory_debugger() - copy_dlls_beside_windows_executable() diff --git a/interface/interface_da.ts b/interface/i18n/interface_da.ts similarity index 100% rename from interface/interface_da.ts rename to interface/i18n/interface_da.ts diff --git a/interface/interface_en.ts b/interface/i18n/interface_en.ts similarity index 100% rename from interface/interface_en.ts rename to interface/i18n/interface_en.ts diff --git a/interface/resources/fonts/fontawesome-webfont.ttf b/interface/resources/fonts/fontawesome-webfont.ttf index ed9372f8ea..d7994e1308 100644 Binary files a/interface/resources/fonts/fontawesome-webfont.ttf and b/interface/resources/fonts/fontawesome-webfont.ttf differ diff --git a/interface/resources/qml/VrMenu.qml b/interface/resources/qml/VrMenu.qml index f0b7bf9fd3..ef7ae852d4 100644 --- a/interface/resources/qml/VrMenu.qml +++ b/interface/resources/qml/VrMenu.qml @@ -185,4 +185,26 @@ Hifi.VrMenu { } } } + + function addMenu(menu, newMenu) { + return menu.addMenu(newMenu); + } + + function addItem(menu, newMenuItem) { + return menu.addItem(newMenuItem); + } + + function insertItem(menu, beforeItem, newMenuItem) { + for (var i = 0; i < menu.items.length; ++i) { + console.log(menu.items[i]); + if (menu.items[i] === beforeItem) { + return menu.insertItem(i, newMenuItem); + } + } + return addItem(menu, newMenuItem); + } + + function removeItem(menu, menuItem) { + menu.removeItem(menuItem); + } } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d94cff19f1..b01f89d90e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -105,6 +105,7 @@ #include #include "AudioClient.h" +#include "CrashHandler.h" #include "DiscoverabilityManager.h" #include "GLCanvas.h" #include "LODManager.h" @@ -190,6 +191,11 @@ static QTimer* billboardPacketTimer = NULL; static QTimer* checkFPStimer = NULL; static QTimer* idleTimer = NULL; +static const unsigned int TARGET_SIM_FRAMERATE = 60; +static const unsigned int THROTTLED_SIM_FRAMERATE = 15; +static const int TARGET_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / TARGET_SIM_FRAMERATE; +static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SIM_FRAMERATE; + const QString CHECK_VERSION_URL = "https://highfidelity.com/latestVersion.xml"; const QString SKIP_FILENAME = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/hifi.skipversion"; @@ -258,6 +264,12 @@ bool setupEssentials(int& argc, char** argv) { // Set build version QCoreApplication::setApplicationVersion(BUILD_VERSION); + Setting::preInit(); + + CrashHandler::checkForAndHandleCrash(); + CrashHandler::writeRunningMarkerFiler(); + qAddPostRoutine(CrashHandler::deleteRunningMarkerFile); + DependencyManager::registerInheritance(); DependencyManager::registerInheritance(); DependencyManager::registerInheritance(); @@ -349,7 +361,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _trayIcon(new QSystemTrayIcon(_window)), _lastNackTime(usecTimestampNow()), _lastSendDownstreamAudioStats(usecTimestampNow()), - _isVSyncOn(true), _isThrottleFPSEnabled(true), _aboutToQuit(false), _notifiedPacketVersionMismatchThisDomain(false), @@ -757,8 +768,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : }); connect(this, &Application::applicationStateChanged, this, &Application::activeChanged); - - setVSyncEnabled(); // make sure VSync is set properly at startup } void Application::aboutToQuit() { @@ -932,7 +941,7 @@ void Application::initializeGL() { // call our idle function whenever we can idleTimer = new QTimer(this); connect(idleTimer, SIGNAL(timeout()), SLOT(idle())); - idleTimer->start(0); + idleTimer->start(TARGET_SIM_FRAME_PERIOD_MS); _idleLoopStdev.reset(); if (_justStarted) { @@ -1439,7 +1448,6 @@ void Application::keyPressEvent(QKeyEvent* event) { bool isMeta = event->modifiers().testFlag(Qt::ControlModifier); bool isOption = event->modifiers().testFlag(Qt::AltModifier); switch (event->key()) { - break; case Qt::Key_Enter: case Qt::Key_Return: if (isOption) { @@ -2047,38 +2055,38 @@ void Application::checkFPS() { } void Application::idle() { - PROFILE_RANGE(__FUNCTION__); - static SimpleAverage interIdleDurations; - - static uint64_t lastIdleStart{ 0 }; - static uint64_t lastIdleEnd{ 0 }; - uint64_t now = usecTimestampNow(); - uint64_t idleStartToStartDuration = now - lastIdleStart; - - if (lastIdleStart > 0 && idleStartToStartDuration > 0) { - _simsPerSecond.updateAverage((float)USECS_PER_SECOND / (float)idleStartToStartDuration); - } - - lastIdleStart = now; - - if (lastIdleEnd != 0) { - interIdleDurations.update(now - lastIdleEnd); - static uint64_t lastReportTime = now; - if ((now - lastReportTime) >= (USECS_PER_SECOND)) { - static QString LOGLINE("Average inter-idle time: %1 us for %2 samples"); - if (Menu::getInstance()->isOptionChecked(MenuOption::LogExtraTimings)) { - qCDebug(interfaceapp_timing) << LOGLINE.arg((int)interIdleDurations.getAverage()).arg(interIdleDurations.getCount()); - } - interIdleDurations.reset(); - lastReportTime = now; - } - } - - PerformanceTimer perfTimer("idle"); if (_aboutToQuit) { return; // bail early, nothing to do here. } + // depending on whether we're throttling or not. + // Once rendering is off on another thread we should be able to have Application::idle run at start(0) in + // perpetuity and not expect events to get backed up. + bool isThrottled = getActiveDisplayPlugin()->isThrottled(); + // Only run simulation code if more than the targetFramePeriod have passed since last time we ran + // This attempts to lock the simulation at 60 updates per second, regardless of framerate + float timeSinceLastUpdateUs = (float)_lastTimeUpdated.nsecsElapsed() / NSECS_PER_USEC; + float secondsSinceLastUpdate = timeSinceLastUpdateUs / USECS_PER_SECOND; + + if (isThrottled && (timeSinceLastUpdateUs / USECS_PER_MSEC) < THROTTLED_SIM_FRAME_PERIOD_MS) { + return; // bail early, we're throttled and not enough time has elapsed + } + + _lastTimeUpdated.start(); + + + { + PROFILE_RANGE(__FUNCTION__); + uint64_t now = usecTimestampNow(); + static uint64_t lastIdleStart{ now }; + uint64_t idleStartToStartDuration = now - lastIdleStart; + if (idleStartToStartDuration != 0) { + _simsPerSecond.updateAverage((float)USECS_PER_SECOND / (float)idleStartToStartDuration); + } + lastIdleStart = now; + } + + PerformanceTimer perfTimer("idle"); // Drop focus from _keyboardFocusedItem if no keyboard messages for 30 seconds if (!_keyboardFocusedItem.isInvalidID()) { const quint64 LOSE_FOCUS_AFTER_ELAPSED_TIME = 30 * USECS_PER_SECOND; // if idle for 30 seconds, drop focus @@ -2094,69 +2102,42 @@ void Application::idle() { bool showWarnings = getLogger()->extraDebugging(); PerformanceWarning warn(showWarnings, "idle()"); - // Only run simulation code if more than the targetFramePeriod have passed since last time we ran - double targetFramePeriod = 0.0; - unsigned int targetFramerate = getRenderTargetFramerate(); - if (targetFramerate > 0) { - targetFramePeriod = 1000.0 / targetFramerate; + { + PerformanceTimer perfTimer("update"); + PerformanceWarning warn(showWarnings, "Application::idle()... update()"); + static const float BIGGEST_DELTA_TIME_SECS = 0.25f; + update(glm::clamp(secondsSinceLastUpdate, 0.0f, BIGGEST_DELTA_TIME_SECS)); } - double timeSinceLastUpdate = (double)_lastTimeUpdated.nsecsElapsed() / 1000000.0; - if (timeSinceLastUpdate > targetFramePeriod) { - _lastTimeUpdated.start(); - { - PerformanceTimer perfTimer("update"); - PerformanceWarning warn(showWarnings, "Application::idle()... update()"); - const float BIGGEST_DELTA_TIME_SECS = 0.25f; - PROFILE_RANGE(__FUNCTION__ "/idleUpdate"); - update(glm::clamp((float)timeSinceLastUpdate / 1000.0f, 0.0f, BIGGEST_DELTA_TIME_SECS)); - } - { - PerformanceTimer perfTimer("updateGL"); - PerformanceWarning warn(showWarnings, "Application::idle()... updateGL()"); - getActiveDisplayPlugin()->idle(); - auto inputPlugins = PluginManager::getInstance()->getInputPlugins(); - foreach(auto inputPlugin, inputPlugins) { - QString name = inputPlugin->getName(); - QAction* action = Menu::getInstance()->getActionForOption(name); - if (action && action->isChecked()) { - inputPlugin->idle(); - } + { + PerformanceTimer perfTimer("pluginIdle"); + PerformanceWarning warn(showWarnings, "Application::idle()... pluginIdle()"); + getActiveDisplayPlugin()->idle(); + auto inputPlugins = PluginManager::getInstance()->getInputPlugins(); + foreach(auto inputPlugin, inputPlugins) { + QString name = inputPlugin->getName(); + QAction* action = Menu::getInstance()->getActionForOption(name); + if (action && action->isChecked()) { + inputPlugin->idle(); } } - { - PerformanceTimer perfTimer("rest"); - PerformanceWarning warn(showWarnings, "Application::idle()... rest of it"); - _idleLoopStdev.addValue(timeSinceLastUpdate); + } + { + PerformanceTimer perfTimer("rest"); + PerformanceWarning warn(showWarnings, "Application::idle()... rest of it"); + _idleLoopStdev.addValue(secondsSinceLastUpdate); - // Record standard deviation and reset counter if needed - const int STDEV_SAMPLES = 500; - if (_idleLoopStdev.getSamples() > STDEV_SAMPLES) { - _idleLoopMeasuredJitter = _idleLoopStdev.getStDev(); - _idleLoopStdev.reset(); - } - } - - float secondsSinceLastUpdate = (float)timeSinceLastUpdate / 1000.0f; - _overlayConductor.update(secondsSinceLastUpdate); - - // depending on whether we're throttling or not. - // Once rendering is off on another thread we should be able to have Application::idle run at start(0) in - // perpetuity and not expect events to get backed up. - - bool isThrottled = getActiveDisplayPlugin()->isThrottled(); - static const int THROTTLED_IDLE_TIMER_DELAY = MSECS_PER_SECOND / 15; - static const int IDLE_TIMER_DELAY_MS = 2; - int desiredInterval = isThrottled ? THROTTLED_IDLE_TIMER_DELAY : IDLE_TIMER_DELAY_MS; - //qDebug() << "isThrottled:" << isThrottled << "desiredInterval:" << desiredInterval; - - if (idleTimer->interval() != desiredInterval) { - idleTimer->start(desiredInterval); + // Record standard deviation and reset counter if needed + const int STDEV_SAMPLES = 500; + if (_idleLoopStdev.getSamples() > STDEV_SAMPLES) { + _idleLoopMeasuredJitter = _idleLoopStdev.getStDev(); + _idleLoopStdev.reset(); } } + + _overlayConductor.update(secondsSinceLastUpdate); // check for any requested background downloads. emit checkBackgroundDownloads(); - lastIdleEnd = usecTimestampNow(); } float Application::getAverageSimsPerSecond() { @@ -4490,90 +4471,10 @@ void Application::takeSnapshot() { } -void Application::setVSyncEnabled() { - _glWidget->makeCurrent(); -#if defined(Q_OS_WIN) - bool vsyncOn = Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerateVSyncOn); - if (wglewGetExtension("WGL_EXT_swap_control")) { - wglSwapIntervalEXT(vsyncOn); - int swapInterval = wglGetSwapIntervalEXT(); - qCDebug(interfaceapp, "V-Sync is %s\n", (swapInterval > 0 ? "ON" : "OFF")); - } else { - qCDebug(interfaceapp, "V-Sync is FORCED ON on this system\n"); - } -#elif defined(Q_OS_LINUX) - // TODO: write the poper code for linux - /* - if (glQueryExtension.... ("GLX_EXT_swap_control")) { - glxSwapIntervalEXT(vsyncOn); - int swapInterval = xglGetSwapIntervalEXT(); - _isVSyncOn = swapInterval; - qCDebug(interfaceapp, "V-Sync is %s\n", (swapInterval > 0 ? "ON" : "OFF")); - } else { - qCDebug(interfaceapp, "V-Sync is FORCED ON on this system\n"); - } - */ -#else - qCDebug(interfaceapp, "V-Sync is FORCED ON on this system\n"); -#endif - _offscreenContext->makeCurrent(); -} - void Application::setThrottleFPSEnabled() { _isThrottleFPSEnabled = Menu::getInstance()->isOptionChecked(MenuOption::ThrottleFPSIfNotFocus); } -bool Application::isVSyncOn() const { -#if defined(Q_OS_WIN) - if (wglewGetExtension("WGL_EXT_swap_control")) { - int swapInterval = wglGetSwapIntervalEXT(); - return (swapInterval > 0); - } -#elif defined(Q_OS_LINUX) - // TODO: write the poper code for linux - /* - if (glQueryExtension.... ("GLX_EXT_swap_control")) { - int swapInterval = xglGetSwapIntervalEXT(); - return (swapInterval > 0); - } else { - return true; - } - */ -#endif - return true; -} - -bool Application::isVSyncEditable() const { -#if defined(Q_OS_WIN) - if (wglewGetExtension("WGL_EXT_swap_control")) { - return true; - } -#elif defined(Q_OS_LINUX) - // TODO: write the poper code for linux - /* - if (glQueryExtension.... ("GLX_EXT_swap_control")) { - return true; - } - */ -#endif - return false; -} - -unsigned int Application::getRenderTargetFramerate() const { - if (Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerateUnlimited)) { - return 0; - } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerate60)) { - return 60; - } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerate50)) { - return 50; - } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerate40)) { - return 40; - } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerate30)) { - return 30; - } - return 0; -} - float Application::getRenderResolutionScale() const { if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionOne)) { return 1.0f; @@ -5155,3 +5056,9 @@ void Application::emulateMouse(Hand* hand, float click, float shift, int index) _oldHandLeftClick[index] = false; } } + +void Application::crashApplication() { + QObject* object = nullptr; + bool value = object->isWindowType(); + qCDebug(interfaceapp) << "Intentionally crashed Interface"; +} diff --git a/interface/src/Application.h b/interface/src/Application.h index ecec761c13..a362f40ace 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -300,9 +300,6 @@ public: float getRenderResolutionScale() const; int getRenderAmbientLight() const; - unsigned int getRenderTargetFramerate() const; - bool isVSyncOn() const; - bool isVSyncEditable() const; bool isAboutToQuit() const { return _aboutToQuit; } // the isHMDmode is true whenever we use the interface from an HMD and not a standard flat display @@ -411,8 +408,6 @@ public slots: void domainSettingsReceived(const QJsonObject& domainSettingsObject); - void setVSyncEnabled(); - void setThrottleFPSEnabled(); bool isThrottleFPSEnabled() { return _isThrottleFPSEnabled; } @@ -438,6 +433,8 @@ public slots: void reloadResourceCaches(); + void crashApplication(); + private slots: void clearDomainOctreeDetails(); void checkFPS(); @@ -626,7 +623,6 @@ private: quint64 _lastNackTime; quint64 _lastSendDownstreamAudioStats; - bool _isVSyncOn; bool _isThrottleFPSEnabled; bool _aboutToQuit; diff --git a/interface/src/CrashHandler.cpp b/interface/src/CrashHandler.cpp new file mode 100644 index 0000000000..ce5facb580 --- /dev/null +++ b/interface/src/CrashHandler.cpp @@ -0,0 +1,183 @@ +// +// CrashHandler.cpp +// interface/src +// +// Created by David Rowe on 24 Aug 2015. +// Copyright 2015 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 "CrashHandler.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "DataServerAccountInfo.h" +#include "Menu.h" + +Q_DECLARE_METATYPE(DataServerAccountInfo) + +static const QString RUNNING_MARKER_FILENAME = "Interface.running"; + +void CrashHandler::checkForAndHandleCrash() { + QFile runningMarkerFile(runningMarkerFilePath()); + if (runningMarkerFile.exists()) { + QSettings::setDefaultFormat(QSettings::IniFormat); + QSettings settings; + settings.beginGroup("Developer"); + QVariant displayCrashOptions = settings.value(MenuOption::DisplayCrashOptions); + settings.endGroup(); + if (!displayCrashOptions.isValid() // Option does not exist in Interface.ini so assume default behavior. + || displayCrashOptions.toBool()) { + Action action = promptUserForAction(); + if (action != DO_NOTHING) { + handleCrash(action); + } + } + } +} + +CrashHandler::Action CrashHandler::promptUserForAction() { + QDialog crashDialog; + crashDialog.setWindowTitle("Interface Crashed Last Run"); + + QVBoxLayout* layout = new QVBoxLayout; + + QLabel* label = new QLabel("If you are having trouble starting would you like to reset your settings?"); + layout->addWidget(label); + + QRadioButton* option1 = new QRadioButton("Reset all my settings"); + QRadioButton* option2 = new QRadioButton("Reset my settings but retain login and avatar info."); + QRadioButton* option3 = new QRadioButton("Continue with my current settings"); + option3->setChecked(true); + layout->addWidget(option1); + layout->addWidget(option2); + layout->addWidget(option3); + layout->addSpacing(12); + layout->addStretch(); + + QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok); + layout->addWidget(buttons); + crashDialog.connect(buttons, SIGNAL(accepted()), SLOT(accept())); + + crashDialog.setLayout(layout); + + int result = crashDialog.exec(); + + if (result == QDialog::Accepted) { + if (option1->isChecked()) { + return CrashHandler::DELETE_INTERFACE_INI; + } + if (option2->isChecked()) { + return CrashHandler::RETAIN_LOGIN_AND_AVATAR_INFO; + } + } + + // Dialog cancelled or "do nothing" option chosen + return CrashHandler::DO_NOTHING; +} + +void CrashHandler::handleCrash(CrashHandler::Action action) { + if (action != CrashHandler::DELETE_INTERFACE_INI && action != CrashHandler::RETAIN_LOGIN_AND_AVATAR_INFO) { + // CrashHandler::DO_NOTHING or unexpected value + return; + } + + QSettings::setDefaultFormat(QSettings::IniFormat); + QSettings settings; + const QString ADDRESS_MANAGER_GROUP = "AddressManager"; + const QString ADDRESS_KEY = "address"; + const QString AVATAR_GROUP = "Avatar"; + const QString DISPLAY_NAME_KEY = "displayName"; + const QString FULL_AVATAR_URL_KEY = "fullAvatarURL"; + const QString FULL_AVATAR_MODEL_NAME_KEY = "fullAvatarModelName"; + const QString ACCOUNTS_GROUP = "accounts"; + QString displayName; + QUrl fullAvatarURL; + QString fullAvatarModelName; + QUrl address; + QMap accounts; + + if (action == CrashHandler::RETAIN_LOGIN_AND_AVATAR_INFO) { + // Read login and avatar info + + qRegisterMetaType("DataServerAccountInfo"); + qRegisterMetaTypeStreamOperators("DataServerAccountInfo"); + + // Location and orientation + settings.beginGroup(ADDRESS_MANAGER_GROUP); + address = settings.value(ADDRESS_KEY).toUrl(); + settings.endGroup(); + + // Display name and avatar + settings.beginGroup(AVATAR_GROUP); + displayName = settings.value(DISPLAY_NAME_KEY).toString(); + fullAvatarURL = settings.value(FULL_AVATAR_URL_KEY).toUrl(); + fullAvatarModelName = settings.value(FULL_AVATAR_MODEL_NAME_KEY).toString(); + settings.endGroup(); + + // Accounts + settings.beginGroup(ACCOUNTS_GROUP); + foreach(const QString& key, settings.allKeys()) { + accounts.insert(key, settings.value(key).value()); + } + settings.endGroup(); + } + + // Delete Interface.ini + QFile settingsFile(settings.fileName()); + if (settingsFile.exists()) { + settingsFile.remove(); + } + + if (action == CrashHandler::RETAIN_LOGIN_AND_AVATAR_INFO) { + // Write login and avatar info + + // Location and orientation + settings.beginGroup(ADDRESS_MANAGER_GROUP); + settings.setValue(ADDRESS_KEY, address); + settings.endGroup(); + + // Display name and avatar + settings.beginGroup(AVATAR_GROUP); + settings.setValue(DISPLAY_NAME_KEY, displayName); + settings.setValue(FULL_AVATAR_URL_KEY, fullAvatarURL); + settings.setValue(FULL_AVATAR_MODEL_NAME_KEY, fullAvatarModelName); + settings.endGroup(); + + // Accounts + settings.beginGroup(ACCOUNTS_GROUP); + foreach(const QString& key, accounts.keys()) { + settings.setValue(key, QVariant::fromValue(accounts.value(key))); + } + settings.endGroup(); + } +} + +void CrashHandler::writeRunningMarkerFiler() { + QFile runningMarkerFile(runningMarkerFilePath()); + if (!runningMarkerFile.exists()) { + runningMarkerFile.open(QIODevice::WriteOnly); + runningMarkerFile.close(); + } +} +void CrashHandler::deleteRunningMarkerFile() { + QFile runningMarkerFile(runningMarkerFilePath()); + if (runningMarkerFile.exists()) { + runningMarkerFile.remove(); + } +} + +const QString CrashHandler::runningMarkerFilePath() { + return QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + RUNNING_MARKER_FILENAME; +} diff --git a/interface/src/CrashHandler.h b/interface/src/CrashHandler.h new file mode 100644 index 0000000000..fc754cf1ac --- /dev/null +++ b/interface/src/CrashHandler.h @@ -0,0 +1,38 @@ +// +// CrashHandler.h +// interface/src +// +// Created by David Rowe on 24 Aug 2015. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_CrashHandler_h +#define hifi_CrashHandler_h + +#include + +class CrashHandler { + +public: + static void checkForAndHandleCrash(); + + static void writeRunningMarkerFiler(); + static void deleteRunningMarkerFile(); + +private: + enum Action { + DELETE_INTERFACE_INI, + RETAIN_LOGIN_AND_AVATAR_INFO, + DO_NOTHING + }; + + static Action promptUserForAction(); + static void handleCrash(Action action); + + static const QString runningMarkerFilePath(); +}; + +#endif // hifi_CrashHandler_h diff --git a/interface/src/GLCanvas.cpp b/interface/src/GLCanvas.cpp index bfa2dacf3a..66aae5343b 100644 --- a/interface/src/GLCanvas.cpp +++ b/interface/src/GLCanvas.cpp @@ -62,7 +62,7 @@ void GLCanvas::paintGL() { // FIXME - I'm not sure why this still remains, it appears as if this GLCanvas gets a single paintGL call near // the beginning of the application starting up. I'm not sure if we really need to call Application::paintGL() // in this case, since the display plugins eventually handle all the painting - if ((!Application::getInstance()->getWindow()->isMinimized() || !Application::getInstance()->isThrottleFPSEnabled())) { + if (!Application::getInstance()->getWindow()->isMinimized() || !Application::getInstance()->isThrottleFPSEnabled()) { Application::getInstance()->paintGL(); } } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 3305da5ba7..8783da487f 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -253,7 +253,7 @@ Menu::Menu() { addActionToQMenuAndActionHash(toolsMenu, MenuOption::PackageModel, 0, qApp, SLOT(packageModel())); - MenuWrapper* displayMenu = addMenu(DisplayPlugin::MENU_PATH); + MenuWrapper* displayMenu = addMenu(DisplayPlugin::MENU_PATH()); { MenuWrapper* displayModeMenu = addMenu(MenuOption::OutputMenu); QActionGroup* displayModeGroup = new QActionGroup(displayModeMenu); @@ -364,25 +364,8 @@ Menu::Menu() { ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight8, 0, false)); ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight9, 0, false)); - { - MenuWrapper* framerateMenu = renderOptionsMenu->addMenu(MenuOption::RenderTargetFramerate); - QActionGroup* framerateGroup = new QActionGroup(framerateMenu); - framerateGroup->setExclusive(true); - framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerateUnlimited, 0, true)); - framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate60, 0, false)); - framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate50, 0, false)); - framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate40, 0, false)); - framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate30, 0, false)); - -#if defined(Q_OS_MAC) -#else - addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::RenderTargetFramerateVSyncOn, 0, true, - qApp, SLOT(setVSyncEnabled())); -#endif - addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::ThrottleFPSIfNotFocus, 0, true, + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::ThrottleFPSIfNotFocus, 0, true, qApp, SLOT(setThrottleFPSEnabled())); - } - MenuWrapper* resolutionMenu = renderOptionsMenu->addMenu(MenuOption::RenderResolution); QActionGroup* resolutionGroup = new QActionGroup(resolutionMenu); @@ -479,6 +462,8 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderFocusIndicator, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowWhosLookingAtMe, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::FixGaze, 0, false); + addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableRigAnimations, 0, false, + avatar, SLOT(setEnableRigAnimations(bool))); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::Connexion, @@ -608,6 +593,9 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowOwned); addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowHulls); + addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::DisplayCrashOptions, 0, true); + addActionToQMenuAndActionHash(developerMenu, MenuOption::CrashInterface, 0, qApp, SLOT(crashApplication())); + MenuWrapper* helpMenu = addMenu("Help"); addActionToQMenuAndActionHash(helpMenu, MenuOption::EditEntitiesHelp, 0, qApp, SLOT(showEditEntitiesHelp())); @@ -1077,6 +1065,9 @@ QAction* MenuWrapper::addAction(const QString& menuName, const QObject* receiver void MenuWrapper::removeAction(QAction* action) { _realMenu->removeAction(action); + VrMenu::executeOrQueue([=](VrMenu* vrMenu) { + vrMenu->removeAction(action); + }); } void MenuWrapper::insertAction(QAction* before, QAction* action) { diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 4bd1e7f664..94e49abcc7 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -164,6 +164,7 @@ namespace MenuOption { const QString CopyAddress = "Copy Address to Clipboard"; const QString CopyPath = "Copy Path to Clipboard"; const QString CoupleEyelids = "Couple Eyelids"; + const QString CrashInterface = "Crash Interface"; const QString DebugAmbientOcclusion = "Debug Ambient Occlusion"; const QString DecreaseAvatarSize = "Decrease Avatar Size"; const QString DeleteBookmark = "Delete Bookmark..."; @@ -172,6 +173,7 @@ namespace MenuOption { const QString DisableLightEntities = "Disable Light Entities"; const QString DisableNackPackets = "Disable Entity NACK Packets"; const QString DiskCacheEditor = "Disk Cache Editor"; + const QString DisplayCrashOptions = "Display Crash Options"; const QString DisplayHands = "Show Hand Info"; const QString DisplayHandTargets = "Show Hand Targets"; const QString DisplayModelBounds = "Display Model Bounds"; @@ -186,6 +188,7 @@ namespace MenuOption { const QString EditEntitiesHelp = "Edit Entities Help..."; const QString Enable3DTVMode = "Enable 3DTV Mode"; const QString EnableCharacterController = "Enable avatar collisions"; + const QString EnableRigAnimations = "Enable Rig Animations"; const QString ExpandMyAvatarSimulateTiming = "Expand /myAvatar/simulation"; const QString ExpandMyAvatarTiming = "Expand /myAvatar"; const QString ExpandOtherAvatarTiming = "Expand /otherAvatar"; @@ -238,13 +241,6 @@ namespace MenuOption { const QString RenderLookAtTargets = "Show Look-at Targets"; const QString RenderLookAtVectors = "Show Look-at Vectors"; const QString RenderSkeletonCollisionShapes = "Show Skeleton Collision Shapes"; - const QString RenderTargetFramerate = "Framerate"; - const QString RenderTargetFramerateUnlimited = "Unlimited"; - const QString RenderTargetFramerate60 = "60"; - const QString RenderTargetFramerate50 = "50"; - const QString RenderTargetFramerate40 = "40"; - const QString RenderTargetFramerate30 = "30"; - const QString RenderTargetFramerateVSyncOn = "V-Sync On"; const QString RenderResolution = "Scale Resolution"; const QString RenderResolutionOne = "1"; const QString RenderResolutionTwoThird = "2/3"; diff --git a/interface/src/ModelSelector.cpp b/interface/src/ModelSelector.cpp index 8e130cec1a..c4922bdd70 100644 --- a/interface/src/ModelSelector.cpp +++ b/interface/src/ModelSelector.cpp @@ -65,9 +65,8 @@ FSTReader::ModelType ModelSelector::getModelType() const { return FSTReader::ATTACHMENT_MODEL; } else if (text == ENTITY_MODEL_STRING) { return FSTReader::ENTITY_MODEL; - } else { - Q_UNREACHABLE(); - } + } + Q_UNREACHABLE(); } void ModelSelector::accept() { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 1fec536082..bf42cfa7e1 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -705,9 +705,10 @@ float loadSetting(QSettings& settings, const char* name, float defaultValue) { } void MyAvatar::setEnableRigAnimations(bool isEnabled) { - Settings settings; - settings.setValue("enableRig", isEnabled); _rig->setEnableRig(isEnabled); + if (!isEnabled) { + _rig->deleteAnimations(); + } } void MyAvatar::loadData() { @@ -769,7 +770,7 @@ void MyAvatar::loadData() { setCollisionSoundURL(settings.value("collisionSoundURL", DEFAULT_AVATAR_COLLISION_SOUND_URL).toString()); settings.endGroup(); - _rig->setEnableRig(settings.value("enableRig").toBool()); + _rig->setEnableRig(Menu::getInstance()->isOptionChecked(MenuOption::EnableRigAnimations)); } void MyAvatar::saveAttachmentData(const AttachmentData& attachment) const { @@ -1307,7 +1308,7 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe bool isThrust = (glm::length2(_thrust) > EPSILON); if (_isPushing || isThrust || (_scriptedMotorTimescale < MAX_KEYBOARD_MOTOR_TIMESCALE && - _motionBehaviors | AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED)) { + (_motionBehaviors & AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED))) { // we don't want to brake if something is pushing the avatar around timescale = _keyboardMotorTimescale; _isBraking = false; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 8ff9211101..9e7ab11aa6 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -87,7 +87,6 @@ public: Q_INVOKABLE AnimationDetails getAnimationDetailsByRole(const QString& role); Q_INVOKABLE AnimationDetails getAnimationDetails(const QString& url); void clearJointAnimationPriorities(); - Q_INVOKABLE void setEnableRigAnimations(bool isEnabled); // get/set avatar data void saveData(); @@ -190,6 +189,7 @@ public slots: void loadLastRecording(); virtual void rebuildSkeletonBody(); + void setEnableRigAnimations(bool isEnabled); signals: void transformChanged(); diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index bd50215de6..752fb55ce6 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -131,13 +131,20 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { // // Thus this should really only be ... else if (_owningAvatar->getHead()->isLookingAtMe()) {... // However, in the !isLookingAtMe case, the eyes aren't rotating the way they should right now. - // (They latch their looking at me position.) We will revisit that as priorities allow. + // We will revisit that as priorities allow, and particularly after the new rig/animation/joints. const FBXGeometry& geometry = _geometry->getFBXGeometry(); Head* head = _owningAvatar->getHead(); - _rig->updateEyeJoints(geometry.leftEyeJointIndex, geometry.rightEyeJointIndex, - getTranslation(), getRotation(), - head->getFinalOrientationInWorldFrame(), head->getCorrectedLookAtPosition()); - } + // If the head is not positioned, updateEyeJoints won't get the math right + glm::quat headOrientation; + _rig->getJointRotation(geometry.headJointIndex, headOrientation); + glm::vec3 eulers = safeEulerAngles(headOrientation); + head->setBasePitch(glm::degrees(-eulers.x)); + head->setBaseYaw(glm::degrees(eulers.y)); + head->setBaseRoll(glm::degrees(-eulers.z)); + _rig->updateEyeJoints(geometry.leftEyeJointIndex, geometry.rightEyeJointIndex, + getTranslation(), getRotation(), + head->getFinalOrientationInWorldFrame(), head->getCorrectedLookAtPosition()); + } } // Called by Avatar::simulate after it has set the joint states (fullUpdate true if changed), diff --git a/interface/src/main.cpp b/interface/src/main.cpp index e591034fb5..1de8f9224c 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -103,7 +103,7 @@ int main(int argc, const char* argv[]) { Application app(argc, const_cast(argv), startupTime); QTranslator translator; - translator.load("interface_en"); + translator.load("i18n/interface_en"); app.installTranslator(&translator); qCDebug(interfaceapp, "Created QT Application."); diff --git a/interface/src/ui/OctreeStatsDialog.cpp b/interface/src/ui/OctreeStatsDialog.cpp index 89e5899acd..7e6111bd83 100644 --- a/interface/src/ui/OctreeStatsDialog.cpp +++ b/interface/src/ui/OctreeStatsDialog.cpp @@ -437,7 +437,6 @@ void OctreeStatsDialog::showOctreeServersOfType(int& serverCount, NodeType_t ser case MOST: { extraDetails << "
" ; - const unsigned long USECS_PER_MSEC = 1000; float lastFullEncode = stats.getLastFullTotalEncodeTime() / USECS_PER_MSEC; float lastFullSend = stats.getLastFullElapsedTime() / USECS_PER_MSEC; float lastFullSendInSeconds = stats.getLastFullElapsedTime() / USECS_PER_SECOND; diff --git a/libraries/animation/CMakeLists.txt b/libraries/animation/CMakeLists.txt index fc7fa23dcc..115a2e360c 100644 --- a/libraries/animation/CMakeLists.txt +++ b/libraries/animation/CMakeLists.txt @@ -3,6 +3,4 @@ set(TARGET_NAME animation) # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library(Network Script) -setup_memory_debugger() - link_hifi_libraries(shared gpu model fbx) diff --git a/libraries/animation/src/JointState.h b/libraries/animation/src/JointState.h index 694700338d..8d2c1a7cd0 100644 --- a/libraries/animation/src/JointState.h +++ b/libraries/animation/src/JointState.h @@ -27,9 +27,10 @@ class AngularConstraint; class JointState { public: JointState() {} - JointState(const JointState& other) : _constraint(NULL) { copyState(other); } + JointState(const JointState& other) { copyState(other); } JointState(const FBXJoint& joint); ~JointState(); + JointState& operator=(const JointState& other) { copyState(other); return *this; } void copyState(const JointState& state); void buildConstraint(); diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 2212e06e31..3e466b94d6 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -100,24 +100,21 @@ AnimationHandlePointer Rig::addAnimationByRole(const QString& role, const QStrin AnimationHandlePointer handle = createAnimationHandle(); QString standard = ""; if (url.isEmpty()) { // Default animations for fight club - const QString& base = "https://hifi-public.s3.amazonaws.com/ozan/"; + const QString& base = "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/"; if (role == "walk") { - standard = base + "support/FightClubBotTest1/Animations/standard_walk.fbx"; - lastFrame = 60; + standard = base + "walk_fwd.fbx"; + } else if (role == "backup") { + standard = base + "walk_bwd.fbx"; } else if (role == "leftTurn") { - standard = base + "support/FightClubBotTest1/Animations/left_turn_noHipRotation.fbx"; - lastFrame = 29; + standard = base + "turn_left.fbx"; } else if (role == "rightTurn") { - standard = base + "support/FightClubBotTest1/Animations/right_turn_noHipRotation.fbx"; - lastFrame = 31; + standard = base + "turn_right.fbx"; } else if (role == "leftStrafe") { - standard = base + "animations/fightclub_bot_anims/side_step_left_inPlace.fbx"; - lastFrame = 31; + standard = base + "strafe_left.fbx"; } else if (role == "rightStrafe") { - standard = base + "animations/fightclub_bot_anims/side_step_right_inPlace.fbx"; - lastFrame = 31; + standard = base + "strafe_right.fbx"; } else if (role == "idle") { - standard = base + "support/FightClubBotTest1/Animations/standard_idle.fbx"; + standard = base + "idle.fbx"; fps = 25.0f; } if (!standard.isEmpty()) { @@ -438,11 +435,12 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos } } }; - updateRole("walk", std::abs(forwardSpeed) > 0.01f); + updateRole("walk", forwardSpeed > 0.01f); + updateRole("backup", forwardSpeed < -0.01f); bool isTurning = std::abs(rightTurningSpeed) > 0.5f; updateRole("rightTurn", isTurning && (rightTurningSpeed > 0)); updateRole("leftTurn", isTurning && (rightTurningSpeed < 0)); - bool isStrafing = std::abs(rightLateralSpeed) > 0.01f; + bool isStrafing = !isTurning && (std::abs(rightLateralSpeed) > 0.01f); updateRole("rightStrafe", isStrafing && (rightLateralSpeed > 0.0f)); updateRole("leftStrafe", isStrafing && (rightLateralSpeed < 0.0f)); updateRole("idle", !isMoving); // Must be last, as it makes isMoving bogus. diff --git a/libraries/audio-client/CMakeLists.txt b/libraries/audio-client/CMakeLists.txt index c313aecbc0..76f4cb4a0f 100644 --- a/libraries/audio-client/CMakeLists.txt +++ b/libraries/audio-client/CMakeLists.txt @@ -1,7 +1,5 @@ set(TARGET_NAME audio-client) -setup_memory_debugger() - # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library(Network Multimedia) diff --git a/libraries/audio/CMakeLists.txt b/libraries/audio/CMakeLists.txt index a0d40b1a10..c03f588d94 100644 --- a/libraries/audio/CMakeLists.txt +++ b/libraries/audio/CMakeLists.txt @@ -1,7 +1,5 @@ set(TARGET_NAME audio) -setup_memory_debugger() - # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library(Network) diff --git a/libraries/audio/src/AudioBuffer.h b/libraries/audio/src/AudioBuffer.h index d2f7c50c91..558d686861 100644 --- a/libraries/audio/src/AudioBuffer.h +++ b/libraries/audio/src/AudioBuffer.h @@ -77,10 +77,8 @@ AudioFrameBuffer< T >::~AudioFrameBuffer() { template< typename T > void AudioFrameBuffer< T >::allocateFrames() { _frameBuffer = new T*[_channelCountMax]; - if (_frameBuffer) { - for (uint32_t i = 0; i < _channelCountMax; ++i) { - _frameBuffer[i] = new T[_frameCountMax]; - } + for (uint32_t i = 0; i < _channelCountMax; ++i) { + _frameBuffer[i] = new T[_frameCountMax]; } } diff --git a/libraries/audio/src/AudioFilterBank.h b/libraries/audio/src/AudioFilterBank.h index a581b79b98..4ea5a3568a 100644 --- a/libraries/audio/src/AudioFilterBank.h +++ b/libraries/audio/src/AudioFilterBank.h @@ -108,14 +108,14 @@ public: void setParameters(uint32_t filterStage, uint32_t filterChannel, const float32_t sampleRate, const float32_t frequency, const float32_t gain, const float32_t slope) { - if (filterStage >= 0 && filterStage < _filterCount && filterChannel >= 0 && filterChannel < _channelCount) { + if (filterStage < _filterCount && filterChannel >= 0 && filterChannel < _channelCount) { _filters[filterStage][filterChannel].setParameters(sampleRate,frequency,gain,slope); } } void getParameters(uint32_t filterStage, uint32_t filterChannel, float32_t& sampleRate, float32_t& frequency, float32_t& gain, float32_t& slope) { - if (filterStage >= 0 && filterStage < _filterCount && filterChannel >= 0 && filterChannel < _channelCount) { + if (filterStage < _filterCount && filterChannel >= 0 && filterChannel < _channelCount) { _filters[filterStage][filterChannel].getParameters(sampleRate,frequency,gain,slope); } } diff --git a/libraries/audio/src/AudioRingBuffer.h b/libraries/audio/src/AudioRingBuffer.h index cc8743de16..e4391d6029 100644 --- a/libraries/audio/src/AudioRingBuffer.h +++ b/libraries/audio/src/AudioRingBuffer.h @@ -85,12 +85,12 @@ public: _bufferFirst(NULL), _bufferLast(NULL), _at(NULL) {} - ConstIterator(int16_t* bufferFirst, int capacity, int16_t* at) : _bufferLength(capacity), _bufferFirst(bufferFirst), _bufferLast(bufferFirst + capacity - 1), _at(at) {} + ConstIterator(const ConstIterator& rhs) = default; bool isNull() const { return _at == NULL; } diff --git a/libraries/auto-updater/CMakeLists.txt b/libraries/auto-updater/CMakeLists.txt index 6960d8368d..c3d6e74a6f 100644 --- a/libraries/auto-updater/CMakeLists.txt +++ b/libraries/auto-updater/CMakeLists.txt @@ -1,6 +1,4 @@ set(TARGET_NAME auto-updater) -setup_memory_debugger() - setup_hifi_library(Network) link_hifi_libraries(shared networking) diff --git a/libraries/avatars/CMakeLists.txt b/libraries/avatars/CMakeLists.txt index b05c667c71..acc939b25c 100644 --- a/libraries/avatars/CMakeLists.txt +++ b/libraries/avatars/CMakeLists.txt @@ -1,7 +1,5 @@ set(TARGET_NAME avatars) -setup_memory_debugger() - # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library(Network Script) diff --git a/libraries/display-plugins/CMakeLists.txt b/libraries/display-plugins/CMakeLists.txt index 79b41fa957..384fa57b62 100644 --- a/libraries/display-plugins/CMakeLists.txt +++ b/libraries/display-plugins/CMakeLists.txt @@ -1,7 +1,5 @@ set(TARGET_NAME display-plugins) -setup_memory_debugger() - # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library(OpenGL) diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp index da8deefa74..914d30d58a 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp @@ -7,21 +7,35 @@ // #include "Basic2DWindowOpenGLDisplayPlugin.h" +#include + +#include +#include +#include + #include -#include -#include const QString Basic2DWindowOpenGLDisplayPlugin::NAME("2D Display"); static const QString FULLSCREEN = "Fullscreen"; +static const QString FRAMERATE = DisplayPlugin::MENU_PATH() + ">Framerate"; +static const QString FRAMERATE_UNLIMITED = "Unlimited"; +static const QString FRAMERATE_60 = "60"; +static const QString FRAMERATE_50 = "50"; +static const QString FRAMERATE_40 = "40"; +static const QString FRAMERATE_30 = "30"; +static const QString VSYNC_ON = "V-Sync On"; const QString& Basic2DWindowOpenGLDisplayPlugin::getName() const { return NAME; } +std::vector _framerateActions; +QAction* _vsyncAction{ nullptr }; + void Basic2DWindowOpenGLDisplayPlugin::activate() { - CONTAINER->addMenu(MENU_PATH); - CONTAINER->addMenuItem(MENU_PATH, FULLSCREEN, + _framerateActions.clear(); + CONTAINER->addMenuItem(MENU_PATH(), FULLSCREEN, [this](bool clicked) { if (clicked) { CONTAINER->setFullscreen(getFullscreenTarget()); @@ -29,18 +43,65 @@ void Basic2DWindowOpenGLDisplayPlugin::activate() { CONTAINER->unsetFullscreen(); } }, true, false); + CONTAINER->addMenu(FRAMERATE); + _framerateActions.push_back( + CONTAINER->addMenuItem(FRAMERATE, FRAMERATE_UNLIMITED, + [this](bool) { updateFramerate(); }, true, true, FRAMERATE)); + _framerateActions.push_back( + CONTAINER->addMenuItem(FRAMERATE, FRAMERATE_60, + [this](bool) { updateFramerate(); }, true, false, FRAMERATE)); + _framerateActions.push_back( + CONTAINER->addMenuItem(FRAMERATE, FRAMERATE_50, + [this](bool) { updateFramerate(); }, true, false, FRAMERATE)); + _framerateActions.push_back( + CONTAINER->addMenuItem(FRAMERATE, FRAMERATE_40, + [this](bool) { updateFramerate(); }, true, false, FRAMERATE)); + _framerateActions.push_back( + CONTAINER->addMenuItem(FRAMERATE, FRAMERATE_30, + [this](bool) { updateFramerate(); }, true, false, FRAMERATE)); + WindowOpenGLDisplayPlugin::activate(); + + // Vsync detection happens in the parent class activate, so we need to check after that + if (_vsyncSupported) { + _vsyncAction = CONTAINER->addMenuItem(MENU_PATH(), VSYNC_ON, [this](bool) {}, true, true); + } else { + _vsyncAction = nullptr; + } + + updateFramerate(); } void Basic2DWindowOpenGLDisplayPlugin::deactivate() { WindowOpenGLDisplayPlugin::deactivate(); } -int Basic2DWindowOpenGLDisplayPlugin::getDesiredInterval(bool isThrottled) const { - static const int THROTTLED_PAINT_TIMER_DELAY = MSECS_PER_SECOND / 15; - static const int PAINT_TIMER_DELAY_MS = 1; +void Basic2DWindowOpenGLDisplayPlugin::display(GLuint sceneTexture, const glm::uvec2& sceneSize) { + if (_vsyncAction) { + bool wantVsync = _vsyncAction->isChecked(); + bool vsyncEnabed = isVsyncEnabled(); + if (vsyncEnabed ^ wantVsync) { + enableVsync(wantVsync); + } + } - return isThrottled ? THROTTLED_PAINT_TIMER_DELAY : PAINT_TIMER_DELAY_MS; + WindowOpenGLDisplayPlugin::display(sceneTexture, sceneSize); +} + + +int Basic2DWindowOpenGLDisplayPlugin::getDesiredInterval() const { + static const int THROTTLED_PAINT_TIMER_DELAY_MS = MSECS_PER_SECOND / 15; + static const int ULIMIITED_PAINT_TIMER_DELAY_MS = 1; + int result = ULIMIITED_PAINT_TIMER_DELAY_MS; + if (_isThrottled) { + result = THROTTLED_PAINT_TIMER_DELAY_MS; + } + if (0 != _framerateTarget) { + result = MSECS_PER_SECOND / _framerateTarget; + } + + qDebug() << "New interval " << result; + return result; } bool Basic2DWindowOpenGLDisplayPlugin::isThrottled() const { @@ -49,14 +110,42 @@ bool Basic2DWindowOpenGLDisplayPlugin::isThrottled() const { bool shouldThrottle = (!CONTAINER->isForeground() && CONTAINER->isOptionChecked(ThrottleFPSIfNotFocus)); if (_isThrottled != shouldThrottle) { - int desiredInterval = getDesiredInterval(shouldThrottle); - _timer.start(desiredInterval); _isThrottled = shouldThrottle; + _timer.start(getDesiredInterval()); } return shouldThrottle; } + +void Basic2DWindowOpenGLDisplayPlugin::updateFramerate() { + QAction* checkedFramerate{ nullptr }; + foreach(auto action, _framerateActions) { + if (action->isChecked()) { + checkedFramerate = action; + break; + } + } + + _framerateTarget = 0; + if (checkedFramerate) { + QString actionText = checkedFramerate->text(); + if (FRAMERATE_60 == actionText) { + _framerateTarget = 60; + } else if (FRAMERATE_50 == actionText) { + _framerateTarget = 50; + } else if (FRAMERATE_40 == actionText) { + _framerateTarget = 40; + } else if (FRAMERATE_30 == actionText) { + _framerateTarget = 30; + } + } + + int newInterval = getDesiredInterval(); + qDebug() << newInterval; + _timer.start(getDesiredInterval()); +} + // FIXME target the screen the window is currently on QScreen* Basic2DWindowOpenGLDisplayPlugin::getFullscreenTarget() { return qApp->primaryScreen(); diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h index 64edfe3600..f4655ab79f 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h @@ -14,19 +14,23 @@ class Basic2DWindowOpenGLDisplayPlugin : public WindowOpenGLDisplayPlugin { Q_OBJECT public: + virtual const QString & getName() const override; + virtual void activate() override; virtual void deactivate() override; - virtual const QString & getName() const override; + virtual void display(GLuint sceneTexture, const glm::uvec2& sceneSize) override; virtual bool isThrottled() const override; protected: - int getDesiredInterval(bool isThrottled) const; + int getDesiredInterval() const; mutable bool _isThrottled = false; private: + void updateFramerate(); static const QString NAME; QScreen* getFullscreenTarget(); + uint32_t _framerateTarget{ 0 }; int _fullscreenTarget{ -1 }; }; diff --git a/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp index 5840b3cbba..598e78e500 100644 --- a/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp @@ -18,7 +18,10 @@ #include "oculus/OculusDisplayPlugin.h" #include "oculus/OculusLegacyDisplayPlugin.h" -const QString DisplayPlugin::MENU_PATH{ "Display" }; +const QString& DisplayPlugin::MENU_PATH() { + static const QString value = "Display"; + return value; +} // TODO migrate to a DLL model where plugins are discovered and loaded at runtime by the PluginManager class DisplayPluginList getDisplayPlugins() { diff --git a/libraries/display-plugins/src/display-plugins/DisplayPlugin.h b/libraries/display-plugins/src/display-plugins/DisplayPlugin.h index 0696cc6229..a9220d68f6 100644 --- a/libraries/display-plugins/src/display-plugins/DisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/DisplayPlugin.h @@ -120,7 +120,7 @@ public: virtual void resetSensors() {} virtual float devicePixelRatio() { return 1.0; } - static const QString MENU_PATH; + static const QString& MENU_PATH(); signals: void recommendedFramebufferSizeChanged(const QSize & size); void requestRender(); diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 0409899739..eb38e1bf4f 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -38,6 +38,11 @@ void OpenGLDisplayPlugin::finishFrame() { void OpenGLDisplayPlugin::customizeContext() { using namespace oglplus; + // TODO: write the poper code for linux +#if defined(Q_OS_WIN) + _vsyncSupported = wglewGetExtension("WGL_EXT_swap_control"); +#endif + Context::BlendFunc(BlendFunction::SrcAlpha, BlendFunction::OneMinusSrcAlpha); Context::Disable(Capability::Blend); Context::Disable(Capability::DepthTest); @@ -46,6 +51,8 @@ void OpenGLDisplayPlugin::customizeContext() { _program = loadDefaultShader(); _plane = loadPlane(_program); + + enableVsync(); } void OpenGLDisplayPlugin::activate() { @@ -114,4 +121,24 @@ void OpenGLDisplayPlugin::display( void OpenGLDisplayPlugin::drawUnitQuad() { _program->Bind(); _plane->Draw(); +} + +void OpenGLDisplayPlugin::enableVsync(bool enable) { + if (!_vsyncSupported) { + return; + } +#ifdef Q_OS_WIN + wglSwapIntervalEXT(enable ? 1 : 0); +#endif +} + +bool OpenGLDisplayPlugin::isVsyncEnabled() { + if (!_vsyncSupported) { + return true; + } +#ifdef Q_OS_WIN + return wglGetSwapIntervalEXT() != 0; +#else + return true; +#endif } \ No newline at end of file diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index 3152500232..0dc94b72f5 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -38,9 +38,13 @@ protected: virtual void doneCurrent() = 0; virtual void swapBuffers() = 0; + virtual bool isVsyncEnabled(); + virtual void enableVsync(bool enable = true); + mutable QTimer _timer; ProgramPtr _program; ShapeWrapperPtr _plane; + bool _vsyncSupported{ false }; }; diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp index df691f06f3..017977bf69 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp @@ -69,7 +69,7 @@ void StereoDisplayPlugin::activate() { if (screen == qApp->primaryScreen()) { checked = true; } - auto action = CONTAINER->addMenuItem(MENU_PATH, name, + auto action = CONTAINER->addMenuItem(MENU_PATH(), name, [this](bool clicked) { updateScreen(); }, true, checked, "Screens"); _screenActions[i] = action; } diff --git a/libraries/embedded-webserver/CMakeLists.txt b/libraries/embedded-webserver/CMakeLists.txt index 2d8915998b..45d9827d42 100644 --- a/libraries/embedded-webserver/CMakeLists.txt +++ b/libraries/embedded-webserver/CMakeLists.txt @@ -1,6 +1,4 @@ set(TARGET_NAME embedded-webserver) -setup_memory_debugger() - # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library(Network) diff --git a/libraries/embedded-webserver/src/HTTPSManager.cpp b/libraries/embedded-webserver/src/HTTPSManager.cpp index 94e1a35e20..a745d7605e 100644 --- a/libraries/embedded-webserver/src/HTTPSManager.cpp +++ b/libraries/embedded-webserver/src/HTTPSManager.cpp @@ -30,6 +30,7 @@ void HTTPSManager::incomingConnection(qintptr socketDescriptor) { sslSocket->setLocalCertificate(_certificate); sslSocket->setPrivateKey(_privateKey); + sslSocket->setPeerVerifyMode(QSslSocket::VerifyNone); if (sslSocket->setSocketDescriptor(socketDescriptor)) { new HTTPSConnection(sslSocket, this); @@ -48,4 +49,4 @@ bool HTTPSManager::handleHTTPSRequest(HTTPSConnection* connection, const QUrl& u bool HTTPSManager::requestHandledByRequestHandler(HTTPConnection* connection, const QUrl& url) { return _sslRequestHandler && _sslRequestHandler->handleHTTPSRequest(reinterpret_cast(connection), url); -} \ No newline at end of file +} diff --git a/libraries/entities-renderer/CMakeLists.txt b/libraries/entities-renderer/CMakeLists.txt index 3387715348..c4dddb8971 100644 --- a/libraries/entities-renderer/CMakeLists.txt +++ b/libraries/entities-renderer/CMakeLists.txt @@ -26,6 +26,4 @@ find_package(PolyVox REQUIRED) target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${POLYVOX_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${POLYVOX_LIBRARIES}) -setup_memory_debugger() - link_hifi_libraries(shared gpu script-engine render render-utils) diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index d9d4ec4a5b..f683083ed1 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -247,7 +247,6 @@ void RenderableParticleEffectEntityItem::updateRenderItem() { glm::vec3 pos = _transform.getTranslation(); Transform t; t.setRotation(rot); - t.setTranslation(pos); payload.setModelTransform(t); // transform _particleMinBound and _particleMaxBound corners into world coords @@ -285,7 +284,7 @@ void RenderableParticleEffectEntityItem::updateRenderItem() { payload.setPipeline(_untexturedPipeline); } }); - + _scene->enqueuePendingChanges(pendingChanges); } @@ -295,7 +294,7 @@ void RenderableParticleEffectEntityItem::createPipelines() { state->setCullMode(gpu::State::CULL_BACK); state->setDepthTest(true, true, gpu::LESS_EQUAL); state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, - gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, + gpu::State::ONE, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); auto vertShader = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(untextured_particle_vert))); auto fragShader = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(untextured_particle_frag))); diff --git a/libraries/entities/CMakeLists.txt b/libraries/entities/CMakeLists.txt index 368257661e..f7936ff125 100644 --- a/libraries/entities/CMakeLists.txt +++ b/libraries/entities/CMakeLists.txt @@ -1,7 +1,5 @@ set(TARGET_NAME entities) -setup_memory_debugger() - # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library(Network Script) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 0ffcc00ead..9fa6ccac65 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -610,6 +610,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef auto nodeList = DependencyManager::get(); const QUuid& myNodeID = nodeList->getSessionUUID(); bool weOwnSimulation = _simulationOwner.matchesValidID(myNodeID); + if (args.bitstreamVersion >= VERSION_ENTITIES_HAVE_SIMULATION_OWNER_AND_ACTIONS_OVER_WIRE) { // pack SimulationOwner and terse update properties near each other diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 0969f9a291..5550f10a4a 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -84,9 +84,10 @@ CONSTRUCT_PROPERTY(shapeType, SHAPE_TYPE_NONE), CONSTRUCT_PROPERTY(maxParticles, ParticleEffectEntityItem::DEFAULT_MAX_PARTICLES), CONSTRUCT_PROPERTY(lifespan, ParticleEffectEntityItem::DEFAULT_LIFESPAN), CONSTRUCT_PROPERTY(emitRate, ParticleEffectEntityItem::DEFAULT_EMIT_RATE), -CONSTRUCT_PROPERTY(emitDirection, ParticleEffectEntityItem::DEFAULT_EMIT_DIRECTION), -CONSTRUCT_PROPERTY(emitStrength, ParticleEffectEntityItem::DEFAULT_EMIT_STRENGTH), -CONSTRUCT_PROPERTY(localGravity, ParticleEffectEntityItem::DEFAULT_LOCAL_GRAVITY), +CONSTRUCT_PROPERTY(emitVelocity, ParticleEffectEntityItem::DEFAULT_EMIT_VELOCITY), +CONSTRUCT_PROPERTY(velocitySpread, ParticleEffectEntityItem::DEFAULT_VELOCITY_SPREAD), +CONSTRUCT_PROPERTY(emitAcceleration, ParticleEffectEntityItem::DEFAULT_EMIT_ACCELERATION), +CONSTRUCT_PROPERTY(accelerationSpread, ParticleEffectEntityItem::DEFAULT_ACCELERATION_SPREAD), CONSTRUCT_PROPERTY(particleRadius, ParticleEffectEntityItem::DEFAULT_PARTICLE_RADIUS), CONSTRUCT_PROPERTY(marketplaceID, ENTITY_ITEM_DEFAULT_MARKETPLACE_ID), CONSTRUCT_PROPERTY(keyLightColor, ZoneEntityItem::DEFAULT_KEYLIGHT_COLOR), @@ -349,9 +350,10 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_MAX_PARTICLES, maxParticles); CHECK_PROPERTY_CHANGE(PROP_LIFESPAN, lifespan); CHECK_PROPERTY_CHANGE(PROP_EMIT_RATE, emitRate); - CHECK_PROPERTY_CHANGE(PROP_EMIT_DIRECTION, emitDirection); - CHECK_PROPERTY_CHANGE(PROP_EMIT_STRENGTH, emitStrength); - CHECK_PROPERTY_CHANGE(PROP_LOCAL_GRAVITY, localGravity); + CHECK_PROPERTY_CHANGE(PROP_EMIT_VELOCITY, emitVelocity); + CHECK_PROPERTY_CHANGE(PROP_VELOCITY_SPREAD, velocitySpread); + CHECK_PROPERTY_CHANGE(PROP_EMIT_ACCELERATION, emitAcceleration); + CHECK_PROPERTY_CHANGE(PROP_ACCELERATION_SPREAD, accelerationSpread); CHECK_PROPERTY_CHANGE(PROP_PARTICLE_RADIUS, particleRadius); CHECK_PROPERTY_CHANGE(PROP_MARKETPLACE_ID, marketplaceID); CHECK_PROPERTY_CHANGE(PROP_NAME, name); @@ -451,9 +453,10 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(maxParticles); COPY_PROPERTY_TO_QSCRIPTVALUE(lifespan); COPY_PROPERTY_TO_QSCRIPTVALUE(emitRate); - COPY_PROPERTY_TO_QSCRIPTVALUE(emitDirection); - COPY_PROPERTY_TO_QSCRIPTVALUE(emitStrength); - COPY_PROPERTY_TO_QSCRIPTVALUE(localGravity); + COPY_PROPERTY_TO_QSCRIPTVALUE(emitVelocity); + COPY_PROPERTY_TO_QSCRIPTVALUE(velocitySpread); + COPY_PROPERTY_TO_QSCRIPTVALUE(emitAcceleration); + COPY_PROPERTY_TO_QSCRIPTVALUE(accelerationSpread); COPY_PROPERTY_TO_QSCRIPTVALUE(particleRadius); COPY_PROPERTY_TO_QSCRIPTVALUE(marketplaceID); COPY_PROPERTY_TO_QSCRIPTVALUE(name); @@ -571,9 +574,10 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(maxParticles, float, setMaxParticles); COPY_PROPERTY_FROM_QSCRIPTVALUE(lifespan, float, setLifespan); COPY_PROPERTY_FROM_QSCRIPTVALUE(emitRate, float, setEmitRate); - COPY_PROPERTY_FROM_QSCRIPTVALUE(emitDirection, glmVec3, setEmitDirection); - COPY_PROPERTY_FROM_QSCRIPTVALUE(emitStrength, float, setEmitStrength); - COPY_PROPERTY_FROM_QSCRIPTVALUE(localGravity, float, setLocalGravity); + COPY_PROPERTY_FROM_QSCRIPTVALUE(emitVelocity, glmVec3, setEmitVelocity); + COPY_PROPERTY_FROM_QSCRIPTVALUE(velocitySpread, glmVec3, setVelocitySpread); + COPY_PROPERTY_FROM_QSCRIPTVALUE(emitAcceleration, glmVec3, setEmitAcceleration); + COPY_PROPERTY_FROM_QSCRIPTVALUE(accelerationSpread, glmVec3, setAccelerationSpread); COPY_PROPERTY_FROM_QSCRIPTVALUE(particleRadius, float, setParticleRadius); COPY_PROPERTY_FROM_QSCRIPTVALUE(marketplaceID, QString, setMarketplaceID); COPY_PROPERTY_FROM_QSCRIPTVALUE(name, QString, setName); @@ -812,10 +816,12 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem APPEND_ENTITY_PROPERTY(PROP_MAX_PARTICLES, properties.getMaxParticles()); APPEND_ENTITY_PROPERTY(PROP_LIFESPAN, properties.getLifespan()); APPEND_ENTITY_PROPERTY(PROP_EMIT_RATE, properties.getEmitRate()); - APPEND_ENTITY_PROPERTY(PROP_EMIT_DIRECTION, properties.getEmitDirection()); - APPEND_ENTITY_PROPERTY(PROP_EMIT_STRENGTH, properties.getEmitStrength()); - APPEND_ENTITY_PROPERTY(PROP_LOCAL_GRAVITY, properties.getLocalGravity()); + APPEND_ENTITY_PROPERTY(PROP_EMIT_VELOCITY, properties.getEmitVelocity()); + APPEND_ENTITY_PROPERTY(PROP_VELOCITY_SPREAD, properties.getVelocitySpread()); + APPEND_ENTITY_PROPERTY(PROP_EMIT_ACCELERATION, properties.getEmitAcceleration()); + APPEND_ENTITY_PROPERTY(PROP_ACCELERATION_SPREAD, properties.getAccelerationSpread()); APPEND_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, properties.getParticleRadius()); + } if (properties.getType() == EntityTypes::Zone) { @@ -1080,9 +1086,10 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MAX_PARTICLES, float, setMaxParticles); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LIFESPAN, float, setLifespan); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_RATE, float, setEmitRate); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_DIRECTION, glm::vec3, setEmitDirection); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_STRENGTH, float, setEmitStrength); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LOCAL_GRAVITY, float, setLocalGravity); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_VELOCITY, glm::vec3, setEmitVelocity); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VELOCITY_SPREAD, glm::vec3, setVelocitySpread); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_ACCELERATION, glm::vec3, setEmitAcceleration); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ACCELERATION_SPREAD, glm::vec3, setAccelerationSpread); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PARTICLE_RADIUS, float, setParticleRadius); } @@ -1211,9 +1218,10 @@ void EntityItemProperties::markAllChanged() { _maxParticlesChanged = true; _lifespanChanged = true; _emitRateChanged = true; - _emitDirectionChanged = true; - _emitStrengthChanged = true; - _localGravityChanged = true; + _emitVelocityChanged = true; + _velocitySpreadChanged = true; + _emitAccelerationChanged = true; + _accelerationSpreadChanged = true; _particleRadiusChanged = true; _marketplaceIDChanged = true; diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index a3c7b0c760..ffc409e933 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -131,9 +131,10 @@ public: DEFINE_PROPERTY(PROP_MAX_PARTICLES, MaxParticles, maxParticles, quint32); DEFINE_PROPERTY(PROP_LIFESPAN, Lifespan, lifespan, float); DEFINE_PROPERTY(PROP_EMIT_RATE, EmitRate, emitRate, float); - DEFINE_PROPERTY_REF(PROP_EMIT_DIRECTION, EmitDirection, emitDirection, glm::vec3); - DEFINE_PROPERTY(PROP_EMIT_STRENGTH, EmitStrength, emitStrength, float); - DEFINE_PROPERTY(PROP_LOCAL_GRAVITY, LocalGravity, localGravity, float); + DEFINE_PROPERTY_REF(PROP_EMIT_VELOCITY, EmitVelocity, emitVelocity, glm::vec3); + DEFINE_PROPERTY_REF(PROP_VELOCITY_SPREAD, VelocitySpread, velocitySpread, glm::vec3); + DEFINE_PROPERTY(PROP_EMIT_ACCELERATION, EmitAcceleration, emitAcceleration, glm::vec3); + DEFINE_PROPERTY(PROP_ACCELERATION_SPREAD, AccelerationSpread, accelerationSpread, glm::vec3); DEFINE_PROPERTY(PROP_PARTICLE_RADIUS, ParticleRadius, particleRadius, float); DEFINE_PROPERTY_REF(PROP_MARKETPLACE_ID, MarketplaceID, marketplaceID, QString); DEFINE_PROPERTY_REF(PROP_KEYLIGHT_COLOR, KeyLightColor, keyLightColor, xColor); @@ -311,9 +312,9 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { DEBUG_PROPERTY_IF_CHANGED(debug, properties, MaxParticles, maxParticles, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Lifespan, lifespan, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, EmitRate, emitRate, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, EmitDirection, emitDirection, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, EmitStrength, emitStrength, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, LocalGravity, localGravity, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, EmitVelocity, emitVelocity, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, EmitAcceleration, emitAcceleration, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, AccelerationSpread, accelerationSpread, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, ParticleRadius, particleRadius, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, MarketplaceID, marketplaceID, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, BackgroundMode, backgroundMode, ""); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index e439710695..abb8241d8f 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -96,9 +96,9 @@ enum EntityPropertyList { PROP_MAX_PARTICLES, PROP_LIFESPAN, PROP_EMIT_RATE, - PROP_EMIT_DIRECTION, + PROP_EMIT_VELOCITY, PROP_EMIT_STRENGTH, - PROP_LOCAL_GRAVITY, + PROP_EMIT_ACCELERATION, PROP_PARTICLE_RADIUS, PROP_COMPOUND_SHAPE_URL, // used by Model + zones entities @@ -134,7 +134,10 @@ enum EntityPropertyList { // Used by PolyLine entity PROP_NORMALS, PROP_STROKE_WIDTHS, - + + // used by particles + PROP_VELOCITY_SPREAD, + PROP_ACCELERATION_SPREAD, //////////////////////////////////////////////////////////////////////////////////////////////////// // ATTENTION: add new properties to end of list just ABOVE this line @@ -168,9 +171,9 @@ enum EntityPropertyList { PROP_ATMOSPHERE_CENTER = PROP_MAX_PARTICLES, PROP_ATMOSPHERE_INNER_RADIUS = PROP_LIFESPAN, PROP_ATMOSPHERE_OUTER_RADIUS = PROP_EMIT_RATE, - PROP_ATMOSPHERE_MIE_SCATTERING = PROP_EMIT_DIRECTION, + PROP_ATMOSPHERE_MIE_SCATTERING = PROP_EMIT_VELOCITY, PROP_ATMOSPHERE_RAYLEIGH_SCATTERING = PROP_EMIT_STRENGTH, - PROP_ATMOSPHERE_SCATTERING_WAVELENGTHS = PROP_LOCAL_GRAVITY, + PROP_ATMOSPHERE_SCATTERING_WAVELENGTHS = PROP_EMIT_ACCELERATION, PROP_ATMOSPHERE_HAS_STARS = PROP_PARTICLE_RADIUS, PROP_BACKGROUND_MODE = PROP_MODEL_URL, PROP_SKYBOX_COLOR = PROP_ANIMATION_URL, diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index 349e0c4d46..9e45efe88d 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -16,7 +16,6 @@ // - Just to get this out the door, I just did forward Euler integration. There are better ways. // - Gravity always points along the Y axis. Support an actual gravity vector. // - Add the ability to add arbitrary forces to the simulation. -// - Add controls for spread (which is currently hard-coded) and varying emission strength (not currently implemented). // - Add drag. // - Add some kind of support for collisions. // - There's no synchronization of the simulation across clients at all. In fact, it's using rand() under the hood, so @@ -50,9 +49,10 @@ const float ParticleEffectEntityItem::DEFAULT_ANIMATION_FPS = 30.0f; const quint32 ParticleEffectEntityItem::DEFAULT_MAX_PARTICLES = 1000; const float ParticleEffectEntityItem::DEFAULT_LIFESPAN = 3.0f; const float ParticleEffectEntityItem::DEFAULT_EMIT_RATE = 15.0f; -const glm::vec3 ParticleEffectEntityItem::DEFAULT_EMIT_DIRECTION(0.0f, 1.0f, 0.0f); -const float ParticleEffectEntityItem::DEFAULT_EMIT_STRENGTH = 25.0f; -const float ParticleEffectEntityItem::DEFAULT_LOCAL_GRAVITY = -9.8f; +const glm::vec3 ParticleEffectEntityItem::DEFAULT_EMIT_VELOCITY(0.0f, 5.0f, 0.0f); +const glm::vec3 ParticleEffectEntityItem::DEFAULT_VELOCITY_SPREAD(3.0f, 0.0f, 3.0f); +const glm::vec3 ParticleEffectEntityItem::DEFAULT_EMIT_ACCELERATION(0.0f, -9.8f, 0.0f); +const glm::vec3 ParticleEffectEntityItem::DEFAULT_ACCELERATION_SPREAD(0.0f, 0.0f, 0.0f); const float ParticleEffectEntityItem::DEFAULT_PARTICLE_RADIUS = 0.025f; const QString ParticleEffectEntityItem::DEFAULT_TEXTURES = ""; @@ -67,9 +67,10 @@ ParticleEffectEntityItem::ParticleEffectEntityItem(const EntityItemID& entityIte _maxParticles(DEFAULT_MAX_PARTICLES), _lifespan(DEFAULT_LIFESPAN), _emitRate(DEFAULT_EMIT_RATE), - _emitDirection(DEFAULT_EMIT_DIRECTION), - _emitStrength(DEFAULT_EMIT_STRENGTH), - _localGravity(DEFAULT_LOCAL_GRAVITY), + _emitVelocity(DEFAULT_EMIT_VELOCITY), + _velocitySpread(DEFAULT_VELOCITY_SPREAD), + _emitAcceleration(DEFAULT_EMIT_ACCELERATION), + _accelerationSpread(DEFAULT_ACCELERATION_SPREAD), _particleRadius(DEFAULT_PARTICLE_RADIUS), _lastAnimated(usecTimestampNow()), _animationLoop(), @@ -80,6 +81,7 @@ ParticleEffectEntityItem::ParticleEffectEntityItem(const EntityItemID& entityIte _particleLifetimes(DEFAULT_MAX_PARTICLES, 0.0f), _particlePositions(DEFAULT_MAX_PARTICLES, glm::vec3(0.0f, 0.0f, 0.0f)), _particleVelocities(DEFAULT_MAX_PARTICLES, glm::vec3(0.0f, 0.0f, 0.0f)), + _particleAccelerations(DEFAULT_MAX_PARTICLES, glm::vec3(0.0f, 0.0f, 0.0f)), _timeUntilNextEmit(0.0f), _particleHeadIndex(0), _particleTailIndex(0), @@ -94,75 +96,59 @@ ParticleEffectEntityItem::ParticleEffectEntityItem(const EntityItemID& entityIte ParticleEffectEntityItem::~ParticleEffectEntityItem() { } -void ParticleEffectEntityItem::setDimensions(const glm::vec3& value) { - computeAndUpdateDimensions(); -} void ParticleEffectEntityItem::setLifespan(float lifespan) { _lifespan = lifespan; +} + +void ParticleEffectEntityItem::setEmitVelocity(const glm::vec3& emitVelocity) { + _emitVelocity = emitVelocity; computeAndUpdateDimensions(); } -void ParticleEffectEntityItem::setEmitDirection(glm::vec3 emitDirection) { - _emitDirection = glm::normalize(emitDirection); +void ParticleEffectEntityItem::setVelocitySpread(const glm::vec3& velocitySpread) { + _velocitySpread = velocitySpread; computeAndUpdateDimensions(); } -void ParticleEffectEntityItem::setEmitStrength(float emitStrength) { - _emitStrength = emitStrength; + +void ParticleEffectEntityItem::setEmitAcceleration(const glm::vec3& emitAcceleration) { + _emitAcceleration = emitAcceleration; computeAndUpdateDimensions(); } -void ParticleEffectEntityItem::setLocalGravity(float localGravity) { - _localGravity = localGravity; +void ParticleEffectEntityItem::setAccelerationSpread(const glm::vec3& accelerationSpread){ + _accelerationSpread = accelerationSpread; computeAndUpdateDimensions(); } void ParticleEffectEntityItem::setParticleRadius(float particleRadius) { _particleRadius = particleRadius; - computeAndUpdateDimensions(); } void ParticleEffectEntityItem::computeAndUpdateDimensions() { - - const float t = _lifespan * 1.1f; // add 10% extra time, to account for incremental timer accumulation error. - const float MAX_RANDOM_FACTOR = (0.5f * 0.25f); - const float maxOffset = (MAX_RANDOM_FACTOR * _emitStrength) + _particleRadius; - - // bounds for x and z is easy to compute because there is no at^2 term. - float xMax = (_emitDirection.x * _emitStrength + maxOffset) * t; - float xMin = (_emitDirection.x * _emitStrength - maxOffset) * t; - - float zMax = (_emitDirection.z * _emitStrength + maxOffset) * t; - float zMin = (_emitDirection.z * _emitStrength - maxOffset) * t; - - // yEnd is where the particle will end. - float a = _localGravity; - float atSquared = a * t * t; - float v = _emitDirection.y * _emitStrength + maxOffset; - float vt = v * t; - float yEnd = 0.5f * atSquared + vt; - - // yApex is where the particle is at it's apex. - float yApexT = (-v / a); - float yApex = 0.0f; - - // only set apex if it's within the lifespan of the particle. - if (yApexT >= 0.0f && yApexT <= t) { - yApex = -(v * v) / (2.0f * a); - } - - float yMax = std::max(yApex, yEnd); - float yMin = std::min(yApex, yEnd); - - // times 2 because dimensions are diameters not radii. - glm::vec3 dims(2.0f * std::max(fabsf(xMin), fabsf(xMax)), - 2.0f * std::max(fabsf(yMin), fabsf(yMax)), - 2.0f * std::max(fabsf(zMin), fabsf(zMax))); - + const float time = _lifespan * 1.1f; // add 10% extra time to account for incremental timer accumulation error + + float maxVelocityX = fabsf(_velocity.x) + _velocitySpread.x; + float maxAccelerationX = fabsf(_acceleration.x) + _accelerationSpread.x; + float maxXDistance = (maxVelocityX * time) + (0.5 * maxAccelerationX * time * time); + + float maxVelocityY = fabs(_velocity.y) + _velocitySpread.y; + float maxAccelerationY = fabsf(_acceleration.y) + _accelerationSpread.y; + float maxYDistance = (maxVelocityY * time) + (0.5 * maxAccelerationY * time * time); + + float maxVelocityZ = fabsf(_velocity.z) + _velocitySpread.z; + float maxAccelerationZ = fabsf(_acceleration.z) + _accelerationSpread.z; + float maxZDistance = (maxVelocityZ * time) + (0.5 * maxAccelerationZ * time * time); + + float maxDistance = std::max(maxXDistance, std::max(maxYDistance, maxZDistance)); + + //times 2 because dimensions are diameters not radii + glm::vec3 dims(2.0 * maxDistance); EntityItem::setDimensions(dims); } + EntityItemProperties ParticleEffectEntityItem::getProperties() const { EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class @@ -176,9 +162,9 @@ EntityItemProperties ParticleEffectEntityItem::getProperties() const { COPY_ENTITY_PROPERTY_TO_PROPERTIES(maxParticles, getMaxParticles); COPY_ENTITY_PROPERTY_TO_PROPERTIES(lifespan, getLifespan); COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitRate, getEmitRate); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitDirection, getEmitDirection); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitStrength, getEmitStrength); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(localGravity, getLocalGravity); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitVelocity, getEmitVelocity); + + COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitAcceleration, getEmitAcceleration); COPY_ENTITY_PROPERTY_TO_PROPERTIES(particleRadius, getParticleRadius); COPY_ENTITY_PROPERTY_TO_PROPERTIES(textures, getTextures); @@ -198,11 +184,12 @@ bool ParticleEffectEntityItem::setProperties(const EntityItemProperties& propert SET_ENTITY_PROPERTY_FROM_PROPERTIES(maxParticles, setMaxParticles); SET_ENTITY_PROPERTY_FROM_PROPERTIES(lifespan, setLifespan); SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitRate, setEmitRate); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitDirection, setEmitDirection); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitStrength, setEmitStrength); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(localGravity, setLocalGravity); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitVelocity, setEmitVelocity); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitAcceleration, setEmitAcceleration); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(accelerationSpread, setAccelerationSpread); SET_ENTITY_PROPERTY_FROM_PROPERTIES(particleRadius, setParticleRadius); SET_ENTITY_PROPERTY_FROM_PROPERTIES(textures, setTextures); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(velocitySpread, setVelocitySpread); if (somethingChanged) { bool wantDebug = false; @@ -247,17 +234,28 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch if (propertyFlags.getHasProperty(PROP_ANIMATION_FRAME_INDEX)) { setAnimationFrameIndex(animationFrameIndex); } - READ_ENTITY_PROPERTY(PROP_ANIMATION_SETTINGS, QString, setAnimationSettings); READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, updateShapeType); READ_ENTITY_PROPERTY(PROP_MAX_PARTICLES, quint32, setMaxParticles); READ_ENTITY_PROPERTY(PROP_LIFESPAN, float, setLifespan); READ_ENTITY_PROPERTY(PROP_EMIT_RATE, float, setEmitRate); - READ_ENTITY_PROPERTY(PROP_EMIT_DIRECTION, glm::vec3, setEmitDirection); - READ_ENTITY_PROPERTY(PROP_EMIT_STRENGTH, float, setEmitStrength); - READ_ENTITY_PROPERTY(PROP_LOCAL_GRAVITY, float, setLocalGravity); - READ_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, float, setParticleRadius); - READ_ENTITY_PROPERTY(PROP_TEXTURES, QString, setTextures); + READ_ENTITY_PROPERTY(PROP_EMIT_VELOCITY, glm::vec3, setEmitVelocity); + + if (args.bitstreamVersion >= VERSION_ENTITIES_PARTICLE_MODIFICATIONS) { + READ_ENTITY_PROPERTY(PROP_EMIT_ACCELERATION, glm::vec3, setEmitAcceleration); + READ_ENTITY_PROPERTY(PROP_ACCELERATION_SPREAD, glm::vec3, setAccelerationSpread); + READ_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, float, setParticleRadius); + READ_ENTITY_PROPERTY(PROP_TEXTURES, QString, setTextures); + READ_ENTITY_PROPERTY(PROP_VELOCITY_SPREAD, glm::vec3, setVelocitySpread); + } else { + // EMIT_STRENGTH FAKEOUT + READ_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, float, setParticleRadius); + // LOCAL_GRAVITY FAKEOUT + READ_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, float, setParticleRadius); + // ACTUALLY PARTICLE RADIUS + READ_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, float, setParticleRadius); + READ_ENTITY_PROPERTY(PROP_TEXTURES, QString, setTextures); + } return bytesRead; } @@ -276,11 +274,12 @@ EntityPropertyFlags ParticleEffectEntityItem::getEntityProperties(EncodeBitstrea requestedProperties += PROP_MAX_PARTICLES; requestedProperties += PROP_LIFESPAN; requestedProperties += PROP_EMIT_RATE; - requestedProperties += PROP_EMIT_DIRECTION; - requestedProperties += PROP_EMIT_STRENGTH; - requestedProperties += PROP_LOCAL_GRAVITY; + requestedProperties += PROP_EMIT_VELOCITY; + requestedProperties += PROP_EMIT_ACCELERATION; + requestedProperties += PROP_ACCELERATION_SPREAD; requestedProperties += PROP_PARTICLE_RADIUS; requestedProperties += PROP_TEXTURES; + requestedProperties += PROP_VELOCITY_SPREAD; return requestedProperties; } @@ -303,11 +302,12 @@ void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData, APPEND_ENTITY_PROPERTY(PROP_MAX_PARTICLES, getMaxParticles()); APPEND_ENTITY_PROPERTY(PROP_LIFESPAN, getLifespan()); APPEND_ENTITY_PROPERTY(PROP_EMIT_RATE, getEmitRate()); - APPEND_ENTITY_PROPERTY(PROP_EMIT_DIRECTION, getEmitDirection()); - APPEND_ENTITY_PROPERTY(PROP_EMIT_STRENGTH, getEmitStrength()); - APPEND_ENTITY_PROPERTY(PROP_LOCAL_GRAVITY, getLocalGravity()); + APPEND_ENTITY_PROPERTY(PROP_EMIT_VELOCITY, getEmitVelocity()); + APPEND_ENTITY_PROPERTY(PROP_EMIT_ACCELERATION, getEmitAcceleration()); + APPEND_ENTITY_PROPERTY(PROP_ACCELERATION_SPREAD, getAccelerationSpread()); APPEND_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, getParticleRadius()); APPEND_ENTITY_PROPERTY(PROP_TEXTURES, getTextures()); + APPEND_ENTITY_PROPERTY(PROP_VELOCITY_SPREAD, getVelocitySpread()); } bool ParticleEffectEntityItem::isAnimatingSomething() const { @@ -487,8 +487,9 @@ void ParticleEffectEntityItem::extendBounds(const glm::vec3& point) { } void ParticleEffectEntityItem::integrateParticle(quint32 index, float deltaTime) { - glm::vec3 atSquared(0.0f, 0.5f * _localGravity * deltaTime * deltaTime, 0.0f); - glm::vec3 at(0.0f, _localGravity * deltaTime, 0.0f); + glm::vec3 accel = _particleAccelerations[index]; + glm::vec3 atSquared = (0.5f * deltaTime * deltaTime) * accel; + glm::vec3 at = accel * deltaTime; _particlePositions[index] += _particleVelocities[index] * deltaTime + atSquared; _particleVelocities[index] += at; } @@ -526,15 +527,22 @@ void ParticleEffectEntityItem::stepSimulation(float deltaTime) { quint32 i = _particleTailIndex; _particleLifetimes[i] = _lifespan; - // jitter the _emitDirection by a random offset - glm::vec3 randOffset; - randOffset.x = (randFloat() - 0.5f) * 0.25f * _emitStrength; - randOffset.y = (randFloat() - 0.5f) * 0.25f * _emitStrength; - randOffset.z = (randFloat() - 0.5f) * 0.25f * _emitStrength; + + glm::vec3 spreadOffset; + spreadOffset.x = -_velocitySpread.x + randFloat() * (_velocitySpread.x * 2.0f); + spreadOffset.y = -_velocitySpread.y + randFloat() * (_velocitySpread.y * 2.0f); + spreadOffset.z = -_velocitySpread.z + randFloat() * (_velocitySpread.z * 2.0f); + // set initial conditions - _particlePositions[i] = glm::vec3(0.0f, 0.0f, 0.0f); - _particleVelocities[i] = _emitDirection * _emitStrength + randOffset; + _particlePositions[i] = getPosition(); + _particleVelocities[i] = _emitVelocity + spreadOffset; + + spreadOffset.x = -_accelerationSpread.x + randFloat() * (_accelerationSpread.x * 2.0f); + spreadOffset.y = -_accelerationSpread.y + randFloat() * (_accelerationSpread.y * 2.0f); + spreadOffset.z = -_accelerationSpread.z + randFloat() * (_accelerationSpread.z * 2.0f); + + _particleAccelerations[i] = _emitAcceleration + spreadOffset; integrateParticle(i, timeLeftInFrame); extendBounds(_particlePositions[i]); diff --git a/libraries/entities/src/ParticleEffectEntityItem.h b/libraries/entities/src/ParticleEffectEntityItem.h index 994c609f0f..4ed9216e85 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.h +++ b/libraries/entities/src/ParticleEffectEntityItem.h @@ -86,8 +86,6 @@ public: void setAnimationLastFrame(float lastFrame) { _animationLoop.setLastFrame(lastFrame); } float getAnimationLastFrame() const { return _animationLoop.getLastFrame(); } - virtual void setDimensions(const glm::vec3& value) override; - static const quint32 DEFAULT_MAX_PARTICLES; void setMaxParticles(quint32 maxParticles); quint32 getMaxParticles() const { return _maxParticles; } @@ -100,24 +98,31 @@ public: void setEmitRate(float emitRate) { _emitRate = emitRate; } float getEmitRate() const { return _emitRate; } - static const glm::vec3 DEFAULT_EMIT_DIRECTION; - void setEmitDirection(glm::vec3 emitDirection); - const glm::vec3& getEmitDirection() const { return _emitDirection; } + static const glm::vec3 DEFAULT_EMIT_VELOCITY; + void setEmitVelocity(const glm::vec3& emitVelocity); + const glm::vec3& getEmitVelocity() const { return _emitVelocity; } + + + static const glm::vec3 DEFAULT_VELOCITY_SPREAD; + void setVelocitySpread(const glm::vec3& velocitySpread); + const glm::vec3& getVelocitySpread() const { return _velocitySpread; } - static const float DEFAULT_EMIT_STRENGTH; - void setEmitStrength(float emitStrength); - float getEmitStrength() const { return _emitStrength; } - static const float DEFAULT_LOCAL_GRAVITY; - void setLocalGravity(float localGravity); - float getLocalGravity() const { return _localGravity; } + static const glm::vec3 DEFAULT_EMIT_ACCELERATION; + void setEmitAcceleration(const glm::vec3& emitAcceleration); + const glm::vec3& getEmitAcceleration() const { return _emitAcceleration; } + + static const glm::vec3 DEFAULT_ACCELERATION_SPREAD; + void setAccelerationSpread(const glm::vec3& accelerationSpread); + const glm::vec3& getAccelerationSpread() const { return _accelerationSpread; } static const float DEFAULT_PARTICLE_RADIUS; void setParticleRadius(float particleRadius); float getParticleRadius() const { return _particleRadius; } - + void computeAndUpdateDimensions(); + bool getAnimationIsPlaying() const { return _animationLoop.isRunning(); } float getAnimationFrameIndex() const { return _animationLoop.getFrameIndex(); } float getAnimationFPS() const { return _animationLoop.getFPS(); } @@ -145,9 +150,10 @@ protected: quint32 _maxParticles; float _lifespan; float _emitRate; - glm::vec3 _emitDirection; - float _emitStrength; - float _localGravity; + glm::vec3 _emitVelocity; + glm::vec3 _velocitySpread; + glm::vec3 _emitAcceleration; + glm::vec3 _accelerationSpread; float _particleRadius; quint64 _lastAnimated; AnimationLoop _animationLoop; @@ -160,6 +166,7 @@ protected: QVector _particleLifetimes; QVector _particlePositions; QVector _particleVelocities; + QVector _particleAccelerations; float _timeUntilNextEmit; // particle arrays are a ring buffer, use these indicies diff --git a/libraries/environment/CMakeLists.txt b/libraries/environment/CMakeLists.txt index fbdc614d26..e3fc143c14 100644 --- a/libraries/environment/CMakeLists.txt +++ b/libraries/environment/CMakeLists.txt @@ -7,6 +7,4 @@ add_dependency_external_projects(glm) find_package(GLM REQUIRED) target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS}) -setup_memory_debugger() - link_hifi_libraries(shared networking) diff --git a/libraries/fbx/CMakeLists.txt b/libraries/fbx/CMakeLists.txt index c06bb0efc1..9d9f57ad20 100644 --- a/libraries/fbx/CMakeLists.txt +++ b/libraries/fbx/CMakeLists.txt @@ -7,6 +7,4 @@ add_dependency_external_projects(glm) find_package(GLM REQUIRED) target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS}) -setup_memory_debugger() - link_hifi_libraries(shared gpu model networking octree) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index f0d13f8792..8bbe8dafd8 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -2003,6 +2003,7 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping material._material = make_shared(); material._material->setEmissive(material.emissive); + // FIXME both cases are identical if (glm::all(glm::equal(material.diffuse, glm::vec3(0.0f)))) { material._material->setDiffuse(material.diffuse); } else { diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 841fdcfad9..3eff3bdec5 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -211,13 +211,17 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { while (true) { switch (tokenizer.nextToken()) { case OBJTokenizer::COMMENT_TOKEN: + #ifdef WANT_DEBUG qCDebug(modelformat) << "OBJ Reader MTLLIB comment:" << tokenizer.getComment(); + #endif break; case OBJTokenizer::DATUM_TOKEN: break; default: materials[matName] = currentMaterial; + #ifdef WANT_DEBUG qCDebug(modelformat) << "OBJ Reader Last material shininess:" << currentMaterial.shininess << " opacity:" << currentMaterial.opacity << " diffuse color:" << currentMaterial.diffuseColor << " specular color:" << currentMaterial.specularColor << " diffuse texture:" << currentMaterial.diffuseTextureFilename << " specular texture:" << currentMaterial.specularTextureFilename; + #endif return; } QByteArray token = tokenizer.getDatum(); @@ -229,14 +233,18 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { matName = tokenizer.getDatum(); currentMaterial = materials[matName]; currentMaterial.diffuseTextureFilename = "test"; + #ifdef WANT_DEBUG qCDebug(modelformat) << "OBJ Reader Starting new material definition " << matName; + #endif currentMaterial.diffuseTextureFilename = ""; } else if (token == "Ns") { currentMaterial.shininess = tokenizer.getFloat(); } else if ((token == "d") || (token == "Tr")) { currentMaterial.opacity = tokenizer.getFloat(); } else if (token == "Ka") { + #ifdef WANT_DEBUG qCDebug(modelformat) << "OBJ Reader Ignoring material Ka " << tokenizer.getVec3(); + #endif } else if (token == "Kd") { currentMaterial.diffuseColor = tokenizer.getVec3(); } else if (token == "Ks") { @@ -244,7 +252,9 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { } else if ((token == "map_Kd") || (token == "map_Ks")) { QByteArray filename = QUrl(tokenizer.getLineAsDatum()).fileName().toUtf8(); if (filename.endsWith(".tga")) { + #ifdef WANT_DEBUG qCDebug(modelformat) << "OBJ Reader WARNING: currently ignoring tga texture " << filename << " in " << _url; + #endif break; } if (isValidTexture(filename)) { @@ -254,7 +264,9 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { currentMaterial.specularTextureFilename = filename; } } else { + #ifdef WANT_DEBUG qCDebug(modelformat) << "OBJ Reader WARNING: " << _url << " ignoring missing texture " << filename; + #endif } } } @@ -318,7 +330,6 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi } QByteArray groupName = tokenizer.getDatum(); currentGroup = groupName; - //qCDebug(modelformat) << "new group:" << groupName; } else if (token == "mtllib" && _url) { if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { break; @@ -330,13 +341,17 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi librariesSeen[libraryName] = true; // Throw away any path part of libraryName, and merge against original url. QUrl libraryUrl = _url->resolved(QUrl(libraryName).fileName()); + #ifdef WANT_DEBUG qCDebug(modelformat) << "OBJ Reader new library:" << libraryName << " at:" << libraryUrl; + #endif QNetworkReply* netReply = request(libraryUrl, false); if (netReply->isFinished() && (netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200)) { parseMaterialLibrary(netReply); } else { + #ifdef WANT_DEBUG qCDebug(modelformat) << "OBJ Reader " << libraryName << " did not answer. Got " << netReply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString(); + #endif } netReply->deleteLater(); } else if (token == "usemtl") { @@ -344,7 +359,9 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi break; } currentMaterialName = tokenizer.getDatum(); + #ifdef WANT_DEBUG qCDebug(modelformat) << "OBJ Reader new current material:" << currentMaterialName; + #endif } else if (token == "v") { vertices.append(tokenizer.getVec3()); } else if (token == "vn") { @@ -394,7 +411,6 @@ done: } else { faceGroups.append(faces); // We're done with this group. Add the faces. } - //qCDebug(modelformat) << "end group:" << meshPart.materialID << " original faces:" << originalFaceCountForDebugging << " triangles:" << faces.count() << " keep going:" << result; return result; } @@ -475,13 +491,17 @@ FBXGeometry* OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping, OBJFace leadFace = faceGroup[0]; // All the faces in the same group will have the same name and material. QString groupMaterialName = leadFace.materialName; if (groupMaterialName.isEmpty() && (leadFace.textureUVIndices.count() > 0)) { + #ifdef WANT_DEBUG qCDebug(modelformat) << "OBJ Reader WARNING: " << url << " needs a texture that isn't specified. Using default mechanism."; + #endif groupMaterialName = SMART_DEFAULT_MATERIAL_NAME; } else if (!groupMaterialName.isEmpty() && !materials.contains(groupMaterialName)) { + #ifdef WANT_DEBUG qCDebug(modelformat) << "OBJ Reader WARNING: " << url << " specifies a material " << groupMaterialName << " that is not defined. Using default mechanism."; + #endif groupMaterialName = SMART_DEFAULT_MATERIAL_NAME; } if (!groupMaterialName.isEmpty()) { @@ -496,7 +516,6 @@ FBXGeometry* OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping, meshPart._material->setGloss(material->shininess); meshPart._material->setOpacity(material->opacity); } - // qCDebug(modelformat) << "OBJ Reader part:" << meshPartCount << "name:" << leadFace.groupName << "material:" << groupMaterialName << "diffuse:" << meshPart._material->getDiffuse() << "faces:" << faceGroup.count() << "triangle indices will start with:" << mesh.vertices.count(); foreach(OBJFace face, faceGroup) { glm::vec3 v0 = vertices[face.vertexIndices[0]]; glm::vec3 v1 = vertices[face.vertexIndices[1]]; diff --git a/libraries/gpu/CMakeLists.txt b/libraries/gpu/CMakeLists.txt index 84320297eb..7a88580f7f 100644 --- a/libraries/gpu/CMakeLists.txt +++ b/libraries/gpu/CMakeLists.txt @@ -1,7 +1,5 @@ set(TARGET_NAME gpu) -setup_memory_debugger() - AUTOSCRIBE_SHADER_LIB(gpu) # use setup_hifi_library macro to setup our project and link appropriate Qt modules diff --git a/libraries/gpu/src/gpu/Framebuffer.h b/libraries/gpu/src/gpu/Framebuffer.h index 6f2b762bb0..310255af9f 100755 --- a/libraries/gpu/src/gpu/Framebuffer.h +++ b/libraries/gpu/src/gpu/Framebuffer.h @@ -149,7 +149,7 @@ protected: void updateSize(const TexturePointer& texture); // Non exposed - Framebuffer(const Framebuffer& framebuffer) {} + Framebuffer(const Framebuffer& framebuffer) = delete; Framebuffer() {} // This shouldn't be used by anything else than the Backend class with the proper casting. diff --git a/libraries/input-plugins/CMakeLists.txt b/libraries/input-plugins/CMakeLists.txt index 5d6dae7ad1..1ac8047edc 100644 --- a/libraries/input-plugins/CMakeLists.txt +++ b/libraries/input-plugins/CMakeLists.txt @@ -33,8 +33,6 @@ endif() #target_include_directories(${TARGET_NAME} PRIVATE ${SIXENSE_INCLUDE_DIRS}) #target_link_libraries(${TARGET_NAME} ${SIXENSE_LIBRARIES}) -setup_memory_debugger() - # perform standard include and linking for found externals foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) diff --git a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp index e76983cce9..3aef37e502 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp +++ b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp @@ -95,7 +95,7 @@ void ViveControllerManager::activate() { vr::RenderModel_t model; if (!_hmd->LoadRenderModel(CONTROLLER_MODEL_STRING.toStdString().c_str(), &model)) { - qDebug("Unable to load render model %s\n", CONTROLLER_MODEL_STRING); + qDebug() << QString("Unable to load render model %1\n").arg(CONTROLLER_MODEL_STRING); } else { model::Mesh* mesh = new model::Mesh(); model::MeshPointer meshPtr(mesh); @@ -198,7 +198,7 @@ void ViveControllerManager::renderHand(UserInputMapper::PoseValue pose, gpu::Bat Transform transform(userInputMapper->getSensorToWorldMat()); transform.postTranslate(pose.getTranslation() + pose.getRotation() * glm::vec3(0, 0, CONTROLLER_LENGTH_OFFSET)); - int sign = index == LEFT_HAND ? 1.0f : -1.0f; + int sign = index == LEFT_HAND ? 1 : -1; glm::quat rotation = pose.getRotation() * glm::angleAxis(PI, glm::vec3(1.0f, 0.0f, 0.0f)) * glm::angleAxis(sign * PI_OVER_TWO, glm::vec3(0.0f, 0.0f, 1.0f)); transform.postRotate(rotation); @@ -325,7 +325,7 @@ void ViveControllerManager::handlePoseEvent(const mat4& mat, int index) { glm::quat rotation = glm::quat_cast(mat); // Flip the rotation appropriately for each hand - int sign = index == LEFT_HAND ? 1.0f : -1.0f; + int sign = index == LEFT_HAND ? 1 : -1; rotation = rotation * glm::angleAxis(PI, glm::vec3(1.0f, 0.0f, 0.0f)) * glm::angleAxis(sign * PI_OVER_TWO, glm::vec3(0.0f, 0.0f, 1.0f)); position += rotation * glm::vec3(0, 0, -CONTROLLER_LENGTH_OFFSET); diff --git a/libraries/model/CMakeLists.txt b/libraries/model/CMakeLists.txt index 2099f83fec..701c132e61 100755 --- a/libraries/model/CMakeLists.txt +++ b/libraries/model/CMakeLists.txt @@ -2,8 +2,6 @@ set(TARGET_NAME model) AUTOSCRIBE_SHADER_LIB(gpu model) -setup_memory_debugger() - # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library() diff --git a/libraries/networking/CMakeLists.txt b/libraries/networking/CMakeLists.txt index d0e0b850c7..400fc5446a 100644 --- a/libraries/networking/CMakeLists.txt +++ b/libraries/networking/CMakeLists.txt @@ -1,7 +1,5 @@ set(TARGET_NAME networking) -setup_memory_debugger() - # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library(Network) diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 1e94492e93..f1d4887b70 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -138,7 +138,7 @@ public: SharedNodePointer addOrUpdateNode(const QUuid& uuid, NodeType_t nodeType, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket, - bool canAdjustLocks, bool canRez, + bool canAdjustLocks = false, bool canRez = false, const QUuid& connectionSecret = QUuid()); bool hasCompletedInitialSTUN() const { return _hasCompletedInitialSTUN; } diff --git a/libraries/networking/src/UUIDHasher.h b/libraries/networking/src/UUIDHasher.h index b05e517841..8405c6a015 100644 --- a/libraries/networking/src/UUIDHasher.h +++ b/libraries/networking/src/UUIDHasher.h @@ -24,10 +24,12 @@ public: } }; -template <> struct std::hash { - size_t operator()(const QUuid& uuid) const { - return qHash(uuid); - } -}; +namespace std { + template <> struct hash { + size_t operator()(const QUuid& uuid) const { + return qHash(uuid); + } + }; +} #endif // hifi_UUIDHasher_h diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 819a65dc26..c6c4c12824 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -40,7 +40,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityData: return VERSION_ENTITIES_PROTOCOL_HEADER_SWAP; default: - return 14; + return 13; } } diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 18394f4c87..28e0bc716a 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -135,6 +135,7 @@ const PacketVersion VERSION_ENTITIES_NEW_PROTOCOL_LAYER = 35; const PacketVersion VERSION_POLYVOX_TEXTURES = 36; const PacketVersion VERSION_ENTITIES_POLYLINE = 37; const PacketVersion VERSION_OCTREE_CENTERED_ORIGIN = 38; -const PacketVersion VERSION_ENTITIES_PROTOCOL_HEADER_SWAP = 39; +const PacketVersion VERSION_ENTITIES_PARTICLE_MODIFICATIONS = 39; +const PacketVersion VERSION_ENTITIES_PROTOCOL_HEADER_SWAP = 40; #endif // hifi_PacketHeaders_h diff --git a/libraries/octree/CMakeLists.txt b/libraries/octree/CMakeLists.txt index 8b9ff6bda2..cc36aead15 100644 --- a/libraries/octree/CMakeLists.txt +++ b/libraries/octree/CMakeLists.txt @@ -1,7 +1,5 @@ set(TARGET_NAME octree) -setup_memory_debugger() - # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library() diff --git a/libraries/octree/src/CoverageMap.cpp b/libraries/octree/src/CoverageMap.cpp index b1feaff6c0..626d4bcf1a 100644 --- a/libraries/octree/src/CoverageMap.cpp +++ b/libraries/octree/src/CoverageMap.cpp @@ -338,7 +338,7 @@ void CoverageRegion::erase() { } **/ // If we're in charge of managing the polygons, then clean them up first - if (_managePolygons) { + if (_polygons && _managePolygons) { for (int i = 0; i < _polygonCount; i++) { delete _polygons[i]; _polygons[i] = NULL; // do we need to do this? diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 0825bcc512..54838ad019 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -1318,7 +1318,8 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element, // If the user also asked for occlusion culling, check if this element is occluded if (params.wantOcclusionCulling && childElement->isLeaf()) { // Don't check occlusion here, just add them to our distance ordered array... - + + // FIXME params.ViewFrustum is used here, but later it is checked against nullptr. OctreeProjectedPolygon* voxelPolygon = new OctreeProjectedPolygon( params.viewFrustum->getProjectedPolygon(childElement->getAACube())); diff --git a/libraries/physics/CMakeLists.txt b/libraries/physics/CMakeLists.txt index 802665b948..b1f9fbb79c 100644 --- a/libraries/physics/CMakeLists.txt +++ b/libraries/physics/CMakeLists.txt @@ -1,7 +1,5 @@ set(TARGET_NAME physics) -setup_memory_debugger() - # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library() diff --git a/libraries/plugins/CMakeLists.txt b/libraries/plugins/CMakeLists.txt index 98fd5fdc93..42b8cb1625 100644 --- a/libraries/plugins/CMakeLists.txt +++ b/libraries/plugins/CMakeLists.txt @@ -8,6 +8,4 @@ link_hifi_libraries(shared) add_dependency_external_projects(glm) find_package(GLM REQUIRED) -setup_memory_debugger() - target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS}) diff --git a/libraries/render-utils/CMakeLists.txt b/libraries/render-utils/CMakeLists.txt index ceb1a192ab..0ea71e54e3 100644 --- a/libraries/render-utils/CMakeLists.txt +++ b/libraries/render-utils/CMakeLists.txt @@ -40,6 +40,4 @@ add_dependency_external_projects(oglplus) find_package(OGLPLUS REQUIRED) target_include_directories(${TARGET_NAME} PUBLIC ${OGLPLUS_INCLUDE_DIRS}) -setup_memory_debugger() - link_hifi_libraries(animation fbx shared gpu model render environment) diff --git a/libraries/render-utils/src/TextureCache.cpp b/libraries/render-utils/src/TextureCache.cpp index 4954629d86..81b60ac801 100644 --- a/libraries/render-utils/src/TextureCache.cpp +++ b/libraries/render-utils/src/TextureCache.cpp @@ -610,6 +610,7 @@ QSharedPointer DilatableNetworkTexture::getDilatedTexture(float dilatio gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::UINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); if (dilatedImage.hasAlphaChannel()) { formatGPU = gpu::Element(gpu::VEC4, gpu::UINT8, (isLinearRGB ? gpu::RGBA : gpu::SRGBA)); + // FIXME either remove the ?: operator or provide different arguments depending on linear formatMip = gpu::Element(gpu::VEC4, gpu::UINT8, (isLinearRGB ? gpu::BGRA : gpu::BGRA)); } texture->_gpuTexture = gpu::TexturePointer(gpu::Texture::create2D(formatGPU, dilatedImage.width(), dilatedImage.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); diff --git a/libraries/render/CMakeLists.txt b/libraries/render/CMakeLists.txt index 1f73d93519..f2bcb7c47c 100644 --- a/libraries/render/CMakeLists.txt +++ b/libraries/render/CMakeLists.txt @@ -1,7 +1,5 @@ set(TARGET_NAME render) -setup_memory_debugger() - AUTOSCRIBE_SHADER_LIB(gpu model) # use setup_hifi_library macro to setup our project and link appropriate Qt modules diff --git a/libraries/script-engine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt index 670a13f081..139b99e426 100644 --- a/libraries/script-engine/CMakeLists.txt +++ b/libraries/script-engine/CMakeLists.txt @@ -1,7 +1,5 @@ set(TARGET_NAME script-engine) -setup_memory_debugger() - # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library(Gui Network Script WebSockets Widgets) diff --git a/libraries/script-engine/src/WebSocketClass.h b/libraries/script-engine/src/WebSocketClass.h index 8ba8ecf362..dbc9729c61 100644 --- a/libraries/script-engine/src/WebSocketClass.h +++ b/libraries/script-engine/src/WebSocketClass.h @@ -81,8 +81,10 @@ public: return OPEN; case QAbstractSocket::SocketState::ClosingState: return CLOSING; + case QAbstractSocket::SocketState::UnconnectedState: + default: + return CLOSED; } - return CLOSED; } void setOnClose(QScriptValue eventFunction) { _onCloseEvent = eventFunction; } diff --git a/libraries/shared/CMakeLists.txt b/libraries/shared/CMakeLists.txt index a80f4194ef..00a80619bc 100644 --- a/libraries/shared/CMakeLists.txt +++ b/libraries/shared/CMakeLists.txt @@ -1,7 +1,5 @@ set(TARGET_NAME shared) -setup_memory_debugger() - # use setup_hifi_library macro to setup our project and link appropriate Qt modules # TODO: there isn't really a good reason to have Script linked here - let's get what is requiring it out (RegisteredMetaTypes.cpp) setup_hifi_library(Gui Network Script Widgets) diff --git a/libraries/shared/src/Extents.cpp b/libraries/shared/src/Extents.cpp index ad00683cf2..10f97cc337 100644 --- a/libraries/shared/src/Extents.cpp +++ b/libraries/shared/src/Extents.cpp @@ -20,8 +20,8 @@ #include "Transform.h" void Extents::reset() { - minimum = glm::vec3(FLT_MAX); - maximum = glm::vec3(-FLT_MAX); + minimum = Vectors::MAX; + maximum = Vectors::MIN; } bool Extents::containsPoint(const glm::vec3& point) const { diff --git a/libraries/shared/src/Extents.h b/libraries/shared/src/Extents.h index 3d5a2dbcec..07fad60a04 100644 --- a/libraries/shared/src/Extents.h +++ b/libraries/shared/src/Extents.h @@ -18,14 +18,15 @@ #include #include "StreamUtils.h" +#include "GLMHelpers.h" class AABox; class Transform; class Extents { public: - Extents(const glm::vec3& minimum, const glm::vec3& maximum) : minimum(minimum), maximum(maximum) { } - Extents() { reset(); } + Extents() { } + Extents(const glm::vec3& minimum, const glm::vec3& maximum) : minimum(minimum), maximum(maximum) {} Extents(const AABox& box) { reset(); add(box); } /// set minimum and maximum to FLT_MAX and -FLT_MAX respectively @@ -49,7 +50,7 @@ public: /// \return whether or not the extents are empty bool isEmpty() const { return minimum == maximum; } - bool isValid() const { return !((minimum == glm::vec3(FLT_MAX)) && (maximum == glm::vec3(-FLT_MAX))); } + bool isValid() const { return !((minimum == Vectors::MAX) && (maximum == Vectors::MIN)); } /// \param vec3 for delta amount to shift the extents by /// \return true if point is within current limits @@ -75,8 +76,8 @@ public: return temp; } - glm::vec3 minimum; - glm::vec3 maximum; + glm::vec3 minimum{ Vectors::MAX }; + glm::vec3 maximum{ Vectors::MIN }; }; inline QDebug operator<<(QDebug debug, const Extents& extents) { diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp index 138d3f2c20..c1d168557d 100644 --- a/libraries/shared/src/GLMHelpers.cpp +++ b/libraries/shared/src/GLMHelpers.cpp @@ -13,6 +13,24 @@ #include "NumericalConstants.h" +const vec3 Vectors::UNIT_X{ 1.0f, 0.0f, 0.0f }; +const vec3 Vectors::UNIT_Y{ 0.0f, 1.0f, 0.0f }; +const vec3 Vectors::UNIT_Z{ 0.0f, 0.0f, 1.0f }; +const vec3 Vectors::UNIT_NEG_X{ -1.0f, 0.0f, 0.0f }; +const vec3 Vectors::UNIT_NEG_Y{ 0.0f, -1.0f, 0.0f }; +const vec3 Vectors::UNIT_NEG_Z{ 0.0f, 0.0f, -1.0f }; +const vec3 Vectors::UNIT_XY{ glm::normalize(UNIT_X + UNIT_Y) }; +const vec3 Vectors::UNIT_XZ{ glm::normalize(UNIT_X + UNIT_Z) }; +const vec3 Vectors::UNIT_YZ{ glm::normalize(UNIT_Y + UNIT_Z) }; +const vec3 Vectors::UNIT_XYZ{ glm::normalize(UNIT_X + UNIT_Y + UNIT_Z) }; +const vec3 Vectors::MAX{ FLT_MAX }; +const vec3 Vectors::MIN{ -FLT_MAX }; +const vec3 Vectors::ZERO{ 0.0f }; +const vec3 Vectors::ONE{ 1.0f }; +const vec3& Vectors::RIGHT = Vectors::UNIT_X; +const vec3& Vectors::UP = Vectors::UNIT_Y; +const vec3& Vectors::FRONT = Vectors::UNIT_NEG_Z; + // Safe version of glm::mix; based on the code in Nick Bobick's article, // http://www.gamasutra.com/features/19980703/quaternions_01.htm (via Clyde, // https://github.com/threerings/clyde/blob/master/src/main/java/com/threerings/math/Quaternion.java) diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index 79addbc5f1..4b03ed2525 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -53,6 +53,28 @@ const glm::vec3 IDENTITY_FRONT = glm::vec3( 0.0f, 0.0f,-1.0f); glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float alpha); +class Vectors { +public: + static const vec3 UNIT_X; + static const vec3 UNIT_Y; + static const vec3 UNIT_Z; + static const vec3 UNIT_NEG_X; + static const vec3 UNIT_NEG_Y; + static const vec3 UNIT_NEG_Z; + static const vec3 UNIT_XY; + static const vec3 UNIT_XZ; + static const vec3 UNIT_YZ; + static const vec3 UNIT_ZX; + static const vec3 UNIT_XYZ; + static const vec3 MAX; + static const vec3 MIN; + static const vec3 ZERO; + static const vec3 ONE; + static const vec3& RIGHT; + static const vec3& UP; + static const vec3& FRONT; +}; + // These pack/unpack functions are designed to start specific known types in as efficient a manner // as possible. Taking advantage of the known characteristics of the semantic types. diff --git a/libraries/shared/src/LogHandler.cpp b/libraries/shared/src/LogHandler.cpp index 60cddb5cfe..d1f23531cb 100644 --- a/libraries/shared/src/LogHandler.cpp +++ b/libraries/shared/src/LogHandler.cpp @@ -38,14 +38,14 @@ LogHandler::LogHandler() : const char* stringForLogType(LogMsgType msgType) { switch (msgType) { - case QtDebugMsg: + case LogDebug: return "DEBUG"; - case QtCriticalMsg: - return "CRITICAL"; - case QtFatalMsg: - return "FATAL"; - case QtWarningMsg: + case LogWarning: return "WARNING"; + case LogCritical: + return "CRITICAL"; + case LogFatal: + return "FATAL"; case LogSuppressed: return "SUPPRESS"; default: diff --git a/libraries/shared/src/LogHandler.h b/libraries/shared/src/LogHandler.h index 914cad212d..6af721f96c 100644 --- a/libraries/shared/src/LogHandler.h +++ b/libraries/shared/src/LogHandler.h @@ -22,10 +22,10 @@ const int VERBOSE_LOG_INTERVAL_SECONDS = 5; enum LogMsgType { - LogDebug, - LogWarning, - LogCritical, - LogFatal, + LogDebug = QtDebugMsg, + LogWarning = QtWarningMsg, + LogCritical = QtCriticalMsg, + LogFatal = QtFatalMsg, LogSuppressed }; diff --git a/libraries/shared/src/NumericalConstants.h b/libraries/shared/src/NumericalConstants.h index b632147641..9a946e35f7 100644 --- a/libraries/shared/src/NumericalConstants.h +++ b/libraries/shared/src/NumericalConstants.h @@ -31,6 +31,7 @@ const float METERS_PER_DECIMETER = 0.1f; const float METERS_PER_CENTIMETER = 0.01f; const float METERS_PER_MILLIMETER = 0.001f; const float MILLIMETERS_PER_METER = 1000.0f; +const quint64 NSECS_PER_USEC = 1000; const quint64 USECS_PER_MSEC = 1000; const quint64 MSECS_PER_SECOND = 1000; const quint64 USECS_PER_SECOND = USECS_PER_MSEC * MSECS_PER_SECOND; diff --git a/libraries/shared/src/RingBufferHistory.h b/libraries/shared/src/RingBufferHistory.h index c0bd1e2a6e..c5222b9c39 100644 --- a/libraries/shared/src/RingBufferHistory.h +++ b/libraries/shared/src/RingBufferHistory.h @@ -174,7 +174,7 @@ public: } bool operator<=(const Iterator& rhs) { - return age() < rhs.age(); + return age() <= rhs.age(); } bool operator>=(const Iterator& rhs) { diff --git a/libraries/shared/src/SettingInterface.cpp b/libraries/shared/src/SettingInterface.cpp index b60ffc0891..11ed64cac4 100644 --- a/libraries/shared/src/SettingInterface.cpp +++ b/libraries/shared/src/SettingInterface.cpp @@ -35,9 +35,9 @@ namespace Setting { settingsManagerThread->quit(); settingsManagerThread->wait(); } - - // Sets up the settings private instance. Should only be run once at startup - void init() { + + // Set up application settings. Should only be run once at startup. + void preInit() { // read the ApplicationInfo.ini file for Name/Version/Domain information QSettings::setDefaultFormat(QSettings::IniFormat); QSettings applicationInfo(PathUtils::resourcesPath() + "info/ApplicationInfo.ini", QSettings::IniFormat); @@ -46,7 +46,19 @@ namespace Setting { QCoreApplication::setApplicationName(applicationInfo.value("name").toString()); QCoreApplication::setOrganizationName(applicationInfo.value("organizationName").toString()); QCoreApplication::setOrganizationDomain(applicationInfo.value("organizationDomain").toString()); - + + // Delete Interface.ini.lock file if it exists, otherwise Interface freezes. + QSettings settings; + QString settingsLockFilename = settings.fileName() + ".lock"; + QFile settingsLockFile(settingsLockFilename); + if (settingsLockFile.exists()) { + bool deleted = settingsLockFile.remove(); + qCDebug(shared) << (deleted ? "Deleted" : "Failed to delete") << "settings lock file" << settingsLockFilename; + } + } + + // Sets up the settings private instance. Should only be run once at startup. preInit() must be run beforehand, + void init() { // Let's set up the settings Private instance on its own thread QThread* thread = new QThread(); Q_CHECK_PTR(thread); @@ -55,14 +67,6 @@ namespace Setting { privateInstance = new Manager(); Q_CHECK_PTR(privateInstance); - // Delete Interface.ini.lock file if it exists, otherwise Interface freezes. - QString settingsLockFilename = privateInstance->fileName() + ".lock"; - QFile settingsLockFile(settingsLockFilename); - if (settingsLockFile.exists()) { - bool deleted = settingsLockFile.remove(); - qCDebug(shared) << (deleted ? "Deleted" : "Failed to delete") << "settings lock file" << settingsLockFilename; - } - QObject::connect(privateInstance, SIGNAL(destroyed()), thread, SLOT(quit())); QObject::connect(thread, SIGNAL(started()), privateInstance, SLOT(startTimer())); QObject::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); diff --git a/libraries/shared/src/SettingInterface.h b/libraries/shared/src/SettingInterface.h index 5092fd09c8..c8b1595a75 100644 --- a/libraries/shared/src/SettingInterface.h +++ b/libraries/shared/src/SettingInterface.h @@ -16,6 +16,7 @@ #include namespace Setting { + void preInit(); void init(); void cleanupSettings(); diff --git a/libraries/shared/src/Transform.cpp b/libraries/shared/src/Transform.cpp index a00761fd50..4b2a481f41 100644 --- a/libraries/shared/src/Transform.cpp +++ b/libraries/shared/src/Transform.cpp @@ -37,7 +37,7 @@ void Transform::evalRotationScale(Quat& rotation, Vec3& scale, const Mat3& rotat norm = (norm > n ? norm : n); } rotationMat = nextRotation; - } while (count < 100 && norm > ACCURACY_THREASHOLD); + } while (count++ < 100 && norm > ACCURACY_THREASHOLD); // extract scale of the matrix as the length of each axis diff --git a/libraries/ui/CMakeLists.txt b/libraries/ui/CMakeLists.txt index 68caa940c1..b07651f7d4 100644 --- a/libraries/ui/CMakeLists.txt +++ b/libraries/ui/CMakeLists.txt @@ -8,6 +8,4 @@ link_hifi_libraries(render-utils shared) add_dependency_external_projects(glm) find_package(GLM REQUIRED) -setup_memory_debugger() - target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS}) diff --git a/libraries/ui/src/VrMenu.cpp b/libraries/ui/src/VrMenu.cpp index ce4aba83d3..1ede74fc86 100644 --- a/libraries/ui/src/VrMenu.cpp +++ b/libraries/ui/src/VrMenu.cpp @@ -83,46 +83,6 @@ VrMenu::VrMenu(QQuickItem* parent) : QQuickItem(parent) { this->setEnabled(false); } - -// QML helper functions -QObject* addMenu(QObject* parent, const QString& text) { - // FIXME add more checking here to ensure no name conflicts - QVariant returnedValue; - QMetaObject::invokeMethod(parent, "addMenu", Qt::DirectConnection, - Q_RETURN_ARG(QVariant, returnedValue), - Q_ARG(QVariant, text)); - QObject* result = returnedValue.value(); - if (result) { - result->setObjectName(text); - } - return result; -} - -class QQuickMenuItem; -QObject* addItem(QObject* parent, const QString& text) { - // FIXME add more checking here to ensure no name conflicts - QQuickMenuItem* returnedValue{ nullptr }; - bool invokeResult = - QMetaObject::invokeMethod(parent, "addItem", Qt::DirectConnection, Q_RETURN_ARG(QQuickMenuItem*, returnedValue), - Q_ARG(QString, text)); - -#ifndef QT_NO_DEBUG - Q_ASSERT(invokeResult); -#else - Q_UNUSED(invokeResult); -#endif - QObject* result = reinterpret_cast(returnedValue); - return result; -} - -const QObject* VrMenu::findMenuObject(const QString& menuOption) const { - if (menuOption.isEmpty()) { - return _rootMenu; - } - const QObject* result = _rootMenu->findChild(menuOption); - return result; -} - QObject* VrMenu::findMenuObject(const QString& menuOption) { if (menuOption.isEmpty()) { return _rootMenu; @@ -147,7 +107,16 @@ void VrMenu::addMenu(QMenu* menu) { } else { Q_ASSERT(false); } - QObject* result = ::addMenu(qmlParent, menu->title()); + QVariant returnedValue; + bool invokeResult = QMetaObject::invokeMethod(this, "addMenu", Qt::DirectConnection, + Q_RETURN_ARG(QVariant, returnedValue), + Q_ARG(QVariant, QVariant::fromValue(qmlParent)), + Q_ARG(QVariant, QVariant::fromValue(menu->title()))); + Q_ASSERT(invokeResult); + QObject* result = returnedValue.value(); + Q_ASSERT(result); + + // Bind the QML and Widget together new MenuUserData(menu, result); } @@ -175,9 +144,15 @@ void VrMenu::addAction(QMenu* menu, QAction* action) { Q_ASSERT(!MenuUserData::forObject(action)); Q_ASSERT(MenuUserData::forObject(menu)); MenuUserData* userData = MenuUserData::forObject(menu); - QObject* parent = findMenuObject(userData->uuid.toString()); - Q_ASSERT(parent); - QObject* result = ::addItem(parent, action->text()); + QObject* menuQml = findMenuObject(userData->uuid.toString()); + Q_ASSERT(menuQml); + QVariant returnedValue; + bool invokeResult = QMetaObject::invokeMethod(this, "addItem", Qt::DirectConnection, + Q_RETURN_ARG(QVariant, returnedValue), + Q_ARG(QVariant, QVariant::fromValue(menuQml)), + Q_ARG(QVariant, QVariant::fromValue(action->text()))); + Q_ASSERT(invokeResult); + QObject* result = returnedValue.value(); Q_ASSERT(result); // Bind the QML and Widget together bindActionToQmlAction(result, action); @@ -190,38 +165,34 @@ void VrMenu::insertAction(QAction* before, QAction* action) { Q_ASSERT(beforeUserData); beforeQml = findMenuObject(beforeUserData->uuid.toString()); } - QObject* menu = beforeQml->parent(); - int index{ -1 }; - QVariant itemsVar = menu->property("items"); - QList items = itemsVar.toList(); - // FIXME add more checking here to ensure no name conflicts - for (index = 0; index < items.length(); ++index) { - QObject* currentQmlItem = items.at(index).value(); - if (currentQmlItem == beforeQml) { - break; - } - } - - QObject* result{ nullptr }; - if (index < 0 || index >= items.length()) { - result = ::addItem(menu, action->text()); - } else { - QQuickMenuItem* returnedValue{ nullptr }; - bool invokeResult = - QMetaObject::invokeMethod(menu, "insertItem", Qt::DirectConnection, Q_RETURN_ARG(QQuickMenuItem*, returnedValue), - Q_ARG(int, index), Q_ARG(QString, action->text())); -#ifndef QT_NO_DEBUG - Q_ASSERT(invokeResult); -#else - Q_UNUSED(invokeResult); -#endif - result = reinterpret_cast(returnedValue); - } + QVariant returnedValue; + bool invokeResult = QMetaObject::invokeMethod(this, "insertItem", Qt::DirectConnection, + Q_RETURN_ARG(QVariant, returnedValue), + Q_ARG(QVariant, QVariant::fromValue(menu)), + Q_ARG(QVariant, QVariant::fromValue(beforeQml)), + Q_ARG(QVariant, QVariant::fromValue(action->text()))); + Q_ASSERT(invokeResult); + QObject* result = returnedValue.value(); Q_ASSERT(result); bindActionToQmlAction(result, action); } void VrMenu::removeAction(QAction* action) { - // FIXME implement + MenuUserData* userData = MenuUserData::forObject(action); + if (!userData) { + qWarning("Attempted to remove menu action with no found QML object"); + return; + } + QObject* item = findMenuObject(userData->uuid.toString()); + QObject* menu = item->parent(); + // Proxy QuickItem requests through the QML layer + bool invokeResult = QMetaObject::invokeMethod(this, "removeItem", Qt::DirectConnection, + Q_ARG(QVariant, QVariant::fromValue(menu)), + Q_ARG(QVariant, QVariant::fromValue(item))); +#ifndef QT_NO_DEBUG + Q_ASSERT(invokeResult); +#else + Q_UNUSED(invokeResult); +#endif } diff --git a/libraries/ui/src/VrMenu.h b/libraries/ui/src/VrMenu.h index 06a16588af..38e3b54739 100644 --- a/libraries/ui/src/VrMenu.h +++ b/libraries/ui/src/VrMenu.h @@ -38,9 +38,7 @@ public: protected: QObject* _rootMenu{ nullptr }; - QObject* findMenuObject(const QString& name); - const QObject* findMenuObject(const QString& name) const; static VrMenu* _instance; friend class MenuUserData; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c2dc30c4bb..1593b649a0 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -35,5 +35,3 @@ set_target_properties("all-tests" PROPERTIES FOLDER "hidden/test-targets") set_target_properties("all-tests" PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD TRUE EXCLUDE_FROM_ALL TRUE) - -setup_memory_debugger() diff --git a/tests/animation/CMakeLists.txt b/tests/animation/CMakeLists.txt index bc1e93a94f..2e9dbc9424 100644 --- a/tests/animation/CMakeLists.txt +++ b/tests/animation/CMakeLists.txt @@ -6,6 +6,4 @@ macro (setup_testcase_dependencies) copy_dlls_beside_windows_executable() endmacro () -setup_memory_debugger() - setup_hifi_testcase() diff --git a/tests/audio/CMakeLists.txt b/tests/audio/CMakeLists.txt index c56ef049bd..f7dd1d23b9 100644 --- a/tests/audio/CMakeLists.txt +++ b/tests/audio/CMakeLists.txt @@ -6,6 +6,4 @@ macro (SETUP_TESTCASE_DEPENDENCIES) copy_dlls_beside_windows_executable() endmacro () -setup_memory_debugger() - setup_hifi_testcase() diff --git a/tests/entities/CMakeLists.txt b/tests/entities/CMakeLists.txt index f83efe7f64..b2c7969386 100644 --- a/tests/entities/CMakeLists.txt +++ b/tests/entities/CMakeLists.txt @@ -9,6 +9,4 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") # link in the shared libraries link_hifi_libraries(entities avatars shared octree gpu model fbx networking animation environment) -setup_memory_debugger() - copy_dlls_beside_windows_executable() diff --git a/tests/jitter/CMakeLists.txt b/tests/jitter/CMakeLists.txt index ba46582b02..98be42530c 100644 --- a/tests/jitter/CMakeLists.txt +++ b/tests/jitter/CMakeLists.txt @@ -7,6 +7,4 @@ macro (setup_testcase_dependencies) copy_dlls_beside_windows_executable() endmacro() -setup_memory_debugger() - setup_hifi_testcase() diff --git a/tests/networking/CMakeLists.txt b/tests/networking/CMakeLists.txt index fcf32d89c8..efa744e4c9 100644 --- a/tests/networking/CMakeLists.txt +++ b/tests/networking/CMakeLists.txt @@ -7,6 +7,4 @@ macro (setup_testcase_dependencies) copy_dlls_beside_windows_executable() endmacro () -setup_memory_debugger() - setup_hifi_testcase() diff --git a/tests/octree/CMakeLists.txt b/tests/octree/CMakeLists.txt index 77511c682a..e2a756105a 100644 --- a/tests/octree/CMakeLists.txt +++ b/tests/octree/CMakeLists.txt @@ -7,6 +7,4 @@ macro (setup_testcase_dependencies) copy_dlls_beside_windows_executable() endmacro () -setup_memory_debugger() - setup_hifi_testcase(Script Network) diff --git a/tests/physics/CMakeLists.txt b/tests/physics/CMakeLists.txt index 1a6f49430b..36cf21c681 100644 --- a/tests/physics/CMakeLists.txt +++ b/tests/physics/CMakeLists.txt @@ -21,6 +21,4 @@ macro (SETUP_TESTCASE_DEPENDENCIES) copy_dlls_beside_windows_executable() endmacro () -setup_memory_debugger() - setup_hifi_testcase(Script) diff --git a/tests/render-utils/CMakeLists.txt b/tests/render-utils/CMakeLists.txt index 1b47f85099..62ed401d2e 100644 --- a/tests/render-utils/CMakeLists.txt +++ b/tests/render-utils/CMakeLists.txt @@ -10,6 +10,4 @@ link_hifi_libraries(render-utils gpu shared) message(${PROJECT_BINARY_DIR}) -setup_memory_debugger() - copy_dlls_beside_windows_executable() diff --git a/tests/shaders/CMakeLists.txt b/tests/shaders/CMakeLists.txt index 3ee9f4ae9f..9b2a079e88 100644 --- a/tests/shaders/CMakeLists.txt +++ b/tests/shaders/CMakeLists.txt @@ -19,6 +19,4 @@ include_directories("${PROJECT_BINARY_DIR}/../../libraries/model/") message(${PROJECT_BINARY_DIR}) -setup_memory_debugger() - copy_dlls_beside_windows_executable() diff --git a/tests/shared/CMakeLists.txt b/tests/shared/CMakeLists.txt index c3d6cfd810..7bddb4b2ed 100644 --- a/tests/shared/CMakeLists.txt +++ b/tests/shared/CMakeLists.txt @@ -8,6 +8,4 @@ macro (setup_testcase_dependencies) copy_dlls_beside_windows_executable() endmacro () -setup_memory_debugger() - setup_hifi_testcase() diff --git a/tests/ui/CMakeLists.txt b/tests/ui/CMakeLists.txt index c0f20a280f..6a2d8c394a 100644 --- a/tests/ui/CMakeLists.txt +++ b/tests/ui/CMakeLists.txt @@ -13,6 +13,4 @@ endif() # link in the shared libraries link_hifi_libraries(ui render-utils gpu shared) -setup_memory_debugger() - copy_dlls_beside_windows_executable() diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 0bb7f86729..2056044a4b 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -1,5 +1,3 @@ -setup_memory_debugger() - # add the tool directories add_subdirectory(mtc) set_target_properties(mtc PROPERTIES FOLDER "Tools") diff --git a/tools/mtc/CMakeLists.txt b/tools/mtc/CMakeLists.txt index fe5f1920bc..16c812673d 100644 --- a/tools/mtc/CMakeLists.txt +++ b/tools/mtc/CMakeLists.txt @@ -1,6 +1,4 @@ set(TARGET_NAME mtc) setup_hifi_project() -setup_memory_debugger() - copy_dlls_beside_windows_executable() diff --git a/tools/scribe/CMakeLists.txt b/tools/scribe/CMakeLists.txt index 0abf70f727..e62a3467e0 100755 --- a/tools/scribe/CMakeLists.txt +++ b/tools/scribe/CMakeLists.txt @@ -1,5 +1,3 @@ set(TARGET_NAME scribe) -setup_memory_debugger() - setup_hifi_project() diff --git a/tools/scribe/src/TextTemplate.cpp b/tools/scribe/src/TextTemplate.cpp index c468df278b..e7537508bc 100755 --- a/tools/scribe/src/TextTemplate.cpp +++ b/tools/scribe/src/TextTemplate.cpp @@ -196,7 +196,7 @@ bool TextTemplate::grabUntilEndTag(std::istream* str, std::string& grabbed, Tag: preEnd = Tag::REM; } - while (!str->eof()) { + while (!str->eof() && !str->fail()) { // looking for the end of the tag means find the next preEnd std::string dataToken; getline((*str), dataToken, preEnd); @@ -233,7 +233,7 @@ bool TextTemplate::stepForward(std::istream* str, std::string& grabbed, std::str if (grabUntilEndTag(str, tag, tagType)) { // skip trailing space and new lines only after Command or Remark tag block if ((tagType == Tag::COMMAND) || (tagType == Tag::REMARK)) { - while (!str->eof()) { + while (!str->eof() && !str->fail()) { char c = str->peek(); if ((c == ' ') || (c == '\t') || (c == '\n')) { str->get(); diff --git a/tools/vhacd-util/CMakeLists.txt b/tools/vhacd-util/CMakeLists.txt index b79a6a1893..c94b2ad083 100644 --- a/tools/vhacd-util/CMakeLists.txt +++ b/tools/vhacd-util/CMakeLists.txt @@ -8,8 +8,6 @@ find_package(VHACD REQUIRED) target_include_directories(${TARGET_NAME} PUBLIC ${VHACD_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${VHACD_LIBRARIES}) -setup_memory_debugger() - if (UNIX AND NOT APPLE) include(FindOpenMP) if(OPENMP_FOUND)