mirror of
https://github.com/overte-org/overte.git
synced 2025-04-07 10:32:25 +02:00
resolve conflicts on merge with upstream/master
This commit is contained in:
commit
80cf7b6c76
121 changed files with 1696 additions and 970 deletions
|
@ -19,6 +19,4 @@ endif (UNIX)
|
|||
|
||||
include_application_version()
|
||||
|
||||
setup_memory_debugger()
|
||||
|
||||
copy_dlls_beside_windows_executable()
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -131,9 +131,6 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<NLPacket> 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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
|
@ -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})
|
||||
|
|
|
@ -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 ()
|
||||
|
|
611
domain-server/src/DomainGatekeeper.cpp
Normal file
611
domain-server/src/DomainGatekeeper.cpp
Normal file
|
@ -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 <openssl/err.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/x509.h>
|
||||
|
||||
#include <AccountManager.h>
|
||||
#include <Assignment.h>
|
||||
#include <JSONBreakableMarshal.h>
|
||||
|
||||
#include "DomainServer.h"
|
||||
#include "DomainServerNodeData.h"
|
||||
|
||||
using SharedAssignmentPointer = QSharedPointer<Assignment>;
|
||||
|
||||
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<NLPacket> 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<DomainServerNodeData*>(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<DomainServerNodeData*>(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<LimitedNodeList>();
|
||||
|
||||
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<DomainServerNodeData*>(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<LimitedNodeList>();
|
||||
|
||||
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<const unsigned char*>(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<const unsigned char*>(usernameWithToken.constData()),
|
||||
usernameWithToken.size(),
|
||||
reinterpret_cast<const unsigned char*>(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<LimitedNodeList>()->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<LimitedNodeList>()->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<LimitedNodeList>();
|
||||
|
||||
// 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<NetworkPeer*>(sender());
|
||||
|
||||
if (senderPeer) {
|
||||
SharedNetworkPeer sharedPeer = _icePeers.value(senderPeer->getUUID());
|
||||
|
||||
if (sharedPeer && !sharedPeer->getActiveSocket()) {
|
||||
pingPunchForConnectingPeer(sharedPeer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DomainGatekeeper::processICEPeerInformationPacket(QSharedPointer<NLPacket> 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<NLPacket> packet) {
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
auto pingReplyPacket = limitedNodeList->constructICEPingReplyPacket(*packet, limitedNodeList->getSessionUUID());
|
||||
|
||||
limitedNodeList->sendPacket(std::move(pingReplyPacket), packet->getSenderSockAddr());
|
||||
}
|
||||
|
||||
void DomainGatekeeper::processICEPingReplyPacket(QSharedPointer<NLPacket> 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());
|
||||
}
|
||||
}
|
93
domain-server/src/DomainGatekeeper.h
Normal file
93
domain-server/src/DomainGatekeeper.h
Normal file
|
@ -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 <unordered_map>
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
|
||||
#include <NLPacket.h>
|
||||
#include <Node.h>
|
||||
#include <UUIDHasher.h>
|
||||
|
||||
#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<NLPacket> packet);
|
||||
void processICEPingPacket(QSharedPointer<NLPacket> packet);
|
||||
void processICEPingReplyPacket(QSharedPointer<NLPacket> packet);
|
||||
void processICEPeerInformationPacket(QSharedPointer<NLPacket> 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<QUuid, PendingAssignedNodeData> _pendingAssignedNodes;
|
||||
|
||||
QHash<QUuid, SharedNetworkPeer> _icePeers;
|
||||
|
||||
QHash<QString, QUuid> _connectionTokenHash;
|
||||
QHash<QString, QByteArray> _userPublicKeys;
|
||||
};
|
||||
|
||||
|
||||
#endif // hifi_DomainGatekeeper_h
|
|
@ -9,9 +9,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/x509.h>
|
||||
#include "DomainServer.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QJsonDocument>
|
||||
|
@ -38,24 +36,19 @@
|
|||
#include <LogHandler.h>
|
||||
|
||||
#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>("DomainServerWebSessionData");
|
||||
qRegisterMetaTypeStreamOperators<DomainServerWebSessionData>("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<Ass
|
|||
}
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
const NodeSet STATICALLY_ASSIGNED_NODES = NodeSet() << NodeType::AudioMixer
|
||||
<< NodeType::AvatarMixer << NodeType::EntityServer;
|
||||
|
||||
|
@ -777,22 +780,22 @@ void DomainServer::processConnectRequestPacket(QSharedPointer<NLPacket> packet)
|
|||
}
|
||||
|
||||
|
||||
=======
|
||||
>>>>>>> d0db56a4a6902e9d1ee706ec02152e94688f4e5c
|
||||
void DomainServer::processListRequestPacket(QSharedPointer<NLPacket> 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<DomainServerNodeData*>(sendingNode->getLinkedData());
|
||||
nodeData->setNodeInterestSet(nodeRequestData.interestList.toSet());
|
||||
|
||||
sendingNode->setPublicSocket(nodePublicAddress);
|
||||
sendingNode->setLocalSocket(nodeLocalAddress);
|
||||
|
||||
QList<NodeType_t> 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<const unsigned char*>(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<const unsigned char*>(usernameWithToken.constData()),
|
||||
usernameWithToken.size(),
|
||||
reinterpret_cast<const unsigned char*>(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<LimitedNodeList>()->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<DomainServerNodeData*>(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<DomainServerNodeData*>(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<NLPacket> packe
|
|||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
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<LimitedNodeList>()->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<LimitedNodeList>();
|
||||
|
||||
// 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<NetworkPeer*>(sender());
|
||||
|
||||
if (senderPeer) {
|
||||
SharedNetworkPeer sharedPeer = _icePeers.value(senderPeer->getUUID());
|
||||
|
||||
if (sharedPeer && !sharedPeer->getActiveSocket()) {
|
||||
pingPunchForConnectingPeer(sharedPeer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DomainServer::processICEPeerInformationPacket(QSharedPointer<NLPacket> 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<NLPacket> packet) {
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
auto pingReplyPacket = limitedNodeList->constructICEPingReplyPacket(*packet, limitedNodeList->getSessionUUID());
|
||||
|
||||
limitedNodeList->sendPacket(std::move(pingReplyPacket), packet->getSenderSockAddr());
|
||||
}
|
||||
|
||||
void DomainServer::processICEPingReplyPacket(QSharedPointer<NLPacket> 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<NLPacket> packet, SharedNodePointer sendingNode) {
|
||||
auto nodeData = dynamic_cast<DomainServerNodeData*>(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<DomainServerNodeData*>(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<SharedAssignmentPointer>::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<SharedAssignmentPointer>::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,
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <HTTPSConnection.h>
|
||||
#include <LimitedNodeList.h>
|
||||
|
||||
#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<NLPacket> packet);
|
||||
void processConnectRequestPacket(QSharedPointer<NLPacket> packet);
|
||||
void processListRequestPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
|
||||
void processNodeJSONStatsPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
|
||||
void processPathQueryPacket(QSharedPointer<NLPacket> packet);
|
||||
void processICEPingPacket(QSharedPointer<NLPacket> packet);
|
||||
void processICEPingReplyPacket(QSharedPointer<NLPacket> packet);
|
||||
void processICEPeerInformationPacket(QSharedPointer<NLPacket> 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<Assignment::Type>& 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<QUuid, SharedAssignmentPointer> _allAssignments;
|
||||
QQueue<SharedAssignmentPointer> _unfulfilledAssignments;
|
||||
QHash<QUuid, PendingAssignedNodeData*> _pendingAssignedNodes;
|
||||
TransactionHash _pendingAssignmentCredits;
|
||||
|
||||
bool _isUsingDTLS;
|
||||
|
@ -150,18 +137,14 @@ private:
|
|||
|
||||
QSet<QUuid> _webAuthenticationStateSet;
|
||||
QHash<QUuid, DomainServerWebSessionData> _cookieSessionHash;
|
||||
|
||||
QHash<QString, QUuid> _connectionTokenHash;
|
||||
|
||||
QHash<QString, QByteArray> _userPublicKeys;
|
||||
|
||||
QHash<QUuid, SharedNetworkPeer> _icePeers;
|
||||
|
||||
QString _automaticNetworkingSetting;
|
||||
|
||||
DomainServerSettingsManager _settingsManager;
|
||||
|
||||
HifiSockAddr _iceServerSocket;
|
||||
|
||||
friend class DomainGatekeeper;
|
||||
};
|
||||
|
||||
|
||||
|
|
42
domain-server/src/NodeConnectionData.cpp
Normal file
42
domain-server/src/NodeConnectionData.cpp
Normal file
|
@ -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;
|
||||
}
|
33
domain-server/src/NodeConnectionData.h
Normal file
33
domain-server/src/NodeConnectionData.h
Normal file
|
@ -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 <Node.h>
|
||||
|
||||
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<NodeType_t> interestList;
|
||||
};
|
||||
|
||||
|
||||
#endif // hifi_NodeConnectionData_h
|
|
@ -16,5 +16,5 @@ PendingAssignedNodeData::PendingAssignedNodeData(const QUuid& assignmentUUID, co
|
|||
_walletUUID(walletUUID),
|
||||
_nodeVersion(nodeVersion)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -34,4 +34,4 @@ private:
|
|||
QString _nodeVersion;
|
||||
};
|
||||
|
||||
#endif // hifi_PendingAssignedNodeData_h
|
||||
#endif // hifi_PendingAssignedNodeData_h
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -88,6 +88,4 @@ if (ANDROID)
|
|||
|
||||
endif (ANDROID)
|
||||
|
||||
setup_memory_debugger()
|
||||
|
||||
copy_dlls_beside_windows_executable()
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
set(TARGET_NAME ice-server)
|
||||
|
||||
setup_memory_debugger()
|
||||
|
||||
# setup the project and link required Qt modules
|
||||
setup_hifi_project(Network)
|
||||
|
||||
|
|
|
@ -205,6 +205,4 @@ else (APPLE)
|
|||
endif()
|
||||
endif (APPLE)
|
||||
|
||||
setup_memory_debugger()
|
||||
|
||||
copy_dlls_beside_windows_executable()
|
||||
|
|
Binary file not shown.
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -105,6 +105,7 @@
|
|||
#include <RenderableWebEntityItem.h>
|
||||
|
||||
#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<LimitedNodeList, NodeList>();
|
||||
DependencyManager::registerInheritance<AvatarHashMap, AvatarManager>();
|
||||
DependencyManager::registerInheritance<EntityActionFactoryInterface, InterfaceActionFactory>();
|
||||
|
@ -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<float> 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";
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
183
interface/src/CrashHandler.cpp
Normal file
183
interface/src/CrashHandler.cpp
Normal file
|
@ -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 <QCoreApplication>
|
||||
#include <QDialog>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QFile>
|
||||
#include <QLabel>
|
||||
#include <PathUtils.h>
|
||||
#include <QRadioButton>
|
||||
#include <QSettings>
|
||||
#include <QStandardPaths>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#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<QString, DataServerAccountInfo> accounts;
|
||||
|
||||
if (action == CrashHandler::RETAIN_LOGIN_AND_AVATAR_INFO) {
|
||||
// Read login and avatar info
|
||||
|
||||
qRegisterMetaType<DataServerAccountInfo>("DataServerAccountInfo");
|
||||
qRegisterMetaTypeStreamOperators<DataServerAccountInfo>("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<DataServerAccountInfo>());
|
||||
}
|
||||
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;
|
||||
}
|
38
interface/src/CrashHandler.h
Normal file
38
interface/src/CrashHandler.h
Normal file
|
@ -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 <QString>
|
||||
|
||||
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
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -103,7 +103,7 @@ int main(int argc, const char* argv[]) {
|
|||
Application app(argc, const_cast<char**>(argv), startupTime);
|
||||
|
||||
QTranslator translator;
|
||||
translator.load("interface_en");
|
||||
translator.load("i18n/interface_en");
|
||||
app.installTranslator(&translator);
|
||||
|
||||
qCDebug(interfaceapp, "Created QT Application.");
|
||||
|
|
|
@ -437,7 +437,6 @@ void OctreeStatsDialog::showOctreeServersOfType(int& serverCount, NodeType_t ser
|
|||
case MOST: {
|
||||
extraDetails << "<br/>" ;
|
||||
|
||||
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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
set(TARGET_NAME auto-updater)
|
||||
|
||||
setup_memory_debugger()
|
||||
|
||||
setup_hifi_library(Network)
|
||||
link_hifi_libraries(shared networking)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -7,21 +7,35 @@
|
|||
//
|
||||
#include "Basic2DWindowOpenGLDisplayPlugin.h"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include <QtGui/QWindow>
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtWidgets/QAction>
|
||||
|
||||
#include <plugins/PluginContainer.h>
|
||||
#include <QWindow>
|
||||
#include <QGuiApplication>
|
||||
|
||||
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<QAction*> _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();
|
||||
|
|
|
@ -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 };
|
||||
};
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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 };
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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<HTTPSConnection*>(connection), url);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)));
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -610,6 +610,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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, "");
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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<float> _particleLifetimes;
|
||||
QVector<glm::vec3> _particlePositions;
|
||||
QVector<glm::vec3> _particleVelocities;
|
||||
QVector<glm::vec3> _particleAccelerations;
|
||||
float _timeUntilNextEmit;
|
||||
|
||||
// particle arrays are a ring buffer, use these indicies
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -2003,6 +2003,7 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
|||
|
||||
material._material = make_shared<model::Material>();
|
||||
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 {
|
||||
|
|
|
@ -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]];
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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})
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -24,10 +24,12 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
template <> struct std::hash<QUuid> {
|
||||
size_t operator()(const QUuid& uuid) const {
|
||||
return qHash(uuid);
|
||||
}
|
||||
};
|
||||
namespace std {
|
||||
template <> struct hash<QUuid> {
|
||||
size_t operator()(const QUuid& uuid) const {
|
||||
return qHash(uuid);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif // hifi_UUIDHasher_h
|
||||
|
|
|
@ -40,7 +40,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
case PacketType::EntityData:
|
||||
return VERSION_ENTITIES_PROTOCOL_HEADER_SWAP;
|
||||
default:
|
||||
return 14;
|
||||
return 13;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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()));
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -610,6 +610,7 @@ QSharedPointer<Texture> 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)));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -18,14 +18,15 @@
|
|||
|
||||
#include <QDebug>
|
||||
#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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -174,7 +174,7 @@ public:
|
|||
}
|
||||
|
||||
bool operator<=(const Iterator& rhs) {
|
||||
return age() < rhs.age();
|
||||
return age() <= rhs.age();
|
||||
}
|
||||
|
||||
bool operator>=(const Iterator& rhs) {
|
||||
|
|
|
@ -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()));
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <QVariant>
|
||||
|
||||
namespace Setting {
|
||||
void preInit();
|
||||
void init();
|
||||
void cleanupSettings();
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue