merge from upstream

This commit is contained in:
Seth Alves 2016-12-20 08:06:01 -08:00
commit dc996bee95
179 changed files with 6145 additions and 1708 deletions

View file

@ -37,6 +37,8 @@
#include "AssignmentClient.h" #include "AssignmentClient.h"
#include "AssignmentClientLogging.h" #include "AssignmentClientLogging.h"
#include "avatars/ScriptableAvatar.h" #include "avatars/ScriptableAvatar.h"
#include <Trace.h>
#include <StatTracker.h>
const QString ASSIGNMENT_CLIENT_TARGET_NAME = "assignment-client"; const QString ASSIGNMENT_CLIENT_TARGET_NAME = "assignment-client";
const long long ASSIGNMENT_REQUEST_INTERVAL_MSECS = 1 * 1000; const long long ASSIGNMENT_REQUEST_INTERVAL_MSECS = 1 * 1000;
@ -48,6 +50,8 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
{ {
LogUtils::init(); LogUtils::init();
DependencyManager::set<tracing::Tracer>();
DependencyManager::set<StatTracker>();
DependencyManager::set<AccountManager>(); DependencyManager::set<AccountManager>();
auto scriptableAvatar = DependencyManager::set<ScriptableAvatar>(); auto scriptableAvatar = DependencyManager::set<ScriptableAvatar>();

View file

@ -845,6 +845,78 @@
} }
], ],
"columns": [
{
"name": "permissions_id",
"label": ""
},
{
"name": "id_can_connect",
"label": "Connect",
"type": "checkbox",
"editable": true,
"default": false
},
{
"name": "id_can_adjust_locks",
"label": "Lock / Unlock",
"type": "checkbox",
"editable": true,
"default": false
},
{
"name": "id_can_rez",
"label": "Rez",
"type": "checkbox",
"editable": true,
"default": false
},
{
"name": "id_can_rez_tmp",
"label": "Rez Temporary",
"type": "checkbox",
"editable": true,
"default": false
},
{
"name": "id_can_write_to_asset_server",
"label": "Write Assets",
"type": "checkbox",
"editable": true,
"default": false
},
{
"name": "id_can_connect_past_max_capacity",
"label": "Ignore Max Capacity",
"type": "checkbox",
"editable": true,
"default": false
},
{
"name": "id_can_kick",
"label": "Kick Users",
"type": "checkbox",
"editable": true,
"default": false
}
]
},
{
"name": "machine_fingerprint_permissions",
"type": "table",
"caption": "Permissions for Users with Machine Fingerprints",
"can_add_new_rows": true,
"groups": [
{
"label": "Machine Fingerprint",
"span": 1
},
{
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide Machine Fingerprint Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users with specific Machine Fingerprints can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific Machine Fingerprints can change the &ldquo;locked&rdquo; property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users with specific Machine Fingerprints can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users with specific Machine Fingerprints can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users with specific Machine Fingerprints can make changes to the domain&rsquo;s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users with specific Machine Fingerprints can connect even if the domain has reached or exceeded its maximum allowed agents.</li></ul><p>Note that permissions assigned to a specific Machine Fingerprint will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). Machine Fingerprint address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
"span": 7
}
],
"columns": [ "columns": [
{ {
"name": "permissions_id", "name": "permissions_id",

View file

@ -120,19 +120,21 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<ReceivedMessag
nodeData->setPlaceName(nodeConnection.placeName); nodeData->setPlaceName(nodeConnection.placeName);
qDebug() << "Allowed connection from node" << uuidStringWithoutCurlyBraces(node->getUUID()) qDebug() << "Allowed connection from node" << uuidStringWithoutCurlyBraces(node->getUUID())
<< "on" << message->getSenderSockAddr() << "with MAC" << nodeConnection.hardwareAddress; << "on" << message->getSenderSockAddr() << "with MAC" << nodeConnection.hardwareAddress
<< "and machine fingerprint" << nodeConnection.machineFingerprint;
// signal that we just connected a node so the DomainServer can get it a list // signal that we just connected a node so the DomainServer can get it a list
// and broadcast its presence right away // and broadcast its presence right away
emit connectedNode(node); emit connectedNode(node);
} else { } else {
qDebug() << "Refusing connection from node at" << message->getSenderSockAddr() qDebug() << "Refusing connection from node at" << message->getSenderSockAddr()
<< "with hardware address" << nodeConnection.hardwareAddress; << "with hardware address" << nodeConnection.hardwareAddress
<< "and machine fingerprint" << nodeConnection.machineFingerprint;
} }
} }
NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QString verifiedUsername, NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QString verifiedUsername, const QHostAddress& senderAddress,
const QHostAddress& senderAddress, const QString& hardwareAddress) { const QString& hardwareAddress, const QUuid& machineFingerprint) {
NodePermissions userPerms; NodePermissions userPerms;
userPerms.setAll(false); userPerms.setAll(false);
@ -155,6 +157,11 @@ NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QStrin
#ifdef WANT_DEBUG #ifdef WANT_DEBUG
qDebug() << "| user-permissions: specific MAC matches, so:" << userPerms; qDebug() << "| user-permissions: specific MAC matches, so:" << userPerms;
#endif
} else if (_server->_settingsManager.hasPermissionsForMachineFingerprint(machineFingerprint)) {
userPerms = _server->_settingsManager.getPermissionsForMachineFingerprint(machineFingerprint);
#ifdef WANT_DEBUG
qDebug(() << "| user-permissions: specific Machine Fingerprint matches, so: " << userPerms;
#endif #endif
} else if (_server->_settingsManager.hasPermissionsForIP(senderAddress)) { } else if (_server->_settingsManager.hasPermissionsForIP(senderAddress)) {
// this user comes from an IP we have in our permissions table, apply those permissions // this user comes from an IP we have in our permissions table, apply those permissions
@ -274,13 +281,15 @@ void DomainGatekeeper::updateNodePermissions() {
HifiSockAddr connectingAddr = node->getActiveSocket() ? *node->getActiveSocket() : node->getPublicSocket(); HifiSockAddr connectingAddr = node->getActiveSocket() ? *node->getActiveSocket() : node->getPublicSocket();
QString hardwareAddress; QString hardwareAddress;
QUuid machineFingerprint;
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(node->getLinkedData()); DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(node->getLinkedData());
if (nodeData) { if (nodeData) {
hardwareAddress = nodeData->getHardwareAddress(); hardwareAddress = nodeData->getHardwareAddress();
machineFingerprint = nodeData->getMachineFingerprint();
} }
userPerms = setPermissionsForUser(isLocalUser, verifiedUsername, connectingAddr.getAddress(), hardwareAddress); userPerms = setPermissionsForUser(isLocalUser, verifiedUsername, connectingAddr.getAddress(), hardwareAddress, machineFingerprint);
} }
node->setPermissions(userPerms); node->setPermissions(userPerms);
@ -334,6 +343,8 @@ SharedNodePointer DomainGatekeeper::processAssignmentConnectRequest(const NodeCo
nodeData->setWalletUUID(it->second.getWalletUUID()); nodeData->setWalletUUID(it->second.getWalletUUID());
nodeData->setNodeVersion(it->second.getNodeVersion()); nodeData->setNodeVersion(it->second.getNodeVersion());
nodeData->setHardwareAddress(nodeConnection.hardwareAddress); nodeData->setHardwareAddress(nodeConnection.hardwareAddress);
nodeData->setMachineFingerprint(nodeConnection.machineFingerprint);
nodeData->setWasAssigned(true); nodeData->setWasAssigned(true);
// cleanup the PendingAssignedNodeData for this assignment now that it's connecting // cleanup the PendingAssignedNodeData for this assignment now that it's connecting
@ -396,7 +407,7 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
} }
userPerms = setPermissionsForUser(isLocalUser, verifiedUsername, nodeConnection.senderSockAddr.getAddress(), userPerms = setPermissionsForUser(isLocalUser, verifiedUsername, nodeConnection.senderSockAddr.getAddress(),
nodeConnection.hardwareAddress); nodeConnection.hardwareAddress, nodeConnection.machineFingerprint);
if (!userPerms.can(NodePermissions::Permission::canConnectToDomain)) { if (!userPerms.can(NodePermissions::Permission::canConnectToDomain)) {
sendConnectionDeniedPacket("You lack the required permissions to connect to this domain.", sendConnectionDeniedPacket("You lack the required permissions to connect to this domain.",
@ -455,6 +466,9 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
// set the hardware address passed in the connect request // set the hardware address passed in the connect request
nodeData->setHardwareAddress(nodeConnection.hardwareAddress); nodeData->setHardwareAddress(nodeConnection.hardwareAddress);
// set the machine fingerprint passed in the connect request
nodeData->setMachineFingerprint(nodeConnection.machineFingerprint);
// also add an interpolation to DomainServerNodeData so that servers can get username in stats // also add an interpolation to DomainServerNodeData so that servers can get username in stats
nodeData->addOverrideForKey(USERNAME_UUID_REPLACEMENT_STATS_KEY, nodeData->addOverrideForKey(USERNAME_UUID_REPLACEMENT_STATS_KEY,
uuidStringWithoutCurlyBraces(newNode->getUUID()), username); uuidStringWithoutCurlyBraces(newNode->getUUID()), username);

View file

@ -107,8 +107,8 @@ private:
QSet<QString> _domainOwnerFriends; // keep track of friends of the domain owner QSet<QString> _domainOwnerFriends; // keep track of friends of the domain owner
QSet<QString> _inFlightGroupMembershipsRequests; // keep track of which we've already asked for QSet<QString> _inFlightGroupMembershipsRequests; // keep track of which we've already asked for
NodePermissions setPermissionsForUser(bool isLocalUser, QString verifiedUsername, NodePermissions setPermissionsForUser(bool isLocalUser, QString verifiedUsername, const QHostAddress& senderAddress,
const QHostAddress& senderAddress, const QString& hardwareAddress); const QString& hardwareAddress, const QUuid& machineFingerprint);
void getGroupMemberships(const QString& username); void getGroupMemberships(const QString& username);
// void getIsGroupMember(const QString& username, const QUuid groupID); // void getIsGroupMember(const QString& username, const QUuid groupID);

View file

@ -43,6 +43,8 @@
#include "DomainServerNodeData.h" #include "DomainServerNodeData.h"
#include "NodeConnectionData.h" #include "NodeConnectionData.h"
#include <Trace.h>
#include <StatTracker.h>
int const DomainServer::EXIT_CODE_REBOOT = 234923; int const DomainServer::EXIT_CODE_REBOOT = 234923;
@ -73,6 +75,9 @@ DomainServer::DomainServer(int argc, char* argv[]) :
{ {
parseCommandLine(); parseCommandLine();
DependencyManager::set<tracing::Tracer>();
DependencyManager::set<StatTracker>();
LogUtils::init(); LogUtils::init();
Setting::init(); Setting::init();
@ -527,6 +532,7 @@ void DomainServer::setupNodeListAndAssignments() {
// NodeList won't be available to the settings manager when it is created, so call registerListener here // 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::DomainSettingsRequest, &_settingsManager, "processSettingsRequestPacket");
packetReceiver.registerListener(PacketType::NodeKickRequest, &_settingsManager, "processNodeKickRequestPacket"); packetReceiver.registerListener(PacketType::NodeKickRequest, &_settingsManager, "processNodeKickRequestPacket");
packetReceiver.registerListener(PacketType::UsernameFromIDRequest, &_settingsManager, "processUsernameFromIDRequestPacket");
// register the gatekeeper for the packets it needs to receive // register the gatekeeper for the packets it needs to receive
packetReceiver.registerListener(PacketType::DomainConnectRequest, &_gatekeeper, "processConnectRequestPacket"); packetReceiver.registerListener(PacketType::DomainConnectRequest, &_gatekeeper, "processConnectRequestPacket");

View file

@ -56,7 +56,10 @@ public:
void setHardwareAddress(const QString& hardwareAddress) { _hardwareAddress = hardwareAddress; } void setHardwareAddress(const QString& hardwareAddress) { _hardwareAddress = hardwareAddress; }
const QString& getHardwareAddress() { return _hardwareAddress; } const QString& getHardwareAddress() { return _hardwareAddress; }
void setMachineFingerprint(const QUuid& machineFingerprint) { _machineFingerprint = machineFingerprint; }
const QUuid& getMachineFingerprint() { return _machineFingerprint; }
void addOverrideForKey(const QString& key, const QString& value, const QString& overrideValue); void addOverrideForKey(const QString& key, const QString& value, const QString& overrideValue);
void removeOverrideForKey(const QString& key, const QString& value); void removeOverrideForKey(const QString& key, const QString& value);
@ -85,6 +88,7 @@ private:
NodeSet _nodeInterestSet; NodeSet _nodeInterestSet;
QString _nodeVersion; QString _nodeVersion;
QString _hardwareAddress; QString _hardwareAddress;
QUuid _machineFingerprint;
QString _placeName; QString _placeName;

View file

@ -444,6 +444,9 @@ void DomainServerSettingsManager::packPermissions() {
// save settings for MAC addresses // save settings for MAC addresses
packPermissionsForMap("permissions", _macPermissions, MAC_PERMISSIONS_KEYPATH); packPermissionsForMap("permissions", _macPermissions, MAC_PERMISSIONS_KEYPATH);
// save settings for Machine Fingerprint
packPermissionsForMap("permissions", _machineFingerprintPermissions, MACHINE_FINGERPRINT_PERMISSIONS_KEYPATH);
// save settings for groups // save settings for groups
packPermissionsForMap("permissions", _groupPermissions, GROUP_PERMISSIONS_KEYPATH); packPermissionsForMap("permissions", _groupPermissions, GROUP_PERMISSIONS_KEYPATH);
@ -522,6 +525,18 @@ void DomainServerSettingsManager::unpackPermissions() {
} }
}); });
needPack |= unpackPermissionsForKeypath(MACHINE_FINGERPRINT_PERMISSIONS_KEYPATH, &_machineFingerprintPermissions,
[&](NodePermissionsPointer perms){
// make sure that this permission row has valid machine fingerprint
if (QUuid(perms->getKey().first) == QUuid()) {
_machineFingerprintPermissions.remove(perms->getKey());
// we removed a row, so we'll need a re-pack
needPack = true;
}
});
needPack |= unpackPermissionsForKeypath(GROUP_PERMISSIONS_KEYPATH, &_groupPermissions, needPack |= unpackPermissionsForKeypath(GROUP_PERMISSIONS_KEYPATH, &_groupPermissions,
[&](NodePermissionsPointer perms){ [&](NodePermissionsPointer perms){
@ -575,7 +590,9 @@ void DomainServerSettingsManager::unpackPermissions() {
QList<QHash<NodePermissionsKey, NodePermissionsPointer>> permissionsSets; QList<QHash<NodePermissionsKey, NodePermissionsPointer>> permissionsSets;
permissionsSets << _standardAgentPermissions.get() << _agentPermissions.get() permissionsSets << _standardAgentPermissions.get() << _agentPermissions.get()
<< _groupPermissions.get() << _groupForbiddens.get() << _groupPermissions.get() << _groupForbiddens.get()
<< _ipPermissions.get() << _macPermissions.get(); << _ipPermissions.get() << _macPermissions.get()
<< _machineFingerprintPermissions.get();
foreach (auto permissionSet, permissionsSets) { foreach (auto permissionSet, permissionsSets) {
QHashIterator<NodePermissionsKey, NodePermissionsPointer> i(permissionSet); QHashIterator<NodePermissionsKey, NodePermissionsPointer> i(permissionSet);
while (i.hasNext()) { while (i.hasNext()) {
@ -707,9 +724,10 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointer<Re
ipPermissions->clear(NodePermissions::Permission::canConnectToDomain); ipPermissions->clear(NodePermissions::Permission::canConnectToDomain);
} }
// potentially remove connect permissions for the MAC address // potentially remove connect permissions for the MAC address and machine fingerprint
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(matchingNode->getLinkedData()); DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(matchingNode->getLinkedData());
if (nodeData) { if (nodeData) {
// mac address first
NodePermissionsKey macAddressKey(nodeData->getHardwareAddress(), 0); NodePermissionsKey macAddressKey(nodeData->getHardwareAddress(), 0);
bool hadMACPermissions = hasPermissionsForMAC(nodeData->getHardwareAddress()); bool hadMACPermissions = hasPermissionsForMAC(nodeData->getHardwareAddress());
@ -721,6 +739,18 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointer<Re
macPermissions->clear(NodePermissions::Permission::canConnectToDomain); macPermissions->clear(NodePermissions::Permission::canConnectToDomain);
} }
// now for machine fingerprint
NodePermissionsKey machineFingerprintKey(nodeData->getMachineFingerprint().toString(), 0);
bool hadFingerprintPermissions = hasPermissionsForMachineFingerprint(nodeData->getMachineFingerprint());
auto fingerprintPermissions = _machineFingerprintPermissions[machineFingerprintKey];
if (!hadFingerprintPermissions || fingerprintPermissions->can(NodePermissions::Permission::canConnectToDomain)) {
newPermissions = true;
fingerprintPermissions->clear(NodePermissions::Permission::canConnectToDomain);
}
} }
} }
@ -748,6 +778,45 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointer<Re
} }
} }
// This function processes the "Get Username from ID" request.
void DomainServerSettingsManager::processUsernameFromIDRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
// Before we do any processing on this packet, make sure it comes from a node that is allowed to kick (is an admin)
if (sendingNode->getCanKick()) {
// From the packet, pull the UUID we're identifying
QUuid nodeUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
if (!nodeUUID.isNull()) {
// First, make sure we actually have a node with this UUID
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
auto matchingNode = limitedNodeList->nodeWithUUID(nodeUUID);
// If we do have a matching node...
if (matchingNode) {
// It's time to figure out the username
QString verifiedUsername = matchingNode->getPermissions().getVerifiedUserName();
// Setup the packet
auto usernameFromIDReplyPacket = NLPacket::create(PacketType::UsernameFromIDReply);
usernameFromIDReplyPacket->write(nodeUUID.toRfc4122());
usernameFromIDReplyPacket->writeString(verifiedUsername);
qDebug() << "Sending username" << verifiedUsername << "associated with node" << nodeUUID;
// Ship it!
limitedNodeList->sendPacket(std::move(usernameFromIDReplyPacket), *sendingNode);
} else {
qWarning() << "Node username request received for unknown node. Refusing to process.";
}
} else {
qWarning() << "Node username request received for invalid node ID. Refusing to process.";
}
} else {
qWarning() << "Refusing to process a username request packet from node" << uuidStringWithoutCurlyBraces(sendingNode->getUUID())
<< "that does not have kick permissions.";
}
}
QStringList DomainServerSettingsManager::getAllNames() const { QStringList DomainServerSettingsManager::getAllNames() const {
QStringList result; QStringList result;
foreach (auto key, _agentPermissions.keys()) { foreach (auto key, _agentPermissions.keys()) {
@ -795,6 +864,16 @@ NodePermissions DomainServerSettingsManager::getPermissionsForMAC(const QString&
return nullPermissions; return nullPermissions;
} }
NodePermissions DomainServerSettingsManager::getPermissionsForMachineFingerprint(const QUuid& machineFingerprint) const {
NodePermissionsKey fingerprintKey = NodePermissionsKey(machineFingerprint.toString(), 0);
if (_machineFingerprintPermissions.contains(fingerprintKey)) {
return *(_machineFingerprintPermissions[fingerprintKey].get());
}
NodePermissions nullPermissions;
nullPermissions.setAll(false);
return nullPermissions;
}
NodePermissions DomainServerSettingsManager::getPermissionsForGroup(const QString& groupName, QUuid rankID) const { NodePermissions DomainServerSettingsManager::getPermissionsForGroup(const QString& groupName, QUuid rankID) const {
NodePermissionsKey groupRankKey = NodePermissionsKey(groupName, rankID); NodePermissionsKey groupRankKey = NodePermissionsKey(groupName, rankID);
if (_groupPermissions.contains(groupRankKey)) { if (_groupPermissions.contains(groupRankKey)) {

View file

@ -32,6 +32,7 @@ const QString AGENT_STANDARD_PERMISSIONS_KEYPATH = "security.standard_permission
const QString AGENT_PERMISSIONS_KEYPATH = "security.permissions"; const QString AGENT_PERMISSIONS_KEYPATH = "security.permissions";
const QString IP_PERMISSIONS_KEYPATH = "security.ip_permissions"; const QString IP_PERMISSIONS_KEYPATH = "security.ip_permissions";
const QString MAC_PERMISSIONS_KEYPATH = "security.mac_permissions"; const QString MAC_PERMISSIONS_KEYPATH = "security.mac_permissions";
const QString MACHINE_FINGERPRINT_PERMISSIONS_KEYPATH = "security.machine_fingerprint_permissions";
const QString GROUP_PERMISSIONS_KEYPATH = "security.group_permissions"; const QString GROUP_PERMISSIONS_KEYPATH = "security.group_permissions";
const QString GROUP_FORBIDDENS_KEYPATH = "security.group_forbiddens"; const QString GROUP_FORBIDDENS_KEYPATH = "security.group_forbiddens";
@ -70,6 +71,10 @@ public:
bool hasPermissionsForMAC(const QString& macAddress) const { return _macPermissions.contains(macAddress, 0); } bool hasPermissionsForMAC(const QString& macAddress) const { return _macPermissions.contains(macAddress, 0); }
NodePermissions getPermissionsForMAC(const QString& macAddress) const; NodePermissions getPermissionsForMAC(const QString& macAddress) const;
// these give access to permissions for specific machine fingerprints from the domain-server settings page
bool hasPermissionsForMachineFingerprint(const QUuid& machineFingerprint) { return _machineFingerprintPermissions.contains(machineFingerprint.toString(), 0); }
NodePermissions getPermissionsForMachineFingerprint(const QUuid& machineFingerprint) const;
// these give access to permissions for specific groups from the domain-server settings page // these give access to permissions for specific groups from the domain-server settings page
bool havePermissionsForGroup(const QString& groupName, QUuid rankID) const { bool havePermissionsForGroup(const QString& groupName, QUuid rankID) const {
return _groupPermissions.contains(groupName, rankID); return _groupPermissions.contains(groupName, rankID);
@ -113,6 +118,7 @@ public slots:
private slots: private slots:
void processSettingsRequestPacket(QSharedPointer<ReceivedMessage> message); void processSettingsRequestPacket(QSharedPointer<ReceivedMessage> message);
void processNodeKickRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode); void processNodeKickRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
void processUsernameFromIDRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
private: private:
QStringList _argumentList; QStringList _argumentList;
@ -151,6 +157,7 @@ private:
NodePermissionsMap _ipPermissions; // permissions granted by node IP address NodePermissionsMap _ipPermissions; // permissions granted by node IP address
NodePermissionsMap _macPermissions; // permissions granted by node MAC address NodePermissionsMap _macPermissions; // permissions granted by node MAC address
NodePermissionsMap _machineFingerprintPermissions; // permissions granted by Machine Fingerprint
NodePermissionsMap _groupPermissions; // permissions granted by membership to specific groups NodePermissionsMap _groupPermissions; // permissions granted by membership to specific groups
NodePermissionsMap _groupForbiddens; // permissions denied due to membership in a specific group NodePermissionsMap _groupForbiddens; // permissions denied due to membership in a specific group

View file

@ -32,6 +32,9 @@ NodeConnectionData NodeConnectionData::fromDataStream(QDataStream& dataStream, c
// read the hardware address sent by the client // read the hardware address sent by the client
dataStream >> newHeader.hardwareAddress; dataStream >> newHeader.hardwareAddress;
// now the machine fingerprint
dataStream >> newHeader.machineFingerprint;
} }
dataStream >> newHeader.nodeType dataStream >> newHeader.nodeType

View file

@ -29,6 +29,7 @@ public:
QList<NodeType_t> interestList; QList<NodeType_t> interestList;
QString placeName; QString placeName;
QString hardwareAddress; QString hardwareAddress;
QUuid machineFingerprint;
QByteArray protocolVersion; QByteArray protocolVersion;
}; };

View file

@ -17,7 +17,10 @@
var KEYBOARD_HEIGHT = 200; var KEYBOARD_HEIGHT = 200;
function shouldRaiseKeyboard() { function shouldRaiseKeyboard() {
if (document.activeElement.nodeName === "INPUT" || document.activeElement.nodeName === "TEXTAREA") { var nodeName = document.activeElement.nodeName;
var nodeType = document.activeElement.type;
if (nodeName === "INPUT" && (nodeType === "text" || nodeType === "number" || nodeType === "password")
|| document.activeElement.nodeName === "TEXTAREA") {
return true; return true;
} else { } else {
// check for contenteditable attribute // check for contenteditable attribute

View file

@ -23,6 +23,8 @@ ScrollingWindow {
property alias eventBridge: eventBridgeWrapper.eventBridge property alias eventBridge: eventBridgeWrapper.eventBridge
signal loadingChanged(int status)
x: 100 x: 100
y: 100 y: 100
@ -44,6 +46,10 @@ ScrollingWindow {
hidePermissionsBar(); hidePermissionsBar();
} }
function setAutoAdd(auto) {
desktop.setAutoAdd(auto);
}
Item { Item {
id:item id:item
width: pane.contentWidth width: pane.contentWidth
@ -197,7 +203,7 @@ ScrollingWindow {
WebView { WebView {
id: webview id: webview
url: "https://highfidelity.com" url: "https://highfidelity.com/"
property alias eventBridgeWrapper: eventBridgeWrapper property alias eventBridgeWrapper: eventBridgeWrapper
@ -243,6 +249,7 @@ ScrollingWindow {
if (loadRequest.status === WebEngineView.LoadSucceededStatus) { if (loadRequest.status === WebEngineView.LoadSucceededStatus) {
addressBar.text = loadRequest.url addressBar.text = loadRequest.url
} }
root.loadingChanged(loadRequest.status);
} }
onIconChanged: { onIconChanged: {
@ -254,7 +261,7 @@ ScrollingWindow {
} }
Component.onCompleted: { Component.onCompleted: {
desktop.initWebviewProfileHandlers(webview.profile) desktop.initWebviewProfileHandlers(webview.profile);
} }
} }

View file

@ -1,167 +0,0 @@
//
// Marketplaces.qml
//
// Created by Elisa Lupin-Jimenez on 3 Aug 2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtWebChannel 1.0
import QtWebEngine 1.1
import QtWebSockets 1.0
import "qrc:///qtwebchannel/qwebchannel.js" as WebChannel
import "controls"
import "controls-uit" as Controls
import "styles"
import "styles-uit"
Rectangle {
HifiConstants { id: hifi }
id: marketplace
anchors.fill: parent
property var marketplacesUrl: "../../scripts/system/html/marketplaces.html"
property int statusBarHeight: 50
property int statusMargin: 50
property string standardMessage: "Check out other marketplaces."
property string claraMessage: "Choose a model and click Download -> Autodesk FBX."
property string claraError: "High Fidelity only supports Autodesk FBX models."
Controls.BaseWebView {
id: webview
url: marketplacesUrl
anchors.top: marketplace.top
width: parent.width
height: parent.height - statusBarHeight
focus: true
Timer {
id: zipTimer
running: false
repeat: false
interval: 1500
property var handler;
onTriggered: handler();
}
Timer {
id: alertTimer
running: false
repeat: false
interval: 9000
property var handler;
onTriggered: handler();
}
property var autoCancel: 'var element = $("a.btn.cancel");
element.click();'
property var simpleDownload: 'var element = $("a.download-file");
element.removeClass("download-file");
element.removeAttr("download");'
function displayErrorStatus() {
alertTimer.handler = function() {
statusLabel.text = claraMessage;
statusBar.color = hifi.colors.blueHighlight;
statusIcon.text = hifi.glyphs.info;
}
alertTimer.start();
}
property var notFbxHandler: 'var element = $("a.btn.btn-primary.viewer-button.download-file")
element.click();'
// this code is for removing other file types from Clara.io's download options
//property var checkFileType: "$('[data-extension]:not([data-extension=\"fbx\"])').parent().remove()"
onLinkHovered: {
desktop.currentUrl = hoveredUrl;
//runJavaScript(checkFileType, function(){console.log("Remove filetypes JS injection");});
if (File.isZippedFbx(desktop.currentUrl)) {
runJavaScript(simpleDownload, function(){console.log("Download JS injection");});
return;
}
if (File.isZipped(desktop.currentUrl)) {
statusLabel.text = claraError;
statusBar.color = hifi.colors.redHighlight;
statusIcon.text = hifi.glyphs.alert;
runJavaScript(notFbxHandler, displayErrorStatus());
}
}
onLoadingChanged: {
if (File.isClaraLink(webview.url)) {
statusLabel.text = claraMessage;
} else {
statusLabel.text = standardMessage;
}
statusBar.color = hifi.colors.blueHighlight;
statusIcon.text = hifi.glyphs.info;
}
onNewViewRequested: {
var component = Qt.createComponent("Browser.qml");
var newWindow = component.createObject(desktop);
request.openIn(newWindow.webView);
if (File.isZippedFbx(desktop.currentUrl)) {
runJavaScript(autoCancel);
zipTimer.handler = function() {
newWindow.destroy();
}
zipTimer.start();
}
}
}
Rectangle {
id: statusBar
anchors.top: webview.bottom
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
color: hifi.colors.blueHighlight
Controls.Button {
id: switchMarketView
anchors.right: parent.right
anchors.rightMargin: statusMargin
anchors.verticalCenter: parent.verticalCenter
width: 150
text: "See all markets"
onClicked: {
webview.url = "../../scripts/system/html/marketplaces.html";
statusLabel.text = standardMessage;
}
}
Controls.Label {
id: statusLabel
anchors.verticalCenter: switchMarketView.verticalCenter
anchors.left: parent.left
anchors.leftMargin: statusMargin
color: hifi.colors.white
text: standardMessage
size: 18
}
HiFiGlyphs {
id: statusIcon
anchors.right: statusLabel.left
anchors.verticalCenter: statusLabel.verticalCenter
text: hifi.glyphs.info
color: hifi.colors.white
size: hifi.fontSizes.tableHeadingIcon
}
}
}

View file

@ -27,6 +27,7 @@ Windows.ScrollingWindow {
destroyOnCloseButton: false destroyOnCloseButton: false
property alias source: webview.url property alias source: webview.url
property alias eventBridge: eventBridgeWrapper.eventBridge; property alias eventBridge: eventBridgeWrapper.eventBridge;
property alias scriptUrl: webview.userScriptUrl
QtObject { QtObject {
id: eventBridgeWrapper id: eventBridgeWrapper
@ -71,6 +72,8 @@ Windows.ScrollingWindow {
focus: true focus: true
webChannel.registeredObjects: [eventBridgeWrapper] webChannel.registeredObjects: [eventBridgeWrapper]
property string userScriptUrl: ""
// Create a global EventBridge object for raiseAndLowerKeyboard. // Create a global EventBridge object for raiseAndLowerKeyboard.
WebEngineScript { WebEngineScript {
id: createGlobalEventBridge id: createGlobalEventBridge
@ -87,7 +90,25 @@ Windows.ScrollingWindow {
worldId: WebEngineScript.MainWorld worldId: WebEngineScript.MainWorld
} }
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ] // User script.
WebEngineScript {
id: userScript
sourceUrl: webview.userScriptUrl
injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished.
worldId: WebEngineScript.MainWorld
}
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
function onWebEventReceived(event) {
if (event.slice(0, 17) === "CLARA.IO DOWNLOAD") {
ApplicationInterface.addAssetToWorldFromURL(event.slice(18));
}
}
Component.onCompleted: {
eventBridge.webEventReceived.connect(onWebEventReceived);
}
} }
} }
} }

View file

@ -2,9 +2,8 @@
import QtQuick 2.3 import QtQuick 2.3
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import QtWebChannel 1.0 import QtWebChannel 1.0
import QtWebEngine 1.1 import QtWebEngine 1.2
import QtWebSockets 1.0 import QtWebSockets 1.0
import "qrc:///qtwebchannel/qwebchannel.js" as WebChannel
import "windows" as Windows import "windows" as Windows
import "controls" import "controls"
@ -23,10 +22,22 @@ Windows.Window {
// Don't destroy on close... otherwise the JS/C++ will have a dangling pointer // Don't destroy on close... otherwise the JS/C++ will have a dangling pointer
destroyOnCloseButton: false destroyOnCloseButton: false
property var source; property var source;
property var eventBridge;
property var component; property var component;
property var dynamicContent; property var dynamicContent;
// Keyboard control properties in case needed by QML content.
property bool keyboardEnabled: false
property bool keyboardRaised: false
property bool punctuationMode: false
// JavaScript event bridge object in case QML content includes Web content.
property alias eventBridge: eventBridgeWrapper.eventBridge;
QtObject {
id: eventBridgeWrapper
WebChannel.id: "eventBridgeWrapper"
property var eventBridge;
}
onSourceChanged: { onSourceChanged: {
if (dynamicContent) { if (dynamicContent) {
@ -72,7 +83,6 @@ Windows.Window {
} }
} }
Item { Item {
id: contentHolder id: contentHolder
anchors.fill: parent anchors.fill: parent

View file

@ -0,0 +1,27 @@
//
// Web3DOverlay.qml
//
// Created by David Rowe on 16 Dec 2016.
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import "controls" as Controls
Controls.WebView {
function onWebEventReceived(event) {
if (event.slice(0, 17) === "CLARA.IO DOWNLOAD") {
ApplicationInterface.addAssetToWorldFromURL(event.slice(18));
}
}
Component.onCompleted: {
eventBridge.webEventReceived.connect(onWebEventReceived);
}
}

View file

@ -6,6 +6,7 @@ import HFWebEngineProfile 1.0
Item { Item {
property alias url: root.url property alias url: root.url
property alias scriptURL: root.userScriptUrl
property alias eventBridge: eventBridgeWrapper.eventBridge property alias eventBridge: eventBridgeWrapper.eventBridge
property bool keyboardEnabled: true // FIXME - Keyboard HMD only: Default to false property bool keyboardEnabled: true // FIXME - Keyboard HMD only: Default to false
property bool keyboardRaised: false property bool keyboardRaised: false
@ -38,6 +39,8 @@ Item {
storageName: "qmlWebEngine" storageName: "qmlWebEngine"
} }
property string userScriptUrl: ""
// creates a global EventBridge object. // creates a global EventBridge object.
WebEngineScript { WebEngineScript {
id: createGlobalEventBridge id: createGlobalEventBridge
@ -54,7 +57,15 @@ Item {
worldId: WebEngineScript.MainWorld worldId: WebEngineScript.MainWorld
} }
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ] // User script.
WebEngineScript {
id: userScript
sourceUrl: root.userScriptUrl
injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished.
worldId: WebEngineScript.MainWorld
}
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
property string newUrl: "" property string newUrl: ""

View file

@ -100,21 +100,25 @@ OriginalDesktop.Desktop {
// Accept a download through the webview // Accept a download through the webview
property bool webViewProfileSetup: false property bool webViewProfileSetup: false
property string currentUrl: "" property string currentUrl: ""
property string downloadUrl: ""
property string adaptedPath: "" property string adaptedPath: ""
property string tempDir: "" property string tempDir: ""
property bool autoAdd: false
function initWebviewProfileHandlers(profile) { function initWebviewProfileHandlers(profile) {
console.log("The webview url in desktop is: " + currentUrl); console.log("The webview url in desktop is: " + currentUrl);
downloadUrl = currentUrl;
if (webViewProfileSetup) return; if (webViewProfileSetup) return;
webViewProfileSetup = true; webViewProfileSetup = true;
profile.downloadRequested.connect(function(download){ profile.downloadRequested.connect(function(download){
console.log("Download start: " + download.state); console.log("Download start: " + download.state);
adaptedPath = File.convertUrlToPath(currentUrl); adaptedPath = File.convertUrlToPath(downloadUrl);
tempDir = File.getTempDir(); tempDir = File.getTempDir();
console.log("Temp dir created: " + tempDir); console.log("Temp dir created: " + tempDir);
download.path = tempDir + "/" + adaptedPath; download.path = tempDir + "/" + adaptedPath;
console.log("Path where object should download: " + download.path); console.log("Path where object should download: " + download.path);
console.log("Auto add: " + autoAdd);
download.accept(); download.accept();
if (download.state === WebEngineDownloadItem.DownloadInterrupted) { if (download.state === WebEngineDownloadItem.DownloadInterrupted) {
console.log("download failed to complete"); console.log("download failed to complete");
@ -123,13 +127,18 @@ OriginalDesktop.Desktop {
profile.downloadFinished.connect(function(download){ profile.downloadFinished.connect(function(download){
if (download.state === WebEngineDownloadItem.DownloadCompleted) { if (download.state === WebEngineDownloadItem.DownloadCompleted) {
File.runUnzip(download.path, currentUrl); File.runUnzip(download.path, downloadUrl, autoAdd);
} else { } else {
console.log("The download was corrupted, state: " + download.state); console.log("The download was corrupted, state: " + download.state);
} }
autoAdd = false;
}) })
} }
function setAutoAdd(auto) {
autoAdd = auto;
}
// Create or fetch a toolbar with the given name // Create or fetch a toolbar with the given name
function getToolbar(name) { function getToolbar(name) {
var result = toolbars[name]; var result = toolbars[name];

View file

@ -0,0 +1,36 @@
//
// NameCard.qml
// qml/hifi
//
// Created by Howard Stearns on 12/9/2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0
import QtQuick 2.5
import "../styles-uit"
Column {
property string displayName: "";
property string userName: "";
property int displayTextHeight: 18;
property int usernameTextHeight: 12;
RalewaySemiBold {
text: parent.displayName;
size: parent.displayTextHeight;
elide: Text.ElideRight;
width: parent.width;
}
RalewayLight {
visible: parent.displayName;
text: parent.userName;
size: parent.usernameTextHeight;
elide: Text.ElideRight;
width: parent.width;
}
}

View file

@ -0,0 +1,225 @@
//
// Pal.qml
// qml/hifi
//
// People Action List
//
// Created by Howard Stearns on 12/12/2016
// Copyright 2016 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
//
/* TODO:
prototype:
- only show kick/mute when canKick
- margins everywhere
- column head centering
- column head font
- proper button .svg on toolbar
mvp:
- Show all participants, including ignored, and populate initial ignore/mute status.
- If name is elided, hover should scroll name left so the full name can be read.
*/
import QtQuick 2.5
import QtQuick.Controls 1.4
Rectangle {
id: pal;
property int keepFromHorizontalScroll: 1;
width: parent.width - keepFromHorizontalScroll;
height: parent.height;
property int nameWidth: width/2;
property int actionWidth: nameWidth / (table.columnCount - 1);
property int rowHeight: 50;
property var userData: [];
property var myData: ({displayName: "", userName: ""}); // valid dummy until set
property bool iAmAdmin: false;
function findSessionIndex(sessionId, optionalData) { // no findIndex in .qml
var i, data = optionalData || userData, length = data.length;
for (var i = 0; i < length; i++) {
if (data[i].sessionId === sessionId) {
return i;
}
}
return -1;
}
function fromScript(message) {
switch (message.method) {
case 'users':
var data = message.params;
var myIndex = findSessionIndex('', data);
iAmAdmin = Users.canKick;
myData = data[myIndex];
data.splice(myIndex, 1);
userData = data;
sortModel();
break;
case 'select':
var sessionId = message.params[0];
var selected = message.params[1];
var userIndex = findSessionIndex(sessionId);
if (selected) {
table.selection.clear(); // for now, no multi-select
table.selection.select(userIndex);
} else {
table.selection.deselect(userIndex);
}
break;
// Received an "updateUsername()" request from the JS
case 'updateUsername':
// The User ID (UUID) is the first parameter in the message.
var userId = message.params[0];
// The text that goes in the userName field is the second parameter in the message.
var userName = message.params[1];
// If the userId is empty, we're updating "myData".
if (!userId) {
myData.userName = userName;
myCard.userName = userName; // Defensive programming
} else {
// Get the index in userModel and userData associated with the passed UUID
var userIndex = findSessionIndex(userId);
// Set the userName appropriately
userModel.get(userIndex).userName = userName;
userData[userIndex].userName = userName; // Defensive programming
}
break;
default:
console.log('Unrecognized message:', JSON.stringify(message));
}
}
ListModel {
id: userModel
}
function sortModel() {
var sortProperty = table.getColumn(table.sortIndicatorColumn).role;
var before = (table.sortIndicatorOrder === Qt.AscendingOrder) ? -1 : 1;
var after = -1 * before;
userData.sort(function (a, b) {
var aValue = a[sortProperty].toString().toLowerCase(), bValue = b[sortProperty].toString().toLowerCase();
switch (true) {
case (aValue < bValue): return before;
case (aValue > bValue): return after;
default: return 0;
}
});
table.selection.clear();
userModel.clear();
var userIndex = 0;
userData.forEach(function (datum) {
function init(property) {
if (datum[property] === undefined) {
datum[property] = false;
}
}
['ignore', 'spacer', 'mute', 'kick'].forEach(init);
datum.userIndex = userIndex++;
userModel.append(datum);
});
}
signal sendToScript(var message);
function noticeSelection() {
var userIds = [];
table.selection.forEach(function (userIndex) {
userIds.push(userData[userIndex].sessionId);
});
pal.sendToScript({method: 'selected', params: userIds});
}
Connections {
target: table.selection
onSelectionChanged: pal.noticeSelection()
}
Column {
NameCard {
id: myCard;
width: nameWidth;
displayName: myData.displayName;
userName: myData.userName;
}
TableView {
id: table;
TableViewColumn {
role: "displayName";
title: "Name";
width: nameWidth
}
TableViewColumn {
role: "ignore";
title: "Ignore"
width: actionWidth
}
TableViewColumn {
title: "";
width: actionWidth
}
TableViewColumn {
visible: iAmAdmin;
role: "mute";
title: "Mute";
width: actionWidth
}
TableViewColumn {
visible: iAmAdmin;
role: "kick";
title: "Ban"
width: actionWidth
}
model: userModel;
rowDelegate: Rectangle { // The only way I know to specify a row height.
height: rowHeight;
// The rest of this is cargo-culted to restore the default styling
SystemPalette {
id: myPalette;
colorGroup: SystemPalette.Active
}
color: {
var baseColor = styleData.alternate?myPalette.alternateBase:myPalette.base
return styleData.selected?myPalette.highlight:baseColor
}
}
itemDelegate: Item {
id: itemCell;
property bool isCheckBox: typeof(styleData.value) === 'boolean';
NameCard {
id: nameCard;
visible: !isCheckBox;
width: nameWidth;
displayName: styleData.value;
userName: model.userName;
}
Rectangle {
radius: itemCell.height / 4;
visible: isCheckBox;
color: styleData.value ? "green" : "red";
anchors.fill: parent;
MouseArea {
anchors.fill: parent;
acceptedButtons: Qt.LeftButton;
hoverEnabled: true;
onClicked: {
var newValue = !model[styleData.role];
var datum = userData[model.userIndex];
datum[styleData.role] = model[styleData.role] = newValue;
Users[styleData.role](model.sessionId);
// Just for now, while we cannot undo things:
userData.splice(model.userIndex, 1);
sortModel();
}
}
}
}
height: pal.height - myCard.height;
width: pal.width;
sortIndicatorVisible: true;
onSortIndicatorColumnChanged: sortModel();
onSortIndicatorOrderChanged: sortModel();
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -18,6 +18,7 @@
#include <QtCore/QPointer> #include <QtCore/QPointer>
#include <QtCore/QSet> #include <QtCore/QSet>
#include <QtCore/QStringList> #include <QtCore/QStringList>
#include <QtQuick/QQuickItem>
#include <QtGui/QImage> #include <QtGui/QImage>
@ -28,6 +29,7 @@
#include <AbstractViewStateInterface.h> #include <AbstractViewStateInterface.h>
#include <EntityEditPacketSender.h> #include <EntityEditPacketSender.h>
#include <EntityTreeRenderer.h> #include <EntityTreeRenderer.h>
#include <FileScriptingInterface.h>
#include <input-plugins/KeyboardMouseDevice.h> #include <input-plugins/KeyboardMouseDevice.h>
#include <input-plugins/TouchscreenDevice.h> #include <input-plugins/TouchscreenDevice.h>
#include <OctreeQuery.h> #include <OctreeQuery.h>
@ -310,6 +312,20 @@ public slots:
void toggleRunningScriptsWidget() const; void toggleRunningScriptsWidget() const;
Q_INVOKABLE void showAssetServerWidget(QString filePath = ""); Q_INVOKABLE void showAssetServerWidget(QString filePath = "");
// FIXME: Move addAssetToWorld* methods to own class?
void addAssetToWorldFromURL(QString url);
void addAssetToWorldFromURLRequestFinished();
void addAssetToWorld(QString filePath);
void addAssetToWorldUnzipFailure(QString filePath);
void addAssetToWorldWithNewMapping(QString filePath, QString mapping, int copy);
void addAssetToWorldUpload(QString filePath, QString mapping);
void addAssetToWorldSetMapping(QString filePath, QString mapping, QString hash);
void addAssetToWorldAddEntity(QString filePath, QString mapping);
void handleUnzip(QString sourceFile, QString destinationFile, bool autoAdd);
FileScriptingInterface* getFileDownloadInterface() { return _fileDownload; }
void handleLocalServerConnection() const; void handleLocalServerConnection() const;
void readArgumentsFromLocalSocket() const; void readArgumentsFromLocalSocket() const;
@ -352,10 +368,17 @@ public slots:
static void runTests(); static void runTests();
void setKeyboardFocusHighlight(const glm::vec3& position, const glm::quat& rotation, const glm::vec3& dimensions);
QUuid getKeyboardFocusEntity() const; // thread-safe QUuid getKeyboardFocusEntity() const; // thread-safe
void setKeyboardFocusEntity(QUuid id); void setKeyboardFocusEntity(QUuid id);
void setKeyboardFocusEntity(EntityItemID entityItemID); void setKeyboardFocusEntity(EntityItemID entityItemID);
unsigned int getKeyboardFocusOverlay();
void setKeyboardFocusOverlay(unsigned int overlayID);
void addAssetToWorldMessageClose();
private slots: private slots:
void showDesktop(); void showDesktop();
void clearDomainOctreeDetails(); void clearDomainOctreeDetails();
@ -392,6 +415,12 @@ private slots:
void updateDisplayMode(); void updateDisplayMode();
void domainConnectionRefused(const QString& reasonMessage, int reason, const QString& extraInfo); void domainConnectionRefused(const QString& reasonMessage, int reason, const QString& extraInfo);
void addAssetToWorldCheckModelSize();
void onAssetToWorldMessageBoxClosed();
void addAssetToWorldInfoTimeout();
void addAssetToWorldErrorTimeout();
private: private:
static void initDisplay(); static void initDisplay();
void init(); void init();
@ -568,7 +597,8 @@ private:
DialogsManagerScriptingInterface* _dialogsManagerScriptingInterface = new DialogsManagerScriptingInterface(); DialogsManagerScriptingInterface* _dialogsManagerScriptingInterface = new DialogsManagerScriptingInterface();
ThreadSafeValueCache<EntityItemID> _keyboardFocusedItem; ThreadSafeValueCache<EntityItemID> _keyboardFocusedEntity;
ThreadSafeValueCache<unsigned int> _keyboardFocusedOverlay;
quint64 _lastAcceptedKeyPress = 0; quint64 _lastAcceptedKeyPress = 0;
bool _isForeground = true; // starts out assumed to be in foreground bool _isForeground = true; // starts out assumed to be in foreground
bool _inPaint = false; bool _inPaint = false;
@ -610,6 +640,22 @@ private:
model::SkyboxPointer _defaultSkybox { new ProceduralSkybox() } ; model::SkyboxPointer _defaultSkybox { new ProceduralSkybox() } ;
gpu::TexturePointer _defaultSkyboxTexture; gpu::TexturePointer _defaultSkyboxTexture;
gpu::TexturePointer _defaultSkyboxAmbientTexture; gpu::TexturePointer _defaultSkyboxAmbientTexture;
QTimer _addAssetToWorldResizeTimer;
QHash<QUuid, int> _addAssetToWorldResizeList;
void addAssetToWorldInfo(QString modelName, QString infoText);
void addAssetToWorldInfoClear(QString modelName);
void addAssetToWorldInfoDone(QString modelName);
void addAssetToWorldError(QString modelName, QString errorText);
QQuickItem* _addAssetToWorldMessageBox{ nullptr };
QStringList _addAssetToWorldInfoKeys; // Model name
QStringList _addAssetToWorldInfoMessages; // Info message
QTimer _addAssetToWorldInfoTimer;
QTimer _addAssetToWorldErrorTimer;
FileScriptingInterface* _fileDownload;
}; };

View file

@ -184,6 +184,7 @@ void Avatar::animateScaleChanges(float deltaTime) {
} }
void Avatar::updateAvatarEntities() { void Avatar::updateAvatarEntities() {
PerformanceTimer perfTimer("attachments");
// - if queueEditEntityMessage sees clientOnly flag it does _myAvatar->updateAvatarEntity() // - if queueEditEntityMessage sees clientOnly flag it does _myAvatar->updateAvatarEntity()
// - updateAvatarEntity saves the bytes and sets _avatarEntityDataLocallyEdited // - updateAvatarEntity saves the bytes and sets _avatarEntityDataLocallyEdited
// - MyAvatar::update notices _avatarEntityDataLocallyEdited and calls sendIdentityPacket // - MyAvatar::update notices _avatarEntityDataLocallyEdited and calls sendIdentityPacket
@ -285,28 +286,38 @@ void Avatar::simulate(float deltaTime) {
} }
animateScaleChanges(deltaTime); animateScaleChanges(deltaTime);
// update the shouldAnimate flag to match whether or not we will render the avatar. bool avatarPositionInView = false;
const float MINIMUM_VISIBILITY_FOR_ON = 0.4f; bool avatarMeshInView = false;
const float MAXIMUM_VISIBILITY_FOR_OFF = 0.6f; { // update the shouldAnimate flag to match whether or not we will render the avatar.
ViewFrustum viewFrustum; PerformanceTimer perfTimer("cull");
qApp->copyViewFrustum(viewFrustum); ViewFrustum viewFrustum;
float visibility = calculateRenderAccuracy(viewFrustum.getPosition(), {
getBounds(), DependencyManager::get<LODManager>()->getOctreeSizeScale()); PerformanceTimer perfTimer("LOD");
if (!_shouldAnimate) { const float MINIMUM_VISIBILITY_FOR_ON = 0.4f;
if (visibility > MINIMUM_VISIBILITY_FOR_ON) { const float MAXIMUM_VISIBILITY_FOR_OFF = 0.6f;
_shouldAnimate = true; qApp->copyViewFrustum(viewFrustum);
qCDebug(interfaceapp) << "Restoring" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for visibility" << visibility; float visibility = calculateRenderAccuracy(viewFrustum.getPosition(),
getBounds(), DependencyManager::get<LODManager>()->getOctreeSizeScale());
if (!_shouldAnimate) {
if (visibility > MINIMUM_VISIBILITY_FOR_ON) {
_shouldAnimate = true;
qCDebug(interfaceapp) << "Restoring" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for visibility" << visibility;
}
} else if (visibility < MAXIMUM_VISIBILITY_FOR_OFF) {
_shouldAnimate = false;
qCDebug(interfaceapp) << "Optimizing" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for visibility" << visibility;
}
} }
} else if (visibility < MAXIMUM_VISIBILITY_FOR_OFF) {
_shouldAnimate = false;
qCDebug(interfaceapp) << "Optimizing" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for visibility" << visibility;
}
// simple frustum check {
float boundingRadius = getBoundingRadius(); PerformanceTimer perfTimer("inView");
qApp->copyDisplayViewFrustum(viewFrustum); // simple frustum check
bool avatarPositionInView = viewFrustum.sphereIntersectsFrustum(getPosition(), boundingRadius); float boundingRadius = getBoundingRadius();
bool avatarMeshInView = viewFrustum.boxIntersectsFrustum(_skeletonModel->getRenderableMeshBound()); qApp->copyDisplayViewFrustum(viewFrustum);
avatarPositionInView = viewFrustum.sphereIntersectsFrustum(getPosition(), boundingRadius);
avatarMeshInView = viewFrustum.boxIntersectsFrustum(_skeletonModel->getRenderableMeshBound());
}
}
if (_shouldAnimate && !_shouldSkipRender && (avatarPositionInView || avatarMeshInView)) { if (_shouldAnimate && !_shouldSkipRender && (avatarPositionInView || avatarMeshInView)) {
{ {
@ -331,6 +342,7 @@ void Avatar::simulate(float deltaTime) {
} else { } else {
// a non-full update is still required so that the position, rotation, scale and bounds of the skeletonModel are updated. // a non-full update is still required so that the position, rotation, scale and bounds of the skeletonModel are updated.
getHead()->setPosition(getPosition()); getHead()->setPosition(getPosition());
PerformanceTimer perfTimer("skeleton");
_skeletonModel->simulate(deltaTime, false); _skeletonModel->simulate(deltaTime, false);
} }
@ -379,6 +391,7 @@ void Avatar::applyPositionDelta(const glm::vec3& delta) {
} }
void Avatar::measureMotionDerivatives(float deltaTime) { void Avatar::measureMotionDerivatives(float deltaTime) {
PerformanceTimer perfTimer("derivatives");
// linear // linear
float invDeltaTime = 1.0f / deltaTime; float invDeltaTime = 1.0f / deltaTime;
// Floating point error prevents us from computing velocity in a naive way // Floating point error prevents us from computing velocity in a naive way
@ -645,6 +658,7 @@ bool Avatar::shouldRenderHead(const RenderArgs* renderArgs) const {
// virtual // virtual
void Avatar::simulateAttachments(float deltaTime) { void Avatar::simulateAttachments(float deltaTime) {
PerformanceTimer perfTimer("attachments");
for (int i = 0; i < (int)_attachmentModels.size(); i++) { for (int i = 0; i < (int)_attachmentModels.size(); i++) {
const AttachmentData& attachment = _attachmentData.at(i); const AttachmentData& attachment = _attachmentData.at(i);
auto& model = _attachmentModels.at(i); auto& model = _attachmentModels.at(i);
@ -1039,6 +1053,7 @@ void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
int Avatar::parseDataFromBuffer(const QByteArray& buffer) { int Avatar::parseDataFromBuffer(const QByteArray& buffer) {
PerformanceTimer perfTimer("unpack");
if (!_initialized) { if (!_initialized) {
// now that we have data for this Avatar we are go for init // now that we have data for this Avatar we are go for init
init(); init();
@ -1258,6 +1273,7 @@ void Avatar::setOrientation(const glm::quat& orientation) {
} }
void Avatar::updatePalms() { void Avatar::updatePalms() {
PerformanceTimer perfTimer("palms");
// update thread-safe caches // update thread-safe caches
_leftPalmRotationCache.set(getUncachedLeftPalmRotation()); _leftPalmRotationCache.set(getUncachedLeftPalmRotation());
_rightPalmRotationCache.set(getUncachedRightPalmRotation()); _rightPalmRotationCache.set(getUncachedRightPalmRotation());

View file

@ -50,12 +50,17 @@ glm::vec2 HMDScriptingInterface::overlayToSpherical(const glm::vec2 & position)
return qApp->getApplicationCompositor().overlayToSpherical(position); return qApp->getApplicationCompositor().overlayToSpherical(position);
} }
bool HMDScriptingInterface::isHMDAvailable() { bool HMDScriptingInterface::isHMDAvailable(const QString& name) {
return PluginUtils::isHMDAvailable(); return PluginUtils::isHMDAvailable(name);
} }
bool HMDScriptingInterface::isHandControllerAvailable() { bool HMDScriptingInterface::isHandControllerAvailable(const QString& name) {
return PluginUtils::isHandControllerAvailable(); return PluginUtils::isHandControllerAvailable(name);
}
bool HMDScriptingInterface::isSubdeviceContainingNameAvailable(const QString& name) {
return PluginUtils::isSubdeviceContainingNameAvailable(name);
} }
void HMDScriptingInterface::requestShowHandControllers() { void HMDScriptingInterface::requestShowHandControllers() {

View file

@ -40,8 +40,9 @@ public:
Q_INVOKABLE QString preferredAudioInput() const; Q_INVOKABLE QString preferredAudioInput() const;
Q_INVOKABLE QString preferredAudioOutput() const; Q_INVOKABLE QString preferredAudioOutput() const;
Q_INVOKABLE bool isHMDAvailable(); Q_INVOKABLE bool isHMDAvailable(const QString& name = "");
Q_INVOKABLE bool isHandControllerAvailable(); Q_INVOKABLE bool isHandControllerAvailable(const QString& name = "");
Q_INVOKABLE bool isSubdeviceContainingNameAvailable(const QString& name);
Q_INVOKABLE void requestShowHandControllers(); Q_INVOKABLE void requestShowHandControllers();
Q_INVOKABLE void requestHideHandControllers(); Q_INVOKABLE void requestHideHandControllers();

View file

@ -10,6 +10,10 @@
#include "TestScriptingInterface.h" #include "TestScriptingInterface.h"
#include <QtCore/QCoreApplication> #include <QtCore/QCoreApplication>
#include <QtCore/QLoggingCategory>
#include <DependencyManager.h>
#include <Trace.h>
TestScriptingInterface* TestScriptingInterface::getInstance() { TestScriptingInterface* TestScriptingInterface::getInstance() {
static TestScriptingInterface sharedInstance; static TestScriptingInterface sharedInstance;
@ -28,3 +32,27 @@ void TestScriptingInterface::waitForDownloadIdle() {
void TestScriptingInterface::waitIdle() { void TestScriptingInterface::waitIdle() {
} }
bool TestScriptingInterface::startTracing(QString logrules) {
if (!logrules.isEmpty()) {
QLoggingCategory::setFilterRules(logrules);
}
if (!DependencyManager::isSet<tracing::Tracer>()) {
return false;
}
DependencyManager::get<tracing::Tracer>()->startTracing();
return true;
}
bool TestScriptingInterface::stopTracing(QString filename) {
if (!DependencyManager::isSet<tracing::Tracer>()) {
return false;
}
auto tracer = DependencyManager::get<tracing::Tracer>();
tracer->stopTracing();
tracer->serialize(filename);
return true;
}

View file

@ -38,6 +38,17 @@ public slots:
*/ */
void waitIdle(); void waitIdle();
/**jsdoc
* Start recording Chrome compatible tracing events
* logRules can be used to specify a set of logging category rules to limit what gets captured
*/
bool startTracing(QString logrules = "");
/**jsdoc
* Stop recording Chrome compatible tracing events and serialize recorded events to a file
* Using a filename with a .gz extension will automatically compress the output file
*/
bool stopTracing(QString filename);
}; };
#endif // hifi_TestScriptingInterface_h #endif // hifi_TestScriptingInterface_h

View file

@ -29,6 +29,7 @@
#include "ui/Stats.h" #include "ui/Stats.h"
#include "ui/AvatarInputs.h" #include "ui/AvatarInputs.h"
#include "OffscreenUi.h" #include "OffscreenUi.h"
#include "InterfaceLogging.h"
#include <QQmlContext> #include <QQmlContext>
const vec4 CONNECTION_STATUS_BORDER_COLOR{ 1.0f, 0.0f, 0.0f, 0.8f }; const vec4 CONNECTION_STATUS_BORDER_COLOR{ 1.0f, 0.0f, 0.0f, 0.8f };
@ -56,7 +57,7 @@ ApplicationOverlay::~ApplicationOverlay() {
// Renders the overlays either to a texture or to the screen // Renders the overlays either to a texture or to the screen
void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
PROFILE_RANGE(__FUNCTION__); PROFILE_RANGE(render, __FUNCTION__);
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()"); PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()");
buildFramebufferObject(); buildFramebufferObject();
@ -95,7 +96,7 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
} }
void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) { void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) {
PROFILE_RANGE(__FUNCTION__); PROFILE_RANGE(app, __FUNCTION__);
if (!_uiTexture) { if (!_uiTexture) {
_uiTexture = gpu::TexturePointer(gpu::Texture::createExternal2D(OffscreenQmlSurface::getDiscardLambda())); _uiTexture = gpu::TexturePointer(gpu::Texture::createExternal2D(OffscreenQmlSurface::getDiscardLambda()));
@ -123,7 +124,7 @@ void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) {
} }
void ApplicationOverlay::renderAudioScope(RenderArgs* renderArgs) { void ApplicationOverlay::renderAudioScope(RenderArgs* renderArgs) {
PROFILE_RANGE(__FUNCTION__); PROFILE_RANGE(app, __FUNCTION__);
gpu::Batch& batch = *renderArgs->_batch; gpu::Batch& batch = *renderArgs->_batch;
auto geometryCache = DependencyManager::get<GeometryCache>(); auto geometryCache = DependencyManager::get<GeometryCache>();
@ -142,7 +143,7 @@ void ApplicationOverlay::renderAudioScope(RenderArgs* renderArgs) {
} }
void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) { void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) {
PROFILE_RANGE(__FUNCTION__); PROFILE_RANGE(app, __FUNCTION__);
gpu::Batch& batch = *renderArgs->_batch; gpu::Batch& batch = *renderArgs->_batch;
auto geometryCache = DependencyManager::get<GeometryCache>(); auto geometryCache = DependencyManager::get<GeometryCache>();
@ -261,7 +262,7 @@ static const auto DEFAULT_SAMPLER = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LI
static const auto DEPTH_FORMAT = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH); static const auto DEPTH_FORMAT = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH);
void ApplicationOverlay::buildFramebufferObject() { void ApplicationOverlay::buildFramebufferObject() {
PROFILE_RANGE(__FUNCTION__); PROFILE_RANGE(app, __FUNCTION__);
auto uiSize = qApp->getUiSize(); auto uiSize = qApp->getUiSize();
if (!_overlayFramebuffer || uiSize != _overlayFramebuffer->getSize()) { if (!_overlayFramebuffer || uiSize != _overlayFramebuffer->getSize()) {
@ -287,4 +288,4 @@ gpu::TexturePointer ApplicationOverlay::getOverlayTexture() {
return gpu::TexturePointer(); return gpu::TexturePointer();
} }
return _overlayFramebuffer->getRenderBuffer(0); return _overlayFramebuffer->getRenderBuffer(0);
} }

View file

@ -202,9 +202,8 @@ bool Image3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec
if (_texture && _texture->isLoaded()) { if (_texture && _texture->isLoaded()) {
// Make sure position and rotation is updated. // Make sure position and rotation is updated.
Transform transform = getTransform(); Transform transform = getTransform();
// XXX this code runs too often for this...
// applyTransformTo(transform, true); // Don't call applyTransformTo() or setTransform() here because this code runs too frequently.
// setTransform(transform);
// Produce the dimensions of the overlay based on the image's aspect ratio and the overlay's scale. // Produce the dimensions of the overlay based on the image's aspect ratio and the overlay's scale.
bool isNull = _fromImage.isNull(); bool isNull = _fromImage.isNull();

View file

@ -81,6 +81,7 @@ void ModelOverlay::render(RenderArgs* args) {
} }
_model->setVisibleInScene(_visible, scene); _model->setVisibleInScene(_visible, scene);
_model->setLayeredInFront(getDrawInFront(), scene);
scene->enqueuePendingChanges(pendingChanges); scene->enqueuePendingChanges(pendingChanges);
} }

View file

@ -19,6 +19,7 @@
#include <RegisteredMetaTypes.h> #include <RegisteredMetaTypes.h>
#include "Application.h" #include "Application.h"
#include "InterfaceLogging.h"
#include "Image3DOverlay.h" #include "Image3DOverlay.h"
#include "Circle3DOverlay.h" #include "Circle3DOverlay.h"
#include "Cube3DOverlay.h" #include "Cube3DOverlay.h"
@ -36,6 +37,7 @@
#include "Web3DOverlay.h" #include "Web3DOverlay.h"
#include <QtQuick/QQuickWindow> #include <QtQuick/QQuickWindow>
Q_LOGGING_CATEGORY(trace_render_overlays, "trace.render.overlays")
Overlays::Overlays() : Overlays::Overlays() :
_nextOverlayID(1) {} _nextOverlayID(1) {}
@ -101,7 +103,7 @@ void Overlays::cleanupOverlaysToDelete() {
} }
void Overlays::renderHUD(RenderArgs* renderArgs) { void Overlays::renderHUD(RenderArgs* renderArgs) {
PROFILE_RANGE(__FUNCTION__); PROFILE_RANGE(render_overlays, __FUNCTION__);
QReadLocker lock(&_lock); QReadLocker lock(&_lock);
gpu::Batch& batch = *renderArgs->_batch; gpu::Batch& batch = *renderArgs->_batch;
@ -292,6 +294,14 @@ QString Overlays::getOverlayType(unsigned int overlayId) const {
return ""; return "";
} }
QObject* Overlays::getOverlayObject(unsigned int id) {
Overlay::Pointer thisOverlay = getOverlay(id);
if (thisOverlay) {
return qobject_cast<QObject*>(&(*thisOverlay));
}
return nullptr;
}
unsigned int Overlays::getParentPanel(unsigned int childId) const { unsigned int Overlays::getParentPanel(unsigned int childId) const {
Overlay::Pointer overlay = getOverlay(childId); Overlay::Pointer overlay = getOverlay(childId);
auto attachable = std::dynamic_pointer_cast<PanelAttachable>(overlay); auto attachable = std::dynamic_pointer_cast<PanelAttachable>(overlay);
@ -388,7 +398,7 @@ QScriptValue OverlayPropertyResultToScriptValue(QScriptEngine* engine, const Ove
if (!value.value.isValid()) { if (!value.value.isValid()) {
return QScriptValue::UndefinedValue; return QScriptValue::UndefinedValue;
} }
return engine->newVariant(value.value); return engine->toScriptValue(value.value);
} }
void OverlayPropertyResultFromScriptValue(const QScriptValue& object, OverlayPropertyResult& value) { void OverlayPropertyResultFromScriptValue(const QScriptValue& object, OverlayPropertyResult& value) {
@ -596,6 +606,38 @@ bool Overlays::isAddedOverlay(unsigned int id) {
return _overlaysHUD.contains(id) || _overlaysWorld.contains(id); return _overlaysHUD.contains(id) || _overlaysWorld.contains(id);
} }
void Overlays::sendMousePressOnOverlay(unsigned int overlayID, const PointerEvent& event) {
emit mousePressOnOverlay(overlayID, event);
}
void Overlays::sendMouseReleaseOnOverlay(unsigned int overlayID, const PointerEvent& event) {
emit mouseReleaseOnOverlay(overlayID, event);
}
void Overlays::sendMouseMoveOnOverlay(unsigned int overlayID, const PointerEvent& event) {
emit mouseMoveOnOverlay(overlayID, event);
}
void Overlays::sendHoverEnterOverlay(unsigned int id, PointerEvent event) {
emit hoverEnterOverlay(id, event);
}
void Overlays::sendHoverOverOverlay(unsigned int id, PointerEvent event) {
emit hoverOverOverlay(id, event);
}
void Overlays::sendHoverLeaveOverlay(unsigned int id, PointerEvent event) {
emit hoverLeaveOverlay(id, event);
}
unsigned int Overlays::getKeyboardFocusOverlay() const {
return qApp->getKeyboardFocusOverlay();
}
void Overlays::setKeyboardFocusOverlay(unsigned int id) {
qApp->setKeyboardFocusOverlay(id);
}
float Overlays::width() const { float Overlays::width() const {
auto offscreenUi = DependencyManager::get<OffscreenUi>(); auto offscreenUi = DependencyManager::get<OffscreenUi>();
return offscreenUi->getWindow()->size().width(); return offscreenUi->getWindow()->size().width();
@ -605,3 +647,152 @@ float Overlays::height() const {
auto offscreenUi = DependencyManager::get<OffscreenUi>(); auto offscreenUi = DependencyManager::get<OffscreenUi>();
return offscreenUi->getWindow()->size().height(); return offscreenUi->getWindow()->size().height();
} }
static const uint32_t MOUSE_POINTER_ID = 0;
static glm::vec2 projectOntoOverlayXYPlane(glm::vec3 position, glm::quat rotation, glm::vec2 dimensions, const PickRay& pickRay,
const RayToOverlayIntersectionResult& rayPickResult) {
// Project the intersection point onto the local xy plane of the overlay.
float distance;
glm::vec3 planePosition = position;
glm::vec3 planeNormal = rotation * Vectors::UNIT_Z;
glm::vec3 overlayDimensions = glm::vec3(dimensions.x, dimensions.y, 0.0f);
glm::vec3 rayDirection = pickRay.direction;
glm::vec3 rayStart = pickRay.origin;
glm::vec3 p;
if (rayPlaneIntersection(planePosition, planeNormal, rayStart, rayDirection, distance)) {
p = rayStart + rayDirection * distance;
} else {
p = rayPickResult.intersection;
}
glm::vec3 localP = glm::inverse(rotation) * (p - position);
glm::vec3 normalizedP = (localP / overlayDimensions) + glm::vec3(0.5f);
return glm::vec2(normalizedP.x * overlayDimensions.x,
(1.0f - normalizedP.y) * overlayDimensions.y); // flip y-axis
}
static uint32_t toPointerButtons(const QMouseEvent& event) {
uint32_t buttons = 0;
buttons |= event.buttons().testFlag(Qt::LeftButton) ? PointerEvent::PrimaryButton : 0;
buttons |= event.buttons().testFlag(Qt::RightButton) ? PointerEvent::SecondaryButton : 0;
buttons |= event.buttons().testFlag(Qt::MiddleButton) ? PointerEvent::TertiaryButton : 0;
return buttons;
}
static PointerEvent::Button toPointerButton(const QMouseEvent& event) {
switch (event.button()) {
case Qt::LeftButton:
return PointerEvent::PrimaryButton;
case Qt::RightButton:
return PointerEvent::SecondaryButton;
case Qt::MiddleButton:
return PointerEvent::TertiaryButton;
default:
return PointerEvent::NoButtons;
}
}
PointerEvent Overlays::calculatePointerEvent(Overlay::Pointer overlay, PickRay ray,
RayToOverlayIntersectionResult rayPickResult, QMouseEvent* event, PointerEvent::EventType eventType) {
auto thisOverlay = std::dynamic_pointer_cast<Web3DOverlay>(overlay);
QReadLocker lock(&_lock);
auto position = thisOverlay->getPosition();
auto rotation = thisOverlay->getRotation();
auto dimensions = thisOverlay->getSize();
glm::vec2 pos2D = projectOntoOverlayXYPlane(position, rotation, dimensions, ray, rayPickResult);
PointerEvent pointerEvent(eventType, MOUSE_POINTER_ID,
pos2D, rayPickResult.intersection,
rayPickResult.surfaceNormal, ray.direction,
toPointerButton(*event), toPointerButtons(*event));
return pointerEvent;
}
void Overlays::mousePressEvent(QMouseEvent* event) {
PerformanceTimer perfTimer("Overlays::mousePressEvent");
PickRay ray = qApp->computePickRay(event->x(), event->y());
RayToOverlayIntersectionResult rayPickResult = findRayIntersection(ray);
if (rayPickResult.intersects) {
_currentClickingOnOverlayID = rayPickResult.overlayID;
// Only Web overlays can have focus.
auto thisOverlay = std::dynamic_pointer_cast<Web3DOverlay>(getOverlay(_currentClickingOnOverlayID));
if (thisOverlay) {
auto pointerEvent = calculatePointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Press);
emit mousePressOnOverlay(_currentClickingOnOverlayID, pointerEvent);
} else {
emit mousePressOffOverlay();
}
} else {
emit mousePressOffOverlay();
}
}
void Overlays::mouseReleaseEvent(QMouseEvent* event) {
PerformanceTimer perfTimer("Overlays::mouseReleaseEvent");
PickRay ray = qApp->computePickRay(event->x(), event->y());
RayToOverlayIntersectionResult rayPickResult = findRayIntersection(ray);
if (rayPickResult.intersects) {
// Only Web overlays can have focus.
auto thisOverlay = std::dynamic_pointer_cast<Web3DOverlay>(getOverlay(rayPickResult.overlayID));
if (thisOverlay) {
auto pointerEvent = calculatePointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Release);
emit mouseReleaseOnOverlay(rayPickResult.overlayID, pointerEvent);
}
}
_currentClickingOnOverlayID = UNKNOWN_OVERLAY_ID;
}
void Overlays::mouseMoveEvent(QMouseEvent* event) {
PerformanceTimer perfTimer("Overlays::mouseMoveEvent");
PickRay ray = qApp->computePickRay(event->x(), event->y());
RayToOverlayIntersectionResult rayPickResult = findRayIntersection(ray);
if (rayPickResult.intersects) {
// Only Web overlays can have focus.
auto thisOverlay = std::dynamic_pointer_cast<Web3DOverlay>(getOverlay(rayPickResult.overlayID));
if (thisOverlay) {
auto pointerEvent = calculatePointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Move);
emit mouseMoveOnOverlay(rayPickResult.overlayID, pointerEvent);
// If previously hovering over a different overlay then leave hover on that overlay.
if (_currentHoverOverOverlayID != UNKNOWN_OVERLAY_ID && rayPickResult.overlayID != _currentHoverOverOverlayID) {
auto thisOverlay = std::dynamic_pointer_cast<Web3DOverlay>(getOverlay(_currentHoverOverOverlayID));
if (thisOverlay) {
auto pointerEvent = calculatePointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Move);
emit hoverLeaveOverlay(_currentHoverOverOverlayID, pointerEvent);
}
}
// If hovering over a new overlay then enter hover on that overlay.
if (rayPickResult.overlayID != _currentHoverOverOverlayID) {
emit hoverEnterOverlay(rayPickResult.overlayID, pointerEvent);
}
// Hover over current overlay.
emit hoverOverOverlay(rayPickResult.overlayID, pointerEvent);
_currentHoverOverOverlayID = rayPickResult.overlayID;
}
} else {
// If previously hovering an overlay then leave hover.
if (_currentHoverOverOverlayID != UNKNOWN_OVERLAY_ID) {
auto thisOverlay = std::dynamic_pointer_cast<Web3DOverlay>(getOverlay(_currentHoverOverOverlayID));
if (thisOverlay) {
auto pointerEvent = calculatePointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Move);
emit hoverLeaveOverlay(_currentHoverOverOverlayID, pointerEvent);
}
_currentHoverOverOverlayID = UNKNOWN_OVERLAY_ID;
}
}
}

View file

@ -18,11 +18,13 @@
#ifndef hifi_Overlays_h #ifndef hifi_Overlays_h
#define hifi_Overlays_h #define hifi_Overlays_h
#include <QMouseEvent>
#include <QReadWriteLock> #include <QReadWriteLock>
#include <QScriptValue> #include <QScriptValue>
#include "Overlay.h" #include <PointerEvent.h>
#include "Overlay.h"
#include "OverlayPanel.h" #include "OverlayPanel.h"
#include "PanelAttachable.h" #include "PanelAttachable.h"
@ -51,7 +53,7 @@ class RayToOverlayIntersectionResult {
public: public:
RayToOverlayIntersectionResult(); RayToOverlayIntersectionResult();
bool intersects; bool intersects;
int overlayID; unsigned int overlayID;
float distance; float distance;
BoxFace face; BoxFace face;
glm::vec3 surfaceNormal; glm::vec3 surfaceNormal;
@ -75,9 +77,13 @@ void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, R
* @namespace Overlays * @namespace Overlays
*/ */
const unsigned int UNKNOWN_OVERLAY_ID = 0;
class Overlays : public QObject { class Overlays : public QObject {
Q_OBJECT Q_OBJECT
Q_PROPERTY(unsigned int keyboardFocusOverlay READ getKeyboardFocusOverlay WRITE setKeyboardFocusOverlay)
public: public:
Overlays(); Overlays();
@ -94,6 +100,10 @@ public:
unsigned int addOverlay(Overlay* overlay) { return addOverlay(Overlay::Pointer(overlay)); } unsigned int addOverlay(Overlay* overlay) { return addOverlay(Overlay::Pointer(overlay)); }
unsigned int addOverlay(Overlay::Pointer overlay); unsigned int addOverlay(Overlay::Pointer overlay);
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
void mouseMoveEvent(QMouseEvent* event);
void cleanupAllOverlays(); void cleanupAllOverlays();
public slots: public slots:
@ -147,6 +157,15 @@ public slots:
*/ */
QString getOverlayType(unsigned int overlayId) const; QString getOverlayType(unsigned int overlayId) const;
/**jsdoc
* Get the overlay Script object.
*
* @function Overlays.getOverlayObject
* @param {Overlays.OverlayID} overlayID The ID of the overlay to get the script object of.
* @return {Object} The script object for the overlay if found.
*/
QObject* getOverlayObject(unsigned int id);
/**jsdoc /**jsdoc
* Get the ID of the overlay at a particular point on the HUD/screen. * Get the ID of the overlay at a particular point on the HUD/screen.
* *
@ -239,6 +258,17 @@ public slots:
/// return true if there is a panel with that id else false /// return true if there is a panel with that id else false
bool isAddedPanel(unsigned int id) { return _panels.contains(id); } bool isAddedPanel(unsigned int id) { return _panels.contains(id); }
void sendMousePressOnOverlay(unsigned int overlayID, const PointerEvent& event);
void sendMouseReleaseOnOverlay(unsigned int overlayID, const PointerEvent& event);
void sendMouseMoveOnOverlay(unsigned int overlayID, const PointerEvent& event);
void sendHoverEnterOverlay(unsigned int id, PointerEvent event);
void sendHoverOverOverlay(unsigned int id, PointerEvent event);
void sendHoverLeaveOverlay(unsigned int id, PointerEvent event);
unsigned int getKeyboardFocusOverlay() const;
void setKeyboardFocusOverlay(unsigned int id);
signals: signals:
/**jsdoc /**jsdoc
* Emitted when an overlay is deleted * Emitted when an overlay is deleted
@ -249,6 +279,15 @@ signals:
void overlayDeleted(unsigned int id); void overlayDeleted(unsigned int id);
void panelDeleted(unsigned int id); void panelDeleted(unsigned int id);
void mousePressOnOverlay(unsigned int overlayID, const PointerEvent& event);
void mouseReleaseOnOverlay(unsigned int overlayID, const PointerEvent& event);
void mouseMoveOnOverlay(unsigned int overlayID, const PointerEvent& event);
void mousePressOffOverlay();
void hoverEnterOverlay(unsigned int overlayID, const PointerEvent& event);
void hoverOverOverlay(unsigned int overlayID, const PointerEvent& event);
void hoverLeaveOverlay(unsigned int overlayID, const PointerEvent& event);
private: private:
void cleanupOverlaysToDelete(); void cleanupOverlaysToDelete();
@ -262,8 +301,12 @@ private:
QReadWriteLock _deleteLock; QReadWriteLock _deleteLock;
QScriptEngine* _scriptEngine; QScriptEngine* _scriptEngine;
bool _enabled = true; bool _enabled = true;
PointerEvent calculatePointerEvent(Overlay::Pointer overlay, PickRay ray, RayToOverlayIntersectionResult rayPickResult,
QMouseEvent* event, PointerEvent::EventType eventType);
unsigned int _currentClickingOnOverlayID { UNKNOWN_OVERLAY_ID };
unsigned int _currentHoverOverOverlayID { UNKNOWN_OVERLAY_ID };
}; };
#endif // hifi_Overlays_h #endif // hifi_Overlays_h

View file

@ -1,7 +1,6 @@
// //
// Web3DOverlay.cpp // Web3DOverlay.cpp
// //
//
// Created by Clement on 7/1/14. // Created by Clement on 7/1/14.
// Modified and renamed by Zander Otavka on 8/4/15 // Modified and renamed by Zander Otavka on 8/4/15
// Copyright 2014 High Fidelity, Inc. // Copyright 2014 High Fidelity, Inc.
@ -12,8 +11,12 @@
#include "Web3DOverlay.h" #include "Web3DOverlay.h"
#include <Application.h>
#include <QQuickWindow>
#include <QtGui/QOpenGLContext> #include <QtGui/QOpenGLContext>
#include <QtQuick/QQuickItem> #include <QtQuick/QQuickItem>
#include <QtQml/QQmlContext>
#include <DependencyManager.h> #include <DependencyManager.h>
#include <GeometryCache.h> #include <GeometryCache.h>
@ -28,17 +31,24 @@
static const float DPI = 30.47f; static const float DPI = 30.47f;
static const float INCHES_TO_METERS = 1.0f / 39.3701f; static const float INCHES_TO_METERS = 1.0f / 39.3701f;
static float OPAQUE_ALPHA_THRESHOLD = 0.99f; static const float METERS_TO_INCHES = 39.3701f;
static const float OPAQUE_ALPHA_THRESHOLD = 0.99f;
QString const Web3DOverlay::TYPE = "web3d"; QString const Web3DOverlay::TYPE = "web3d";
Web3DOverlay::Web3DOverlay() : _dpi(DPI) { Web3DOverlay::Web3DOverlay() : _dpi(DPI) {
_touchDevice.setCapabilities(QTouchDevice::Position);
_touchDevice.setType(QTouchDevice::TouchScreen);
_touchDevice.setName("RenderableWebEntityItemTouchDevice");
_touchDevice.setMaximumTouchPoints(4);
_geometryId = DependencyManager::get<GeometryCache>()->allocateID(); _geometryId = DependencyManager::get<GeometryCache>()->allocateID();
} }
Web3DOverlay::Web3DOverlay(const Web3DOverlay* Web3DOverlay) : Web3DOverlay::Web3DOverlay(const Web3DOverlay* Web3DOverlay) :
Billboard3DOverlay(Web3DOverlay), Billboard3DOverlay(Web3DOverlay),
_url(Web3DOverlay->_url), _url(Web3DOverlay->_url),
_scriptURL(Web3DOverlay->_scriptURL),
_dpi(Web3DOverlay->_dpi), _dpi(Web3DOverlay->_dpi),
_resolution(Web3DOverlay->_resolution) _resolution(Web3DOverlay->_resolution)
{ {
@ -50,6 +60,19 @@ Web3DOverlay::~Web3DOverlay() {
_webSurface->pause(); _webSurface->pause();
_webSurface->disconnect(_connection); _webSurface->disconnect(_connection);
QObject::disconnect(_mousePressConnection);
_mousePressConnection = QMetaObject::Connection();
QObject::disconnect(_mouseReleaseConnection);
_mouseReleaseConnection = QMetaObject::Connection();
QObject::disconnect(_mouseMoveConnection);
_mouseMoveConnection = QMetaObject::Connection();
QObject::disconnect(_hoverLeaveConnection);
_hoverLeaveConnection = QMetaObject::Connection();
QObject::disconnect(_emitScriptEventConnection);
_emitScriptEventConnection = QMetaObject::Connection();
QObject::disconnect(_webEventReceivedConnection);
_webEventReceivedConnection = QMetaObject::Connection();
// The lifetime of the QML surface MUST be managed by the main thread // The lifetime of the QML surface MUST be managed by the main thread
// Additionally, we MUST use local variables copied by value, rather than // Additionally, we MUST use local variables copied by value, rather than
@ -67,11 +90,15 @@ Web3DOverlay::~Web3DOverlay() {
} }
void Web3DOverlay::update(float deltatime) { void Web3DOverlay::update(float deltatime) {
// FIXME: applyTransformTo causes tablet overlay to detach from tablet entity.
// Perhaps rather than deleting the following code it should be run only if isFacingAvatar() is true?
/*
if (usecTimestampNow() > _transformExpiry) { if (usecTimestampNow() > _transformExpiry) {
Transform transform = getTransform(); Transform transform = getTransform();
applyTransformTo(transform); applyTransformTo(transform);
setTransform(transform); setTransform(transform);
} }
*/
} }
void Web3DOverlay::render(RenderArgs* args) { void Web3DOverlay::render(RenderArgs* args) {
@ -92,21 +119,62 @@ void Web3DOverlay::render(RenderArgs* args) {
// and the current rendering load) // and the current rendering load)
_webSurface->setMaxFps(10); _webSurface->setMaxFps(10);
_webSurface->create(currentContext); _webSurface->create(currentContext);
_webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/controls/")); _webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/"));
_webSurface->load("WebView.qml"); _webSurface->load("Web3DOverlay.qml");
_webSurface->resume(); _webSurface->resume();
_webSurface->getRootItem()->setProperty("url", _url); _webSurface->getRootItem()->setProperty("url", _url);
_webSurface->getRootItem()->setProperty("scriptURL", _scriptURL);
_webSurface->getRootContext()->setContextProperty("ApplicationInterface", qApp);
_webSurface->resize(QSize(_resolution.x, _resolution.y)); _webSurface->resize(QSize(_resolution.x, _resolution.y));
currentContext->makeCurrent(currentSurface); currentContext->makeCurrent(currentSurface);
auto forwardPointerEvent = [=](unsigned int overlayID, const PointerEvent& event) {
if (overlayID == getOverlayID()) {
handlePointerEvent(event);
}
};
_mousePressConnection = connect(&(qApp->getOverlays()), &Overlays::mousePressOnOverlay, forwardPointerEvent);
_mouseReleaseConnection = connect(&(qApp->getOverlays()), &Overlays::mouseReleaseOnOverlay, forwardPointerEvent);
_mouseMoveConnection = connect(&(qApp->getOverlays()), &Overlays::mouseMoveOnOverlay, forwardPointerEvent);
_hoverLeaveConnection = connect(&(qApp->getOverlays()), &Overlays::hoverLeaveOverlay,
[=](unsigned int overlayID, const PointerEvent& event) {
if (this->_pressed && this->getOverlayID() == overlayID) {
// If the user mouses off the overlay while the button is down, simulate a touch end.
QTouchEvent::TouchPoint point;
point.setId(event.getID());
point.setState(Qt::TouchPointReleased);
glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _dpi);
QPointF windowPoint(windowPos.x, windowPos.y);
point.setScenePos(windowPoint);
point.setPos(windowPoint);
QList<QTouchEvent::TouchPoint> touchPoints;
touchPoints.push_back(point);
QTouchEvent* touchEvent = new QTouchEvent(QEvent::TouchEnd, nullptr, Qt::NoModifier, Qt::TouchPointReleased,
touchPoints);
touchEvent->setWindow(_webSurface->getWindow());
touchEvent->setDevice(&_touchDevice);
touchEvent->setTarget(_webSurface->getRootItem());
QCoreApplication::postEvent(_webSurface->getWindow(), touchEvent);
}
});
_emitScriptEventConnection = connect(this, &Web3DOverlay::scriptEventReceived, _webSurface.data(), &OffscreenQmlSurface::emitScriptEvent);
_webEventReceivedConnection = connect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, this, &Web3DOverlay::webEventReceived);
} }
vec2 size = _resolution / _dpi * INCHES_TO_METERS; vec2 halfSize = getSize() / 2.0f;
vec2 halfSize = size / 2.0f;
vec4 color(toGlm(getColor()), getAlpha()); vec4 color(toGlm(getColor()), getAlpha());
Transform transform = getTransform(); Transform transform = getTransform();
// FIXME: applyTransformTo causes tablet overlay to detach from tablet entity.
// Perhaps rather than deleting the following code it should be run only if isFacingAvatar() is true?
/*
applyTransformTo(transform, true); applyTransformTo(transform, true);
setTransform(transform); setTransform(transform);
*/
if (glm::length2(getDimensions()) != 1.0f) { if (glm::length2(getDimensions()) != 1.0f) {
transform.postScale(vec3(getDimensions(), 1.0f)); transform.postScale(vec3(getDimensions(), 1.0f));
} }
@ -144,6 +212,78 @@ const render::ShapeKey Web3DOverlay::getShapeKey() {
return builder.build(); return builder.build();
} }
QObject* Web3DOverlay::getEventHandler() {
if (!_webSurface) {
return nullptr;
}
return _webSurface->getEventHandler();
}
void Web3DOverlay::setProxyWindow(QWindow* proxyWindow) {
if (!_webSurface) {
return;
}
_webSurface->setProxyWindow(proxyWindow);
}
void Web3DOverlay::handlePointerEvent(const PointerEvent& event) {
if (!_webSurface) {
return;
}
glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _dpi);
QPointF windowPoint(windowPos.x, windowPos.y);
if (event.getType() == PointerEvent::Move) {
// Forward a mouse move event to the Web surface.
QMouseEvent* mouseEvent = new QMouseEvent(QEvent::MouseMove, windowPoint, windowPoint, windowPoint, Qt::NoButton,
Qt::NoButton, Qt::NoModifier);
QCoreApplication::postEvent(_webSurface->getWindow(), mouseEvent);
}
if (event.getType() == PointerEvent::Press) {
this->_pressed = true;
} else if (event.getType() == PointerEvent::Release) {
this->_pressed = false;
}
QEvent::Type type;
Qt::TouchPointState touchPointState;
switch (event.getType()) {
case PointerEvent::Press:
type = QEvent::TouchBegin;
touchPointState = Qt::TouchPointPressed;
break;
case PointerEvent::Release:
type = QEvent::TouchEnd;
touchPointState = Qt::TouchPointReleased;
break;
case PointerEvent::Move:
default:
type = QEvent::TouchUpdate;
touchPointState = Qt::TouchPointMoved;
break;
}
QTouchEvent::TouchPoint point;
point.setId(event.getID());
point.setState(touchPointState);
point.setPos(windowPoint);
point.setScreenPos(windowPoint);
QList<QTouchEvent::TouchPoint> touchPoints;
touchPoints.push_back(point);
QTouchEvent* touchEvent = new QTouchEvent(type);
touchEvent->setWindow(_webSurface->getWindow());
touchEvent->setDevice(&_touchDevice);
touchEvent->setTarget(_webSurface->getRootItem());
touchEvent->setTouchPoints(touchPoints);
touchEvent->setTouchPointStates(touchPointState);
QCoreApplication::postEvent(_webSurface->getWindow(), touchEvent);
}
void Web3DOverlay::setProperties(const QVariantMap& properties) { void Web3DOverlay::setProperties(const QVariantMap& properties) {
Billboard3DOverlay::setProperties(properties); Billboard3DOverlay::setProperties(properties);
@ -155,6 +295,14 @@ void Web3DOverlay::setProperties(const QVariantMap& properties) {
} }
} }
auto scriptURLValue = properties["scriptURL"];
if (scriptURLValue.isValid()) {
QString newScriptURL = scriptURLValue.toString();
if (newScriptURL != _scriptURL) {
setScriptURL(newScriptURL);
}
}
auto resolution = properties["resolution"]; auto resolution = properties["resolution"];
if (resolution.isValid()) { if (resolution.isValid()) {
bool valid; bool valid;
@ -164,7 +312,6 @@ void Web3DOverlay::setProperties(const QVariantMap& properties) {
} }
} }
auto dpi = properties["dpi"]; auto dpi = properties["dpi"];
if (dpi.isValid()) { if (dpi.isValid()) {
_dpi = dpi.toFloat(); _dpi = dpi.toFloat();
@ -175,6 +322,12 @@ QVariant Web3DOverlay::getProperty(const QString& property) {
if (property == "url") { if (property == "url") {
return _url; return _url;
} }
if (property == "scriptURL") {
return _scriptURL;
}
if (property == "resolution") {
return vec2toVariant(_resolution);
}
if (property == "dpi") { if (property == "dpi") {
return _dpi; return _dpi;
} }
@ -188,22 +341,38 @@ void Web3DOverlay::setURL(const QString& url) {
_webSurface->getRootItem()->setProperty("url", url); _webSurface->getRootItem()->setProperty("url", url);
}); });
} }
} }
void Web3DOverlay::setScriptURL(const QString& scriptURL) {
_scriptURL = scriptURL;
if (_webSurface) {
AbstractViewStateInterface::instance()->postLambdaEvent([this, scriptURL] {
_webSurface->getRootItem()->setProperty("scriptURL", scriptURL);
});
}
}
glm::vec2 Web3DOverlay::getSize() {
return _resolution / _dpi * INCHES_TO_METERS * getDimensions();
};
bool Web3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal) { bool Web3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal) {
// FIXME - face and surfaceNormal not being returned // FIXME - face and surfaceNormal not being returned
// Make sure position and rotation is updated. // Don't call applyTransformTo() or setTransform() here because this code runs too frequently.
Transform transform;
applyTransformTo(transform, true);
setTransform(transform);
vec2 size = _resolution / _dpi * INCHES_TO_METERS * vec2(getDimensions());
// Produce the dimensions of the overlay based on the image's aspect ratio and the overlay's scale. // Produce the dimensions of the overlay based on the image's aspect ratio and the overlay's scale.
return findRayRectangleIntersection(origin, direction, getRotation(), getPosition(), size, distance); return findRayRectangleIntersection(origin, direction, getRotation(), getPosition(), getSize(), distance);
} }
Web3DOverlay* Web3DOverlay::createClone() const { Web3DOverlay* Web3DOverlay::createClone() const {
return new Web3DOverlay(this); return new Web3DOverlay(this);
} }
void Web3DOverlay::emitScriptEvent(const QVariant& message) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "emitScriptEvent", Qt::QueuedConnection, Q_ARG(QVariant, message));
} else {
emit scriptEventReceived(message);
}
}

View file

@ -11,6 +11,10 @@
#include "Billboard3DOverlay.h" #include "Billboard3DOverlay.h"
#include <QTouchEvent>
#include <PointerEvent.h>
class OffscreenQmlSurface; class OffscreenQmlSurface;
class Web3DOverlay : public Billboard3DOverlay { class Web3DOverlay : public Billboard3DOverlay {
@ -29,25 +33,51 @@ public:
virtual void update(float deltatime) override; virtual void update(float deltatime) override;
QObject* getEventHandler();
void setProxyWindow(QWindow* proxyWindow);
void handlePointerEvent(const PointerEvent& event);
// setters // setters
void setURL(const QString& url); void setURL(const QString& url);
void setScriptURL(const QString& script);
void setProperties(const QVariantMap& properties) override; void setProperties(const QVariantMap& properties) override;
QVariant getProperty(const QString& property) override; QVariant getProperty(const QString& property) override;
glm::vec2 getSize();
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
BoxFace& face, glm::vec3& surfaceNormal) override; BoxFace& face, glm::vec3& surfaceNormal) override;
virtual Web3DOverlay* createClone() const override; virtual Web3DOverlay* createClone() const override;
public slots:
void emitScriptEvent(const QVariant& scriptMessage);
signals:
void scriptEventReceived(const QVariant& message);
void webEventReceived(const QVariant& message);
private: private:
QSharedPointer<OffscreenQmlSurface> _webSurface; QSharedPointer<OffscreenQmlSurface> _webSurface;
QMetaObject::Connection _connection; QMetaObject::Connection _connection;
gpu::TexturePointer _texture; gpu::TexturePointer _texture;
QString _url; QString _url;
QString _scriptURL;
float _dpi; float _dpi;
vec2 _resolution{ 640, 480 }; vec2 _resolution{ 640, 480 };
int _geometryId { 0 }; int _geometryId { 0 };
bool _pressed{ false };
QTouchDevice _touchDevice;
QMetaObject::Connection _mousePressConnection;
QMetaObject::Connection _mouseReleaseConnection;
QMetaObject::Connection _mouseMoveConnection;
QMetaObject::Connection _hoverLeaveConnection;
QMetaObject::Connection _emitScriptEventConnection;
QMetaObject::Connection _webEventReceivedConnection;
}; };
#endif // hifi_Web3DOverlay_h #endif // hifi_Web3DOverlay_h

View file

@ -394,7 +394,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
loadPoses(underPoses); loadPoses(underPoses);
} else { } else {
PROFILE_RANGE_EX("ik/relax", 0xffff00ff, 0); PROFILE_RANGE_EX(simulation_animation, "ik/relax", 0xffff00ff, 0);
// relax toward underPoses // relax toward underPoses
// HACK: this relaxation needs to be constant per-frame rather than per-realtime // HACK: this relaxation needs to be constant per-frame rather than per-realtime
@ -433,7 +433,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
// build a list of targets from _targetVarVec // build a list of targets from _targetVarVec
std::vector<IKTarget> targets; std::vector<IKTarget> targets;
{ {
PROFILE_RANGE_EX("ik/computeTargets", 0xffff00ff, 0); PROFILE_RANGE_EX(simulation_animation, "ik/computeTargets", 0xffff00ff, 0);
computeTargets(animVars, targets, underPoses); computeTargets(animVars, targets, underPoses);
} }
@ -450,7 +450,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
} else { } else {
{ {
PROFILE_RANGE_EX("ik/shiftHips", 0xffff00ff, 0); PROFILE_RANGE_EX(simulation_animation, "ik/shiftHips", 0xffff00ff, 0);
// shift hips according to the _hipsOffset from the previous frame // shift hips according to the _hipsOffset from the previous frame
float offsetLength = glm::length(_hipsOffset); float offsetLength = glm::length(_hipsOffset);
@ -476,12 +476,12 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
} }
{ {
PROFILE_RANGE_EX("ik/ccd", 0xffff00ff, 0); PROFILE_RANGE_EX(simulation_animation, "ik/ccd", 0xffff00ff, 0);
solveWithCyclicCoordinateDescent(targets); solveWithCyclicCoordinateDescent(targets);
} }
{ {
PROFILE_RANGE_EX("ik/measureHipsOffset", 0xffff00ff, 0); PROFILE_RANGE_EX(simulation_animation, "ik/measureHipsOffset", 0xffff00ff, 0);
// measure new _hipsOffset for next frame // measure new _hipsOffset for next frame
// by looking for discrepancies between where a targeted endEffector is // by looking for discrepancies between where a targeted endEffector is

View file

@ -14,6 +14,9 @@
#include "AnimationCache.h" #include "AnimationCache.h"
#include "AnimationLogging.h" #include "AnimationLogging.h"
#include <Trace.h>
#include <StatTracker.h>
#include <Profile.h>
int animationPointerMetaTypeId = qRegisterMetaType<AnimationPointer>(); int animationPointerMetaTypeId = qRegisterMetaType<AnimationPointer>();
@ -45,9 +48,14 @@ Animation::Animation(const QUrl& url) : Resource(url) {}
AnimationReader::AnimationReader(const QUrl& url, const QByteArray& data) : AnimationReader::AnimationReader(const QUrl& url, const QByteArray& data) :
_url(url), _url(url),
_data(data) { _data(data) {
DependencyManager::get<StatTracker>()->incrementStat("PendingProcessing");
} }
void AnimationReader::run() { void AnimationReader::run() {
DependencyManager::get<StatTracker>()->decrementStat("PendingProcessing");
CounterStat counter("Processing");
PROFILE_RANGE_EX(resource_parse, __FUNCTION__, 0xFF00FF00, 0, { { "url", _url.toString() } });
auto originalPriority = QThread::currentThread()->priority(); auto originalPriority = QThread::currentThread()->priority();
if (originalPriority == QThread::InheritPriority) { if (originalPriority == QThread::InheritPriority) {
originalPriority = QThread::NormalPriority; originalPriority = QThread::NormalPriority;

View file

@ -65,6 +65,8 @@ public:
explicit Animation(const QUrl& url); explicit Animation(const QUrl& url);
QString getType() const override { return "Animation"; }
const FBXGeometry& getGeometry() const { return *_geometry; } const FBXGeometry& getGeometry() const { return *_geometry; }
virtual bool isLoaded() const override; virtual bool isLoaded() const override;

View file

@ -20,6 +20,7 @@
#include <GeometryUtil.h> #include <GeometryUtil.h>
#include <NumericalConstants.h> #include <NumericalConstants.h>
#include <DebugDraw.h> #include <DebugDraw.h>
#include <PerfStat.h>
#include <ScriptValueUtils.h> #include <ScriptValueUtils.h>
#include <shared/NsightHelpers.h> #include <shared/NsightHelpers.h>
@ -882,7 +883,7 @@ void Rig::updateAnimationStateHandlers() { // called on avatar update thread (wh
void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) {
PROFILE_RANGE_EX(__FUNCTION__, 0xffff00ff, 0); PROFILE_RANGE_EX(simulation_animation, __FUNCTION__, 0xffff00ff, 0);
setModelOffset(rootTransform); setModelOffset(rootTransform);
@ -1249,6 +1250,7 @@ void Rig::copyJointsIntoJointData(QVector<JointData>& jointDataVec) const {
} }
void Rig::copyJointsFromJointData(const QVector<JointData>& jointDataVec) { void Rig::copyJointsFromJointData(const QVector<JointData>& jointDataVec) {
PerformanceTimer perfTimer("copyJoints");
if (_animSkeleton && jointDataVec.size() == (int)_internalPoseSet._overrideFlags.size()) { if (_animSkeleton && jointDataVec.size() == (int)_internalPoseSet._overrideFlags.size()) {
// transform all the default poses into rig space. // transform all the default poses into rig space.

View file

@ -1135,8 +1135,9 @@ void AudioClient::mixLocalAudioInjectors(float* mixBuffer) {
} else if (injector->isStereo()) { } else if (injector->isStereo()) {
// stereo gets directly mixed into mixBuffer // stereo gets directly mixed into mixBuffer
float gain = injector->getVolume();
for (int i = 0; i < AudioConstants::NETWORK_FRAME_SAMPLES_STEREO; i++) { for (int i = 0; i < AudioConstants::NETWORK_FRAME_SAMPLES_STEREO; i++) {
mixBuffer[i] += (float)_scratchBuffer[i] * (1/32768.0f); mixBuffer[i] += (float)_scratchBuffer[i] * (1/32768.0f) * gain;
} }
} else { } else {

View file

@ -166,7 +166,6 @@ bool AudioInjector::injectLocally() {
_localBuffer->open(QIODevice::ReadOnly); _localBuffer->open(QIODevice::ReadOnly);
_localBuffer->setShouldLoop(_options.loop); _localBuffer->setShouldLoop(_options.loop);
_localBuffer->setVolume(_options.volume);
// give our current send position to the local buffer // give our current send position to the local buffer
_localBuffer->setCurrentOffset(_currentSendOffset); _localBuffer->setCurrentOffset(_currentSendOffset);

View file

@ -63,7 +63,7 @@ public:
AudioFOA& getLocalFOA() { return _localFOA; } AudioFOA& getLocalFOA() { return _localFOA; }
bool isLocalOnly() const { return _options.localOnly; } bool isLocalOnly() const { return _options.localOnly; }
float getVolume() const { return _options.volume; } float getVolume() const { return glm::clamp(_options.volume, 0.0f, 1.0f); }
glm::vec3 getPosition() const { return _options.position; } glm::vec3 getPosition() const { return _options.position; }
glm::quat getOrientation() const { return _options.orientation; } glm::quat getOrientation() const { return _options.orientation; }
bool isStereo() const { return _options.stereo; } bool isStereo() const { return _options.stereo; }

View file

@ -16,8 +16,7 @@ AudioInjectorLocalBuffer::AudioInjectorLocalBuffer(const QByteArray& rawAudioArr
_rawAudioArray(rawAudioArray), _rawAudioArray(rawAudioArray),
_shouldLoop(false), _shouldLoop(false),
_isStopped(false), _isStopped(false),
_currentOffset(0), _currentOffset(0)
_volume(1.0f)
{ {
} }
@ -36,17 +35,6 @@ bool AudioInjectorLocalBuffer::seek(qint64 pos) {
} }
} }
void copy(char* to, char* from, int size, qreal factor) {
int16_t* toArray = (int16_t*) to;
int16_t* fromArray = (int16_t*) from;
int sampleSize = size / sizeof(int16_t);
for (int i = 0; i < sampleSize; i++) {
*toArray = factor * (*fromArray);
toArray++;
fromArray++;
}
}
qint64 AudioInjectorLocalBuffer::readData(char* data, qint64 maxSize) { qint64 AudioInjectorLocalBuffer::readData(char* data, qint64 maxSize) {
if (!_isStopped) { if (!_isStopped) {
@ -60,7 +48,7 @@ qint64 AudioInjectorLocalBuffer::readData(char* data, qint64 maxSize) {
bytesRead = bytesToEnd; bytesRead = bytesToEnd;
} }
copy(data, _rawAudioArray.data() + _currentOffset, bytesRead, _volume); memcpy(data, _rawAudioArray.data() + _currentOffset, bytesRead);
// now check if we are supposed to loop and if we can copy more from the beginning // now check if we are supposed to loop and if we can copy more from the beginning
if (_shouldLoop && maxSize != bytesRead) { if (_shouldLoop && maxSize != bytesRead) {
@ -88,7 +76,7 @@ qint64 AudioInjectorLocalBuffer::recursiveReadFromFront(char* data, qint64 maxSi
} }
// copy that amount // copy that amount
copy(data, _rawAudioArray.data(), bytesRead, _volume); memcpy(data, _rawAudioArray.data(), bytesRead);
// check if we need to call ourselves again and pull from the front again // check if we need to call ourselves again and pull from the front again
if (bytesRead < maxSize) { if (bytesRead < maxSize) {
@ -97,4 +85,4 @@ qint64 AudioInjectorLocalBuffer::recursiveReadFromFront(char* data, qint64 maxSi
_currentOffset = bytesRead; _currentOffset = bytesRead;
return bytesRead; return bytesRead;
} }
} }

View file

@ -30,7 +30,6 @@ public:
void setShouldLoop(bool shouldLoop) { _shouldLoop = shouldLoop; } void setShouldLoop(bool shouldLoop) { _shouldLoop = shouldLoop; }
void setCurrentOffset(int currentOffset) { _currentOffset = currentOffset; } void setCurrentOffset(int currentOffset) { _currentOffset = currentOffset; }
void setVolume(float volume) { _volume = glm::clamp(volume, 0.0f, 1.0f); }
private: private:
qint64 recursiveReadFromFront(char* data, qint64 maxSize); qint64 recursiveReadFromFront(char* data, qint64 maxSize);
@ -40,7 +39,6 @@ private:
bool _isStopped; bool _isStopped;
int _currentOffset; int _currentOffset;
float _volume;
}; };
#endif // hifi_AudioInjectorLocalBuffer_h #endif // hifi_AudioInjectorLocalBuffer_h

File diff suppressed because it is too large Load diff

View file

@ -14,7 +14,7 @@
#include <stdint.h> #include <stdint.h>
static const int SRC_MAX_CHANNELS = 2; static const int SRC_MAX_CHANNELS = 4;
// polyphase filter // polyphase filter
static const int SRC_PHASEBITS = 8; static const int SRC_PHASEBITS = 8;
@ -48,8 +48,6 @@ public:
int getMinInput(int outputFrames); int getMinInput(int outputFrames);
int getMaxInput(int outputFrames); int getMaxInput(int outputFrames);
int getExactInput(int outputFrames);
private: private:
float* _polyphaseFilter; float* _polyphaseFilter;
int* _stepTable; int* _stepTable;
@ -77,12 +75,18 @@ private:
int multirateFilter1(const float* input0, float* output0, int inputFrames); int multirateFilter1(const float* input0, float* output0, int inputFrames);
int multirateFilter2(const float* input0, const float* input1, float* output0, float* output1, int inputFrames); int multirateFilter2(const float* input0, const float* input1, float* output0, float* output1, int inputFrames);
int multirateFilter4(const float* input0, const float* input1, const float* input2, const float* input3,
float* output0, float* output1, float* output2, float* output3, int inputFrames);
int multirateFilter1_SSE(const float* input0, float* output0, int inputFrames); int multirateFilter1_ref(const float* input0, float* output0, int inputFrames);
int multirateFilter2_SSE(const float* input0, const float* input1, float* output0, float* output1, int inputFrames); int multirateFilter2_ref(const float* input0, const float* input1, float* output0, float* output1, int inputFrames);
int multirateFilter4_ref(const float* input0, const float* input1, const float* input2, const float* input3,
float* output0, float* output1, float* output2, float* output3, int inputFrames);
int multirateFilter1_AVX2(const float* input0, float* output0, int inputFrames); int multirateFilter1_AVX2(const float* input0, float* output0, int inputFrames);
int multirateFilter2_AVX2(const float* input0, const float* input1, float* output0, float* output1, int inputFrames); int multirateFilter2_AVX2(const float* input0, const float* input1, float* output0, float* output1, int inputFrames);
int multirateFilter4_AVX2(const float* input0, const float* input1, const float* input2, const float* input3,
float* output0, float* output1, float* output2, float* output3, int inputFrames);
void convertInput(const int16_t* input, float** outputs, int numFrames); void convertInput(const int16_t* input, float** outputs, int numFrames);
void convertOutput(float** inputs, int16_t* output, int numFrames); void convertOutput(float** inputs, int16_t* output, int numFrames);

View file

@ -97,54 +97,9 @@ void Sound::downSample(const QByteArray& rawAudioByteArray, int sampleRate) {
// no resampling needed // no resampling needed
_byteArray = rawAudioByteArray; _byteArray = rawAudioByteArray;
} else if (_isAmbisonic) {
// FIXME: add a proper Ambisonic resampler!
int numChannels = 4;
AudioSRC resampler[4] { {sampleRate, AudioConstants::SAMPLE_RATE, 1},
{sampleRate, AudioConstants::SAMPLE_RATE, 1},
{sampleRate, AudioConstants::SAMPLE_RATE, 1},
{sampleRate, AudioConstants::SAMPLE_RATE, 1} };
// resize to max possible output
int numSourceFrames = rawAudioByteArray.size() / (numChannels * sizeof(AudioConstants::AudioSample));
int maxDestinationFrames = resampler[0].getMaxOutput(numSourceFrames);
int maxDestinationBytes = maxDestinationFrames * numChannels * sizeof(AudioConstants::AudioSample);
_byteArray.resize(maxDestinationBytes);
int numDestinationFrames = 0;
// iterate over channels
int16_t* srcBuffer = new int16_t[numSourceFrames];
int16_t* dstBuffer = new int16_t[maxDestinationFrames];
for (int ch = 0; ch < 4; ch++) {
int16_t* src = (int16_t*)rawAudioByteArray.data();
int16_t* dst = (int16_t*)_byteArray.data();
// deinterleave samples
for (int i = 0; i < numSourceFrames; i++) {
srcBuffer[i] = src[4*i + ch];
}
// resample one channel
numDestinationFrames = resampler[ch].render(srcBuffer, dstBuffer, numSourceFrames);
// reinterleave samples
for (int i = 0; i < numDestinationFrames; i++) {
dst[4*i + ch] = dstBuffer[i];
}
}
delete[] srcBuffer;
delete[] dstBuffer;
// truncate to actual output
int numDestinationBytes = numDestinationFrames * numChannels * sizeof(AudioConstants::AudioSample);
_byteArray.resize(numDestinationBytes);
} else { } else {
int numChannels = _isStereo ? 2 : 1; int numChannels = _isAmbisonic ? AudioConstants::AMBISONIC : (_isStereo ? AudioConstants::STEREO : AudioConstants::MONO);
AudioSRC resampler(sampleRate, AudioConstants::SAMPLE_RATE, numChannels); AudioSRC resampler(sampleRate, AudioConstants::SAMPLE_RATE, numChannels);
// resize to max possible output // resize to max possible output

View file

@ -198,4 +198,109 @@ int AudioSRC::multirateFilter2_AVX2(const float* input0, const float* input1, fl
return outputFrames; return outputFrames;
} }
int AudioSRC::multirateFilter4_AVX2(const float* input0, const float* input1, const float* input2, const float* input3,
float* output0, float* output1, float* output2, float* output3, int inputFrames) {
int outputFrames = 0;
assert(_numTaps % 8 == 0); // SIMD8
if (_step == 0) { // rational
int32_t i = HI32(_offset);
while (i < inputFrames) {
const float* c0 = &_polyphaseFilter[_numTaps * _phase];
__m256 acc0 = _mm256_setzero_ps();
__m256 acc1 = _mm256_setzero_ps();
__m256 acc2 = _mm256_setzero_ps();
__m256 acc3 = _mm256_setzero_ps();
for (int j = 0; j < _numTaps; j += 8) {
//float coef = c0[j];
__m256 coef0 = _mm256_loadu_ps(&c0[j]);
//acc += input[i + j] * coef;
acc0 = _mm256_fmadd_ps(_mm256_loadu_ps(&input0[i + j]), coef0, acc0);
acc1 = _mm256_fmadd_ps(_mm256_loadu_ps(&input1[i + j]), coef0, acc1);
acc2 = _mm256_fmadd_ps(_mm256_loadu_ps(&input2[i + j]), coef0, acc2);
acc3 = _mm256_fmadd_ps(_mm256_loadu_ps(&input3[i + j]), coef0, acc3);
}
// horizontal sum
acc0 = _mm256_hadd_ps(acc0, acc1);
acc2 = _mm256_hadd_ps(acc2, acc3);
acc0 = _mm256_hadd_ps(acc0, acc2);
__m128 t0 = _mm_add_ps(_mm256_castps256_ps128(acc0), _mm256_extractf128_ps(acc0, 1));
_mm_store_ss(&output0[outputFrames], t0);
_mm_store_ss(&output1[outputFrames], _mm_shuffle_ps(t0, t0, _MM_SHUFFLE(0,0,0,1)));
_mm_store_ss(&output2[outputFrames], _mm_shuffle_ps(t0, t0, _MM_SHUFFLE(0,0,0,2)));
_mm_store_ss(&output3[outputFrames], _mm_shuffle_ps(t0, t0, _MM_SHUFFLE(0,0,0,3)));
outputFrames += 1;
i += _stepTable[_phase];
if (++_phase == _upFactor) {
_phase = 0;
}
}
_offset = (int64_t)(i - inputFrames) << 32;
} else { // irrational
while (HI32(_offset) < inputFrames) {
int32_t i = HI32(_offset);
uint32_t f = LO32(_offset);
uint32_t phase = f >> SRC_FRACBITS;
float ftmp = (f & SRC_FRACMASK) * QFRAC_TO_FLOAT;
const float* c0 = &_polyphaseFilter[_numTaps * (phase + 0)];
const float* c1 = &_polyphaseFilter[_numTaps * (phase + 1)];
__m256 acc0 = _mm256_setzero_ps();
__m256 acc1 = _mm256_setzero_ps();
__m256 acc2 = _mm256_setzero_ps();
__m256 acc3 = _mm256_setzero_ps();
__m256 frac = _mm256_broadcast_ss(&ftmp);
for (int j = 0; j < _numTaps; j += 8) {
//float coef = c0[j] + frac * (c1[j] - c0[j]);
__m256 coef0 = _mm256_loadu_ps(&c0[j]);
__m256 coef1 = _mm256_loadu_ps(&c1[j]);
coef1 = _mm256_sub_ps(coef1, coef0);
coef0 = _mm256_fmadd_ps(coef1, frac, coef0);
//acc += input[i + j] * coef;
acc0 = _mm256_fmadd_ps(_mm256_loadu_ps(&input0[i + j]), coef0, acc0);
acc1 = _mm256_fmadd_ps(_mm256_loadu_ps(&input1[i + j]), coef0, acc1);
acc2 = _mm256_fmadd_ps(_mm256_loadu_ps(&input2[i + j]), coef0, acc2);
acc3 = _mm256_fmadd_ps(_mm256_loadu_ps(&input3[i + j]), coef0, acc3);
}
// horizontal sum
acc0 = _mm256_hadd_ps(acc0, acc1);
acc2 = _mm256_hadd_ps(acc2, acc3);
acc0 = _mm256_hadd_ps(acc0, acc2);
__m128 t0 = _mm_add_ps(_mm256_castps256_ps128(acc0), _mm256_extractf128_ps(acc0, 1));
_mm_store_ss(&output0[outputFrames], t0);
_mm_store_ss(&output1[outputFrames], _mm_shuffle_ps(t0, t0, _MM_SHUFFLE(0,0,0,1)));
_mm_store_ss(&output2[outputFrames], _mm_shuffle_ps(t0, t0, _MM_SHUFFLE(0,0,0,2)));
_mm_store_ss(&output3[outputFrames], _mm_shuffle_ps(t0, t0, _MM_SHUFFLE(0,0,0,3)));
outputFrames += 1;
_offset += _step;
}
_offset -= (int64_t)inputFrames << 32;
}
_mm256_zeroupper();
return outputFrames;
}
#endif #endif

View file

@ -13,6 +13,7 @@
#include <NodeList.h> #include <NodeList.h>
#include <udt/PacketHeaders.h> #include <udt/PacketHeaders.h>
#include <PerfStat.h>
#include <SharedUtil.h> #include <SharedUtil.h>
#include "AvatarLogging.h" #include "AvatarLogging.h"
@ -98,6 +99,7 @@ AvatarSharedPointer AvatarHashMap::findAvatar(const QUuid& sessionUUID) {
} }
void AvatarHashMap::processAvatarDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) { void AvatarHashMap::processAvatarDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
PerformanceTimer perfTimer("receiveAvatar");
// enumerate over all of the avatars in this packet // enumerate over all of the avatars in this packet
// only add them if mixerWeakPointer points to something (meaning that mixer is still around) // only add them if mixerWeakPointer points to something (meaning that mixer is still around)
while (message->getBytesLeftToRead()) { while (message->getBytesLeftToRead()) {

View file

@ -16,7 +16,6 @@
#include <QtOpenGL/QGLWidget> #include <QtOpenGL/QGLWidget>
#include <QtGui/QImage> #include <QtGui/QImage>
#if defined(Q_OS_MAC) #if defined(Q_OS_MAC)
#include <OpenGL/CGLCurrent.h> #include <OpenGL/CGLCurrent.h>
#endif #endif
@ -44,6 +43,7 @@
#include <CursorManager.h> #include <CursorManager.h>
#include "CompositorHelper.h" #include "CompositorHelper.h"
#include "Logging.h"
const char* SRGB_TO_LINEAR_FRAG = R"SCRIBE( const char* SRGB_TO_LINEAR_FRAG = R"SCRIBE(
@ -129,7 +129,7 @@ public:
_context->makeCurrent(); _context->makeCurrent();
while (!_shutdown) { while (!_shutdown) {
if (_pendingMainThreadOperation) { if (_pendingMainThreadOperation) {
PROFILE_RANGE("MainThreadOp") PROFILE_RANGE(render, "MainThreadOp")
{ {
Lock lock(_mutex); Lock lock(_mutex);
_context->doneCurrent(); _context->doneCurrent();
@ -203,7 +203,7 @@ public:
// Execute the frame and present it to the display device. // Execute the frame and present it to the display device.
_context->makeCurrent(); _context->makeCurrent();
{ {
PROFILE_RANGE("PluginPresent") PROFILE_RANGE(render, "PluginPresent")
currentPlugin->present(); currentPlugin->present();
CHECK_GL_ERROR(); CHECK_GL_ERROR();
} }
@ -560,22 +560,22 @@ void OpenGLDisplayPlugin::compositeLayers() {
updateCompositeFramebuffer(); updateCompositeFramebuffer();
{ {
PROFILE_RANGE_EX("compositeScene", 0xff0077ff, (uint64_t)presentCount()) PROFILE_RANGE_EX(render, "compositeScene", 0xff0077ff, (uint64_t)presentCount())
compositeScene(); compositeScene();
} }
{ {
PROFILE_RANGE_EX("compositeOverlay", 0xff0077ff, (uint64_t)presentCount()) PROFILE_RANGE_EX(render, "compositeOverlay", 0xff0077ff, (uint64_t)presentCount())
compositeOverlay(); compositeOverlay();
} }
auto compositorHelper = DependencyManager::get<CompositorHelper>(); auto compositorHelper = DependencyManager::get<CompositorHelper>();
if (compositorHelper->getReticleVisible()) { if (compositorHelper->getReticleVisible()) {
PROFILE_RANGE_EX("compositePointer", 0xff0077ff, (uint64_t)presentCount()) PROFILE_RANGE_EX(render, "compositePointer", 0xff0077ff, (uint64_t)presentCount())
compositePointer(); compositePointer();
} }
{ {
PROFILE_RANGE_EX("compositeExtra", 0xff0077ff, (uint64_t)presentCount()) PROFILE_RANGE_EX(render, "compositeExtra", 0xff0077ff, (uint64_t)presentCount())
compositeExtra(); compositeExtra();
} }
} }
@ -595,12 +595,12 @@ void OpenGLDisplayPlugin::internalPresent() {
} }
void OpenGLDisplayPlugin::present() { void OpenGLDisplayPlugin::present() {
PROFILE_RANGE_EX(__FUNCTION__, 0xffffff00, (uint64_t)presentCount()) PROFILE_RANGE_EX(render, __FUNCTION__, 0xffffff00, (uint64_t)presentCount())
updateFrameData(); updateFrameData();
incrementPresentCount(); incrementPresentCount();
{ {
PROFILE_RANGE_EX("recycle", 0xff00ff00, (uint64_t)presentCount()) PROFILE_RANGE_EX(render, "recycle", 0xff00ff00, (uint64_t)presentCount())
_gpuContext->recycle(); _gpuContext->recycle();
} }
@ -614,19 +614,19 @@ void OpenGLDisplayPlugin::present() {
_lastFrame = _currentFrame.get(); _lastFrame = _currentFrame.get();
}); });
// Execute the frame rendering commands // Execute the frame rendering commands
PROFILE_RANGE_EX("execute", 0xff00ff00, (uint64_t)presentCount()) PROFILE_RANGE_EX(render, "execute", 0xff00ff00, (uint64_t)presentCount())
_gpuContext->executeFrame(_currentFrame); _gpuContext->executeFrame(_currentFrame);
} }
// Write all layers to a local framebuffer // Write all layers to a local framebuffer
{ {
PROFILE_RANGE_EX("composite", 0xff00ffff, (uint64_t)presentCount()) PROFILE_RANGE_EX(render, "composite", 0xff00ffff, (uint64_t)presentCount())
compositeLayers(); compositeLayers();
} }
// Take the composite framebuffer and send it to the output device // Take the composite framebuffer and send it to the output device
{ {
PROFILE_RANGE_EX("internalPresent", 0xff00ffff, (uint64_t)presentCount()) PROFILE_RANGE_EX(render, "internalPresent", 0xff00ffff, (uint64_t)presentCount())
internalPresent(); internalPresent();
} }

View file

@ -206,7 +206,7 @@ float HmdDisplayPlugin::getLeftCenterPixel() const {
} }
void HmdDisplayPlugin::internalPresent() { void HmdDisplayPlugin::internalPresent() {
PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)presentCount()) PROFILE_RANGE_EX(render, __FUNCTION__, 0xff00ff00, (uint64_t)presentCount())
// Composite together the scene, overlay and mouse cursor // Composite together the scene, overlay and mouse cursor
hmdPresent(); hmdPresent();

View file

@ -836,6 +836,7 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) {
// If the new hover entity does not match the previous hover entity then we are entering the new one // If the new hover entity does not match the previous hover entity then we are entering the new one
// this is true if the _currentHoverOverEntityID is known or unknown // this is true if the _currentHoverOverEntityID is known or unknown
if (rayPickResult.entityID != _currentHoverOverEntityID) { if (rayPickResult.entityID != _currentHoverOverEntityID) {
emit hoverEnterEntity(rayPickResult.entityID, pointerEvent);
if (_entitiesScriptEngine) { if (_entitiesScriptEngine) {
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "hoverEnterEntity", pointerEvent); _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "hoverEnterEntity", pointerEvent);
} }

View file

@ -211,7 +211,7 @@ namespace render {
template <> void payloadRender(const RenderableModelEntityItemMeta::Pointer& payload, RenderArgs* args) { template <> void payloadRender(const RenderableModelEntityItemMeta::Pointer& payload, RenderArgs* args) {
if (args) { if (args) {
if (payload && payload->entity) { if (payload && payload->entity) {
PROFILE_RANGE("MetaModelRender"); PROFILE_RANGE(render, "MetaModelRender");
payload->entity->render(args); payload->entity->render(args);
} }
} }
@ -533,7 +533,8 @@ void RenderableModelEntityItem::update(const quint64& now) {
properties.setLastEdited(usecTimestampNow()); // we must set the edit time since we're editing it properties.setLastEdited(usecTimestampNow()); // we must set the edit time since we're editing it
auto extents = _model->getMeshExtents(); auto extents = _model->getMeshExtents();
properties.setDimensions(extents.maximum - extents.minimum); properties.setDimensions(extents.maximum - extents.minimum);
qCDebug(entitiesrenderer) << "Autoresizing:" << (!getName().isEmpty() ? getName() : getModelURL()); qCDebug(entitiesrenderer) << "Autoresizing" << (!getName().isEmpty() ? getName() : getModelURL())
<< "from mesh extents";
QMetaObject::invokeMethod(DependencyManager::get<EntityScriptingInterface>().data(), "editEntity", QMetaObject::invokeMethod(DependencyManager::get<EntityScriptingInterface>().data(), "editEntity",
Qt::QueuedConnection, Qt::QueuedConnection,
Q_ARG(QUuid, getEntityItemID()), Q_ARG(QUuid, getEntityItemID()),
@ -1169,6 +1170,7 @@ void RenderableModelEntityItem::setJointTranslationsSet(const QVector<bool>& tra
void RenderableModelEntityItem::locationChanged(bool tellPhysics) { void RenderableModelEntityItem::locationChanged(bool tellPhysics) {
PerformanceTimer pertTimer("locationChanged");
EntityItem::locationChanged(tellPhysics); EntityItem::locationChanged(tellPhysics);
if (_model && _model->isActive()) { if (_model && _model->isActive()) {
_model->setRotation(getRotation()); _model->setRotation(getRotation());

View file

@ -350,8 +350,6 @@ void RenderableWebEntityItem::handlePointerEvent(const PointerEvent& event) {
touchEvent->setTouchPoints(touchPoints); touchEvent->setTouchPoints(touchPoints);
touchEvent->setTouchPointStates(touchPointState); touchEvent->setTouchPointStates(touchPointState);
_lastTouchEvent = *touchEvent;
QCoreApplication::postEvent(_webSurface->getWindow(), touchEvent); QCoreApplication::postEvent(_webSurface->getWindow(), touchEvent);
} }
} }

View file

@ -65,9 +65,7 @@ private:
QSharedPointer<OffscreenQmlSurface> _webSurface; QSharedPointer<OffscreenQmlSurface> _webSurface;
QMetaObject::Connection _connection; QMetaObject::Connection _connection;
gpu::TexturePointer _texture; gpu::TexturePointer _texture;
ivec2 _lastPress { INT_MIN };
bool _pressed{ false }; bool _pressed{ false };
QTouchEvent _lastTouchEvent { QEvent::TouchUpdate };
uint64_t _lastRenderTime{ 0 }; uint64_t _lastRenderTime{ 0 };
QTouchDevice _touchDevice; QTouchDevice _touchDevice;

View file

@ -316,7 +316,7 @@ private:
float _localRenderAlpha; float _localRenderAlpha;
bool _localRenderAlphaChanged; bool _localRenderAlphaChanged;
bool _defaultSettings; bool _defaultSettings;
bool _dimensionsInitialized = true; // Only false if creating an entity localy with no dimensions properties bool _dimensionsInitialized = true; // Only false if creating an entity locally with no dimensions properties
// NOTE: The following are pseudo client only properties. They are only used in clients which can access // NOTE: The following are pseudo client only properties. They are only used in clients which can access
// properties of model geometry. But these properties are not serialized like other properties. // properties of model geometry. But these properties are not serialized like other properties.

View file

@ -35,6 +35,7 @@ EntityScriptingInterface::EntityScriptingInterface(bool bidOnSimulationOwnership
connect(nodeList.data(), &NodeList::isAllowedEditorChanged, this, &EntityScriptingInterface::canAdjustLocksChanged); connect(nodeList.data(), &NodeList::isAllowedEditorChanged, this, &EntityScriptingInterface::canAdjustLocksChanged);
connect(nodeList.data(), &NodeList::canRezChanged, this, &EntityScriptingInterface::canRezChanged); connect(nodeList.data(), &NodeList::canRezChanged, this, &EntityScriptingInterface::canRezChanged);
connect(nodeList.data(), &NodeList::canRezTmpChanged, this, &EntityScriptingInterface::canRezTmpChanged); connect(nodeList.data(), &NodeList::canRezTmpChanged, this, &EntityScriptingInterface::canRezTmpChanged);
connect(nodeList.data(), &NodeList::canWriteAssetsChanged, this, &EntityScriptingInterface::canWriteAssetsChanged);
} }
void EntityScriptingInterface::queueEntityMessage(PacketType packetType, void EntityScriptingInterface::queueEntityMessage(PacketType packetType,
@ -63,6 +64,11 @@ bool EntityScriptingInterface::canRezTmp() {
return nodeList->getThisNodeCanRezTmp(); return nodeList->getThisNodeCanRezTmp();
} }
bool EntityScriptingInterface::canWriteAssets() {
auto nodeList = DependencyManager::get<NodeList>();
return nodeList->getThisNodeCanWriteAssets();
}
void EntityScriptingInterface::setEntityTree(EntityTreePointer elementTree) { void EntityScriptingInterface::setEntityTree(EntityTreePointer elementTree) {
if (_entityTree) { if (_entityTree) {
disconnect(_entityTree.get(), &EntityTree::addingEntity, this, &EntityScriptingInterface::addingEntity); disconnect(_entityTree.get(), &EntityTree::addingEntity, this, &EntityScriptingInterface::addingEntity);

View file

@ -110,6 +110,12 @@ public slots:
*/ */
Q_INVOKABLE bool canRezTmp(); Q_INVOKABLE bool canRezTmp();
/**jsdoc
* @function Entities.canWriteAsseets
* @return {bool} `true` if the DomainServer will allow this Node/Avatar to write to the asset server
*/
Q_INVOKABLE bool canWriteAssets();
/**jsdoc /**jsdoc
* Add a new entity with the specified properties. If `clientOnly` is true, the entity will * Add a new entity with the specified properties. If `clientOnly` is true, the entity will
* not be sent to the server and will only be visible/accessible on the local client. * not be sent to the server and will only be visible/accessible on the local client.
@ -284,6 +290,7 @@ signals:
void canAdjustLocksChanged(bool canAdjustLocks); void canAdjustLocksChanged(bool canAdjustLocks);
void canRezChanged(bool canRez); void canRezChanged(bool canRez);
void canRezTmpChanged(bool canRez); void canRezTmpChanged(bool canRez);
void canWriteAssetsChanged(bool canWriteAssets);
void mousePressOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); void mousePressOnEntity(const EntityItemID& entityItemID, const PointerEvent& event);
void mouseMoveOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); void mouseMoveOnEntity(const EntityItemID& entityItemID, const PointerEvent& event);

View file

@ -321,7 +321,7 @@ FBXNode parseTextFBXNode(Tokenizer& tokenizer) {
} }
FBXNode FBXReader::parseFBX(QIODevice* device) { FBXNode FBXReader::parseFBX(QIODevice* device) {
PROFILE_RANGE_EX(__FUNCTION__, 0xff0000ff, device); PROFILE_RANGE_EX(resource_parse, __FUNCTION__, 0xff0000ff, device);
// verify the prolog // verify the prolog
const QByteArray BINARY_PROLOG = "Kaydara FBX Binary "; const QByteArray BINARY_PROLOG = "Kaydara FBX Binary ";
if (device->peek(BINARY_PROLOG.size()) != BINARY_PROLOG) { if (device->peek(BINARY_PROLOG.size()) != BINARY_PROLOG) {

View file

@ -420,7 +420,7 @@ done:
FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, const QUrl& url) { FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, const QUrl& url) {
PROFILE_RANGE_EX(__FUNCTION__, 0xffff0000, nullptr); PROFILE_RANGE_EX(resource_parse, __FUNCTION__, 0xffff0000, nullptr);
QBuffer buffer { &model }; QBuffer buffer { &model };
buffer.open(QIODevice::ReadOnly); buffer.open(QIODevice::ReadOnly);

View file

@ -11,4 +11,4 @@
#include "GLLogging.h" #include "GLLogging.h"
Q_LOGGING_CATEGORY(glLogging, "hifi.glLogging") Q_LOGGING_CATEGORY(glLogging, "hifi.gl")

View file

@ -38,6 +38,9 @@
#include "GLLogging.h" #include "GLLogging.h"
#include "Context.h" #include "Context.h"
Q_LOGGING_CATEGORY(trace_render_qml, "trace.render.qml")
Q_LOGGING_CATEGORY(trace_render_qml_gl, "trace.render.qml.gl")
struct TextureSet { struct TextureSet {
// The number of surfaces with this size // The number of surfaces with this size
size_t count { 0 }; size_t count { 0 };
@ -276,6 +279,7 @@ void OffscreenQmlSurface::render() {
return; return;
} }
PROFILE_RANGE(render_qml_gl, __FUNCTION__)
_canvas->makeCurrent(); _canvas->makeCurrent();
_renderControl->sync(); _renderControl->sync();
@ -284,7 +288,6 @@ void OffscreenQmlSurface::render() {
GLuint texture = offscreenTextures.getNextTexture(_size); GLuint texture = offscreenTextures.getNextTexture(_size);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _fbo); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _fbo);
glFramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0); glFramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0);
PROFILE_RANGE("qml_render->rendercontrol")
_renderControl->render(); _renderControl->render();
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBindTexture(GL_TEXTURE_2D, texture); glBindTexture(GL_TEXTURE_2D, texture);
@ -617,12 +620,12 @@ void OffscreenQmlSurface::updateQuick() {
} }
if (_polish) { if (_polish) {
PROFILE_RANGE(render_qml, "OffscreenQML polish")
_renderControl->polishItems(); _renderControl->polishItems();
_polish = false; _polish = false;
} }
if (_render) { if (_render) {
PROFILE_RANGE(__FUNCTION__);
render(); render();
_render = false; _render = false;
} }

View file

@ -30,6 +30,7 @@
#include "GLTexture.h" #include "GLTexture.h"
#include "GLShader.h" #include "GLShader.h"
using namespace gpu; using namespace gpu;
using namespace gpu::gl; using namespace gpu::gl;
@ -199,7 +200,7 @@ void GLBackend::renderPassTransfer(const Batch& batch) {
_inRenderTransferPass = true; _inRenderTransferPass = true;
{ // Sync all the buffers { // Sync all the buffers
PROFILE_RANGE("syncGPUBuffer"); PROFILE_RANGE(render_gpu_gl, "syncGPUBuffer");
for (auto& cached : batch._buffers._items) { for (auto& cached : batch._buffers._items) {
if (cached._data) { if (cached._data) {
@ -209,7 +210,7 @@ void GLBackend::renderPassTransfer(const Batch& batch) {
} }
{ // Sync all the buffers { // Sync all the buffers
PROFILE_RANGE("syncCPUTransform"); PROFILE_RANGE(render_gpu_gl, "syncCPUTransform");
_transform._cameras.clear(); _transform._cameras.clear();
_transform._cameraOffsets.clear(); _transform._cameraOffsets.clear();
@ -241,7 +242,7 @@ void GLBackend::renderPassTransfer(const Batch& batch) {
} }
{ // Sync the transform buffers { // Sync the transform buffers
PROFILE_RANGE("syncGPUTransform"); PROFILE_RANGE(render_gpu_gl, "syncGPUTransform");
transferTransformState(batch); transferTransformState(batch);
} }
@ -303,7 +304,7 @@ void GLBackend::render(const Batch& batch) {
} }
{ {
PROFILE_RANGE("Transfer"); PROFILE_RANGE(render_gpu_gl, "Transfer");
renderPassTransfer(batch); renderPassTransfer(batch);
} }
@ -313,7 +314,7 @@ void GLBackend::render(const Batch& batch) {
} }
#endif #endif
{ {
PROFILE_RANGE(_stereo._enable ? "Render Stereo" : "Render"); PROFILE_RANGE(render_gpu_gl, _stereo._enable ? "Render Stereo" : "Render");
renderPassDraw(batch); renderPassDraw(batch);
} }
#ifdef GPU_STEREO_DRAWCALL_INSTANCED #ifdef GPU_STEREO_DRAWCALL_INSTANCED

View file

@ -10,6 +10,7 @@
// //
#include "GLBackend.h" #include "GLBackend.h"
#include "GLQuery.h" #include "GLQuery.h"
#include "GLShared.h"
using namespace gpu; using namespace gpu;
using namespace gpu::gl; using namespace gpu::gl;
@ -27,7 +28,7 @@ void GLBackend::do_beginQuery(const Batch& batch, size_t paramOffset) {
auto query = batch._queries.get(batch._params[paramOffset]._uint); auto query = batch._queries.get(batch._params[paramOffset]._uint);
GLQuery* glquery = syncGPUObject(*query); GLQuery* glquery = syncGPUObject(*query);
if (glquery) { if (glquery) {
PROFILE_RANGE_BEGIN(glquery->_profileRangeId, query->getName().c_str(), 0xFFFF7F00); PROFILE_RANGE_BEGIN(render_gpu_gl, glquery->_profileRangeId, query->getName().c_str(), 0xFFFF7F00);
++_queryStage._rangeQueryDepth; ++_queryStage._rangeQueryDepth;
glGetInteger64v(GL_TIMESTAMP, (GLint64*)&glquery->_batchElapsedTime); glGetInteger64v(GL_TIMESTAMP, (GLint64*)&glquery->_batchElapsedTime);
@ -61,7 +62,7 @@ void GLBackend::do_endQuery(const Batch& batch, size_t paramOffset) {
glGetInteger64v(GL_TIMESTAMP, &now); glGetInteger64v(GL_TIMESTAMP, &now);
glquery->_batchElapsedTime = now - glquery->_batchElapsedTime; glquery->_batchElapsedTime = now - glquery->_batchElapsedTime;
PROFILE_RANGE_END(glquery->_profileRangeId); PROFILE_RANGE_END(render_gpu_gl, glquery->_profileRangeId);
(void)CHECK_GL_ERROR(); (void)CHECK_GL_ERROR();
} }

View file

@ -16,6 +16,7 @@
#include <fstream> #include <fstream>
Q_LOGGING_CATEGORY(gpugllogging, "hifi.gpu.gl") Q_LOGGING_CATEGORY(gpugllogging, "hifi.gpu.gl")
Q_LOGGING_CATEGORY(trace_render_gpu_gl, "trace.render.gpu.gl")
namespace gpu { namespace gl { namespace gpu { namespace gl {

View file

@ -15,6 +15,7 @@
#include <QLoggingCategory> #include <QLoggingCategory>
Q_DECLARE_LOGGING_CATEGORY(gpugllogging) Q_DECLARE_LOGGING_CATEGORY(gpugllogging)
Q_DECLARE_LOGGING_CATEGORY(trace_render_gpu_gl)
namespace gpu { namespace gl { namespace gpu { namespace gl {

View file

@ -12,7 +12,6 @@
#include <unordered_set> #include <unordered_set>
#include <unordered_map> #include <unordered_map>
#include <QtCore/QThread>
#include "../gl/GLTexelFormat.h" #include "../gl/GLTexelFormat.h"
@ -123,7 +122,7 @@ void GL41Texture::transferMip(uint16_t mipLevel, uint8_t face) const {
} }
void GL41Texture::startTransfer() { void GL41Texture::startTransfer() {
PROFILE_RANGE(__FUNCTION__); PROFILE_RANGE(render_gpu_gl, __FUNCTION__);
Parent::startTransfer(); Parent::startTransfer();
glBindTexture(_target, _id); glBindTexture(_target, _id);

View file

@ -17,6 +17,7 @@
#include <QtCore/QDebug> #include <QtCore/QDebug>
#include <QtCore/QThread> #include <QtCore/QThread>
#include <Trace.h>
#include <NumericalConstants.h> #include <NumericalConstants.h>
@ -757,6 +758,9 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<
if(width != cubeTexture.getHeight()) { if(width != cubeTexture.getHeight()) {
return false; return false;
} }
PROFILE_RANGE(render_gpu, "sphericalHarmonicsFromTexture");
const uint sqOrder = order*order; const uint sqOrder = order*order;
// allocate memory for calculations // allocate memory for calculations
@ -788,6 +792,7 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<
// for each face of cube texture // for each face of cube texture
for(int face=0; face < gpu::Texture::NUM_CUBE_FACES; face++) { for(int face=0; face < gpu::Texture::NUM_CUBE_FACES; face++) {
PROFILE_RANGE(render_gpu, "ProcessFace");
auto numComponents = cubeTexture.accessStoredMipFace(0,face)->getFormat().getScalarCount(); auto numComponents = cubeTexture.accessStoredMipFace(0,face)->getFormat().getScalarCount();
auto data = cubeTexture.accessStoredMipFace(0,face)->readData(); auto data = cubeTexture.accessStoredMipFace(0,face)->readData();
@ -995,4 +1000,4 @@ Texture::ExternalUpdates Texture::getUpdates() const {
_externalUpdates.swap(result); _externalUpdates.swap(result);
} }
return result; return result;
} }

View file

@ -21,6 +21,10 @@
#include <QThreadPool> #include <QThreadPool>
#include "ModelNetworkingLogging.h" #include "ModelNetworkingLogging.h"
#include <Trace.h>
#include <StatTracker.h>
Q_LOGGING_CATEGORY(trace_resource_parse_geometry, "trace.resource.parse.geometry")
class GeometryReader; class GeometryReader;
@ -39,6 +43,8 @@ class GeometryMappingResource : public GeometryResource {
public: public:
GeometryMappingResource(const QUrl& url) : GeometryResource(url) {}; GeometryMappingResource(const QUrl& url) : GeometryResource(url) {};
QString getType() const override { return "GeometryMapping"; }
virtual void downloadFinished(const QByteArray& data) override; virtual void downloadFinished(const QByteArray& data) override;
private slots: private slots:
@ -50,6 +56,9 @@ private:
}; };
void GeometryMappingResource::downloadFinished(const QByteArray& data) { void GeometryMappingResource::downloadFinished(const QByteArray& data) {
PROFILE_ASYNC_BEGIN(resource_parse_geometry, "GeometryMappingResource::downloadFinished", _url.toString(),
{ { "url", _url.toString() } });
auto mapping = FSTReader::readMapping(data); auto mapping = FSTReader::readMapping(data);
QString filename = mapping.value("filename").toString(); QString filename = mapping.value("filename").toString();
@ -113,6 +122,7 @@ void GeometryMappingResource::onGeometryMappingLoaded(bool success) {
disconnect(_connection); // FIXME Should not have to do this disconnect(_connection); // FIXME Should not have to do this
} }
PROFILE_ASYNC_END(resource_parse_geometry, "GeometryMappingResource::downloadFinished", _url.toString());
finishedLoading(success); finishedLoading(success);
} }
@ -120,8 +130,10 @@ class GeometryReader : public QRunnable {
public: public:
GeometryReader(QWeakPointer<Resource>& resource, const QUrl& url, const QVariantHash& mapping, GeometryReader(QWeakPointer<Resource>& resource, const QUrl& url, const QVariantHash& mapping,
const QByteArray& data) : const QByteArray& data) :
_resource(resource), _url(url), _mapping(mapping), _data(data) {} _resource(resource), _url(url), _mapping(mapping), _data(data) {
virtual ~GeometryReader() = default;
DependencyManager::get<StatTracker>()->incrementStat("PendingProcessing");
}
virtual void run() override; virtual void run() override;
@ -133,6 +145,9 @@ private:
}; };
void GeometryReader::run() { void GeometryReader::run() {
DependencyManager::get<StatTracker>()->decrementStat("PendingProcessing");
CounterStat counter("Processing");
PROFILE_RANGE_EX(resource_parse_geometry, "GeometryReader::run", 0xFF00FF00, 0, { { "url", _url.toString() } });
auto originalPriority = QThread::currentThread()->priority(); auto originalPriority = QThread::currentThread()->priority();
if (originalPriority == QThread::InheritPriority) { if (originalPriority == QThread::InheritPriority) {
originalPriority = QThread::NormalPriority; originalPriority = QThread::NormalPriority;
@ -197,6 +212,8 @@ public:
GeometryDefinitionResource(const QUrl& url, const QVariantHash& mapping, const QUrl& textureBaseUrl) : GeometryDefinitionResource(const QUrl& url, const QVariantHash& mapping, const QUrl& textureBaseUrl) :
GeometryResource(url, resolveTextureBaseUrl(url, textureBaseUrl)), _mapping(mapping) {} GeometryResource(url, resolveTextureBaseUrl(url, textureBaseUrl)), _mapping(mapping) {}
QString getType() const override { return "GeometryDefinition"; }
virtual void downloadFinished(const QByteArray& data) override; virtual void downloadFinished(const QByteArray& data) override;
protected: protected:

View file

@ -14,6 +14,9 @@
class NetworkShader : public Resource { class NetworkShader : public Resource {
public: public:
NetworkShader(const QUrl& url); NetworkShader(const QUrl& url);
QString getType() const override { return "NetworkShader"; }
virtual void downloadFinished(const QByteArray& data) override; virtual void downloadFinished(const QByteArray& data) override;
QString _source; QString _source;

View file

@ -33,6 +33,10 @@
#include <PathUtils.h> #include <PathUtils.h>
#include "ModelNetworkingLogging.h" #include "ModelNetworkingLogging.h"
#include <Trace.h>
#include <StatTracker.h>
Q_LOGGING_CATEGORY(trace_resource_parse_image, "trace.resource.parse.image")
TextureCache::TextureCache() { TextureCache::TextureCache() {
setUnusedResourceCacheSize(0); setUnusedResourceCacheSize(0);
@ -331,6 +335,7 @@ ImageReader::ImageReader(const QWeakPointer<Resource>& resource, const QByteArra
outFile.close(); outFile.close();
} }
#endif #endif
DependencyManager::get<StatTracker>()->incrementStat("PendingProcessing");
} }
void ImageReader::listSupportedImageFormats() { void ImageReader::listSupportedImageFormats() {
@ -342,7 +347,11 @@ void ImageReader::listSupportedImageFormats() {
} }
void ImageReader::run() { void ImageReader::run() {
PROFILE_RANGE_EX(__FUNCTION__, 0xffff0000, nullptr); DependencyManager::get<StatTracker>()->decrementStat("PendingProcessing");
CounterStat counter("Processing");
PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffff0000, 0, { { "url", _url.toString() } });
auto originalPriority = QThread::currentThread()->priority(); auto originalPriority = QThread::currentThread()->priority();
if (originalPriority == QThread::InheritPriority) { if (originalPriority == QThread::InheritPriority) {
originalPriority = QThread::NormalPriority; originalPriority = QThread::NormalPriority;
@ -356,7 +365,6 @@ void ImageReader::run() {
qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref"; qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref";
return; return;
} }
listSupportedImageFormats(); listSupportedImageFormats();
// Help the QImage loader by extracting the image file format from the url filename ext. // Help the QImage loader by extracting the image file format from the url filename ext.
@ -378,7 +386,6 @@ void ImageReader::run() {
} }
return; return;
} }
gpu::TexturePointer texture = nullptr; gpu::TexturePointer texture = nullptr;
{ {
// Double-check the resource still exists between long operations. // Double-check the resource still exists between long operations.
@ -390,7 +397,7 @@ void ImageReader::run() {
auto url = _url.toString().toStdString(); auto url = _url.toString().toStdString();
PROFILE_RANGE_EX(__FUNCTION__"::textureLoader", 0xffffff00, nullptr); PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffffff00, 0);
texture.reset(resource.dynamicCast<NetworkTexture>()->getTextureLoader()(image, url)); texture.reset(resource.dynamicCast<NetworkTexture>()->getTextureLoader()(image, url));
} }

View file

@ -63,10 +63,13 @@ public:
NetworkTexture(const QUrl& url, Type type, const QByteArray& content); NetworkTexture(const QUrl& url, Type type, const QByteArray& content);
NetworkTexture(const QUrl& url, const TextureLoaderFunc& textureLoader, const QByteArray& content); NetworkTexture(const QUrl& url, const TextureLoaderFunc& textureLoader, const QByteArray& content);
QString getType() const override { return "NetworkTexture"; }
int getOriginalWidth() const { return _originalWidth; } int getOriginalWidth() const { return _originalWidth; }
int getOriginalHeight() const { return _originalHeight; } int getOriginalHeight() const { return _originalHeight; }
int getWidth() const { return _width; } int getWidth() const { return _width; }
int getHeight() const { return _height; } int getHeight() const { return _height; }
Type getTextureType() const { return _type; }
TextureLoaderFunc getTextureLoader() const; TextureLoaderFunc getTextureLoader() const;

View file

@ -14,6 +14,8 @@
#include <QPainter> #include <QPainter>
#include <QDebug> #include <QDebug>
#include <Profile.h>
#include "ModelLogging.h" #include "ModelLogging.h"
using namespace model; using namespace model;
@ -744,6 +746,8 @@ const CubeLayout CubeLayout::CUBEMAP_LAYOUTS[] = {
const int CubeLayout::NUM_CUBEMAP_LAYOUTS = sizeof(CubeLayout::CUBEMAP_LAYOUTS) / sizeof(CubeLayout); const int CubeLayout::NUM_CUBEMAP_LAYOUTS = sizeof(CubeLayout::CUBEMAP_LAYOUTS) / sizeof(CubeLayout);
gpu::Texture* TextureUsage::processCubeTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips, bool generateIrradiance) { gpu::Texture* TextureUsage::processCubeTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips, bool generateIrradiance) {
PROFILE_RANGE(resource_parse, "processCubeTextureColorFromImage");
gpu::Texture* theTexture = nullptr; gpu::Texture* theTexture = nullptr;
if ((srcImage.width() > 0) && (srcImage.height() > 0)) { if ((srcImage.width() > 0) && (srcImage.height() > 0)) {
QImage image = processSourceImage(srcImage, true); QImage image = processSourceImage(srcImage, true);
@ -801,11 +805,13 @@ gpu::Texture* TextureUsage::processCubeTextureColorFromImage(const QImage& srcIm
} }
if (generateMips) { if (generateMips) {
PROFILE_RANGE(resource_parse, "generateMips");
theTexture->autoGenerateMips(-1); theTexture->autoGenerateMips(-1);
} }
// Generate irradiance while we are at it // Generate irradiance while we are at it
if (generateIrradiance) { if (generateIrradiance) {
PROFILE_RANGE(resource_parse, "generateIrradiance");
theTexture->generateIrradiance(); theTexture->generateIrradiance();
} }
} }

View file

@ -17,6 +17,7 @@ find_package(TBB REQUIRED)
if (APPLE) if (APPLE)
find_library(FRAMEWORK_IOKIT IOKit) find_library(FRAMEWORK_IOKIT IOKit)
find_library(CORE_FOUNDATION CoreFoundation)
endif () endif ()
if (APPLE AND ${OPENSSL_INCLUDE_DIR} STREQUAL "/usr/include") if (APPLE AND ${OPENSSL_INCLUDE_DIR} STREQUAL "/usr/include")
@ -32,7 +33,7 @@ target_link_libraries(${TARGET_NAME} ${OPENSSL_LIBRARIES} ${TBB_LIBRARIES})
# IOKit is needed for getting machine fingerprint # IOKit is needed for getting machine fingerprint
if (APPLE) if (APPLE)
target_link_libraries(${TARGET_NAME} ${FRAMEWORK_IOKIT}) target_link_libraries(${TARGET_NAME} ${FRAMEWORK_IOKIT} ${CORE_FOUNDATION})
endif (APPLE) endif (APPLE)
# libcrypto uses dlopen in libdl # libcrypto uses dlopen in libdl

View file

@ -604,7 +604,7 @@ bool AssetClient::cancelGetAssetRequest(MessageID id) {
for (auto& kv : _pendingRequests) { for (auto& kv : _pendingRequests) {
auto& messageCallbackMap = kv.second; auto& messageCallbackMap = kv.second;
auto requestIt = messageCallbackMap.find(id); auto requestIt = messageCallbackMap.find(id);
if (requestIt != kv.second.end()) { if (requestIt != messageCallbackMap.end()) {
auto& message = requestIt->second.message; auto& message = requestIt->second.message;
if (message) { if (message) {

View file

@ -19,8 +19,12 @@
#include "NetworkLogging.h" #include "NetworkLogging.h"
#include "NodeList.h" #include "NodeList.h"
#include "ResourceCache.h" #include "ResourceCache.h"
#include <Trace.h>
static int requestID = 0;
AssetRequest::AssetRequest(const QString& hash) : AssetRequest::AssetRequest(const QString& hash) :
_requestID(++requestID),
_hash(hash) _hash(hash)
{ {
} }

View file

@ -56,6 +56,7 @@ signals:
void progress(qint64 totalReceived, qint64 total); void progress(qint64 totalReceived, qint64 total);
private: private:
int _requestID;
State _state = NotStarted; State _state = NotStarted;
Error _error = NoError; Error _error = NoError;
AssetInfo _info; AssetInfo _info;

View file

@ -17,6 +17,8 @@
#include "AssetUtils.h" #include "AssetUtils.h"
#include "MappingRequest.h" #include "MappingRequest.h"
#include "NetworkLogging.h" #include "NetworkLogging.h"
#include <Trace.h>
#include <Profile.h>
static const int DOWNLOAD_PROGRESS_LOG_INTERVAL_SECONDS = 5; static const int DOWNLOAD_PROGRESS_LOG_INTERVAL_SECONDS = 5;
@ -27,12 +29,14 @@ AssetResourceRequest::AssetResourceRequest(const QUrl& url) :
} }
AssetResourceRequest::~AssetResourceRequest() { AssetResourceRequest::~AssetResourceRequest() {
if (_assetMappingRequest) { if (_assetRequest || _assetMappingRequest) {
_assetMappingRequest->deleteLater(); if (_assetMappingRequest) {
} _assetMappingRequest->deleteLater();
}
if (_assetRequest) {
_assetRequest->deleteLater(); if (_assetRequest) {
_assetRequest->deleteLater();
}
} }
} }
@ -108,7 +112,6 @@ void AssetResourceRequest::requestMappingForPath(const AssetPath& path) {
} }
void AssetResourceRequest::requestHash(const AssetHash& hash) { void AssetResourceRequest::requestHash(const AssetHash& hash) {
// Make request to atp // Make request to atp
auto assetClient = DependencyManager::get<AssetClient>(); auto assetClient = DependencyManager::get<AssetClient>();
_assetRequest = assetClient->createRequest(hash); _assetRequest = assetClient->createRequest(hash);
@ -118,7 +121,7 @@ void AssetResourceRequest::requestHash(const AssetHash& hash) {
Q_ASSERT(_state == InProgress); Q_ASSERT(_state == InProgress);
Q_ASSERT(req == _assetRequest); Q_ASSERT(req == _assetRequest);
Q_ASSERT(req->getState() == AssetRequest::Finished); Q_ASSERT(req->getState() == AssetRequest::Finished);
switch (req->getError()) { switch (req->getError()) {
case AssetRequest::Error::NoError: case AssetRequest::Error::NoError:
_data = req->getData(); _data = req->getData();

View file

@ -19,6 +19,7 @@
#include <BuildInfo.h> #include <BuildInfo.h>
#include "Assignment.h" #include "Assignment.h"
#include <QtCore/QStandardPaths> #include <QtCore/QStandardPaths>
#include <QtCore/QDir>
Assignment::Type Assignment::typeForNodeType(NodeType_t nodeType) { Assignment::Type Assignment::typeForNodeType(NodeType_t nodeType) {
switch (nodeType) { switch (nodeType) {
@ -51,7 +52,7 @@ Assignment::Assignment() :
} }
Assignment::Assignment(Assignment::Command command, Assignment::Type type, const QString& pool, Assignment::Location location) : Assignment::Assignment(Assignment::Command command, Assignment::Type type, const QString& pool, Assignment::Location location, QString dataDirectory) :
_uuid(), _uuid(),
_command(command), _command(command),
_type(type), _type(type),
@ -60,7 +61,8 @@ Assignment::Assignment(Assignment::Command command, Assignment::Type type, const
_payload(), _payload(),
_isStatic(false), _isStatic(false),
_walletUUID(), _walletUUID(),
_nodeVersion() _nodeVersion(),
_dataDirectory(dataDirectory)
{ {
if (_command == Assignment::CreateCommand) { if (_command == Assignment::CreateCommand) {
// this is a newly created assignment, generate a random UUID // this is a newly created assignment, generate a random UUID

View file

@ -55,7 +55,8 @@ public:
Assignment(Assignment::Command command, Assignment(Assignment::Command command,
Assignment::Type type, Assignment::Type type,
const QString& pool = emptyPool, const QString& pool = emptyPool,
Assignment::Location location = Assignment::LocalLocation); Assignment::Location location = Assignment::LocalLocation,
QString dataDirectory = QString());
Assignment(const Assignment& otherAssignment); Assignment(const Assignment& otherAssignment);
Assignment& operator=(const Assignment &rhsAssignment); Assignment& operator=(const Assignment &rhsAssignment);
@ -103,6 +104,7 @@ protected:
bool _isStatic; /// defines if this assignment needs to be re-queued in the domain-server if it stops being fulfilled bool _isStatic; /// defines if this assignment needs to be re-queued in the domain-server if it stops being fulfilled
QUuid _walletUUID; /// the UUID for the wallet that should be paid for this assignment QUuid _walletUUID; /// the UUID for the wallet that should be paid for this assignment
QString _nodeVersion; QString _nodeVersion;
QString _dataDirectory;
}; };
uint qHash(const Assignment::Type& key, uint seed); uint qHash(const Assignment::Type& key, uint seed);

View file

@ -10,8 +10,13 @@
// //
#include "FingerprintUtils.h" #include "FingerprintUtils.h"
#include <QDebug> #include <QDebug>
#include <SettingHandle.h> #include <SettingHandle.h>
#include <SettingManager.h>
#include <DependencyManager.h>
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#include <comdef.h> #include <comdef.h>
#include <Wbemidl.h> #include <Wbemidl.h>
@ -44,7 +49,15 @@ QString FingerprintUtils::getMachineFingerprintString() {
HRESULT hres; HRESULT hres;
IWbemLocator *pLoc = NULL; IWbemLocator *pLoc = NULL;
// initialize com // initialize com. Interface already does, but other
// users of this lib don't necessarily do so.
hres = CoInitializeEx(0, COINIT_MULTITHREADED);
if (FAILED(hres)) {
qDebug() << "Failed to initialize COM library!";
return uuidString;
}
// initialize WbemLocator
hres = CoCreateInstance( hres = CoCreateInstance(
CLSID_WbemLocator, CLSID_WbemLocator,
0, 0,
@ -164,6 +177,11 @@ QUuid FingerprintUtils::getMachineFingerprint() {
// any errors in getting the string // any errors in getting the string
QUuid uuid(uuidString); QUuid uuid(uuidString);
if (uuid == QUuid()) { if (uuid == QUuid()) {
// if you cannot read a fallback key cuz we aren't saving them, just generate one for
// this session and move on
if (DependencyManager::get<Setting::Manager>().isNull()) {
return QUuid::createUuid();
}
// read fallback key (if any) // read fallback key (if any)
Settings settings; Settings settings;
uuid = QUuid(settings.value(FALLBACK_FINGERPRINT_KEY).toString()); uuid = QUuid(settings.value(FALLBACK_FINGERPRINT_KEY).toString());

View file

@ -109,6 +109,10 @@ void HifiSockAddr::handleLookupResult(const QHostInfo& hostInfo) {
} }
} }
QString HifiSockAddr::toString() const {
return _address.toString() + ":" + QString::number(_port);
}
QDebug operator<<(QDebug debug, const HifiSockAddr& sockAddr) { QDebug operator<<(QDebug debug, const HifiSockAddr& sockAddr) {
debug.nospace() << sockAddr._address.toString().toLocal8Bit().constData() << ":" << sockAddr._port; debug.nospace() << sockAddr._address.toString().toLocal8Bit().constData() << ":" << sockAddr._port;
return debug.space(); return debug.space();

View file

@ -53,6 +53,8 @@ public:
static int packSockAddr(unsigned char* packetData, const HifiSockAddr& packSockAddr); static int packSockAddr(unsigned char* packetData, const HifiSockAddr& packSockAddr);
static int unpackSockAddr(const unsigned char* packetData, HifiSockAddr& unpackDestSockAddr); static int unpackSockAddr(const unsigned char* packetData, HifiSockAddr& unpackDestSockAddr);
QString toString() const;
friend QDebug operator<<(QDebug debug, const HifiSockAddr& sockAddr); friend QDebug operator<<(QDebug debug, const HifiSockAddr& sockAddr);
friend QDataStream& operator<<(QDataStream& dataStream, const HifiSockAddr& sockAddr); friend QDataStream& operator<<(QDataStream& dataStream, const HifiSockAddr& sockAddr);
friend QDataStream& operator>>(QDataStream& dataStream, HifiSockAddr& sockAddr); friend QDataStream& operator>>(QDataStream& dataStream, HifiSockAddr& sockAddr);

View file

@ -37,6 +37,7 @@
#include "HifiSockAddr.h" #include "HifiSockAddr.h"
#include "NetworkLogging.h" #include "NetworkLogging.h"
#include "udt/Packet.h" #include "udt/Packet.h"
#include <Trace.h>
static Setting::Handle<quint16> LIMITED_NODELIST_LOCAL_PORT("LimitedNodeList.LocalPort", 0); static Setting::Handle<quint16> LIMITED_NODELIST_LOCAL_PORT("LimitedNodeList.LocalPort", 0);
@ -1116,7 +1117,6 @@ void LimitedNodeList::flagTimeForConnectionStep(ConnectionStep connectionStep) {
} }
void LimitedNodeList::flagTimeForConnectionStep(ConnectionStep connectionStep, quint64 timestamp) { void LimitedNodeList::flagTimeForConnectionStep(ConnectionStep connectionStep, quint64 timestamp) {
if (connectionStep == ConnectionStep::LookupAddress) { if (connectionStep == ConnectionStep::LookupAddress) {
QWriteLocker writeLock(&_connectionTimeLock); QWriteLocker writeLock(&_connectionTimeLock);

View file

@ -103,8 +103,7 @@ public:
ReceiveFirstAudioPacket ReceiveFirstAudioPacket
}; };
Q_ENUMS(ConnectionStep); Q_ENUM(ConnectionStep);
const QUuid& getSessionUUID() const { return _sessionUUID; } const QUuid& getSessionUUID() const { return _sessionUUID; }
void setSessionUUID(const QUuid& sessionUUID); void setSessionUUID(const QUuid& sessionUUID);

Some files were not shown because too many files have changed in this diff Show more