Merge branch 'master' of github.com:highfidelity/hifi into controllerDispatcher-interval

This commit is contained in:
Dante Ruiz 2017-12-07 16:19:03 -08:00
commit 0286f4646a
82 changed files with 1257 additions and 1154 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -65,21 +65,23 @@ var EventBridge;
// we need to listen to events that might precede the addition of this elements. // 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?) // 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() { setTimeout(function() {
EventBridge.forceHtmlAudioOutputDeviceUpdate(); EventBridge.forceHtmlAudioOutputDeviceUpdate();
}, 1200); }, 1200);
}; }, false);
document.onclick = function(){
document.addEventListener("click",function(){
setTimeout(function() { setTimeout(function() {
EventBridge.forceHtmlAudioOutputDeviceUpdate(); EventBridge.forceHtmlAudioOutputDeviceUpdate();
}, 1200); }, 1200);
}; }, false);
document.onchange = function(){
document.addEventListener("change",function(){
setTimeout(function() { setTimeout(function() {
EventBridge.forceHtmlAudioOutputDeviceUpdate(); EventBridge.forceHtmlAudioOutputDeviceUpdate();
}, 1200); }, 1200);
}; }, false);
tempEventBridge._callbacks.forEach(function (callback) { tempEventBridge._callbacks.forEach(function (callback) {
EventBridge.scriptEventReceived.connect(callback); EventBridge.scriptEventReceived.connect(callback);

View file

@ -1,127 +0,0 @@
<!-- Copyright 2016 High Fidelity, Inc. -->
<html>
<head>
<meta charset="utf-8"/>
<input type="hidden" id="version" value="1"/>
<title>Welcome to Interface</title>
<style>
body {
background: black;
width: 100%;
overflow-x: hidden;
margin: 0;
padding: 0;
}
#kbm_button {
position: absolute;
left: 70;
top: 118;
width: 297;
height: 80;
}
#hand_controllers_button {
position: absolute;
left: 367;
top: 118;
width: 267;
height: 80;
}
#game_controller_button {
position: absolute;
left: 634;
top: 118;
width: 297;
height: 80;
}
#image_area {
width: 1024;
height: 720;
margin: auto;
position: absolute;
top: 0; left: 0; bottom: 0; right: 0;
}
</style>
<script>
var handControllerImageURL = null;
function showKbm() {
document.getElementById("main_image").setAttribute("src", "img/controls-help-keyboard.png");
}
function showHandControllers() {
document.getElementById("main_image").setAttribute("src", handControllerImageURL);
}
function showGamepad() {
document.getElementById("main_image").setAttribute("src", "img/controls-help-gamepad.png");
}
// This is not meant to be a complete or hardened query string parser - it only
// needs to handle the values we send in and have control over.
//
// queryString is a string of the form "key1=value1&key2=value2&key3&key4=value4"
function parseQueryString(queryString) {
var params = {};
var paramsParts = queryString.split("&");
for (var i = 0; i < paramsParts.length; ++i) {
var paramKeyValue = paramsParts[i].split("=");
if (paramKeyValue.length == 1) {
params[paramKeyValue[0]] = undefined;
} else if (paramKeyValue.length == 2) {
params[paramKeyValue[0]] = paramKeyValue[1];
} else {
console.error("Error parsing param keyvalue: ", paramParts);
}
}
return params;
}
function load() {
var parts = window.location.href.split("?");
var params = {};
if (parts.length > 0) {
params = parseQueryString(parts[1]);
}
switch (params.handControllerName) {
case "oculus":
handControllerImageURL = "img/controls-help-oculus.png";
break;
case "vive":
default:
handControllerImageURL = "img/controls-help-vive.png";
}
switch (params.defaultTab) {
case "gamepad":
showGamepad();
break;
case "handControllers":
showHandControllers();
break;
case "kbm":
default:
showKbm();
}
}
</script>
</head>
<body onload="load()">
<div id="image_area">
<img id="main_image" src="img/controls-help-keyboard.png" width="1024px" height="720px"></img>
<a href="#" id="kbm_button" onmousedown="showKbm()"></a>
<a href="#" id="hand_controllers_button" onmousedown="showHandControllers()"></a>
<a href="#" id="game_controller_button" onmousedown="showGamepad()"></a>
</div>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 604 KiB

After

Width:  |  Height:  |  Size: 298 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 503 KiB

After

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 585 KiB

After

Width:  |  Height:  |  Size: 289 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 547 KiB

After

Width:  |  Height:  |  Size: 254 KiB

View file

@ -12,9 +12,9 @@
var MAX_WARNINGS = 3; var MAX_WARNINGS = 3;
var numWarnings = 0; var numWarnings = 0;
var isWindowFocused = true; var isWindowFocused = true;
var isKeyboardRaised = false; window.isKeyboardRaised = false;
var isNumericKeyboard = false; window.isNumericKeyboard = false;
var isPasswordField = false; window.isPasswordField = false;
function shouldSetPasswordField() { function shouldSetPasswordField() {
var nodeType = document.activeElement.type; var nodeType = document.activeElement.type;
@ -62,7 +62,7 @@
var passwordField = shouldSetPasswordField(); var passwordField = shouldSetPasswordField();
if (isWindowFocused && if (isWindowFocused &&
(keyboardRaised !== isKeyboardRaised || numericKeyboard !== isNumericKeyboard || passwordField !== isPasswordField)) { (keyboardRaised !== window.isKeyboardRaised || numericKeyboard !== window.isNumericKeyboard || passwordField !== window.isPasswordField)) {
if (typeof EventBridge !== "undefined" && EventBridge !== null) { if (typeof EventBridge !== "undefined" && EventBridge !== null) {
EventBridge.emitWebEvent( EventBridge.emitWebEvent(
@ -75,20 +75,20 @@
} }
} }
if (!isKeyboardRaised) { if (!window.isKeyboardRaised) {
scheduleBringToView(250); // Allow time for keyboard to be raised in QML. scheduleBringToView(250); // Allow time for keyboard to be raised in QML.
// 2DO: should it be rather done from 'client area height changed' event? // 2DO: should it be rather done from 'client area height changed' event?
} }
isKeyboardRaised = keyboardRaised; window.isKeyboardRaised = keyboardRaised;
isNumericKeyboard = numericKeyboard; window.isNumericKeyboard = numericKeyboard;
isPasswordField = passwordField; window.isPasswordField = passwordField;
} }
}, POLL_FREQUENCY); }, POLL_FREQUENCY);
window.addEventListener("click", function () { window.addEventListener("click", function () {
var keyboardRaised = shouldRaiseKeyboard(); var keyboardRaised = shouldRaiseKeyboard();
if(keyboardRaised && isKeyboardRaised) { if (keyboardRaised && window.isKeyboardRaised) {
scheduleBringToView(150); scheduleBringToView(150);
} }
}); });
@ -99,7 +99,7 @@
window.addEventListener("blur", function () { window.addEventListener("blur", function () {
isWindowFocused = false; isWindowFocused = false;
isKeyboardRaised = false; window.isKeyboardRaised = false;
isNumericKeyboard = false; window.isNumericKeyboard = false;
}); });
})(); })();

View file

@ -11,6 +11,7 @@
import QtQuick 2.5 import QtQuick 2.5
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 import QtQuick.Controls.Styles 1.4
import QtQuick.Controls 2.2 as QQC2
import "../styles-uit" import "../styles-uit"
@ -24,6 +25,45 @@ TableView {
model: ListModel { } model: ListModel { }
Component.onCompleted: {
if (flickableItem !== null && flickableItem !== undefined) {
tableView.flickableItem.QQC2.ScrollBar.vertical = scrollbar
}
}
QQC2.ScrollBar {
id: scrollbar
parent: tableView.flickableItem
policy: QQC2.ScrollBar.AsNeeded
orientation: Qt.Vertical
visible: size < 1.0
topPadding: tableView.headerVisible ? hifi.dimensions.tableHeaderHeight + 1 : 1
anchors.top: tableView.top
anchors.left: tableView.right
anchors.bottom: tableView.bottom
background: Item {
implicitWidth: hifi.dimensions.scrollbarBackgroundWidth
Rectangle {
anchors {
fill: parent;
topMargin: tableView.headerVisible ? hifi.dimensions.tableHeaderHeight : 0
}
color: isLightColorScheme ? hifi.colors.tableScrollBackgroundLight
: hifi.colors.tableScrollBackgroundDark
}
}
contentItem: Item {
implicitWidth: hifi.dimensions.scrollbarHandleWidth
Rectangle {
anchors.fill: parent
radius: (width - 4)/2
color: isLightColorScheme ? hifi.colors.tableScrollHandleLight : hifi.colors.tableScrollHandleDark
}
}
}
headerVisible: false headerVisible: false
headerDelegate: Rectangle { headerDelegate: Rectangle {
height: hifi.dimensions.tableHeaderHeight height: hifi.dimensions.tableHeaderHeight
@ -98,74 +138,13 @@ TableView {
backgroundVisible: true backgroundVisible: true
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
verticalScrollBarPolicy: Qt.ScrollBarAsNeeded verticalScrollBarPolicy: Qt.ScrollBarAlwaysOff
style: TableViewStyle { style: TableViewStyle {
// Needed in order for rows to keep displaying rows after end of table entries. // Needed in order for rows to keep displaying rows after end of table entries.
backgroundColor: tableView.isLightColorScheme ? hifi.colors.tableBackgroundLight : hifi.colors.tableBackgroundDark backgroundColor: tableView.isLightColorScheme ? hifi.colors.tableBackgroundLight : hifi.colors.tableBackgroundDark
alternateBackgroundColor: tableView.isLightColorScheme ? hifi.colors.tableRowLightOdd : hifi.colors.tableRowDarkOdd alternateBackgroundColor: tableView.isLightColorScheme ? hifi.colors.tableRowLightOdd : hifi.colors.tableRowDarkOdd
padding.top: headerVisible ? hifi.dimensions.tableHeaderHeight: 0 padding.top: headerVisible ? hifi.dimensions.tableHeaderHeight: 0
handle: Item {
id: scrollbarHandle
implicitWidth: hifi.dimensions.scrollbarHandleWidth
Rectangle {
anchors {
fill: parent
topMargin: 3
bottomMargin: 3 // ""
leftMargin: 1 // Move it right
rightMargin: -1 // ""
}
radius: hifi.dimensions.scrollbarHandleWidth/2
color: isLightColorScheme ? hifi.colors.tableScrollHandleLight : hifi.colors.tableScrollHandleDark
}
}
scrollBarBackground: Item {
implicitWidth: hifi.dimensions.scrollbarBackgroundWidth
Rectangle {
anchors {
fill: parent
margins: -1 // Expand
topMargin: -1
}
color: isLightColorScheme ? hifi.colors.tableScrollBackgroundLight : hifi.colors.tableScrollBackgroundDark
// Extend header color above scrollbar background
Rectangle {
anchors {
top: parent.top
topMargin: -hifi.dimensions.tableHeaderHeight
left: parent.left
right: parent.right
}
height: hifi.dimensions.tableHeaderHeight
color: tableView.isLightColorScheme ? hifi.colors.tableBackgroundLight : hifi.colors.tableBackgroundDark
visible: headerVisible
}
Rectangle {
// Extend header bottom border
anchors {
top: parent.top
left: parent.left
right: parent.right
}
height: 1
color: isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight
visible: headerVisible
}
}
}
incrementControl: Item {
visible: false
}
decrementControl: Item {
visible: false
}
} }
rowDelegate: Rectangle { rowDelegate: Rectangle {

View file

@ -9,9 +9,11 @@
// //
import QtQml.Models 2.2 import QtQml.Models 2.2
import QtQuick 2.5 import QtQuick 2.7
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 import QtQuick.Controls.Styles 1.4
import QtQuick.Controls 2.2 as QQC2
import "../styles-uit" import "../styles-uit"
@ -35,6 +37,45 @@ TreeView {
headerVisible: false headerVisible: false
Component.onCompleted: {
if (flickableItem !== null && flickableItem !== undefined) {
treeView.flickableItem.QQC2.ScrollBar.vertical = scrollbar
}
}
QQC2.ScrollBar {
id: scrollbar
parent: treeView.flickableItem
policy: QQC2.ScrollBar.AsNeeded
orientation: Qt.Vertical
visible: size < 1.0
topPadding: treeView.headerVisible ? hifi.dimensions.tableHeaderHeight + 1 : 1
anchors.top: treeView.top
anchors.left: treeView.right
anchors.bottom: treeView.bottom
background: Item {
implicitWidth: hifi.dimensions.scrollbarBackgroundWidth
Rectangle {
anchors {
fill: parent;
topMargin: treeView.headerVisible ? hifi.dimensions.tableHeaderHeight: 0
}
color: isLightColorScheme ? hifi.colors.tableScrollBackgroundLight
: hifi.colors.tableScrollBackgroundDark
}
}
contentItem: Item {
implicitWidth: hifi.dimensions.scrollbarHandleWidth
Rectangle {
anchors.fill: parent
radius: (width - 4)/2
color: isLightColorScheme ? hifi.colors.tableScrollHandleLight : hifi.colors.tableScrollHandleDark
}
}
}
// Use rectangle to draw border with rounded corners. // Use rectangle to draw border with rounded corners.
frameVisible: false frameVisible: false
Rectangle { Rectangle {
@ -50,7 +91,7 @@ TreeView {
backgroundVisible: true backgroundVisible: true
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
verticalScrollBarPolicy: Qt.ScrollBarAsNeeded verticalScrollBarPolicy: Qt.ScrollBarAlwaysOff
style: TreeViewStyle { style: TreeViewStyle {
// Needed in order for rows to keep displaying rows after end of table entries. // Needed in order for rows to keep displaying rows after end of table entries.
@ -126,66 +167,6 @@ TreeView {
leftMargin: hifi.dimensions.tablePadding / 2 leftMargin: hifi.dimensions.tablePadding / 2
} }
} }
handle: Item {
id: scrollbarHandle
implicitWidth: hifi.dimensions.scrollbarHandleWidth
Rectangle {
anchors {
fill: parent
topMargin: treeView.headerVisible ? hifi.dimensions.tableHeaderHeight + 3 : 3
bottomMargin: 3 // ""
leftMargin: 1 // Move it right
rightMargin: -1 // ""
}
radius: hifi.dimensions.scrollbarHandleWidth / 2
color: treeView.isLightColorScheme ? hifi.colors.tableScrollHandleLight : hifi.colors.tableScrollHandleDark
}
}
scrollBarBackground: Item {
implicitWidth: hifi.dimensions.scrollbarBackgroundWidth
Rectangle {
anchors {
fill: parent
topMargin: treeView.headerVisible ? hifi.dimensions.tableHeaderHeight - 1 : -1
margins: -1 // Expand
}
color: treeView.isLightColorScheme ? hifi.colors.tableScrollBackgroundLight : hifi.colors.tableScrollBackgroundDark
// Extend header color above scrollbar background
Rectangle {
anchors {
top: parent.top
topMargin: -hifi.dimensions.tableHeaderHeight
left: parent.left
right: parent.right
}
height: hifi.dimensions.tableHeaderHeight
color: treeView.isLightColorScheme ? hifi.colors.tableBackgroundLight : hifi.colors.tableBackgroundDark
visible: treeView.headerVisible
}
Rectangle {
// Extend header bottom border
anchors {
top: parent.top
left: parent.left
right: parent.right
}
height: 1
color: treeView.isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight
visible: treeView.headerVisible
}
}
}
incrementControl: Item {
visible: false
}
decrementControl: Item {
visible: false
}
} }
rowDelegate: Rectangle { rowDelegate: Rectangle {
@ -193,8 +174,8 @@ TreeView {
color: styleData.selected color: styleData.selected
? hifi.colors.primaryHighlight ? hifi.colors.primaryHighlight
: treeView.isLightColorScheme : treeView.isLightColorScheme
? (styleData.alternate ? hifi.colors.tableRowLightEven : hifi.colors.tableRowLightOdd) ? (styleData.alternate ? hifi.colors.tableRowLightEven : hifi.colors.tableRowLightOdd)
: (styleData.alternate ? hifi.colors.tableRowDarkEven : hifi.colors.tableRowDarkOdd) : (styleData.alternate ? hifi.colors.tableRowDarkEven : hifi.colors.tableRowDarkOdd)
} }
itemDelegate: FiraSansSemiBold { itemDelegate: FiraSansSemiBold {
@ -209,8 +190,8 @@ TreeView {
text: styleData.value text: styleData.value
size: hifi.fontSizes.tableText size: hifi.fontSizes.tableText
color: colorScheme == hifi.colorSchemes.light color: colorScheme == hifi.colorSchemes.light
? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight) ? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight)
: (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText) : (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText)
elide: Text.ElideRight elide: Text.ElideRight
} }

View file

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

View file

@ -430,7 +430,7 @@ Rectangle {
var a = new Date(timestamp); var a = new Date(timestamp);
var year = a.getFullYear(); var year = a.getFullYear();
var month = addLeadingZero(a.getMonth()); var month = addLeadingZero(a.getMonth() + 1);
var day = addLeadingZero(a.getDate()); var day = addLeadingZero(a.getDate());
var hour = a.getHours(); var hour = a.getHours();
var drawnHour = hour; var drawnHour = hour;

View file

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

View file

@ -343,6 +343,9 @@ Rectangle {
ListModel { ListModel {
id: previousPurchasesModel; id: previousPurchasesModel;
} }
HifiCommerceCommon.SortableListModel {
id: tempPurchasesModel;
}
HifiCommerceCommon.SortableListModel { HifiCommerceCommon.SortableListModel {
id: filteredPurchasesModel; id: filteredPurchasesModel;
} }
@ -635,20 +638,40 @@ Rectangle {
} }
function buildFilteredPurchasesModel() { function buildFilteredPurchasesModel() {
filteredPurchasesModel.clear(); var sameItemCount = 0;
tempPurchasesModel.clear();
for (var i = 0; i < purchasesModel.count; i++) { for (var i = 0; i < purchasesModel.count; i++) {
if (purchasesModel.get(i).title.toLowerCase().indexOf(filterBar.text.toLowerCase()) !== -1) { if (purchasesModel.get(i).title.toLowerCase().indexOf(filterBar.text.toLowerCase()) !== -1) {
if (purchasesModel.get(i).status !== "confirmed" && !root.isShowingMyItems) { if (purchasesModel.get(i).status !== "confirmed" && !root.isShowingMyItems) {
filteredPurchasesModel.insert(0, purchasesModel.get(i)); tempPurchasesModel.insert(0, purchasesModel.get(i));
} else if ((root.isShowingMyItems && purchasesModel.get(i).edition_number === "0") || } else if ((root.isShowingMyItems && purchasesModel.get(i).edition_number === "0") ||
(!root.isShowingMyItems && purchasesModel.get(i).edition_number !== "0")) { (!root.isShowingMyItems && purchasesModel.get(i).edition_number !== "0")) {
filteredPurchasesModel.append(purchasesModel.get(i)); tempPurchasesModel.append(purchasesModel.get(i));
} }
} }
} }
populateDisplayedItemCounts(); for (var i = 0; i < tempPurchasesModel.count; i++) {
sortByDate(); if (!filteredPurchasesModel.get(i)) {
sameItemCount = -1;
break;
} else if (tempPurchasesModel.get(i).itemId === filteredPurchasesModel.get(i).itemId &&
tempPurchasesModel.get(i).edition_number === filteredPurchasesModel.get(i).edition_number &&
tempPurchasesModel.get(i).status === filteredPurchasesModel.get(i).status) {
sameItemCount++;
}
}
if (sameItemCount !== tempPurchasesModel.count) {
filteredPurchasesModel.clear();
for (var i = 0; i < tempPurchasesModel.count; i++) {
filteredPurchasesModel.append(tempPurchasesModel.get(i));
}
populateDisplayedItemCounts();
sortByDate();
}
} }
function checkIfAnyItemStatusChanged() { function checkIfAnyItemStatusChanged() {

View file

@ -206,16 +206,6 @@ Item {
root.isPasswordField = (focus && passphraseField.echoMode === TextInput.Password); 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: { onAccepted: {
submitPassphraseInputButton.enabled = false; submitPassphraseInputButton.enabled = false;
commerce.setPassphrase(passphraseField.text); commerce.setPassphrase(passphraseField.text);
@ -362,25 +352,6 @@ Item {
right: parent.right; 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 { HifiControlsUit.Keyboard {
id: keyboard; id: keyboard;
raised: HMD.mounted && root.keyboardRaised; raised: HMD.mounted && root.keyboardRaised;

View file

@ -82,17 +82,6 @@ Item {
if (focus) { if (focus) {
var hidePassword = (currentPassphraseField.echoMode === TextInput.Password); var hidePassword = (currentPassphraseField.echoMode === TextInput.Password);
sendSignalToWallet({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword}); 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; activeFocusOnPress: true;
activeFocusOnTab: true; activeFocusOnTab: true;
MouseArea {
anchors.fill: parent;
onPressed: {
var hidePassword = (passphraseField.echoMode === TextInput.Password);
sendSignalToWallet({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword});
mouse.accepted = false;
}
}
onFocusChanged: { onFocusChanged: {
if (focus) { if (focus) {
var hidePassword = (passphraseField.echoMode === TextInput.Password); var hidePassword = (passphraseField.echoMode === TextInput.Password);
sendMessageToLightbox({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword}); sendMessageToLightbox({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword});
} else if (!passphraseFieldAgain.focus) {
sendMessageToLightbox({method: 'walletSetup_lowerKeyboard', isPasswordField: false});
} }
} }
@ -151,21 +129,10 @@ Item {
activeFocusOnPress: true; activeFocusOnPress: true;
activeFocusOnTab: true; activeFocusOnTab: true;
MouseArea {
anchors.fill: parent;
onPressed: {
var hidePassword = (passphraseFieldAgain.echoMode === TextInput.Password);
sendSignalToWallet({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword});
mouse.accepted = false;
}
}
onFocusChanged: { onFocusChanged: {
if (focus) { if (focus) {
var hidePassword = (passphraseFieldAgain.echoMode === TextInput.Password); var hidePassword = (passphraseFieldAgain.echoMode === TextInput.Password);
sendMessageToLightbox({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword}); sendMessageToLightbox({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword});
} else if (!passphraseField.focus) {
sendMessageToLightbox({method: 'walletSetup_lowerKeyboard', isPasswordField: false});
} }
} }

View file

@ -47,6 +47,12 @@ Rectangle {
} else if (walletStatus === 1) { } else if (walletStatus === 1) {
if (root.activeView !== "walletSetup") { if (root.activeView !== "walletSetup") {
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) { } else if (walletStatus === 2) {
if (root.activeView !== "passphraseModal") { if (root.activeView !== "passphraseModal") {
@ -172,7 +178,7 @@ Rectangle {
Connections { Connections {
onSendSignalToWallet: { onSendSignalToWallet: {
if (msg.method === 'walletSetup_finished') { if (msg.method === 'walletSetup_finished') {
if (msg.referrer === '') { if (msg.referrer === '' || msg.referrer === 'marketplace cta') {
root.activeView = "initialize"; root.activeView = "initialize";
commerce.getWalletStatus(); commerce.getWalletStatus();
} else if (msg.referrer === 'purchases') { } else if (msg.referrer === 'purchases') {
@ -666,25 +672,6 @@ Rectangle {
right: parent.right; 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 { HifiControlsUit.Keyboard {
id: keyboard; id: keyboard;
raised: HMD.mounted && root.keyboardRaised; raised: HMD.mounted && root.keyboardRaised;
@ -719,12 +706,28 @@ Rectangle {
case 'updateWalletReferrer': case 'updateWalletReferrer':
walletSetup.referrer = message.referrer; walletSetup.referrer = message.referrer;
break; break;
case 'inspectionCertificate_resetCert':
// NOP
break;
default: default:
console.log('Unrecognized message from wallet.js:', JSON.stringify(message)); console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
} }
} }
signal sendToScript(var message); signal sendToScript(var message);
// generateUUID() taken from:
// https://stackoverflow.com/a/8809472
function generateUUID() { // Public Domain/MIT
var d = new Date().getTime();
if (typeof performance !== 'undefined' && typeof performance.now === 'function'){
d += performance.now(); //use high-precision timer if available
}
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = (d + Math.random() * 16) % 16 | 0;
d = Math.floor(d / 16);
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
});
}
// //
// FUNCTION DEFINITIONS END // FUNCTION DEFINITIONS END
// //

View file

@ -38,10 +38,28 @@ Item {
onHistoryResult : { onHistoryResult : {
historyReceived = true; historyReceived = true;
if (result.status === 'success') { if (result.status === 'success') {
transactionHistoryModel.clear(); var sameItemCount = 0;
transactionHistoryModel.append(result.data.history); tempTransactionHistoryModel.clear();
calculatePendingAndInvalidated(); tempTransactionHistoryModel.append(result.data.history);
for (var i = 0; i < tempTransactionHistoryModel.count; i++) {
if (!transactionHistoryModel.get(i)) {
sameItemCount = -1;
break;
} else if (tempTransactionHistoryModel.get(i).transaction_type === transactionHistoryModel.get(i).transaction_type &&
tempTransactionHistoryModel.get(i).text === transactionHistoryModel.get(i).text) {
sameItemCount++;
}
}
if (sameItemCount !== tempTransactionHistoryModel.count) {
transactionHistoryModel.clear();
for (var i = 0; i < tempTransactionHistoryModel.count; i++) {
transactionHistoryModel.append(tempTransactionHistoryModel.get(i));
}
calculatePendingAndInvalidated();
}
} }
refreshTimer.start(); refreshTimer.start();
} }
@ -50,6 +68,7 @@ Item {
Connections { Connections {
target: GlobalServices target: GlobalServices
onMyUsernameChanged: { onMyUsernameChanged: {
transactionHistoryModel.clear();
usernameText.text = Account.username; usernameText.text = Account.username;
} }
} }
@ -143,10 +162,9 @@ Item {
Timer { Timer {
id: refreshTimer; id: refreshTimer;
interval: 4000; // Remove this after demo? interval: 4000;
onTriggered: { onTriggered: {
console.log("Refreshing Wallet Home..."); console.log("Refreshing Wallet Home...");
historyReceived = false;
commerce.balance(); commerce.balance();
commerce.history(); commerce.history();
} }
@ -187,6 +205,9 @@ Item {
// Style // Style
color: hifi.colors.baseGrayHighlight; color: hifi.colors.baseGrayHighlight;
} }
ListModel {
id: tempTransactionHistoryModel;
}
ListModel { ListModel {
id: transactionHistoryModel; id: transactionHistoryModel;
} }
@ -339,7 +360,7 @@ Item {
var a = new Date(timestamp); var a = new Date(timestamp);
var year = a.getFullYear(); var year = a.getFullYear();
var month = addLeadingZero(a.getMonth()); var month = addLeadingZero(a.getMonth() + 1);
var day = addLeadingZero(a.getDate()); var day = addLeadingZero(a.getDate());
var hour = a.getHours(); var hour = a.getHours();
var drawnHour = hour; var drawnHour = hour;

View file

@ -31,6 +31,10 @@ Item {
property bool hasShownSecurityImageTip: false; property bool hasShownSecurityImageTip: false;
property string referrer; property string referrer;
property string keyFilePath; property string keyFilePath;
property date startingTimestamp;
property string setupAttemptID;
readonly property int setupFlowVersion: 1;
readonly property var setupStepNames: [ "Setup Prompt", "Security Image Selection", "Passphrase Selection", "Private Keys Ready" ];
Image { Image {
anchors.fill: parent; anchors.fill: parent;
@ -67,6 +71,13 @@ Item {
anchors.fill: parent; anchors.fill: parent;
} }
onActiveViewChanged: {
var timestamp = new Date();
var currentStepNumber = root.activeView.substring(5);
UserActivityLogger.commerceWalletSetupProgress(timestamp, root.setupAttemptID,
Math.round((timestamp - root.startingTimestamp)/1000), currentStepNumber, root.setupStepNames[currentStepNumber - 1]);
}
// //
// TITLE BAR START // TITLE BAR START
// //
@ -371,7 +382,7 @@ Item {
Item { Item {
id: securityImageTip; id: securityImageTip;
visible: false; visible: !root.hasShownSecurityImageTip && root.activeView === "step_3";
z: 999; z: 999;
anchors.fill: root; anchors.fill: root;
@ -421,7 +432,6 @@ Item {
text: "Got It"; text: "Got It";
onClicked: { onClicked: {
root.hasShownSecurityImageTip = true; root.hasShownSecurityImageTip = true;
securityImageTip.visible = false;
passphraseSelection.focusFirstTextField(); passphraseSelection.focusFirstTextField();
} }
} }
@ -439,9 +449,6 @@ Item {
onVisibleChanged: { onVisibleChanged: {
if (visible) { if (visible) {
commerce.getWalletAuthenticatedStatus(); commerce.getWalletAuthenticatedStatus();
if (!root.hasShownSecurityImageTip) {
securityImageTip.visible = true;
}
} }
} }
@ -732,7 +739,11 @@ Item {
text: "Finish"; text: "Finish";
onClicked: { onClicked: {
root.visible = false; root.visible = false;
root.hasShownSecurityImageTip = false;
sendSignalToWallet({method: 'walletSetup_finished', referrer: root.referrer ? root.referrer : ""}); sendSignalToWallet({method: 'walletSetup_finished', referrer: root.referrer ? root.referrer : ""});
var timestamp = new Date();
UserActivityLogger.commerceWalletSetupFinished(timestamp, setupAttemptID, Math.round((timestamp - root.startingTimestamp)/1000));
} }
} }
} }

View file

@ -9,33 +9,30 @@ Overlay {
Image { Image {
id: image id: image
property bool scaleFix: true; property bool scaleFix: true
property real xOffset: 0 property real xStart: 0
property real yOffset: 0 property real yStart: 0
property real xSize: 0
property real ySize: 0
property real imageScale: 1.0 property real imageScale: 1.0
property var resizer: Timer { property var resizer: Timer {
interval: 50 interval: 50
repeat: false repeat: false
running: false running: false
onTriggered: { onTriggered: {
var targetAspect = root.width / root.height; if (image.xSize === 0) {
var sourceAspect = image.sourceSize.width / image.sourceSize.height; image.xSize = image.sourceSize.width - image.xStart;
if (sourceAspect <= targetAspect) {
if (root.width === image.sourceSize.width) {
return;
}
image.imageScale = root.width / image.sourceSize.width;
} else if (sourceAspect > targetAspect){
if (root.height === image.sourceSize.height) {
return;
}
image.imageScale = root.height / image.sourceSize.height;
} }
image.sourceSize = Qt.size(image.sourceSize.width * image.imageScale, image.sourceSize.height * image.imageScale); if (image.ySize === 0) {
image.ySize = image.sourceSize.height - image.yStart;
}
image.anchors.leftMargin = -image.xStart * root.width / image.xSize;
image.anchors.topMargin = -image.yStart * root.height / image.ySize;
image.anchors.rightMargin = (image.xStart + image.xSize - image.sourceSize.width) * root.width / image.xSize;
image.anchors.bottomMargin = (image.yStart + image.ySize - image.sourceSize.height) * root.height / image.ySize;
} }
} }
x: -1 * xOffset * imageScale
y: -1 * yOffset * imageScale
onSourceSizeChanged: { onSourceSizeChanged: {
if (sourceSize.width !== 0 && sourceSize.height !== 0 && progress === 1.0 && scaleFix) { if (sourceSize.width !== 0 && sourceSize.height !== 0 && progress === 1.0 && scaleFix) {
@ -43,6 +40,8 @@ Overlay {
resizer.start(); resizer.start();
} }
} }
anchors.fill: parent
} }
ColorOverlay { ColorOverlay {
@ -57,8 +56,10 @@ Overlay {
var key = keys[i]; var key = keys[i];
var value = subImage[key]; var value = subImage[key];
switch (key) { switch (key) {
case "x": image.xOffset = value; break; case "x": image.xStart = value; break;
case "y": image.yOffset = value; break; case "y": image.yStart = value; break;
case "width": image.xSize = value; break;
case "height": image.ySize = value; break;
} }
} }
} }
@ -78,6 +79,7 @@ Overlay {
case "imageURL": image.source = value; break; case "imageURL": image.source = value; break;
case "subImage": updateSubImage(value); break; case "subImage": updateSubImage(value); break;
case "color": color.color = Qt.rgba(value.red / 255, value.green / 255, value.blue / 255, root.opacity); break; case "color": color.color = Qt.rgba(value.red / 255, value.green / 255, value.blue / 255, root.opacity); break;
case "bounds": break; // The bounds property is handled in C++.
default: console.log("OVERLAY Unhandled image property " + key); default: console.log("OVERLAY Unhandled image property " + key);
} }
} }

View file

@ -29,6 +29,7 @@ Overlay {
case "borderColor": rectangle.border.color = Qt.rgba(value.red / 255, value.green / 255, value.blue / 255, rectangle.border.color.a); break; case "borderColor": rectangle.border.color = Qt.rgba(value.red / 255, value.green / 255, value.blue / 255, rectangle.border.color.a); break;
case "borderWidth": rectangle.border.width = value; break; case "borderWidth": rectangle.border.width = value; break;
case "radius": rectangle.radius = value; break; case "radius": rectangle.radius = value; break;
case "bounds": break; // The bounds property is handled in C++.
default: console.warn("OVERLAY Unhandled rectangle property " + key); default: console.warn("OVERLAY Unhandled rectangle property " + key);
} }
} }

View file

@ -46,6 +46,7 @@ Overlay {
case "backgroundColor": background.color = Qt.rgba(value.red / 255, value.green / 255, value.blue / 255, background.color.a); break; case "backgroundColor": background.color = Qt.rgba(value.red / 255, value.green / 255, value.blue / 255, background.color.a); break;
case "font": textField.font.pixelSize = value.size; break; case "font": textField.font.pixelSize = value.size; break;
case "lineHeight": textField.lineHeight = value; break; case "lineHeight": textField.lineHeight = value; break;
case "bounds": break; // The bounds property is handled in C++.
default: console.warn("OVERLAY text unhandled property " + key); default: console.warn("OVERLAY text unhandled property " + key);
} }
} }

View file

@ -162,7 +162,7 @@ Item {
readonly property real controlLineHeight: 28 // Height of spinbox control on 1920 x 1080 monitor readonly property real controlLineHeight: 28 // Height of spinbox control on 1920 x 1080 monitor
readonly property real controlInterlineHeight: 21 // 75% of controlLineHeight readonly property real controlInterlineHeight: 21 // 75% of controlLineHeight
readonly property vector2d menuPadding: Qt.vector2d(14, 102) readonly property vector2d menuPadding: Qt.vector2d(14, 102)
readonly property real scrollbarBackgroundWidth: 18 readonly property real scrollbarBackgroundWidth: 20
readonly property real scrollbarHandleWidth: scrollbarBackgroundWidth - 2 readonly property real scrollbarHandleWidth: scrollbarBackgroundWidth - 2
readonly property real tabletMenuHeader: 90 readonly property real tabletMenuHeader: 90
} }

View file

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

View file

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

View file

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

View file

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

View file

@ -83,19 +83,28 @@ void QmlCommerce::buy(const QString& assetId, int cost, const bool controlledFai
void QmlCommerce::balance() { void QmlCommerce::balance() {
auto ledger = DependencyManager::get<Ledger>(); auto ledger = DependencyManager::get<Ledger>();
auto wallet = DependencyManager::get<Wallet>(); auto wallet = DependencyManager::get<Wallet>();
ledger->balance(wallet->listPublicKeys()); QStringList cachedPublicKeys = wallet->listPublicKeys();
if (!cachedPublicKeys.isEmpty()) {
ledger->balance(cachedPublicKeys);
}
} }
void QmlCommerce::inventory() { void QmlCommerce::inventory() {
auto ledger = DependencyManager::get<Ledger>(); auto ledger = DependencyManager::get<Ledger>();
auto wallet = DependencyManager::get<Wallet>(); auto wallet = DependencyManager::get<Wallet>();
ledger->inventory(wallet->listPublicKeys()); QStringList cachedPublicKeys = wallet->listPublicKeys();
if (!cachedPublicKeys.isEmpty()) {
ledger->inventory(cachedPublicKeys);
}
} }
void QmlCommerce::history() { void QmlCommerce::history() {
auto ledger = DependencyManager::get<Ledger>(); auto ledger = DependencyManager::get<Ledger>();
auto wallet = DependencyManager::get<Wallet>(); auto wallet = DependencyManager::get<Wallet>();
ledger->history(wallet->listPublicKeys()); QStringList cachedPublicKeys = wallet->listPublicKeys();
if (!cachedPublicKeys.isEmpty()) {
ledger->history(cachedPublicKeys);
}
} }
void QmlCommerce::changePassphrase(const QString& oldPassphrase, const QString& newPassphrase) { void QmlCommerce::changePassphrase(const QString& oldPassphrase, const QString& newPassphrase) {
@ -128,6 +137,11 @@ void QmlCommerce::reset() {
wallet->reset(); wallet->reset();
} }
void QmlCommerce::resetLocalWalletOnly() {
auto wallet = DependencyManager::get<Wallet>();
wallet->reset();
}
void QmlCommerce::account() { void QmlCommerce::account() {
auto ledger = DependencyManager::get<Ledger>(); auto ledger = DependencyManager::get<Ledger>();
ledger->account(); ledger->account();

View file

@ -65,6 +65,7 @@ protected:
Q_INVOKABLE void history(); Q_INVOKABLE void history();
Q_INVOKABLE void generateKeyPair(); Q_INVOKABLE void generateKeyPair();
Q_INVOKABLE void reset(); Q_INVOKABLE void reset();
Q_INVOKABLE void resetLocalWalletOnly();
Q_INVOKABLE void account(); Q_INVOKABLE void account();
Q_INVOKABLE void certificateInfo(const QString& certificateId); Q_INVOKABLE void certificateInfo(const QString& certificateId);

View file

@ -321,6 +321,16 @@ Wallet::Wallet() {
auto accountManager = DependencyManager::get<AccountManager>(); auto accountManager = DependencyManager::get<AccountManager>();
connect(accountManager.data(), &AccountManager::usernameChanged, this, [&]() { connect(accountManager.data(), &AccountManager::usernameChanged, this, [&]() {
getWalletStatus(); getWalletStatus();
_publicKeys.clear();
if (_securityImage) {
delete _securityImage;
}
_securityImage = nullptr;
// tell the provider we got nothing
updateImageProvider();
_passphrase->clear();
}); });
} }

View file

@ -84,18 +84,7 @@ glm::vec2 RayPick::projectOntoXYPlane(const glm::vec3& worldPos, const glm::vec3
glm::vec2 RayPick::projectOntoOverlayXYPlane(const QUuid& overlayID, const glm::vec3& worldPos, bool unNormalized) { glm::vec2 RayPick::projectOntoOverlayXYPlane(const QUuid& overlayID, const glm::vec3& worldPos, bool unNormalized) {
glm::vec3 position = vec3FromVariant(qApp->getOverlays().getProperty(overlayID, "position").value); glm::vec3 position = vec3FromVariant(qApp->getOverlays().getProperty(overlayID, "position").value);
glm::quat rotation = quatFromVariant(qApp->getOverlays().getProperty(overlayID, "rotation").value); glm::quat rotation = quatFromVariant(qApp->getOverlays().getProperty(overlayID, "rotation").value);
glm::vec3 dimensions; glm::vec3 dimensions = glm::vec3(vec2FromVariant(qApp->getOverlays().getProperty(overlayID, "dimensions").value), 0.01f);
float dpi = qApp->getOverlays().getProperty(overlayID, "dpi").value.toFloat();
if (dpi > 0) {
// Calculate physical dimensions for web3d overlay from resolution and dpi; "dimensions" property is used as a scale.
glm::vec3 resolution = glm::vec3(vec2FromVariant(qApp->getOverlays().getProperty(overlayID, "resolution").value), 1);
glm::vec3 scale = glm::vec3(vec2FromVariant(qApp->getOverlays().getProperty(overlayID, "dimensions").value), 0.01f);
const float INCHES_TO_METERS = 1.0f / 39.3701f;
dimensions = (resolution * INCHES_TO_METERS / dpi) * scale;
} else {
dimensions = glm::vec3(vec2FromVariant(qApp->getOverlays().getProperty(overlayID, "dimensions").value), 0.01);
}
return projectOntoXYPlane(worldPos, position, rotation, dimensions, ENTITY_ITEM_DEFAULT_REGISTRATION_POINT, unNormalized); return projectOntoXYPlane(worldPos, position, rotation, dimensions, ENTITY_ITEM_DEFAULT_REGISTRATION_POINT, unNormalized);
} }

View file

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

View file

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

View file

@ -166,26 +166,36 @@ void Line3DOverlay::setProperties(const QVariantMap& originalProperties) {
bool newEndSet { false }; bool newEndSet { false };
auto start = properties["start"]; auto start = properties["start"];
// if "start" property was not there, check to see if they included aliases: startPoint // If "start" property was not there, check to see if they included aliases: startPoint, p1
if (!start.isValid()) { if (!start.isValid()) {
start = properties["startPoint"]; start = properties["startPoint"];
} }
if (!start.isValid()) {
start = properties["p1"];
}
if (start.isValid()) { if (start.isValid()) {
newStart = vec3FromVariant(start); newStart = vec3FromVariant(start);
newStartSet = true; newStartSet = true;
} }
properties.remove("start"); // so that Base3DOverlay doesn't respond to it properties.remove("start"); // so that Base3DOverlay doesn't respond to it
properties.remove("startPoint");
properties.remove("p1");
auto end = properties["end"]; auto end = properties["end"];
// if "end" property was not there, check to see if they included aliases: endPoint // If "end" property was not there, check to see if they included aliases: endPoint, p2
if (!end.isValid()) { if (!end.isValid()) {
end = properties["endPoint"]; end = properties["endPoint"];
} }
if (!end.isValid()) {
end = properties["p2"];
}
if (end.isValid()) { if (end.isValid()) {
newEnd = vec3FromVariant(end); newEnd = vec3FromVariant(end);
newEndSet = true; newEndSet = true;
} }
properties.remove("end"); // so that Base3DOverlay doesn't respond to it properties.remove("end"); // so that Base3DOverlay doesn't respond to it
properties.remove("endPoint");
properties.remove("p2");
auto length = properties["length"]; auto length = properties["length"];
if (length.isValid()) { if (length.isValid()) {
@ -252,14 +262,23 @@ QVariant Line3DOverlay::getProperty(const QString& property) {
if (property == "end" || property == "endPoint" || property == "p2") { if (property == "end" || property == "endPoint" || property == "p2") {
return vec3toVariant(getEnd()); return vec3toVariant(getEnd());
} }
if (property == "length") {
return QVariant(getLength());
}
if (property == "endParentID") {
return _endParentID;
}
if (property == "endParentJointIndex") {
return _endParentJointIndex;
}
if (property == "localStart") { if (property == "localStart") {
return vec3toVariant(getLocalStart()); return vec3toVariant(getLocalStart());
} }
if (property == "localEnd") { if (property == "localEnd") {
return vec3toVariant(getLocalEnd()); return vec3toVariant(getLocalEnd());
} }
if (property == "length") { if (property == "glow") {
return QVariant(getLength()); return getGlow();
} }
if (property == "lineWidth") { if (property == "lineWidth") {
return _lineWidth; return _lineWidth;

View file

@ -35,7 +35,8 @@ ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) :
_modelTextures(QVariantMap()), _modelTextures(QVariantMap()),
_url(modelOverlay->_url), _url(modelOverlay->_url),
_updateModel(false), _updateModel(false),
_loadPriority(modelOverlay->getLoadPriority()) _scaleToFit(modelOverlay->_scaleToFit),
_loadPriority(modelOverlay->_loadPriority)
{ {
_model->init(); _model->init();
_model->setLoadingPriority(_loadPriority); _model->setLoadingPriority(_loadPriority);
@ -134,6 +135,9 @@ void ModelOverlay::setProperties(const QVariantMap& properties) {
} }
auto dimensions = properties["dimensions"]; auto dimensions = properties["dimensions"];
if (!dimensions.isValid()) {
dimensions = properties["size"];
}
if (dimensions.isValid()) { if (dimensions.isValid()) {
_scaleToFit = true; _scaleToFit = true;
setDimensions(vec3FromVariant(dimensions)); setDimensions(vec3FromVariant(dimensions));

View file

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

View file

@ -305,13 +305,6 @@ public slots:
OverlayID getKeyboardFocusOverlay(); OverlayID getKeyboardFocusOverlay();
void setKeyboardFocusOverlay(const OverlayID& id); void setKeyboardFocusOverlay(const OverlayID& id);
void mousePressPointerEvent(const OverlayID& overlayID, const PointerEvent& event);
void mouseMovePointerEvent(const OverlayID& overlayID, const PointerEvent& event);
void mouseReleasePointerEvent(const OverlayID& overlayID, const PointerEvent& event);
void hoverEnterPointerEvent(const OverlayID& overlayID, const PointerEvent& event);
void hoverOverPointerEvent(const OverlayID& overlayID, const PointerEvent& event);
void hoverLeavePointerEvent(const OverlayID& overlayID, const PointerEvent& event);
signals: signals:
/**jsdoc /**jsdoc
* Emitted when an overlay is deleted * Emitted when an overlay is deleted
@ -358,6 +351,14 @@ private:
OverlayID _currentHoverOverOverlayID { UNKNOWN_OVERLAY_ID }; OverlayID _currentHoverOverOverlayID { UNKNOWN_OVERLAY_ID };
RayToOverlayIntersectionResult findRayIntersectionForMouseEvent(PickRay ray); RayToOverlayIntersectionResult findRayIntersectionForMouseEvent(PickRay ray);
private slots:
void mousePressPointerEvent(const OverlayID& overlayID, const PointerEvent& event);
void mouseMovePointerEvent(const OverlayID& overlayID, const PointerEvent& event);
void mouseReleasePointerEvent(const OverlayID& overlayID, const PointerEvent& event);
void hoverEnterPointerEvent(const OverlayID& overlayID, const PointerEvent& event);
void hoverOverPointerEvent(const OverlayID& overlayID, const PointerEvent& event);
void hoverLeavePointerEvent(const OverlayID& overlayID, const PointerEvent& event);
}; };
#endif // hifi_Overlays_h #endif // hifi_Overlays_h

View file

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

View file

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

View file

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

View file

@ -55,17 +55,15 @@
#include <plugins/InputConfiguration.h> #include <plugins/InputConfiguration.h>
#include "ui/Snapshot.h" #include "ui/Snapshot.h"
#include "SoundCache.h" #include "SoundCache.h"
#include "raypick/PointerScriptingInterface.h" #include "raypick/PointerScriptingInterface.h"
static const float DPI = 30.47f; static int MAX_WINDOW_SIZE = 4096;
static const float INCHES_TO_METERS = 1.0f / 39.3701f;
static const float METERS_TO_INCHES = 39.3701f; static const float METERS_TO_INCHES = 39.3701f;
static const float OPAQUE_ALPHA_THRESHOLD = 0.99f; static const float OPAQUE_ALPHA_THRESHOLD = 0.99f;
const QString Web3DOverlay::TYPE = "web3d"; const QString Web3DOverlay::TYPE = "web3d";
const QString Web3DOverlay::QML = "Web3DOverlay.qml"; const QString Web3DOverlay::QML = "Web3DOverlay.qml";
Web3DOverlay::Web3DOverlay() : _dpi(DPI) { Web3DOverlay::Web3DOverlay() {
_touchDevice.setCapabilities(QTouchDevice::Position); _touchDevice.setCapabilities(QTouchDevice::Position);
_touchDevice.setType(QTouchDevice::TouchScreen); _touchDevice.setType(QTouchDevice::TouchScreen);
_touchDevice.setName("Web3DOverlayTouchDevice"); _touchDevice.setName("Web3DOverlayTouchDevice");
@ -82,7 +80,6 @@ Web3DOverlay::Web3DOverlay(const Web3DOverlay* Web3DOverlay) :
_url(Web3DOverlay->_url), _url(Web3DOverlay->_url),
_scriptURL(Web3DOverlay->_scriptURL), _scriptURL(Web3DOverlay->_scriptURL),
_dpi(Web3DOverlay->_dpi), _dpi(Web3DOverlay->_dpi),
_resolution(Web3DOverlay->_resolution),
_showKeyboardFocusHighlight(Web3DOverlay->_showKeyboardFocusHighlight) _showKeyboardFocusHighlight(Web3DOverlay->_showKeyboardFocusHighlight)
{ {
_geometryId = DependencyManager::get<GeometryCache>()->allocateID(); _geometryId = DependencyManager::get<GeometryCache>()->allocateID();
@ -154,7 +151,7 @@ void Web3DOverlay::buildWebSurface() {
setupQmlSurface(); setupQmlSurface();
} }
_webSurface->getSurfaceContext()->setContextProperty("globalPosition", vec3toVariant(getWorldPosition())); _webSurface->getSurfaceContext()->setContextProperty("globalPosition", vec3toVariant(getWorldPosition()));
_webSurface->resize(QSize(_resolution.x, _resolution.y)); onResizeWebSurface();
_webSurface->resume(); _webSurface->resume();
}); });
@ -244,8 +241,16 @@ void Web3DOverlay::setMaxFPS(uint8_t maxFPS) {
} }
void Web3DOverlay::onResizeWebSurface() { void Web3DOverlay::onResizeWebSurface() {
_mayNeedResize = false; glm::vec2 dims = glm::vec2(getDimensions());
_webSurface->resize(QSize(_resolution.x, _resolution.y)); dims *= METERS_TO_INCHES * _dpi;
// ensure no side is never larger then MAX_WINDOW_SIZE
float max = (dims.x > dims.y) ? dims.x : dims.y;
if (max > MAX_WINDOW_SIZE) {
dims *= MAX_WINDOW_SIZE / max;
}
_webSurface->resize(QSize(dims.x, dims.y));
} }
unsigned int Web3DOverlay::deviceIdByTouchPoint(qreal x, qreal y) { unsigned int Web3DOverlay::deviceIdByTouchPoint(qreal x, qreal y) {
@ -266,14 +271,14 @@ void Web3DOverlay::render(RenderArgs* args) {
return; return;
} }
if (_currentMaxFPS != _desiredMaxFPS) {
setMaxFPS(_desiredMaxFPS);
}
if (_mayNeedResize) { if (_mayNeedResize) {
emit resizeWebSurface(); emit resizeWebSurface();
} }
if (_currentMaxFPS != _desiredMaxFPS) {
setMaxFPS(_desiredMaxFPS);
}
vec4 color(toGlm(getColor()), getAlpha()); vec4 color(toGlm(getColor()), getAlpha());
if (!_texture) { if (!_texture) {
@ -310,7 +315,7 @@ void Web3DOverlay::render(RenderArgs* args) {
Transform Web3DOverlay::evalRenderTransform() { Transform Web3DOverlay::evalRenderTransform() {
Transform transform = Parent::evalRenderTransform(); Transform transform = Parent::evalRenderTransform();
transform.setScale(1.0f); transform.setScale(1.0f);
transform.postScale(glm::vec3(getSize(), 1.0f)); transform.postScale(glm::vec3(getDimensions(), 1.0f));
return transform; return transform;
} }
@ -434,18 +439,10 @@ void Web3DOverlay::setProperties(const QVariantMap& properties) {
} }
} }
auto resolution = properties["resolution"];
if (resolution.isValid()) {
bool valid;
auto res = vec2FromVariant(resolution, valid);
if (valid) {
_resolution = res;
}
}
auto dpi = properties["dpi"]; auto dpi = properties["dpi"];
if (dpi.isValid()) { if (dpi.isValid()) {
_dpi = dpi.toFloat(); _dpi = dpi.toFloat();
_mayNeedResize = true;
} }
auto maxFPS = properties["maxFPS"]; auto maxFPS = properties["maxFPS"];
@ -467,8 +464,6 @@ void Web3DOverlay::setProperties(const QVariantMap& properties) {
_inputMode = Touch; _inputMode = Touch;
} }
} }
_mayNeedResize = true;
} }
QVariant Web3DOverlay::getProperty(const QString& property) { QVariant Web3DOverlay::getProperty(const QString& property) {
@ -478,9 +473,6 @@ QVariant Web3DOverlay::getProperty(const QString& property) {
if (property == "scriptURL") { if (property == "scriptURL") {
return _scriptURL; return _scriptURL;
} }
if (property == "resolution") {
return vec2toVariant(_resolution);
}
if (property == "dpi") { if (property == "dpi") {
return _dpi; return _dpi;
} }
@ -536,17 +528,18 @@ void Web3DOverlay::setScriptURL(const QString& scriptURL) {
} }
} }
glm::vec2 Web3DOverlay::getSize() const {
return _resolution / _dpi * INCHES_TO_METERS * getDimensions();
};
bool Web3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal) { bool Web3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal) {
// FIXME - face and surfaceNormal not being returned glm::vec2 dimensions = getDimensions();
glm::quat rotation = getWorldOrientation();
glm::vec3 position = getWorldPosition();
// Don't call applyTransformTo() or setTransform() here because this code runs too frequently. if (findRayRectangleIntersection(origin, direction, rotation, position, dimensions, distance)) {
surfaceNormal = rotation * Vectors::UNIT_Z;
// Produce the dimensions of the overlay based on the image's aspect ratio and the overlay's scale. face = glm::dot(surfaceNormal, direction) > 0 ? MIN_Z_FACE : MAX_Z_FACE;
return findRayRectangleIntersection(origin, direction, getWorldOrientation(), getWorldPosition(), getSize(), distance); return true;
} else {
return false;
}
} }
Web3DOverlay* Web3DOverlay::createClone() const { Web3DOverlay* Web3DOverlay::createClone() const {

View file

@ -52,8 +52,6 @@ public:
void setProperties(const QVariantMap& properties) override; void setProperties(const QVariantMap& properties) override;
QVariant getProperty(const QString& property) override; QVariant getProperty(const QString& property) override;
glm::vec2 getSize() const override;
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
BoxFace& face, glm::vec3& surfaceNormal) override; BoxFace& face, glm::vec3& surfaceNormal) override;
@ -93,10 +91,9 @@ private:
gpu::TexturePointer _texture; gpu::TexturePointer _texture;
QString _url; QString _url;
QString _scriptURL; QString _scriptURL;
float _dpi; float _dpi { 30.0f };
vec2 _resolution{ 640, 480 };
int _geometryId { 0 }; int _geometryId { 0 };
bool _showKeyboardFocusHighlight{ true }; bool _showKeyboardFocusHighlight { true };
QTouchDevice _touchDevice; QTouchDevice _touchDevice;

View file

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

View file

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

View file

@ -437,9 +437,11 @@ glm::mat4 CompositorHelper::getReticleTransform(const glm::mat4& eyePose, const
} else { } else {
d = glm::normalize(overlaySurfacePoint); d = glm::normalize(overlaySurfacePoint);
} }
reticlePosition = headPosition + (d * getReticleDepth()); // Our sensor to world matrix always has uniform scale
float sensorSpaceReticleDepth = getReticleDepth() / extractScale(_sensorToWorldMatrix).x;
reticlePosition = headPosition + (d * sensorSpaceReticleDepth);
quat reticleOrientation = cancelOutRoll(glm::quat_cast(_currentDisplayPlugin->getHeadPose())); quat reticleOrientation = cancelOutRoll(glm::quat_cast(_currentDisplayPlugin->getHeadPose()));
vec3 reticleScale = vec3(Cursor::Manager::instance().getScale() * reticleSize * getReticleDepth()); vec3 reticleScale = vec3(Cursor::Manager::instance().getScale() * reticleSize * sensorSpaceReticleDepth);
return glm::inverse(eyePose) * createMatFromScaleQuatAndPos(reticleScale, reticleOrientation, reticlePosition); return glm::inverse(eyePose) * createMatFromScaleQuatAndPos(reticleScale, reticleOrientation, reticlePosition);
} else { } else {
static const float CURSOR_PIXEL_SIZE = 32.0f; static const float CURSOR_PIXEL_SIZE = 32.0f;

View file

@ -75,8 +75,11 @@ RenderableModelEntityItem::RenderableModelEntityItem(const EntityItemID& entityI
RenderableModelEntityItem::~RenderableModelEntityItem() { } RenderableModelEntityItem::~RenderableModelEntityItem() { }
void RenderableModelEntityItem::setDimensions(const glm::vec3& value) { void RenderableModelEntityItem::setDimensions(const glm::vec3& value) {
_dimensionsInitialized = true; glm::vec3 newDimensions = glm::max(value, glm::vec3(0.0f)); // can never have negative dimensions
ModelEntityItem::setDimensions(value); if (getDimensions() != newDimensions) {
_dimensionsInitialized = true;
ModelEntityItem::setDimensions(value);
}
} }
QVariantMap parseTexturesToMap(QString textures, const QVariantMap& defaultTextures) { QVariantMap parseTexturesToMap(QString textures, const QVariantMap& defaultTextures) {
@ -1160,7 +1163,6 @@ bool ModelEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoin
return true; return true;
} }
if (model->getScaleToFitDimensions() != entity->getDimensions() || if (model->getScaleToFitDimensions() != entity->getDimensions() ||
model->getRegistrationPoint() != entity->getRegistrationPoint()) { model->getRegistrationPoint() != entity->getRegistrationPoint()) {
return true; return true;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -121,63 +121,58 @@ uint64_t uvec2ToUint64(const uvec2& v) {
class AudioHandler : public QObject, QRunnable { class AudioHandler : public QObject, QRunnable {
Q_OBJECT Q_OBJECT
public: public:
AudioHandler(QObject* container, const QString& deviceName, int runDelayMs = 0, QObject* parent = nullptr) : QObject(parent) { AudioHandler(QSharedPointer<OffscreenQmlSurface> surface, const QString& deviceName, QObject* parent = nullptr) : QObject(parent) {
_container = container;
_newTargetDevice = deviceName; _newTargetDevice = deviceName;
_runDelayMs = runDelayMs; _surface = surface;
setAutoDelete(true); setAutoDelete(true);
QThreadPool::globalInstance()->start(this); if (deviceName.size() > 0) {
QThreadPool::globalInstance()->start(this);
}
} }
virtual ~AudioHandler() { virtual ~AudioHandler() {
qDebug() << "Audio Handler Destroyed"; qDebug() << "Audio Handler Destroyed";
} }
void run() override { void run() override {
if (_newTargetDevice.isEmpty()) { if (!_surface.isNull() && _surface->getRootItem() && !_surface->getCleaned()) {
return; for (auto player : _surface->getRootItem()->findChildren<QMediaPlayer*>()) {
} auto mediaState = player->state();
if (_runDelayMs > 0) { QMediaService *svc = player->service();
QThread::msleep(_runDelayMs); if (nullptr == svc) {
} return;
auto audioIO = DependencyManager::get<AudioClient>(); }
QString deviceName = audioIO->getActiveAudioDevice(QAudio::AudioOutput).deviceName(); QAudioOutputSelectorControl *out = qobject_cast<QAudioOutputSelectorControl *>
for (auto player : _container->findChildren<QMediaPlayer*>()) { (svc->requestControl(QAudioOutputSelectorControl_iid));
auto mediaState = player->state(); if (nullptr == out) {
QMediaService *svc = player->service(); return;
if (nullptr == svc) { }
return; QString deviceOuput;
} auto outputs = out->availableOutputs();
QAudioOutputSelectorControl *out = qobject_cast<QAudioOutputSelectorControl *> for (int i = 0; i < outputs.size(); i++) {
(svc->requestControl(QAudioOutputSelectorControl_iid)); QString output = outputs[i];
if (nullptr == out) { QString description = out->outputDescription(output);
return; if (description == _newTargetDevice) {
} deviceOuput = output;
QString deviceOuput; break;
auto outputs = out->availableOutputs(); }
for (int i = 0; i < outputs.size(); i++) { }
QString output = outputs[i]; out->setActiveOutput(deviceOuput);
QString description = out->outputDescription(output); svc->releaseControl(out);
if (description == deviceName) { // if multimedia was paused, it will start playing automatically after changing audio device
deviceOuput = output; // this will reset it back to a paused state
break; 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: private:
QString _newTargetDevice; QString _newTargetDevice;
QObject* _container; QSharedPointer<OffscreenQmlSurface> _surface;
int _runDelayMs;
}; };
class OffscreenTextures { class OffscreenTextures {
@ -502,6 +497,7 @@ QOpenGLContext* OffscreenQmlSurface::getSharedContext() {
} }
void OffscreenQmlSurface::cleanup() { void OffscreenQmlSurface::cleanup() {
_isCleaned = true;
_canvas->makeCurrent(); _canvas->makeCurrent();
_renderControl->invalidate(); _renderControl->invalidate();
@ -600,6 +596,7 @@ OffscreenQmlSurface::OffscreenQmlSurface() {
OffscreenQmlSurface::~OffscreenQmlSurface() { OffscreenQmlSurface::~OffscreenQmlSurface() {
QObject::disconnect(&_updateTimer); QObject::disconnect(&_updateTimer);
disconnectAudioOutputTimer();
QObject::disconnect(qApp); QObject::disconnect(qApp);
cleanup(); cleanup();
@ -613,6 +610,15 @@ OffscreenQmlSurface::~OffscreenQmlSurface() {
void OffscreenQmlSurface::onAboutToQuit() { void OffscreenQmlSurface::onAboutToQuit() {
_paused = true; _paused = true;
QObject::disconnect(&_updateTimer); QObject::disconnect(&_updateTimer);
disconnectAudioOutputTimer();
}
void OffscreenQmlSurface::disconnectAudioOutputTimer() {
if (_audioOutputUpdateTimer.isActive()) {
_audioOutputUpdateTimer.stop();
}
QObject::disconnect(&_audioOutputUpdateTimer);
} }
void OffscreenQmlSurface::create() { 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, // 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. // a timer with a small interval is used to get better performance.
QObject::connect(&_updateTimer, &QTimer::timeout, this, &OffscreenQmlSurface::updateQuick); QObject::connect(&_updateTimer, &QTimer::timeout, this, &OffscreenQmlSurface::updateQuick);
@ -699,10 +713,11 @@ void OffscreenQmlSurface::forceQmlAudioOutputDeviceUpdate() {
QMetaObject::invokeMethod(this, "forceQmlAudioOutputDeviceUpdate", Qt::QueuedConnection); QMetaObject::invokeMethod(this, "forceQmlAudioOutputDeviceUpdate", Qt::QueuedConnection);
} else { } else {
auto audioIO = DependencyManager::get<AudioClient>(); auto audioIO = DependencyManager::get<AudioClient>();
QString deviceName = audioIO->getActiveAudioDevice(QAudio::AudioOutput).deviceName(); _currentAudioOutputDevice = audioIO->getActiveAudioDevice(QAudio::AudioOutput).deviceName();
int waitForAudioQmlMs = 500; if (_audioOutputUpdateTimer.isActive()) {
// The audio device need to be change using oth _audioOutputUpdateTimer.stop();
new AudioHandler(_rootItem, deviceName, waitForAudioQmlMs); }
_audioOutputUpdateTimer.start();
} }
} }

View file

@ -40,7 +40,7 @@ class QQuickItem;
using QmlContextCallback = std::function<void(QQmlContext*, QObject*)>; using QmlContextCallback = std::function<void(QQmlContext*, QObject*)>;
class OffscreenQmlSurface : public QObject { class OffscreenQmlSurface : public QObject, public QEnableSharedFromThis<OffscreenQmlSurface> {
Q_OBJECT Q_OBJECT
Q_PROPERTY(bool focusText READ isFocusText NOTIFY focusTextChanged) Q_PROPERTY(bool focusText READ isFocusText NOTIFY focusTextChanged)
public: public:
@ -75,6 +75,7 @@ public:
void pause(); void pause();
void resume(); void resume();
bool isPaused() const; bool isPaused() const;
bool getCleaned() { return _isCleaned; }
void setBaseUrl(const QUrl& baseUrl); void setBaseUrl(const QUrl& baseUrl);
QQuickItem* getRootItem(); QQuickItem* getRootItem();
@ -116,6 +117,7 @@ public slots:
void changeAudioOutputDevice(const QString& deviceName, bool isHtmlUpdate = false); void changeAudioOutputDevice(const QString& deviceName, bool isHtmlUpdate = false);
void forceHtmlAudioOutputDeviceUpdate(); void forceHtmlAudioOutputDeviceUpdate();
void forceQmlAudioOutputDeviceUpdate(); void forceQmlAudioOutputDeviceUpdate();
signals: signals:
void audioOutputDeviceChanged(const QString& deviceName); void audioOutputDeviceChanged(const QString& deviceName);
@ -147,6 +149,7 @@ private:
void render(); void render();
void cleanup(); void cleanup();
QJsonObject getGLContextData(); QJsonObject getGLContextData();
void disconnectAudioOutputTimer();
private slots: private slots:
void updateQuick(); void updateQuick();
@ -170,6 +173,9 @@ private:
uint64_t _lastRenderTime { 0 }; uint64_t _lastRenderTime { 0 };
uvec2 _size; uvec2 _size;
QTimer _audioOutputUpdateTimer;
QString _currentAudioOutputDevice;
// Texture management // Texture management
TextureAndFence _latestTextureAndFence { 0, 0 }; TextureAndFence _latestTextureAndFence { 0, 0 };
@ -177,6 +183,7 @@ private:
bool _polish { true }; bool _polish { true };
bool _paused { true }; bool _paused { true };
bool _focusText { false }; bool _focusText { false };
bool _isCleaned{ false };
uint8_t _maxFps { 60 }; uint8_t _maxFps { 60 };
MouseTranslator _mouseTranslator { [](const QPointF& p) { return p.toPoint(); } }; MouseTranslator _mouseTranslator { [](const QPointF& p) { return p.toPoint(); } };
QWindow* _proxyWindow { nullptr }; QWindow* _proxyWindow { nullptr };

View file

@ -337,6 +337,7 @@ void ViveControllerManager::InputDevice::update(float deltaTime, const controlle
_poseStateMap.clear(); _poseStateMap.clear();
_buttonPressedMap.clear(); _buttonPressedMap.clear();
_validTrackedObjects.clear(); _validTrackedObjects.clear();
_trackedControllers = 0;
// While the keyboard is open, we defer strictly to the keyboard values // While the keyboard is open, we defer strictly to the keyboard values
if (isOpenVrKeyboardShown()) { if (isOpenVrKeyboardShown()) {
@ -369,14 +370,12 @@ void ViveControllerManager::InputDevice::update(float deltaTime, const controlle
} }
} }
int numTrackedControllers = 0;
if (leftHandDeviceIndex != vr::k_unTrackedDeviceIndexInvalid) { if (leftHandDeviceIndex != vr::k_unTrackedDeviceIndexInvalid) {
numTrackedControllers++; _trackedControllers++;
} }
if (rightHandDeviceIndex != vr::k_unTrackedDeviceIndexInvalid) { if (rightHandDeviceIndex != vr::k_unTrackedDeviceIndexInvalid) {
numTrackedControllers++; _trackedControllers++;
} }
_trackedControllers = numTrackedControllers;
calibrateFromHandController(inputCalibrationData); calibrateFromHandController(inputCalibrationData);
calibrateFromUI(inputCalibrationData); calibrateFromUI(inputCalibrationData);
@ -527,6 +526,7 @@ void ViveControllerManager::InputDevice::handleTrackedObject(uint32_t deviceInde
// but _validTrackedObjects remain in sensor frame // but _validTrackedObjects remain in sensor frame
_validTrackedObjects.push_back(std::make_pair(poseIndex, pose)); _validTrackedObjects.push_back(std::make_pair(poseIndex, pose));
_trackedControllers++;
} else { } else {
controller::Pose invalidPose; controller::Pose invalidPose;
_poseStateMap[poseIndex] = invalidPose; _poseStateMap[poseIndex] = invalidPose;
@ -758,9 +758,9 @@ void ViveControllerManager::InputDevice::handleHmd(uint32_t deviceIndex, const c
} else { } else {
const mat4& mat = mat4(); const mat4& mat = mat4();
const vec3 zero = vec3(); const vec3 zero = vec3();
handleHeadPoseEvent(inputCalibrationData, mat, zero, zero); handleHeadPoseEvent(inputCalibrationData, mat, zero, zero);
} }
_trackedControllers++;
} }
} }

View file

@ -62,7 +62,7 @@
this.pointingAtTablet = function(controllerData) { this.pointingAtTablet = function(controllerData) {
var rayPick = controllerData.rayPicks[this.hand]; 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() { this.getOtherModule = function() {

View file

@ -81,7 +81,8 @@ Script.include("/~/system/libraries/cloneEntityUtils.js");
}; };
this.otherHandIsParent = function(props) { this.otherHandIsParent = function(props) {
return this.getOtherModule().thisHandIsParent(props); var otherModule = this.getOtherModule();
return (otherModule.thisHandIsParent(props) && otherModule.grabbing);
}; };
this.startNearParentingGrabEntity = function (controllerData, targetProps) { this.startNearParentingGrabEntity = function (controllerData, targetProps) {

View file

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

View file

@ -112,8 +112,8 @@ var DEFAULT_LIGHT_DIMENSIONS = Vec3.multiply(20, DEFAULT_DIMENSIONS);
var MENU_AUTO_FOCUS_ON_SELECT = "Auto Focus on Select"; var MENU_AUTO_FOCUS_ON_SELECT = "Auto Focus on Select";
var MENU_EASE_ON_FOCUS = "Ease Orientation on Focus"; var MENU_EASE_ON_FOCUS = "Ease Orientation on Focus";
var MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE = "Show Lights and Particle Systems in Edit Mode"; var MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE = "Show Lights and Particle Systems in Create Mode";
var MENU_SHOW_ZONES_IN_EDIT_MODE = "Show Zones in Edit Mode"; var MENU_SHOW_ZONES_IN_EDIT_MODE = "Show Zones in Create Mode";
var SETTING_AUTO_FOCUS_ON_SELECT = "autoFocusOnSelect"; var SETTING_AUTO_FOCUS_ON_SELECT = "autoFocusOnSelect";
var SETTING_EASE_ON_FOCUS = "cameraEaseOnFocus"; var SETTING_EASE_ON_FOCUS = "cameraEaseOnFocus";

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

@ -15,6 +15,9 @@ function setUpKeyboardControl() {
var KEYBOARD_HEIGHT = 200; var KEYBOARD_HEIGHT = 200;
function raiseKeyboard() { function raiseKeyboard() {
window.isKeyboardRaised = true;
window.isNumericKeyboard = this.type === "number";
if (lowerTimer !== null) { if (lowerTimer !== null) {
clearTimeout(lowerTimer); clearTimeout(lowerTimer);
lowerTimer = null; lowerTimer = null;
@ -35,6 +38,9 @@ function setUpKeyboardControl() {
} }
function doLowerKeyboard() { function doLowerKeyboard() {
window.isKeyboardRaised = false;
window.isNumericKeyboard = false;
EventBridge.emitWebEvent("_LOWER_KEYBOARD"); EventBridge.emitWebEvent("_LOWER_KEYBOARD");
lowerTimer = null; lowerTimer = null;
isRaised = false; isRaised = false;

View file

@ -118,15 +118,16 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, visible) {
Overlays.deleteOverlay(this.webOverlayID); Overlays.deleteOverlay(this.webOverlayID);
} }
var WEB_ENTITY_Z_OFFSET = (tabletDepth / 2) * (1 / sensorScaleFactor); var WEB_ENTITY_Z_OFFSET = (tabletDepth / 2.0) / sensorScaleFactor;
var WEB_ENTITY_Y_OFFSET = 0.004 * (1 / sensorScaleFactor); var WEB_ENTITY_Y_OFFSET = 0.004;
var screenWidth = 0.82 * tabletWidth;
var screenHeight = 0.81 * tabletHeight;
this.webOverlayID = Overlays.addOverlay("web3d", { this.webOverlayID = Overlays.addOverlay("web3d", {
name: "WebTablet Web", name: "WebTablet Web",
url: url, url: url,
localPosition: { x: 0, y: WEB_ENTITY_Y_OFFSET, z: -WEB_ENTITY_Z_OFFSET }, localPosition: { x: 0, y: WEB_ENTITY_Y_OFFSET, z: -WEB_ENTITY_Z_OFFSET },
localRotation: Quat.angleAxis(180, Y_AXIS), localRotation: Quat.angleAxis(180, Y_AXIS),
resolution: this.getTabletTextureResolution(), dimensions: {x: screenWidth, y: screenHeight, z: 0.1},
dpi: tabletDpi, dpi: tabletDpi,
color: { red: 255, green: 255, blue: 255 }, color: { red: 255, green: 255, blue: 255 },
alpha: 1.0, alpha: 1.0,
@ -136,12 +137,15 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, visible) {
visible: visible visible: visible
}); });
var HOME_BUTTON_Y_OFFSET = ((tabletHeight / 2) - (tabletHeight / 20)) * (1 / sensorScaleFactor); var HOME_BUTTON_Y_OFFSET = ((tabletHeight / 2) - (tabletHeight / 20)) * (1 / sensorScaleFactor) - 0.003;
this.homeButtonID = Overlays.addOverlay("sphere", { // FIXME: Circle3D overlays currently at the wrong dimensions, so we need to account for that here
var homeButtonDim = 4.0 * tabletScaleFactor / 3.0;
this.homeButtonID = Overlays.addOverlay("circle3d", {
name: "homeButton", name: "homeButton",
localPosition: {x: -0.001, y: -HOME_BUTTON_Y_OFFSET, z: 0.0}, localPosition: { x: 0.0, y: -HOME_BUTTON_Y_OFFSET, z: -WEB_ENTITY_Z_OFFSET },
localRotation: {x: 0, y: 1, z: 0, w: 0}, localRotation: { x: 0, y: 1, z: 0, w: 0},
dimensions: { x: 4 * tabletScaleFactor, y: 4 * tabletScaleFactor, z: 4 * tabletScaleFactor}, dimensions: { x: homeButtonDim, y: homeButtonDim, z: homeButtonDim },
solid: true,
alpha: 0.0, alpha: 0.0,
visible: visible, visible: visible,
drawInFront: false, drawInFront: false,
@ -151,14 +155,14 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, visible) {
this.homeButtonHighlightID = Overlays.addOverlay("circle3d", { this.homeButtonHighlightID = Overlays.addOverlay("circle3d", {
name: "homeButtonHighlight", name: "homeButtonHighlight",
localPosition: { x: 0, y: -HOME_BUTTON_Y_OFFSET + 0.003, z: -0.0158 }, localPosition: { x: 0, y: -HOME_BUTTON_Y_OFFSET, z: -WEB_ENTITY_Z_OFFSET },
localRotation: { x: 0, y: 1, z: 0, w: 0 }, localRotation: { x: 0, y: 1, z: 0, w: 0 },
dimensions: { x: 4 * tabletScaleFactor, y: 4 * tabletScaleFactor, z: 4 * tabletScaleFactor }, dimensions: { x: homeButtonDim, y: homeButtonDim, z: homeButtonDim },
color: { red: 255, green: 255, blue: 255 },
solid: true, solid: true,
innerRadius: 0.9, innerRadius: 0.9,
ignoreIntersection: true, ignoreIntersection: true,
alpha: 1.0, alpha: 1.0,
color: { red: 255, green: 255, blue: 255 },
visible: visible, visible: visible,
drawInFront: false, drawInFront: false,
parentID: this.tabletEntityID, parentID: this.tabletEntityID,
@ -265,11 +269,16 @@ WebTablet.prototype.setLandscape = function(newLandscapeValue) {
this.landscape = newLandscapeValue; this.landscape = newLandscapeValue;
Overlays.editOverlay(this.tabletEntityID, Overlays.editOverlay(this.tabletEntityID,
{ rotation: this.landscape ? Quat.multiply(Camera.orientation, ROT_LANDSCAPE) : { rotation: Quat.multiply(Camera.orientation, this.landscape ? ROT_LANDSCAPE : ROT_Y_180) });
Quat.multiply(Camera.orientation, ROT_Y_180) });
var tabletWidth = getTabletWidthFromSettings() * MyAvatar.sensorToWorldScale;
var tabletScaleFactor = tabletWidth / TABLET_NATURAL_DIMENSIONS.x;
var tabletHeight = TABLET_NATURAL_DIMENSIONS.y * tabletScaleFactor;
var screenWidth = 0.82 * tabletWidth;
var screenHeight = 0.81 * tabletHeight;
Overlays.editOverlay(this.webOverlayID, { Overlays.editOverlay(this.webOverlayID, {
resolution: this.getTabletTextureResolution(), rotation: Quat.multiply(Camera.orientation, ROT_LANDSCAPE_WINDOW),
rotation: Quat.multiply(Camera.orientation, ROT_LANDSCAPE_WINDOW) dimensions: {x: this.landscape ? screenHeight : screenWidth, y: this.landscape ? screenWidth : screenHeight, z: 0.1}
}); });
}; };
@ -505,31 +514,17 @@ WebTablet.prototype.getPosition = function () {
}; };
WebTablet.prototype.mousePressEvent = function (event) { WebTablet.prototype.mousePressEvent = function (event) {
var pickRay = Camera.computePickRay(event.x, event.y); if (!HMD.active) {
var entityPickResults; var pickRay = Camera.computePickRay(event.x, event.y);
entityPickResults = Overlays.findRayIntersection(pickRay, true, [this.tabletEntityID]); var tabletBackPickResults = Overlays.findRayIntersection(pickRay, true, [this.tabletEntityID]);
if (entityPickResults.intersects && (entityPickResults.entityID === this.tabletEntityID || if (tabletBackPickResults.intersects) {
entityPickResults.overlayID === this.tabletEntityID)) { var overlayPickResults = Overlays.findRayIntersection(pickRay, true, [this.webOverlayID, this.homeButtonID]);
var overlayPickResults = Overlays.findRayIntersection(pickRay, true, [this.webOverlayID, this.homeButtonID], []); if (!overlayPickResults.intersects) {
if (overlayPickResults.intersects && overlayPickResults.overlayID === this.homeButtonID) { this.dragging = true;
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); var invCameraXform = new Xform(Camera.orientation, Camera.position).inv();
var onHomeScreen = tablet.onHomeScreen(); this.initialLocalIntersectionPoint = invCameraXform.xformPoint(tabletBackPickResults.intersection);
var isMessageOpen = tablet.isMessageDialogOpen(); this.initialLocalPosition = Overlays.getProperty(this.tabletEntityID, "localPosition");
if (onHomeScreen) {
if (isMessageOpen === false) {
HMD.closeTablet();
}
} else {
if (isMessageOpen === false) {
tablet.gotoHomeScreen();
this.setHomeButtonTexture();
}
} }
} else if (!HMD.active && (!overlayPickResults.intersects || overlayPickResults.overlayID !== this.webOverlayID)) {
this.dragging = true;
var invCameraXform = new Xform(Camera.orientation, Camera.position).inv();
this.initialLocalIntersectionPoint = invCameraXform.xformPoint(entityPickResults.intersection);
this.initialLocalPosition = Overlays.getProperty(this.tabletEntityID, "localPosition");
} }
} }
}; };

View file

@ -272,22 +272,8 @@ projectOntoEntityXYPlane = function (entityID, worldPos, props) {
projectOntoOverlayXYPlane = function projectOntoOverlayXYPlane(overlayID, worldPos) { projectOntoOverlayXYPlane = function projectOntoOverlayXYPlane(overlayID, worldPos) {
var position = Overlays.getProperty(overlayID, "position"); var position = Overlays.getProperty(overlayID, "position");
var rotation = Overlays.getProperty(overlayID, "rotation"); var rotation = Overlays.getProperty(overlayID, "rotation");
var dimensions; var dimensions = Overlays.getProperty(overlayID, "dimensions");
dimensions.z = 0.01; // we are projecting onto the XY plane of the overlay, so ignore the z dimension
var dpi = Overlays.getProperty(overlayID, "dpi");
if (dpi) {
// Calculate physical dimensions for web3d overlay from resolution and dpi; "dimensions" property is used as a scale.
var resolution = Overlays.getProperty(overlayID, "resolution");
resolution.z = 1; // Circumvent divide-by-zero.
var scale = Overlays.getProperty(overlayID, "dimensions");
scale.z = 0.01; // overlay dimensions are 2D, not 3D.
dimensions = Vec3.multiplyVbyV(Vec3.multiply(resolution, INCHES_TO_METERS / dpi), scale);
} else {
dimensions = Overlays.getProperty(overlayID, "dimensions");
if (dimensions.z) {
dimensions.z = 0.01; // overlay dimensions are 2D, not 3D.
}
}
return projectOntoXYPlane(worldPos, position, rotation, dimensions, DEFAULT_REGISTRATION_POINT); return projectOntoXYPlane(worldPos, position, rotation, dimensions, DEFAULT_REGISTRATION_POINT);
}; };

View file

@ -169,32 +169,12 @@ function calculateTouchTargetFromOverlay(touchTip, overlayID) {
// calclulate normalized position // calclulate normalized position
var invRot = Quat.inverse(overlayRotation); var invRot = Quat.inverse(overlayRotation);
var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(position, overlayPosition)); var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(position, overlayPosition));
var dpi = Overlays.getProperty(overlayID, "dpi");
var dimensions; var dimensions = Overlays.getProperty(overlayID, "dimensions");
if (dpi) { if (dimensions === undefined) {
// Calculate physical dimensions for web3d overlay from resolution and dpi; "dimensions" property return;
// is used as a scale.
var resolution = Overlays.getProperty(overlayID, "resolution");
if (resolution === undefined) {
return;
}
resolution.z = 1; // Circumvent divide-by-zero.
var scale = Overlays.getProperty(overlayID, "dimensions");
if (scale === undefined) {
return;
}
scale.z = 0.01; // overlay dimensions are 2D, not 3D.
dimensions = Vec3.multiplyVbyV(Vec3.multiply(resolution, INCHES_TO_METERS / dpi), scale);
} else {
dimensions = Overlays.getProperty(overlayID, "dimensions");
if (dimensions === undefined) {
return;
}
if (!dimensions.z) {
dimensions.z = 0.01; // sometimes overlay dimensions are 2D, not 3D.
}
} }
dimensions.z = 0.01; // we are projecting onto the XY plane of the overlay, so ignore the z dimension
var invDimensions = { x: 1 / dimensions.x, y: 1 / dimensions.y, z: 1 / dimensions.z }; var invDimensions = { x: 1 / dimensions.x, y: 1 / dimensions.y, z: 1 / dimensions.z };
var normalizedPosition = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), DEFAULT_REGISTRATION_POINT); var normalizedPosition = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), DEFAULT_REGISTRATION_POINT);

View file

@ -185,7 +185,7 @@ logTrace = function(str) {
// (the vector that would move the point outside the sphere) // (the vector that would move the point outside the sphere)
// otherwise returns false // otherwise returns false
findSphereHit = function(point, sphereRadius) { findSphereHit = function(point, sphereRadius) {
var EPSILON = 0.000001; //smallish positive number - used as margin of error for some computations var EPSILON = 0.000001; //smallish positive number - used as margin of error for some computations
var vectorLength = Vec3.length(point); var vectorLength = Vec3.length(point);
if (vectorLength < EPSILON) { if (vectorLength < EPSILON) {
return true; return true;
@ -400,25 +400,28 @@ resizeTablet = function (width, newParentJointIndex, sensorToWorldScaleOverride)
}); });
// update webOverlay // update webOverlay
var WEB_ENTITY_Z_OFFSET = (tabletDepth / 2) * sensorScaleOffsetOverride; var WEB_ENTITY_Z_OFFSET = (tabletDepth / 2.0) * sensorScaleOffsetOverride;
var WEB_ENTITY_Y_OFFSET = 0.004 * sensorScaleOffsetOverride; var WEB_ENTITY_Y_OFFSET = 0.004 * sensorScaleFactor * sensorScaleOffsetOverride;
var screenWidth = 0.82 * tabletWidth;
var screenHeight = 0.81 * tabletHeight;
var landscape = Tablet.getTablet("com.highfidelity.interface.tablet.system").landscape;
Overlays.editOverlay(HMD.tabletScreenID, { Overlays.editOverlay(HMD.tabletScreenID, {
localPosition: { x: 0, y: WEB_ENTITY_Y_OFFSET, z: -WEB_ENTITY_Z_OFFSET }, localPosition: { x: 0, y: WEB_ENTITY_Y_OFFSET, z: -WEB_ENTITY_Z_OFFSET },
dimensions: {x: landscape ? screenHeight : screenWidth, y: landscape ? screenWidth : screenHeight, z: 0.1},
dpi: tabletDpi dpi: tabletDpi
}); });
// update homeButton // update homeButton
var HOME_BUTTON_Y_OFFSET = ((tabletHeight / 2) - (tabletHeight / 20)) * sensorScaleOffsetOverride; var HOME_BUTTON_Y_OFFSET = ((tabletHeight / 2) - (tabletHeight / 20) - 0.003 * sensorScaleFactor) * sensorScaleOffsetOverride;
var homeButtonDim = 4 * tabletScaleFactor; // FIXME: Circle3D overlays currently at the wrong dimensions, so we need to account for that here
var homeButtonDim = 4.0 * tabletScaleFactor / 3.0;
Overlays.editOverlay(HMD.homeButtonID, { Overlays.editOverlay(HMD.homeButtonID, {
localPosition: {x: -0.001, y: -HOME_BUTTON_Y_OFFSET, z: 0.0}, localPosition: { x: 0, y: -HOME_BUTTON_Y_OFFSET, z: -WEB_ENTITY_Z_OFFSET },
dimensions: { x: homeButtonDim, y: homeButtonDim, z: homeButtonDim} dimensions: { x: homeButtonDim, y: homeButtonDim, z: homeButtonDim }
}); });
// Circle3D overlays render at 1.5x their proper dimensions
var highlightDim = homeButtonDim / 3.0;
Overlays.editOverlay(HMD.homeButtonHighlightID, { Overlays.editOverlay(HMD.homeButtonHighlightID, {
localPosition: { x: 0, y: -HOME_BUTTON_Y_OFFSET + 0.003, z: -0.0158 }, localPosition: { x: 0, y: -HOME_BUTTON_Y_OFFSET, z: -WEB_ENTITY_Z_OFFSET },
dimensions: { x: highlightDim, y: highlightDim, z: highlightDim } dimensions: { x: homeButtonDim, y: homeButtonDim, z: homeButtonDim }
}); });
}; };

View file

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

View file

@ -47,7 +47,7 @@
} }
return false; return false;
} }
if (Overlays.getProperty(HMD.homeButtonID, "type") != "sphere" || if (Overlays.getProperty(HMD.homeButtonID, "type") != "circle3d" ||
Overlays.getProperty(HMD.tabletScreenID, "type") != "web3d") { Overlays.getProperty(HMD.tabletScreenID, "type") != "web3d") {
if (debugTablet) { if (debugTablet) {
print("TABLET is invalid due to other"); print("TABLET is invalid due to other");