mirror of
https://github.com/lubosz/overte.git
synced 2025-04-24 11:43:16 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi
This commit is contained in:
commit
bcf3afd758
52 changed files with 954 additions and 711 deletions
|
@ -25,6 +25,23 @@ AvatarMixerClientData::AvatarMixerClientData(const QUuid& 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) {
|
||||
if (!_packetQueue.node) {
|
||||
_packetQueue.node = node;
|
||||
|
|
|
@ -110,16 +110,10 @@ public:
|
|||
bool getRequestsDomainListData() { return _requestsDomainListData; }
|
||||
void setRequestsDomainListData(bool requesting) { _requestsDomainListData = requesting; }
|
||||
|
||||
ViewFrustum getViewFrustom() const { return _currentViewFrustum; }
|
||||
ViewFrustum getViewFrustum() const { return _currentViewFrustum; }
|
||||
|
||||
quint64 getLastOtherAvatarEncodeTime(QUuid otherAvatar) {
|
||||
quint64 result = 0;
|
||||
if (_lastOtherAvatarEncodeTime.find(otherAvatar) != _lastOtherAvatarEncodeTime.end()) {
|
||||
result = _lastOtherAvatarEncodeTime[otherAvatar];
|
||||
}
|
||||
_lastOtherAvatarEncodeTime[otherAvatar] = usecTimestampNow();
|
||||
return result;
|
||||
}
|
||||
uint64_t getLastOtherAvatarEncodeTime(QUuid otherAvatar) const;
|
||||
void setLastOtherAvatarEncodeTime(const QUuid& otherAvatar, const uint64_t& time);
|
||||
|
||||
QVector<JointData>& getLastOtherAvatarSentJoints(QUuid otherAvatar) {
|
||||
_lastOtherAvatarSentJoints[otherAvatar].resize(_avatar->getJointCount());
|
||||
|
@ -143,7 +137,7 @@ private:
|
|||
|
||||
// this is a map of the last time we encoded an "other" avatar for
|
||||
// sending to "this" node
|
||||
std::unordered_map<QUuid, quint64> _lastOtherAvatarEncodeTime;
|
||||
std::unordered_map<QUuid, uint64_t> _lastOtherAvatarEncodeTime;
|
||||
std::unordered_map<QUuid, QVector<JointData>> _lastOtherAvatarSentJoints;
|
||||
|
||||
uint64_t _identityChangeTimestamp;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <NodeList.h>
|
||||
#include <Node.h>
|
||||
#include <OctreeConstants.h>
|
||||
#include <PrioritySortUtil.h>
|
||||
#include <udt/PacketHeaders.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <StDev.h>
|
||||
|
@ -32,6 +33,10 @@
|
|||
#include "AvatarMixerClientData.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) {
|
||||
_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
|
||||
// for calling the AvatarData::sortAvatars() function and getting our sorted list of client nodes
|
||||
QList<AvatarSharedPointer> avatarList;
|
||||
std::vector<AvatarSharedPointer> avatarsToSort;
|
||||
std::unordered_map<AvatarSharedPointer, SharedNodePointer> avatarDataToNodes;
|
||||
|
||||
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
|
||||
if (otherNode->getType() == NodeType::Agent
|
||||
|
@ -195,36 +198,61 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
const AvatarMixerClientData* otherNodeData = reinterpret_cast<const AvatarMixerClientData*>(otherNode->getLinkedData());
|
||||
|
||||
AvatarSharedPointer otherAvatar = otherNodeData->getAvatarSharedPointer();
|
||||
avatarList << otherAvatar;
|
||||
avatarsToSort.push_back(otherAvatar);
|
||||
avatarDataToNodes[otherAvatar] = otherNode;
|
||||
}
|
||||
});
|
||||
|
||||
AvatarSharedPointer thisAvatar = nodeData->getAvatarSharedPointer();
|
||||
ViewFrustum cameraView = nodeData->getViewFrustom();
|
||||
std::priority_queue<AvatarPriority> sortedAvatars;
|
||||
AvatarData::sortAvatars(avatarList, cameraView, sortedAvatars,
|
||||
[&](AvatarSharedPointer avatar)->uint64_t {
|
||||
auto avatarNode = avatarDataToNodes[avatar];
|
||||
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{
|
||||
glm::vec3 nodeBoxHalfScale = (avatar->getWorldPosition() - avatar->getGlobalBoundingBoxCorner() * avatar->getSensorToWorldScale());
|
||||
return glm::max(nodeBoxHalfScale.x, glm::max(nodeBoxHalfScale.y, nodeBoxHalfScale.z));
|
||||
}, [&](AvatarSharedPointer avatar)->bool {
|
||||
// now that we've assembled the avatarDataToNodes map we can replace PrioritySortUtil::getAvatarAgeCallback
|
||||
// with the true implementation
|
||||
PrioritySortUtil::getAvatarAgeCallback = [&] (const AvatarSharedPointer& avatar) {
|
||||
auto avatarNode = avatarDataToNodes[avatar];
|
||||
assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map
|
||||
return nodeData->getLastOtherAvatarEncodeTime(avatarNode->getUUID());
|
||||
};
|
||||
|
||||
class SortableAvatar: public PrioritySortUtil::Sortable {
|
||||
public:
|
||||
SortableAvatar() = delete;
|
||||
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) {
|
||||
return true; // ignore ourselves...
|
||||
// don't echo updates to self
|
||||
continue;
|
||||
}
|
||||
|
||||
bool shouldIgnore = false;
|
||||
|
||||
// We will also ignore other nodes for a couple of different reasons:
|
||||
// We ignore other nodes for a couple of reasons:
|
||||
// 1) ignore bubbles and ignore specific node
|
||||
// 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
|
||||
// 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
|
||||
|
||||
const AvatarMixerClientData* avatarNodeData = reinterpret_cast<const AvatarMixerClientData*>(avatarNode->getLinkedData());
|
||||
|
@ -240,7 +268,6 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
|| (avatarNode->isIgnoringNodeWithID(node->getUUID()) && !getsAnyIgnored)) {
|
||||
shouldIgnore = true;
|
||||
} else {
|
||||
|
||||
// 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
|
||||
if (node->isIgnoreRadiusEnabled() || (avatarNode->isIgnoreRadiusEnabled() && !getsAnyIgnored)) {
|
||||
|
@ -267,8 +294,6 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
nodeData->removeFromRadiusIgnoringSet(node, avatarNode->getUUID());
|
||||
}
|
||||
}
|
||||
quint64 endIgnoreCalculation = usecTimestampNow();
|
||||
_stats.ignoreCalculationElapsedTime += (endIgnoreCalculation - startIgnoreCalculation);
|
||||
|
||||
if (!shouldIgnore) {
|
||||
AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(avatarNode->getUUID());
|
||||
|
@ -292,20 +317,21 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
++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
|
||||
int avatarRank = 0;
|
||||
|
||||
// this is overly conservative, because it includes some avatars we might not consider
|
||||
int remainingAvatars = (int)sortedAvatars.size();
|
||||
|
||||
while (!sortedAvatars.empty()) {
|
||||
AvatarPriority sortData = sortedAvatars.top();
|
||||
const auto& avatarData = sortedAvatars.top().getAvatar();
|
||||
sortedAvatars.pop();
|
||||
const auto& avatarData = sortData.avatar;
|
||||
avatarRank++;
|
||||
remainingAvatars--;
|
||||
|
||||
auto otherNode = avatarDataToNodes[avatarData];
|
||||
|
@ -332,10 +358,8 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
nodeData->setLastBroadcastTime(otherNode->getUUID(), usecTimestampNow());
|
||||
}
|
||||
|
||||
// determine if avatar is in view which determines how much data to send
|
||||
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();
|
||||
AABox otherNodeBox(otherNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale);
|
||||
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
|
||||
nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(),
|
||||
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();
|
||||
|
||||
quint64 endAvatarDataPacking = usecTimestampNow();
|
||||
_stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking);
|
||||
};
|
||||
}
|
||||
|
||||
quint64 startPacketSending = usecTimestampNow();
|
||||
|
||||
|
|
|
@ -29,10 +29,6 @@ macro(GENERATE_INSTALLERS)
|
|||
|
||||
|
||||
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")
|
||||
|
||||
# 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_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)
|
||||
# produce a drag and drop DMG on OS X
|
||||
set(CPACK_GENERATOR "DragNDrop")
|
||||
|
@ -84,4 +84,3 @@ macro(GENERATE_INSTALLERS)
|
|||
|
||||
include(CPack)
|
||||
endmacro()
|
||||
|
||||
|
|
|
@ -45,5 +45,4 @@ else()
|
|||
endif()
|
||||
|
||||
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")
|
||||
|
|
|
@ -65,21 +65,23 @@ var EventBridge;
|
|||
// we need to listen to events that might precede the addition of this elements.
|
||||
// A more robust hack will be to add a setInterval that look for DOM changes every 100-300 ms (low performance?)
|
||||
|
||||
window.onload = function(){
|
||||
window.addEventListener("load",function(event) {
|
||||
setTimeout(function() {
|
||||
EventBridge.forceHtmlAudioOutputDeviceUpdate();
|
||||
}, 1200);
|
||||
};
|
||||
document.onclick = function(){
|
||||
}, false);
|
||||
|
||||
document.addEventListener("click",function(){
|
||||
setTimeout(function() {
|
||||
EventBridge.forceHtmlAudioOutputDeviceUpdate();
|
||||
}, 1200);
|
||||
};
|
||||
document.onchange = function(){
|
||||
}, false);
|
||||
|
||||
document.addEventListener("change",function(){
|
||||
setTimeout(function() {
|
||||
EventBridge.forceHtmlAudioOutputDeviceUpdate();
|
||||
}, 1200);
|
||||
};
|
||||
}, false);
|
||||
|
||||
tempEventBridge._callbacks.forEach(function (callback) {
|
||||
EventBridge.scriptEventReceived.connect(callback);
|
||||
|
|
|
@ -12,9 +12,9 @@
|
|||
var MAX_WARNINGS = 3;
|
||||
var numWarnings = 0;
|
||||
var isWindowFocused = true;
|
||||
var isKeyboardRaised = false;
|
||||
var isNumericKeyboard = false;
|
||||
var isPasswordField = false;
|
||||
window.isKeyboardRaised = false;
|
||||
window.isNumericKeyboard = false;
|
||||
window.isPasswordField = false;
|
||||
|
||||
function shouldSetPasswordField() {
|
||||
var nodeType = document.activeElement.type;
|
||||
|
@ -62,7 +62,7 @@
|
|||
var passwordField = shouldSetPasswordField();
|
||||
|
||||
if (isWindowFocused &&
|
||||
(keyboardRaised !== isKeyboardRaised || numericKeyboard !== isNumericKeyboard || passwordField !== isPasswordField)) {
|
||||
(keyboardRaised !== window.isKeyboardRaised || numericKeyboard !== window.isNumericKeyboard || passwordField !== window.isPasswordField)) {
|
||||
|
||||
if (typeof EventBridge !== "undefined" && EventBridge !== null) {
|
||||
EventBridge.emitWebEvent(
|
||||
|
@ -75,20 +75,20 @@
|
|||
}
|
||||
}
|
||||
|
||||
if (!isKeyboardRaised) {
|
||||
if (!window.isKeyboardRaised) {
|
||||
scheduleBringToView(250); // Allow time for keyboard to be raised in QML.
|
||||
// 2DO: should it be rather done from 'client area height changed' event?
|
||||
}
|
||||
|
||||
isKeyboardRaised = keyboardRaised;
|
||||
isNumericKeyboard = numericKeyboard;
|
||||
isPasswordField = passwordField;
|
||||
window.isKeyboardRaised = keyboardRaised;
|
||||
window.isNumericKeyboard = numericKeyboard;
|
||||
window.isPasswordField = passwordField;
|
||||
}
|
||||
}, POLL_FREQUENCY);
|
||||
|
||||
window.addEventListener("click", function () {
|
||||
var keyboardRaised = shouldRaiseKeyboard();
|
||||
if(keyboardRaised && isKeyboardRaised) {
|
||||
if (keyboardRaised && window.isKeyboardRaised) {
|
||||
scheduleBringToView(150);
|
||||
}
|
||||
});
|
||||
|
@ -99,7 +99,7 @@
|
|||
|
||||
window.addEventListener("blur", function () {
|
||||
isWindowFocused = false;
|
||||
isKeyboardRaised = false;
|
||||
isNumericKeyboard = false;
|
||||
window.isKeyboardRaised = false;
|
||||
window.isNumericKeyboard = false;
|
||||
});
|
||||
})();
|
||||
|
|
|
@ -79,10 +79,12 @@ Rectangle {
|
|||
if (result.status !== 'success') {
|
||||
failureErrorText.text = result.message;
|
||||
root.activeView = "checkoutFailure";
|
||||
UserActivityLogger.commercePurchaseFailure(root.itemId, root.itemPrice, !root.alreadyOwned, result.message);
|
||||
} else {
|
||||
root.itemHref = result.data.download_url;
|
||||
root.isWearable = result.data.categories.indexOf("Wearables") > -1;
|
||||
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});
|
||||
rezzedNotifContainer.visible = true;
|
||||
rezzedNotifContainerTimer.start();
|
||||
UserActivityLogger.commerceEntityRezzed(root.itemId, "checkout", root.isWearable ? "rez" : "wear");
|
||||
}
|
||||
}
|
||||
RalewaySemiBold {
|
||||
|
|
|
@ -349,6 +349,7 @@ Item {
|
|||
sendToPurchases({method: 'purchases_rezClicked', itemHref: root.itemHref, isWearable: root.isWearable});
|
||||
rezzedNotifContainer.visible = true;
|
||||
rezzedNotifContainerTimer.start();
|
||||
UserActivityLogger.commerceEntityRezzed(root.itemId, "purchases", root.isWearable ? "rez" : "wear");
|
||||
}
|
||||
|
||||
style: ButtonStyle {
|
||||
|
|
|
@ -206,16 +206,6 @@ Item {
|
|||
root.isPasswordField = (focus && passphraseField.echoMode === TextInput.Password);
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
|
||||
onClicked: {
|
||||
root.keyboardRaised = true;
|
||||
root.isPasswordField = (passphraseField.echoMode === TextInput.Password);
|
||||
mouse.accepted = false;
|
||||
}
|
||||
}
|
||||
|
||||
onAccepted: {
|
||||
submitPassphraseInputButton.enabled = false;
|
||||
commerce.setPassphrase(passphraseField.text);
|
||||
|
@ -362,25 +352,6 @@ Item {
|
|||
right: parent.right;
|
||||
}
|
||||
|
||||
Image {
|
||||
id: lowerKeyboardButton;
|
||||
z: 999;
|
||||
source: "images/lowerKeyboard.png";
|
||||
anchors.right: keyboard.right;
|
||||
anchors.top: keyboard.showMirrorText ? keyboard.top : undefined;
|
||||
anchors.bottom: keyboard.showMirrorText ? undefined : keyboard.bottom;
|
||||
height: 50;
|
||||
width: 60;
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
|
||||
onClicked: {
|
||||
root.keyboardRaised = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.Keyboard {
|
||||
id: keyboard;
|
||||
raised: HMD.mounted && root.keyboardRaised;
|
||||
|
|
|
@ -82,17 +82,6 @@ Item {
|
|||
if (focus) {
|
||||
var hidePassword = (currentPassphraseField.echoMode === TextInput.Password);
|
||||
sendSignalToWallet({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword});
|
||||
} else if (!passphraseFieldAgain.focus) {
|
||||
sendSignalToWallet({method: 'walletSetup_lowerKeyboard', isPasswordField: false});
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
onPressed: {
|
||||
var hidePassword = (currentPassphraseField.echoMode === TextInput.Password);
|
||||
sendSignalToWallet({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword});
|
||||
mouse.accepted = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,21 +104,10 @@ Item {
|
|||
activeFocusOnPress: true;
|
||||
activeFocusOnTab: true;
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
onPressed: {
|
||||
var hidePassword = (passphraseField.echoMode === TextInput.Password);
|
||||
sendSignalToWallet({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword});
|
||||
mouse.accepted = false;
|
||||
}
|
||||
}
|
||||
|
||||
onFocusChanged: {
|
||||
if (focus) {
|
||||
var hidePassword = (passphraseField.echoMode === TextInput.Password);
|
||||
sendMessageToLightbox({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword});
|
||||
} else if (!passphraseFieldAgain.focus) {
|
||||
sendMessageToLightbox({method: 'walletSetup_lowerKeyboard', isPasswordField: false});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,21 +129,10 @@ Item {
|
|||
activeFocusOnPress: true;
|
||||
activeFocusOnTab: true;
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
onPressed: {
|
||||
var hidePassword = (passphraseFieldAgain.echoMode === TextInput.Password);
|
||||
sendSignalToWallet({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword});
|
||||
mouse.accepted = false;
|
||||
}
|
||||
}
|
||||
|
||||
onFocusChanged: {
|
||||
if (focus) {
|
||||
var hidePassword = (passphraseFieldAgain.echoMode === TextInput.Password);
|
||||
sendMessageToLightbox({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword});
|
||||
} else if (!passphraseField.focus) {
|
||||
sendMessageToLightbox({method: 'walletSetup_lowerKeyboard', isPasswordField: false});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -47,6 +47,12 @@ Rectangle {
|
|||
} else if (walletStatus === 1) {
|
||||
if (root.activeView !== "walletSetup") {
|
||||
root.activeView = "walletSetup";
|
||||
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) {
|
||||
if (root.activeView !== "passphraseModal") {
|
||||
|
@ -172,7 +178,7 @@ Rectangle {
|
|||
Connections {
|
||||
onSendSignalToWallet: {
|
||||
if (msg.method === 'walletSetup_finished') {
|
||||
if (msg.referrer === '') {
|
||||
if (msg.referrer === '' || msg.referrer === 'marketplace cta') {
|
||||
root.activeView = "initialize";
|
||||
commerce.getWalletStatus();
|
||||
} else if (msg.referrer === 'purchases') {
|
||||
|
@ -666,25 +672,6 @@ Rectangle {
|
|||
right: parent.right;
|
||||
}
|
||||
|
||||
Image {
|
||||
id: lowerKeyboardButton;
|
||||
z: 999;
|
||||
source: "images/lowerKeyboard.png";
|
||||
anchors.right: keyboard.right;
|
||||
anchors.top: keyboard.showMirrorText ? keyboard.top : undefined;
|
||||
anchors.bottom: keyboard.showMirrorText ? undefined : keyboard.bottom;
|
||||
height: 50;
|
||||
width: 60;
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
|
||||
onClicked: {
|
||||
root.keyboardRaised = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.Keyboard {
|
||||
id: keyboard;
|
||||
raised: HMD.mounted && root.keyboardRaised;
|
||||
|
@ -719,12 +706,28 @@ Rectangle {
|
|||
case 'updateWalletReferrer':
|
||||
walletSetup.referrer = message.referrer;
|
||||
break;
|
||||
case 'inspectionCertificate_resetCert':
|
||||
// NOP
|
||||
break;
|
||||
default:
|
||||
console.log('Unrecognized message from wallet.js:', JSON.stringify(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
|
||||
//
|
||||
|
|
|
@ -68,6 +68,7 @@ Item {
|
|||
Connections {
|
||||
target: GlobalServices
|
||||
onMyUsernameChanged: {
|
||||
transactionHistoryModel.clear();
|
||||
usernameText.text = Account.username;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,10 @@ Item {
|
|||
property bool hasShownSecurityImageTip: false;
|
||||
property string referrer;
|
||||
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 {
|
||||
anchors.fill: parent;
|
||||
|
@ -67,6 +71,13 @@ Item {
|
|||
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
|
||||
//
|
||||
|
@ -371,7 +382,7 @@ Item {
|
|||
|
||||
Item {
|
||||
id: securityImageTip;
|
||||
visible: false;
|
||||
visible: !root.hasShownSecurityImageTip && root.activeView === "step_3";
|
||||
z: 999;
|
||||
anchors.fill: root;
|
||||
|
||||
|
@ -421,7 +432,6 @@ Item {
|
|||
text: "Got It";
|
||||
onClicked: {
|
||||
root.hasShownSecurityImageTip = true;
|
||||
securityImageTip.visible = false;
|
||||
passphraseSelection.focusFirstTextField();
|
||||
}
|
||||
}
|
||||
|
@ -439,9 +449,6 @@ Item {
|
|||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
commerce.getWalletAuthenticatedStatus();
|
||||
if (!root.hasShownSecurityImageTip) {
|
||||
securityImageTip.visible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -732,7 +739,11 @@ Item {
|
|||
text: "Finish";
|
||||
onClicked: {
|
||||
root.visible = false;
|
||||
root.hasShownSecurityImageTip = false;
|
||||
sendSignalToWallet({method: 'walletSetup_finished', referrer: root.referrer ? root.referrer : ""});
|
||||
|
||||
var timestamp = new Date();
|
||||
UserActivityLogger.commerceWalletSetupFinished(timestamp, setupAttemptID, Math.round((timestamp - root.startingTimestamp)/1000));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,33 +9,30 @@ Overlay {
|
|||
|
||||
Image {
|
||||
id: image
|
||||
property bool scaleFix: true;
|
||||
property real xOffset: 0
|
||||
property real yOffset: 0
|
||||
property bool scaleFix: true
|
||||
property real xStart: 0
|
||||
property real yStart: 0
|
||||
property real xSize: 0
|
||||
property real ySize: 0
|
||||
property real imageScale: 1.0
|
||||
property var resizer: Timer {
|
||||
interval: 50
|
||||
repeat: false
|
||||
running: false
|
||||
onTriggered: {
|
||||
var targetAspect = root.width / root.height;
|
||||
var sourceAspect = image.sourceSize.width / image.sourceSize.height;
|
||||
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;
|
||||
if (image.xSize === 0) {
|
||||
image.xSize = image.sourceSize.width - image.xStart;
|
||||
}
|
||||
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: {
|
||||
if (sourceSize.width !== 0 && sourceSize.height !== 0 && progress === 1.0 && scaleFix) {
|
||||
|
@ -43,6 +40,8 @@ Overlay {
|
|||
resizer.start();
|
||||
}
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
ColorOverlay {
|
||||
|
@ -57,8 +56,10 @@ Overlay {
|
|||
var key = keys[i];
|
||||
var value = subImage[key];
|
||||
switch (key) {
|
||||
case "x": image.xOffset = value; break;
|
||||
case "y": image.yOffset = value; break;
|
||||
case "x": image.xStart = value; break;
|
||||
case "y": image.yStart = value; break;
|
||||
case "width": image.xSize = value; break;
|
||||
case "height": image.ySize = value; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7075,11 +7075,11 @@ QRect Application::getRecommendedHUDRect() const {
|
|||
return result;
|
||||
}
|
||||
|
||||
QSize Application::getDeviceSize() const {
|
||||
glm::vec2 Application::getDeviceSize() const {
|
||||
static const int MIN_SIZE = 1;
|
||||
QSize result(MIN_SIZE, MIN_SIZE);
|
||||
glm::vec2 result(MIN_SIZE);
|
||||
if (_displayPlugin) {
|
||||
result = fromGlm(getActiveDisplayPlugin()->getRecommendedRenderSize());
|
||||
result = getActiveDisplayPlugin()->getRecommendedRenderSize();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -7098,10 +7098,6 @@ bool Application::hasFocus() const {
|
|||
return (QApplication::activeWindow() != nullptr);
|
||||
}
|
||||
|
||||
glm::vec2 Application::getViewportDimensions() const {
|
||||
return toGlm(getDeviceSize());
|
||||
}
|
||||
|
||||
void Application::setMaxOctreePacketsPerSecond(int maxOctreePPS) {
|
||||
if (maxOctreePPS != _maxOctreePPS) {
|
||||
_maxOctreePPS = maxOctreePPS;
|
||||
|
|
|
@ -158,7 +158,7 @@ public:
|
|||
|
||||
glm::uvec2 getUiSize() const;
|
||||
QRect getRecommendedHUDRect() const;
|
||||
QSize getDeviceSize() const;
|
||||
glm::vec2 getDeviceSize() const;
|
||||
bool hasFocus() const;
|
||||
|
||||
void showCursor(const Cursor::Icon& cursor);
|
||||
|
@ -228,8 +228,6 @@ public:
|
|||
|
||||
FileLogger* getLogger() const { return _logger; }
|
||||
|
||||
glm::vec2 getViewportDimensions() const;
|
||||
|
||||
NodeToJurisdictionMap& getEntityServerJurisdictions() { return _entityServerJurisdictions; }
|
||||
|
||||
float getRenderResolutionScale() const;
|
||||
|
|
|
@ -104,8 +104,7 @@ void Application::paintGL() {
|
|||
PerformanceTimer perfTimer("renderOverlay");
|
||||
// NOTE: There is no batch associated with this renderArgs
|
||||
// the ApplicationOverlay class assumes it's viewport is setup to be the device size
|
||||
QSize size = getDeviceSize();
|
||||
renderArgs._viewport = glm::ivec4(0, 0, size.width(), size.height());
|
||||
renderArgs._viewport = glm::ivec4(0, 0, getDeviceSize());
|
||||
_applicationOverlay.renderOverlay(&renderArgs);
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <shared/QtHelpers.h>
|
||||
#include <AvatarData.h>
|
||||
#include <PerfStat.h>
|
||||
#include <PrioritySortUtil.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
#include <Rig.h>
|
||||
#include <SettingHandle.h>
|
||||
|
@ -142,32 +143,39 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
|
||||
PerformanceTimer perfTimer("otherAvatars");
|
||||
|
||||
auto avatarMap = getHashCopy();
|
||||
QList<AvatarSharedPointer> avatarList = avatarMap.values();
|
||||
class SortableAvatar: public PrioritySortUtil::Sortable {
|
||||
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;
|
||||
qApp->copyDisplayViewFrustum(cameraView);
|
||||
PrioritySortUtil::PriorityQueue<SortableAvatar> sortedAvatars(cameraView,
|
||||
AvatarData::_avatarSortCoefficientSize,
|
||||
AvatarData::_avatarSortCoefficientCenter,
|
||||
AvatarData::_avatarSortCoefficientAge);
|
||||
|
||||
std::priority_queue<AvatarPriority> sortedAvatars;
|
||||
AvatarData::sortAvatars(avatarList, cameraView, sortedAvatars,
|
||||
|
||||
[](AvatarSharedPointer avatar)->uint64_t{
|
||||
return std::static_pointer_cast<Avatar>(avatar)->getLastRenderUpdateTime();
|
||||
},
|
||||
|
||||
[](AvatarSharedPointer avatar)->float{
|
||||
return std::static_pointer_cast<Avatar>(avatar)->getBoundingRadius();
|
||||
},
|
||||
|
||||
[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;
|
||||
});
|
||||
// sort
|
||||
auto avatarMap = getHashCopy();
|
||||
AvatarHash::iterator itr = avatarMap.begin();
|
||||
while (itr != avatarMap.end()) {
|
||||
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
|
||||
if (avatar != _myAvatar && avatar->isInitialized()) {
|
||||
sortedAvatars.push(SortableAvatar(avatar));
|
||||
}
|
||||
++itr;
|
||||
}
|
||||
|
||||
// process in sorted order
|
||||
uint64_t startTime = usecTimestampNow();
|
||||
const uint64_t UPDATE_BUDGET = 2000; // usec
|
||||
uint64_t updateExpiry = startTime + UPDATE_BUDGET;
|
||||
|
@ -176,8 +184,8 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
|
||||
render::Transaction transaction;
|
||||
while (!sortedAvatars.empty()) {
|
||||
const AvatarPriority& sortData = sortedAvatars.top();
|
||||
const auto& avatar = std::static_pointer_cast<Avatar>(sortData.avatar);
|
||||
const SortableAvatar& sortData = sortedAvatars.top();
|
||||
const auto& avatar = std::static_pointer_cast<Avatar>(sortData.getAvatar());
|
||||
|
||||
bool ignoring = DependencyManager::get<NodeList>()->isPersonalMutingNode(avatar->getID());
|
||||
if (ignoring) {
|
||||
|
@ -207,7 +215,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
uint64_t now = usecTimestampNow();
|
||||
if (now < updateExpiry) {
|
||||
// we're within budget
|
||||
bool inView = sortData.priority > OUT_OF_VIEW_THRESHOLD;
|
||||
bool inView = sortData.getPriority() > OUT_OF_VIEW_THRESHOLD;
|
||||
if (inView && avatar->hasNewJointData()) {
|
||||
numAvatarsUpdated++;
|
||||
}
|
||||
|
@ -221,7 +229,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
// --> some avatar velocity measurements may be a little off
|
||||
|
||||
// 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) {
|
||||
break;
|
||||
}
|
||||
|
@ -230,9 +238,9 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
}
|
||||
sortedAvatars.pop();
|
||||
while (inView && !sortedAvatars.empty()) {
|
||||
const AvatarPriority& newSortData = sortedAvatars.top();
|
||||
const auto& newAvatar = std::static_pointer_cast<Avatar>(newSortData.avatar);
|
||||
inView = newSortData.priority > OUT_OF_VIEW_THRESHOLD;
|
||||
const SortableAvatar& newSortData = sortedAvatars.top();
|
||||
const auto& newAvatar = std::static_pointer_cast<Avatar>(newSortData.getAvatar());
|
||||
inView = newSortData.getPriority() > OUT_OF_VIEW_THRESHOLD;
|
||||
if (inView && newAvatar->hasNewJointData()) {
|
||||
numAVatarsNotUpdated++;
|
||||
}
|
||||
|
|
|
@ -128,6 +128,11 @@ void QmlCommerce::reset() {
|
|||
wallet->reset();
|
||||
}
|
||||
|
||||
void QmlCommerce::resetLocalWalletOnly() {
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
wallet->reset();
|
||||
}
|
||||
|
||||
void QmlCommerce::account() {
|
||||
auto ledger = DependencyManager::get<Ledger>();
|
||||
ledger->account();
|
||||
|
|
|
@ -65,6 +65,7 @@ protected:
|
|||
Q_INVOKABLE void history();
|
||||
Q_INVOKABLE void generateKeyPair();
|
||||
Q_INVOKABLE void reset();
|
||||
Q_INVOKABLE void resetLocalWalletOnly();
|
||||
Q_INVOKABLE void account();
|
||||
|
||||
Q_INVOKABLE void certificateInfo(const QString& certificateId);
|
||||
|
|
|
@ -321,6 +321,16 @@ Wallet::Wallet() {
|
|||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
connect(accountManager.data(), &AccountManager::usernameChanged, this, [&]() {
|
||||
getWalletStatus();
|
||||
_publicKeys.clear();
|
||||
|
||||
if (_securityImage) {
|
||||
delete _securityImage;
|
||||
}
|
||||
_securityImage = nullptr;
|
||||
|
||||
// tell the provider we got nothing
|
||||
updateImageProvider();
|
||||
_passphrase->clear();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -176,6 +176,10 @@ bool WindowScriptingInterface::isPointOnDesktopWindow(QVariant 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
|
||||
/// might be in same thread as a script that sets the reticle to invisible
|
||||
void WindowScriptingInterface::ensureReticleVisible() const {
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#ifndef hifi_WindowScriptingInterface_h
|
||||
#define hifi_WindowScriptingInterface_h
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QString>
|
||||
#include <QtQuick/QQuickItem>
|
||||
|
@ -73,6 +75,7 @@ public slots:
|
|||
bool isPhysicsEnabled();
|
||||
bool setDisplayTexture(const QString& name);
|
||||
bool isPointOnDesktopWindow(QVariant point);
|
||||
glm::vec2 getDeviceSize() const;
|
||||
|
||||
int openMessageBox(QString title, QString text, int buttons, int defaultButton);
|
||||
void updateMessageBox(int id, QString title, QString text, int buttons, int defaultButton);
|
||||
|
|
|
@ -35,7 +35,8 @@ ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) :
|
|||
_modelTextures(QVariantMap()),
|
||||
_url(modelOverlay->_url),
|
||||
_updateModel(false),
|
||||
_loadPriority(modelOverlay->getLoadPriority())
|
||||
_scaleToFit(modelOverlay->_scaleToFit),
|
||||
_loadPriority(modelOverlay->_loadPriority)
|
||||
{
|
||||
_model->init();
|
||||
_model->setLoadingPriority(_loadPriority);
|
||||
|
|
|
@ -75,8 +75,8 @@ private:
|
|||
QVariantMap _modelTextures;
|
||||
|
||||
QUrl _url;
|
||||
bool _updateModel = { false };
|
||||
bool _scaleToFit = { false };
|
||||
bool _updateModel { false };
|
||||
bool _scaleToFit { false };
|
||||
float _loadPriority { 0.0f };
|
||||
|
||||
AnimationPointer _animation;
|
||||
|
@ -87,7 +87,7 @@ private:
|
|||
bool _animationRunning { false };
|
||||
bool _animationLoop { false };
|
||||
float _animationFirstFrame { 0.0f };
|
||||
float _animationLastFrame = { 0.0f };
|
||||
float _animationLastFrame { 0.0f };
|
||||
bool _animationHold { false };
|
||||
bool _animationAllowTranslation { false };
|
||||
uint64_t _lastAnimated { 0 };
|
||||
|
|
|
@ -18,8 +18,9 @@
|
|||
|
||||
QString const Shape3DOverlay::TYPE = "shape";
|
||||
|
||||
Shape3DOverlay::Shape3DOverlay(const Shape3DOverlay* Shape3DOverlay) :
|
||||
Volume3DOverlay(Shape3DOverlay)
|
||||
Shape3DOverlay::Shape3DOverlay(const Shape3DOverlay* shape3DOverlay) :
|
||||
Volume3DOverlay(shape3DOverlay),
|
||||
_shape(shape3DOverlay->_shape)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ public:
|
|||
virtual QString getType() const override { return TYPE; }
|
||||
|
||||
Shape3DOverlay() {}
|
||||
Shape3DOverlay(const Shape3DOverlay* Shape3DOverlay);
|
||||
Shape3DOverlay(const Shape3DOverlay* shape3DOverlay);
|
||||
|
||||
virtual void render(RenderArgs* args) override;
|
||||
virtual const render::ShapeKey getShapeKey() override;
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
#include <RegisteredMetaTypes.h>
|
||||
|
||||
Volume3DOverlay::Volume3DOverlay(const Volume3DOverlay* volume3DOverlay) :
|
||||
Base3DOverlay(volume3DOverlay)
|
||||
Base3DOverlay(volume3DOverlay),
|
||||
_localBoundingBox(volume3DOverlay->_localBoundingBox)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -2387,63 +2387,10 @@ void RayToAvatarIntersectionResultFromScriptValue(const QScriptValue& object, Ra
|
|||
|
||||
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::_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 obj = engine->newObject();
|
||||
for (auto entityID : value.keys()) {
|
||||
|
|
|
@ -629,14 +629,6 @@ public:
|
|||
|
||||
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
|
||||
// These coefficients exposed for fine tuning the sort priority for transfering new _jointData to the render pipeline.
|
||||
static float _avatarSortCoefficientSize;
|
||||
|
|
|
@ -1605,6 +1605,48 @@ void EntityItem::setParentID(const QUuid& value) {
|
|||
if (tree && !oldParentID.isNull()) {
|
||||
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);
|
||||
// children are forced to be kinematic
|
||||
// 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) {
|
||||
bool iAmHoldingThis = false;
|
||||
// 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;
|
||||
}
|
||||
if ((bool)(_dirtyFlags & Simulation::NO_BOOTSTRAPPING)) {
|
||||
userMask &= ~USER_COLLISION_GROUP_MY_AVATAR;
|
||||
}
|
||||
mask = Physics::getDefaultCollisionMask(group) & (int16_t)(userMask);
|
||||
}
|
||||
|
@ -1961,7 +1972,20 @@ bool EntityItem::addActionInternal(EntitySimulationPointer simulation, EntityDyn
|
|||
if (success) {
|
||||
_allActionsDataCache = newDataCache;
|
||||
_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 {
|
||||
qCDebug(entities) << "EntityItem::addActionInternal -- serializeActions failed";
|
||||
}
|
||||
|
@ -2002,6 +2026,29 @@ bool EntityItem::removeAction(EntitySimulationPointer simulation, const QUuid& a
|
|||
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) {
|
||||
_previouslyDeletedActions.insert(actionID, usecTimestampNow());
|
||||
if (_objectActions.contains(actionID)) {
|
||||
|
@ -2015,7 +2062,6 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi
|
|||
|
||||
action->setOwnerEntity(nullptr);
|
||||
action->setIsMine(false);
|
||||
_objectActions.remove(actionID);
|
||||
|
||||
if (simulation) {
|
||||
action->removeFromSimulation(simulation);
|
||||
|
@ -2024,7 +2070,23 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi
|
|||
bool success = true;
|
||||
serializeActions(success, _allActionsDataCache);
|
||||
_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);
|
||||
return success;
|
||||
}
|
||||
|
|
|
@ -470,6 +470,7 @@ protected:
|
|||
void setSimulated(bool simulated) { _simulated = simulated; }
|
||||
|
||||
const QByteArray getDynamicDataInternal() const;
|
||||
bool stillHasGrabActions() const;
|
||||
void setDynamicDataInternal(QByteArray dynamicData);
|
||||
|
||||
virtual void dimensionsChanged() override;
|
||||
|
|
|
@ -27,6 +27,7 @@ namespace Simulation {
|
|||
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_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_VELOCITIES = DIRTY_LINEAR_VELOCITY | DIRTY_ANGULAR_VELOCITY;
|
||||
|
|
|
@ -1006,14 +1006,11 @@ NetworkTexturePointer TextureCache::getResourceTexture(QUrl resourceTextureUrl)
|
|||
if (!_spectatorCameraNetworkTexture) {
|
||||
_spectatorCameraNetworkTexture.reset(new NetworkTexture(resourceTextureUrl));
|
||||
}
|
||||
if (_spectatorCameraFramebuffer) {
|
||||
texture = _spectatorCameraFramebuffer->getRenderBuffer(0);
|
||||
if (texture) {
|
||||
texture->setSource(SPECTATOR_CAMERA_FRAME_URL.toString().toStdString());
|
||||
_spectatorCameraNetworkTexture->setImage(texture, texture->getWidth(), texture->getHeight());
|
||||
return _spectatorCameraNetworkTexture;
|
||||
}
|
||||
if (!_spectatorCameraFramebuffer) {
|
||||
getSpectatorCameraFramebuffer(); // initialize frame buffer
|
||||
}
|
||||
updateSpectatorCameraNetworkTexture();
|
||||
return _spectatorCameraNetworkTexture;
|
||||
}
|
||||
// FIXME: Generalize this, DRY up this code
|
||||
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 (!_spectatorCameraFramebuffer || _spectatorCameraFramebuffer->getWidth() != width || _spectatorCameraFramebuffer->getHeight() != height) {
|
||||
_spectatorCameraFramebuffer.reset(gpu::Framebuffer::create("spectatorCamera", gpu::Element::COLOR_SRGBA_32, width, height));
|
||||
updateSpectatorCameraNetworkTexture();
|
||||
emit spectatorCameraFramebufferReset();
|
||||
}
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -171,6 +171,7 @@ public:
|
|||
const gpu::FramebufferPointer& getHmdPreviewFramebuffer(int width, int height);
|
||||
const gpu::FramebufferPointer& getSpectatorCameraFramebuffer();
|
||||
const gpu::FramebufferPointer& getSpectatorCameraFramebuffer(int width, int height);
|
||||
void updateSpectatorCameraNetworkTexture();
|
||||
|
||||
static const int DEFAULT_SPECTATOR_CAM_WIDTH { 2048 };
|
||||
static const int DEFAULT_SPECTATOR_CAM_HEIGHT { 1024 };
|
||||
|
|
|
@ -88,3 +88,56 @@ void UserActivityLoggerScriptingInterface::doLogAction(QString action, QJsonObje
|
|||
Q_ARG(QString, action),
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -33,6 +33,12 @@ public:
|
|||
Q_INVOKABLE void bubbleToggled(bool newValue);
|
||||
Q_INVOKABLE void bubbleActivated();
|
||||
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:
|
||||
void doLogAction(QString action, QJsonObject details = {});
|
||||
};
|
||||
|
|
|
@ -700,7 +700,7 @@ uint32_t EntityMotionState::getIncomingDirtyFlags() {
|
|||
void EntityMotionState::clearIncomingDirtyFlags() {
|
||||
assert(entityTreeIsLocked());
|
||||
if (_body && _entity) {
|
||||
_entity->clearDirtyFlags();
|
||||
_entity->clearDirtyFlags(DIRTY_PHYSICS_FLAGS);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,23 +32,23 @@ void Transaction::removeItem(ItemID id) {
|
|||
}
|
||||
|
||||
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) {
|
||||
_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) {
|
||||
_reAppliedTransitions.emplace_back(TransitionReApply{ id });
|
||||
_reAppliedTransitions.emplace_back(id);
|
||||
}
|
||||
|
||||
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) {
|
||||
_updatedItems.emplace_back(Update{ id, functor });
|
||||
_updatedItems.emplace_back(id, functor);
|
||||
}
|
||||
|
||||
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) {
|
||||
_highlightResets.emplace_back(HighlightReset{ selectionName, style });
|
||||
_highlightResets.emplace_back(selectionName, style );
|
||||
}
|
||||
|
||||
void Transaction::removeHighlightFromSelection(const std::string& selectionName) {
|
||||
_highlightRemoves.emplace_back(selectionName);
|
||||
}
|
||||
|
||||
void Transaction::querySelectionHighlight(const std::string& selectionName, SelectionHighlightQueryFunc func) {
|
||||
_highlightQueries.emplace_back(HighlightQuery{ selectionName, func });
|
||||
void Transaction::querySelectionHighlight(const std::string& selectionName, const SelectionHighlightQueryFunc& 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) {
|
||||
_resetItems.insert(_resetItems.end(), transaction._resetItems.begin(), transaction._resetItems.end());
|
||||
_removedItems.insert(_removedItems.end(), transaction._removedItems.begin(), transaction._removedItems.end());
|
||||
_updatedItems.insert(_updatedItems.end(), transaction._updatedItems.begin(), transaction._updatedItems.end());
|
||||
_resetSelections.insert(_resetSelections.end(), transaction._resetSelections.begin(), transaction._resetSelections.end());
|
||||
_addedTransitions.insert(_addedTransitions.end(), transaction._addedTransitions.begin(), transaction._addedTransitions.end());
|
||||
_queriedTransitions.insert(_queriedTransitions.end(), transaction._queriedTransitions.begin(), transaction._queriedTransitions.end());
|
||||
_reAppliedTransitions.insert(_reAppliedTransitions.end(), transaction._reAppliedTransitions.begin(), transaction._reAppliedTransitions.end());
|
||||
_highlightResets.insert(_highlightResets.end(), transaction._highlightResets.begin(), transaction._highlightResets.end());
|
||||
_highlightRemoves.insert(_highlightRemoves.end(), transaction._highlightRemoves.begin(), transaction._highlightRemoves.end());
|
||||
_highlightQueries.insert(_highlightQueries.end(), transaction._highlightQueries.begin(), transaction._highlightQueries.end());
|
||||
copyElements(_resetItems, transaction._resetItems);
|
||||
copyElements(_removedItems, transaction._removedItems);
|
||||
copyElements(_updatedItems, transaction._updatedItems);
|
||||
copyElements(_resetSelections, transaction._resetSelections);
|
||||
copyElements(_addedTransitions, transaction._addedTransitions);
|
||||
copyElements(_queriedTransitions, transaction._queriedTransitions);
|
||||
copyElements(_reAppliedTransitions, transaction._reAppliedTransitions);
|
||||
copyElements(_highlightResets, transaction._highlightResets);
|
||||
copyElements(_highlightRemoves, transaction._highlightRemoves);
|
||||
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
|
||||
void Scene::enqueueTransaction(const Transaction& transaction) {
|
||||
_transactionQueueMutex.lock();
|
||||
_transactionQueue.push(transaction);
|
||||
_transactionQueueMutex.unlock();
|
||||
std::unique_lock<std::mutex> lock(_transactionQueueMutex);
|
||||
_transactionQueue.emplace_back(transaction);
|
||||
}
|
||||
|
||||
void consolidateTransaction(TransactionQueue& queue, Transaction& singleBatch) {
|
||||
while (!queue.empty()) {
|
||||
const auto& transaction = queue.front();
|
||||
singleBatch.merge(transaction);
|
||||
queue.pop();
|
||||
};
|
||||
void Scene::enqueueTransaction(Transaction&& transaction) {
|
||||
std::unique_lock<std::mutex> lock(_transactionQueueMutex);
|
||||
_transactionQueue.emplace_back(std::move(transaction));
|
||||
}
|
||||
|
||||
uint32_t Scene::enqueueFrame() {
|
||||
PROFILE_RANGE(render, __FUNCTION__);
|
||||
Transaction consolidatedTransaction;
|
||||
TransactionQueue localTransactionQueue;
|
||||
{
|
||||
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);
|
||||
_transactionFrames.push_back(consolidatedTransaction);
|
||||
_transactionFrameNumber++;
|
||||
frameNumber = _transactionFrameNumber;
|
||||
}
|
||||
|
||||
return frameNumber;
|
||||
return ++_transactionFrameNumber;
|
||||
}
|
||||
|
||||
|
||||
void Scene::processTransactionQueue() {
|
||||
PROFILE_RANGE(render, __FUNCTION__);
|
||||
|
||||
TransactionFrames queuedFrames;
|
||||
static TransactionFrames queuedFrames;
|
||||
{
|
||||
// capture the queued frames and clear the queue
|
||||
std::unique_lock<std::mutex> lock(_transactionFramesMutex);
|
||||
queuedFrames = _transactionFrames;
|
||||
_transactionFrames.clear();
|
||||
queuedFrames.swap(_transactionFrames);
|
||||
}
|
||||
|
||||
// go through the queue of frames and process them
|
||||
for (auto& frame : queuedFrames) {
|
||||
processTransactionFrame(frame);
|
||||
}
|
||||
|
||||
queuedFrames.clear();
|
||||
}
|
||||
|
||||
void Scene::processTransactionFrame(const Transaction& transaction) {
|
||||
|
|
|
@ -65,9 +65,14 @@ public:
|
|||
|
||||
void resetSelectionHighlight(const std::string& selectionName, const HighlightStyle& style = HighlightStyle());
|
||||
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(Transaction&& transaction);
|
||||
void clear();
|
||||
|
||||
// Checkers if there is work to do when processing the transaction
|
||||
bool touchTransactions() const { return !_resetSelections.empty(); }
|
||||
|
@ -107,7 +112,7 @@ protected:
|
|||
HighlightRemoves _highlightRemoves;
|
||||
HighlightQueries _highlightQueries;
|
||||
};
|
||||
typedef std::queue<Transaction> TransactionQueue;
|
||||
typedef std::vector<Transaction> TransactionQueue;
|
||||
|
||||
|
||||
// Scene is a container for Items
|
||||
|
@ -133,6 +138,9 @@ public:
|
|||
// Enqueue transaction to the scene
|
||||
void enqueueTransaction(const Transaction& transaction);
|
||||
|
||||
// Enqueue transaction to the scene
|
||||
void enqueueTransaction(Transaction&& transaction);
|
||||
|
||||
// Enqueue end of frame transactions boundary
|
||||
uint32_t enqueueFrame();
|
||||
|
||||
|
@ -187,7 +195,7 @@ protected:
|
|||
|
||||
|
||||
std::mutex _transactionFramesMutex;
|
||||
using TransactionFrames = std::list<Transaction>;
|
||||
using TransactionFrames = std::vector<Transaction>;
|
||||
TransactionFrames _transactionFrames;
|
||||
uint32_t _transactionFrameNumber{ 0 };
|
||||
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
#define hifi_PrioritySortUtil_h
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <queue>
|
||||
|
||||
#include "NumericalConstants.h"
|
||||
#include "ViewFrustum.h"
|
||||
|
||||
/* 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:
|
||||
|
||||
PrioritySortUtil::Prioritizer prioritizer(viewFrustum);
|
||||
PrioritySortUtil::PriorityQueue<SortableWrapper> sortedThings(viewFrustum);
|
||||
std::priority_queue< PrioritySortUtil::Sortable<Thing> > sortedThings;
|
||||
for (thing in things) {
|
||||
float priority = prioritizer.computePriority(PrioritySortUtil::PrioritizableThing(thing));
|
||||
sortedThings.push(PrioritySortUtil::Sortable<Thing> entry(thing, priority));
|
||||
sortedThings.push(SortableWrapper(thing));
|
||||
}
|
||||
|
||||
(3) Loop over your priority queue and do timeboxed work:
|
||||
|
@ -65,6 +67,7 @@ namespace PrioritySortUtil {
|
|||
virtual uint64_t getTimestamp() const = 0;
|
||||
|
||||
void setPriority(float priority) { _priority = priority; }
|
||||
float getPriority() const { return _priority; }
|
||||
bool operator<(const Sortable& other) const { return _priority < other._priority; }
|
||||
private:
|
||||
float _priority { 0.0f };
|
||||
|
@ -109,11 +112,19 @@ namespace PrioritySortUtil {
|
|||
glm::vec3 position = thing.getPosition();
|
||||
glm::vec3 offset = position - _view.getPosition();
|
||||
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)
|
||||
+ _centerWeight * (glm::dot(offset, _view.getDirection()) / distance)
|
||||
+ _ageWeight * (float)(usecTimestampNow() - thing.getTimestamp());
|
||||
// we modulatate "age" drift rate by the cosineAngle term to make periphrial objects sort forward
|
||||
// at a reduced rate but we don't want the "age" term to go zero or negative so we clamp it
|
||||
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
|
||||
if (distance - radius > _view.getCenterRadius()) {
|
||||
|
|
|
@ -121,63 +121,58 @@ uint64_t uvec2ToUint64(const uvec2& v) {
|
|||
class AudioHandler : public QObject, QRunnable {
|
||||
Q_OBJECT
|
||||
public:
|
||||
AudioHandler(QObject* container, const QString& deviceName, int runDelayMs = 0, QObject* parent = nullptr) : QObject(parent) {
|
||||
_container = container;
|
||||
AudioHandler(QSharedPointer<OffscreenQmlSurface> surface, const QString& deviceName, QObject* parent = nullptr) : QObject(parent) {
|
||||
_newTargetDevice = deviceName;
|
||||
_runDelayMs = runDelayMs;
|
||||
_surface = surface;
|
||||
setAutoDelete(true);
|
||||
QThreadPool::globalInstance()->start(this);
|
||||
if (deviceName.size() > 0) {
|
||||
QThreadPool::globalInstance()->start(this);
|
||||
}
|
||||
}
|
||||
virtual ~AudioHandler() {
|
||||
qDebug() << "Audio Handler Destroyed";
|
||||
}
|
||||
void run() override {
|
||||
if (_newTargetDevice.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (_runDelayMs > 0) {
|
||||
QThread::msleep(_runDelayMs);
|
||||
}
|
||||
auto audioIO = DependencyManager::get<AudioClient>();
|
||||
QString deviceName = audioIO->getActiveAudioDevice(QAudio::AudioOutput).deviceName();
|
||||
for (auto player : _container->findChildren<QMediaPlayer*>()) {
|
||||
auto mediaState = player->state();
|
||||
QMediaService *svc = player->service();
|
||||
if (nullptr == svc) {
|
||||
return;
|
||||
}
|
||||
QAudioOutputSelectorControl *out = qobject_cast<QAudioOutputSelectorControl *>
|
||||
(svc->requestControl(QAudioOutputSelectorControl_iid));
|
||||
if (nullptr == out) {
|
||||
return;
|
||||
}
|
||||
QString deviceOuput;
|
||||
auto outputs = out->availableOutputs();
|
||||
for (int i = 0; i < outputs.size(); i++) {
|
||||
QString output = outputs[i];
|
||||
QString description = out->outputDescription(output);
|
||||
if (description == deviceName) {
|
||||
deviceOuput = output;
|
||||
break;
|
||||
if (!_surface.isNull() && _surface->getRootItem() && !_surface->getCleaned()) {
|
||||
for (auto player : _surface->getRootItem()->findChildren<QMediaPlayer*>()) {
|
||||
auto mediaState = player->state();
|
||||
QMediaService *svc = player->service();
|
||||
if (nullptr == svc) {
|
||||
return;
|
||||
}
|
||||
QAudioOutputSelectorControl *out = qobject_cast<QAudioOutputSelectorControl *>
|
||||
(svc->requestControl(QAudioOutputSelectorControl_iid));
|
||||
if (nullptr == out) {
|
||||
return;
|
||||
}
|
||||
QString deviceOuput;
|
||||
auto outputs = out->availableOutputs();
|
||||
for (int i = 0; i < outputs.size(); i++) {
|
||||
QString output = outputs[i];
|
||||
QString description = out->outputDescription(output);
|
||||
if (description == _newTargetDevice) {
|
||||
deviceOuput = output;
|
||||
break;
|
||||
}
|
||||
}
|
||||
out->setActiveOutput(deviceOuput);
|
||||
svc->releaseControl(out);
|
||||
// if multimedia was paused, it will start playing automatically after changing audio device
|
||||
// this will reset it back to a paused state
|
||||
if (mediaState == QMediaPlayer::State::PausedState) {
|
||||
player->pause();
|
||||
}
|
||||
else if (mediaState == QMediaPlayer::State::StoppedState) {
|
||||
player->stop();
|
||||
}
|
||||
}
|
||||
out->setActiveOutput(deviceOuput);
|
||||
svc->releaseControl(out);
|
||||
// if multimedia was paused, it will start playing automatically after changing audio device
|
||||
// this will reset it back to a paused state
|
||||
if (mediaState == QMediaPlayer::State::PausedState) {
|
||||
player->pause();
|
||||
} else if (mediaState == QMediaPlayer::State::StoppedState) {
|
||||
player->stop();
|
||||
}
|
||||
}
|
||||
qDebug() << "QML Audio changed to " << deviceName;
|
||||
qDebug() << "QML Audio changed to " << _newTargetDevice;
|
||||
}
|
||||
|
||||
private:
|
||||
QString _newTargetDevice;
|
||||
QObject* _container;
|
||||
int _runDelayMs;
|
||||
QSharedPointer<OffscreenQmlSurface> _surface;
|
||||
};
|
||||
|
||||
class OffscreenTextures {
|
||||
|
@ -502,6 +497,7 @@ QOpenGLContext* OffscreenQmlSurface::getSharedContext() {
|
|||
}
|
||||
|
||||
void OffscreenQmlSurface::cleanup() {
|
||||
_isCleaned = true;
|
||||
_canvas->makeCurrent();
|
||||
|
||||
_renderControl->invalidate();
|
||||
|
@ -600,6 +596,7 @@ OffscreenQmlSurface::OffscreenQmlSurface() {
|
|||
|
||||
OffscreenQmlSurface::~OffscreenQmlSurface() {
|
||||
QObject::disconnect(&_updateTimer);
|
||||
disconnectAudioOutputTimer();
|
||||
QObject::disconnect(qApp);
|
||||
|
||||
cleanup();
|
||||
|
@ -613,6 +610,15 @@ OffscreenQmlSurface::~OffscreenQmlSurface() {
|
|||
void OffscreenQmlSurface::onAboutToQuit() {
|
||||
_paused = true;
|
||||
QObject::disconnect(&_updateTimer);
|
||||
disconnectAudioOutputTimer();
|
||||
|
||||
}
|
||||
|
||||
void OffscreenQmlSurface::disconnectAudioOutputTimer() {
|
||||
if (_audioOutputUpdateTimer.isActive()) {
|
||||
_audioOutputUpdateTimer.stop();
|
||||
}
|
||||
QObject::disconnect(&_audioOutputUpdateTimer);
|
||||
}
|
||||
|
||||
void OffscreenQmlSurface::create() {
|
||||
|
@ -671,6 +677,14 @@ void OffscreenQmlSurface::create() {
|
|||
}
|
||||
});
|
||||
|
||||
// Setup the update of the QML media components with the current audio output device
|
||||
QObject::connect(&_audioOutputUpdateTimer, &QTimer::timeout, this, [this]() {
|
||||
new AudioHandler(sharedFromThis(), _currentAudioOutputDevice);
|
||||
});
|
||||
int waitForAudioQmlMs = 200;
|
||||
_audioOutputUpdateTimer.setInterval(waitForAudioQmlMs);
|
||||
_audioOutputUpdateTimer.setSingleShot(true);
|
||||
|
||||
// When Quick says there is a need to render, we will not render immediately. Instead,
|
||||
// a timer with a small interval is used to get better performance.
|
||||
QObject::connect(&_updateTimer, &QTimer::timeout, this, &OffscreenQmlSurface::updateQuick);
|
||||
|
@ -699,10 +713,11 @@ void OffscreenQmlSurface::forceQmlAudioOutputDeviceUpdate() {
|
|||
QMetaObject::invokeMethod(this, "forceQmlAudioOutputDeviceUpdate", Qt::QueuedConnection);
|
||||
} else {
|
||||
auto audioIO = DependencyManager::get<AudioClient>();
|
||||
QString deviceName = audioIO->getActiveAudioDevice(QAudio::AudioOutput).deviceName();
|
||||
int waitForAudioQmlMs = 500;
|
||||
// The audio device need to be change using oth
|
||||
new AudioHandler(_rootItem, deviceName, waitForAudioQmlMs);
|
||||
_currentAudioOutputDevice = audioIO->getActiveAudioDevice(QAudio::AudioOutput).deviceName();
|
||||
if (_audioOutputUpdateTimer.isActive()) {
|
||||
_audioOutputUpdateTimer.stop();
|
||||
}
|
||||
_audioOutputUpdateTimer.start();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ class QQuickItem;
|
|||
|
||||
using QmlContextCallback = std::function<void(QQmlContext*, QObject*)>;
|
||||
|
||||
class OffscreenQmlSurface : public QObject {
|
||||
class OffscreenQmlSurface : public QObject, public QEnableSharedFromThis<OffscreenQmlSurface> {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool focusText READ isFocusText NOTIFY focusTextChanged)
|
||||
public:
|
||||
|
@ -75,6 +75,7 @@ public:
|
|||
void pause();
|
||||
void resume();
|
||||
bool isPaused() const;
|
||||
bool getCleaned() { return _isCleaned; }
|
||||
|
||||
void setBaseUrl(const QUrl& baseUrl);
|
||||
QQuickItem* getRootItem();
|
||||
|
@ -116,6 +117,7 @@ public slots:
|
|||
void changeAudioOutputDevice(const QString& deviceName, bool isHtmlUpdate = false);
|
||||
void forceHtmlAudioOutputDeviceUpdate();
|
||||
void forceQmlAudioOutputDeviceUpdate();
|
||||
|
||||
signals:
|
||||
void audioOutputDeviceChanged(const QString& deviceName);
|
||||
|
||||
|
@ -147,6 +149,7 @@ private:
|
|||
void render();
|
||||
void cleanup();
|
||||
QJsonObject getGLContextData();
|
||||
void disconnectAudioOutputTimer();
|
||||
|
||||
private slots:
|
||||
void updateQuick();
|
||||
|
@ -170,6 +173,9 @@ private:
|
|||
uint64_t _lastRenderTime { 0 };
|
||||
uvec2 _size;
|
||||
|
||||
QTimer _audioOutputUpdateTimer;
|
||||
QString _currentAudioOutputDevice;
|
||||
|
||||
// Texture management
|
||||
TextureAndFence _latestTextureAndFence { 0, 0 };
|
||||
|
||||
|
@ -177,6 +183,7 @@ private:
|
|||
bool _polish { true };
|
||||
bool _paused { true };
|
||||
bool _focusText { false };
|
||||
bool _isCleaned{ false };
|
||||
uint8_t _maxFps { 60 };
|
||||
MouseTranslator _mouseTranslator { [](const QPointF& p) { return p.toPoint(); } };
|
||||
QWindow* _proxyWindow { nullptr };
|
||||
|
|
|
@ -62,7 +62,7 @@
|
|||
|
||||
this.pointingAtTablet = function(controllerData) {
|
||||
var rayPick = controllerData.rayPicks[this.hand];
|
||||
return (rayPick.objectID === HMD.tabletScreenID || rayPick.objectID === HMD.homeButtonID);
|
||||
return (HMD.tabletScreenID && HMD.homeButtonID && (rayPick.objectID === HMD.tabletScreenID || rayPick.objectID === HMD.homeButtonID));
|
||||
};
|
||||
|
||||
this.getOtherModule = function() {
|
||||
|
|
|
@ -81,7 +81,8 @@ Script.include("/~/system/libraries/cloneEntityUtils.js");
|
|||
};
|
||||
|
||||
this.otherHandIsParent = function(props) {
|
||||
return this.getOtherModule().thisHandIsParent(props);
|
||||
var otherModule = this.getOtherModule();
|
||||
return (otherModule.thisHandIsParent(props) && otherModule.grabbing);
|
||||
};
|
||||
|
||||
this.startNearParentingGrabEntity = function (controllerData, targetProps) {
|
||||
|
|
|
@ -63,7 +63,7 @@ var toolBar = (function() {
|
|||
y: -TOOLBAR_MARGIN_Y - toolHeight
|
||||
});
|
||||
browseDirectoryButton = toolBar.addTool({
|
||||
imageURL: toolIconUrl + "directory-01.svg",
|
||||
imageURL: toolIconUrl + "directory.svg",
|
||||
subImage: {
|
||||
x: 0,
|
||||
y: Tool.IMAGE_WIDTH,
|
||||
|
|
|
@ -1415,6 +1415,9 @@ input#reset-to-natural-dimensions {
|
|||
}
|
||||
|
||||
/* ----- Order of Menu items for Primitive ----- */
|
||||
/* Entity Menu classes are specified by selected entity
|
||||
within entityProperties.js
|
||||
*/
|
||||
#properties-list.ShapeMenu #general,
|
||||
#properties-list.BoxMenu #general,
|
||||
#properties-list.SphereMenu #general {
|
||||
|
@ -1469,6 +1472,34 @@ input#reset-to-natural-dimensions {
|
|||
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 ----- */
|
||||
#properties-list.LightMenu #general {
|
||||
|
@ -1500,8 +1531,8 @@ input#reset-to-natural-dimensions {
|
|||
display: none;
|
||||
}
|
||||
/* items to hide */
|
||||
#properties-list.LightMenu .shape-group.shape-section.property.dropdown,
|
||||
#properties-list.LightMenu color-section.color-control1 {
|
||||
#properties-list.LightMenu #shape-list,
|
||||
#properties-list.LightMenu #base-color-section {
|
||||
display: none
|
||||
}
|
||||
|
||||
|
@ -1536,8 +1567,8 @@ input#reset-to-natural-dimensions {
|
|||
display: none;
|
||||
}
|
||||
/* items to hide */
|
||||
#properties-list.ModelMenu .shape-group.shape-section.property.dropdown,
|
||||
#properties-list.ModelMenu .color-section.color-control1 {
|
||||
#properties-list.ModelMenu #shape-list,
|
||||
#properties-list.ModelMenu #base-color-section {
|
||||
display: none
|
||||
}
|
||||
|
||||
|
@ -1572,8 +1603,8 @@ input#reset-to-natural-dimensions {
|
|||
display: none;
|
||||
}
|
||||
/* items to hide */
|
||||
#properties-list.ZoneMenu .shape-group.shape-section.property.dropdown,
|
||||
#properties-list.ZoneMenu .color-section.color-control1 {
|
||||
#properties-list.ZoneMenu #shape-list,
|
||||
#properties-list.ZoneMenu #base-color-section {
|
||||
display: none
|
||||
}
|
||||
|
||||
|
@ -1608,8 +1639,8 @@ input#reset-to-natural-dimensions {
|
|||
display: none;
|
||||
}
|
||||
/* items to hide */
|
||||
#properties-list.WebMenu .shape-group.shape-section.property.dropdown,
|
||||
#properties-list.WebMenu .color-section.color-control1 {
|
||||
#properties-list.WebMenu #shape-list,
|
||||
#properties-list.WebMenu #base-color-section {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
@ -1645,8 +1676,8 @@ input#reset-to-natural-dimensions {
|
|||
display: none;
|
||||
}
|
||||
/* items to hide */
|
||||
#properties-list.TextMenu .shape-group.shape-section.property.dropdown,
|
||||
#properties-list.TextMenu .color-section.color-control1 {
|
||||
#properties-list.TextMenu #shape-list,
|
||||
#properties-list.TextMenu #base-color-section {
|
||||
display: none
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
|
||||
|
||||
<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>
|
||||
<select name="SelectShape" id="property-shape">
|
||||
<option value="Cube">Box</option>
|
||||
|
@ -66,12 +66,19 @@
|
|||
<label for="property-name">Name</label>
|
||||
<input type="text" id="property-name">
|
||||
</div>
|
||||
<div class="physical-group color-section property color-control1">
|
||||
<div class="color-picker" id="property-color-control1"></div>
|
||||
<div class="physical-group color-section property rgb fstuple" id="base-color-section">
|
||||
<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 id="collision-info">
|
||||
|
||||
<fieldset id="collision-info" class="major">
|
||||
<legend class="section-header"> Collision<span>M</span> </legend>
|
||||
<fieldset class="minor">
|
||||
<div class="behavior-group property checkbox">
|
||||
<input type="checkbox" id="property-collisionless">
|
||||
|
@ -216,17 +223,6 @@
|
|||
</div>
|
||||
</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>
|
||||
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -15,6 +15,9 @@ function setUpKeyboardControl() {
|
|||
var KEYBOARD_HEIGHT = 200;
|
||||
|
||||
function raiseKeyboard() {
|
||||
window.isKeyboardRaised = true;
|
||||
window.isNumericKeyboard = this.type === "number";
|
||||
|
||||
if (lowerTimer !== null) {
|
||||
clearTimeout(lowerTimer);
|
||||
lowerTimer = null;
|
||||
|
@ -35,6 +38,9 @@ function setUpKeyboardControl() {
|
|||
}
|
||||
|
||||
function doLowerKeyboard() {
|
||||
window.isKeyboardRaised = false;
|
||||
window.isNumericKeyboard = false;
|
||||
|
||||
EventBridge.emitWebEvent("_LOWER_KEYBOARD");
|
||||
lowerTimer = null;
|
||||
isRaised = false;
|
||||
|
|
|
@ -110,8 +110,10 @@
|
|||
var filterText; // Used for updating Purchases QML
|
||||
function onScreenChanged(type, url) {
|
||||
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);
|
||||
wireEventBridge(onCommerceScreen);
|
||||
onWalletScreen = url.indexOf(MARKETPLACE_WALLET_QML_PATH) !== -1;
|
||||
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) {
|
||||
tablet.sendToQml({
|
||||
|
@ -122,7 +124,7 @@
|
|||
}
|
||||
|
||||
// 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) {
|
||||
ContextOverlay.isInMarketplaceInspectionMode = true;
|
||||
} else {
|
||||
|
@ -322,6 +324,11 @@
|
|||
} else if (parsedJsonMessage.type === "LOGIN") {
|
||||
openLoginWindow();
|
||||
} else if (parsedJsonMessage.type === "WALLET_SETUP") {
|
||||
wireEventBridge(true);
|
||||
tablet.sendToQml({
|
||||
method: 'updateWalletReferrer',
|
||||
referrer: "marketplace cta"
|
||||
});
|
||||
openWallet();
|
||||
} else if (parsedJsonMessage.type === "MY_ITEMS") {
|
||||
referrerURL = MARKETPLACE_URL_INITIAL;
|
||||
|
@ -399,6 +406,7 @@
|
|||
referrer: "purchases"
|
||||
});
|
||||
openWallet();
|
||||
break;
|
||||
case 'checkout_walletNotSetUp':
|
||||
wireEventBridge(true);
|
||||
tablet.sendToQml({
|
||||
|
|
Loading…
Reference in a new issue