Merge branch 'master' of github.com:highfidelity/hifi into fix-far-to-equip

This commit is contained in:
Dante Ruiz 2017-12-07 13:42:26 -08:00
commit 4110295677
39 changed files with 836 additions and 558 deletions

View file

@ -25,6 +25,23 @@ AvatarMixerClientData::AvatarMixerClientData(const QUuid& nodeID) :
_avatar->setID(nodeID); _avatar->setID(nodeID);
} }
uint64_t AvatarMixerClientData::getLastOtherAvatarEncodeTime(QUuid otherAvatar) const {
std::unordered_map<QUuid, uint64_t>::const_iterator itr = _lastOtherAvatarEncodeTime.find(otherAvatar);
if (itr != _lastOtherAvatarEncodeTime.end()) {
return itr->second;
}
return 0;
}
void AvatarMixerClientData::setLastOtherAvatarEncodeTime(const QUuid& otherAvatar, const uint64_t& time) {
std::unordered_map<QUuid, uint64_t>::iterator itr = _lastOtherAvatarEncodeTime.find(otherAvatar);
if (itr != _lastOtherAvatarEncodeTime.end()) {
itr->second = time;
} else {
_lastOtherAvatarEncodeTime.emplace(std::pair<QUuid, uint64_t>(otherAvatar, time));
}
}
void AvatarMixerClientData::queuePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer node) { void AvatarMixerClientData::queuePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer node) {
if (!_packetQueue.node) { if (!_packetQueue.node) {
_packetQueue.node = node; _packetQueue.node = node;

View file

@ -110,16 +110,10 @@ public:
bool getRequestsDomainListData() { return _requestsDomainListData; } bool getRequestsDomainListData() { return _requestsDomainListData; }
void setRequestsDomainListData(bool requesting) { _requestsDomainListData = requesting; } void setRequestsDomainListData(bool requesting) { _requestsDomainListData = requesting; }
ViewFrustum getViewFrustom() const { return _currentViewFrustum; } ViewFrustum getViewFrustum() const { return _currentViewFrustum; }
quint64 getLastOtherAvatarEncodeTime(QUuid otherAvatar) { uint64_t getLastOtherAvatarEncodeTime(QUuid otherAvatar) const;
quint64 result = 0; void setLastOtherAvatarEncodeTime(const QUuid& otherAvatar, const uint64_t& time);
if (_lastOtherAvatarEncodeTime.find(otherAvatar) != _lastOtherAvatarEncodeTime.end()) {
result = _lastOtherAvatarEncodeTime[otherAvatar];
}
_lastOtherAvatarEncodeTime[otherAvatar] = usecTimestampNow();
return result;
}
QVector<JointData>& getLastOtherAvatarSentJoints(QUuid otherAvatar) { QVector<JointData>& getLastOtherAvatarSentJoints(QUuid otherAvatar) {
_lastOtherAvatarSentJoints[otherAvatar].resize(_avatar->getJointCount()); _lastOtherAvatarSentJoints[otherAvatar].resize(_avatar->getJointCount());
@ -143,7 +137,7 @@ private:
// this is a map of the last time we encoded an "other" avatar for // this is a map of the last time we encoded an "other" avatar for
// sending to "this" node // sending to "this" node
std::unordered_map<QUuid, quint64> _lastOtherAvatarEncodeTime; std::unordered_map<QUuid, uint64_t> _lastOtherAvatarEncodeTime;
std::unordered_map<QUuid, QVector<JointData>> _lastOtherAvatarSentJoints; std::unordered_map<QUuid, QVector<JointData>> _lastOtherAvatarSentJoints;
uint64_t _identityChangeTimestamp; uint64_t _identityChangeTimestamp;

View file

@ -22,6 +22,7 @@
#include <NodeList.h> #include <NodeList.h>
#include <Node.h> #include <Node.h>
#include <OctreeConstants.h> #include <OctreeConstants.h>
#include <PrioritySortUtil.h>
#include <udt/PacketHeaders.h> #include <udt/PacketHeaders.h>
#include <SharedUtil.h> #include <SharedUtil.h>
#include <StDev.h> #include <StDev.h>
@ -32,6 +33,10 @@
#include "AvatarMixerClientData.h" #include "AvatarMixerClientData.h"
#include "AvatarMixerSlave.h" #include "AvatarMixerSlave.h"
namespace PrioritySortUtil {
// we declare this callback here but override it later
std::function<uint64_t(const AvatarSharedPointer&)> getAvatarAgeCallback = [] (const AvatarSharedPointer& avatar) { return 0; };
}
void AvatarMixerSlave::configure(ConstIter begin, ConstIter end) { void AvatarMixerSlave::configure(ConstIter begin, ConstIter end) {
_begin = begin; _begin = begin;
@ -184,10 +189,8 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
// setup list of AvatarData as well as maps to map betweeen the AvatarData and the original nodes // setup list of AvatarData as well as maps to map betweeen the AvatarData and the original nodes
// for calling the AvatarData::sortAvatars() function and getting our sorted list of client nodes std::vector<AvatarSharedPointer> avatarsToSort;
QList<AvatarSharedPointer> avatarList;
std::unordered_map<AvatarSharedPointer, SharedNodePointer> avatarDataToNodes; std::unordered_map<AvatarSharedPointer, SharedNodePointer> avatarDataToNodes;
std::for_each(_begin, _end, [&](const SharedNodePointer& otherNode) { std::for_each(_begin, _end, [&](const SharedNodePointer& otherNode) {
// make sure this is an agent that we have avatar data for before considering it for inclusion // make sure this is an agent that we have avatar data for before considering it for inclusion
if (otherNode->getType() == NodeType::Agent if (otherNode->getType() == NodeType::Agent
@ -195,36 +198,61 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
const AvatarMixerClientData* otherNodeData = reinterpret_cast<const AvatarMixerClientData*>(otherNode->getLinkedData()); const AvatarMixerClientData* otherNodeData = reinterpret_cast<const AvatarMixerClientData*>(otherNode->getLinkedData());
AvatarSharedPointer otherAvatar = otherNodeData->getAvatarSharedPointer(); AvatarSharedPointer otherAvatar = otherNodeData->getAvatarSharedPointer();
avatarList << otherAvatar; avatarsToSort.push_back(otherAvatar);
avatarDataToNodes[otherAvatar] = otherNode; avatarDataToNodes[otherAvatar] = otherNode;
} }
}); });
AvatarSharedPointer thisAvatar = nodeData->getAvatarSharedPointer(); // now that we've assembled the avatarDataToNodes map we can replace PrioritySortUtil::getAvatarAgeCallback
ViewFrustum cameraView = nodeData->getViewFrustom(); // with the true implementation
std::priority_queue<AvatarPriority> sortedAvatars; PrioritySortUtil::getAvatarAgeCallback = [&] (const AvatarSharedPointer& avatar) {
AvatarData::sortAvatars(avatarList, cameraView, sortedAvatars, auto avatarNode = avatarDataToNodes[avatar];
[&](AvatarSharedPointer avatar)->uint64_t { assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map
auto avatarNode = avatarDataToNodes[avatar]; return nodeData->getLastOtherAvatarEncodeTime(avatarNode->getUUID());
assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map };
return nodeData->getLastBroadcastTime(avatarNode->getUUID());
}, [&](AvatarSharedPointer avatar)->float{ class SortableAvatar: public PrioritySortUtil::Sortable {
glm::vec3 nodeBoxHalfScale = (avatar->getWorldPosition() - avatar->getGlobalBoundingBoxCorner() * avatar->getSensorToWorldScale()); public:
return glm::max(nodeBoxHalfScale.x, glm::max(nodeBoxHalfScale.y, nodeBoxHalfScale.z)); SortableAvatar() = delete;
}, [&](AvatarSharedPointer avatar)->bool { SortableAvatar(const AvatarSharedPointer& avatar) : _avatar(avatar) {}
glm::vec3 getPosition() const override { return _avatar->getWorldPosition(); }
float getRadius() const override {
glm::vec3 nodeBoxHalfScale = (_avatar->getWorldPosition() - _avatar->getGlobalBoundingBoxCorner() * _avatar->getSensorToWorldScale());
return glm::max(nodeBoxHalfScale.x, glm::max(nodeBoxHalfScale.y, nodeBoxHalfScale.z));
}
uint64_t getTimestamp() const override {
// use the callback implemented above
return PrioritySortUtil::getAvatarAgeCallback(_avatar);
}
const AvatarSharedPointer& getAvatar() const { return _avatar; }
private:
AvatarSharedPointer _avatar;
};
// prepare to sort
ViewFrustum cameraView = nodeData->getViewFrustum();
PrioritySortUtil::PriorityQueue<SortableAvatar> sortedAvatars(cameraView,
AvatarData::_avatarSortCoefficientSize,
AvatarData::_avatarSortCoefficientCenter,
AvatarData::_avatarSortCoefficientAge);
// ignore or sort
const AvatarSharedPointer& thisAvatar = nodeData->getAvatarSharedPointer();
for (const auto& avatar : avatarsToSort) {
if (avatar == thisAvatar) { if (avatar == thisAvatar) {
return true; // ignore ourselves... // don't echo updates to self
continue;
} }
bool shouldIgnore = false; bool shouldIgnore = false;
// We ignore other nodes for a couple of reasons:
// We will also ignore other nodes for a couple of different reasons:
// 1) ignore bubbles and ignore specific node // 1) ignore bubbles and ignore specific node
// 2) the node hasn't really updated it's frame data recently, this can // 2) the node hasn't really updated it's frame data recently, this can
// happen if for example the avatar is connected on a desktop and sending // happen if for example the avatar is connected on a desktop and sending
// updates at ~30hz. So every 3 frames we skip a frame. // updates at ~30hz. So every 3 frames we skip a frame.
auto avatarNode = avatarDataToNodes[avatar];
auto avatarNode = avatarDataToNodes[avatar];
assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map
const AvatarMixerClientData* avatarNodeData = reinterpret_cast<const AvatarMixerClientData*>(avatarNode->getLinkedData()); const AvatarMixerClientData* avatarNodeData = reinterpret_cast<const AvatarMixerClientData*>(avatarNode->getLinkedData());
@ -240,7 +268,6 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|| (avatarNode->isIgnoringNodeWithID(node->getUUID()) && !getsAnyIgnored)) { || (avatarNode->isIgnoringNodeWithID(node->getUUID()) && !getsAnyIgnored)) {
shouldIgnore = true; shouldIgnore = true;
} else { } else {
// Check to see if the space bubble is enabled // Check to see if the space bubble is enabled
// Don't bother with these checks if the other avatar has their bubble enabled and we're gettingAnyIgnored // Don't bother with these checks if the other avatar has their bubble enabled and we're gettingAnyIgnored
if (node->isIgnoreRadiusEnabled() || (avatarNode->isIgnoreRadiusEnabled() && !getsAnyIgnored)) { if (node->isIgnoreRadiusEnabled() || (avatarNode->isIgnoreRadiusEnabled() && !getsAnyIgnored)) {
@ -267,8 +294,6 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
nodeData->removeFromRadiusIgnoringSet(node, avatarNode->getUUID()); nodeData->removeFromRadiusIgnoringSet(node, avatarNode->getUUID());
} }
} }
quint64 endIgnoreCalculation = usecTimestampNow();
_stats.ignoreCalculationElapsedTime += (endIgnoreCalculation - startIgnoreCalculation);
if (!shouldIgnore) { if (!shouldIgnore) {
AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(avatarNode->getUUID()); AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(avatarNode->getUUID());
@ -292,20 +317,21 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
++numAvatarsWithSkippedFrames; ++numAvatarsWithSkippedFrames;
} }
} }
return shouldIgnore; quint64 endIgnoreCalculation = usecTimestampNow();
}); _stats.ignoreCalculationElapsedTime += (endIgnoreCalculation - startIgnoreCalculation);
if (!shouldIgnore) {
// sort this one for later
sortedAvatars.push(SortableAvatar(avatar));
}
}
// loop through our sorted avatars and allocate our bandwidth to them accordingly // loop through our sorted avatars and allocate our bandwidth to them accordingly
int avatarRank = 0;
// this is overly conservative, because it includes some avatars we might not consider
int remainingAvatars = (int)sortedAvatars.size(); int remainingAvatars = (int)sortedAvatars.size();
while (!sortedAvatars.empty()) { while (!sortedAvatars.empty()) {
AvatarPriority sortData = sortedAvatars.top(); const auto& avatarData = sortedAvatars.top().getAvatar();
sortedAvatars.pop(); sortedAvatars.pop();
const auto& avatarData = sortData.avatar;
avatarRank++;
remainingAvatars--; remainingAvatars--;
auto otherNode = avatarDataToNodes[avatarData]; auto otherNode = avatarDataToNodes[avatarData];
@ -332,10 +358,8 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
nodeData->setLastBroadcastTime(otherNode->getUUID(), usecTimestampNow()); nodeData->setLastBroadcastTime(otherNode->getUUID(), usecTimestampNow());
} }
// determine if avatar is in view which determines how much data to send
glm::vec3 otherPosition = otherAvatar->getClientGlobalPosition(); glm::vec3 otherPosition = otherAvatar->getClientGlobalPosition();
// determine if avatar is in view, to determine how much data to include...
glm::vec3 otherNodeBoxScale = (otherPosition - otherNodeData->getGlobalBoundingBoxCorner()) * 2.0f * otherAvatar->getSensorToWorldScale(); glm::vec3 otherNodeBoxScale = (otherPosition - otherNodeData->getGlobalBoundingBoxCorner()) * 2.0f * otherAvatar->getSensorToWorldScale();
AABox otherNodeBox(otherNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale); AABox otherNodeBox(otherNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale);
bool isInView = nodeData->otherAvatarInView(otherNodeBox); bool isInView = nodeData->otherAvatarInView(otherNodeBox);
@ -405,14 +429,18 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
// set the last sent sequence number for this sender on the receiver // set the last sent sequence number for this sender on the receiver
nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(), nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(),
otherNodeData->getLastReceivedSequenceNumber()); otherNodeData->getLastReceivedSequenceNumber());
nodeData->setLastOtherAvatarEncodeTime(otherNode->getUUID(), usecTimestampNow());
} }
} else {
// TODO? this avatar is not included now, and will probably not be included next frame.
// It would be nice if we could tweak its future sort priority to put it at the back of the list.
} }
avatarPacketList->endSegment(); avatarPacketList->endSegment();
quint64 endAvatarDataPacking = usecTimestampNow(); quint64 endAvatarDataPacking = usecTimestampNow();
_stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking);
}; }
quint64 startPacketSending = usecTimestampNow(); quint64 startPacketSending = usecTimestampNow();

View file

@ -29,10 +29,6 @@ macro(GENERATE_INSTALLERS)
if (WIN32) if (WIN32)
# Do not install the Visual Studio C runtime libraries. The installer will do this automatically
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE)
include(InstallRequiredSystemLibraries)
set(CPACK_NSIS_MUI_ICON "${HF_CMAKE_DIR}/installer/installer.ico") set(CPACK_NSIS_MUI_ICON "${HF_CMAKE_DIR}/installer/installer.ico")
# install and reference the Add/Remove icon # install and reference the Add/Remove icon
@ -49,6 +45,10 @@ macro(GENERATE_INSTALLERS)
set(_UNINSTALLER_HEADER_BAD_PATH "${HF_CMAKE_DIR}/installer/uninstaller-header.bmp") set(_UNINSTALLER_HEADER_BAD_PATH "${HF_CMAKE_DIR}/installer/uninstaller-header.bmp")
set(UNINSTALLER_HEADER_IMAGE "") set(UNINSTALLER_HEADER_IMAGE "")
fix_path_for_nsis(${_UNINSTALLER_HEADER_BAD_PATH} UNINSTALLER_HEADER_IMAGE) fix_path_for_nsis(${_UNINSTALLER_HEADER_BAD_PATH} UNINSTALLER_HEADER_IMAGE)
# grab the latest VC redist (2017) and add it to the installer, our NSIS template
# will call it during the install
install(CODE "file(DOWNLOAD https://go.microsoft.com/fwlink/?LinkId=746572 \"\${CMAKE_INSTALL_PREFIX}/vcredist_x64.exe\")")
elseif (APPLE) elseif (APPLE)
# produce a drag and drop DMG on OS X # produce a drag and drop DMG on OS X
set(CPACK_GENERATOR "DragNDrop") set(CPACK_GENERATOR "DragNDrop")
@ -84,4 +84,3 @@ macro(GENERATE_INSTALLERS)
include(CPack) include(CPack)
endmacro() endmacro()

View file

@ -45,5 +45,4 @@ else()
endif() endif()
file(GLOB EXTRA_PLUGINS "${BUNDLE_PLUGIN_DIR}/*.${PLUGIN_EXTENSION}") file(GLOB EXTRA_PLUGINS "${BUNDLE_PLUGIN_DIR}/*.${PLUGIN_EXTENSION}")
fixup_bundle("${BUNDLE_EXECUTABLE}" "${EXTRA_PLUGINS}" "@FIXUP_LIBS@") fixup_bundle("${BUNDLE_EXECUTABLE}" "${EXTRA_PLUGINS}" "@FIXUP_LIBS@" IGNORE_ITEM "vcredist_x86.exe;vcredist_x64.exe")

View file

@ -79,10 +79,12 @@ Rectangle {
if (result.status !== 'success') { if (result.status !== 'success') {
failureErrorText.text = result.message; failureErrorText.text = result.message;
root.activeView = "checkoutFailure"; root.activeView = "checkoutFailure";
UserActivityLogger.commercePurchaseFailure(root.itemId, root.itemPrice, !root.alreadyOwned, result.message);
} else { } else {
root.itemHref = result.data.download_url; root.itemHref = result.data.download_url;
root.isWearable = result.data.categories.indexOf("Wearables") > -1; root.isWearable = result.data.categories.indexOf("Wearables") > -1;
root.activeView = "checkoutSuccess"; root.activeView = "checkoutSuccess";
UserActivityLogger.commercePurchaseSuccess(root.itemId, root.itemPrice, !root.alreadyOwned);
} }
} }
@ -599,6 +601,7 @@ Rectangle {
sendToScript({method: 'checkout_rezClicked', itemHref: root.itemHref, isWearable: root.isWearable}); sendToScript({method: 'checkout_rezClicked', itemHref: root.itemHref, isWearable: root.isWearable});
rezzedNotifContainer.visible = true; rezzedNotifContainer.visible = true;
rezzedNotifContainerTimer.start(); rezzedNotifContainerTimer.start();
UserActivityLogger.commerceEntityRezzed(root.itemId, "checkout", root.isWearable ? "rez" : "wear");
} }
} }
RalewaySemiBold { RalewaySemiBold {

View file

@ -349,6 +349,7 @@ Item {
sendToPurchases({method: 'purchases_rezClicked', itemHref: root.itemHref, isWearable: root.isWearable}); sendToPurchases({method: 'purchases_rezClicked', itemHref: root.itemHref, isWearable: root.isWearable});
rezzedNotifContainer.visible = true; rezzedNotifContainer.visible = true;
rezzedNotifContainerTimer.start(); rezzedNotifContainerTimer.start();
UserActivityLogger.commerceEntityRezzed(root.itemId, "purchases", root.isWearable ? "rez" : "wear");
} }
style: ButtonStyle { style: ButtonStyle {

View file

@ -48,6 +48,11 @@ Rectangle {
if (root.activeView !== "walletSetup") { if (root.activeView !== "walletSetup") {
root.activeView = "walletSetup"; root.activeView = "walletSetup";
commerce.resetLocalWalletOnly(); commerce.resetLocalWalletOnly();
var timestamp = new Date();
walletSetup.startingTimestamp = timestamp;
walletSetup.setupAttemptID = generateUUID();
UserActivityLogger.commerceWalletSetupStarted(timestamp, setupAttemptID, walletSetup.setupFlowVersion, walletSetup.referrer ? walletSetup.referrer : "wallet app",
(AddressManager.placename || AddressManager.hostname || '') + (AddressManager.pathname ? AddressManager.pathname.match(/\/[^\/]+/)[0] : ''));
} }
} else if (walletStatus === 2) { } else if (walletStatus === 2) {
if (root.activeView !== "passphraseModal") { if (root.activeView !== "passphraseModal") {
@ -173,7 +178,7 @@ Rectangle {
Connections { Connections {
onSendSignalToWallet: { onSendSignalToWallet: {
if (msg.method === 'walletSetup_finished') { if (msg.method === 'walletSetup_finished') {
if (msg.referrer === '') { if (msg.referrer === '' || msg.referrer === 'marketplace cta') {
root.activeView = "initialize"; root.activeView = "initialize";
commerce.getWalletStatus(); commerce.getWalletStatus();
} else if (msg.referrer === 'purchases') { } else if (msg.referrer === 'purchases') {
@ -701,12 +706,28 @@ Rectangle {
case 'updateWalletReferrer': case 'updateWalletReferrer':
walletSetup.referrer = message.referrer; walletSetup.referrer = message.referrer;
break; break;
case 'inspectionCertificate_resetCert':
// NOP
break;
default: default:
console.log('Unrecognized message from wallet.js:', JSON.stringify(message)); console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
} }
} }
signal sendToScript(var message); signal sendToScript(var message);
// generateUUID() taken from:
// https://stackoverflow.com/a/8809472
function generateUUID() { // Public Domain/MIT
var d = new Date().getTime();
if (typeof performance !== 'undefined' && typeof performance.now === 'function'){
d += performance.now(); //use high-precision timer if available
}
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = (d + Math.random() * 16) % 16 | 0;
d = Math.floor(d / 16);
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
});
}
// //
// FUNCTION DEFINITIONS END // FUNCTION DEFINITIONS END
// //

View file

@ -31,6 +31,10 @@ Item {
property bool hasShownSecurityImageTip: false; property bool hasShownSecurityImageTip: false;
property string referrer; property string referrer;
property string keyFilePath; property string keyFilePath;
property date startingTimestamp;
property string setupAttemptID;
readonly property int setupFlowVersion: 1;
readonly property var setupStepNames: [ "Setup Prompt", "Security Image Selection", "Passphrase Selection", "Private Keys Ready" ];
Image { Image {
anchors.fill: parent; anchors.fill: parent;
@ -67,6 +71,13 @@ Item {
anchors.fill: parent; anchors.fill: parent;
} }
onActiveViewChanged: {
var timestamp = new Date();
var currentStepNumber = root.activeView.substring(5);
UserActivityLogger.commerceWalletSetupProgress(timestamp, root.setupAttemptID,
Math.round((timestamp - root.startingTimestamp)/1000), currentStepNumber, root.setupStepNames[currentStepNumber - 1]);
}
// //
// TITLE BAR START // TITLE BAR START
// //
@ -730,6 +741,9 @@ Item {
root.visible = false; root.visible = false;
root.hasShownSecurityImageTip = false; root.hasShownSecurityImageTip = false;
sendSignalToWallet({method: 'walletSetup_finished', referrer: root.referrer ? root.referrer : ""}); sendSignalToWallet({method: 'walletSetup_finished', referrer: root.referrer ? root.referrer : ""});
var timestamp = new Date();
UserActivityLogger.commerceWalletSetupFinished(timestamp, setupAttemptID, Math.round((timestamp - root.startingTimestamp)/1000));
} }
} }
} }

View file

@ -9,33 +9,30 @@ Overlay {
Image { Image {
id: image id: image
property bool scaleFix: true; property bool scaleFix: true
property real xOffset: 0 property real xStart: 0
property real yOffset: 0 property real yStart: 0
property real xSize: 0
property real ySize: 0
property real imageScale: 1.0 property real imageScale: 1.0
property var resizer: Timer { property var resizer: Timer {
interval: 50 interval: 50
repeat: false repeat: false
running: false running: false
onTriggered: { onTriggered: {
var targetAspect = root.width / root.height; if (image.xSize === 0) {
var sourceAspect = image.sourceSize.width / image.sourceSize.height; image.xSize = image.sourceSize.width - image.xStart;
if (sourceAspect <= targetAspect) {
if (root.width === image.sourceSize.width) {
return;
}
image.imageScale = root.width / image.sourceSize.width;
} else if (sourceAspect > targetAspect){
if (root.height === image.sourceSize.height) {
return;
}
image.imageScale = root.height / image.sourceSize.height;
} }
image.sourceSize = Qt.size(image.sourceSize.width * image.imageScale, image.sourceSize.height * image.imageScale); if (image.ySize === 0) {
image.ySize = image.sourceSize.height - image.yStart;
}
image.anchors.leftMargin = -image.xStart * root.width / image.xSize;
image.anchors.topMargin = -image.yStart * root.height / image.ySize;
image.anchors.rightMargin = (image.xStart + image.xSize - image.sourceSize.width) * root.width / image.xSize;
image.anchors.bottomMargin = (image.yStart + image.ySize - image.sourceSize.height) * root.height / image.ySize;
} }
} }
x: -1 * xOffset * imageScale
y: -1 * yOffset * imageScale
onSourceSizeChanged: { onSourceSizeChanged: {
if (sourceSize.width !== 0 && sourceSize.height !== 0 && progress === 1.0 && scaleFix) { if (sourceSize.width !== 0 && sourceSize.height !== 0 && progress === 1.0 && scaleFix) {
@ -43,6 +40,8 @@ Overlay {
resizer.start(); resizer.start();
} }
} }
anchors.fill: parent
} }
ColorOverlay { ColorOverlay {
@ -57,8 +56,10 @@ Overlay {
var key = keys[i]; var key = keys[i];
var value = subImage[key]; var value = subImage[key];
switch (key) { switch (key) {
case "x": image.xOffset = value; break; case "x": image.xStart = value; break;
case "y": image.yOffset = value; break; case "y": image.yStart = value; break;
case "width": image.xSize = value; break;
case "height": image.ySize = value; break;
} }
} }
} }

View file

@ -7075,11 +7075,11 @@ QRect Application::getRecommendedHUDRect() const {
return result; return result;
} }
QSize Application::getDeviceSize() const { glm::vec2 Application::getDeviceSize() const {
static const int MIN_SIZE = 1; static const int MIN_SIZE = 1;
QSize result(MIN_SIZE, MIN_SIZE); glm::vec2 result(MIN_SIZE);
if (_displayPlugin) { if (_displayPlugin) {
result = fromGlm(getActiveDisplayPlugin()->getRecommendedRenderSize()); result = getActiveDisplayPlugin()->getRecommendedRenderSize();
} }
return result; return result;
} }
@ -7098,10 +7098,6 @@ bool Application::hasFocus() const {
return (QApplication::activeWindow() != nullptr); return (QApplication::activeWindow() != nullptr);
} }
glm::vec2 Application::getViewportDimensions() const {
return toGlm(getDeviceSize());
}
void Application::setMaxOctreePacketsPerSecond(int maxOctreePPS) { void Application::setMaxOctreePacketsPerSecond(int maxOctreePPS) {
if (maxOctreePPS != _maxOctreePPS) { if (maxOctreePPS != _maxOctreePPS) {
_maxOctreePPS = maxOctreePPS; _maxOctreePPS = maxOctreePPS;

View file

@ -158,7 +158,7 @@ public:
glm::uvec2 getUiSize() const; glm::uvec2 getUiSize() const;
QRect getRecommendedHUDRect() const; QRect getRecommendedHUDRect() const;
QSize getDeviceSize() const; glm::vec2 getDeviceSize() const;
bool hasFocus() const; bool hasFocus() const;
void showCursor(const Cursor::Icon& cursor); void showCursor(const Cursor::Icon& cursor);
@ -228,8 +228,6 @@ public:
FileLogger* getLogger() const { return _logger; } FileLogger* getLogger() const { return _logger; }
glm::vec2 getViewportDimensions() const;
NodeToJurisdictionMap& getEntityServerJurisdictions() { return _entityServerJurisdictions; } NodeToJurisdictionMap& getEntityServerJurisdictions() { return _entityServerJurisdictions; }
float getRenderResolutionScale() const; float getRenderResolutionScale() const;

View file

@ -104,8 +104,7 @@ void Application::paintGL() {
PerformanceTimer perfTimer("renderOverlay"); PerformanceTimer perfTimer("renderOverlay");
// NOTE: There is no batch associated with this renderArgs // NOTE: There is no batch associated with this renderArgs
// the ApplicationOverlay class assumes it's viewport is setup to be the device size // the ApplicationOverlay class assumes it's viewport is setup to be the device size
QSize size = getDeviceSize(); renderArgs._viewport = glm::ivec4(0, 0, getDeviceSize());
renderArgs._viewport = glm::ivec4(0, 0, size.width(), size.height());
_applicationOverlay.renderOverlay(&renderArgs); _applicationOverlay.renderOverlay(&renderArgs);
} }

View file

@ -28,6 +28,7 @@
#include <shared/QtHelpers.h> #include <shared/QtHelpers.h>
#include <AvatarData.h> #include <AvatarData.h>
#include <PerfStat.h> #include <PerfStat.h>
#include <PrioritySortUtil.h>
#include <RegisteredMetaTypes.h> #include <RegisteredMetaTypes.h>
#include <Rig.h> #include <Rig.h>
#include <SettingHandle.h> #include <SettingHandle.h>
@ -142,32 +143,39 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
PerformanceTimer perfTimer("otherAvatars"); PerformanceTimer perfTimer("otherAvatars");
auto avatarMap = getHashCopy(); class SortableAvatar: public PrioritySortUtil::Sortable {
QList<AvatarSharedPointer> avatarList = avatarMap.values(); public:
SortableAvatar() = delete;
SortableAvatar(const AvatarSharedPointer& avatar) : _avatar(avatar) {}
glm::vec3 getPosition() const override { return _avatar->getWorldPosition(); }
float getRadius() const override { return std::static_pointer_cast<Avatar>(_avatar)->getBoundingRadius(); }
uint64_t getTimestamp() const override { return std::static_pointer_cast<Avatar>(_avatar)->getLastRenderUpdateTime(); }
const AvatarSharedPointer& getAvatar() const { return _avatar; }
private:
AvatarSharedPointer _avatar;
};
ViewFrustum cameraView; ViewFrustum cameraView;
qApp->copyDisplayViewFrustum(cameraView); qApp->copyDisplayViewFrustum(cameraView);
PrioritySortUtil::PriorityQueue<SortableAvatar> sortedAvatars(cameraView,
AvatarData::_avatarSortCoefficientSize,
AvatarData::_avatarSortCoefficientCenter,
AvatarData::_avatarSortCoefficientAge);
std::priority_queue<AvatarPriority> sortedAvatars; // sort
AvatarData::sortAvatars(avatarList, cameraView, sortedAvatars, auto avatarMap = getHashCopy();
AvatarHash::iterator itr = avatarMap.begin();
[](AvatarSharedPointer avatar)->uint64_t{ while (itr != avatarMap.end()) {
return std::static_pointer_cast<Avatar>(avatar)->getLastRenderUpdateTime(); const auto& avatar = std::static_pointer_cast<Avatar>(*itr);
}, // DO NOT update _myAvatar! Its update has already been done earlier in the main loop.
// DO NOT update or fade out uninitialized Avatars
[](AvatarSharedPointer avatar)->float{ if (avatar != _myAvatar && avatar->isInitialized()) {
return std::static_pointer_cast<Avatar>(avatar)->getBoundingRadius(); sortedAvatars.push(SortableAvatar(avatar));
}, }
++itr;
[this](AvatarSharedPointer avatar)->bool{ }
const auto& castedAvatar = std::static_pointer_cast<Avatar>(avatar);
if (castedAvatar == _myAvatar || !castedAvatar->isInitialized()) {
// DO NOT update _myAvatar! Its update has already been done earlier in the main loop.
// DO NOT update or fade out uninitialized Avatars
return true; // ignore it
}
return false;
});
// process in sorted order
uint64_t startTime = usecTimestampNow(); uint64_t startTime = usecTimestampNow();
const uint64_t UPDATE_BUDGET = 2000; // usec const uint64_t UPDATE_BUDGET = 2000; // usec
uint64_t updateExpiry = startTime + UPDATE_BUDGET; uint64_t updateExpiry = startTime + UPDATE_BUDGET;
@ -176,8 +184,8 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
render::Transaction transaction; render::Transaction transaction;
while (!sortedAvatars.empty()) { while (!sortedAvatars.empty()) {
const AvatarPriority& sortData = sortedAvatars.top(); const SortableAvatar& sortData = sortedAvatars.top();
const auto& avatar = std::static_pointer_cast<Avatar>(sortData.avatar); const auto& avatar = std::static_pointer_cast<Avatar>(sortData.getAvatar());
bool ignoring = DependencyManager::get<NodeList>()->isPersonalMutingNode(avatar->getID()); bool ignoring = DependencyManager::get<NodeList>()->isPersonalMutingNode(avatar->getID());
if (ignoring) { if (ignoring) {
@ -207,7 +215,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
uint64_t now = usecTimestampNow(); uint64_t now = usecTimestampNow();
if (now < updateExpiry) { if (now < updateExpiry) {
// we're within budget // we're within budget
bool inView = sortData.priority > OUT_OF_VIEW_THRESHOLD; bool inView = sortData.getPriority() > OUT_OF_VIEW_THRESHOLD;
if (inView && avatar->hasNewJointData()) { if (inView && avatar->hasNewJointData()) {
numAvatarsUpdated++; numAvatarsUpdated++;
} }
@ -221,7 +229,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
// --> some avatar velocity measurements may be a little off // --> some avatar velocity measurements may be a little off
// no time simulate, but we take the time to count how many were tragically missed // no time simulate, but we take the time to count how many were tragically missed
bool inView = sortData.priority > OUT_OF_VIEW_THRESHOLD; bool inView = sortData.getPriority() > OUT_OF_VIEW_THRESHOLD;
if (!inView) { if (!inView) {
break; break;
} }
@ -230,9 +238,9 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
} }
sortedAvatars.pop(); sortedAvatars.pop();
while (inView && !sortedAvatars.empty()) { while (inView && !sortedAvatars.empty()) {
const AvatarPriority& newSortData = sortedAvatars.top(); const SortableAvatar& newSortData = sortedAvatars.top();
const auto& newAvatar = std::static_pointer_cast<Avatar>(newSortData.avatar); const auto& newAvatar = std::static_pointer_cast<Avatar>(newSortData.getAvatar());
inView = newSortData.priority > OUT_OF_VIEW_THRESHOLD; inView = newSortData.getPriority() > OUT_OF_VIEW_THRESHOLD;
if (inView && newAvatar->hasNewJointData()) { if (inView && newAvatar->hasNewJointData()) {
numAVatarsNotUpdated++; numAVatarsNotUpdated++;
} }

View file

@ -176,6 +176,10 @@ bool WindowScriptingInterface::isPointOnDesktopWindow(QVariant point) {
return offscreenUi->isPointOnDesktopWindow(point); return offscreenUi->isPointOnDesktopWindow(point);
} }
glm::vec2 WindowScriptingInterface::getDeviceSize() const {
return qApp->getDeviceSize();
}
/// Makes sure that the reticle is visible, use this in blocking forms that require a reticle and /// Makes sure that the reticle is visible, use this in blocking forms that require a reticle and
/// might be in same thread as a script that sets the reticle to invisible /// might be in same thread as a script that sets the reticle to invisible
void WindowScriptingInterface::ensureReticleVisible() const { void WindowScriptingInterface::ensureReticleVisible() const {

View file

@ -12,6 +12,8 @@
#ifndef hifi_WindowScriptingInterface_h #ifndef hifi_WindowScriptingInterface_h
#define hifi_WindowScriptingInterface_h #define hifi_WindowScriptingInterface_h
#include <glm/glm.hpp>
#include <QtCore/QObject> #include <QtCore/QObject>
#include <QtCore/QString> #include <QtCore/QString>
#include <QtQuick/QQuickItem> #include <QtQuick/QQuickItem>
@ -73,6 +75,7 @@ public slots:
bool isPhysicsEnabled(); bool isPhysicsEnabled();
bool setDisplayTexture(const QString& name); bool setDisplayTexture(const QString& name);
bool isPointOnDesktopWindow(QVariant point); bool isPointOnDesktopWindow(QVariant point);
glm::vec2 getDeviceSize() const;
int openMessageBox(QString title, QString text, int buttons, int defaultButton); int openMessageBox(QString title, QString text, int buttons, int defaultButton);
void updateMessageBox(int id, QString title, QString text, int buttons, int defaultButton); void updateMessageBox(int id, QString title, QString text, int buttons, int defaultButton);

View file

@ -35,7 +35,8 @@ ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) :
_modelTextures(QVariantMap()), _modelTextures(QVariantMap()),
_url(modelOverlay->_url), _url(modelOverlay->_url),
_updateModel(false), _updateModel(false),
_loadPriority(modelOverlay->getLoadPriority()) _scaleToFit(modelOverlay->_scaleToFit),
_loadPriority(modelOverlay->_loadPriority)
{ {
_model->init(); _model->init();
_model->setLoadingPriority(_loadPriority); _model->setLoadingPriority(_loadPriority);

View file

@ -75,8 +75,8 @@ private:
QVariantMap _modelTextures; QVariantMap _modelTextures;
QUrl _url; QUrl _url;
bool _updateModel = { false }; bool _updateModel { false };
bool _scaleToFit = { false }; bool _scaleToFit { false };
float _loadPriority { 0.0f }; float _loadPriority { 0.0f };
AnimationPointer _animation; AnimationPointer _animation;
@ -87,7 +87,7 @@ private:
bool _animationRunning { false }; bool _animationRunning { false };
bool _animationLoop { false }; bool _animationLoop { false };
float _animationFirstFrame { 0.0f }; float _animationFirstFrame { 0.0f };
float _animationLastFrame = { 0.0f }; float _animationLastFrame { 0.0f };
bool _animationHold { false }; bool _animationHold { false };
bool _animationAllowTranslation { false }; bool _animationAllowTranslation { false };
uint64_t _lastAnimated { 0 }; uint64_t _lastAnimated { 0 };

View file

@ -18,8 +18,9 @@
QString const Shape3DOverlay::TYPE = "shape"; QString const Shape3DOverlay::TYPE = "shape";
Shape3DOverlay::Shape3DOverlay(const Shape3DOverlay* Shape3DOverlay) : Shape3DOverlay::Shape3DOverlay(const Shape3DOverlay* shape3DOverlay) :
Volume3DOverlay(Shape3DOverlay) Volume3DOverlay(shape3DOverlay),
_shape(shape3DOverlay->_shape)
{ {
} }

View file

@ -23,7 +23,7 @@ public:
virtual QString getType() const override { return TYPE; } virtual QString getType() const override { return TYPE; }
Shape3DOverlay() {} Shape3DOverlay() {}
Shape3DOverlay(const Shape3DOverlay* Shape3DOverlay); Shape3DOverlay(const Shape3DOverlay* shape3DOverlay);
virtual void render(RenderArgs* args) override; virtual void render(RenderArgs* args) override;
virtual const render::ShapeKey getShapeKey() override; virtual const render::ShapeKey getShapeKey() override;

View file

@ -14,7 +14,8 @@
#include <RegisteredMetaTypes.h> #include <RegisteredMetaTypes.h>
Volume3DOverlay::Volume3DOverlay(const Volume3DOverlay* volume3DOverlay) : Volume3DOverlay::Volume3DOverlay(const Volume3DOverlay* volume3DOverlay) :
Base3DOverlay(volume3DOverlay) Base3DOverlay(volume3DOverlay),
_localBoundingBox(volume3DOverlay->_localBoundingBox)
{ {
} }

View file

@ -2387,63 +2387,10 @@ void RayToAvatarIntersectionResultFromScriptValue(const QScriptValue& object, Ra
const float AvatarData::OUT_OF_VIEW_PENALTY = -10.0f; const float AvatarData::OUT_OF_VIEW_PENALTY = -10.0f;
float AvatarData::_avatarSortCoefficientSize { 0.5f }; float AvatarData::_avatarSortCoefficientSize { 1.0f };
float AvatarData::_avatarSortCoefficientCenter { 0.25 }; float AvatarData::_avatarSortCoefficientCenter { 0.25 };
float AvatarData::_avatarSortCoefficientAge { 1.0f }; float AvatarData::_avatarSortCoefficientAge { 1.0f };
void AvatarData::sortAvatars(
QList<AvatarSharedPointer> avatarList,
const ViewFrustum& cameraView,
std::priority_queue<AvatarPriority>& sortedAvatarsOut,
std::function<uint64_t(AvatarSharedPointer)> getLastUpdated,
std::function<float(AvatarSharedPointer)> getBoundingRadius,
std::function<bool(AvatarSharedPointer)> shouldIgnore) {
PROFILE_RANGE(simulation, "sort");
uint64_t now = usecTimestampNow();
glm::vec3 frustumCenter = cameraView.getPosition();
const glm::vec3& forward = cameraView.getDirection();
for (int32_t i = 0; i < avatarList.size(); ++i) {
const auto& avatar = avatarList.at(i);
if (shouldIgnore(avatar)) {
continue;
}
// priority = weighted linear combination of:
// (a) apparentSize
// (b) proximity to center of view
// (c) time since last update
glm::vec3 avatarPosition = avatar->getWorldPosition();
glm::vec3 offset = avatarPosition - frustumCenter;
float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero
// FIXME - AvatarData has something equivolent to this
float radius = getBoundingRadius(avatar);
float apparentSize = 2.0f * radius / distance;
float cosineAngle = glm::dot(offset, forward) / distance;
float age = (float)(now - getLastUpdated(avatar)) / (float)(USECS_PER_SECOND);
// NOTE: we are adding values of different units to get a single measure of "priority".
// Thus we multiply each component by a conversion "weight" that scales its units relative to the others.
// These weights are pure magic tuning and should be hard coded in the relation below,
// but are currently exposed for anyone who would like to explore fine tuning:
float priority = _avatarSortCoefficientSize * apparentSize
+ _avatarSortCoefficientCenter * cosineAngle
+ _avatarSortCoefficientAge * age;
// decrement priority of avatars outside keyhole
if (distance > cameraView.getCenterRadius()) {
if (!cameraView.sphereIntersectsFrustum(avatarPosition, radius)) {
priority += OUT_OF_VIEW_PENALTY;
}
}
sortedAvatarsOut.push(AvatarPriority(avatar, priority));
}
}
QScriptValue AvatarEntityMapToScriptValue(QScriptEngine* engine, const AvatarEntityMap& value) { QScriptValue AvatarEntityMapToScriptValue(QScriptEngine* engine, const AvatarEntityMap& value) {
QScriptValue obj = engine->newObject(); QScriptValue obj = engine->newObject();
for (auto entityID : value.keys()) { for (auto entityID : value.keys()) {

View file

@ -629,14 +629,6 @@ public:
static const float OUT_OF_VIEW_PENALTY; static const float OUT_OF_VIEW_PENALTY;
static void sortAvatars(
QList<AvatarSharedPointer> avatarList,
const ViewFrustum& cameraView,
std::priority_queue<AvatarPriority>& sortedAvatarsOut,
std::function<uint64_t(AvatarSharedPointer)> getLastUpdated,
std::function<float(AvatarSharedPointer)> getBoundingRadius,
std::function<bool(AvatarSharedPointer)> shouldIgnore);
// TODO: remove this HACK once we settle on optimal sort coefficients // TODO: remove this HACK once we settle on optimal sort coefficients
// These coefficients exposed for fine tuning the sort priority for transfering new _jointData to the render pipeline. // These coefficients exposed for fine tuning the sort priority for transfering new _jointData to the render pipeline.
static float _avatarSortCoefficientSize; static float _avatarSortCoefficientSize;

View file

@ -1605,6 +1605,48 @@ void EntityItem::setParentID(const QUuid& value) {
if (tree && !oldParentID.isNull()) { if (tree && !oldParentID.isNull()) {
tree->removeFromChildrenOfAvatars(getThisPointer()); tree->removeFromChildrenOfAvatars(getThisPointer());
} }
uint32_t oldParentNoBootstrapping = 0;
uint32_t newParentNoBootstrapping = 0;
if (!value.isNull() && tree) {
EntityItemPointer entity = tree->findEntityByEntityItemID(value);
if (entity) {
newParentNoBootstrapping = entity->getDirtyFlags() & Simulation::NO_BOOTSTRAPPING;
}
}
if (!oldParentID.isNull() && tree) {
EntityItemPointer entity = tree->findEntityByEntityItemID(oldParentID);
if (entity) {
oldParentNoBootstrapping = entity->getDirtyFlags() & Simulation::NO_BOOTSTRAPPING;
}
}
if (!value.isNull() && (value == Physics::getSessionUUID() || value == AVATAR_SELF_ID)) {
newParentNoBootstrapping |= Simulation::NO_BOOTSTRAPPING;
}
if ((bool)(oldParentNoBootstrapping ^ newParentNoBootstrapping)) {
if ((bool)(newParentNoBootstrapping & Simulation::NO_BOOTSTRAPPING)) {
markDirtyFlags(Simulation::NO_BOOTSTRAPPING);
forEachDescendant([&](SpatiallyNestablePointer object) {
if (object->getNestableType() == NestableType::Entity) {
EntityItemPointer entity = std::static_pointer_cast<EntityItem>(object);
entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP | Simulation::NO_BOOTSTRAPPING);
}
});
} else {
clearDirtyFlags(Simulation::NO_BOOTSTRAPPING);
forEachDescendant([&](SpatiallyNestablePointer object) {
if (object->getNestableType() == NestableType::Entity) {
EntityItemPointer entity = std::static_pointer_cast<EntityItem>(object);
entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP);
entity->clearDirtyFlags(Simulation::NO_BOOTSTRAPPING);
}
});
}
}
SpatiallyNestable::setParentID(value); SpatiallyNestable::setParentID(value);
// children are forced to be kinematic // children are forced to be kinematic
// may need to not collide with own avatar // may need to not collide with own avatar
@ -1834,39 +1876,8 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask
} }
} }
if (userMask & USER_COLLISION_GROUP_MY_AVATAR) { if ((bool)(_dirtyFlags & Simulation::NO_BOOTSTRAPPING)) {
bool iAmHoldingThis = false; userMask &= ~USER_COLLISION_GROUP_MY_AVATAR;
// if this entity is a descendant of MyAvatar, don't collide with MyAvatar. This avoids the
// "bootstrapping" problem where you can shoot yourself across the room by grabbing something
// and holding it against your own avatar.
if (isChildOfMyAvatar()) {
iAmHoldingThis = true;
}
// also, don't bootstrap our own avatar with a hold action
QList<EntityDynamicPointer> holdActions = getActionsOfType(DYNAMIC_TYPE_HOLD);
QList<EntityDynamicPointer>::const_iterator i = holdActions.begin();
while (i != holdActions.end()) {
EntityDynamicPointer action = *i;
if (action->isMine()) {
iAmHoldingThis = true;
break;
}
i++;
}
QList<EntityDynamicPointer> farGrabActions = getActionsOfType(DYNAMIC_TYPE_FAR_GRAB);
i = farGrabActions.begin();
while (i != farGrabActions.end()) {
EntityDynamicPointer action = *i;
if (action->isMine()) {
iAmHoldingThis = true;
break;
}
i++;
}
if (iAmHoldingThis) {
userMask &= ~USER_COLLISION_GROUP_MY_AVATAR;
}
} }
mask = Physics::getDefaultCollisionMask(group) & (int16_t)(userMask); mask = Physics::getDefaultCollisionMask(group) & (int16_t)(userMask);
} }
@ -1961,7 +1972,20 @@ bool EntityItem::addActionInternal(EntitySimulationPointer simulation, EntityDyn
if (success) { if (success) {
_allActionsDataCache = newDataCache; _allActionsDataCache = newDataCache;
_dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION; _dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
_dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar
auto actionType = action->getType();
if (actionType == DYNAMIC_TYPE_HOLD || actionType == DYNAMIC_TYPE_FAR_GRAB) {
if (!(bool)(_dirtyFlags & Simulation::NO_BOOTSTRAPPING)) {
_dirtyFlags |= Simulation::NO_BOOTSTRAPPING;
_dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar
forEachDescendant([&](SpatiallyNestablePointer child) {
if (child->getNestableType() == NestableType::Entity) {
EntityItemPointer entity = std::static_pointer_cast<EntityItem>(child);
entity->markDirtyFlags(Simulation::NO_BOOTSTRAPPING | Simulation::DIRTY_COLLISION_GROUP);
}
});
}
}
} else { } else {
qCDebug(entities) << "EntityItem::addActionInternal -- serializeActions failed"; qCDebug(entities) << "EntityItem::addActionInternal -- serializeActions failed";
} }
@ -2002,6 +2026,29 @@ bool EntityItem::removeAction(EntitySimulationPointer simulation, const QUuid& a
return success; return success;
} }
bool EntityItem::stillHasGrabActions() const {
QList<EntityDynamicPointer> holdActions = getActionsOfType(DYNAMIC_TYPE_HOLD);
QList<EntityDynamicPointer>::const_iterator i = holdActions.begin();
while (i != holdActions.end()) {
EntityDynamicPointer action = *i;
if (action->isMine()) {
return true;
}
i++;
}
QList<EntityDynamicPointer> farGrabActions = getActionsOfType(DYNAMIC_TYPE_FAR_GRAB);
i = farGrabActions.begin();
while (i != farGrabActions.end()) {
EntityDynamicPointer action = *i;
if (action->isMine()) {
return true;
}
i++;
}
return false;
}
bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPointer simulation) { bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPointer simulation) {
_previouslyDeletedActions.insert(actionID, usecTimestampNow()); _previouslyDeletedActions.insert(actionID, usecTimestampNow());
if (_objectActions.contains(actionID)) { if (_objectActions.contains(actionID)) {
@ -2015,7 +2062,6 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi
action->setOwnerEntity(nullptr); action->setOwnerEntity(nullptr);
action->setIsMine(false); action->setIsMine(false);
_objectActions.remove(actionID);
if (simulation) { if (simulation) {
action->removeFromSimulation(simulation); action->removeFromSimulation(simulation);
@ -2024,7 +2070,23 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi
bool success = true; bool success = true;
serializeActions(success, _allActionsDataCache); serializeActions(success, _allActionsDataCache);
_dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION; _dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
_dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar auto removedActionType = action->getType();
if ((removedActionType == DYNAMIC_TYPE_HOLD || removedActionType == DYNAMIC_TYPE_FAR_GRAB) && !stillHasGrabActions()) {
_dirtyFlags &= ~Simulation::NO_BOOTSTRAPPING;
_dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar
forEachDescendant([&](SpatiallyNestablePointer child) {
if (child->getNestableType() == NestableType::Entity) {
EntityItemPointer entity = std::static_pointer_cast<EntityItem>(child);
entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP);
entity->clearDirtyFlags(Simulation::NO_BOOTSTRAPPING);
}
});
} else {
// NO-OP: we assume NO_BOOTSTRAPPING bits and collision group are correct
// because they should have been set correctly when the action was added
// and/or when children were linked
}
_objectActions.remove(actionID);
setDynamicDataNeedsTransmit(true); setDynamicDataNeedsTransmit(true);
return success; return success;
} }

View file

@ -470,6 +470,7 @@ protected:
void setSimulated(bool simulated) { _simulated = simulated; } void setSimulated(bool simulated) { _simulated = simulated; }
const QByteArray getDynamicDataInternal() const; const QByteArray getDynamicDataInternal() const;
bool stillHasGrabActions() const;
void setDynamicDataInternal(QByteArray dynamicData); void setDynamicDataInternal(QByteArray dynamicData);
virtual void dimensionsChanged() override; virtual void dimensionsChanged() override;

View file

@ -27,6 +27,7 @@ namespace Simulation {
const uint32_t DIRTY_PHYSICS_ACTIVATION = 0x0800; // should activate object in physics engine const uint32_t DIRTY_PHYSICS_ACTIVATION = 0x0800; // should activate object in physics engine
const uint32_t DIRTY_SIMULATOR_ID = 0x1000; // the simulatorID has changed const uint32_t DIRTY_SIMULATOR_ID = 0x1000; // the simulatorID has changed
const uint32_t DIRTY_SIMULATION_OWNERSHIP_PRIORITY = 0x2000; // our own bid priority has changed const uint32_t DIRTY_SIMULATION_OWNERSHIP_PRIORITY = 0x2000; // our own bid priority has changed
const uint32_t NO_BOOTSTRAPPING = 0x4000;
const uint32_t DIRTY_TRANSFORM = DIRTY_POSITION | DIRTY_ROTATION; const uint32_t DIRTY_TRANSFORM = DIRTY_POSITION | DIRTY_ROTATION;
const uint32_t DIRTY_VELOCITIES = DIRTY_LINEAR_VELOCITY | DIRTY_ANGULAR_VELOCITY; const uint32_t DIRTY_VELOCITIES = DIRTY_LINEAR_VELOCITY | DIRTY_ANGULAR_VELOCITY;

View file

@ -1006,14 +1006,11 @@ NetworkTexturePointer TextureCache::getResourceTexture(QUrl resourceTextureUrl)
if (!_spectatorCameraNetworkTexture) { if (!_spectatorCameraNetworkTexture) {
_spectatorCameraNetworkTexture.reset(new NetworkTexture(resourceTextureUrl)); _spectatorCameraNetworkTexture.reset(new NetworkTexture(resourceTextureUrl));
} }
if (_spectatorCameraFramebuffer) { if (!_spectatorCameraFramebuffer) {
texture = _spectatorCameraFramebuffer->getRenderBuffer(0); getSpectatorCameraFramebuffer(); // initialize frame buffer
if (texture) {
texture->setSource(SPECTATOR_CAMERA_FRAME_URL.toString().toStdString());
_spectatorCameraNetworkTexture->setImage(texture, texture->getWidth(), texture->getHeight());
return _spectatorCameraNetworkTexture;
}
} }
updateSpectatorCameraNetworkTexture();
return _spectatorCameraNetworkTexture;
} }
// FIXME: Generalize this, DRY up this code // FIXME: Generalize this, DRY up this code
if (resourceTextureUrl == HMD_PREVIEW_FRAME_URL) { if (resourceTextureUrl == HMD_PREVIEW_FRAME_URL) {
@ -1052,7 +1049,18 @@ const gpu::FramebufferPointer& TextureCache::getSpectatorCameraFramebuffer(int w
// If we aren't taking a screenshot, we might need to resize or create the camera buffer // If we aren't taking a screenshot, we might need to resize or create the camera buffer
if (!_spectatorCameraFramebuffer || _spectatorCameraFramebuffer->getWidth() != width || _spectatorCameraFramebuffer->getHeight() != height) { if (!_spectatorCameraFramebuffer || _spectatorCameraFramebuffer->getWidth() != width || _spectatorCameraFramebuffer->getHeight() != height) {
_spectatorCameraFramebuffer.reset(gpu::Framebuffer::create("spectatorCamera", gpu::Element::COLOR_SRGBA_32, width, height)); _spectatorCameraFramebuffer.reset(gpu::Framebuffer::create("spectatorCamera", gpu::Element::COLOR_SRGBA_32, width, height));
updateSpectatorCameraNetworkTexture();
emit spectatorCameraFramebufferReset(); emit spectatorCameraFramebufferReset();
} }
return _spectatorCameraFramebuffer; return _spectatorCameraFramebuffer;
} }
void TextureCache::updateSpectatorCameraNetworkTexture() {
if (_spectatorCameraFramebuffer && _spectatorCameraNetworkTexture) {
gpu::TexturePointer texture = _spectatorCameraFramebuffer->getRenderBuffer(0);
if (texture) {
texture->setSource(SPECTATOR_CAMERA_FRAME_URL.toString().toStdString());
_spectatorCameraNetworkTexture->setImage(texture, texture->getWidth(), texture->getHeight());
}
}
}

View file

@ -171,6 +171,7 @@ public:
const gpu::FramebufferPointer& getHmdPreviewFramebuffer(int width, int height); const gpu::FramebufferPointer& getHmdPreviewFramebuffer(int width, int height);
const gpu::FramebufferPointer& getSpectatorCameraFramebuffer(); const gpu::FramebufferPointer& getSpectatorCameraFramebuffer();
const gpu::FramebufferPointer& getSpectatorCameraFramebuffer(int width, int height); const gpu::FramebufferPointer& getSpectatorCameraFramebuffer(int width, int height);
void updateSpectatorCameraNetworkTexture();
static const int DEFAULT_SPECTATOR_CAM_WIDTH { 2048 }; static const int DEFAULT_SPECTATOR_CAM_WIDTH { 2048 };
static const int DEFAULT_SPECTATOR_CAM_HEIGHT { 1024 }; static const int DEFAULT_SPECTATOR_CAM_HEIGHT { 1024 };

View file

@ -88,3 +88,56 @@ void UserActivityLoggerScriptingInterface::doLogAction(QString action, QJsonObje
Q_ARG(QString, action), Q_ARG(QString, action),
Q_ARG(QJsonObject, details)); Q_ARG(QJsonObject, details));
} }
void UserActivityLoggerScriptingInterface::commercePurchaseSuccess(QString marketplaceID, int cost, bool firstPurchaseOfThisItem) {
QJsonObject payload;
payload["marketplaceID"] = marketplaceID;
payload["cost"] = cost;
payload["firstPurchaseOfThisItem"] = firstPurchaseOfThisItem;
doLogAction("commercePurchaseSuccess", payload);
}
void UserActivityLoggerScriptingInterface::commercePurchaseFailure(QString marketplaceID, int cost, bool firstPurchaseOfThisItem, QString errorDetails) {
QJsonObject payload;
payload["marketplaceID"] = marketplaceID;
payload["cost"] = cost;
payload["firstPurchaseOfThisItem"] = firstPurchaseOfThisItem;
payload["errorDetails"] = errorDetails;
doLogAction("commercePurchaseFailure", payload);
}
void UserActivityLoggerScriptingInterface::commerceEntityRezzed(QString marketplaceID, QString source, QString type) {
QJsonObject payload;
payload["marketplaceID"] = marketplaceID;
payload["source"] = source;
payload["type"] = type;
doLogAction("commerceEntityRezzed", payload);
}
void UserActivityLoggerScriptingInterface::commerceWalletSetupStarted(int timestamp, QString setupAttemptID, int setupFlowVersion, QString referrer, QString currentDomain) {
QJsonObject payload;
payload["timestamp"] = timestamp;
payload["setupAttemptID"] = setupAttemptID;
payload["setupFlowVersion"] = setupFlowVersion;
payload["referrer"] = referrer;
payload["currentDomain"] = currentDomain;
doLogAction("commerceWalletSetupStarted", payload);
}
void UserActivityLoggerScriptingInterface::commerceWalletSetupProgress(int timestamp, QString setupAttemptID, int secondsElapsed, int currentStepNumber, QString currentStepName) {
QJsonObject payload;
payload["timestamp"] = timestamp;
payload["setupAttemptID"] = setupAttemptID;
payload["secondsElapsed"] = secondsElapsed;
payload["currentStepNumber"] = currentStepNumber;
payload["currentStepName"] = currentStepName;
doLogAction("commerceWalletSetupProgress", payload);
}
void UserActivityLoggerScriptingInterface::commerceWalletSetupFinished(int timestamp, QString setupAttemptID, int secondsToComplete) {
QJsonObject payload;
payload["timestamp"] = timestamp;
payload["setupAttemptID"] = setupAttemptID;
payload["secondsToComplete"] = secondsToComplete;
doLogAction("commerceWalletSetupFinished", payload);
}

View file

@ -33,6 +33,12 @@ public:
Q_INVOKABLE void bubbleToggled(bool newValue); Q_INVOKABLE void bubbleToggled(bool newValue);
Q_INVOKABLE void bubbleActivated(); Q_INVOKABLE void bubbleActivated();
Q_INVOKABLE void logAction(QString action, QVariantMap details = QVariantMap{}); Q_INVOKABLE void logAction(QString action, QVariantMap details = QVariantMap{});
Q_INVOKABLE void commercePurchaseSuccess(QString marketplaceID, int cost, bool firstPurchaseOfThisItem);
Q_INVOKABLE void commercePurchaseFailure(QString marketplaceID, int cost, bool firstPurchaseOfThisItem, QString errorDetails);
Q_INVOKABLE void commerceEntityRezzed(QString marketplaceID, QString source, QString type);
Q_INVOKABLE void commerceWalletSetupStarted(int timestamp, QString setupAttemptID, int setupFlowVersion, QString referrer, QString currentDomain);
Q_INVOKABLE void commerceWalletSetupProgress(int timestamp, QString setupAttemptID, int secondsElapsed, int currentStepNumber, QString currentStepName);
Q_INVOKABLE void commerceWalletSetupFinished(int timestamp, QString setupAttemptID, int secondsToComplete);
private: private:
void doLogAction(QString action, QJsonObject details = {}); void doLogAction(QString action, QJsonObject details = {});
}; };

View file

@ -700,7 +700,7 @@ uint32_t EntityMotionState::getIncomingDirtyFlags() {
void EntityMotionState::clearIncomingDirtyFlags() { void EntityMotionState::clearIncomingDirtyFlags() {
assert(entityTreeIsLocked()); assert(entityTreeIsLocked());
if (_body && _entity) { if (_body && _entity) {
_entity->clearDirtyFlags(); _entity->clearDirtyFlags(DIRTY_PHYSICS_FLAGS);
} }
} }

View file

@ -32,23 +32,23 @@ void Transaction::removeItem(ItemID id) {
} }
void Transaction::addTransitionToItem(ItemID id, Transition::Type transition, ItemID boundId) { void Transaction::addTransitionToItem(ItemID id, Transition::Type transition, ItemID boundId) {
_addedTransitions.emplace_back(TransitionAdd{ id, transition, boundId }); _addedTransitions.emplace_back(id, transition, boundId);
} }
void Transaction::removeTransitionFromItem(ItemID id) { void Transaction::removeTransitionFromItem(ItemID id) {
_addedTransitions.emplace_back(TransitionAdd{ id, Transition::NONE, render::Item::INVALID_ITEM_ID }); _addedTransitions.emplace_back(id, Transition::NONE, render::Item::INVALID_ITEM_ID);
} }
void Transaction::reApplyTransitionToItem(ItemID id) { void Transaction::reApplyTransitionToItem(ItemID id) {
_reAppliedTransitions.emplace_back(TransitionReApply{ id }); _reAppliedTransitions.emplace_back(id);
} }
void Transaction::queryTransitionOnItem(ItemID id, TransitionQueryFunc func) { void Transaction::queryTransitionOnItem(ItemID id, TransitionQueryFunc func) {
_queriedTransitions.emplace_back(TransitionQuery{ id, func }); _queriedTransitions.emplace_back(id, func);
} }
void Transaction::updateItem(ItemID id, const UpdateFunctorPointer& functor) { void Transaction::updateItem(ItemID id, const UpdateFunctorPointer& functor) {
_updatedItems.emplace_back(Update{ id, functor }); _updatedItems.emplace_back(id, functor);
} }
void Transaction::resetSelection(const Selection& selection) { void Transaction::resetSelection(const Selection& selection) {
@ -56,28 +56,122 @@ void Transaction::resetSelection(const Selection& selection) {
} }
void Transaction::resetSelectionHighlight(const std::string& selectionName, const HighlightStyle& style) { void Transaction::resetSelectionHighlight(const std::string& selectionName, const HighlightStyle& style) {
_highlightResets.emplace_back(HighlightReset{ selectionName, style }); _highlightResets.emplace_back(selectionName, style );
} }
void Transaction::removeHighlightFromSelection(const std::string& selectionName) { void Transaction::removeHighlightFromSelection(const std::string& selectionName) {
_highlightRemoves.emplace_back(selectionName); _highlightRemoves.emplace_back(selectionName);
} }
void Transaction::querySelectionHighlight(const std::string& selectionName, SelectionHighlightQueryFunc func) { void Transaction::querySelectionHighlight(const std::string& selectionName, const SelectionHighlightQueryFunc& func) {
_highlightQueries.emplace_back(HighlightQuery{ selectionName, func }); _highlightQueries.emplace_back(selectionName, func);
}
void Transaction::reserve(const std::vector<Transaction>& transactionContainer) {
size_t resetItemsCount = 0;
size_t removedItemsCount = 0;
size_t updatedItemsCount = 0;
size_t resetSelectionsCount = 0;
size_t addedTransitionsCount = 0;
size_t queriedTransitionsCount = 0;
size_t reAppliedTransitionsCount = 0;
size_t highlightResetsCount = 0;
size_t highlightRemovesCount = 0;
size_t highlightQueriesCount = 0;
for (const auto& transaction : transactionContainer) {
resetItemsCount += transaction._resetItems.size();
removedItemsCount += transaction._removedItems.size();
updatedItemsCount += transaction._updatedItems.size();
resetSelectionsCount += transaction._resetSelections.size();
addedTransitionsCount += transaction._addedTransitions.size();
queriedTransitionsCount += transaction._queriedTransitions.size();
reAppliedTransitionsCount += transaction._reAppliedTransitions.size();
highlightResetsCount += transaction._highlightResets.size();
highlightRemovesCount += transaction._highlightRemoves.size();
highlightQueriesCount += transaction._highlightQueries.size();
}
_resetItems.reserve(resetItemsCount);
_removedItems.reserve(removedItemsCount);
_updatedItems.reserve(updatedItemsCount);
_resetSelections.reserve(resetSelectionsCount);
_addedTransitions.reserve(addedTransitionsCount);
_queriedTransitions.reserve(queriedTransitionsCount);
_reAppliedTransitions.reserve(reAppliedTransitionsCount);
_highlightResets.reserve(highlightResetsCount);
_highlightRemoves.reserve(highlightRemovesCount);
_highlightQueries.reserve(highlightQueriesCount);
}
void Transaction::merge(const std::vector<Transaction>& transactionContainer) {
reserve(transactionContainer);
for (const auto& transaction : transactionContainer) {
merge(transaction);
}
}
void Transaction::merge(std::vector<Transaction>&& transactionContainer) {
reserve(transactionContainer);
auto begin = std::make_move_iterator(transactionContainer.begin());
auto end = std::make_move_iterator(transactionContainer.end());
for (auto itr = begin; itr != end; ++itr) {
merge(*itr);
}
transactionContainer.clear();
}
template <typename T>
void moveElements(T& target, T& source) {
target.insert(target.end(), std::make_move_iterator(source.begin()), std::make_move_iterator(source.end()));
source.clear();
}
template <typename T>
void copyElements(T& target, const T& source) {
target.insert(target.end(), source.begin(), source.end());
}
void Transaction::merge(Transaction&& transaction) {
moveElements(_resetItems, transaction._resetItems);
moveElements(_removedItems, transaction._removedItems);
moveElements(_updatedItems, transaction._updatedItems);
moveElements(_resetSelections, transaction._resetSelections);
moveElements(_addedTransitions, transaction._addedTransitions);
moveElements(_queriedTransitions, transaction._queriedTransitions);
moveElements(_reAppliedTransitions, transaction._reAppliedTransitions);
moveElements(_highlightResets, transaction._highlightResets);
moveElements(_highlightRemoves, transaction._highlightRemoves);
moveElements(_highlightQueries, transaction._highlightQueries);
} }
void Transaction::merge(const Transaction& transaction) { void Transaction::merge(const Transaction& transaction) {
_resetItems.insert(_resetItems.end(), transaction._resetItems.begin(), transaction._resetItems.end()); copyElements(_resetItems, transaction._resetItems);
_removedItems.insert(_removedItems.end(), transaction._removedItems.begin(), transaction._removedItems.end()); copyElements(_removedItems, transaction._removedItems);
_updatedItems.insert(_updatedItems.end(), transaction._updatedItems.begin(), transaction._updatedItems.end()); copyElements(_updatedItems, transaction._updatedItems);
_resetSelections.insert(_resetSelections.end(), transaction._resetSelections.begin(), transaction._resetSelections.end()); copyElements(_resetSelections, transaction._resetSelections);
_addedTransitions.insert(_addedTransitions.end(), transaction._addedTransitions.begin(), transaction._addedTransitions.end()); copyElements(_addedTransitions, transaction._addedTransitions);
_queriedTransitions.insert(_queriedTransitions.end(), transaction._queriedTransitions.begin(), transaction._queriedTransitions.end()); copyElements(_queriedTransitions, transaction._queriedTransitions);
_reAppliedTransitions.insert(_reAppliedTransitions.end(), transaction._reAppliedTransitions.begin(), transaction._reAppliedTransitions.end()); copyElements(_reAppliedTransitions, transaction._reAppliedTransitions);
_highlightResets.insert(_highlightResets.end(), transaction._highlightResets.begin(), transaction._highlightResets.end()); copyElements(_highlightResets, transaction._highlightResets);
_highlightRemoves.insert(_highlightRemoves.end(), transaction._highlightRemoves.begin(), transaction._highlightRemoves.end()); copyElements(_highlightRemoves, transaction._highlightRemoves);
_highlightQueries.insert(_highlightQueries.end(), transaction._highlightQueries.begin(), transaction._highlightQueries.end()); copyElements(_highlightQueries, transaction._highlightQueries);
}
void Transaction::clear() {
_resetItems.clear();
_removedItems.clear();
_updatedItems.clear();
_resetSelections.clear();
_addedTransitions.clear();
_queriedTransitions.clear();
_reAppliedTransitions.clear();
_highlightResets.clear();
_highlightRemoves.clear();
_highlightQueries.clear();
} }
@ -102,54 +196,50 @@ bool Scene::isAllocatedID(const ItemID& id) const {
/// Enqueue change batch to the scene /// Enqueue change batch to the scene
void Scene::enqueueTransaction(const Transaction& transaction) { void Scene::enqueueTransaction(const Transaction& transaction) {
_transactionQueueMutex.lock(); std::unique_lock<std::mutex> lock(_transactionQueueMutex);
_transactionQueue.push(transaction); _transactionQueue.emplace_back(transaction);
_transactionQueueMutex.unlock();
} }
void consolidateTransaction(TransactionQueue& queue, Transaction& singleBatch) { void Scene::enqueueTransaction(Transaction&& transaction) {
while (!queue.empty()) { std::unique_lock<std::mutex> lock(_transactionQueueMutex);
const auto& transaction = queue.front(); _transactionQueue.emplace_back(std::move(transaction));
singleBatch.merge(transaction);
queue.pop();
};
} }
uint32_t Scene::enqueueFrame() { uint32_t Scene::enqueueFrame() {
PROFILE_RANGE(render, __FUNCTION__); PROFILE_RANGE(render, __FUNCTION__);
Transaction consolidatedTransaction; TransactionQueue localTransactionQueue;
{ {
std::unique_lock<std::mutex> lock(_transactionQueueMutex); std::unique_lock<std::mutex> lock(_transactionQueueMutex);
consolidateTransaction(_transactionQueue, consolidatedTransaction); localTransactionQueue.swap(_transactionQueue);
} }
uint32_t frameNumber = 0; Transaction consolidatedTransaction;
consolidatedTransaction.merge(std::move(localTransactionQueue));
{ {
std::unique_lock<std::mutex> lock(_transactionFramesMutex); std::unique_lock<std::mutex> lock(_transactionFramesMutex);
_transactionFrames.push_back(consolidatedTransaction); _transactionFrames.push_back(consolidatedTransaction);
_transactionFrameNumber++;
frameNumber = _transactionFrameNumber;
} }
return frameNumber; return ++_transactionFrameNumber;
} }
void Scene::processTransactionQueue() { void Scene::processTransactionQueue() {
PROFILE_RANGE(render, __FUNCTION__); PROFILE_RANGE(render, __FUNCTION__);
TransactionFrames queuedFrames; static TransactionFrames queuedFrames;
{ {
// capture the queued frames and clear the queue // capture the queued frames and clear the queue
std::unique_lock<std::mutex> lock(_transactionFramesMutex); std::unique_lock<std::mutex> lock(_transactionFramesMutex);
queuedFrames = _transactionFrames; queuedFrames.swap(_transactionFrames);
_transactionFrames.clear();
} }
// go through the queue of frames and process them // go through the queue of frames and process them
for (auto& frame : queuedFrames) { for (auto& frame : queuedFrames) {
processTransactionFrame(frame); processTransactionFrame(frame);
} }
queuedFrames.clear();
} }
void Scene::processTransactionFrame(const Transaction& transaction) { void Scene::processTransactionFrame(const Transaction& transaction) {

View file

@ -65,9 +65,14 @@ public:
void resetSelectionHighlight(const std::string& selectionName, const HighlightStyle& style = HighlightStyle()); void resetSelectionHighlight(const std::string& selectionName, const HighlightStyle& style = HighlightStyle());
void removeHighlightFromSelection(const std::string& selectionName); void removeHighlightFromSelection(const std::string& selectionName);
void querySelectionHighlight(const std::string& selectionName, SelectionHighlightQueryFunc func); void querySelectionHighlight(const std::string& selectionName, const SelectionHighlightQueryFunc& func);
void reserve(const std::vector<Transaction>& transactionContainer);
void merge(const std::vector<Transaction>& transactionContainer);
void merge(std::vector<Transaction>&& transactionContainer);
void merge(const Transaction& transaction); void merge(const Transaction& transaction);
void merge(Transaction&& transaction);
void clear();
// Checkers if there is work to do when processing the transaction // Checkers if there is work to do when processing the transaction
bool touchTransactions() const { return !_resetSelections.empty(); } bool touchTransactions() const { return !_resetSelections.empty(); }
@ -107,7 +112,7 @@ protected:
HighlightRemoves _highlightRemoves; HighlightRemoves _highlightRemoves;
HighlightQueries _highlightQueries; HighlightQueries _highlightQueries;
}; };
typedef std::queue<Transaction> TransactionQueue; typedef std::vector<Transaction> TransactionQueue;
// Scene is a container for Items // Scene is a container for Items
@ -133,6 +138,9 @@ public:
// Enqueue transaction to the scene // Enqueue transaction to the scene
void enqueueTransaction(const Transaction& transaction); void enqueueTransaction(const Transaction& transaction);
// Enqueue transaction to the scene
void enqueueTransaction(Transaction&& transaction);
// Enqueue end of frame transactions boundary // Enqueue end of frame transactions boundary
uint32_t enqueueFrame(); uint32_t enqueueFrame();
@ -187,7 +195,7 @@ protected:
std::mutex _transactionFramesMutex; std::mutex _transactionFramesMutex;
using TransactionFrames = std::list<Transaction>; using TransactionFrames = std::vector<Transaction>;
TransactionFrames _transactionFrames; TransactionFrames _transactionFrames;
uint32_t _transactionFrameNumber{ 0 }; uint32_t _transactionFrameNumber{ 0 };

View file

@ -12,6 +12,9 @@
#define hifi_PrioritySortUtil_h #define hifi_PrioritySortUtil_h
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <queue>
#include "NumericalConstants.h"
#include "ViewFrustum.h" #include "ViewFrustum.h"
/* PrioritySortUtil is a helper for sorting 3D things relative to a ViewFrustum. To use: /* PrioritySortUtil is a helper for sorting 3D things relative to a ViewFrustum. To use:
@ -32,11 +35,10 @@
(2) Make a PrioritySortUtil::PriorityQueue<Thing> and add them to the queue: (2) Make a PrioritySortUtil::PriorityQueue<Thing> and add them to the queue:
PrioritySortUtil::Prioritizer prioritizer(viewFrustum); PrioritySortUtil::PriorityQueue<SortableWrapper> sortedThings(viewFrustum);
std::priority_queue< PrioritySortUtil::Sortable<Thing> > sortedThings; std::priority_queue< PrioritySortUtil::Sortable<Thing> > sortedThings;
for (thing in things) { for (thing in things) {
float priority = prioritizer.computePriority(PrioritySortUtil::PrioritizableThing(thing)); sortedThings.push(SortableWrapper(thing));
sortedThings.push(PrioritySortUtil::Sortable<Thing> entry(thing, priority));
} }
(3) Loop over your priority queue and do timeboxed work: (3) Loop over your priority queue and do timeboxed work:
@ -65,6 +67,7 @@ namespace PrioritySortUtil {
virtual uint64_t getTimestamp() const = 0; virtual uint64_t getTimestamp() const = 0;
void setPriority(float priority) { _priority = priority; } void setPriority(float priority) { _priority = priority; }
float getPriority() const { return _priority; }
bool operator<(const Sortable& other) const { return _priority < other._priority; } bool operator<(const Sortable& other) const { return _priority < other._priority; }
private: private:
float _priority { 0.0f }; float _priority { 0.0f };
@ -109,11 +112,19 @@ namespace PrioritySortUtil {
glm::vec3 position = thing.getPosition(); glm::vec3 position = thing.getPosition();
glm::vec3 offset = position - _view.getPosition(); glm::vec3 offset = position - _view.getPosition();
float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero
float radius = thing.getRadius(); const float MIN_RADIUS = 0.1f; // WORKAROUND for zero size objects (we still want them to sort by distance)
float radius = glm::min(thing.getRadius(), MIN_RADIUS);
float cosineAngle = (glm::dot(offset, _view.getDirection()) / distance);
float age = (float)(usecTimestampNow() - thing.getTimestamp());
float priority = _angularWeight * (radius / distance) // we modulatate "age" drift rate by the cosineAngle term to make periphrial objects sort forward
+ _centerWeight * (glm::dot(offset, _view.getDirection()) / distance) // at a reduced rate but we don't want the "age" term to go zero or negative so we clamp it
+ _ageWeight * (float)(usecTimestampNow() - thing.getTimestamp()); const float MIN_COSINE_ANGLE_FACTOR = 0.1f;
float cosineAngleFactor = glm::max(cosineAngle, MIN_COSINE_ANGLE_FACTOR);
float priority = _angularWeight * glm::max(radius, MIN_RADIUS) / distance
+ _centerWeight * cosineAngle
+ _ageWeight * cosineAngleFactor * age;
// decrement priority of things outside keyhole // decrement priority of things outside keyhole
if (distance - radius > _view.getCenterRadius()) { if (distance - radius > _view.getCenterRadius()) {

View file

@ -63,7 +63,7 @@ var toolBar = (function() {
y: -TOOLBAR_MARGIN_Y - toolHeight y: -TOOLBAR_MARGIN_Y - toolHeight
}); });
browseDirectoryButton = toolBar.addTool({ browseDirectoryButton = toolBar.addTool({
imageURL: toolIconUrl + "directory-01.svg", imageURL: toolIconUrl + "directory.svg",
subImage: { subImage: {
x: 0, x: 0,
y: Tool.IMAGE_WIDTH, y: Tool.IMAGE_WIDTH,

View file

@ -1415,6 +1415,9 @@ input#reset-to-natural-dimensions {
} }
/* ----- Order of Menu items for Primitive ----- */ /* ----- Order of Menu items for Primitive ----- */
/* Entity Menu classes are specified by selected entity
within entityProperties.js
*/
#properties-list.ShapeMenu #general, #properties-list.ShapeMenu #general,
#properties-list.BoxMenu #general, #properties-list.BoxMenu #general,
#properties-list.SphereMenu #general { #properties-list.SphereMenu #general {
@ -1469,6 +1472,34 @@ input#reset-to-natural-dimensions {
display: none; display: none;
} }
/* ----- ParticleEffectMenu ----- */
#properties-list.ParticleEffectMenu #general {
order: 1;
}
#properties-list.ParticleEffectMenu #collision-info {
order: 2;
}
#properties-list.ParticleEffectMenu #physical {
order: 3;
}
#properties-list.ParticleEffectMenu #spatial {
order: 4;
}
#properties-list.ParticleEffectMenu #behavior {
order: 5;
}
/* items to hide */
#properties-list.ParticleEffectMenu #base-color-section,
#properties-list.ParticleEffectMenu #hyperlink,
#properties-list.ParticleEffectMenu #light,
#properties-list.ParticleEffectMenu #model,
#properties-list.ParticleEffectMenu #shape-list,
#properties-list.ParticleEffectMenu #text,
#properties-list.ParticleEffectMenu #web,
#properties-list.ParticleEffectMenu #zone {
display: none;
}
/* ----- Order of Menu items for Light ----- */ /* ----- Order of Menu items for Light ----- */
#properties-list.LightMenu #general { #properties-list.LightMenu #general {
@ -1500,8 +1531,8 @@ input#reset-to-natural-dimensions {
display: none; display: none;
} }
/* items to hide */ /* items to hide */
#properties-list.LightMenu .shape-group.shape-section.property.dropdown, #properties-list.LightMenu #shape-list,
#properties-list.LightMenu color-section.color-control1 { #properties-list.LightMenu #base-color-section {
display: none display: none
} }
@ -1536,8 +1567,8 @@ input#reset-to-natural-dimensions {
display: none; display: none;
} }
/* items to hide */ /* items to hide */
#properties-list.ModelMenu .shape-group.shape-section.property.dropdown, #properties-list.ModelMenu #shape-list,
#properties-list.ModelMenu .color-section.color-control1 { #properties-list.ModelMenu #base-color-section {
display: none display: none
} }
@ -1572,8 +1603,8 @@ input#reset-to-natural-dimensions {
display: none; display: none;
} }
/* items to hide */ /* items to hide */
#properties-list.ZoneMenu .shape-group.shape-section.property.dropdown, #properties-list.ZoneMenu #shape-list,
#properties-list.ZoneMenu .color-section.color-control1 { #properties-list.ZoneMenu #base-color-section {
display: none display: none
} }
@ -1608,8 +1639,8 @@ input#reset-to-natural-dimensions {
display: none; display: none;
} }
/* items to hide */ /* items to hide */
#properties-list.WebMenu .shape-group.shape-section.property.dropdown, #properties-list.WebMenu #shape-list,
#properties-list.WebMenu .color-section.color-control1 { #properties-list.WebMenu #base-color-section {
display: none; display: none;
} }
@ -1645,8 +1676,8 @@ input#reset-to-natural-dimensions {
display: none; display: none;
} }
/* items to hide */ /* items to hide */
#properties-list.TextMenu .shape-group.shape-section.property.dropdown, #properties-list.TextMenu #shape-list,
#properties-list.TextMenu .color-section.color-control1 { #properties-list.TextMenu #base-color-section {
display: none display: none
} }

View file

@ -44,7 +44,7 @@
<fieldset id="general" class="major"> <fieldset id="general" class="major">
<div class="shape-group shape-section property dropdown"> <div class="shape-group shape-section property dropdown" id="shape-list">
<label for="property-shape">Shape</label> <label for="property-shape">Shape</label>
<select name="SelectShape" id="property-shape"> <select name="SelectShape" id="property-shape">
<option value="Cube">Box</option> <option value="Cube">Box</option>
@ -66,12 +66,19 @@
<label for="property-name">Name</label> <label for="property-name">Name</label>
<input type="text" id="property-name"> <input type="text" id="property-name">
</div> </div>
<div class="physical-group color-section property color-control1"> <div class="physical-group color-section property rgb fstuple" id="base-color-section">
<div class="color-picker" id="property-color-control1"></div> <div class="color-picker" id="property-color-control2"></div>
<label>Entity color</label> <label>Entity color</label>
<div class="tuple">
<div><input type="number" class="red" id="property-color-red"><label for="property-color-red">Red:</label></div>
<div><input type="number" class="green" id="property-color-green"><label for="property-color-green">Green:</label></div>
<div><input type="number" class="blue" id="property-color-blue"><label for="property-color-blue">Blue:</label></div>
</div>
</div> </div>
</fieldset> </fieldset>
<fieldset id="collision-info">
<fieldset id="collision-info" class="major">
<legend class="section-header"> Collision<span>M</span> </legend>
<fieldset class="minor"> <fieldset class="minor">
<div class="behavior-group property checkbox"> <div class="behavior-group property checkbox">
<input type="checkbox" id="property-collisionless"> <input type="checkbox" id="property-collisionless">
@ -216,17 +223,6 @@
</div> </div>
</fieldset> </fieldset>
</fieldset> </fieldset>
<fieldset class="minor">
<div class="physical-group color-section property rgb fstuple">
<div class="color-picker" id="property-color-control2"></div>
<label>Entity color</label>
<div class="tuple">
<div><input type="number" class="red" id="property-color-red"><label for="property-color-red">Red:</label></div>
<div><input type="number" class="green" id="property-color-green"><label for="property-color-green">Green:</label></div>
<div><input type="number" class="blue" id="property-color-blue"><label for="property-color-blue">Blue:</label></div>
</div>
</div>
</fieldset>
</fieldset> </fieldset>

File diff suppressed because it is too large Load diff

View file

@ -110,8 +110,10 @@
var filterText; // Used for updating Purchases QML var filterText; // Used for updating Purchases QML
function onScreenChanged(type, url) { function onScreenChanged(type, url) {
onMarketplaceScreen = type === "Web" && url.indexOf(MARKETPLACE_URL) !== -1; onMarketplaceScreen = type === "Web" && url.indexOf(MARKETPLACE_URL) !== -1;
onCommerceScreen = type === "QML" && (url.indexOf(MARKETPLACE_CHECKOUT_QML_PATH_BASE) !== -1 || url === MARKETPLACE_PURCHASES_QML_PATH || url.indexOf(MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH) !== -1); onWalletScreen = url.indexOf(MARKETPLACE_WALLET_QML_PATH) !== -1;
wireEventBridge(onCommerceScreen); onCommerceScreen = type === "QML" && (url.indexOf(MARKETPLACE_CHECKOUT_QML_PATH_BASE) !== -1 || url === MARKETPLACE_PURCHASES_QML_PATH
|| url.indexOf(MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH) !== -1);
wireEventBridge(onMarketplaceScreen || onCommerceScreen || onWalletScreen);
if (url === MARKETPLACE_PURCHASES_QML_PATH) { if (url === MARKETPLACE_PURCHASES_QML_PATH) {
tablet.sendToQml({ tablet.sendToQml({
@ -122,7 +124,7 @@
} }
// for toolbar mode: change button to active when window is first openend, false otherwise. // for toolbar mode: change button to active when window is first openend, false otherwise.
marketplaceButton.editProperties({ isActive: onMarketplaceScreen || onCommerceScreen }); marketplaceButton.editProperties({ isActive: (onMarketplaceScreen || onCommerceScreen) && !onWalletScreen });
if (type === "Web" && url.indexOf(MARKETPLACE_URL) !== -1) { if (type === "Web" && url.indexOf(MARKETPLACE_URL) !== -1) {
ContextOverlay.isInMarketplaceInspectionMode = true; ContextOverlay.isInMarketplaceInspectionMode = true;
} else { } else {
@ -322,6 +324,11 @@
} else if (parsedJsonMessage.type === "LOGIN") { } else if (parsedJsonMessage.type === "LOGIN") {
openLoginWindow(); openLoginWindow();
} else if (parsedJsonMessage.type === "WALLET_SETUP") { } else if (parsedJsonMessage.type === "WALLET_SETUP") {
wireEventBridge(true);
tablet.sendToQml({
method: 'updateWalletReferrer',
referrer: "marketplace cta"
});
openWallet(); openWallet();
} else if (parsedJsonMessage.type === "MY_ITEMS") { } else if (parsedJsonMessage.type === "MY_ITEMS") {
referrerURL = MARKETPLACE_URL_INITIAL; referrerURL = MARKETPLACE_URL_INITIAL;
@ -399,6 +406,7 @@
referrer: "purchases" referrer: "purchases"
}); });
openWallet(); openWallet();
break;
case 'checkout_walletNotSetUp': case 'checkout_walletNotSetUp':
wireEventBridge(true); wireEventBridge(true);
tablet.sendToQml({ tablet.sendToQml({