Merge branch 'master' of github.com:highfidelity/hifi into textToSpeech

This commit is contained in:
Zach Fox 2018-10-23 15:51:34 -07:00
commit d1556ae4e8
58 changed files with 1350 additions and 399 deletions

View file

@ -222,7 +222,8 @@ void Agent::requestScript() {
return; return;
} }
auto request = DependencyManager::get<ResourceManager>()->createResourceRequest(this, scriptURL); auto request = DependencyManager::get<ResourceManager>()->createResourceRequest(
this, scriptURL, true, -1, "Agent::requestScript");
if (!request) { if (!request) {
qWarning() << "Could not create ResourceRequest for Agent script at" << scriptURL.toString(); qWarning() << "Could not create ResourceRequest for Agent script at" << scriptURL.toString();

View file

@ -21,7 +21,6 @@
#include <QtCore/QTimer> #include <QtCore/QTimer>
#include <QUuid> #include <QUuid>
#include <ClientTraitsHandler.h>
#include <EntityEditPacketSender.h> #include <EntityEditPacketSender.h>
#include <EntityTree.h> #include <EntityTree.h>
#include <ScriptEngine.h> #include <ScriptEngine.h>

View file

@ -35,6 +35,7 @@
#include "AssignmentClientLogging.h" #include "AssignmentClientLogging.h"
#include "AssignmentFactory.h" #include "AssignmentFactory.h"
#include "ResourceRequestObserver.h"
const QString ASSIGNMENT_CLIENT_TARGET_NAME = "assignment-client"; const QString ASSIGNMENT_CLIENT_TARGET_NAME = "assignment-client";
const long long ASSIGNMENT_REQUEST_INTERVAL_MSECS = 1 * 1000; const long long ASSIGNMENT_REQUEST_INTERVAL_MSECS = 1 * 1000;
@ -49,6 +50,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
DependencyManager::set<tracing::Tracer>(); DependencyManager::set<tracing::Tracer>();
DependencyManager::set<StatTracker>(); DependencyManager::set<StatTracker>();
DependencyManager::set<AccountManager>(); DependencyManager::set<AccountManager>();
DependencyManager::set<ResourceRequestObserver>();
auto addressManager = DependencyManager::set<AddressManager>(); auto addressManager = DependencyManager::set<AddressManager>();

View file

@ -4,8 +4,8 @@ set(EXTERNAL_NAME serverless-content)
ExternalProject_Add( ExternalProject_Add(
${EXTERNAL_NAME} ${EXTERNAL_NAME}
URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC73.zip URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC75.zip
URL_MD5 0c5edfb63cafb042311d3cf25261fbf2 URL_MD5 b4225d058952e17976ac228330ce8d51
CONFIGURE_COMMAND "" CONFIGURE_COMMAND ""
BUILD_COMMAND "" BUILD_COMMAND ""
INSTALL_COMMAND "" INSTALL_COMMAND ""

View file

@ -31,7 +31,7 @@ Rectangle {
scaleSlider.notify = false; scaleSlider.notify = false;
scaleSlider.value = Math.round(avatarScale * 10); scaleSlider.value = Math.round(avatarScale * 10);
scaleSlider.notify = true;; scaleSlider.notify = true;
if (settings.dominantHand === 'left') { if (settings.dominantHand === 'left') {
leftHandRadioButton.checked = true; leftHandRadioButton.checked = true;

View file

@ -0,0 +1,364 @@
//
// ItemUnderTest
// qml/hifi/commerce/marketplaceItemTester
//
// Load items not in the marketplace for testing purposes
//
// Created by Kerry Ivan Kurian on 2018-10-18
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.10
import QtQuick.Controls 2.3
import Hifi 1.0 as Hifi
import "qrc:////qml//styles-uit" as HifiStylesUit
import "qrc:////qml//controls-uit" as HifiControlsUit
Rectangle {
id: root;
color: hifi.colors.baseGray
width: parent.width - 16
height: childrenRect.height + itemHeaderContainer.anchors.topMargin + detailsContainer.anchors.topMargin
property var detailsExpanded: false
property var actions: {
"forward": function(resource, assetType, resourceObjectId){
switch(assetType) {
case "application":
Commerce.installApp(resource, true);
break;
case "avatar":
MyAvatar.useFullAvatarURL(resource);
break;
case "content set":
urlHandler.handleUrl("hifi://localhost/0,0,0");
Commerce.replaceContentSet(toUrl(resource), "");
break;
case "entity":
case "wearable":
rezEntity(resource, assetType, resourceObjectId);
break;
default:
print("Marketplace item tester unsupported assetType " + assetType);
}
},
"trash": function(resource, assetType){
if ("application" === assetType) {
Commerce.uninstallApp(resource);
}
sendToScript({
method: "tester_deleteResourceObject",
objectId: resourceListModel.get(index).resourceObjectId});
resourceListModel.remove(index);
}
}
Item {
id: itemHeaderContainer
anchors.top: parent.top
anchors.topMargin: 8
anchors.left: parent.left
anchors.leftMargin: 8
width: parent.width - 16
height: childrenRect.height
Item {
id: itemNameContainer
width: parent.width * 0.5
height: childrenRect.height
HifiStylesUit.RalewaySemiBold {
id: resourceName
height: paintedHeight
width: parent.width
text: {
var match = resource.match(/\/([^/]*)$/);
return match ? match[1] : resource;
}
size: 14
color: hifi.colors.white
wrapMode: Text.WrapAnywhere
}
HifiStylesUit.RalewayRegular {
id: resourceUrl
anchors.top: resourceName.bottom;
anchors.topMargin: 4;
height: paintedHeight
width: parent.width
text: resource
size: 12
color: hifi.colors.faintGray;
wrapMode: Text.WrapAnywhere
}
}
HifiControlsUit.ComboBox {
id: comboBox
anchors.left: itemNameContainer.right
anchors.leftMargin: 4
anchors.verticalCenter: itemNameContainer.verticalCenter
height: 30
width: parent.width * 0.3 - anchors.leftMargin
model: [
"application",
"avatar",
"content set",
"entity",
"wearable",
"unknown"
]
currentIndex: (("entity or wearable" === assetType) ?
model.indexOf("unknown") : model.indexOf(assetType))
Component.onCompleted: {
onCurrentIndexChanged.connect(function() {
assetType = model[currentIndex];
sendToScript({
method: "tester_updateResourceObjectAssetType",
objectId: resourceListModel.get(index)["resourceObjectId"],
assetType: assetType });
});
}
}
Button {
id: actionButton
property var glyphs: {
"application": hifi.glyphs.install,
"avatar": hifi.glyphs.avatar,
"content set": hifi.glyphs.globe,
"entity": hifi.glyphs.wand,
"trash": hifi.glyphs.trash,
"unknown": hifi.glyphs.circleSlash,
"wearable": hifi.glyphs.hat
}
property int color: hifi.buttons.blue;
property int colorScheme: hifi.colorSchemes.dark;
anchors.left: comboBox.right
anchors.leftMargin: 4
anchors.verticalCenter: itemNameContainer.verticalCenter
width: parent.width * 0.10 - anchors.leftMargin
height: width
enabled: comboBox.model[comboBox.currentIndex] !== "unknown"
onClicked: {
if (model.currentlyRecordingResources) {
model.currentlyRecordingResources = false;
} else {
model.resourceAccessEventText = "";
model.currentlyRecordingResources = true;
root.actions["forward"](resource, comboBox.currentText, resourceObjectId);
}
sendToScript({
method: "tester_updateResourceRecordingStatus",
objectId: resourceListModel.get(index).resourceObjectId,
status: model.currentlyRecordingResources
});
}
background: Rectangle {
radius: 4;
gradient: Gradient {
GradientStop {
position: 0.2
color: {
if (!actionButton.enabled) {
hifi.buttons.disabledColorStart[actionButton.colorScheme]
} else if (actionButton.pressed) {
hifi.buttons.pressedColor[actionButton.color]
} else if (actionButton.hovered) {
hifi.buttons.hoveredColor[actionButton.color]
} else {
hifi.buttons.colorStart[actionButton.color]
}
}
}
GradientStop {
position: 1.0
color: {
if (!actionButton.enabled) {
hifi.buttons.disabledColorFinish[actionButton.colorScheme]
} else if (actionButton.pressed) {
hifi.buttons.pressedColor[actionButton.color]
} else if (actionButton.hovered) {
hifi.buttons.hoveredColor[actionButton.color]
} else {
hifi.buttons.colorFinish[actionButton.color]
}
}
}
}
}
contentItem: Item {
HifiStylesUit.HiFiGlyphs {
id: rezIcon;
text: model.currentlyRecordingResources ? hifi.glyphs.scriptStop : actionButton.glyphs[comboBox.model[comboBox.currentIndex]];
anchors.fill: parent
size: 30;
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
color: enabled ? hifi.buttons.textColor[actionButton.color]
: hifi.buttons.disabledTextColor[actionButton.colorScheme]
}
}
}
Button {
id: trashButton
property int color: hifi.buttons.red;
property int colorScheme: hifi.colorSchemes.dark;
anchors.left: actionButton.right
anchors.verticalCenter: itemNameContainer.verticalCenter
anchors.leftMargin: 4
width: parent.width * 0.10 - anchors.leftMargin
height: width
onClicked: {
root.actions["trash"](resource, comboBox.currentText, resourceObjectId);
}
background: Rectangle {
radius: 4;
gradient: Gradient {
GradientStop {
position: 0.2
color: {
if (!trashButton.enabled) {
hifi.buttons.disabledColorStart[trashButton.colorScheme]
} else if (trashButton.pressed) {
hifi.buttons.pressedColor[trashButton.color]
} else if (trashButton.hovered) {
hifi.buttons.hoveredColor[trashButton.color]
} else {
hifi.buttons.colorStart[trashButton.color]
}
}
}
GradientStop {
position: 1.0
color: {
if (!trashButton.enabled) {
hifi.buttons.disabledColorFinish[trashButton.colorScheme]
} else if (trashButton.pressed) {
hifi.buttons.pressedColor[trashButton.color]
} else if (trashButton.hovered) {
hifi.buttons.hoveredColor[trashButton.color]
} else {
hifi.buttons.colorFinish[trashButton.color]
}
}
}
}
}
contentItem: Item {
HifiStylesUit.HiFiGlyphs {
id: trashIcon;
text: hifi.glyphs.trash
anchors.fill: parent
size: 22;
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
color: enabled ? hifi.buttons.textColor[trashButton.color]
: hifi.buttons.disabledTextColor[trashButton.colorScheme]
}
}
}
}
Item {
id: detailsContainer
width: parent.width - 16
height: root.detailsExpanded ? 300 : 26
anchors.top: itemHeaderContainer.bottom
anchors.topMargin: 12
anchors.left: parent.left
anchors.leftMargin: 8
HifiStylesUit.HiFiGlyphs {
id: detailsToggle
anchors.left: parent.left
anchors.leftMargin: -4
anchors.top: parent.top
anchors.topMargin: -2
width: 22
text: root.detailsExpanded ? hifi.glyphs.minimize : hifi.glyphs.maximize
color: hifi.colors.white
size: 22
MouseArea {
anchors.fill: parent
onClicked: root.detailsExpanded = !root.detailsExpanded
}
}
ScrollView {
id: detailsTextContainer
anchors.top: parent.top
anchors.left: detailsToggle.right
anchors.leftMargin: 4
anchors.right: parent.right
height: detailsContainer.height - (root.detailsExpanded ? (copyToClipboardButton.height + copyToClipboardButton.anchors.topMargin) : 0)
clip: true
TextArea {
id: detailsText
readOnly: true
color: hifi.colors.white
text: {
var numUniqueResources = (model.resourceAccessEventText.split("\n").length - 1);
if (root.detailsExpanded && numUniqueResources > 0) {
return model.resourceAccessEventText
} else {
return numUniqueResources.toString() + " unique source/resource url pair" + (numUniqueResources === 1 ? "" : "s") + " recorded"
}
}
font: Qt.font({ family: "Courier", pointSize: 8, weight: Font.Normal })
wrapMode: TextEdit.NoWrap
background: Rectangle {
anchors.fill: parent;
color: hifi.colors.baseGrayShadow;
border.width: 0;
}
}
MouseArea {
anchors.fill: parent
onClicked: {
if (root.detailsExpanded) {
detailsText.selectAll();
} else {
root.detailsExpanded = true;
}
}
}
}
HifiControlsUit.Button {
id: copyToClipboardButton;
visible: root.detailsExpanded
color: hifi.buttons.noneBorderlessWhite
colorScheme: hifi.colorSchemes.dark
anchors.top: detailsTextContainer.bottom
anchors.topMargin: 8
anchors.right: parent.right
width: 160
height: 30
text: "Copy to Clipboard"
onClicked: {
Window.copyToClipboard(detailsText.text);
}
}
}
}

View file

@ -4,21 +4,19 @@
// //
// Load items not in the marketplace for testing purposes // Load items not in the marketplace for testing purposes
// //
// Created by Zach Fox on 2018-09-05 // Created by Kerry Ivan Kurian on 2018-09-05
// Copyright 2018 High Fidelity, Inc. // Copyright 2018 High Fidelity, Inc.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
import QtQuick 2.5 import QtQuick 2.10
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick.Dialogs 1.0
import QtQuick.Layouts 1.1 import QtQuick.Layouts 1.1
import QtQuick.Controls 2.3
import Hifi 1.0 as Hifi import Hifi 1.0 as Hifi
import "../../../styles-uit" as HifiStylesUit import "qrc:////qml//styles-uit" as HifiStylesUit
import "../../../controls-uit" as HifiControlsUit import "qrc:////qml//controls-uit" as HifiControlsUit
@ -27,225 +25,147 @@ Rectangle {
property string installedApps property string installedApps
property var nextResourceObjectId: 0 property var nextResourceObjectId: 0
signal sendToScript(var message)
HifiStylesUit.HifiConstants { id: hifi } HifiStylesUit.HifiConstants { id: hifi }
ListModel { id: resourceListModel } ListModel { id: resourceListModel }
color: hifi.colors.white color: hifi.colors.darkGray
//
// TITLE BAR START
//
Item {
id: titleBarContainer
// Size
width: root.width
height: 50
// Anchors
anchors.left: parent.left
anchors.top: parent.top
// Title bar text
HifiStylesUit.RalewaySemiBold {
id: titleBarText
text: "Marketplace Item Tester"
// Text size
size: 24
// Anchors
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.leftMargin: 16
width: paintedWidth
// Style
color: hifi.colors.lightGrayText
// Alignment
horizontalAlignment: Text.AlignHLeft
verticalAlignment: Text.AlignVCenter
}
// Separator
HifiControlsUit.Separator {
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.bottomMargin: 1
}
}
//
// TITLE BAR END
//
Rectangle {
id: spinner
z: 999
anchors.top: titleBarContainer.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: buttonContainer.top
color: hifi.colors.darkGray
AnimatedImage { AnimatedImage {
id: spinner;
source: "spinner.gif" source: "spinner.gif"
width: 74; width: 74
height: width; height: width
anchors.verticalCenter: parent.verticalCenter; anchors.centerIn: parent
anchors.horizontalCenter: parent.horizontalCenter;
}
function fromScript(message) {
switch (message.method) {
case "newResourceObjectInTest":
var resourceObject = message.resourceObject;
resourceListModel.append(resourceObject);
spinner.visible = false;
break;
case "nextObjectIdInTest":
nextResourceObjectId = message.id;
spinner.visible = false;
break;
} }
} }
function buildResourceObj(resource) { Rectangle {
resource = resource.trim(); id: instructionsContainer
var assetType = (resource.match(/\.app\.json$/) ? "application" : z: 998
resource.match(/\.fst$/) ? "avatar" : color: hifi.colors.darkGray
resource.match(/\.json\.gz$/) ? "content set" : visible: resourceListModel.count === 0 && !spinner.visible
resource.match(/\.json$/) ? "entity or wearable" : anchors.top: titleBarContainer.bottom
"unknown"); anchors.topMargin: 20
return { "id": nextResourceObjectId++, anchors.left: parent.left
"resource": resource, anchors.leftMargin: 20
"assetType": assetType }; anchors.right: parent.right
} anchors.rightMargin: 20
anchors.bottom: buttonContainer.top
anchors.bottomMargin: 20
function installResourceObj(resourceObj) { HifiStylesUit.RalewayRegular {
if ("application" === resourceObj.assetType) { text: "Use Marketplace Item Tester to test out your items before submitting them to the Marketplace." +
Commerce.installApp(resourceObj.resource); "\n\nUse one of the buttons below to load your item."
// Text size
size: 20
// Anchors
anchors.fill: parent
// Style
color: hifi.colors.lightGrayText
wrapMode: Text.Wrap
// Alignment
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
} }
} }
function addAllInstalledAppsToList() {
var i, apps = Commerce.getInstalledApps().split(","), len = apps.length;
for(i = 0; i < len - 1; ++i) {
if (i in apps) {
resourceListModel.append(buildResourceObj(apps[i]));
}
}
}
function toUrl(resource) {
var httpPattern = /^http/i;
return httpPattern.test(resource) ? resource : "file:///" + resource;
}
function rezEntity(resource, entityType) {
sendToScript({
method: 'tester_rezClicked',
itemHref: toUrl(resource),
itemType: entityType});
}
ListView { ListView {
anchors.fill: parent id: itemList
anchors.leftMargin: 12 visible: !instructionsContainer.visible
anchors.bottomMargin: 40 anchors.top: titleBarContainer.bottom
anchors.rightMargin: 12 anchors.topMargin: 20
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: buttonContainer.top
anchors.bottomMargin: 20
ScrollBar.vertical: ScrollBar {
visible: !instructionsContainer.visible
policy: ScrollBar.AlwaysOn
parent: itemList.parent
anchors.top: itemList.top
anchors.right: itemList.right
anchors.bottom: itemList.bottom
width: 16
}
clip: true
model: resourceListModel model: resourceListModel
spacing: 5 spacing: 8
interactive: false
delegate: RowLayout { delegate: ItemUnderTest { }
anchors.left: parent.left
width: parent.width
spacing: 5
property var actions: {
"forward": function(resource, assetType){
switch(assetType) {
case "application":
Commerce.openApp(resource);
break;
case "avatar":
MyAvatar.useFullAvatarURL(resource);
break;
case "content set":
urlHandler.handleUrl("hifi://localhost/0,0,0");
Commerce.replaceContentSet(toUrl(resource), "");
break;
case "entity":
case "wearable":
rezEntity(resource, assetType);
break;
default:
print("Marketplace item tester unsupported assetType " + assetType);
}
},
"trash": function(resource, assetType){
if ("application" === assetType) {
Commerce.uninstallApp(resource);
}
sendToScript({
method: "tester_deleteResourceObject",
objectId: resourceListModel.get(index).id});
resourceListModel.remove(index);
}
} }
Column { Item {
Layout.preferredWidth: root.width * .6 id: buttonContainer
spacing: 5
Text {
text: {
var match = resource.match(/\/([^/]*)$/);
return match ? match[1] : resource;
}
font.pointSize: 12
horizontalAlignment: Text.AlignBottom
}
Text {
text: resource
font.pointSize: 8
width: root.width * .6
horizontalAlignment: Text.AlignBottom
wrapMode: Text.WrapAnywhere
}
}
ComboBox {
id: comboBox
Layout.preferredWidth: root.width * .2
model: [
"application",
"avatar",
"content set",
"entity",
"wearable",
"unknown"
]
currentIndex: (("entity or wearable" === assetType) ?
model.indexOf("unknown") : model.indexOf(assetType))
Component.onCompleted: {
onCurrentIndexChanged.connect(function() {
assetType = model[currentIndex];
sendToScript({
method: "tester_updateResourceObjectAssetType",
objectId: resourceListModel.get(index)["id"],
assetType: assetType });
});
}
}
Repeater {
model: [ "forward", "trash" ]
HifiStylesUit.HiFiGlyphs {
property var glyphs: {
"application": hifi.glyphs.install,
"avatar": hifi.glyphs.avatar,
"content set": hifi.glyphs.globe,
"entity": hifi.glyphs.wand,
"trash": hifi.glyphs.trash,
"unknown": hifi.glyphs.circleSlash,
"wearable": hifi.glyphs.hat,
}
text: (("trash" === modelData) ?
glyphs.trash :
glyphs[comboBox.model[comboBox.currentIndex]])
size: ("trash" === modelData) ? 22 : 30
color: hifi.colors.black
horizontalAlignment: Text.AlignHCenter
MouseArea {
anchors.fill: parent
onClicked: {
actions[modelData](resource, comboBox.currentText);
}
}
}
}
}
headerPositioning: ListView.OverlayHeader
header: HifiStylesUit.RalewayRegular {
id: rootHeader
text: "Marketplace Item Tester"
height: 80
width: paintedWidth
size: 22
color: hifi.colors.black
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: 12 anchors.leftMargin: 12
} anchors.right: parent.right
anchors.rightMargin: 12
footerPositioning: ListView.OverlayFooter anchors.bottom: parent.bottom
footer: Row { anchors.bottomMargin: 12
id: rootActions height: 40
spacing: 20
anchors.horizontalCenter: parent.horizontalCenter
property string currentAction property string currentAction
property var actions: { property var actions: {
"Load File": function() { "Load File": function() {
rootActions.currentAction = "load file"; buttonContainer.currentAction = "load file";
Window.browseChanged.connect(onResourceSelected); Window.browseChanged.connect(onResourceSelected);
Window.browseAsync("Please select a file (*.app.json *.json *.fst *.json.gz)", "", "Assets (*.app.json *.json *.fst *.json.gz)"); Window.browseAsync("Please select a file (*.app.json *.json *.fst *.json.gz)", "", "Assets (*.app.json *.json *.fst *.json.gz)");
}, },
"Load URL": function() { "Load URL": function() {
rootActions.currentAction = "load url"; buttonContainer.currentAction = "load url";
Window.promptTextChanged.connect(onResourceSelected); Window.promptTextChanged.connect(onResourceSelected);
Window.promptAsync("Please enter a URL", ""); Window.promptAsync("Please enter a URL", "");
} }
@ -257,6 +177,7 @@ Rectangle {
// Alas, there is nothing we can do about that so charge // Alas, there is nothing we can do about that so charge
// ahead as though we are sure the present signal is one // ahead as though we are sure the present signal is one
// we expect. // we expect.
print("!!!! resource selected");
switch(currentAction) { switch(currentAction) {
case "load file": case "load file":
Window.browseChanged.disconnect(onResourceSelected); Window.browseChanged.disconnect(onResourceSelected);
@ -266,25 +187,91 @@ Rectangle {
break; break;
} }
if (resource) { if (resource) {
print("!!!! building resource object");
var resourceObj = buildResourceObj(resource); var resourceObj = buildResourceObj(resource);
installResourceObj(resourceObj); print("!!!! notifying script of resource object");
sendToScript({ sendToScript({
method: 'tester_newResourceObject', method: 'tester_newResourceObject',
resourceObject: resourceObj }); resourceObject: resourceObj
});
} }
} }
Repeater {
model: [ "Load File", "Load URL" ]
HifiControlsUit.Button { HifiControlsUit.Button {
enabled: !spinner.visible
anchors.right: parent.horizontalCenter
anchors.rightMargin: width/4
anchors.verticalCenter: parent.verticalCenter
color: hifi.buttons.blue color: hifi.buttons.blue
fontSize: 20 fontSize: 20
text: modelData text: "Load File"
width: root.width / 3 width: parent.width / 3
height: 40 height: parent.height
onClicked: actions[text]() onClicked: buttonContainer.actions[text]()
}
HifiControlsUit.Button {
enabled: !spinner.visible
anchors.left: parent.horizontalCenter
anchors.leftMargin: width/4
anchors.verticalCenter: parent.verticalCenter
color: hifi.buttons.blue
fontSize: 20
text: "Load URL"
width: parent.width / 3
height: parent.height
onClicked: buttonContainer.actions[text]()
} }
} }
function fromScript(message) {
switch (message.method) {
case "newResourceObjectInTest":
var resourceObject = message.resourceObject;
resourceListModel.clear(); // REMOVE THIS once we support specific referrers
resourceListModel.append(resourceObject);
spinner.visible = false;
break;
case "nextObjectIdInTest":
print("!!!! message from script! " + JSON.stringify(message));
nextResourceObjectId = message.id;
spinner.visible = false;
break;
case "resourceRequestEvent":
// When we support multiple items under test simultaneously,
// we'll have to replace "0" with the correct index.
resourceListModel.setProperty(0, "resourceAccessEventText", message.resourceAccessEventText);
break;
} }
} }
function buildResourceObj(resource) {
resource = resource.trim();
var assetType = (resource.match(/\.app\.json$/) ? "application" :
resource.match(/\.fst$/) ? "avatar" :
resource.match(/\.json\.gz$/) ? "content set" :
resource.match(/\.json$/) ? "entity or wearable" :
"unknown");
// Uncomment this once we support more than one item in test at the same time
//nextResourceObjectId++;
return { "resourceObjectId": nextResourceObjectId,
"resource": resource,
"assetType": assetType };
}
function toUrl(resource) {
var httpPattern = /^http/i;
return httpPattern.test(resource) ? resource : "file:///" + resource;
}
function rezEntity(resource, entityType, resourceObjectId) {
print("!!!! tester_rezClicked");
sendToScript({
method: 'tester_rezClicked',
itemHref: toUrl(resource),
itemType: entityType,
itemId: resourceObjectId });
}
signal sendToScript(var message)
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 58 KiB

View file

@ -227,6 +227,7 @@
#include "commerce/Ledger.h" #include "commerce/Ledger.h"
#include "commerce/Wallet.h" #include "commerce/Wallet.h"
#include "commerce/QmlCommerce.h" #include "commerce/QmlCommerce.h"
#include "ResourceRequestObserver.h"
#include "webbrowser/WebBrowserSuggestionsEngine.h" #include "webbrowser/WebBrowserSuggestionsEngine.h"
#include <DesktopPreviewProvider.h> #include <DesktopPreviewProvider.h>
@ -949,6 +950,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
DependencyManager::set<TTSScriptingInterface>(); DependencyManager::set<TTSScriptingInterface>();
DependencyManager::set<FadeEffect>(); DependencyManager::set<FadeEffect>();
DependencyManager::set<ResourceRequestObserver>();
return previousSessionCrashed; return previousSessionCrashed;
} }
@ -3141,6 +3143,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) {
surfaceContext->setContextProperty("ContextOverlay", DependencyManager::get<ContextOverlayInterface>().data()); surfaceContext->setContextProperty("ContextOverlay", DependencyManager::get<ContextOverlayInterface>().data());
surfaceContext->setContextProperty("Wallet", DependencyManager::get<WalletScriptingInterface>().data()); surfaceContext->setContextProperty("Wallet", DependencyManager::get<WalletScriptingInterface>().data());
surfaceContext->setContextProperty("HiFiAbout", AboutUtil::getInstance()); surfaceContext->setContextProperty("HiFiAbout", AboutUtil::getInstance());
surfaceContext->setContextProperty("ResourceRequestObserver", DependencyManager::get<ResourceRequestObserver>().data());
if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) { if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
surfaceContext->setContextProperty("Steam", new SteamScriptingInterface(engine, steamClient.get())); surfaceContext->setContextProperty("Steam", new SteamScriptingInterface(engine, steamClient.get()));
@ -5034,12 +5037,12 @@ void Application::saveSettings() const {
PluginManager::getInstance()->saveSettings(); PluginManager::getInstance()->saveSettings();
} }
bool Application::importEntities(const QString& urlOrFilename) { bool Application::importEntities(const QString& urlOrFilename, const bool isObservable, const qint64 callerId) {
bool success = false; bool success = false;
_entityClipboard->withWriteLock([&] { _entityClipboard->withWriteLock([&] {
_entityClipboard->eraseAllOctreeElements(); _entityClipboard->eraseAllOctreeElements();
success = _entityClipboard->readFromURL(urlOrFilename); success = _entityClipboard->readFromURL(urlOrFilename, isObservable, callerId);
if (success) { if (success) {
_entityClipboard->reaverageOctreeElements(); _entityClipboard->reaverageOctreeElements();
} }
@ -6823,6 +6826,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
scriptEngine->registerGlobalObject("Wallet", DependencyManager::get<WalletScriptingInterface>().data()); scriptEngine->registerGlobalObject("Wallet", DependencyManager::get<WalletScriptingInterface>().data());
scriptEngine->registerGlobalObject("AddressManager", DependencyManager::get<AddressManager>().data()); scriptEngine->registerGlobalObject("AddressManager", DependencyManager::get<AddressManager>().data());
scriptEngine->registerGlobalObject("HifiAbout", AboutUtil::getInstance()); scriptEngine->registerGlobalObject("HifiAbout", AboutUtil::getInstance());
scriptEngine->registerGlobalObject("ResourceRequestObserver", DependencyManager::get<ResourceRequestObserver>().data());
qScriptRegisterMetaType(scriptEngine.data(), OverlayIDtoScriptValue, OverlayIDfromScriptValue); qScriptRegisterMetaType(scriptEngine.data(), OverlayIDtoScriptValue, OverlayIDfromScriptValue);
@ -7209,7 +7213,8 @@ void Application::addAssetToWorldFromURL(QString url) {
addAssetToWorldInfo(filename, "Downloading model file " + filename + "."); addAssetToWorldInfo(filename, "Downloading model file " + filename + ".");
auto request = DependencyManager::get<ResourceManager>()->createResourceRequest(nullptr, QUrl(url)); auto request = DependencyManager::get<ResourceManager>()->createResourceRequest(
nullptr, QUrl(url), true, -1, "Application::addAssetToWorldFromURL");
connect(request, &ResourceRequest::finished, this, &Application::addAssetToWorldFromURLRequestFinished); connect(request, &ResourceRequest::finished, this, &Application::addAssetToWorldFromURLRequestFinished);
request->send(); request->send();
} }

View file

@ -342,7 +342,7 @@ public slots:
QVector<EntityItemID> pasteEntities(float x, float y, float z); QVector<EntityItemID> pasteEntities(float x, float y, float z);
bool exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs, const glm::vec3* givenOffset = nullptr); bool exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs, const glm::vec3* givenOffset = nullptr);
bool exportEntities(const QString& filename, float x, float y, float z, float scale); bool exportEntities(const QString& filename, float x, float y, float z, float scale);
bool importEntities(const QString& url); bool importEntities(const QString& url, const bool isObservable = true, const qint64 callerId = -1);
void updateThreadPoolCount() const; void updateThreadPoolCount() const;
void updateSystemTabletMode(); void updateSystemTabletMode();
void goToErrorDomainURL(QUrl errorDomainURL); void goToErrorDomainURL(QUrl errorDomainURL);

View file

@ -53,7 +53,8 @@ void ATPAssetMigrator::loadEntityServerFile() {
auto migrateResources = [=](QUrl migrationURL, QJsonValueRef jsonValue, bool isModelURL) { auto migrateResources = [=](QUrl migrationURL, QJsonValueRef jsonValue, bool isModelURL) {
auto request = auto request =
DependencyManager::get<ResourceManager>()->createResourceRequest(this, migrationURL); DependencyManager::get<ResourceManager>()->createResourceRequest(
this, migrationURL, true, -1, "ATPAssetMigrator::loadEntityServerFile");
if (request) { if (request) {
qCDebug(asset_migrator) << "Requesting" << migrationURL << "for ATP asset migration"; qCDebug(asset_migrator) << "Requesting" << migrationURL << "for ATP asset migration";

View file

@ -26,6 +26,7 @@
#include <AccountManager.h> #include <AccountManager.h>
#include <AddressManager.h> #include <AddressManager.h>
#include <AudioClient.h> #include <AudioClient.h>
#include <ClientTraitsHandler.h>
#include <display-plugins/DisplayPlugin.h> #include <display-plugins/DisplayPlugin.h>
#include <FSTReader.h> #include <FSTReader.h>
#include <GeometryUtil.h> #include <GeometryUtil.h>
@ -463,10 +464,74 @@ void MyAvatar::reset(bool andRecenter, bool andReload, bool andHead) {
} }
} }
void MyAvatar::updateSitStandState(float newHeightReading, float dt) {
const float STANDING_HEIGHT_MULTIPLE = 1.2f;
const float SITTING_HEIGHT_MULTIPLE = 0.833f;
const float SITTING_TIMEOUT = 4.0f; // 4 seconds
const float STANDING_TIMEOUT = 0.3333f; // 1/3 second
const float SITTING_UPPER_BOUND = 1.52f;
if (!getIsSitStandStateLocked()) {
if (!getIsAway() && qApp->isHMDMode()) {
if (getIsInSittingState()) {
if (newHeightReading > (STANDING_HEIGHT_MULTIPLE * _tippingPoint)) {
// if we recenter upwards then no longer in sitting state
_sitStandStateTimer += dt;
if (_sitStandStateTimer > STANDING_TIMEOUT) {
_averageUserHeightSensorSpace = newHeightReading;
_tippingPoint = newHeightReading;
setIsInSittingState(false);
}
} else if (newHeightReading < (SITTING_HEIGHT_MULTIPLE * _tippingPoint)) {
// if we are mis labelled as sitting but we are standing in the real world this will
// make sure that a real sit is still recognized so we won't be stuck in sitting unable to change state
_sitStandStateTimer += dt;
if (_sitStandStateTimer > SITTING_TIMEOUT) {
_averageUserHeightSensorSpace = newHeightReading;
_tippingPoint = newHeightReading;
// here we stay in sit state but reset the average height
setIsInSittingState(true);
}
} else {
// sanity check if average height greater than 5ft they are not sitting(or get off your dangerous barstool please)
if (_averageUserHeightSensorSpace > SITTING_UPPER_BOUND) {
setIsInSittingState(false);
} else {
// tipping point is average height when sitting.
_tippingPoint = _averageUserHeightSensorSpace;
_sitStandStateTimer = 0.0f;
}
}
} else {
// in the standing state
if (newHeightReading < (SITTING_HEIGHT_MULTIPLE * _tippingPoint)) {
_sitStandStateTimer += dt;
if (_sitStandStateTimer > SITTING_TIMEOUT) {
_averageUserHeightSensorSpace = newHeightReading;
_tippingPoint = newHeightReading;
setIsInSittingState(true);
}
} else {
// use the mode height for the tipping point when we are standing.
_tippingPoint = getCurrentStandingHeight();
_sitStandStateTimer = 0.0f;
}
}
} else {
//if you are away then reset the average and set state to standing.
_averageUserHeightSensorSpace = _userHeight.get();
_tippingPoint = _userHeight.get();
setIsInSittingState(false);
}
}
}
void MyAvatar::update(float deltaTime) { void MyAvatar::update(float deltaTime) {
// update moving average of HMD facing in xz plane. // update moving average of HMD facing in xz plane.
const float HMD_FACING_TIMESCALE = getRotationRecenterFilterLength(); const float HMD_FACING_TIMESCALE = getRotationRecenterFilterLength();
const float PERCENTAGE_WEIGHT_HEAD_VS_SHOULDERS_AZIMUTH = 0.0f; // 100 percent shoulders const float PERCENTAGE_WEIGHT_HEAD_VS_SHOULDERS_AZIMUTH = 0.0f; // 100 percent shoulders
const float COSINE_THIRTY_DEGREES = 0.866f;
const float SQUATTY_TIMEOUT = 30.0f; // 30 seconds
const float HEIGHT_FILTER_COEFFICIENT = 0.01f;
float tau = deltaTime / HMD_FACING_TIMESCALE; float tau = deltaTime / HMD_FACING_TIMESCALE;
setHipToHandController(computeHandAzimuth()); setHipToHandController(computeHandAzimuth());
@ -493,11 +558,36 @@ void MyAvatar::update(float deltaTime) {
_smoothOrientationTimer += deltaTime; _smoothOrientationTimer += deltaTime;
} }
float newHeightReading = getControllerPoseInAvatarFrame(controller::Action::HEAD).getTranslation().y; controller::Pose newHeightReading = getControllerPoseInSensorFrame(controller::Action::HEAD);
int newHeightReadingInCentimeters = glm::floor(newHeightReading * CENTIMETERS_PER_METER); if (newHeightReading.isValid()) {
int newHeightReadingInCentimeters = glm::floor(newHeightReading.getTranslation().y * CENTIMETERS_PER_METER);
_averageUserHeightSensorSpace = lerp(_averageUserHeightSensorSpace, newHeightReading.getTranslation().y, HEIGHT_FILTER_COEFFICIENT);
_recentModeReadings.insert(newHeightReadingInCentimeters); _recentModeReadings.insert(newHeightReadingInCentimeters);
setCurrentStandingHeight(computeStandingHeightMode(getControllerPoseInAvatarFrame(controller::Action::HEAD))); setCurrentStandingHeight(computeStandingHeightMode(newHeightReading));
setAverageHeadRotation(computeAverageHeadRotation(getControllerPoseInAvatarFrame(controller::Action::HEAD))); setAverageHeadRotation(computeAverageHeadRotation(getControllerPoseInAvatarFrame(controller::Action::HEAD)));
}
// if the spine is straight and the head is below the default position by 5 cm then increment squatty count.
const float SQUAT_THRESHOLD = 0.05f;
glm::vec3 headDefaultPositionAvatarSpace = getAbsoluteDefaultJointTranslationInObjectFrame(getJointIndex("Head"));
glm::quat spine2OrientationAvatarSpace = getAbsoluteJointRotationInObjectFrame(getJointIndex("Spine2"));
glm::vec3 upSpine2 = spine2OrientationAvatarSpace * glm::vec3(0.0f, 1.0f, 0.0f);
if (glm::length(upSpine2) > 0.0f) {
upSpine2 = glm::normalize(upSpine2);
}
float angleSpine2 = glm::dot(upSpine2, glm::vec3(0.0f, 1.0f, 0.0f));
if (getControllerPoseInAvatarFrame(controller::Action::HEAD).getTranslation().y < (headDefaultPositionAvatarSpace.y - SQUAT_THRESHOLD) && (angleSpine2 > COSINE_THIRTY_DEGREES)) {
_squatTimer += deltaTime;
if (_squatTimer > SQUATTY_TIMEOUT) {
_squatTimer = 0.0f;
_follow._squatDetected = true;
}
} else {
_squatTimer = 0.0f;
}
// put update sit stand state counts here
updateSitStandState(newHeightReading.getTranslation().y, deltaTime);
if (_drawAverageFacingEnabled) { if (_drawAverageFacingEnabled) {
auto sensorHeadPose = getControllerPoseInSensorFrame(controller::Action::HEAD); auto sensorHeadPose = getControllerPoseInSensorFrame(controller::Action::HEAD);
@ -3557,12 +3647,9 @@ glm::vec3 MyAvatar::computeCounterBalance() {
glm::vec3 counterBalancedCg = (1.0f / DEFAULT_AVATAR_HIPS_MASS) * counterBalancedForHead; glm::vec3 counterBalancedCg = (1.0f / DEFAULT_AVATAR_HIPS_MASS) * counterBalancedForHead;
// find the height of the hips // find the height of the hips
const float UPPER_LEG_FRACTION = 0.3333f;
glm::vec3 xzDiff((cgHeadMass.position.x - counterBalancedCg.x), 0.0f, (cgHeadMass.position.z - counterBalancedCg.z)); glm::vec3 xzDiff((cgHeadMass.position.x - counterBalancedCg.x), 0.0f, (cgHeadMass.position.z - counterBalancedCg.z));
float headMinusHipXz = glm::length(xzDiff); float headMinusHipXz = glm::length(xzDiff);
float headHipDefault = glm::length(tposeHead - tposeHips); float headHipDefault = glm::length(tposeHead - tposeHips);
float hipFootDefault = tposeHips.y - tposeRightFoot.y;
float sitSquatThreshold = tposeHips.y - (UPPER_LEG_FRACTION * hipFootDefault);
float hipHeight = 0.0f; float hipHeight = 0.0f;
if (headHipDefault > headMinusHipXz) { if (headHipDefault > headMinusHipXz) {
hipHeight = sqrtf((headHipDefault * headHipDefault) - (headMinusHipXz * headMinusHipXz)); hipHeight = sqrtf((headHipDefault * headHipDefault) - (headMinusHipXz * headMinusHipXz));
@ -3574,10 +3661,6 @@ glm::vec3 MyAvatar::computeCounterBalance() {
if (counterBalancedCg.y > (tposeHips.y + 0.05f)) { if (counterBalancedCg.y > (tposeHips.y + 0.05f)) {
// if the height is higher than default hips, clamp to default hips // if the height is higher than default hips, clamp to default hips
counterBalancedCg.y = tposeHips.y + 0.05f; counterBalancedCg.y = tposeHips.y + 0.05f;
} else if (counterBalancedCg.y < sitSquatThreshold) {
//do a height reset
setResetMode(true);
_follow.activate(FollowHelper::Vertical);
} }
return counterBalancedCg; return counterBalancedCg;
} }
@ -3818,6 +3901,18 @@ bool MyAvatar::getIsInWalkingState() const {
return _isInWalkingState; return _isInWalkingState;
} }
bool MyAvatar::getIsInSittingState() const {
return _isInSittingState.get();
}
MyAvatar::SitStandModelType MyAvatar::getUserRecenterModel() const {
return _userRecenterModel.get();
}
bool MyAvatar::getIsSitStandStateLocked() const {
return _lockSitStandState.get();
}
float MyAvatar::getWalkSpeed() const { float MyAvatar::getWalkSpeed() const {
return _walkSpeed.get() * _walkSpeedScalar; return _walkSpeed.get() * _walkSpeedScalar;
} }
@ -3838,6 +3933,61 @@ void MyAvatar::setIsInWalkingState(bool isWalking) {
_isInWalkingState = isWalking; _isInWalkingState = isWalking;
} }
void MyAvatar::setIsInSittingState(bool isSitting) {
_sitStandStateTimer = 0.0f;
_squatTimer = 0.0f;
// on reset height we need the count to be more than one in case the user sits and stands up quickly.
_isInSittingState.set(isSitting);
setResetMode(true);
if (isSitting) {
setCenterOfGravityModelEnabled(false);
} else {
setCenterOfGravityModelEnabled(true);
}
setSitStandStateChange(true);
}
void MyAvatar::setUserRecenterModel(MyAvatar::SitStandModelType modelName) {
_userRecenterModel.set(modelName);
switch (modelName) {
case MyAvatar::SitStandModelType::ForceSit:
setHMDLeanRecenterEnabled(true);
setIsInSittingState(true);
setIsSitStandStateLocked(true);
break;
case MyAvatar::SitStandModelType::ForceStand:
setHMDLeanRecenterEnabled(true);
setIsInSittingState(false);
setIsSitStandStateLocked(true);
break;
case MyAvatar::SitStandModelType::Auto:
default:
setHMDLeanRecenterEnabled(true);
setIsInSittingState(false);
setIsSitStandStateLocked(false);
break;
case MyAvatar::SitStandModelType::DisableHMDLean:
setHMDLeanRecenterEnabled(false);
setIsInSittingState(false);
setIsSitStandStateLocked(false);
break;
}
}
void MyAvatar::setIsSitStandStateLocked(bool isLocked) {
_lockSitStandState.set(isLocked);
_sitStandStateTimer = 0.0f;
_squatTimer = 0.0f;
_averageUserHeightSensorSpace = _userHeight.get();
_tippingPoint = _userHeight.get();
if (!isLocked) {
// always start the auto transition mode in standing state.
setIsInSittingState(false);
}
}
void MyAvatar::setWalkSpeed(float value) { void MyAvatar::setWalkSpeed(float value) {
_walkSpeed.set(value); _walkSpeed.set(value);
} }
@ -3854,6 +4004,14 @@ float MyAvatar::getSprintSpeed() const {
return _sprintSpeed.get(); return _sprintSpeed.get();
} }
void MyAvatar::setSitStandStateChange(bool stateChanged) {
_sitStandStateChange = stateChanged;
}
float MyAvatar::getSitStandStateChange() const {
return _sitStandStateChange;
}
QVector<QString> MyAvatar::getScriptUrls() { QVector<QString> MyAvatar::getScriptUrls() {
QVector<QString> scripts = _skeletonModel->isLoaded() ? _skeletonModel->getFBXGeometry().scripts : QVector<QString>(); QVector<QString> scripts = _skeletonModel->isLoaded() ? _skeletonModel->getFBXGeometry().scripts : QVector<QString>();
return scripts; return scripts;
@ -3997,6 +4155,7 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar,
// x axis of currentBodyMatrix in world space. // x axis of currentBodyMatrix in world space.
glm::vec3 right = glm::normalize(glm::vec3(currentBodyMatrix[0][0], currentBodyMatrix[1][0], currentBodyMatrix[2][0])); glm::vec3 right = glm::normalize(glm::vec3(currentBodyMatrix[0][0], currentBodyMatrix[1][0], currentBodyMatrix[2][0]));
glm::vec3 offset = extractTranslation(desiredBodyMatrix) - extractTranslation(currentBodyMatrix); glm::vec3 offset = extractTranslation(desiredBodyMatrix) - extractTranslation(currentBodyMatrix);
controller::Pose currentHeadPose = myAvatar.getControllerPoseInAvatarFrame(controller::Action::HEAD);
float forwardLeanAmount = glm::dot(forward, offset); float forwardLeanAmount = glm::dot(forward, offset);
float lateralLeanAmount = glm::dot(right, offset); float lateralLeanAmount = glm::dot(right, offset);
@ -4005,14 +4164,19 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar,
const float MAX_FORWARD_LEAN = 0.15f; const float MAX_FORWARD_LEAN = 0.15f;
const float MAX_BACKWARD_LEAN = 0.1f; const float MAX_BACKWARD_LEAN = 0.1f;
bool stepDetected = false;
if (forwardLeanAmount > 0 && forwardLeanAmount > MAX_FORWARD_LEAN) { if (myAvatar.getIsInSittingState()) {
return true; if (!withinBaseOfSupport(currentHeadPose)) {
} else if (forwardLeanAmount < 0 && forwardLeanAmount < -MAX_BACKWARD_LEAN) { stepDetected = true;
return true;
} }
} else if (forwardLeanAmount > 0 && forwardLeanAmount > MAX_FORWARD_LEAN) {
return fabs(lateralLeanAmount) > MAX_LATERAL_LEAN; stepDetected = true;
} else if (forwardLeanAmount < 0 && forwardLeanAmount < -MAX_BACKWARD_LEAN) {
stepDetected = true;
} else {
stepDetected = fabs(lateralLeanAmount) > MAX_LATERAL_LEAN;
}
return stepDetected;
} }
bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) const { bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) const {
@ -4021,6 +4185,7 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) cons
controller::Pose currentHeadPose = myAvatar.getControllerPoseInAvatarFrame(controller::Action::HEAD); controller::Pose currentHeadPose = myAvatar.getControllerPoseInAvatarFrame(controller::Action::HEAD);
controller::Pose currentLeftHandPose = myAvatar.getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND); controller::Pose currentLeftHandPose = myAvatar.getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND);
controller::Pose currentRightHandPose = myAvatar.getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND); controller::Pose currentRightHandPose = myAvatar.getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND);
controller::Pose currentHeadSensorPose = myAvatar.getControllerPoseInSensorFrame(controller::Action::HEAD);
bool stepDetected = false; bool stepDetected = false;
float myScale = myAvatar.getAvatarScale(); float myScale = myAvatar.getAvatarScale();
@ -4030,7 +4195,7 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) cons
} else { } else {
if (!withinBaseOfSupport(currentHeadPose) && if (!withinBaseOfSupport(currentHeadPose) &&
headAngularVelocityBelowThreshold(currentHeadPose) && headAngularVelocityBelowThreshold(currentHeadPose) &&
isWithinThresholdHeightMode(currentHeadPose, myAvatar.getCurrentStandingHeight(), myScale) && isWithinThresholdHeightMode(currentHeadSensorPose, myAvatar.getCurrentStandingHeight(), myScale) &&
handDirectionMatchesHeadDirection(currentLeftHandPose, currentRightHandPose, currentHeadPose) && handDirectionMatchesHeadDirection(currentLeftHandPose, currentRightHandPose, currentHeadPose) &&
handAngularVelocityBelowThreshold(currentLeftHandPose, currentRightHandPose) && handAngularVelocityBelowThreshold(currentLeftHandPose, currentRightHandPose) &&
headVelocityGreaterThanThreshold(currentHeadPose) && headVelocityGreaterThanThreshold(currentHeadPose) &&
@ -4046,6 +4211,7 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) cons
glm::vec3 currentHeadPosition = currentHeadPose.getTranslation(); glm::vec3 currentHeadPosition = currentHeadPose.getTranslation();
float anatomicalHeadToHipsDistance = glm::length(defaultHeadPosition - defaultHipsPosition); float anatomicalHeadToHipsDistance = glm::length(defaultHeadPosition - defaultHipsPosition);
if (!isActive(Horizontal) && if (!isActive(Horizontal) &&
(!isActive(Vertical)) &&
(glm::length(currentHeadPosition - defaultHipsPosition) > (anatomicalHeadToHipsDistance + (DEFAULT_AVATAR_SPINE_STRETCH_LIMIT * anatomicalHeadToHipsDistance)))) { (glm::length(currentHeadPosition - defaultHipsPosition) > (anatomicalHeadToHipsDistance + (DEFAULT_AVATAR_SPINE_STRETCH_LIMIT * anatomicalHeadToHipsDistance)))) {
myAvatar.setResetMode(true); myAvatar.setResetMode(true);
stepDetected = true; stepDetected = true;
@ -4061,10 +4227,32 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) cons
bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const { bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
const float CYLINDER_TOP = 0.1f; const float CYLINDER_TOP = 0.1f;
const float CYLINDER_BOTTOM = -1.5f; const float CYLINDER_BOTTOM = -1.5f;
const float SITTING_BOTTOM = -0.02f;
glm::vec3 offset = extractTranslation(desiredBodyMatrix) - extractTranslation(currentBodyMatrix); glm::vec3 offset = extractTranslation(desiredBodyMatrix) - extractTranslation(currentBodyMatrix);
bool returnValue = false;
return (offset.y > CYLINDER_TOP) || (offset.y < CYLINDER_BOTTOM); if (myAvatar.getSitStandStateChange()) {
returnValue = true;
} else {
if (myAvatar.getIsInSittingState()) {
if (myAvatar.getIsSitStandStateLocked()) {
returnValue = (offset.y > CYLINDER_TOP);
}
if (offset.y < SITTING_BOTTOM) {
// we recenter more easily when in sitting state.
returnValue = true;
}
} else {
// in the standing state
returnValue = (offset.y > CYLINDER_TOP) || (offset.y < CYLINDER_BOTTOM);
// finally check for squats in standing
if (_squatDetected) {
returnValue = true;
}
}
}
return returnValue;
} }
void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix,
@ -4085,9 +4273,10 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat
} }
} }
} else { } else {
// center of gravity model is not enabled
if (!isActive(Horizontal) && (shouldActivateHorizontal(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) { if (!isActive(Horizontal) && (shouldActivateHorizontal(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) {
activate(Horizontal); activate(Horizontal);
if (myAvatar.getEnableStepResetRotation()) { if (myAvatar.getEnableStepResetRotation() && !myAvatar.getIsInSittingState()) {
activate(Rotation); activate(Rotation);
myAvatar.setHeadControllerFacingMovingAverage(myAvatar.getHeadControllerFacing()); myAvatar.setHeadControllerFacingMovingAverage(myAvatar.getHeadControllerFacing());
} }
@ -4095,6 +4284,9 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat
} }
if (!isActive(Vertical) && (shouldActivateVertical(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) { if (!isActive(Vertical) && (shouldActivateVertical(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) {
activate(Vertical); activate(Vertical);
if (_squatDetected) {
_squatDetected = false;
}
} }
} else { } else {
if (!isActive(Rotation) && getForceActivateRotation()) { if (!isActive(Rotation) && getForceActivateRotation()) {
@ -4144,7 +4336,7 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat
myAvatar.getCharacterController()->setFollowParameters(followWorldPose, getMaxTimeRemaining()); myAvatar.getCharacterController()->setFollowParameters(followWorldPose, getMaxTimeRemaining());
} }
glm::mat4 MyAvatar::FollowHelper::postPhysicsUpdate(const MyAvatar& myAvatar, const glm::mat4& currentBodyMatrix) { glm::mat4 MyAvatar::FollowHelper::postPhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& currentBodyMatrix) {
if (isActive()) { if (isActive()) {
float dt = myAvatar.getCharacterController()->getFollowTime(); float dt = myAvatar.getCharacterController()->getFollowTime();
decrementTimeRemaining(dt); decrementTimeRemaining(dt);
@ -4161,6 +4353,11 @@ glm::mat4 MyAvatar::FollowHelper::postPhysicsUpdate(const MyAvatar& myAvatar, co
glm::mat4 newBodyMat = createMatFromQuatAndPos(sensorAngularDisplacement * glmExtractRotation(currentBodyMatrix), glm::mat4 newBodyMat = createMatFromQuatAndPos(sensorAngularDisplacement * glmExtractRotation(currentBodyMatrix),
sensorLinearDisplacement + extractTranslation(currentBodyMatrix)); sensorLinearDisplacement + extractTranslation(currentBodyMatrix));
if (myAvatar.getSitStandStateChange()) {
myAvatar.setSitStandStateChange(false);
deactivate(Vertical);
setTranslation(newBodyMat, extractTranslation(myAvatar.deriveBodyFromHMDSensor()));
}
return newBodyMat; return newBodyMat;
} else { } else {
return currentBodyMatrix; return currentBodyMatrix;

View file

@ -21,7 +21,6 @@
#include <AvatarConstants.h> #include <AvatarConstants.h>
#include <avatars-renderer/Avatar.h> #include <avatars-renderer/Avatar.h>
#include <avatars-renderer/ScriptAvatar.h> #include <avatars-renderer/ScriptAvatar.h>
#include <ClientTraitsHandler.h>
#include <controllers/Pose.h> #include <controllers/Pose.h>
#include <controllers/Actions.h> #include <controllers/Actions.h>
#include <EntityItem.h> #include <EntityItem.h>
@ -142,6 +141,8 @@ class MyAvatar : public Avatar {
* @property {number} walkSpeed * @property {number} walkSpeed
* @property {number} walkBackwardSpeed * @property {number} walkBackwardSpeed
* @property {number} sprintSpeed * @property {number} sprintSpeed
* @property {number} isInSittingState
* @property {number} userRecenterModel
* *
* @property {Vec3} skeletonOffset - Can be used to apply a translation offset between the avatar's position and the * @property {Vec3} skeletonOffset - Can be used to apply a translation offset between the avatar's position and the
* registration point of the 3D model. * registration point of the 3D model.
@ -242,6 +243,9 @@ class MyAvatar : public Avatar {
Q_PROPERTY(float walkSpeed READ getWalkSpeed WRITE setWalkSpeed); Q_PROPERTY(float walkSpeed READ getWalkSpeed WRITE setWalkSpeed);
Q_PROPERTY(float walkBackwardSpeed READ getWalkBackwardSpeed WRITE setWalkBackwardSpeed); Q_PROPERTY(float walkBackwardSpeed READ getWalkBackwardSpeed WRITE setWalkBackwardSpeed);
Q_PROPERTY(float sprintSpeed READ getSprintSpeed WRITE setSprintSpeed); Q_PROPERTY(float sprintSpeed READ getSprintSpeed WRITE setSprintSpeed);
Q_PROPERTY(bool isInSittingState READ getIsInSittingState WRITE setIsInSittingState);
Q_PROPERTY(MyAvatar::SitStandModelType userRecenterModel READ getUserRecenterModel WRITE setUserRecenterModel);
Q_PROPERTY(bool isSitStandStateLocked READ getIsSitStandStateLocked WRITE setIsSitStandStateLocked);
const QString DOMINANT_LEFT_HAND = "left"; const QString DOMINANT_LEFT_HAND = "left";
const QString DOMINANT_RIGHT_HAND = "right"; const QString DOMINANT_RIGHT_HAND = "right";
@ -262,6 +266,15 @@ public:
}; };
Q_ENUM(DriveKeys) Q_ENUM(DriveKeys)
enum SitStandModelType {
ForceSit = 0,
ForceStand,
Auto,
DisableHMDLean,
NumSitStandTypes
};
Q_ENUM(SitStandModelType)
explicit MyAvatar(QThread* thread); explicit MyAvatar(QThread* thread);
virtual ~MyAvatar(); virtual ~MyAvatar();
@ -1121,12 +1134,21 @@ public:
void setIsInWalkingState(bool isWalking); void setIsInWalkingState(bool isWalking);
bool getIsInWalkingState() const; bool getIsInWalkingState() const;
void setIsInSittingState(bool isSitting);
bool getIsInSittingState() const;
void setUserRecenterModel(MyAvatar::SitStandModelType modelName);
MyAvatar::SitStandModelType getUserRecenterModel() const;
void setIsSitStandStateLocked(bool isLocked);
bool getIsSitStandStateLocked() const;
void setWalkSpeed(float value); void setWalkSpeed(float value);
float getWalkSpeed() const; float getWalkSpeed() const;
void setWalkBackwardSpeed(float value); void setWalkBackwardSpeed(float value);
float getWalkBackwardSpeed() const; float getWalkBackwardSpeed() const;
void setSprintSpeed(float value); void setSprintSpeed(float value);
float getSprintSpeed() const; float getSprintSpeed() const;
void setSitStandStateChange(bool stateChanged);
float getSitStandStateChange() const;
void updateSitStandState(float newHeightReading, float dt);
QVector<QString> getScriptUrls(); QVector<QString> getScriptUrls();
@ -1532,6 +1554,7 @@ signals:
*/ */
void disableHandTouchForIDChanged(const QUuid& entityID, bool disable); void disableHandTouchForIDChanged(const QUuid& entityID, bool disable);
private slots: private slots:
void leaveDomain(); void leaveDomain();
void updateCollisionCapsuleCache(); void updateCollisionCapsuleCache();
@ -1742,7 +1765,7 @@ private:
bool shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const; bool shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const;
bool shouldActivateHorizontalCG(MyAvatar& myAvatar) const; bool shouldActivateHorizontalCG(MyAvatar& myAvatar) const;
void prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& bodySensorMatrix, const glm::mat4& currentBodyMatrix, bool hasDriveInput); void prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& bodySensorMatrix, const glm::mat4& currentBodyMatrix, bool hasDriveInput);
glm::mat4 postPhysicsUpdate(const MyAvatar& myAvatar, const glm::mat4& currentBodyMatrix); glm::mat4 postPhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& currentBodyMatrix);
bool getForceActivateRotation() const; bool getForceActivateRotation() const;
void setForceActivateRotation(bool val); void setForceActivateRotation(bool val);
bool getForceActivateVertical() const; bool getForceActivateVertical() const;
@ -1751,6 +1774,7 @@ private:
void setForceActivateHorizontal(bool val); void setForceActivateHorizontal(bool val);
bool getToggleHipsFollowing() const; bool getToggleHipsFollowing() const;
void setToggleHipsFollowing(bool followHead); void setToggleHipsFollowing(bool followHead);
bool _squatDetected { false };
std::atomic<bool> _forceActivateRotation { false }; std::atomic<bool> _forceActivateRotation { false };
std::atomic<bool> _forceActivateVertical { false }; std::atomic<bool> _forceActivateVertical { false };
std::atomic<bool> _forceActivateHorizontal { false }; std::atomic<bool> _forceActivateHorizontal { false };
@ -1820,10 +1844,13 @@ private:
std::mutex _pinnedJointsMutex; std::mutex _pinnedJointsMutex;
std::vector<int> _pinnedJoints; std::vector<int> _pinnedJoints;
void updateChildCauterization(SpatiallyNestablePointer object, bool cauterize);
// height of user in sensor space, when standing erect. // height of user in sensor space, when standing erect.
ThreadSafeValueCache<float> _userHeight { DEFAULT_AVATAR_HEIGHT }; ThreadSafeValueCache<float> _userHeight { DEFAULT_AVATAR_HEIGHT };
float _averageUserHeightSensorSpace { _userHeight.get() };
void updateChildCauterization(SpatiallyNestablePointer object, bool cauterize); bool _sitStandStateChange { false };
ThreadSafeValueCache<bool> _lockSitStandState { false };
// max unscaled forward movement speed // max unscaled forward movement speed
ThreadSafeValueCache<float> _walkSpeed { DEFAULT_AVATAR_MAX_WALKING_SPEED }; ThreadSafeValueCache<float> _walkSpeed { DEFAULT_AVATAR_MAX_WALKING_SPEED };
@ -1831,6 +1858,11 @@ private:
ThreadSafeValueCache<float> _sprintSpeed { AVATAR_SPRINT_SPEED_SCALAR }; ThreadSafeValueCache<float> _sprintSpeed { AVATAR_SPRINT_SPEED_SCALAR };
float _walkSpeedScalar { AVATAR_WALK_SPEED_SCALAR }; float _walkSpeedScalar { AVATAR_WALK_SPEED_SCALAR };
bool _isInWalkingState { false }; bool _isInWalkingState { false };
ThreadSafeValueCache<bool> _isInSittingState { false };
ThreadSafeValueCache<MyAvatar::SitStandModelType> _userRecenterModel { MyAvatar::SitStandModelType::Auto };
float _sitStandStateTimer { 0.0f };
float _squatTimer { 0.0f };
float _tippingPoint { _userHeight.get() };
// load avatar scripts once when rig is ready // load avatar scripts once when rig is ready
bool _shouldLoadScripts { false }; bool _shouldLoadScripts { false };

View file

@ -46,7 +46,7 @@ static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) {
} }
glm::mat4 hipsMat; glm::mat4 hipsMat;
if (myAvatar->getCenterOfGravityModelEnabled() && !isFlying && !(myAvatar->getIsInWalkingState()) && myAvatar->getHMDLeanRecenterEnabled()) { if (myAvatar->getCenterOfGravityModelEnabled() && !isFlying && !(myAvatar->getIsInWalkingState()) && !(myAvatar->getIsInSittingState()) && myAvatar->getHMDLeanRecenterEnabled()) {
// then we use center of gravity model // then we use center of gravity model
hipsMat = myAvatar->deriveBodyUsingCgModel(); hipsMat = myAvatar->deriveBodyUsingCgModel();
} else { } else {
@ -250,6 +250,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
bool headExists = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Head"), currentHeadPose); bool headExists = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Head"), currentHeadPose);
bool hipsExists = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Hips"), currentHipsPose); bool hipsExists = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Hips"), currentHipsPose);
if (spine2Exists && headExists && hipsExists) { if (spine2Exists && headExists && hipsExists) {
AnimPose rigSpaceYaw(myAvatar->getSpine2RotationRigSpace()); AnimPose rigSpaceYaw(myAvatar->getSpine2RotationRigSpace());
glm::vec3 u, v, w; glm::vec3 u, v, w;
glm::vec3 fwd = rigSpaceYaw.rot() * glm::vec3(0.0f, 0.0f, 1.0f); glm::vec3 fwd = rigSpaceYaw.rot() * glm::vec3(0.0f, 0.0f, 1.0f);

View file

@ -315,7 +315,7 @@ QString QmlCommerce::getInstalledApps(const QString& justInstalledAppID) {
return installedAppsFromMarketplace; return installedAppsFromMarketplace;
} }
bool QmlCommerce::installApp(const QString& itemHref) { bool QmlCommerce::installApp(const QString& itemHref, const bool& alsoOpenImmediately) {
if (!QDir(_appsPath).exists()) { if (!QDir(_appsPath).exists()) {
if (!QDir().mkdir(_appsPath)) { if (!QDir().mkdir(_appsPath)) {
qCDebug(commerce) << "Couldn't make _appsPath directory."; qCDebug(commerce) << "Couldn't make _appsPath directory.";
@ -325,7 +325,8 @@ bool QmlCommerce::installApp(const QString& itemHref) {
QUrl appHref(itemHref); QUrl appHref(itemHref);
auto request = DependencyManager::get<ResourceManager>()->createResourceRequest(this, appHref); auto request =
DependencyManager::get<ResourceManager>()->createResourceRequest(this, appHref, true, -1, "QmlCommerce::installApp");
if (!request) { if (!request) {
qCDebug(commerce) << "Couldn't create resource request for app."; qCDebug(commerce) << "Couldn't create resource request for app.";
@ -357,6 +358,9 @@ bool QmlCommerce::installApp(const QString& itemHref) {
QJsonObject appFileJsonObject = appFileJsonDocument.object(); QJsonObject appFileJsonObject = appFileJsonDocument.object();
QString scriptUrl = appFileJsonObject["scriptURL"].toString(); QString scriptUrl = appFileJsonObject["scriptURL"].toString();
// Don't try to re-load (install) a script if it's already running
QStringList runningScripts = DependencyManager::get<ScriptEngines>()->getRunningScripts();
if (!runningScripts.contains(scriptUrl)) {
if ((DependencyManager::get<ScriptEngines>()->loadScript(scriptUrl.trimmed())).isNull()) { if ((DependencyManager::get<ScriptEngines>()->loadScript(scriptUrl.trimmed())).isNull()) {
qCDebug(commerce) << "Couldn't load script."; qCDebug(commerce) << "Couldn't load script.";
return false; return false;
@ -364,6 +368,12 @@ bool QmlCommerce::installApp(const QString& itemHref) {
QFileInfo appFileInfo(appFile); QFileInfo appFileInfo(appFile);
emit appInstalled(appFileInfo.baseName()); emit appInstalled(appFileInfo.baseName());
}
if (alsoOpenImmediately) {
QmlCommerce::openApp(itemHref);
}
return true; return true;
}); });
request->send(); request->send();

View file

@ -88,7 +88,7 @@ protected:
Q_INVOKABLE void replaceContentSet(const QString& itemHref, const QString& certificateID); Q_INVOKABLE void replaceContentSet(const QString& itemHref, const QString& certificateID);
Q_INVOKABLE QString getInstalledApps(const QString& justInstalledAppID = ""); Q_INVOKABLE QString getInstalledApps(const QString& justInstalledAppID = "");
Q_INVOKABLE bool installApp(const QString& appHref); Q_INVOKABLE bool installApp(const QString& appHref, const bool& alsoOpenImmediately = false);
Q_INVOKABLE bool uninstallApp(const QString& appHref); Q_INVOKABLE bool uninstallApp(const QString& appHref);
Q_INVOKABLE bool openApp(const QString& appHref); Q_INVOKABLE bool openApp(const QString& appHref);

View file

@ -46,11 +46,17 @@ bool ClipboardScriptingInterface::exportEntities(const QString& filename, float
return retVal; return retVal;
} }
bool ClipboardScriptingInterface::importEntities(const QString& filename) { bool ClipboardScriptingInterface::importEntities(
const QString& filename,
const bool isObservable,
const qint64 callerId
) {
bool retVal; bool retVal;
BLOCKING_INVOKE_METHOD(qApp, "importEntities", BLOCKING_INVOKE_METHOD(qApp, "importEntities",
Q_RETURN_ARG(bool, retVal), Q_RETURN_ARG(bool, retVal),
Q_ARG(const QString&, filename)); Q_ARG(const QString&, filename),
Q_ARG(const bool, isObservable),
Q_ARG(const qint64, callerId));
return retVal; return retVal;
} }

View file

@ -50,9 +50,11 @@ public:
* You can generate a JSON file using {@link Clipboard.exportEntities}. * You can generate a JSON file using {@link Clipboard.exportEntities}.
* @function Clipboard.importEntities * @function Clipboard.importEntities
* @param {string} filename Path and name of file to import. * @param {string} filename Path and name of file to import.
* @param {boolean} does the ResourceRequestObserver observe this request?
* @param {number} optional internal id of object causing this import.
* @returns {boolean} <code>true</code> if the import was successful, otherwise <code>false</code>. * @returns {boolean} <code>true</code> if the import was successful, otherwise <code>false</code>.
*/ */
Q_INVOKABLE bool importEntities(const QString& filename); Q_INVOKABLE bool importEntities(const QString& filename, const bool isObservable = true, const qint64 callerId = -1);
/**jsdoc /**jsdoc
* Export the entities specified to a JSON file. * Export the entities specified to a JSON file.

View file

@ -259,6 +259,39 @@ void setupPreferences() {
auto preference = new CheckPreference(VR_MOVEMENT, "Show room boundaries while teleporting", getter, setter); auto preference = new CheckPreference(VR_MOVEMENT, "Show room boundaries while teleporting", getter, setter);
preferences->addPreference(preference); preferences->addPreference(preference);
} }
{
auto getter = [myAvatar]()->int {
switch (myAvatar->getUserRecenterModel()) {
case MyAvatar::SitStandModelType::Auto:
default:
return 0;
case MyAvatar::SitStandModelType::ForceSit:
return 1;
case MyAvatar::SitStandModelType::DisableHMDLean:
return 2;
}
};
auto setter = [myAvatar](int value) {
switch (value) {
case 0:
default:
myAvatar->setUserRecenterModel(MyAvatar::SitStandModelType::Auto);
break;
case 1:
myAvatar->setUserRecenterModel(MyAvatar::SitStandModelType::ForceSit);
break;
case 2:
myAvatar->setUserRecenterModel(MyAvatar::SitStandModelType::DisableHMDLean);
break;
}
};
auto preference = new RadioButtonsPreference(VR_MOVEMENT, "Auto / Force Sit / Disable Recenter", getter, setter);
QStringList items;
items << "Auto - turns on avatar leaning when standing in real world" << "Seated - disables all avatar leaning while sitting in real world" << "Disabled - allows avatar sitting on the floor [Experimental]";
preference->setHeading("Avatar leaning behavior");
preference->setItems(items);
preferences->addPreference(preference);
}
{ {
auto getter = [=]()->float { return myAvatar->getUserHeight(); }; auto getter = [=]()->float { return myAvatar->getUserHeight(); };
auto setter = [=](float value) { myAvatar->setUserHeight(value); }; auto setter = [=](float value) { myAvatar->setUserHeight(value); };

View file

@ -59,6 +59,7 @@
#include "raypick/PointerScriptingInterface.h" #include "raypick/PointerScriptingInterface.h"
#include <display-plugins/CompositorHelper.h> #include <display-plugins/CompositorHelper.h>
#include "AboutUtil.h" #include "AboutUtil.h"
#include "ResourceRequestObserver.h"
static int MAX_WINDOW_SIZE = 4096; static int MAX_WINDOW_SIZE = 4096;
static const float METERS_TO_INCHES = 39.3701f; static const float METERS_TO_INCHES = 39.3701f;
@ -269,6 +270,7 @@ void Web3DOverlay::setupQmlSurface(bool isTablet) {
_webSurface->getSurfaceContext()->setContextProperty("Window", DependencyManager::get<WindowScriptingInterface>().data()); _webSurface->getSurfaceContext()->setContextProperty("Window", DependencyManager::get<WindowScriptingInterface>().data());
_webSurface->getSurfaceContext()->setContextProperty("Reticle", qApp->getApplicationCompositor().getReticleInterface()); _webSurface->getSurfaceContext()->setContextProperty("Reticle", qApp->getApplicationCompositor().getReticleInterface());
_webSurface->getSurfaceContext()->setContextProperty("HiFiAbout", AboutUtil::getInstance()); _webSurface->getSurfaceContext()->setContextProperty("HiFiAbout", AboutUtil::getInstance());
_webSurface->getSurfaceContext()->setContextProperty("ResourceRequestObserver", DependencyManager::get<ResourceRequestObserver>().data());
// Override min fps for tablet UI, for silky smooth scrolling // Override min fps for tablet UI, for silky smooth scrolling
setMaxFPS(90); setMaxFPS(90);

View file

@ -1731,6 +1731,7 @@ void Avatar::getCapsule(glm::vec3& start, glm::vec3& end, float& radius) {
glm::vec3 Avatar::getWorldFeetPosition() { glm::vec3 Avatar::getWorldFeetPosition() {
ShapeInfo shapeInfo; ShapeInfo shapeInfo;
computeShapeInfo(shapeInfo); computeShapeInfo(shapeInfo);
glm::vec3 halfExtents = shapeInfo.getHalfExtents(); // x = radius, y = halfHeight glm::vec3 halfExtents = shapeInfo.getHalfExtents(); // x = radius, y = halfHeight
glm::vec3 localFeet(0.0f, shapeInfo.getOffset().y - halfExtents.y - halfExtents.x, 0.0f); glm::vec3 localFeet(0.0f, shapeInfo.getOffset().y - halfExtents.y - halfExtents.x, 0.0f);

View file

@ -44,6 +44,7 @@
#include "AvatarLogging.h" #include "AvatarLogging.h"
#include "AvatarTraits.h" #include "AvatarTraits.h"
#include "ClientTraitsHandler.h" #include "ClientTraitsHandler.h"
#include "ResourceRequestObserver.h"
//#define WANT_DEBUG //#define WANT_DEBUG
@ -2161,11 +2162,21 @@ void AvatarData::updateJointMappings() {
} }
if (_skeletonModelURL.fileName().toLower().endsWith(".fst")) { if (_skeletonModelURL.fileName().toLower().endsWith(".fst")) {
////
// TODO: Should we rely upon HTTPResourceRequest for ResourceRequestObserver instead?
// HTTPResourceRequest::doSend() covers all of the following and
// then some. It doesn't cover the connect() call, so we may
// want to add a HTTPResourceRequest::doSend() method that does
// connects.
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest networkRequest = QNetworkRequest(_skeletonModelURL); QNetworkRequest networkRequest = QNetworkRequest(_skeletonModelURL);
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
DependencyManager::get<ResourceRequestObserver>()->update(
_skeletonModelURL, -1, "AvatarData::updateJointMappings");
QNetworkReply* networkReply = networkAccessManager.get(networkRequest); QNetworkReply* networkReply = networkAccessManager.get(networkRequest);
//
////
connect(networkReply, &QNetworkReply::finished, this, &AvatarData::setJointMappingsFromNetworkReply); connect(networkReply, &QNetworkReply::finished, this, &AvatarData::setJointMappingsFromNetworkReply);
} }
} }

View file

@ -31,7 +31,27 @@ ClientTraitsHandler::ClientTraitsHandler(AvatarData* owningAvatar) :
nodeList->getPacketReceiver().registerListener(PacketType::SetAvatarTraits, this, "processTraitOverride"); nodeList->getPacketReceiver().registerListener(PacketType::SetAvatarTraits, this, "processTraitOverride");
} }
void ClientTraitsHandler::markTraitUpdated(AvatarTraits::TraitType updatedTrait) {
Lock lock(_traitLock);
_traitStatuses[updatedTrait] = Updated;
_hasChangedTraits = true;
}
void ClientTraitsHandler::markInstancedTraitUpdated(AvatarTraits::TraitType traitType, QUuid updatedInstanceID) {
Lock lock(_traitLock);
_traitStatuses.instanceInsert(traitType, updatedInstanceID, Updated);
_hasChangedTraits = true;
}
void ClientTraitsHandler::markInstancedTraitDeleted(AvatarTraits::TraitType traitType, QUuid deleteInstanceID) {
Lock lock(_traitLock);
_traitStatuses.instanceInsert(traitType, deleteInstanceID, Deleted);
_hasChangedTraits = true;
}
void ClientTraitsHandler::resetForNewMixer() { void ClientTraitsHandler::resetForNewMixer() {
Lock lock(_traitLock);
// re-set the current version to 0 // re-set the current version to 0
_currentTraitVersion = AvatarTraits::DEFAULT_TRAIT_VERSION; _currentTraitVersion = AvatarTraits::DEFAULT_TRAIT_VERSION;
@ -46,6 +66,8 @@ void ClientTraitsHandler::resetForNewMixer() {
} }
void ClientTraitsHandler::sendChangedTraitsToMixer() { void ClientTraitsHandler::sendChangedTraitsToMixer() {
Lock lock(_traitLock);
if (hasChangedTraits() || _shouldPerformInitialSend) { if (hasChangedTraits() || _shouldPerformInitialSend) {
// we have at least one changed trait to send // we have at least one changed trait to send
@ -113,6 +135,7 @@ void ClientTraitsHandler::sendChangedTraitsToMixer() {
void ClientTraitsHandler::processTraitOverride(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) { void ClientTraitsHandler::processTraitOverride(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
if (sendingNode->getType() == NodeType::AvatarMixer) { if (sendingNode->getType() == NodeType::AvatarMixer) {
Lock lock(_traitLock);
while (message->getBytesLeftToRead()) { while (message->getBytesLeftToRead()) {
AvatarTraits::TraitType traitType; AvatarTraits::TraitType traitType;
message->readPrimitive(&traitType); message->readPrimitive(&traitType);

View file

@ -26,14 +26,11 @@ public:
void sendChangedTraitsToMixer(); void sendChangedTraitsToMixer();
bool hasChangedTraits() { return _hasChangedTraits; } bool hasChangedTraits() const { return _hasChangedTraits; }
void markTraitUpdated(AvatarTraits::TraitType updatedTrait) void markTraitUpdated(AvatarTraits::TraitType updatedTrait);
{ _traitStatuses[updatedTrait] = Updated; _hasChangedTraits = true; } void markInstancedTraitUpdated(AvatarTraits::TraitType traitType, QUuid updatedInstanceID);
void markInstancedTraitUpdated(AvatarTraits::TraitType traitType, QUuid updatedInstanceID) void markInstancedTraitDeleted(AvatarTraits::TraitType traitType, QUuid deleteInstanceID);
{ _traitStatuses.instanceInsert(traitType, updatedInstanceID, Updated); _hasChangedTraits = true; }
void markInstancedTraitDeleted(AvatarTraits::TraitType traitType, QUuid deleteInstanceID)
{ _traitStatuses.instanceInsert(traitType, deleteInstanceID, Deleted); _hasChangedTraits = true; }
void resetForNewMixer(); void resetForNewMixer();
@ -41,17 +38,21 @@ public slots:
void processTraitOverride(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode); void processTraitOverride(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
private: private:
using Mutex = std::recursive_mutex;
using Lock = std::lock_guard<Mutex>;
enum ClientTraitStatus { enum ClientTraitStatus {
Unchanged, Unchanged,
Updated, Updated,
Deleted Deleted
}; };
AvatarData* _owningAvatar; AvatarData* const _owningAvatar;
Mutex _traitLock;
AvatarTraits::AssociatedTraitValues<ClientTraitStatus, Unchanged> _traitStatuses; AvatarTraits::AssociatedTraitValues<ClientTraitStatus, Unchanged> _traitStatuses;
AvatarTraits::TraitVersion _currentTraitVersion { AvatarTraits::DEFAULT_TRAIT_VERSION };
AvatarTraits::TraitVersion _currentTraitVersion { AvatarTraits::DEFAULT_TRAIT_VERSION };
AvatarTraits::TraitVersion _currentSkeletonVersion { AvatarTraits::NULL_TRAIT_VERSION }; AvatarTraits::TraitVersion _currentSkeletonVersion { AvatarTraits::NULL_TRAIT_VERSION };
bool _shouldPerformInitialSend { false }; bool _shouldPerformInitialSend { false };

View file

@ -200,7 +200,8 @@ void EntityEditFilters::addFilter(EntityItemID entityID, QString filterURL) {
_filterDataMap.insert(entityID, filterData); _filterDataMap.insert(entityID, filterData);
_lock.unlock(); _lock.unlock();
auto scriptRequest = DependencyManager::get<ResourceManager>()->createResourceRequest(this, scriptURL); auto scriptRequest = DependencyManager::get<ResourceManager>()->createResourceRequest(
this, scriptURL, true, -1, "EntityEditFilters::addFilter");
if (!scriptRequest) { if (!scriptRequest) {
qWarning() << "Could not create ResourceRequest for Entity Edit filter script at" << scriptURL.toString(); qWarning() << "Could not create ResourceRequest for Entity Edit filter script at" << scriptURL.toString();
scriptRequestFinished(entityID); scriptRequestFinished(entityID);

View file

@ -953,7 +953,8 @@ bool GLTFReader::doesResourceExist(const QString& url) {
} }
std::tuple<bool, QByteArray> GLTFReader::requestData(QUrl& url) { std::tuple<bool, QByteArray> GLTFReader::requestData(QUrl& url) {
auto request = DependencyManager::get<ResourceManager>()->createResourceRequest(nullptr, url); auto request = DependencyManager::get<ResourceManager>()->createResourceRequest(
nullptr, url, true, -1, "GLTFReader::requestData");
if (!request) { if (!request) {
return std::make_tuple(false, QByteArray()); return std::make_tuple(false, QByteArray());

View file

@ -443,7 +443,8 @@ void OBJReader::parseTextureLine(const QByteArray& textureLine, QByteArray& file
} }
std::tuple<bool, QByteArray> requestData(QUrl& url) { std::tuple<bool, QByteArray> requestData(QUrl& url) {
auto request = DependencyManager::get<ResourceManager>()->createResourceRequest(nullptr, url); auto request = DependencyManager::get<ResourceManager>()->createResourceRequest(
nullptr, url, true, -1, "(OBJReader) requestData");
if (!request) { if (!request) {
return std::make_tuple(false, QByteArray()); return std::make_tuple(false, QByteArray());

View file

@ -456,7 +456,8 @@ void NetworkTexture::makeRequest() {
// Add a fragment to the base url so we can identify the section of the ktx being requested when debugging // Add a fragment to the base url so we can identify the section of the ktx being requested when debugging
// The actual requested url is _activeUrl and will not contain the fragment // The actual requested url is _activeUrl and will not contain the fragment
_url.setFragment("head"); _url.setFragment("head");
_ktxHeaderRequest = DependencyManager::get<ResourceManager>()->createResourceRequest(this, _activeUrl); _ktxHeaderRequest = DependencyManager::get<ResourceManager>()->createResourceRequest(
this, _activeUrl, true, -1, "NetworkTexture::makeRequest");
if (!_ktxHeaderRequest) { if (!_ktxHeaderRequest) {
qCDebug(networking).noquote() << "Failed to get request for" << _url.toDisplayString(); qCDebug(networking).noquote() << "Failed to get request for" << _url.toDisplayString();
@ -617,7 +618,8 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) {
bool isHighMipRequest = low == NULL_MIP_LEVEL && high == NULL_MIP_LEVEL; bool isHighMipRequest = low == NULL_MIP_LEVEL && high == NULL_MIP_LEVEL;
_ktxMipRequest = DependencyManager::get<ResourceManager>()->createResourceRequest(this, _activeUrl); _ktxMipRequest = DependencyManager::get<ResourceManager>()->createResourceRequest(
this, _activeUrl, true, -1, "NetworkTexture::startMipRangeRequest");
if (!_ktxMipRequest) { if (!_ktxMipRequest) {
qCWarning(networking).noquote() << "Failed to get request for" << _url.toDisplayString(); qCWarning(networking).noquote() << "Failed to get request for" << _url.toDisplayString();

View file

@ -24,8 +24,12 @@
static const int DOWNLOAD_PROGRESS_LOG_INTERVAL_SECONDS = 5; static const int DOWNLOAD_PROGRESS_LOG_INTERVAL_SECONDS = 5;
AssetResourceRequest::AssetResourceRequest(const QUrl& url) : AssetResourceRequest::AssetResourceRequest(
ResourceRequest(url) const QUrl& url,
const bool isObservable,
const qint64 callerId,
const QString& extra) :
ResourceRequest(url, isObservable, callerId, extra)
{ {
_lastProgressDebug = p_high_resolution_clock::now() - std::chrono::seconds(DOWNLOAD_PROGRESS_LOG_INTERVAL_SECONDS); _lastProgressDebug = p_high_resolution_clock::now() - std::chrono::seconds(DOWNLOAD_PROGRESS_LOG_INTERVAL_SECONDS);
} }

View file

@ -22,7 +22,11 @@
class AssetResourceRequest : public ResourceRequest { class AssetResourceRequest : public ResourceRequest {
Q_OBJECT Q_OBJECT
public: public:
AssetResourceRequest(const QUrl& url); AssetResourceRequest(
const QUrl& url,
const bool isObservable = true,
const qint64 callerId = -1,
const QString& extra = "");
virtual ~AssetResourceRequest() override; virtual ~AssetResourceRequest() override;
protected: protected:

View file

@ -14,7 +14,8 @@
#include "ResourceManager.h" #include "ResourceManager.h"
AtpReply::AtpReply(const QUrl& url, QObject* parent) : AtpReply::AtpReply(const QUrl& url, QObject* parent) :
_resourceRequest(DependencyManager::get<ResourceManager>()->createResourceRequest(parent, url)) { _resourceRequest(DependencyManager::get<ResourceManager>()->createResourceRequest(
parent, url, true, -1, "AtpReply::AtpReply")) {
setOperation(QNetworkAccessManager::GetOperation); setOperation(QNetworkAccessManager::GetOperation);
connect(_resourceRequest, &AssetResourceRequest::progress, this, &AtpReply::downloadProgress); connect(_resourceRequest, &AssetResourceRequest::progress, this, &AtpReply::downloadProgress);

View file

@ -19,7 +19,12 @@
class FileResourceRequest : public ResourceRequest { class FileResourceRequest : public ResourceRequest {
Q_OBJECT Q_OBJECT
public: public:
FileResourceRequest(const QUrl& url) : ResourceRequest(url) { } FileResourceRequest(
const QUrl& url,
const bool isObservable = true,
const qint64 callerId = -1,
const QString& extra = ""
) : ResourceRequest(url, isObservable, callerId, extra) { }
protected: protected:
virtual void doSend() override; virtual void doSend() override;

View file

@ -21,7 +21,12 @@
class HTTPResourceRequest : public ResourceRequest { class HTTPResourceRequest : public ResourceRequest {
Q_OBJECT Q_OBJECT
public: public:
HTTPResourceRequest(const QUrl& url) : ResourceRequest(url) { } HTTPResourceRequest(
const QUrl& url,
const bool isObservable = true,
const qint64 callerId = -1,
const QString& = ""
) : ResourceRequest(url, isObservable, callerId) { }
~HTTPResourceRequest(); ~HTTPResourceRequest();
protected: protected:

View file

@ -1187,12 +1187,24 @@ void LimitedNodeList::sendPeerQueryToIceServer(const HifiSockAddr& iceServerSock
SharedNodePointer LimitedNodeList::findNodeWithAddr(const HifiSockAddr& addr) { SharedNodePointer LimitedNodeList::findNodeWithAddr(const HifiSockAddr& addr) {
QReadLocker locker(&_nodeMutex); QReadLocker locker(&_nodeMutex);
auto it = std::find_if(std::begin(_nodeHash), std::end(_nodeHash), [&](const UUIDNodePair& pair) { auto it = std::find_if(std::begin(_nodeHash), std::end(_nodeHash), [&addr](const UUIDNodePair& pair) {
return pair.second->getActiveSocket() ? (*pair.second->getActiveSocket() == addr) : false; return pair.second->getPublicSocket() == addr
|| pair.second->getLocalSocket() == addr
|| pair.second->getSymmetricSocket() == addr;
}); });
return (it != std::end(_nodeHash)) ? it->second : SharedNodePointer(); return (it != std::end(_nodeHash)) ? it->second : SharedNodePointer();
} }
bool LimitedNodeList::sockAddrBelongsToNode(const HifiSockAddr& sockAddr) {
QReadLocker locker(&_nodeMutex);
auto it = std::find_if(std::begin(_nodeHash), std::end(_nodeHash), [&sockAddr](const UUIDNodePair& pair) {
return pair.second->getPublicSocket() == sockAddr
|| pair.second->getLocalSocket() == sockAddr
|| pair.second->getSymmetricSocket() == sockAddr;
});
return it != std::end(_nodeHash);
}
void LimitedNodeList::sendPacketToIceServer(PacketType packetType, const HifiSockAddr& iceServerSockAddr, void LimitedNodeList::sendPacketToIceServer(PacketType packetType, const HifiSockAddr& iceServerSockAddr,
const QUuid& clientID, const QUuid& peerID) { const QUuid& clientID, const QUuid& peerID) {
auto icePacket = NLPacket::create(packetType); auto icePacket = NLPacket::create(packetType);

View file

@ -386,7 +386,7 @@ protected:
void sendPacketToIceServer(PacketType packetType, const HifiSockAddr& iceServerSockAddr, const QUuid& clientID, void sendPacketToIceServer(PacketType packetType, const HifiSockAddr& iceServerSockAddr, const QUuid& clientID,
const QUuid& peerRequestID = QUuid()); const QUuid& peerRequestID = QUuid());
bool sockAddrBelongsToNode(const HifiSockAddr& sockAddr) { return findNodeWithAddr(sockAddr) != SharedNodePointer(); } bool sockAddrBelongsToNode(const HifiSockAddr& sockAddr);
NodeHash _nodeHash; NodeHash _nodeHash;
mutable QReadWriteLock _nodeMutex { QReadWriteLock::Recursive }; mutable QReadWriteLock _nodeMutex { QReadWriteLock::Recursive };

View file

@ -10,6 +10,7 @@
// //
#include "ResourceCache.h" #include "ResourceCache.h"
#include "ResourceRequestObserver.h"
#include <cfloat> #include <cfloat>
#include <cmath> #include <cmath>
@ -252,7 +253,9 @@ ResourceCache::ResourceCache(QObject* parent) : QObject(parent) {
} }
} }
ResourceCache::~ResourceCache() {} ResourceCache::~ResourceCache() {
clearUnusedResources();
}
void ResourceCache::clearATPAssets() { void ResourceCache::clearATPAssets() {
{ {
@ -338,13 +341,13 @@ QSharedPointer<Resource> ResourceCache::getResource(const QUrl& url, const QUrl&
} }
if (resource) { if (resource) {
removeUnusedResource(resource); removeUnusedResource(resource);
return resource;
} }
if (!url.isValid() && !url.isEmpty() && fallback.isValid()) { if (!resource && !url.isValid() && !url.isEmpty() && fallback.isValid()) {
return getResource(fallback, QUrl()); resource = getResource(fallback, QUrl());
} }
if (!resource) {
resource = createResource( resource = createResource(
url, url,
fallback.isValid() ? getResource(fallback, QUrl()) : QSharedPointer<Resource>(), fallback.isValid() ? getResource(fallback, QUrl()) : QSharedPointer<Resource>(),
@ -359,7 +362,10 @@ QSharedPointer<Resource> ResourceCache::getResource(const QUrl& url, const QUrl&
} }
removeUnusedResource(resource); removeUnusedResource(resource);
resource->ensureLoading(); resource->ensureLoading();
}
DependencyManager::get<ResourceRequestObserver>()->update(
resource->getURL(), -1, "ResourceCache::getResource");
return resource; return resource;
} }
@ -682,7 +688,8 @@ void Resource::makeRequest() {
PROFILE_ASYNC_BEGIN(resource, "Resource:" + getType(), QString::number(_requestID), { { "url", _url.toString() }, { "activeURL", _activeUrl.toString() } }); PROFILE_ASYNC_BEGIN(resource, "Resource:" + getType(), QString::number(_requestID), { { "url", _url.toString() }, { "activeURL", _activeUrl.toString() } });
_request = DependencyManager::get<ResourceManager>()->createResourceRequest(this, _activeUrl); _request = DependencyManager::get<ResourceManager>()->createResourceRequest(
this, _activeUrl, true, -1, "Resource::makeRequest");
if (!_request) { if (!_request) {
qCDebug(networking).noquote() << "Failed to get request for" << _url.toDisplayString(); qCDebug(networking).noquote() << "Failed to get request for" << _url.toDisplayString();

View file

@ -112,22 +112,28 @@ void ResourceManager::cleanup() {
_thread.wait(); _thread.wait();
} }
ResourceRequest* ResourceManager::createResourceRequest(QObject* parent, const QUrl& url) { ResourceRequest* ResourceManager::createResourceRequest(
QObject* parent,
const QUrl& url,
const bool isObservable,
const qint64 callerId,
const QString& extra
) {
auto normalizedURL = normalizeURL(url); auto normalizedURL = normalizeURL(url);
auto scheme = normalizedURL.scheme(); auto scheme = normalizedURL.scheme();
ResourceRequest* request = nullptr; ResourceRequest* request = nullptr;
if (scheme == HIFI_URL_SCHEME_FILE || scheme == URL_SCHEME_QRC) { if (scheme == HIFI_URL_SCHEME_FILE || scheme == URL_SCHEME_QRC) {
request = new FileResourceRequest(normalizedURL); request = new FileResourceRequest(normalizedURL, isObservable, callerId, extra);
} else if (scheme == HIFI_URL_SCHEME_HTTP || scheme == HIFI_URL_SCHEME_HTTPS || scheme == HIFI_URL_SCHEME_FTP) { } else if (scheme == HIFI_URL_SCHEME_HTTP || scheme == HIFI_URL_SCHEME_HTTPS || scheme == HIFI_URL_SCHEME_FTP) {
request = new HTTPResourceRequest(normalizedURL); request = new HTTPResourceRequest(normalizedURL, isObservable, callerId, extra);
} else if (scheme == URL_SCHEME_ATP) { } else if (scheme == URL_SCHEME_ATP) {
if (!_atpSupportEnabled) { if (!_atpSupportEnabled) {
qCDebug(networking) << "ATP support not enabled, unable to create request for URL: " << url.url(); qCDebug(networking) << "ATP support not enabled, unable to create request for URL: " << url.url();
return nullptr; return nullptr;
} }
request = new AssetResourceRequest(normalizedURL); request = new AssetResourceRequest(normalizedURL, isObservable, callerId, extra);
} else { } else {
qCDebug(networking) << "Unknown scheme (" << scheme << ") for URL: " << url.url(); qCDebug(networking) << "Unknown scheme (" << scheme << ") for URL: " << url.url();
return nullptr; return nullptr;
@ -163,7 +169,7 @@ bool ResourceManager::resourceExists(const QUrl& url) {
return reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200; return reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200;
} else if (scheme == URL_SCHEME_ATP && _atpSupportEnabled) { } else if (scheme == URL_SCHEME_ATP && _atpSupportEnabled) {
auto request = new AssetResourceRequest(url); auto request = new AssetResourceRequest(url, ResourceRequest::IS_NOT_OBSERVABLE);
ByteRange range; ByteRange range;
range.fromInclusive = 1; range.fromInclusive = 1;
range.toExclusive = 1; range.toExclusive = 1;

View file

@ -34,7 +34,12 @@ public:
QString normalizeURL(const QString& urlString); QString normalizeURL(const QString& urlString);
QUrl normalizeURL(const QUrl& url); QUrl normalizeURL(const QUrl& url);
ResourceRequest* createResourceRequest(QObject* parent, const QUrl& url); ResourceRequest* createResourceRequest(
QObject* parent,
const QUrl& url,
const bool isObservable = true,
const qint64 callerId = -1,
const QString& extra = "");
void init(); void init();
void cleanup(); void cleanup();

View file

@ -10,13 +10,13 @@
// //
#include "ResourceRequest.h" #include "ResourceRequest.h"
#include "ResourceRequestObserver.h"
#include <DependencyManager.h> #include <DependencyManager.h>
#include <StatTracker.h> #include <StatTracker.h>
#include <QtCore/QThread> #include <QtCore/QThread>
ResourceRequest::ResourceRequest(const QUrl& url) : _url(url) { }
void ResourceRequest::send() { void ResourceRequest::send() {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
@ -24,6 +24,10 @@ void ResourceRequest::send() {
return; return;
} }
if (_isObservable) {
DependencyManager::get<ResourceRequestObserver>()->update(_url, _callerId, _extra + " => ResourceRequest::send");
}
Q_ASSERT(_state == NotStarted); Q_ASSERT(_state == NotStarted);
_state = InProgress; _state = InProgress;

View file

@ -40,7 +40,20 @@ const QString STAT_FILE_RESOURCE_TOTAL_BYTES = "FILEBytesDownloaded";
class ResourceRequest : public QObject { class ResourceRequest : public QObject {
Q_OBJECT Q_OBJECT
public: public:
ResourceRequest(const QUrl& url); static const bool IS_OBSERVABLE = true;
static const bool IS_NOT_OBSERVABLE = false;
ResourceRequest(
const QUrl& url,
const bool isObservable = IS_OBSERVABLE,
const qint64 callerId = -1,
const QString& extra = ""
) : _url(url),
_isObservable(isObservable),
_callerId(callerId),
_extra(extra)
{ }
virtual ~ResourceRequest() = default; virtual ~ResourceRequest() = default;
enum State { enum State {
@ -99,6 +112,9 @@ protected:
bool _rangeRequestSuccessful { false }; bool _rangeRequestSuccessful { false };
uint64_t _totalSizeOfResource { 0 }; uint64_t _totalSizeOfResource { 0 };
int64_t _lastRecordedBytesDownloaded { 0 }; int64_t _lastRecordedBytesDownloaded { 0 };
bool _isObservable;
qint64 _callerId;
QString _extra;
}; };
#endif #endif

View file

@ -228,13 +228,13 @@ qint64 Socket::writeDatagram(const QByteArray& datagram, const HifiSockAddr& soc
return bytesWritten; return bytesWritten;
} }
Connection* Socket::findOrCreateConnection(const HifiSockAddr& sockAddr) { Connection* Socket::findOrCreateConnection(const HifiSockAddr& sockAddr, bool filterCreate) {
auto it = _connectionsHash.find(sockAddr); auto it = _connectionsHash.find(sockAddr);
if (it == _connectionsHash.end()) { if (it == _connectionsHash.end()) {
// we did not have a matching connection, time to see if we should make one // we did not have a matching connection, time to see if we should make one
if (_connectionCreationFilterOperator && !_connectionCreationFilterOperator(sockAddr)) { if (filterCreate && _connectionCreationFilterOperator && !_connectionCreationFilterOperator(sockAddr)) {
// the connection creation filter did not allow us to create a new connection // the connection creation filter did not allow us to create a new connection
#ifdef UDT_CONNECTION_DEBUG #ifdef UDT_CONNECTION_DEBUG
qCDebug(networking) << "Socket::findOrCreateConnection refusing to create connection for" << sockAddr qCDebug(networking) << "Socket::findOrCreateConnection refusing to create connection for" << sockAddr
@ -376,7 +376,7 @@ void Socket::readPendingDatagrams() {
controlPacket->setReceiveTime(receiveTime); controlPacket->setReceiveTime(receiveTime);
// move this control packet to the matching connection, if there is one // move this control packet to the matching connection, if there is one
auto connection = findOrCreateConnection(senderSockAddr); auto connection = findOrCreateConnection(senderSockAddr, true);
if (connection) { if (connection) {
connection->processControl(move(controlPacket)); connection->processControl(move(controlPacket));
@ -394,7 +394,7 @@ void Socket::readPendingDatagrams() {
if (!_packetFilterOperator || _packetFilterOperator(*packet)) { if (!_packetFilterOperator || _packetFilterOperator(*packet)) {
if (packet->isReliable()) { if (packet->isReliable()) {
// if this was a reliable packet then signal the matching connection with the sequence number // if this was a reliable packet then signal the matching connection with the sequence number
auto connection = findOrCreateConnection(senderSockAddr); auto connection = findOrCreateConnection(senderSockAddr, true);
if (!connection || !connection->processReceivedSequenceNumber(packet->getSequenceNumber(), if (!connection || !connection->processReceivedSequenceNumber(packet->getSequenceNumber(),
packet->getDataSize(), packet->getDataSize(),
@ -409,7 +409,7 @@ void Socket::readPendingDatagrams() {
} }
if (packet->isPartOfMessage()) { if (packet->isPartOfMessage()) {
auto connection = findOrCreateConnection(senderSockAddr); auto connection = findOrCreateConnection(senderSockAddr, true);
if (connection) { if (connection) {
connection->queueReceivedMessagePacket(std::move(packet)); connection->queueReceivedMessagePacket(std::move(packet));
} }

View file

@ -109,7 +109,7 @@ private slots:
private: private:
void setSystemBufferSizes(); void setSystemBufferSizes();
Connection* findOrCreateConnection(const HifiSockAddr& sockAddr); Connection* findOrCreateConnection(const HifiSockAddr& sockAddr, bool filterCreation = false);
bool socketMatchesNodeOrDomain(const HifiSockAddr& sockAddr); bool socketMatchesNodeOrDomain(const HifiSockAddr& sockAddr);
// privatized methods used by UDTTest - they are private since they must be called on the Socket thread // privatized methods used by UDTTest - they are private since they must be called on the Socket thread

View file

@ -734,11 +734,17 @@ QString getMarketplaceID(const QString& urlString) {
return QString(); return QString();
} }
bool Octree::readFromURL(const QString& urlString) { bool Octree::readFromURL(
const QString& urlString,
const bool isObservable,
const qint64 callerId
) {
QString trimmedUrl = urlString.trimmed(); QString trimmedUrl = urlString.trimmed();
QString marketplaceID = getMarketplaceID(trimmedUrl); QString marketplaceID = getMarketplaceID(trimmedUrl);
auto request = qDebug() << "!!!!! going to createResourceRequest " << callerId;
std::unique_ptr<ResourceRequest>(DependencyManager::get<ResourceManager>()->createResourceRequest(this, trimmedUrl)); auto request = std::unique_ptr<ResourceRequest>(
DependencyManager::get<ResourceManager>()->createResourceRequest(
this, trimmedUrl, isObservable, callerId, "Octree::readFromURL"));
if (!request) { if (!request) {
return false; return false;
@ -768,7 +774,11 @@ bool Octree::readFromURL(const QString& urlString) {
} }
bool Octree::readFromStream(uint64_t streamLength, QDataStream& inputStream, const QString& marketplaceID) { bool Octree::readFromStream(
uint64_t streamLength,
QDataStream& inputStream,
const QString& marketplaceID
) {
// decide if this is binary SVO or JSON-formatted SVO // decide if this is binary SVO or JSON-formatted SVO
QIODevice *device = inputStream.device(); QIODevice *device = inputStream.device();
char firstChar; char firstChar;
@ -809,7 +819,11 @@ QJsonDocument addMarketplaceIDToDocumentEntities(QJsonDocument& doc, const QStri
const int READ_JSON_BUFFER_SIZE = 2048; const int READ_JSON_BUFFER_SIZE = 2048;
bool Octree::readJSONFromStream(uint64_t streamLength, QDataStream& inputStream, const QString& marketplaceID /*=""*/) { bool Octree::readJSONFromStream(
uint64_t streamLength,
QDataStream& inputStream,
const QString& marketplaceID /*=""*/
) {
// if the data is gzipped we may not have a useful bytesAvailable() result, so just keep reading until // if the data is gzipped we may not have a useful bytesAvailable() result, so just keep reading until
// we get an eof. Leave streamLength parameter for consistency. // we get an eof. Leave streamLength parameter for consistency.

View file

@ -210,7 +210,7 @@ public:
// Octree importers // Octree importers
bool readFromFile(const char* filename); bool readFromFile(const char* filename);
bool readFromURL(const QString& url); // will support file urls as well... bool readFromURL(const QString& url, const bool isObservable = true, const qint64 callerId = -1); // will support file urls as well...
bool readFromStream(uint64_t streamLength, QDataStream& inputStream, const QString& marketplaceID=""); bool readFromStream(uint64_t streamLength, QDataStream& inputStream, const QString& marketplaceID="");
bool readSVOFromStream(uint64_t streamLength, QDataStream& inputStream); bool readSVOFromStream(uint64_t streamLength, QDataStream& inputStream);
bool readJSONFromStream(uint64_t streamLength, QDataStream& inputStream, const QString& marketplaceID=""); bool readJSONFromStream(uint64_t streamLength, QDataStream& inputStream, const QString& marketplaceID="");

View file

@ -138,7 +138,8 @@ QString FileScriptingInterface::convertUrlToPath(QUrl url) {
// this function is not in use // this function is not in use
void FileScriptingInterface::downloadZip(QString path, const QString link) { void FileScriptingInterface::downloadZip(QString path, const QString link) {
QUrl url = QUrl(link); QUrl url = QUrl(link);
auto request = DependencyManager::get<ResourceManager>()->createResourceRequest(nullptr, url); auto request = DependencyManager::get<ResourceManager>()->createResourceRequest(
nullptr, url, true, -1, "FileScriptingInterface::downloadZip");
connect(request, &ResourceRequest::finished, this, [this, path]{ connect(request, &ResourceRequest::finished, this, [this, path]{
unzipFile(path, ""); // so intellisense isn't mad unzipFile(path, ""); // so intellisense isn't mad
}); });

View file

@ -109,7 +109,8 @@ void ScriptCache::getScriptContents(const QString& scriptOrURL, contentAvailable
#ifdef THREAD_DEBUGGING #ifdef THREAD_DEBUGGING
qCDebug(scriptengine) << "about to call: ResourceManager::createResourceRequest(this, url); on thread [" << QThread::currentThread() << "] expected thread [" << thread() << "]"; qCDebug(scriptengine) << "about to call: ResourceManager::createResourceRequest(this, url); on thread [" << QThread::currentThread() << "] expected thread [" << thread() << "]";
#endif #endif
auto request = DependencyManager::get<ResourceManager>()->createResourceRequest(nullptr, url); auto request = DependencyManager::get<ResourceManager>()->createResourceRequest(
nullptr, url, true, -1, "ScriptCache::getScriptContents");
Q_ASSERT(request); Q_ASSERT(request);
request->setCacheEnabled(!forceDownload); request->setCacheEnabled(!forceDownload);
connect(request, &ResourceRequest::finished, this, [=]{ scriptContentAvailable(maxRetries); }); connect(request, &ResourceRequest::finished, this, [=]{ scriptContentAvailable(maxRetries); });
@ -166,7 +167,8 @@ void ScriptCache::scriptContentAvailable(int maxRetries) {
qCDebug(scriptengine) << QString("Retrying script request [%1 / %2]: %3") qCDebug(scriptengine) << QString("Retrying script request [%1 / %2]: %3")
.arg(attempt).arg(maxRetries).arg(url.toString()); .arg(attempt).arg(maxRetries).arg(url.toString());
auto request = DependencyManager::get<ResourceManager>()->createResourceRequest(nullptr, url); auto request = DependencyManager::get<ResourceManager>()->createResourceRequest(
nullptr, url, true, -1, "ScriptCache::scriptContentAvailable");
Q_ASSERT(request); Q_ASSERT(request);
// We've already made a request, so the cache must be disabled or it wasn't there, so enabling // We've already made a request, so the cache must be disabled or it wasn't there, so enabling

View file

@ -21,6 +21,7 @@
#include <NetworkAccessManager.h> #include <NetworkAccessManager.h>
#include <NetworkingConstants.h> #include <NetworkingConstants.h>
#include "ResourceRequestObserver.h"
#include "ScriptEngine.h" #include "ScriptEngine.h"
const QString METAVERSE_API_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/api/"; const QString METAVERSE_API_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/api/";
@ -189,7 +190,7 @@ void XMLHttpRequestClass::send(const QScriptValue& data) {
} }
void XMLHttpRequestClass::doSend() { void XMLHttpRequestClass::doSend() {
DependencyManager::get<ResourceRequestObserver>()->update(_url, -1, "XMLHttpRequestClass::doSend");
_reply = NetworkAccessManager::getInstance().sendCustomRequest(_request, _method.toLatin1(), _sendData); _reply = NetworkAccessManager::getInstance().sendCustomRequest(_request, _method.toLatin1(), _sendData);
connectToReply(_reply); connectToReply(_reply);

View file

@ -0,0 +1,28 @@
//
// ResourceAccessMonitor.h
// libraries/networking/src
//
// Created by Kerry Ivan Kurian on 9/27/18.
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QJsonArray>
#include <QJsonObject>
#include <QString>
#include <QUrl>
#include "ResourceRequestObserver.h"
void ResourceRequestObserver::update(const QUrl& requestUrl,
const qint64 callerId,
const QString& extra) {
QJsonArray array;
QJsonObject data { { "url", requestUrl.toString() },
{ "callerId", callerId },
{ "extra", extra }
};
emit resourceRequestEvent(data.toVariantMap());
}

View file

@ -0,0 +1,29 @@
//
// ResourceRequestObserver.h
// libraries/commerce/src
//
// Created by Kerry Ivan Kurian on 9/27/18.
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QJsonObject>
#include <QString>
#include <QNetworkRequest>
#include "DependencyManager.h"
class ResourceRequestObserver : public QObject, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
public:
void update(const QUrl& requestUrl, const qint64 callerId = -1, const QString& extra = "");
signals:
void resourceRequestEvent(QVariantMap result);
};

View file

@ -85,6 +85,11 @@ private:
if (!OVR_SUCCESS(ovr_Create(&session, &luid))) { if (!OVR_SUCCESS(ovr_Create(&session, &luid))) {
qCWarning(oculusLog) << "Failed to acquire Oculus session" << ovr::getError(); qCWarning(oculusLog) << "Failed to acquire Oculus session" << ovr::getError();
return; return;
} else {
ovrResult setFloorLevelOrigin = ovr_SetTrackingOriginType(session, ovrTrackingOrigin::ovrTrackingOrigin_FloorLevel);
if (!OVR_SUCCESS(setFloorLevelOrigin)) {
qCWarning(oculusLog) << "Failed to set the Oculus tracking origin to floor level" << ovr::getError();
}
} }
} }

View file

@ -463,7 +463,7 @@ bool OpenVrDisplayPlugin::internalActivate() {
auto chaperone = vr::VRChaperone(); auto chaperone = vr::VRChaperone();
if (chaperone) { if (chaperone) {
float const UI_RADIUS = 1.0f; float const UI_RADIUS = 1.0f;
float const UI_HEIGHT = 1.6f; float const UI_HEIGHT = 0.0f;
float const UI_Z_OFFSET = 0.5; float const UI_Z_OFFSET = 0.5;
float xSize, zSize; float xSize, zSize;

Binary file not shown.

Binary file not shown.

View file

@ -298,7 +298,7 @@ Script.include("/~/system/libraries/controllers.js");
}; };
this.restoreIgnoredEntities = function() { this.restoreIgnoredEntities = function() {
for (var i = 0; i < this.ignoredEntities; i++) { for (var i = 0; i < this.ignoredEntities.length; i++) {
var data = { var data = {
action: 'remove', action: 'remove',
id: this.ignoredEntities[i] id: this.ignoredEntities[i]
@ -317,15 +317,6 @@ Script.include("/~/system/libraries/controllers.js");
if ((intersection.type === Picks.INTERSECTED_ENTITY && entityType === "Web") || if ((intersection.type === Picks.INTERSECTED_ENTITY && entityType === "Web") ||
intersection.type === Picks.INTERSECTED_OVERLAY || Window.isPointOnDesktopWindow(point2d)) { intersection.type === Picks.INTERSECTED_OVERLAY || Window.isPointOnDesktopWindow(point2d)) {
return true; return true;
} else if (intersection.type === Picks.INTERSECTED_ENTITY && !Window.isPhysicsEnabled()) {
// add to ignored items.
var data = {
action: 'add',
id: intersection.objectID
};
Messages.sendMessage('Hifi-Hand-RayPick-Blacklist', JSON.stringify(data));
this.ignoredEntities.push(intersection.objectID);
} }
return false; return false;
}; };
@ -386,7 +377,6 @@ Script.include("/~/system/libraries/controllers.js");
this.isReady = function (controllerData) { this.isReady = function (controllerData) {
if (HMD.active) { if (HMD.active) {
if (this.notPointingAtEntity(controllerData)) { if (this.notPointingAtEntity(controllerData)) {
this.restoreIgnoredEntities();
return makeRunningValues(false, [], []); return makeRunningValues(false, [], []);
} }
@ -398,17 +388,28 @@ Script.include("/~/system/libraries/controllers.js");
return makeRunningValues(true, [], []); return makeRunningValues(true, [], []);
} else { } else {
this.destroyContextOverlay(); this.destroyContextOverlay();
this.restoreIgnoredEntities();
return makeRunningValues(false, [], []); return makeRunningValues(false, [], []);
} }
} }
this.restoreIgnoredEntities();
return makeRunningValues(false, [], []); return makeRunningValues(false, [], []);
}; };
this.run = function (controllerData) { this.run = function (controllerData) {
var intersection = controllerData.rayPicks[this.hand];
if (intersection.type === Picks.INTERSECTED_ENTITY && !Window.isPhysicsEnabled()) {
// add to ignored items.
if (this.ignoredEntities.indexOf(intersection.objectID) === -1) {
var data = {
action: 'add',
id: intersection.objectID
};
Messages.sendMessage('Hifi-Hand-RayPick-Blacklist', JSON.stringify(data));
this.ignoredEntities.push(intersection.objectID);
}
}
if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE || if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE ||
this.notPointingAtEntity(controllerData) || this.targetIsNull()) { (this.notPointingAtEntity(controllerData) && Window.isPhysicsEnabled()) || this.targetIsNull()) {
this.endFarGrabAction(); this.endFarGrabAction();
Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity",
this.highlightedEntity); this.highlightedEntity);

View file

@ -701,6 +701,10 @@ Script.include("/~/system/libraries/controllers.js");
}; };
this.isReady = function(controllerData, deltaTime) { this.isReady = function(controllerData, deltaTime) {
if (Window.interstitialModeEnabled && !Window.isPhysicsEnabled()) {
return makeRunningValues(false, [], []);
}
var otherModule = this.getOtherModule(); var otherModule = this.getOtherModule();
if (!this.disabled && this.buttonValue !== 0 && !otherModule.active) { if (!this.disabled && this.buttonValue !== 0 && !otherModule.active) {
this.active = true; this.active = true;

View file

@ -18,6 +18,7 @@ Script.include("/~/system/libraries/controllers.js");
this.hand = hand; this.hand = hand;
this.otherHand = this.hand === RIGHT_HAND ? LEFT_HAND : RIGHT_HAND; this.otherHand = this.hand === RIGHT_HAND ? LEFT_HAND : RIGHT_HAND;
this.running = false; this.running = false;
this.ignoredObjects = [];
this.parameters = makeDispatcherModuleParameters( this.parameters = makeDispatcherModuleParameters(
160, 160,
@ -72,6 +73,48 @@ Script.include("/~/system/libraries/controllers.js");
return this.hand === RIGHT_HAND ? leftOverlayLaserInput : rightOverlayLaserInput; return this.hand === RIGHT_HAND ? leftOverlayLaserInput : rightOverlayLaserInput;
}; };
this.addObjectToIgnoreList = function(controllerData) {
if (Window.interstitialModeEnabled && !Window.isPhysicsEnabled()) {
var intersection = controllerData.rayPicks[this.hand];
var objectID = intersection.objectID;
if (intersection.type === Picks.INTERSECTED_OVERLAY) {
var overlayIndex = this.ignoredObjects.indexOf(objectID);
var overlayName = Overlays.getProperty(objectID, "name");
if (overlayName !== "Loading-Destination-Card-Text" && overlayName !== "Loading-Destination-Card-GoTo-Image" &&
overlayName !== "Loading-Destination-Card-GoTo-Image-Hover") {
var data = {
action: 'add',
id: objectID
};
Messages.sendMessage('Hifi-Hand-RayPick-Blacklist', JSON.stringify(data));
this.ignoredObjects.push(objectID);
}
} else if (intersection.type === Picks.INTERSECTED_ENTITY) {
var entityIndex = this.ignoredObjects.indexOf(objectID);
var data = {
action: 'add',
id: objectID
};
Messages.sendMessage('Hifi-Hand-RayPick-Blacklist', JSON.stringify(data));
this.ignoredObjects.push(objectID);
}
}
};
this.restoreIgnoredObjects = function() {
for (var index = 0; index < this.ignoredObjects.length; index++) {
var data = {
action: 'remove',
id: this.ignoredObjects[index]
};
Messages.sendMessage('Hifi-Hand-RayPick-Blacklist', JSON.stringify(data));
}
this.ignoredObjects = [];
};
this.isPointingAtTriggerable = function(controllerData, triggerPressed, checkEntitiesOnly) { this.isPointingAtTriggerable = function(controllerData, triggerPressed, checkEntitiesOnly) {
// allow pointing at tablet, unlocked web entities, or web overlays automatically without pressing trigger, // allow pointing at tablet, unlocked web entities, or web overlays automatically without pressing trigger,
// but for pointing at locked web entities or non-web overlays user must be pressing trigger // but for pointing at locked web entities or non-web overlays user must be pressing trigger
@ -137,6 +180,10 @@ Script.include("/~/system/libraries/controllers.js");
return makeRunningValues(true, [], []); return makeRunningValues(true, [], []);
} }
} }
if (Window.interstitialModeEnabled && Window.isPhysicsEnabled()) {
this.restoreIgnoredObjects();
}
return makeRunningValues(false, [], []); return makeRunningValues(false, [], []);
}; };
@ -149,6 +196,7 @@ Script.include("/~/system/libraries/controllers.js");
var allowThisModule = !otherModuleRunning && !grabModuleNeedsToRun; var allowThisModule = !otherModuleRunning && !grabModuleNeedsToRun;
var isTriggerPressed = controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE; var isTriggerPressed = controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE;
var laserOn = isTriggerPressed || this.parameters.handLaser.allwaysOn; var laserOn = isTriggerPressed || this.parameters.handLaser.allwaysOn;
this.addObjectToIgnoreList(controllerData);
if (allowThisModule) { if (allowThisModule) {
if (isTriggerPressed && !this.isPointingAtTriggerable(controllerData, isTriggerPressed, true)) { if (isTriggerPressed && !this.isPointingAtTriggerable(controllerData, isTriggerPressed, true)) {
// if trigger is down + not pointing at a web entity, keep running web surface laser // if trigger is down + not pointing at a web entity, keep running web surface laser

View file

@ -18,10 +18,11 @@
var DEBUG = false; var DEBUG = false;
var MIN_LOADING_PROGRESS = 3.6; var MIN_LOADING_PROGRESS = 3.6;
var TOTAL_LOADING_PROGRESS = 3.8; var TOTAL_LOADING_PROGRESS = 3.8;
var EPSILON = 0.01; var EPSILON = 0.05;
var TEXTURE_EPSILON = 0.01;
var isVisible = false; var isVisible = false;
var VOLUME = 0.4; var VOLUME = 0.4;
var tune = SoundCache.getSound("http://hifi-content.s3.amazonaws.com/alexia/LoadingScreens/crystals_and_voices.wav"); var tune = SoundCache.getSound(Script.resolvePath("/~/system/assets/sounds/crystals_and_voices.mp3"));
var sample = null; var sample = null;
var MAX_LEFT_MARGIN = 1.9; var MAX_LEFT_MARGIN = 1.9;
var INNER_CIRCLE_WIDTH = 4.7; var INNER_CIRCLE_WIDTH = 4.7;
@ -65,7 +66,7 @@
name: "Loading-Sphere", name: "Loading-Sphere",
position: Vec3.sum(Vec3.sum(MyAvatar.position, { x: 0.0, y: -1.0, z: 0.0 }), Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.95, z: 0 })), position: Vec3.sum(Vec3.sum(MyAvatar.position, { x: 0.0, y: -1.0, z: 0.0 }), Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.95, z: 0 })),
orientation: Quat.multiply(Quat.fromVec3Degrees({ x: 0, y: 180, z: 0 }), MyAvatar.orientation), orientation: Quat.multiply(Quat.fromVec3Degrees({ x: 0, y: 180, z: 0 }), MyAvatar.orientation),
url: "http://hifi-content.s3.amazonaws.com/alexia/LoadingScreens/black-sphere.fbx", url: Script.resolvePath("/~/system/assets/models/black-sphere.fbx"),
dimensions: DEFAULT_DIMENSIONS, dimensions: DEFAULT_DIMENSIONS,
alpha: 1, alpha: 1,
visible: isVisible, visible: isVisible,
@ -113,7 +114,6 @@
backgroundAlpha: 1, backgroundAlpha: 1,
lineHeight: 0.13, lineHeight: 0.13,
visible: isVisible, visible: isVisible,
backgroundAlpha: 0,
ignoreRayIntersection: true, ignoreRayIntersection: true,
drawInFront: true, drawInFront: true,
grabbable: false, grabbable: false,
@ -212,7 +212,7 @@
parentID: anchorOverlay parentID: anchorOverlay
}); });
var TARGET_UPDATE_HZ = 60; // 50hz good enough, but we're using update var TARGET_UPDATE_HZ = 30;
var BASIC_TIMER_INTERVAL_MS = 1000 / TARGET_UPDATE_HZ; var BASIC_TIMER_INTERVAL_MS = 1000 / TARGET_UPDATE_HZ;
var lastInterval = Date.now(); var lastInterval = Date.now();
var currentDomain = "no domain"; var currentDomain = "no domain";
@ -267,7 +267,7 @@
connectionToDomainFailed = false; connectionToDomainFailed = false;
previousCameraMode = Camera.mode; previousCameraMode = Camera.mode;
Camera.mode = "first person"; Camera.mode = "first person";
timer = Script.setTimeout(update, BASIC_TIMER_INTERVAL_MS); timer = Script.setTimeout(update, 2000);
} }
} }
@ -459,6 +459,8 @@
} }
} }
var MAX_TEXTURE_STABILITY_COUNT = 30;
var INTERVAL_PROGRESS = 0.04;
function update() { function update() {
var renderStats = Render.getConfig("Stats"); var renderStats = Render.getConfig("Stats");
var physicsEnabled = Window.isPhysicsEnabled(); var physicsEnabled = Window.isPhysicsEnabled();
@ -472,7 +474,7 @@
target = progress; target = progress;
} }
if (currentProgress >= (TOTAL_LOADING_PROGRESS * 0.4)) { if (currentProgress >= ((TOTAL_LOADING_PROGRESS * 0.4) - TEXTURE_EPSILON)) {
var textureResourceGPUMemSize = renderStats.textureResourceGPUMemSize; var textureResourceGPUMemSize = renderStats.textureResourceGPUMemSize;
var texturePopulatedGPUMemSize = renderStats.textureResourcePopulatedGPUMemSize; var texturePopulatedGPUMemSize = renderStats.textureResourcePopulatedGPUMemSize;
@ -484,10 +486,9 @@
textureMemSizeAtLastCheck = textureResourceGPUMemSize; textureMemSizeAtLastCheck = textureResourceGPUMemSize;
if (textureMemSizeStabilityCount >= 20) { if (textureMemSizeStabilityCount >= MAX_TEXTURE_STABILITY_COUNT) {
if (textureResourceGPUMemSize > 0) { if (textureResourceGPUMemSize > 0) {
// print((texturePopulatedGPUMemSize / textureResourceGPUMemSize));
var gpuPercantage = (TOTAL_LOADING_PROGRESS * 0.6) * (texturePopulatedGPUMemSize / textureResourceGPUMemSize); var gpuPercantage = (TOTAL_LOADING_PROGRESS * 0.6) * (texturePopulatedGPUMemSize / textureResourceGPUMemSize);
var totalProgress = progress + gpuPercantage; var totalProgress = progress + gpuPercantage;
if (totalProgress >= target) { if (totalProgress >= target) {
@ -501,7 +502,7 @@
target = TOTAL_LOADING_PROGRESS; target = TOTAL_LOADING_PROGRESS;
} }
currentProgress = lerp(currentProgress, target, 0.2); currentProgress = lerp(currentProgress, target, INTERVAL_PROGRESS);
var properties = { var properties = {
localPosition: { x: (1.85 - (currentProgress / 2) - (-0.029 * (currentProgress / TOTAL_LOADING_PROGRESS))), y: -0.935, z: 0.0 }, localPosition: { x: (1.85 - (currentProgress / 2) - (-0.029 * (currentProgress / TOTAL_LOADING_PROGRESS))), y: -0.935, z: 0.0 },
dimensions: { dimensions: {
@ -557,7 +558,11 @@
MyAvatar.sensorToWorldScaleChanged.connect(scaleInterstitialPage); MyAvatar.sensorToWorldScaleChanged.connect(scaleInterstitialPage);
MyAvatar.sessionUUIDChanged.connect(function() { MyAvatar.sessionUUIDChanged.connect(function() {
var avatarSessionUUID = MyAvatar.sessionUUID; var avatarSessionUUID = MyAvatar.sessionUUID;
Overlays.editOverlay(loadingSphereID, { parentID: avatarSessionUUID }); Overlays.editOverlay(loadingSphereID, {
position: Vec3.sum(Vec3.sum(MyAvatar.position, {x: 0.0, y: -1.0, z: 0.0}), Vec3.multiplyQbyV(MyAvatar.orientation, {x: 0, y: 0.95, z: 0})),
orientation: Quat.multiply(Quat.fromVec3Degrees({x: 0, y: 180, z: 0}), MyAvatar.orientation),
parentID: avatarSessionUUID
});
}); });
var toggle = true; var toggle = true;

View file

@ -49,6 +49,42 @@ var NO_BUTTON = 0; // QMessageBox::NoButton
var NO_PERMISSIONS_ERROR_MESSAGE = "Cannot download model because you can't write to \nthe domain's Asset Server."; var NO_PERMISSIONS_ERROR_MESSAGE = "Cannot download model because you can't write to \nthe domain's Asset Server.";
var resourceRequestEvents = [];
function signalResourceRequestEvent(data) {
// Once we can tie resource request events to specific resources,
// we will have to update the "0" in here.
var resourceData = "from: " + data.extra + ": " + data.url.toString().replace("__NONE__,", "");
if (resourceObjectsInTest[0].resourceDataArray.indexOf(resourceData) === -1) {
resourceObjectsInTest[0].resourceDataArray.push(resourceData);
resourceObjectsInTest[0].resourceAccessEventText += "[" + data.date.toISOString() + "] " +
resourceData + "\n";
ui.tablet.sendToQml({
method: "resourceRequestEvent",
data: data,
resourceAccessEventText: resourceObjectsInTest[0].resourceAccessEventText
});
}
}
function onResourceRequestEvent(data) {
// Once we can tie resource request events to specific resources,
// we will have to update the "0" in here.
if (resourceObjectsInTest[0] && resourceObjectsInTest[0].currentlyRecordingResources) {
var resourceRequestEvent = {
"date": new Date(),
"url": data.url,
"callerId": data.callerId,
"extra": data.extra
};
resourceRequestEvents.push(resourceRequestEvent);
signalResourceRequestEvent(resourceRequestEvent);
}
}
function onMessageBoxClosed(id, button) { function onMessageBoxClosed(id, button) {
if (id === messageBox && button === CANCEL_BUTTON) { if (id === messageBox && button === CANCEL_BUTTON) {
isDownloadBeingCancelled = true; isDownloadBeingCancelled = true;
@ -522,13 +558,18 @@ function getPositionToCreateEntity(extra) {
return position; return position;
} }
function rezEntity(itemHref, itemType) { function defaultFor(arg, val) {
return typeof arg !== 'undefined' ? arg : val;
}
function rezEntity(itemHref, itemType, marketplaceItemTesterId) {
var isWearable = itemType === "wearable"; var isWearable = itemType === "wearable";
var success = Clipboard.importEntities(itemHref); var success = Clipboard.importEntities(itemHref, true, marketplaceItemTesterId);
var wearableLocalPosition = null; var wearableLocalPosition = null;
var wearableLocalRotation = null; var wearableLocalRotation = null;
var wearableLocalDimensions = null; var wearableLocalDimensions = null;
var wearableDimensions = null; var wearableDimensions = null;
marketplaceItemTesterId = defaultFor(marketplaceItemTesterId, -1);
if (itemType === "contentSet") { if (itemType === "contentSet") {
console.log("Item is a content set; codepath shouldn't go here."); console.log("Item is a content set; codepath shouldn't go here.");
@ -816,7 +857,8 @@ var resourceObjectsInTest = [];
function signalNewResourceObjectInTest(resourceObject) { function signalNewResourceObjectInTest(resourceObject) {
ui.tablet.sendToQml({ ui.tablet.sendToQml({
method: "newResourceObjectInTest", method: "newResourceObjectInTest",
resourceObject: resourceObject }); resourceObject: resourceObject
});
} }
var onQmlMessageReceived = function onQmlMessageReceived(message) { var onQmlMessageReceived = function onQmlMessageReceived(message) {
@ -877,11 +919,15 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) {
case 'checkout_rezClicked': case 'checkout_rezClicked':
case 'purchases_rezClicked': case 'purchases_rezClicked':
case 'tester_rezClicked': case 'tester_rezClicked':
rezEntity(message.itemHref, message.itemType); rezEntity(message.itemHref, message.itemType, message.itemId);
break; break;
case 'tester_newResourceObject': case 'tester_newResourceObject':
var resourceObject = message.resourceObject; var resourceObject = message.resourceObject;
resourceObjectsInTest[resourceObject.id] = resourceObject; resourceObjectsInTest = []; // REMOVE THIS once we support specific referrers
resourceObject.currentlyRecordingResources = false;
resourceObject.resourceAccessEventText = "";
resourceObjectsInTest[resourceObject.resourceObjectId] = resourceObject;
resourceObjectsInTest[resourceObject.resourceObjectId].resourceDataArray = [];
signalNewResourceObjectInTest(resourceObject); signalNewResourceObjectInTest(resourceObject);
break; break;
case 'tester_updateResourceObjectAssetType': case 'tester_updateResourceObjectAssetType':
@ -890,6 +936,13 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) {
case 'tester_deleteResourceObject': case 'tester_deleteResourceObject':
delete resourceObjectsInTest[message.objectId]; delete resourceObjectsInTest[message.objectId];
break; break;
case 'tester_updateResourceRecordingStatus':
resourceObjectsInTest[message.objectId].currentlyRecordingResources = message.status;
if (message.status) {
resourceObjectsInTest[message.objectId].resourceDataArray = [];
resourceObjectsInTest[message.objectId].resourceAccessEventText = "";
}
break;
case 'header_marketplaceImageClicked': case 'header_marketplaceImageClicked':
case 'purchases_backClicked': case 'purchases_backClicked':
openMarketplace(message.referrerURL); openMarketplace(message.referrerURL);
@ -1029,16 +1082,22 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) {
}; };
function pushResourceObjectsInTest() { function pushResourceObjectsInTest() {
var maxObjectId = -1; var maxResourceObjectId = -1;
for (var objectId in resourceObjectsInTest) { var length = resourceObjectsInTest.length;
signalNewResourceObjectInTest(resourceObjectsInTest[objectId]); for (var i = 0; i < length; i++) {
maxObjectId = (maxObjectId < objectId) ? parseInt(objectId) : maxObjectId; if (i in resourceObjectsInTest) {
signalNewResourceObjectInTest(resourceObjectsInTest[i]);
var resourceObjectId = resourceObjectsInTest[i].resourceObjectId;
maxResourceObjectId = (maxResourceObjectId < resourceObjectId) ? parseInt(resourceObjectId) : maxResourceObjectId;
}
} }
// N.B. Thinking about removing the following sendToQml? Be sure // N.B. Thinking about removing the following sendToQml? Be sure
// that the marketplace item tester QML has heard from us, at least // that the marketplace item tester QML has heard from us, at least
// so that it can indicate to the user that all of the resoruce // so that it can indicate to the user that all of the resoruce
// objects in test have been transmitted to it. // objects in test have been transmitted to it.
ui.tablet.sendToQml({ method: "nextObjectIdInTest", id: maxObjectId + 1 }); //ui.tablet.sendToQml({ method: "nextObjectIdInTest", id: maxResourceObjectId + 1 });
// Since, for now, we only support 1 object in test, always send id: 0
ui.tablet.sendToQml({ method: "nextObjectIdInTest", id: 0 });
} }
// Function Name: onTabletScreenChanged() // Function Name: onTabletScreenChanged()
@ -1193,6 +1252,7 @@ function startup() {
ui.tablet.webEventReceived.connect(onWebEventReceived); ui.tablet.webEventReceived.connect(onWebEventReceived);
Wallet.walletStatusChanged.connect(sendCommerceSettings); Wallet.walletStatusChanged.connect(sendCommerceSettings);
Window.messageBoxClosed.connect(onMessageBoxClosed); Window.messageBoxClosed.connect(onMessageBoxClosed);
ResourceRequestObserver.resourceRequestEvent.connect(onResourceRequestEvent);
Wallet.refreshWalletStatus(); Wallet.refreshWalletStatus();
} }
@ -1226,6 +1286,7 @@ function shutdown() {
GlobalServices.myUsernameChanged.disconnect(onUsernameChanged); GlobalServices.myUsernameChanged.disconnect(onUsernameChanged);
Entities.canWriteAssetsChanged.disconnect(onCanWriteAssetsChanged); Entities.canWriteAssetsChanged.disconnect(onCanWriteAssetsChanged);
ContextOverlay.contextOverlayClicked.disconnect(openInspectionCertificateQML); ContextOverlay.contextOverlayClicked.disconnect(openInspectionCertificateQML);
ResourceRequestObserver.resourceRequestEvent.disconnect(onResourceRequestEvent);
off(); off();
} }