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;
}
auto request = DependencyManager::get<ResourceManager>()->createResourceRequest(this, scriptURL);
auto request = DependencyManager::get<ResourceManager>()->createResourceRequest(
this, scriptURL, true, -1, "Agent::requestScript");
if (!request) {
qWarning() << "Could not create ResourceRequest for Agent script at" << scriptURL.toString();

View file

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

View file

@ -35,6 +35,7 @@
#include "AssignmentClientLogging.h"
#include "AssignmentFactory.h"
#include "ResourceRequestObserver.h"
const QString ASSIGNMENT_CLIENT_TARGET_NAME = "assignment-client";
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<StatTracker>();
DependencyManager::set<AccountManager>();
DependencyManager::set<ResourceRequestObserver>();
auto addressManager = DependencyManager::set<AddressManager>();

View file

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

View file

@ -31,7 +31,7 @@ Rectangle {
scaleSlider.notify = false;
scaleSlider.value = Math.round(avatarScale * 10);
scaleSlider.notify = true;;
scaleSlider.notify = true;
if (settings.dominantHand === 'left') {
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
//
// Created by Zach Fox on 2018-09-05
// Created by Kerry Ivan Kurian on 2018-09-05
// 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.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick.Dialogs 1.0
import QtQuick 2.10
import QtQuick.Layouts 1.1
import QtQuick.Controls 2.3
import Hifi 1.0 as Hifi
import "../../../styles-uit" as HifiStylesUit
import "../../../controls-uit" as HifiControlsUit
import "qrc:////qml//styles-uit" as HifiStylesUit
import "qrc:////qml//controls-uit" as HifiControlsUit
@ -27,33 +25,223 @@ Rectangle {
property string installedApps
property var nextResourceObjectId: 0
signal sendToScript(var message)
HifiStylesUit.HifiConstants { id: hifi }
ListModel { id: resourceListModel }
color: hifi.colors.white
color: hifi.colors.darkGray
AnimatedImage {
id: spinner;
source: "spinner.gif"
width: 74;
height: width;
anchors.verticalCenter: parent.verticalCenter;
anchors.horizontalCenter: parent.horizontalCenter;
//
// 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 {
source: "spinner.gif"
width: 74
height: width
anchors.centerIn: parent
}
}
Rectangle {
id: instructionsContainer
z: 998
color: hifi.colors.darkGray
visible: resourceListModel.count === 0 && !spinner.visible
anchors.top: titleBarContainer.bottom
anchors.topMargin: 20
anchors.left: parent.left
anchors.leftMargin: 20
anchors.right: parent.right
anchors.rightMargin: 20
anchors.bottom: buttonContainer.top
anchors.bottomMargin: 20
HifiStylesUit.RalewayRegular {
text: "Use Marketplace Item Tester to test out your items before submitting them to the Marketplace." +
"\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
}
}
ListView {
id: itemList
visible: !instructionsContainer.visible
anchors.top: titleBarContainer.bottom
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
spacing: 8
delegate: ItemUnderTest { }
}
Item {
id: buttonContainer
anchors.left: parent.left
anchors.leftMargin: 12
anchors.right: parent.right
anchors.rightMargin: 12
anchors.bottom: parent.bottom
anchors.bottomMargin: 12
height: 40
property string currentAction
property var actions: {
"Load File": function() {
buttonContainer.currentAction = "load file";
Window.browseChanged.connect(onResourceSelected);
Window.browseAsync("Please select a file (*.app.json *.json *.fst *.json.gz)", "", "Assets (*.app.json *.json *.fst *.json.gz)");
},
"Load URL": function() {
buttonContainer.currentAction = "load url";
Window.promptTextChanged.connect(onResourceSelected);
Window.promptAsync("Please enter a URL", "");
}
}
function onResourceSelected(resource) {
// It is possible that we received the present signal
// from something other than our browserAsync window.
// Alas, there is nothing we can do about that so charge
// ahead as though we are sure the present signal is one
// we expect.
print("!!!! resource selected");
switch(currentAction) {
case "load file":
Window.browseChanged.disconnect(onResourceSelected);
break
case "load url":
Window.promptTextChanged.disconnect(onResourceSelected);
break;
}
if (resource) {
print("!!!! building resource object");
var resourceObj = buildResourceObj(resource);
print("!!!! notifying script of resource object");
sendToScript({
method: 'tester_newResourceObject',
resourceObject: resourceObj
});
}
}
HifiControlsUit.Button {
enabled: !spinner.visible
anchors.right: parent.horizontalCenter
anchors.rightMargin: width/4
anchors.verticalCenter: parent.verticalCenter
color: hifi.buttons.blue
fontSize: 20
text: "Load File"
width: parent.width / 3
height: parent.height
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;
}
}
@ -64,227 +252,26 @@ Rectangle {
resource.match(/\.json\.gz$/) ? "content set" :
resource.match(/\.json$/) ? "entity or wearable" :
"unknown");
return { "id": nextResourceObjectId++,
// Uncomment this once we support more than one item in test at the same time
//nextResourceObjectId++;
return { "resourceObjectId": nextResourceObjectId,
"resource": resource,
"assetType": assetType };
}
function installResourceObj(resourceObj) {
if ("application" === resourceObj.assetType) {
Commerce.installApp(resourceObj.resource);
}
}
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) {
function rezEntity(resource, entityType, resourceObjectId) {
print("!!!! tester_rezClicked");
sendToScript({
method: 'tester_rezClicked',
itemHref: toUrl(resource),
itemType: entityType});
itemType: entityType,
itemId: resourceObjectId });
}
ListView {
anchors.fill: parent
anchors.leftMargin: 12
anchors.bottomMargin: 40
anchors.rightMargin: 12
model: resourceListModel
spacing: 5
interactive: false
delegate: RowLayout {
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 {
Layout.preferredWidth: root.width * .6
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.leftMargin: 12
}
footerPositioning: ListView.OverlayFooter
footer: Row {
id: rootActions
spacing: 20
anchors.horizontalCenter: parent.horizontalCenter
property string currentAction
property var actions: {
"Load File": function(){
rootActions.currentAction = "load file";
Window.browseChanged.connect(onResourceSelected);
Window.browseAsync("Please select a file (*.app.json *.json *.fst *.json.gz)", "", "Assets (*.app.json *.json *.fst *.json.gz)");
},
"Load URL": function(){
rootActions.currentAction = "load url";
Window.promptTextChanged.connect(onResourceSelected);
Window.promptAsync("Please enter a URL", "");
}
}
function onResourceSelected(resource) {
// It is possible that we received the present signal
// from something other than our browserAsync window.
// Alas, there is nothing we can do about that so charge
// ahead as though we are sure the present signal is one
// we expect.
switch(currentAction) {
case "load file":
Window.browseChanged.disconnect(onResourceSelected);
break
case "load url":
Window.promptTextChanged.disconnect(onResourceSelected);
break;
}
if (resource) {
var resourceObj = buildResourceObj(resource);
installResourceObj(resourceObj);
sendToScript({
method: 'tester_newResourceObject',
resourceObject: resourceObj });
}
}
Repeater {
model: [ "Load File", "Load URL" ]
HifiControlsUit.Button {
color: hifi.buttons.blue
fontSize: 20
text: modelData
width: root.width / 3
height: 40
onClicked: actions[text]()
}
}
}
}
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/Wallet.h"
#include "commerce/QmlCommerce.h"
#include "ResourceRequestObserver.h"
#include "webbrowser/WebBrowserSuggestionsEngine.h"
#include <DesktopPreviewProvider.h>
@ -949,6 +950,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
DependencyManager::set<TTSScriptingInterface>();
DependencyManager::set<FadeEffect>();
DependencyManager::set<ResourceRequestObserver>();
return previousSessionCrashed;
}
@ -3141,6 +3143,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) {
surfaceContext->setContextProperty("ContextOverlay", DependencyManager::get<ContextOverlayInterface>().data());
surfaceContext->setContextProperty("Wallet", DependencyManager::get<WalletScriptingInterface>().data());
surfaceContext->setContextProperty("HiFiAbout", AboutUtil::getInstance());
surfaceContext->setContextProperty("ResourceRequestObserver", DependencyManager::get<ResourceRequestObserver>().data());
if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
surfaceContext->setContextProperty("Steam", new SteamScriptingInterface(engine, steamClient.get()));
@ -5034,12 +5037,12 @@ void Application::saveSettings() const {
PluginManager::getInstance()->saveSettings();
}
bool Application::importEntities(const QString& urlOrFilename) {
bool Application::importEntities(const QString& urlOrFilename, const bool isObservable, const qint64 callerId) {
bool success = false;
_entityClipboard->withWriteLock([&] {
_entityClipboard->eraseAllOctreeElements();
success = _entityClipboard->readFromURL(urlOrFilename);
success = _entityClipboard->readFromURL(urlOrFilename, isObservable, callerId);
if (success) {
_entityClipboard->reaverageOctreeElements();
}
@ -6823,6 +6826,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
scriptEngine->registerGlobalObject("Wallet", DependencyManager::get<WalletScriptingInterface>().data());
scriptEngine->registerGlobalObject("AddressManager", DependencyManager::get<AddressManager>().data());
scriptEngine->registerGlobalObject("HifiAbout", AboutUtil::getInstance());
scriptEngine->registerGlobalObject("ResourceRequestObserver", DependencyManager::get<ResourceRequestObserver>().data());
qScriptRegisterMetaType(scriptEngine.data(), OverlayIDtoScriptValue, OverlayIDfromScriptValue);
@ -7209,7 +7213,8 @@ void Application::addAssetToWorldFromURL(QString url) {
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);
request->send();
}

View file

@ -342,7 +342,7 @@ public slots:
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, 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 updateSystemTabletMode();
void goToErrorDomainURL(QUrl errorDomainURL);

View file

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

View file

@ -26,6 +26,7 @@
#include <AccountManager.h>
#include <AddressManager.h>
#include <AudioClient.h>
#include <ClientTraitsHandler.h>
#include <display-plugins/DisplayPlugin.h>
#include <FSTReader.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) {
// update moving average of HMD facing in xz plane.
const float HMD_FACING_TIMESCALE = getRotationRecenterFilterLength();
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;
setHipToHandController(computeHandAzimuth());
@ -493,11 +558,36 @@ void MyAvatar::update(float deltaTime) {
_smoothOrientationTimer += deltaTime;
}
float newHeightReading = getControllerPoseInAvatarFrame(controller::Action::HEAD).getTranslation().y;
int newHeightReadingInCentimeters = glm::floor(newHeightReading * CENTIMETERS_PER_METER);
_recentModeReadings.insert(newHeightReadingInCentimeters);
setCurrentStandingHeight(computeStandingHeightMode(getControllerPoseInAvatarFrame(controller::Action::HEAD)));
setAverageHeadRotation(computeAverageHeadRotation(getControllerPoseInAvatarFrame(controller::Action::HEAD)));
controller::Pose newHeightReading = getControllerPoseInSensorFrame(controller::Action::HEAD);
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);
setCurrentStandingHeight(computeStandingHeightMode(newHeightReading));
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) {
auto sensorHeadPose = getControllerPoseInSensorFrame(controller::Action::HEAD);
@ -3557,12 +3647,9 @@ glm::vec3 MyAvatar::computeCounterBalance() {
glm::vec3 counterBalancedCg = (1.0f / DEFAULT_AVATAR_HIPS_MASS) * counterBalancedForHead;
// 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));
float headMinusHipXz = glm::length(xzDiff);
float headHipDefault = glm::length(tposeHead - tposeHips);
float hipFootDefault = tposeHips.y - tposeRightFoot.y;
float sitSquatThreshold = tposeHips.y - (UPPER_LEG_FRACTION * hipFootDefault);
float hipHeight = 0.0f;
if (headHipDefault > headMinusHipXz) {
hipHeight = sqrtf((headHipDefault * headHipDefault) - (headMinusHipXz * headMinusHipXz));
@ -3574,10 +3661,6 @@ glm::vec3 MyAvatar::computeCounterBalance() {
if (counterBalancedCg.y > (tposeHips.y + 0.05f)) {
// if the height is higher than default hips, clamp to default hips
counterBalancedCg.y = tposeHips.y + 0.05f;
} else if (counterBalancedCg.y < sitSquatThreshold) {
//do a height reset
setResetMode(true);
_follow.activate(FollowHelper::Vertical);
}
return counterBalancedCg;
}
@ -3818,6 +3901,18 @@ bool MyAvatar::getIsInWalkingState() const {
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 {
return _walkSpeed.get() * _walkSpeedScalar;
}
@ -3838,6 +3933,61 @@ void MyAvatar::setIsInWalkingState(bool 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) {
_walkSpeed.set(value);
}
@ -3854,6 +4004,14 @@ float MyAvatar::getSprintSpeed() const {
return _sprintSpeed.get();
}
void MyAvatar::setSitStandStateChange(bool stateChanged) {
_sitStandStateChange = stateChanged;
}
float MyAvatar::getSitStandStateChange() const {
return _sitStandStateChange;
}
QVector<QString> MyAvatar::getScriptUrls() {
QVector<QString> scripts = _skeletonModel->isLoaded() ? _skeletonModel->getFBXGeometry().scripts : QVector<QString>();
return scripts;
@ -3997,6 +4155,7 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar,
// 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 offset = extractTranslation(desiredBodyMatrix) - extractTranslation(currentBodyMatrix);
controller::Pose currentHeadPose = myAvatar.getControllerPoseInAvatarFrame(controller::Action::HEAD);
float forwardLeanAmount = glm::dot(forward, 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_BACKWARD_LEAN = 0.1f;
if (forwardLeanAmount > 0 && forwardLeanAmount > MAX_FORWARD_LEAN) {
return true;
bool stepDetected = false;
if (myAvatar.getIsInSittingState()) {
if (!withinBaseOfSupport(currentHeadPose)) {
stepDetected = true;
}
} else if (forwardLeanAmount > 0 && forwardLeanAmount > MAX_FORWARD_LEAN) {
stepDetected = true;
} else if (forwardLeanAmount < 0 && forwardLeanAmount < -MAX_BACKWARD_LEAN) {
return true;
stepDetected = true;
} else {
stepDetected = fabs(lateralLeanAmount) > MAX_LATERAL_LEAN;
}
return fabs(lateralLeanAmount) > MAX_LATERAL_LEAN;
return stepDetected;
}
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 currentLeftHandPose = myAvatar.getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND);
controller::Pose currentRightHandPose = myAvatar.getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND);
controller::Pose currentHeadSensorPose = myAvatar.getControllerPoseInSensorFrame(controller::Action::HEAD);
bool stepDetected = false;
float myScale = myAvatar.getAvatarScale();
@ -4030,7 +4195,7 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) cons
} else {
if (!withinBaseOfSupport(currentHeadPose) &&
headAngularVelocityBelowThreshold(currentHeadPose) &&
isWithinThresholdHeightMode(currentHeadPose, myAvatar.getCurrentStandingHeight(), myScale) &&
isWithinThresholdHeightMode(currentHeadSensorPose, myAvatar.getCurrentStandingHeight(), myScale) &&
handDirectionMatchesHeadDirection(currentLeftHandPose, currentRightHandPose, currentHeadPose) &&
handAngularVelocityBelowThreshold(currentLeftHandPose, currentRightHandPose) &&
headVelocityGreaterThanThreshold(currentHeadPose) &&
@ -4046,6 +4211,7 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) cons
glm::vec3 currentHeadPosition = currentHeadPose.getTranslation();
float anatomicalHeadToHipsDistance = glm::length(defaultHeadPosition - defaultHipsPosition);
if (!isActive(Horizontal) &&
(!isActive(Vertical)) &&
(glm::length(currentHeadPosition - defaultHipsPosition) > (anatomicalHeadToHipsDistance + (DEFAULT_AVATAR_SPINE_STRETCH_LIMIT * anatomicalHeadToHipsDistance)))) {
myAvatar.setResetMode(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 {
const float CYLINDER_TOP = 0.1f;
const float CYLINDER_BOTTOM = -1.5f;
const float SITTING_BOTTOM = -0.02f;
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,
@ -4085,9 +4273,10 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat
}
}
} else {
// center of gravity model is not enabled
if (!isActive(Horizontal) && (shouldActivateHorizontal(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) {
activate(Horizontal);
if (myAvatar.getEnableStepResetRotation()) {
if (myAvatar.getEnableStepResetRotation() && !myAvatar.getIsInSittingState()) {
activate(Rotation);
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)) {
activate(Vertical);
if (_squatDetected) {
_squatDetected = false;
}
}
} else {
if (!isActive(Rotation) && getForceActivateRotation()) {
@ -4144,7 +4336,7 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat
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()) {
float dt = myAvatar.getCharacterController()->getFollowTime();
decrementTimeRemaining(dt);
@ -4161,6 +4353,11 @@ glm::mat4 MyAvatar::FollowHelper::postPhysicsUpdate(const MyAvatar& myAvatar, co
glm::mat4 newBodyMat = createMatFromQuatAndPos(sensorAngularDisplacement * glmExtractRotation(currentBodyMatrix),
sensorLinearDisplacement + extractTranslation(currentBodyMatrix));
if (myAvatar.getSitStandStateChange()) {
myAvatar.setSitStandStateChange(false);
deactivate(Vertical);
setTranslation(newBodyMat, extractTranslation(myAvatar.deriveBodyFromHMDSensor()));
}
return newBodyMat;
} else {
return currentBodyMatrix;

View file

@ -21,7 +21,6 @@
#include <AvatarConstants.h>
#include <avatars-renderer/Avatar.h>
#include <avatars-renderer/ScriptAvatar.h>
#include <ClientTraitsHandler.h>
#include <controllers/Pose.h>
#include <controllers/Actions.h>
#include <EntityItem.h>
@ -142,6 +141,8 @@ class MyAvatar : public Avatar {
* @property {number} walkSpeed
* @property {number} walkBackwardSpeed
* @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
* 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 walkBackwardSpeed READ getWalkBackwardSpeed WRITE setWalkBackwardSpeed);
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_RIGHT_HAND = "right";
@ -262,6 +266,15 @@ public:
};
Q_ENUM(DriveKeys)
enum SitStandModelType {
ForceSit = 0,
ForceStand,
Auto,
DisableHMDLean,
NumSitStandTypes
};
Q_ENUM(SitStandModelType)
explicit MyAvatar(QThread* thread);
virtual ~MyAvatar();
@ -1121,12 +1134,21 @@ public:
void setIsInWalkingState(bool isWalking);
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);
float getWalkSpeed() const;
void setWalkBackwardSpeed(float value);
float getWalkBackwardSpeed() const;
void setSprintSpeed(float value);
float getSprintSpeed() const;
void setSitStandStateChange(bool stateChanged);
float getSitStandStateChange() const;
void updateSitStandState(float newHeightReading, float dt);
QVector<QString> getScriptUrls();
@ -1532,6 +1554,7 @@ signals:
*/
void disableHandTouchForIDChanged(const QUuid& entityID, bool disable);
private slots:
void leaveDomain();
void updateCollisionCapsuleCache();
@ -1742,7 +1765,7 @@ private:
bool shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const;
bool shouldActivateHorizontalCG(MyAvatar& myAvatar) const;
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;
void setForceActivateRotation(bool val);
bool getForceActivateVertical() const;
@ -1751,6 +1774,7 @@ private:
void setForceActivateHorizontal(bool val);
bool getToggleHipsFollowing() const;
void setToggleHipsFollowing(bool followHead);
bool _squatDetected { false };
std::atomic<bool> _forceActivateRotation { false };
std::atomic<bool> _forceActivateVertical { false };
std::atomic<bool> _forceActivateHorizontal { false };
@ -1820,10 +1844,13 @@ private:
std::mutex _pinnedJointsMutex;
std::vector<int> _pinnedJoints;
void updateChildCauterization(SpatiallyNestablePointer object, bool cauterize);
// height of user in sensor space, when standing erect.
ThreadSafeValueCache<float> _userHeight { DEFAULT_AVATAR_HEIGHT };
void updateChildCauterization(SpatiallyNestablePointer object, bool cauterize);
float _averageUserHeightSensorSpace { _userHeight.get() };
bool _sitStandStateChange { false };
ThreadSafeValueCache<bool> _lockSitStandState { false };
// max unscaled forward movement speed
ThreadSafeValueCache<float> _walkSpeed { DEFAULT_AVATAR_MAX_WALKING_SPEED };
@ -1831,6 +1858,11 @@ private:
ThreadSafeValueCache<float> _sprintSpeed { AVATAR_SPRINT_SPEED_SCALAR };
float _walkSpeedScalar { AVATAR_WALK_SPEED_SCALAR };
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
bool _shouldLoadScripts { false };

View file

@ -46,7 +46,7 @@ static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) {
}
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
hipsMat = myAvatar->deriveBodyUsingCgModel();
} else {
@ -250,6 +250,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
bool headExists = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Head"), currentHeadPose);
bool hipsExists = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Hips"), currentHipsPose);
if (spine2Exists && headExists && hipsExists) {
AnimPose rigSpaceYaw(myAvatar->getSpine2RotationRigSpace());
glm::vec3 u, v, w;
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;
}
bool QmlCommerce::installApp(const QString& itemHref) {
bool QmlCommerce::installApp(const QString& itemHref, const bool& alsoOpenImmediately) {
if (!QDir(_appsPath).exists()) {
if (!QDir().mkdir(_appsPath)) {
qCDebug(commerce) << "Couldn't make _appsPath directory.";
@ -325,7 +325,8 @@ bool QmlCommerce::installApp(const QString& 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) {
qCDebug(commerce) << "Couldn't create resource request for app.";
@ -357,13 +358,22 @@ bool QmlCommerce::installApp(const QString& itemHref) {
QJsonObject appFileJsonObject = appFileJsonDocument.object();
QString scriptUrl = appFileJsonObject["scriptURL"].toString();
if ((DependencyManager::get<ScriptEngines>()->loadScript(scriptUrl.trimmed())).isNull()) {
qCDebug(commerce) << "Couldn't load script.";
return false;
// 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()) {
qCDebug(commerce) << "Couldn't load script.";
return false;
}
QFileInfo appFileInfo(appFile);
emit appInstalled(appFileInfo.baseName());
}
if (alsoOpenImmediately) {
QmlCommerce::openApp(itemHref);
}
QFileInfo appFileInfo(appFile);
emit appInstalled(appFileInfo.baseName());
return true;
});
request->send();

View file

@ -88,7 +88,7 @@ protected:
Q_INVOKABLE void replaceContentSet(const QString& itemHref, const QString& certificateID);
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 openApp(const QString& appHref);

View file

@ -46,11 +46,17 @@ bool ClipboardScriptingInterface::exportEntities(const QString& filename, float
return retVal;
}
bool ClipboardScriptingInterface::importEntities(const QString& filename) {
bool ClipboardScriptingInterface::importEntities(
const QString& filename,
const bool isObservable,
const qint64 callerId
) {
bool retVal;
BLOCKING_INVOKE_METHOD(qApp, "importEntities",
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;
}

View file

@ -50,9 +50,11 @@ public:
* You can generate a JSON file using {@link Clipboard.exportEntities}.
* @function Clipboard.importEntities
* @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>.
*/
Q_INVOKABLE bool importEntities(const QString& filename);
Q_INVOKABLE bool importEntities(const QString& filename, const bool isObservable = true, const qint64 callerId = -1);
/**jsdoc
* 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);
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 setter = [=](float value) { myAvatar->setUserHeight(value); };

View file

@ -59,6 +59,7 @@
#include "raypick/PointerScriptingInterface.h"
#include <display-plugins/CompositorHelper.h>
#include "AboutUtil.h"
#include "ResourceRequestObserver.h"
static int MAX_WINDOW_SIZE = 4096;
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("Reticle", qApp->getApplicationCompositor().getReticleInterface());
_webSurface->getSurfaceContext()->setContextProperty("HiFiAbout", AboutUtil::getInstance());
_webSurface->getSurfaceContext()->setContextProperty("ResourceRequestObserver", DependencyManager::get<ResourceRequestObserver>().data());
// Override min fps for tablet UI, for silky smooth scrolling
setMaxFPS(90);

View file

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

View file

@ -44,6 +44,7 @@
#include "AvatarLogging.h"
#include "AvatarTraits.h"
#include "ClientTraitsHandler.h"
#include "ResourceRequestObserver.h"
//#define WANT_DEBUG
@ -2161,11 +2162,21 @@ void AvatarData::updateJointMappings() {
}
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();
QNetworkRequest networkRequest = QNetworkRequest(_skeletonModelURL);
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
DependencyManager::get<ResourceRequestObserver>()->update(
_skeletonModelURL, -1, "AvatarData::updateJointMappings");
QNetworkReply* networkReply = networkAccessManager.get(networkRequest);
//
////
connect(networkReply, &QNetworkReply::finished, this, &AvatarData::setJointMappingsFromNetworkReply);
}
}

View file

@ -31,7 +31,27 @@ ClientTraitsHandler::ClientTraitsHandler(AvatarData* owningAvatar) :
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() {
Lock lock(_traitLock);
// re-set the current version to 0
_currentTraitVersion = AvatarTraits::DEFAULT_TRAIT_VERSION;
@ -46,6 +66,8 @@ void ClientTraitsHandler::resetForNewMixer() {
}
void ClientTraitsHandler::sendChangedTraitsToMixer() {
Lock lock(_traitLock);
if (hasChangedTraits() || _shouldPerformInitialSend) {
// we have at least one changed trait to send
@ -113,6 +135,7 @@ void ClientTraitsHandler::sendChangedTraitsToMixer() {
void ClientTraitsHandler::processTraitOverride(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
if (sendingNode->getType() == NodeType::AvatarMixer) {
Lock lock(_traitLock);
while (message->getBytesLeftToRead()) {
AvatarTraits::TraitType traitType;
message->readPrimitive(&traitType);

View file

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

View file

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

View file

@ -953,7 +953,8 @@ bool GLTFReader::doesResourceExist(const QString& 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) {
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) {
auto request = DependencyManager::get<ResourceManager>()->createResourceRequest(nullptr, url);
auto request = DependencyManager::get<ResourceManager>()->createResourceRequest(
nullptr, url, true, -1, "(OBJReader) requestData");
if (!request) {
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
// The actual requested url is _activeUrl and will not contain the fragment
_url.setFragment("head");
_ktxHeaderRequest = DependencyManager::get<ResourceManager>()->createResourceRequest(this, _activeUrl);
_ktxHeaderRequest = DependencyManager::get<ResourceManager>()->createResourceRequest(
this, _activeUrl, true, -1, "NetworkTexture::makeRequest");
if (!_ktxHeaderRequest) {
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;
_ktxMipRequest = DependencyManager::get<ResourceManager>()->createResourceRequest(this, _activeUrl);
_ktxMipRequest = DependencyManager::get<ResourceManager>()->createResourceRequest(
this, _activeUrl, true, -1, "NetworkTexture::startMipRangeRequest");
if (!_ktxMipRequest) {
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;
AssetResourceRequest::AssetResourceRequest(const QUrl& url) :
ResourceRequest(url)
AssetResourceRequest::AssetResourceRequest(
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);
}

View file

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

View file

@ -14,7 +14,8 @@
#include "ResourceManager.h"
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);
connect(_resourceRequest, &AssetResourceRequest::progress, this, &AtpReply::downloadProgress);

View file

@ -19,7 +19,12 @@
class FileResourceRequest : public ResourceRequest {
Q_OBJECT
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:
virtual void doSend() override;

View file

@ -21,7 +21,12 @@
class HTTPResourceRequest : public ResourceRequest {
Q_OBJECT
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();
protected:

View file

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

View file

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

View file

@ -10,6 +10,7 @@
//
#include "ResourceCache.h"
#include "ResourceRequestObserver.h"
#include <cfloat>
#include <cmath>
@ -252,7 +253,9 @@ ResourceCache::ResourceCache(QObject* parent) : QObject(parent) {
}
}
ResourceCache::~ResourceCache() {}
ResourceCache::~ResourceCache() {
clearUnusedResources();
}
void ResourceCache::clearATPAssets() {
{
@ -338,28 +341,31 @@ QSharedPointer<Resource> ResourceCache::getResource(const QUrl& url, const QUrl&
}
if (resource) {
removeUnusedResource(resource);
return resource;
}
if (!url.isValid() && !url.isEmpty() && fallback.isValid()) {
return getResource(fallback, QUrl());
if (!resource && !url.isValid() && !url.isEmpty() && fallback.isValid()) {
resource = getResource(fallback, QUrl());
}
resource = createResource(
url,
fallback.isValid() ? getResource(fallback, QUrl()) : QSharedPointer<Resource>(),
extra);
resource->setSelf(resource);
resource->setCache(this);
resource->moveToThread(qApp->thread());
connect(resource.data(), &Resource::updateSize, this, &ResourceCache::updateTotalSize);
{
QWriteLocker locker(&_resourcesLock);
_resources.insert(url, resource);
if (!resource) {
resource = createResource(
url,
fallback.isValid() ? getResource(fallback, QUrl()) : QSharedPointer<Resource>(),
extra);
resource->setSelf(resource);
resource->setCache(this);
resource->moveToThread(qApp->thread());
connect(resource.data(), &Resource::updateSize, this, &ResourceCache::updateTotalSize);
{
QWriteLocker locker(&_resourcesLock);
_resources.insert(url, resource);
}
removeUnusedResource(resource);
resource->ensureLoading();
}
removeUnusedResource(resource);
resource->ensureLoading();
DependencyManager::get<ResourceRequestObserver>()->update(
resource->getURL(), -1, "ResourceCache::getResource");
return resource;
}
@ -682,7 +688,8 @@ void Resource::makeRequest() {
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) {
qCDebug(networking).noquote() << "Failed to get request for" << _url.toDisplayString();

View file

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

View file

@ -34,7 +34,12 @@ public:
QString normalizeURL(const QString& urlString);
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 cleanup();

View file

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

View file

@ -40,7 +40,20 @@ const QString STAT_FILE_RESOURCE_TOTAL_BYTES = "FILEBytesDownloaded";
class ResourceRequest : public QObject {
Q_OBJECT
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;
enum State {
@ -99,6 +112,9 @@ protected:
bool _rangeRequestSuccessful { false };
uint64_t _totalSizeOfResource { 0 };
int64_t _lastRecordedBytesDownloaded { 0 };
bool _isObservable;
qint64 _callerId;
QString _extra;
};
#endif

View file

@ -228,13 +228,13 @@ qint64 Socket::writeDatagram(const QByteArray& datagram, const HifiSockAddr& soc
return bytesWritten;
}
Connection* Socket::findOrCreateConnection(const HifiSockAddr& sockAddr) {
Connection* Socket::findOrCreateConnection(const HifiSockAddr& sockAddr, bool filterCreate) {
auto it = _connectionsHash.find(sockAddr);
if (it == _connectionsHash.end()) {
// 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
#ifdef UDT_CONNECTION_DEBUG
qCDebug(networking) << "Socket::findOrCreateConnection refusing to create connection for" << sockAddr
@ -376,7 +376,7 @@ void Socket::readPendingDatagrams() {
controlPacket->setReceiveTime(receiveTime);
// move this control packet to the matching connection, if there is one
auto connection = findOrCreateConnection(senderSockAddr);
auto connection = findOrCreateConnection(senderSockAddr, true);
if (connection) {
connection->processControl(move(controlPacket));
@ -394,7 +394,7 @@ void Socket::readPendingDatagrams() {
if (!_packetFilterOperator || _packetFilterOperator(*packet)) {
if (packet->isReliable()) {
// 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(),
packet->getDataSize(),
@ -409,7 +409,7 @@ void Socket::readPendingDatagrams() {
}
if (packet->isPartOfMessage()) {
auto connection = findOrCreateConnection(senderSockAddr);
auto connection = findOrCreateConnection(senderSockAddr, true);
if (connection) {
connection->queueReceivedMessagePacket(std::move(packet));
}

View file

@ -109,7 +109,7 @@ private slots:
private:
void setSystemBufferSizes();
Connection* findOrCreateConnection(const HifiSockAddr& sockAddr);
Connection* findOrCreateConnection(const HifiSockAddr& sockAddr, bool filterCreation = false);
bool socketMatchesNodeOrDomain(const HifiSockAddr& sockAddr);
// 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();
}
bool Octree::readFromURL(const QString& urlString) {
bool Octree::readFromURL(
const QString& urlString,
const bool isObservable,
const qint64 callerId
) {
QString trimmedUrl = urlString.trimmed();
QString marketplaceID = getMarketplaceID(trimmedUrl);
auto request =
std::unique_ptr<ResourceRequest>(DependencyManager::get<ResourceManager>()->createResourceRequest(this, trimmedUrl));
qDebug() << "!!!!! going to createResourceRequest " << callerId;
auto request = std::unique_ptr<ResourceRequest>(
DependencyManager::get<ResourceManager>()->createResourceRequest(
this, trimmedUrl, isObservable, callerId, "Octree::readFromURL"));
if (!request) {
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
QIODevice *device = inputStream.device();
char firstChar;
@ -809,7 +819,11 @@ QJsonDocument addMarketplaceIDToDocumentEntities(QJsonDocument& doc, const QStri
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
// we get an eof. Leave streamLength parameter for consistency.

View file

@ -210,7 +210,7 @@ public:
// Octree importers
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 readSVOFromStream(uint64_t streamLength, QDataStream& inputStream);
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
void FileScriptingInterface::downloadZip(QString path, const QString 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]{
unzipFile(path, ""); // so intellisense isn't mad
});

View file

@ -109,7 +109,8 @@ void ScriptCache::getScriptContents(const QString& scriptOrURL, contentAvailable
#ifdef THREAD_DEBUGGING
qCDebug(scriptengine) << "about to call: ResourceManager::createResourceRequest(this, url); on thread [" << QThread::currentThread() << "] expected thread [" << thread() << "]";
#endif
auto request = DependencyManager::get<ResourceManager>()->createResourceRequest(nullptr, url);
auto request = DependencyManager::get<ResourceManager>()->createResourceRequest(
nullptr, url, true, -1, "ScriptCache::getScriptContents");
Q_ASSERT(request);
request->setCacheEnabled(!forceDownload);
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")
.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);
// 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 <NetworkingConstants.h>
#include "ResourceRequestObserver.h"
#include "ScriptEngine.h"
const QString METAVERSE_API_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/api/";
@ -189,7 +190,7 @@ void XMLHttpRequestClass::send(const QScriptValue& data) {
}
void XMLHttpRequestClass::doSend() {
DependencyManager::get<ResourceRequestObserver>()->update(_url, -1, "XMLHttpRequestClass::doSend");
_reply = NetworkAccessManager::getInstance().sendCustomRequest(_request, _method.toLatin1(), _sendData);
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))) {
qCWarning(oculusLog) << "Failed to acquire Oculus session" << ovr::getError();
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();
if (chaperone) {
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 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() {
for (var i = 0; i < this.ignoredEntities; i++) {
for (var i = 0; i < this.ignoredEntities.length; i++) {
var data = {
action: 'remove',
id: this.ignoredEntities[i]
@ -317,15 +317,6 @@ Script.include("/~/system/libraries/controllers.js");
if ((intersection.type === Picks.INTERSECTED_ENTITY && entityType === "Web") ||
intersection.type === Picks.INTERSECTED_OVERLAY || Window.isPointOnDesktopWindow(point2d)) {
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;
};
@ -386,7 +377,6 @@ Script.include("/~/system/libraries/controllers.js");
this.isReady = function (controllerData) {
if (HMD.active) {
if (this.notPointingAtEntity(controllerData)) {
this.restoreIgnoredEntities();
return makeRunningValues(false, [], []);
}
@ -398,17 +388,28 @@ Script.include("/~/system/libraries/controllers.js");
return makeRunningValues(true, [], []);
} else {
this.destroyContextOverlay();
this.restoreIgnoredEntities();
return makeRunningValues(false, [], []);
}
}
this.restoreIgnoredEntities();
return makeRunningValues(false, [], []);
};
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 ||
this.notPointingAtEntity(controllerData) || this.targetIsNull()) {
(this.notPointingAtEntity(controllerData) && Window.isPhysicsEnabled()) || this.targetIsNull()) {
this.endFarGrabAction();
Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity",
this.highlightedEntity);

View file

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

View file

@ -18,6 +18,7 @@ Script.include("/~/system/libraries/controllers.js");
this.hand = hand;
this.otherHand = this.hand === RIGHT_HAND ? LEFT_HAND : RIGHT_HAND;
this.running = false;
this.ignoredObjects = [];
this.parameters = makeDispatcherModuleParameters(
160,
@ -72,6 +73,48 @@ Script.include("/~/system/libraries/controllers.js");
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) {
// 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
@ -137,6 +180,10 @@ Script.include("/~/system/libraries/controllers.js");
return makeRunningValues(true, [], []);
}
}
if (Window.interstitialModeEnabled && Window.isPhysicsEnabled()) {
this.restoreIgnoredObjects();
}
return makeRunningValues(false, [], []);
};
@ -149,6 +196,7 @@ Script.include("/~/system/libraries/controllers.js");
var allowThisModule = !otherModuleRunning && !grabModuleNeedsToRun;
var isTriggerPressed = controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE;
var laserOn = isTriggerPressed || this.parameters.handLaser.allwaysOn;
this.addObjectToIgnoreList(controllerData);
if (allowThisModule) {
if (isTriggerPressed && !this.isPointingAtTriggerable(controllerData, isTriggerPressed, true)) {
// 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 MIN_LOADING_PROGRESS = 3.6;
var TOTAL_LOADING_PROGRESS = 3.8;
var EPSILON = 0.01;
var EPSILON = 0.05;
var TEXTURE_EPSILON = 0.01;
var isVisible = false;
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 MAX_LEFT_MARGIN = 1.9;
var INNER_CIRCLE_WIDTH = 4.7;
@ -63,9 +64,9 @@
var loadingSphereID = Overlays.addOverlay("model", {
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})),
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",
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),
url: Script.resolvePath("/~/system/assets/models/black-sphere.fbx"),
dimensions: DEFAULT_DIMENSIONS,
alpha: 1,
visible: isVisible,
@ -76,12 +77,12 @@
});
var anchorOverlay = Overlays.addOverlay("cube", {
dimensions: {x: 0.2, y: 0.2, z: 0.2},
dimensions: { x: 0.2, y: 0.2, z: 0.2 },
visible: false,
grabbable: false,
ignoreRayIntersection: true,
localPosition: {x: 0.0, y: getAnchorLocalYOffset(), z: DEFAULT_Z_OFFSET },
orientation: Quat.multiply(Quat.fromVec3Degrees({x: 0, y: 180, z: 0}), MyAvatar.orientation),
localPosition: { x: 0.0, y: getAnchorLocalYOffset(), z: DEFAULT_Z_OFFSET },
orientation: Quat.multiply(Quat.fromVec3Degrees({ x: 0, y: 180, z: 0 }), MyAvatar.orientation),
solid: true,
drawInFront: true,
parentID: loadingSphereID
@ -113,7 +114,6 @@
backgroundAlpha: 1,
lineHeight: 0.13,
visible: isVisible,
backgroundAlpha: 0,
ignoreRayIntersection: true,
drawInFront: true,
grabbable: false,
@ -125,7 +125,7 @@
var domainToolTip = Overlays.addOverlay("text3d", {
name: "Loading-Tooltip",
localPosition: { x: 0.0 , y: -1.6, z: 0.0 },
localPosition: { x: 0.0, y: -1.6, z: 0.0 },
text: toolTip,
textAlpha: 1,
backgroundAlpha: 0.00393,
@ -140,14 +140,14 @@
var loadingToTheSpotText = Overlays.addOverlay("text3d", {
name: "Loading-Destination-Card-Text",
localPosition: { x: 0.0 , y: -1.687, z: -0.3 },
localPosition: { x: 0.0, y: -1.687, z: -0.3 },
text: "Go To TheSpot",
textAlpha: 1,
backgroundAlpha: 0.00393,
lineHeight: 0.10,
visible: isVisible,
ignoreRayIntersection: true,
dimensions: {x: 1, y: 0.17},
dimensions: { x: 1, y: 0.17 },
drawInFront: true,
grabbable: false,
localOrientation: Quat.fromVec3Degrees({ x: 0, y: 180, z: 0 }),
@ -156,7 +156,7 @@
var loadingToTheSpotID = Overlays.addOverlay("image3d", {
name: "Loading-Destination-Card-GoTo-Image",
localPosition: { x: 0.0 , y: -1.75, z: -0.3 },
localPosition: { x: 0.0, y: -1.75, z: -0.3 },
url: Script.resourcesPath() + "images/interstitialPage/button.png",
alpha: 1,
visible: isVisible,
@ -170,7 +170,7 @@
var loadingToTheSpotHoverID = Overlays.addOverlay("image3d", {
name: "Loading-Destination-Card-GoTo-Image-Hover",
localPosition: { x: 0.0 , y: -1.75, z: -0.3 },
localPosition: { x: 0.0, y: -1.75, z: -0.3 },
url: Script.resourcesPath() + "images/interstitialPage/button_hover.png",
alpha: 1,
visible: false,
@ -187,7 +187,7 @@
localPosition: { x: 0.0, y: -0.99, z: 0.3 },
url: Script.resourcesPath() + "images/loadingBar_placard.png",
alpha: 1,
dimensions: { x: 4, y: 2.8},
dimensions: { x: 4, y: 2.8 },
visible: isVisible,
emissive: true,
ignoreRayIntersection: false,
@ -202,7 +202,7 @@
localPosition: { x: 0.0, y: -0.90, z: 0.0 },
url: Script.resourcesPath() + "images/loadingBar_progress.png",
alpha: 1,
dimensions: {x: 3.8, y: 2.8},
dimensions: { x: 3.8, y: 2.8 },
visible: isVisible,
emissive: true,
ignoreRayIntersection: false,
@ -212,7 +212,7 @@
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 lastInterval = Date.now();
var currentDomain = "no domain";
@ -245,7 +245,7 @@
}
function resetValues() {
var properties = {
var properties = {
localPosition: { x: 1.85, y: -0.935, z: 0.0 },
dimensions: {
x: 0.1,
@ -267,7 +267,7 @@
connectionToDomainFailed = false;
previousCameraMode = Camera.mode;
Camera.mode = "first person";
timer = Script.setTimeout(update, BASIC_TIMER_INTERVAL_MS);
timer = Script.setTimeout(update, 2000);
}
}
@ -348,12 +348,12 @@
}
}
var THE_PLACE = (HifiAbout.buildVersion === "dev") ? "hifi://TheSpot-dev": "hifi://TheSpot";
var THE_PLACE = (HifiAbout.buildVersion === "dev") ? "hifi://TheSpot-dev" : "hifi://TheSpot";
function clickedOnOverlay(overlayID, event) {
if (loadingToTheSpotHoverID === overlayID) {
location.handleLookupString(THE_PLACE);
Overlays.editOverlay(loadingToTheSpotHoverID, {visible: false});
Overlays.editOverlay(loadingToTheSpotID, {visible: true});
Overlays.editOverlay(loadingToTheSpotHoverID, { visible: false });
Overlays.editOverlay(loadingToTheSpotID, { visible: true });
}
}
@ -362,8 +362,8 @@
return;
}
if (overlayID === loadingToTheSpotID) {
Overlays.editOverlay(loadingToTheSpotID, {visible: false});
Overlays.editOverlay(loadingToTheSpotHoverID, {visible: true});
Overlays.editOverlay(loadingToTheSpotID, { visible: false });
Overlays.editOverlay(loadingToTheSpotHoverID, { visible: true });
}
}
@ -372,8 +372,8 @@
return;
}
if (overlayID === loadingToTheSpotHoverID) {
Overlays.editOverlay(loadingToTheSpotHoverID, {visible: false});
Overlays.editOverlay(loadingToTheSpotID, {visible: true});
Overlays.editOverlay(loadingToTheSpotHoverID, { visible: false });
Overlays.editOverlay(loadingToTheSpotID, { visible: true });
}
}
@ -453,12 +453,14 @@
function sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds){
if ((new Date().getTime() - start) > milliseconds) {
break;
}
}
}
var MAX_TEXTURE_STABILITY_COUNT = 30;
var INTERVAL_PROGRESS = 0.04;
function update() {
var renderStats = Render.getConfig("Stats");
var physicsEnabled = Window.isPhysicsEnabled();
@ -472,7 +474,7 @@
target = progress;
}
if (currentProgress >= (TOTAL_LOADING_PROGRESS * 0.4)) {
if (currentProgress >= ((TOTAL_LOADING_PROGRESS * 0.4) - TEXTURE_EPSILON)) {
var textureResourceGPUMemSize = renderStats.textureResourceGPUMemSize;
var texturePopulatedGPUMemSize = renderStats.textureResourcePopulatedGPUMemSize;
@ -484,10 +486,9 @@
textureMemSizeAtLastCheck = textureResourceGPUMemSize;
if (textureMemSizeStabilityCount >= 20) {
if (textureMemSizeStabilityCount >= MAX_TEXTURE_STABILITY_COUNT) {
if (textureResourceGPUMemSize > 0) {
// print((texturePopulatedGPUMemSize / textureResourceGPUMemSize));
var gpuPercantage = (TOTAL_LOADING_PROGRESS * 0.6) * (texturePopulatedGPUMemSize / textureResourceGPUMemSize);
var totalProgress = progress + gpuPercantage;
if (totalProgress >= target) {
@ -501,7 +502,7 @@
target = TOTAL_LOADING_PROGRESS;
}
currentProgress = lerp(currentProgress, target, 0.2);
currentProgress = lerp(currentProgress, target, INTERVAL_PROGRESS);
var properties = {
localPosition: { x: (1.85 - (currentProgress / 2) - (-0.029 * (currentProgress / TOTAL_LOADING_PROGRESS))), y: -0.935, z: 0.0 },
dimensions: {
@ -515,7 +516,7 @@
if (errorConnectingToDomain) {
updateOverlays(errorConnectingToDomain);
// setting hover id to invisible
Overlays.editOverlay(loadingToTheSpotHoverID, {visible: false});
Overlays.editOverlay(loadingToTheSpotHoverID, { visible: false });
endAudio();
currentDomain = "no domain";
timer = null;
@ -531,7 +532,7 @@
} else if ((physicsEnabled && (currentProgress >= (TOTAL_LOADING_PROGRESS - EPSILON)))) {
updateOverlays((physicsEnabled || connectionToDomainFailed));
// setting hover id to invisible
Overlays.editOverlay(loadingToTheSpotHoverID, {visible: false});
Overlays.editOverlay(loadingToTheSpotHoverID, { visible: false });
endAudio();
currentDomain = "no domain";
timer = null;
@ -539,8 +540,8 @@
}
timer = Script.setTimeout(update, BASIC_TIMER_INTERVAL_MS);
}
var whiteColor = {red: 255, green: 255, blue: 255};
var greyColor = {red: 125, green: 125, blue: 125};
var whiteColor = { red: 255, green: 255, blue: 255 };
var greyColor = { red: 125, green: 125, blue: 125 };
Overlays.mouseReleaseOnOverlay.connect(clickedOnOverlay);
Overlays.hoverEnterOverlay.connect(onEnterOverlay);
@ -557,7 +558,11 @@
MyAvatar.sensorToWorldScaleChanged.connect(scaleInterstitialPage);
MyAvatar.sessionUUIDChanged.connect(function() {
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;

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 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) {
if (id === messageBox && button === CANCEL_BUTTON) {
isDownloadBeingCancelled = true;
@ -522,13 +558,18 @@ function getPositionToCreateEntity(extra) {
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 success = Clipboard.importEntities(itemHref);
var success = Clipboard.importEntities(itemHref, true, marketplaceItemTesterId);
var wearableLocalPosition = null;
var wearableLocalRotation = null;
var wearableLocalDimensions = null;
var wearableDimensions = null;
marketplaceItemTesterId = defaultFor(marketplaceItemTesterId, -1);
if (itemType === "contentSet") {
console.log("Item is a content set; codepath shouldn't go here.");
@ -816,7 +857,8 @@ var resourceObjectsInTest = [];
function signalNewResourceObjectInTest(resourceObject) {
ui.tablet.sendToQml({
method: "newResourceObjectInTest",
resourceObject: resourceObject });
resourceObject: resourceObject
});
}
var onQmlMessageReceived = function onQmlMessageReceived(message) {
@ -877,11 +919,15 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) {
case 'checkout_rezClicked':
case 'purchases_rezClicked':
case 'tester_rezClicked':
rezEntity(message.itemHref, message.itemType);
rezEntity(message.itemHref, message.itemType, message.itemId);
break;
case 'tester_newResourceObject':
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);
break;
case 'tester_updateResourceObjectAssetType':
@ -890,6 +936,13 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) {
case 'tester_deleteResourceObject':
delete resourceObjectsInTest[message.objectId];
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 'purchases_backClicked':
openMarketplace(message.referrerURL);
@ -1029,16 +1082,22 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) {
};
function pushResourceObjectsInTest() {
var maxObjectId = -1;
for (var objectId in resourceObjectsInTest) {
signalNewResourceObjectInTest(resourceObjectsInTest[objectId]);
maxObjectId = (maxObjectId < objectId) ? parseInt(objectId) : maxObjectId;
var maxResourceObjectId = -1;
var length = resourceObjectsInTest.length;
for (var i = 0; i < length; i++) {
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
// 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
// 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()
@ -1193,6 +1252,7 @@ function startup() {
ui.tablet.webEventReceived.connect(onWebEventReceived);
Wallet.walletStatusChanged.connect(sendCommerceSettings);
Window.messageBoxClosed.connect(onMessageBoxClosed);
ResourceRequestObserver.resourceRequestEvent.connect(onResourceRequestEvent);
Wallet.refreshWalletStatus();
}
@ -1226,6 +1286,7 @@ function shutdown() {
GlobalServices.myUsernameChanged.disconnect(onUsernameChanged);
Entities.canWriteAssetsChanged.disconnect(onCanWriteAssetsChanged);
ContextOverlay.contextOverlayClicked.disconnect(openInspectionCertificateQML);
ResourceRequestObserver.resourceRequestEvent.disconnect(onResourceRequestEvent);
off();
}