Merge remote-tracking branch 'upstream/master' into feature/quest

This commit is contained in:
Brad Davis 2019-02-06 10:15:36 -08:00
commit 91e18f569b
42 changed files with 1000 additions and 334 deletions

View file

@ -112,7 +112,6 @@ void EntityScriptServer::handleReloadEntityServerScriptPacket(QSharedPointer<Rec
if (_entityViewer.getTree() && !_shuttingDown) { if (_entityViewer.getTree() && !_shuttingDown) {
qCDebug(entity_script_server) << "Reloading: " << entityID; qCDebug(entity_script_server) << "Reloading: " << entityID;
_entitiesScriptEngine->unloadEntityScript(entityID);
checkAndCallPreload(entityID, true); checkAndCallPreload(entityID, true);
} }
} }
@ -184,7 +183,6 @@ void EntityScriptServer::updateEntityPPS() {
pps = std::min(_maxEntityPPS, pps); pps = std::min(_maxEntityPPS, pps);
} }
_entityEditSender.setPacketsPerSecond(pps); _entityEditSender.setPacketsPerSecond(pps);
qDebug() << QString("Updating entity PPS to: %1 @ %2 PPS per script = %3 PPS").arg(numRunningScripts).arg(_entityPPSPerScript).arg(pps);
} }
void EntityScriptServer::handleEntityServerScriptLogPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) { void EntityScriptServer::handleEntityServerScriptLogPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
@ -525,23 +523,25 @@ void EntityScriptServer::deletingEntity(const EntityItemID& entityID) {
void EntityScriptServer::entityServerScriptChanging(const EntityItemID& entityID, bool reload) { void EntityScriptServer::entityServerScriptChanging(const EntityItemID& entityID, bool reload) {
if (_entityViewer.getTree() && !_shuttingDown) { if (_entityViewer.getTree() && !_shuttingDown) {
_entitiesScriptEngine->unloadEntityScript(entityID, true);
checkAndCallPreload(entityID, reload); checkAndCallPreload(entityID, reload);
} }
} }
void EntityScriptServer::checkAndCallPreload(const EntityItemID& entityID, bool reload) { void EntityScriptServer::checkAndCallPreload(const EntityItemID& entityID, bool forceRedownload) {
if (_entityViewer.getTree() && !_shuttingDown && _entitiesScriptEngine) { if (_entityViewer.getTree() && !_shuttingDown && _entitiesScriptEngine) {
EntityItemPointer entity = _entityViewer.getTree()->findEntityByEntityItemID(entityID); EntityItemPointer entity = _entityViewer.getTree()->findEntityByEntityItemID(entityID);
EntityScriptDetails details; EntityScriptDetails details;
bool notRunning = !_entitiesScriptEngine->getEntityScriptDetails(entityID, details); bool isRunning = _entitiesScriptEngine->getEntityScriptDetails(entityID, details);
if (entity && (reload || notRunning || details.scriptText != entity->getServerScripts())) { if (entity && (forceRedownload || !isRunning || details.scriptText != entity->getServerScripts())) {
if (isRunning) {
_entitiesScriptEngine->unloadEntityScript(entityID, true);
}
QString scriptUrl = entity->getServerScripts(); QString scriptUrl = entity->getServerScripts();
if (!scriptUrl.isEmpty()) { if (!scriptUrl.isEmpty()) {
scriptUrl = DependencyManager::get<ResourceManager>()->normalizeURL(scriptUrl); scriptUrl = DependencyManager::get<ResourceManager>()->normalizeURL(scriptUrl);
qCDebug(entity_script_server) << "Loading entity server script" << scriptUrl << "for" << entityID; _entitiesScriptEngine->loadEntityScript(entityID, scriptUrl, forceRedownload);
_entitiesScriptEngine->loadEntityScript(entityID, scriptUrl, reload);
} }
} }
} }

View file

@ -67,7 +67,7 @@ private:
void addingEntity(const EntityItemID& entityID); void addingEntity(const EntityItemID& entityID);
void deletingEntity(const EntityItemID& entityID); void deletingEntity(const EntityItemID& entityID);
void entityServerScriptChanging(const EntityItemID& entityID, bool reload); void entityServerScriptChanging(const EntityItemID& entityID, bool reload);
void checkAndCallPreload(const EntityItemID& entityID, bool reload = false); void checkAndCallPreload(const EntityItemID& entityID, bool forceRedownload = false);
void cleanupOldKilledListeners(); void cleanupOldKilledListeners();

View file

@ -0,0 +1,275 @@
import QtQuick 2.5
import QtWebChannel 1.0
import QtWebEngine 1.5
import controlsUit 1.0
import stylesUit 1.0
import "qrc:////qml//windows"
ScrollingWindow {
id: root
HifiConstants { id: hifi }
//HifiStyles.HifiConstants { id: hifistyles }
title: "Browser"
resizable: true
destroyOnHidden: true
width: 800
height: 600
property variant permissionsBar: {'securityOrigin':'none','feature':'none'}
property alias url: webview.url
property alias webView: webview
signal loadingChanged(int status)
x: 100
y: 100
Component.onCompleted: {
focus = true
shown = true
addressBar.text = webview.url
}
function setProfile(profile) {
webview.profile = profile;
}
function showPermissionsBar(){
permissionsContainer.visible=true;
}
function hidePermissionsBar(){
permissionsContainer.visible=false;
}
function allowPermissions(){
webview.grantFeaturePermission(permissionsBar.securityOrigin, permissionsBar.feature, true);
hidePermissionsBar();
}
function setAutoAdd(auto) {
desktop.setAutoAdd(auto);
}
Item {
id:item
width: pane.contentWidth
implicitHeight: pane.scrollHeight
Row {
id: buttons
spacing: 4
anchors.top: parent.top
anchors.topMargin: 8
anchors.left: parent.left
anchors.leftMargin: 8
HiFiGlyphs {
id: back;
enabled: webview.canGoBack;
text: hifi.glyphs.backward
color: enabled ? hifi.colors.text : hifi.colors.disabledText
size: 48
MouseArea { anchors.fill: parent; onClicked: webview.goBack() }
}
HiFiGlyphs {
id: forward;
enabled: webview.canGoForward;
text: hifi.glyphs.forward
color: enabled ? hifi.colors.text : hifi.colors.disabledText
size: 48
MouseArea { anchors.fill: parent; onClicked: webview.goForward() }
}
HiFiGlyphs {
id: reload;
enabled: webview.canGoForward;
text: webview.loading ? hifi.glyphs.close : hifi.glyphs.reload
color: enabled ? hifi.colors.text : hifi.colors.disabledText
size: 48
MouseArea { anchors.fill: parent; onClicked: webview.goForward() }
}
}
Item {
id: border
height: 48
anchors.top: parent.top
anchors.topMargin: 8
anchors.right: parent.right
anchors.rightMargin: 8
anchors.left: buttons.right
anchors.leftMargin: 8
Item {
id: barIcon
width: parent.height
height: parent.height
Image {
source: webview.icon;
x: (parent.height - height) / 2
y: (parent.width - width) / 2
sourceSize: Qt.size(width, height);
verticalAlignment: Image.AlignVCenter;
horizontalAlignment: Image.AlignHCenter
}
}
TextField {
id: addressBar
anchors.right: parent.right
anchors.rightMargin: 8
anchors.left: barIcon.right
anchors.leftMargin: 0
anchors.verticalCenter: parent.verticalCenter
focus: true
colorScheme: hifi.colorSchemes.dark
placeholderText: "Enter URL"
Component.onCompleted: ScriptDiscoveryService.scriptsModelFilter.filterRegExp = new RegExp("^.*$", "i")
Keys.onPressed: {
switch(event.key) {
case Qt.Key_Enter:
case Qt.Key_Return:
event.accepted = true
if (text.indexOf("http") != 0) {
text = "http://" + text;
}
root.hidePermissionsBar();
root.keyboardRaised = false;
webview.url = text;
break;
}
}
}
}
Rectangle {
id:permissionsContainer
visible:false
color: "#000000"
width: parent.width
anchors.top: buttons.bottom
height:40
z:100
gradient: Gradient {
GradientStop { position: 0.0; color: "black" }
GradientStop { position: 1.0; color: "grey" }
}
RalewayLight {
id: permissionsInfo
anchors.right:permissionsRow.left
anchors.rightMargin: 32
anchors.topMargin:8
anchors.top:parent.top
text: "This site wants to use your microphone/camera"
size: 18
color: hifi.colors.white
}
Row {
id: permissionsRow
spacing: 4
anchors.top:parent.top
anchors.topMargin: 8
anchors.right: parent.right
visible: true
z:101
Button {
id:allow
text: "Allow"
color: hifi.buttons.blue
colorScheme: root.colorScheme
width: 120
enabled: true
onClicked: root.allowPermissions();
z:101
}
Button {
id:block
text: "Block"
color: hifi.buttons.red
colorScheme: root.colorScheme
width: 120
enabled: true
onClicked: root.hidePermissionsBar();
z:101
}
}
}
WebView {
id: webview
url: "https://highfidelity.com/"
profile: FileTypeProfile;
// Create a global EventBridge object for raiseAndLowerKeyboard.
WebEngineScript {
id: createGlobalEventBridge
sourceCode: eventBridgeJavaScriptToInject
injectionPoint: WebEngineScript.Deferred
worldId: WebEngineScript.MainWorld
}
// Detect when may want to raise and lower keyboard.
WebEngineScript {
id: raiseAndLowerKeyboard
injectionPoint: WebEngineScript.Deferred
sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
worldId: WebEngineScript.MainWorld
}
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ]
anchors.top: buttons.bottom
anchors.topMargin: 8
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
onFeaturePermissionRequested: {
if (feature == 2) { // QWebEnginePage::MediaAudioCapture
grantFeaturePermission(securityOrigin, feature, true);
} else {
permissionsBar.securityOrigin = securityOrigin;
permissionsBar.feature = feature;
root.showPermissionsBar();
}
}
onLoadingChanged: {
if (loadRequest.status === WebEngineView.LoadSucceededStatus) {
addressBar.text = loadRequest.url
}
root.loadingChanged(loadRequest.status);
}
onWindowCloseRequested: {
root.destroy();
}
Component.onCompleted: {
webChannel.registerObject("eventBridge", eventBridge);
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
desktop.initWebviewProfileHandlers(webview.profile);
}
}
} // item
Keys.onPressed: {
switch(event.key) {
case Qt.Key_L:
if (event.modifiers == Qt.ControlModifier) {
event.accepted = true
addressBar.selectAll()
addressBar.forceActiveFocus()
}
break;
}
}
} // dialog

View file

@ -0,0 +1,50 @@
//
// InfoView.qml
//
// Created by Bradley Austin Davis on 27 Apr 2015
// Copyright 2015 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 Hifi 1.0 as Hifi
import controlsUit 1.0
import "qrc:////qml//windows" as Windows
Windows.ScrollingWindow {
id: root
width: 800
height: 800
resizable: true
Hifi.InfoView {
id: infoView
width: pane.contentWidth
implicitHeight: pane.scrollHeight
WebView {
id: webview
objectName: "WebView"
anchors.fill: parent
url: infoView.url
}
}
Component.onCompleted: {
centerWindow(root);
}
onVisibleChanged: {
if (visible) {
centerWindow(root);
}
}
function centerWindow() {
desktop.centerOnVisible(root);
}
}

View file

@ -0,0 +1,125 @@
import QtQuick 2.5
import QtWebChannel 1.0
import QtWebEngine 1.5
import "controls"
import controlsUit 1.0 as HifiControls
import "styles" as HifiStyles
import stylesUit 1.0
import "windows"
Item {
id: root
HifiConstants { id: hifi }
HifiStyles.HifiConstants { id: hifistyles }
height: 600
property variant permissionsBar: {'securityOrigin':'none','feature':'none'}
property alias url: webview.url
property bool canGoBack: webview.canGoBack
property bool canGoForward: webview.canGoForward
signal loadingChanged(int status)
x: 0
y: 0
function setProfile(profile) {
webview.profile = profile;
}
WebEngineView {
id: webview
objectName: "webEngineView"
x: 0
y: 0
width: parent.width
height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height : parent.height
profile: HFWebEngineProfile;
property string userScriptUrl: ""
// creates a global EventBridge object.
WebEngineScript {
id: createGlobalEventBridge
sourceCode: eventBridgeJavaScriptToInject
injectionPoint: WebEngineScript.DocumentCreation
worldId: WebEngineScript.MainWorld
}
// detects when to raise and lower virtual keyboard
WebEngineScript {
id: raiseAndLowerKeyboard
injectionPoint: WebEngineScript.Deferred
sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
worldId: WebEngineScript.MainWorld
}
// User script.
WebEngineScript {
id: userScript
sourceUrl: webview.userScriptUrl
injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished.
worldId: WebEngineScript.MainWorld
}
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
property string newUrl: ""
Component.onCompleted: {
webChannel.registerObject("eventBridge", eventBridge);
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
// Ensure the JS from the web-engine makes it to our logging
webview.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);
});
webview.profile.httpUserAgent = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36";
web.address = url;
}
onFeaturePermissionRequested: {
grantFeaturePermission(securityOrigin, feature, true);
}
onLoadingChanged: {
keyboardRaised = false;
punctuationMode = false;
keyboard.resetShiftMode(false);
// Required to support clicking on "hifi://" links
if (WebEngineView.LoadStartedStatus == loadRequest.status) {
urlAppend(loadRequest.url.toString())
var url = loadRequest.url.toString();
if (urlHandler.canHandleUrl(url)) {
if (urlHandler.handleUrl(url)) {
root.stop();
}
}
}
}
onNewViewRequested: {
request.openIn(webView);
}
HifiControls.WebSpinner { }
}
Keys.onPressed: {
switch(event.key) {
case Qt.Key_L:
if (event.modifiers == Qt.ControlModifier) {
event.accepted = true
addressBar.selectAll()
addressBar.forceActiveFocus()
}
break;
}
}
}

View file

@ -1,16 +1,14 @@
import QtQuick 2.5 import QtQuick 2.5
import QtWebChannel 1.0
import QtWebView 1.1
import controlsUit 1.0 import controlsUit 1.0
import "styles" as HifiStyles
import stylesUit 1.0 import stylesUit 1.0
import "windows" import "windows"
ScrollingWindow { ScrollingWindow {
id: root id: root
HifiConstants { id: hifi } HifiConstants { id: hifi }
HifiStyles.HifiConstants { id: hifistyles } //HifiStyles.HifiConstants { id: hifistyles }
title: "Browser" title: "Browser"
resizable: true resizable: true
destroyOnHidden: true destroyOnHidden: true
@ -32,7 +30,6 @@ ScrollingWindow {
} }
function setProfile(profile) { function setProfile(profile) {
webview.profile = profile;
} }
function showPermissionsBar(){ function showPermissionsBar(){
@ -44,7 +41,6 @@ ScrollingWindow {
} }
function allowPermissions(){ function allowPermissions(){
webview.grantFeaturePermission(permissionsBar.securityOrigin, permissionsBar.feature, true);
hidePermissionsBar(); hidePermissionsBar();
} }
@ -68,7 +64,7 @@ ScrollingWindow {
id: back; id: back;
enabled: webview.canGoBack; enabled: webview.canGoBack;
text: hifi.glyphs.backward text: hifi.glyphs.backward
color: enabled ? hifistyles.colors.text : hifistyles.colors.disabledText color: enabled ? hifi.colors.text : hifi.colors.disabledText
size: 48 size: 48
MouseArea { anchors.fill: parent; onClicked: webview.goBack() } MouseArea { anchors.fill: parent; onClicked: webview.goBack() }
} }
@ -77,7 +73,7 @@ ScrollingWindow {
id: forward; id: forward;
enabled: webview.canGoForward; enabled: webview.canGoForward;
text: hifi.glyphs.forward text: hifi.glyphs.forward
color: enabled ? hifistyles.colors.text : hifistyles.colors.disabledText color: enabled ? hifi.colors.text : hifi.colors.disabledText
size: 48 size: 48
MouseArea { anchors.fill: parent; onClicked: webview.goForward() } MouseArea { anchors.fill: parent; onClicked: webview.goForward() }
} }
@ -86,7 +82,7 @@ ScrollingWindow {
id: reload; id: reload;
enabled: webview.canGoForward; enabled: webview.canGoForward;
text: webview.loading ? hifi.glyphs.close : hifi.glyphs.reload text: webview.loading ? hifi.glyphs.close : hifi.glyphs.reload
color: enabled ? hifistyles.colors.text : hifistyles.colors.disabledText color: enabled ? hifi.colors.text : hifi.colors.disabledText
size: 48 size: 48
MouseArea { anchors.fill: parent; onClicked: webview.goForward() } MouseArea { anchors.fill: parent; onClicked: webview.goForward() }
} }
@ -202,43 +198,10 @@ ScrollingWindow {
} }
} }
WebView { ProxyWebView {
id: webview id: webview
anchors.centerIn: parent
url: "https://highfidelity.com/" url: "https://highfidelity.com/"
profile: FileTypeProfile;
anchors.top: buttons.bottom
anchors.topMargin: 8
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
onFeaturePermissionRequested: {
if (feature == 2) { // QWebEnginePage::MediaAudioCapture
grantFeaturePermission(securityOrigin, feature, true);
} else {
permissionsBar.securityOrigin = securityOrigin;
permissionsBar.feature = feature;
root.showPermissionsBar();
}
}
onLoadingChanged: {
if (loadRequest.status === WebView.LoadSucceededStatus) {
addressBar.text = loadRequest.url
}
root.loadingChanged(loadRequest.status);
}
onWindowCloseRequested: {
root.destroy();
}
Component.onCompleted: {
webChannel.registerObject("eventBridge", eventBridge);
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
desktop.initWebviewProfileHandlers(webview.profile);
}
} }
} // item } // item

View file

@ -19,13 +19,12 @@ Windows.ScrollingWindow {
width: 800 width: 800
height: 800 height: 800
resizable: true resizable: true
Hifi.InfoView { Hifi.InfoView {
id: infoView id: infoView
width: pane.contentWidth width: pane.contentWidth
implicitHeight: pane.scrollHeight implicitHeight: pane.scrollHeight
WebView { ProxyWebView {
id: webview id: webview
objectName: "WebView" objectName: "WebView"
anchors.fill: parent anchors.fill: parent

View file

@ -1,17 +1,11 @@
import QtQuick 2.5 import QtQuick 2.5
import QtWebChannel 1.0
import QtWebView 1.1
import "controls"
import controlsUit 1.0 as HifiControls import controlsUit 1.0 as HifiControls
import "styles" as HifiStyles
import stylesUit 1.0 import stylesUit 1.0
import "windows"
Item { Item {
id: root id: root
HifiConstants { id: hifi } HifiConstants { id: hifi }
HifiStyles.HifiConstants { id: hifistyles }
height: 600 height: 600
property variant permissionsBar: {'securityOrigin':'none','feature':'none'} property variant permissionsBar: {'securityOrigin':'none','feature':'none'}
@ -30,64 +24,9 @@ Item {
webview.profile = profile; webview.profile = profile;
} }
WebView { HifiControls.ProxyWebView {
id: webview id: webview
objectName: "webEngineView"
x: 0
y: 0
width: parent.width width: parent.width
height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height : parent.height height: parent.height
property string userScriptUrl: ""
property string newUrl: ""
Component.onCompleted: {
webChannel.registerObject("eventBridge", eventBridge);
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
// Ensure the JS from the web-engine makes it to our logging
webview.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);
});
webview.profile.httpUserAgent = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36";
web.address = url;
}
onLoadingChanged: {
keyboardRaised = false;
punctuationMode = false;
keyboard.resetShiftMode(false);
// Required to support clicking on "hifi://" links
if (WebView.LoadStartedStatus == loadRequest.status) {
urlAppend(loadRequest.url.toString())
var url = loadRequest.url.toString();
if (urlHandler.canHandleUrl(url)) {
if (urlHandler.handleUrl(url)) {
root.stop();
}
}
}
}
onNewViewRequested: {
request.openIn(webView);
}
HifiControls.WebSpinner { }
}
Keys.onPressed: {
switch(event.key) {
case Qt.Key_L:
if (event.modifiers == Qt.ControlModifier) {
event.accepted = true
addressBar.selectAll()
addressBar.forceActiveFocus()
}
break;
}
} }
} }

View file

@ -0,0 +1,189 @@
import QtQuick 2.7
import QtWebEngine 1.5
import QtWebChannel 1.0
import QtQuick.Controls 2.2
import stylesUit 1.0 as StylesUIt
Item {
id: flick
property alias url: webViewCore.url
property alias canGoBack: webViewCore.canGoBack
property alias webViewCore: webViewCore
property alias webViewCoreProfile: webViewCore.profile
property string webViewCoreUserAgent
property string userScriptUrl: ""
property string urlTag: "noDownload=false";
signal newViewRequestedCallback(var request)
signal loadingChangedCallback(var loadRequest)
width: parent.width
property bool interactive: false
property bool blurOnCtrlShift: true
StylesUIt.HifiConstants {
id: hifi
}
function stop() {
webViewCore.stop();
}
Timer {
id: delayedUnfocuser
repeat: false
interval: 200
onTriggered: {
// The idea behind this is to delay unfocusing, so that fast lower/raise will not result actual unfocusing.
// Fast lower/raise happens every time keyboard is being re-raised (see the code below in OffscreenQmlSurface::setKeyboardRaised)
//
// if (raised) {
// item->setProperty("keyboardRaised", QVariant(!raised));
// }
//
// item->setProperty("keyboardRaised", QVariant(raised));
//
webViewCore.runJavaScript("if (document.activeElement) document.activeElement.blur();", function(result) {
console.log('unfocus completed: ', result);
});
}
}
function unfocus() {
delayedUnfocuser.start();
}
function stopUnfocus() {
delayedUnfocuser.stop();
}
function onLoadingChanged(loadRequest) {
if (WebEngineView.LoadStartedStatus === loadRequest.status) {
// Required to support clicking on "hifi://" links
var url = loadRequest.url.toString();
url = (url.indexOf("?") >= 0) ? url + urlTag : url + "?" + urlTag;
if (urlHandler.canHandleUrl(url)) {
if (urlHandler.handleUrl(url)) {
webViewCore.stop();
}
}
}
if (WebEngineView.LoadFailedStatus === loadRequest.status) {
console.log("Tablet WebEngineView failed to load url: " + loadRequest.url.toString());
}
if (WebEngineView.LoadSucceededStatus === loadRequest.status) {
//disable Chromium's scroll bars
}
}
WebEngineView {
id: webViewCore
width: parent.width
height: parent.height
profile: HFWebEngineProfile;
settings.pluginsEnabled: true
settings.touchIconsEnabled: true
settings.allowRunningInsecureContent: true
// creates a global EventBridge object.
WebEngineScript {
id: createGlobalEventBridge
sourceCode: eventBridgeJavaScriptToInject
injectionPoint: WebEngineScript.DocumentCreation
worldId: WebEngineScript.MainWorld
}
// detects when to raise and lower virtual keyboard
WebEngineScript {
id: raiseAndLowerKeyboard
injectionPoint: WebEngineScript.Deferred
sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
worldId: WebEngineScript.MainWorld
}
// User script.
WebEngineScript {
id: userScript
sourceUrl: flick.userScriptUrl
injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished.
worldId: WebEngineScript.MainWorld
}
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
Component.onCompleted: {
webChannel.registerObject("eventBridge", eventBridge);
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
if (webViewCoreUserAgent !== undefined) {
webViewCore.profile.httpUserAgent = webViewCoreUserAgent
} else {
webViewCore.profile.httpUserAgent += " (HighFidelityInterface)";
}
// Ensure the JS from the web-engine makes it to our logging
webViewCore.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);
});
}
onFeaturePermissionRequested: {
grantFeaturePermission(securityOrigin, feature, true);
}
//disable popup
onContextMenuRequested: {
request.accepted = true;
}
onNewViewRequested: {
newViewRequestedCallback(request)
}
// Prior to 5.10, the WebEngineView loading property is true during initial page loading and then stays false
// as in-page javascript adds more html content. However, in 5.10 there is a bug such that adding html turns
// loading true, and never turns it false again. safeLoading provides a workaround, but it should be removed
// when QT fixes this.
property bool safeLoading: false
property bool loadingLatched: false
property var loadingRequest: null
onLoadingChanged: {
webViewCore.loadingRequest = loadRequest;
webViewCore.safeLoading = webViewCore.loading && !loadingLatched;
webViewCore.loadingLatched |= webViewCore.loading;
}
onSafeLoadingChanged: {
flick.onLoadingChanged(webViewCore.loadingRequest)
loadingChangedCallback(webViewCore.loadingRequest)
}
}
AnimatedImage {
//anchoring doesnt works here when changing content size
x: flick.width/2 - width/2
y: flick.height/2 - height/2
source: "../../icons/loader-snake-64-w.gif"
visible: webViewCore.safeLoading && /^(http.*|)$/i.test(webViewCore.url.toString())
playing: visible
z: 10000
}
Keys.onPressed: {
if (blurOnCtrlShift && (event.modifiers & Qt.ShiftModifier) && (event.modifiers & Qt.ControlModifier)) {
webViewCore.focus = false;
}
}
}

View file

@ -1,10 +1,9 @@
import QtQuick 2.7 import QtQuick 2.7
import QtWebView 1.1
import QtWebChannel 1.0
import QtQuick.Controls 2.2 import QtQuick.Controls 2.2
import stylesUit 1.0 as StylesUIt import stylesUit 1.0 as StylesUIt
import controlsUit 1.0 as ControlsUit
Item { Item {
id: flick id: flick
@ -33,130 +32,21 @@ Item {
} }
function stop() { function stop() {
webViewCore.stop();
}
Timer {
id: delayedUnfocuser
repeat: false
interval: 200
onTriggered: {
// The idea behind this is to delay unfocusing, so that fast lower/raise will not result actual unfocusing.
// Fast lower/raise happens every time keyboard is being re-raised (see the code below in OffscreenQmlSurface::setKeyboardRaised)
//
// if (raised) {
// item->setProperty("keyboardRaised", QVariant(!raised));
// }
//
// item->setProperty("keyboardRaised", QVariant(raised));
//
webViewCore.runJavaScript("if (document.activeElement) document.activeElement.blur();", function(result) {
console.log('unfocus completed: ', result);
});
}
} }
function unfocus() { function unfocus() {
delayedUnfocuser.start();
} }
function stopUnfocus() { function stopUnfocus() {
delayedUnfocuser.stop();
} }
function onLoadingChanged(loadRequest) { function onLoadingChanged(loadRequest) {
if (WebView.LoadStartedStatus === loadRequest.status) {
// Required to support clicking on "hifi://" links
var url = loadRequest.url.toString();
url = (url.indexOf("?") >= 0) ? url + urlTag : url + "?" + urlTag;
if (urlHandler.canHandleUrl(url)) {
if (urlHandler.handleUrl(url)) {
webViewCore.stop();
}
}
} }
if (WebView.LoadFailedStatus === loadRequest.status) { ControlsUit.ProxyWebView {
console.log("Tablet Web View failed to load url: " + loadRequest.url.toString());
}
if (WebView.LoadSucceededStatus === loadRequest.status) {
//disable Chromium's scroll bars
}
}
WebView {
id: webViewCore id: webViewCore
width: parent.width width: parent.width
height: parent.height height: parent.height
settings.pluginsEnabled: true
settings.touchIconsEnabled: true
settings.allowRunningInsecureContent: true
Component.onCompleted: {
webChannel.registerObject("eventBridge", eventBridge);
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
if (webViewCoreUserAgent !== undefined) {
webViewCore.profile.httpUserAgent = webViewCoreUserAgent
} else {
webViewCore.profile.httpUserAgent += " (HighFidelityInterface)";
}
// Ensure the JS from the web-engine makes it to our logging
webViewCore.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);
});
}
onFeaturePermissionRequested: {
grantFeaturePermission(securityOrigin, feature, true);
}
//disable popup
onContextMenuRequested: {
request.accepted = true;
}
onNewViewRequested: {
newViewRequestedCallback(request)
}
// Prior to 5.10, the WebEngineView loading property is true during initial page loading and then stays false
// as in-page javascript adds more html content. However, in 5.10 there is a bug such that adding html turns
// loading true, and never turns it false again. safeLoading provides a workaround, but it should be removed
// when QT fixes this.
property bool safeLoading: false
property bool loadingLatched: false
property var loadingRequest: null
onLoadingChanged: {
webViewCore.loadingRequest = loadRequest;
webViewCore.safeLoading = webViewCore.loading && !loadingLatched;
webViewCore.loadingLatched |= webViewCore.loading;
}
onSafeLoadingChanged: {
flick.onLoadingChanged(webViewCore.loadingRequest)
loadingChangedCallback(webViewCore.loadingRequest)
}
}
AnimatedImage {
//anchoring doesnt works here when changing content size
x: flick.width/2 - width/2
y: flick.height/2 - height/2
source: "../../icons/loader-snake-64-w.gif"
visible: webViewCore.safeLoading && /^(http.*|)$/i.test(webViewCore.url.toString())
playing: visible
z: 10000
}
Keys.onPressed: {
if (blurOnCtrlShift && (event.modifiers & Qt.ShiftModifier) && (event.modifiers & Qt.ControlModifier)) {
webViewCore.focus = false;
}
} }
} }

View file

@ -0,0 +1,38 @@
//
// WebView.qml
//
// Created by Bradley Austin Davis on 12 Jan 2016
// Copyright 2016 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.7
import QtWebEngine 1.5
WebEngineView {
id: root
Component.onCompleted: {
console.log("Connecting JS messaging to Hifi Logging")
// Ensure the JS from the web-engine makes it to our logging
root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
console.log("Web Window JS message: " + sourceID + " " + lineNumber + " " + message);
});
}
onLoadingChanged: {
// Required to support clicking on "hifi://" links
if (WebEngineView.LoadStartedStatus == loadRequest.status) {
var url = loadRequest.url.toString();
if (urlHandler.canHandleUrl(url)) {
if (urlHandler.handleUrl(url)) {
root.stop();
}
}
}
}
WebSpinner { }
}

View file

@ -8,37 +8,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
import QtQuick 2.7 import "."
import QtWebView 1.1
WebView { ProxyWebView {
id: root id: root
Component.onCompleted: {
console.log("Connecting JS messaging to Hifi Logging")
// Ensure the JS from the web-engine makes it to our logging
root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
console.log("Web Window JS message: " + sourceID + " " + lineNumber + " " + message);
});
}
onLoadingChanged: {
// Required to support clicking on "hifi://" links
if (WebView.LoadStartedStatus == loadRequest.status) {
var url = loadRequest.url.toString();
if (urlHandler.canHandleUrl(url)) {
if (urlHandler.handleUrl(url)) {
root.stop();
}
}
}
}
signal newViewRequested();
signal windowCloseRequested();
signal featurePermissionRequested();
property var profile: null;
WebSpinner { }
} }

View file

@ -0,0 +1,30 @@
import QtQuick 2.7
import stylesUit 1.0
Rectangle {
HifiConstants {
id: hifi
}
color: hifi.colors.darkGray
signal onNewViewRequested();
property string url: "";
property bool canGoBack: false
property bool canGoForward: false
property string icon: ""
property var profile: {}
property bool safeLoading: false
property bool loadingLatched: false
property var loadingRequest: null
Text {
anchors.centerIn: parent
text: "This feature is not supported"
font.pixelSize: 32
color: hifi.colors.white
}
}

View file

@ -29,6 +29,7 @@ TextEdit 1.0 TextEdit.qml
TextField 1.0 TextField.qml TextField 1.0 TextField.qml
ToolTip 1.0 ToolTip.qml ToolTip 1.0 ToolTip.qml
Tree 1.0 Tree.qml Tree 1.0 Tree.qml
ProxyWebView 1.0 ProxyWebView.qml
VerticalSpacer 1.0 VerticalSpacer.qml VerticalSpacer 1.0 VerticalSpacer.qml
WebGlyphButton 1.0 WebGlyphButton.qml WebGlyphButton 1.0 WebGlyphButton.qml
WebSpinner 1.0 WebSpinner.qml WebSpinner 1.0 WebSpinner.qml

View file

@ -21,11 +21,10 @@ import "../../../../controls" as HifiControls
import "../" as HifiCommerceCommon import "../" as HifiCommerceCommon
import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere. import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere.
Rectangle { Item {
HifiConstants { id: hifi; } HifiConstants { id: hifi; }
id: root; id: root;
color: hifi.colors.baseGray
property int parentAppTitleBarHeight; property int parentAppTitleBarHeight;
property int parentAppNavBarHeight; property int parentAppNavBarHeight;
property string currentActiveView: "sendAssetHome"; property string currentActiveView: "sendAssetHome";

View file

@ -3,10 +3,13 @@ import Hifi 1.0
import "../../dialogs" import "../../dialogs"
import "../../controls" import "../../controls"
import stylesUit 1.0
Item { Rectangle {
HifiConstants { id: hifi; }
id: tabletRoot id: tabletRoot
objectName: "tabletRoot" objectName: "tabletRoot"
color: hifi.colors.baseGray
property string username: "Unknown user" property string username: "Unknown user"
property string usernameShort: "Unknown user" property string usernameShort: "Unknown user"
property var rootMenu; property var rootMenu;

View file

@ -767,6 +767,11 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
static const auto SUPPRESS_SETTINGS_RESET = "--suppress-settings-reset"; static const auto SUPPRESS_SETTINGS_RESET = "--suppress-settings-reset";
bool suppressPrompt = cmdOptionExists(argc, const_cast<const char**>(argv), SUPPRESS_SETTINGS_RESET); bool suppressPrompt = cmdOptionExists(argc, const_cast<const char**>(argv), SUPPRESS_SETTINGS_RESET);
// set the OCULUS_STORE property so the oculus plugin can know if we ran from the Oculus Store
static const auto OCULUS_STORE_ARG = "--oculus-store";
bool isStore = cmdOptionExists(argc, const_cast<const char**>(argv), OCULUS_STORE_ARG);
qApp->setProperty(hifi::properties::OCULUS_STORE, isStore);
// Ignore any previous crashes if running from command line with a test script. // Ignore any previous crashes if running from command line with a test script.
bool inTestMode { false }; bool inTestMode { false };
for (int i = 0; i < argc; ++i) { for (int i = 0; i < argc; ++i) {
@ -1148,10 +1153,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
qCDebug(interfaceapp) << "[VERSION] We will use DEVELOPMENT global services."; qCDebug(interfaceapp) << "[VERSION] We will use DEVELOPMENT global services.";
#endif #endif
// set the OCULUS_STORE property so the oculus plugin can know if we ran from the Oculus Store bool isStore = property(hifi::properties::OCULUS_STORE).toBool();
static const QString OCULUS_STORE_ARG = "--oculus-store";
bool isStore = arguments().indexOf(OCULUS_STORE_ARG) != -1;
setProperty(hifi::properties::OCULUS_STORE, isStore);
DependencyManager::get<WalletScriptingInterface>()->setLimitedCommerce(isStore); // Or we could make it a separate arg, or if either arg is set, etc. And should this instead by a hifi::properties? DependencyManager::get<WalletScriptingInterface>()->setLimitedCommerce(isStore); // Or we could make it a separate arg, or if either arg is set, etc. And should this instead by a hifi::properties?
updateHeartbeat(); updateHeartbeat();

View file

@ -5300,6 +5300,21 @@ void MyAvatar::releaseGrab(const QUuid& grabID) {
bool tellHandler { false }; bool tellHandler { false };
_avatarGrabsLock.withWriteLock([&] { _avatarGrabsLock.withWriteLock([&] {
std::map<QUuid, GrabPointer>::iterator itr;
itr = _avatarGrabs.find(grabID);
if (itr != _avatarGrabs.end()) {
GrabPointer grab = itr->second;
if (grab) {
grab->setReleased(true);
bool success;
SpatiallyNestablePointer target = SpatiallyNestable::findByID(grab->getTargetID(), success);
if (target && success) {
target->disableGrab(grab);
}
}
}
if (_avatarGrabData.remove(grabID)) { if (_avatarGrabData.remove(grabID)) {
_grabsToDelete.push_back(grabID); _grabsToDelete.push_back(grabID);
tellHandler = true; tellHandler = true;

View file

@ -59,7 +59,7 @@ void AddressBarDialog::loadHome() {
auto locationBookmarks = DependencyManager::get<LocationBookmarks>(); auto locationBookmarks = DependencyManager::get<LocationBookmarks>();
QString homeLocation = locationBookmarks->addressForBookmark(LocationBookmarks::HOME_BOOKMARK); QString homeLocation = locationBookmarks->addressForBookmark(LocationBookmarks::HOME_BOOKMARK);
if (homeLocation == "") { if (homeLocation == "") {
homeLocation = DEFAULT_HIFI_ADDRESS; homeLocation = DEFAULT_HOME_ADDRESS;
} }
DependencyManager::get<AddressManager>()->handleLookupString(homeLocation); DependencyManager::get<AddressManager>()->handleLookupString(homeLocation);
} }

View file

@ -154,7 +154,7 @@ void AnimBlendLinearMove::setFrameAndPhase(float dt, float alpha, int prevPoseIn
// detect loop trigger events // detect loop trigger events
if (_phase >= 1.0f) { if (_phase >= 1.0f) {
triggersOut.setTrigger(_id + "Loop"); triggersOut.setTrigger(_id + "OnLoop");
_phase = glm::fract(_phase); _phase = glm::fract(_phase);
} }

View file

@ -209,15 +209,14 @@ public:
void set(const QString& key, const QString& value) { _map[key] = AnimVariant(value); } void set(const QString& key, const QString& value) { _map[key] = AnimVariant(value); }
void unset(const QString& key) { _map.erase(key); } void unset(const QString& key) { _map.erase(key); }
void setTrigger(const QString& key) { _triggers.insert(key); } void setTrigger(const QString& key) { _map[key] = AnimVariant(true); }
void clearTriggers() { _triggers.clear(); }
void setRigToGeometryTransform(const glm::mat4& rigToGeometry) { void setRigToGeometryTransform(const glm::mat4& rigToGeometry) {
_rigToGeometryMat = rigToGeometry; _rigToGeometryMat = rigToGeometry;
_rigToGeometryRot = glmExtractRotation(rigToGeometry); _rigToGeometryRot = glmExtractRotation(rigToGeometry);
} }
void clearMap() { _map.clear(); } void clearMap() { _map.clear(); _triggers.clear(); }
bool hasKey(const QString& key) const { return _map.find(key) != _map.end(); } bool hasKey(const QString& key) const { return _map.find(key) != _map.end(); }
const AnimVariant& get(const QString& key) const { const AnimVariant& get(const QString& key) const {
@ -238,7 +237,7 @@ public:
// For stat debugging. // For stat debugging.
std::map<QString, QString> toDebugMap() const; std::map<QString, QString> toDebugMap() const;
#ifdef NDEBUG #ifndef NDEBUG
void dump() const { void dump() const {
qCDebug(animation) << "AnimVariantMap ="; qCDebug(animation) << "AnimVariantMap =";
for (auto& pair : _map) { for (auto& pair : _map) {

View file

@ -1207,9 +1207,7 @@ void Rig::updateAnimations(float deltaTime, const glm::mat4& rootTransform, cons
_networkPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses(); _networkPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses();
} }
_lastAnimVars = _animVars; _lastAnimVars = _animVars;
_animVars.clearTriggers();
_animVars = triggersOut; _animVars = triggersOut;
_networkVars.clearTriggers();
_networkVars = networkTriggersOut; _networkVars = networkTriggersOut;
_lastContext = context; _lastContext = context;
} }

View file

@ -412,6 +412,9 @@ void Avatar::accumulateGrabPositions(std::map<QUuid, GrabLocationAccumulator>& g
if (!grab || !grab->getActionID().isNull()) { if (!grab || !grab->getActionID().isNull()) {
continue; // the accumulated value isn't used, in this case. continue; // the accumulated value isn't used, in this case.
} }
if (grab->getReleased()) {
continue;
}
glm::vec3 jointTranslation = getAbsoluteJointTranslationInObjectFrame(grab->getParentJointIndex()); glm::vec3 jointTranslation = getAbsoluteJointTranslationInObjectFrame(grab->getParentJointIndex());
glm::quat jointRotation = getAbsoluteJointRotationInObjectFrame(grab->getParentJointIndex()); glm::quat jointRotation = getAbsoluteJointRotationInObjectFrame(grab->getParentJointIndex());

View file

@ -632,9 +632,11 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
// include jointData if there is room for the most minimal section. i.e. no translations or rotations. // include jointData if there is room for the most minimal section. i.e. no translations or rotations.
IF_AVATAR_SPACE(PACKET_HAS_JOINT_DATA, AvatarDataPacket::minJointDataSize(numJoints)) { IF_AVATAR_SPACE(PACKET_HAS_JOINT_DATA, AvatarDataPacket::minJointDataSize(numJoints)) {
// Allow for faux joints + translation bit-vector: // Minimum space required for another rotation joint -
const ptrdiff_t minSizeForJoint = sizeof(AvatarDataPacket::SixByteQuat) // size of joint + following translation bit-vector + translation scale + faux joints:
+ jointBitVectorSize + AvatarDataPacket::FAUX_JOINTS_SIZE; const ptrdiff_t minSizeForJoint = sizeof(AvatarDataPacket::SixByteQuat) + jointBitVectorSize +
sizeof(float) + AvatarDataPacket::FAUX_JOINTS_SIZE;
auto startSection = destinationBuffer; auto startSection = destinationBuffer;
// compute maxTranslationDimension before we send any joint data. // compute maxTranslationDimension before we send any joint data.
@ -724,6 +726,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
const JointData& data = joints[i]; const JointData& data = joints[i];
const JointData& last = lastSentJointData[i]; const JointData& last = lastSentJointData[i];
// Note minSizeForJoint is conservative since there isn't a following bit-vector + scale.
if (packetEnd - destinationBuffer >= minSizeForJoint) { if (packetEnd - destinationBuffer >= minSizeForJoint) {
if (!data.translationIsDefaultPose) { if (!data.translationIsDefaultPose) {
if (sendAll || last.translationIsDefaultPose || (!cullSmallChanges && last.translation != data.translation) if (sendAll || last.translationIsDefaultPose || (!cullSmallChanges && last.translation != data.translation)

View file

@ -59,6 +59,7 @@ public:
virtual bool isReadyForAdd() const { return true; } virtual bool isReadyForAdd() const { return true; }
bool isActive() { return _active; } bool isActive() { return _active; }
void deactivate() { _active = false; }
virtual void removeFromSimulation(EntitySimulationPointer simulation) const = 0; virtual void removeFromSimulation(EntitySimulationPointer simulation) const = 0;
virtual EntityItemWeakPointer getOwnerEntity() const = 0; virtual EntityItemWeakPointer getOwnerEntity() const = 0;

View file

@ -3506,3 +3506,13 @@ void EntityItem::removeGrab(GrabPointer grab) {
} }
disableNoBootstrap(); disableNoBootstrap();
} }
void EntityItem::disableGrab(GrabPointer grab) {
QUuid actionID = grab->getActionID();
if (!actionID.isNull()) {
EntityDynamicPointer action = _grabActions.value(actionID);
if (action) {
action->deactivate();
}
}
}

View file

@ -561,6 +561,7 @@ public:
virtual void addGrab(GrabPointer grab) override; virtual void addGrab(GrabPointer grab) override;
virtual void removeGrab(GrabPointer grab) override; virtual void removeGrab(GrabPointer grab) override;
virtual void disableGrab(GrabPointer grab) override;
signals: signals:
void requestRenderUpdate(); void requestRenderUpdate();

View file

@ -30,7 +30,8 @@
#include "UserActivityLogger.h" #include "UserActivityLogger.h"
#include "udt/PacketHeaders.h" #include "udt/PacketHeaders.h"
const QString DEFAULT_HIFI_ADDRESS = "file:///~/serverless/tutorial.json"; const QString DEFAULT_HIFI_ADDRESS = "hifi://welcome";
const QString DEFAULT_HOME_ADDRESS = "file:///~/serverless/tutorial.json";
const QString REDIRECT_HIFI_ADDRESS = "file:///~/serverless/redirect.json"; const QString REDIRECT_HIFI_ADDRESS = "file:///~/serverless/redirect.json";
const QString ADDRESS_MANAGER_SETTINGS_GROUP = "AddressManager"; const QString ADDRESS_MANAGER_SETTINGS_GROUP = "AddressManager";
const QString SETTINGS_CURRENT_ADDRESS_KEY = "address"; const QString SETTINGS_CURRENT_ADDRESS_KEY = "address";

View file

@ -24,6 +24,7 @@
extern const QString DEFAULT_HIFI_ADDRESS; extern const QString DEFAULT_HIFI_ADDRESS;
extern const QString REDIRECT_HIFI_ADDRESS; extern const QString REDIRECT_HIFI_ADDRESS;
extern const QString DEFAULT_HOME_ADDRESS;
const QString SANDBOX_HIFI_ADDRESS = "hifi://localhost"; const QString SANDBOX_HIFI_ADDRESS = "hifi://localhost";
const QString INDEX_PATH = "/"; const QString INDEX_PATH = "/";

View file

@ -115,6 +115,18 @@ void EntityMotionState::updateServerPhysicsVariables() {
} }
void EntityMotionState::handleDeactivation() { void EntityMotionState::handleDeactivation() {
if (_entity->getDirtyFlags() & (Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES)) {
// Some non-physical event (script-call or network-packet) has modified the entity's transform and/or velocities
// at the last minute before deactivation --> the values stored in _server* and _body are stale.
// We assume the EntityMotionState is the last to know, so we copy from EntityItem and let things sort themselves out.
Transform localTransform;
_entity->getLocalTransformAndVelocities(localTransform, _serverVelocity, _serverAngularVelocity);
_serverPosition = localTransform.getTranslation();
_serverRotation = localTransform.getRotation();
_serverAcceleration = _entity->getAcceleration();
_serverActionData = _entity->getDynamicData();
_lastStep = ObjectMotionState::getWorldSimulationStep();
} else {
// copy _server data to entity // copy _server data to entity
Transform localTransform = _entity->getLocalTransform(); Transform localTransform = _entity->getLocalTransform();
localTransform.setTranslation(_serverPosition); localTransform.setTranslation(_serverPosition);
@ -126,6 +138,7 @@ void EntityMotionState::handleDeactivation() {
worldTrans.setRotation(glmToBullet(_entity->getWorldOrientation())); worldTrans.setRotation(glmToBullet(_entity->getWorldOrientation()));
_body->setWorldTransform(worldTrans); _body->setWorldTransform(worldTrans);
// no need to update velocities... should already be zero // no need to update velocities... should already be zero
}
} }
// virtual // virtual

View file

@ -26,6 +26,7 @@ ProceduralSkybox::ProceduralSkybox() : graphics::Skybox() {
const int8_t STENCIL_BACKGROUND = 0; const int8_t STENCIL_BACKGROUND = 0;
_procedural._opaqueState->setStencilTest(true, 0xFF, gpu::State::StencilTest(STENCIL_BACKGROUND, 0xFF, gpu::EQUAL, _procedural._opaqueState->setStencilTest(true, 0xFF, gpu::State::StencilTest(STENCIL_BACKGROUND, 0xFF, gpu::EQUAL,
gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP)); gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
_procedural._opaqueState->setDepthTest(gpu::State::DepthTest(false));
} }
bool ProceduralSkybox::empty() { bool ProceduralSkybox::empty() {

View file

@ -16,6 +16,7 @@
#include <SettingHandle.h> #include <SettingHandle.h>
#include <UserActivityLogger.h> #include <UserActivityLogger.h>
#include <PathUtils.h> #include <PathUtils.h>
#include <shared/FileUtils.h>
#include "ScriptEngine.h" #include "ScriptEngine.h"
#include "ScriptEngineLogging.h" #include "ScriptEngineLogging.h"
@ -476,6 +477,8 @@ ScriptEnginePointer ScriptEngines::loadScript(const QUrl& scriptFilename, bool i
scriptUrl = normalizeScriptURL(scriptFilename); scriptUrl = normalizeScriptURL(scriptFilename);
} }
scriptUrl = QUrl(FileUtils::selectFile(scriptUrl.toString()));
auto scriptEngine = getScriptEngine(scriptUrl); auto scriptEngine = getScriptEngine(scriptUrl);
if (scriptEngine && !scriptEngine->isStopping()) { if (scriptEngine && !scriptEngine->isStopping()) {
return scriptEngine; return scriptEngine;

View file

@ -26,16 +26,16 @@ public:
void accumulate(glm::vec3 position, glm::quat orientation) { void accumulate(glm::vec3 position, glm::quat orientation) {
_position += position; _position += position;
_orientation = orientation; // XXX _orientation = orientation; // XXX
count++; _count++;
} }
glm::vec3 finalizePosition() { return count > 0 ? _position * (1.0f / count) : glm::vec3(0.0f); } glm::vec3 finalizePosition() { return _count > 0 ? _position * (1.0f / _count) : glm::vec3(0.0f); }
glm::quat finalizeOrientation() { return _orientation; } // XXX glm::quat finalizeOrientation() { return _orientation; } // XXX
protected: protected:
glm::vec3 _position; glm::vec3 _position;
glm::quat _orientation; glm::quat _orientation;
int count { 0 }; int _count { 0 };
}; };
class Grab { class Grab {
@ -48,7 +48,8 @@ public:
_parentJointIndex(newParentJointIndex), _parentJointIndex(newParentJointIndex),
_hand(newHand), _hand(newHand),
_positionalOffset(newPositionalOffset), _positionalOffset(newPositionalOffset),
_rotationalOffset(newRotationalOffset) {} _rotationalOffset(newRotationalOffset),
_released(false) {}
QByteArray toByteArray(); QByteArray toByteArray();
bool fromByteArray(const QByteArray& grabData); bool fromByteArray(const QByteArray& grabData);
@ -61,6 +62,7 @@ public:
_positionalOffset = other->_positionalOffset; _positionalOffset = other->_positionalOffset;
_rotationalOffset = other->_rotationalOffset; _rotationalOffset = other->_rotationalOffset;
_actionID = other->_actionID; _actionID = other->_actionID;
_released = other->_released;
return *this; return *this;
} }
@ -85,6 +87,9 @@ public:
glm::quat getRotationalOffset() const { return _rotationalOffset; } glm::quat getRotationalOffset() const { return _rotationalOffset; }
void setRotationalOffset(glm::quat rotationalOffset) { _rotationalOffset = rotationalOffset; } void setRotationalOffset(glm::quat rotationalOffset) { _rotationalOffset = rotationalOffset; }
bool getReleased() const { return _released; }
void setReleased(bool value) { _released = value; }
protected: protected:
QUuid _actionID; // if an action is created in bullet for this grab, this is the ID QUuid _actionID; // if an action is created in bullet for this grab, this is the ID
QUuid _ownerID; // avatar ID of grabber QUuid _ownerID; // avatar ID of grabber
@ -93,6 +98,7 @@ protected:
QString _hand; // "left" or "right" QString _hand; // "left" or "right"
glm::vec3 _positionalOffset; // relative to joint glm::vec3 _positionalOffset; // relative to joint
glm::quat _rotationalOffset; // relative to joint glm::quat _rotationalOffset; // relative to joint
bool _released { false }; // released and scheduled for deletion
}; };

View file

@ -1390,7 +1390,12 @@ void SpatiallyNestable::removeGrab(GrabPointer grab) {
bool SpatiallyNestable::hasGrabs() { bool SpatiallyNestable::hasGrabs() {
bool result { false }; bool result { false };
_grabsLock.withReadLock([&] { _grabsLock.withReadLock([&] {
result = !_grabs.isEmpty(); foreach (const GrabPointer &grab, _grabs) {
if (grab && !grab->getReleased()) {
result = true;
break;
}
}
}); });
return result; return result;
} }

View file

@ -218,6 +218,7 @@ public:
virtual void addGrab(GrabPointer grab); virtual void addGrab(GrabPointer grab);
virtual void removeGrab(GrabPointer grab); virtual void removeGrab(GrabPointer grab);
virtual void disableGrab(GrabPointer grab) {};
bool hasGrabs(); bool hasGrabs();
virtual QUuid getEditSenderID(); virtual QUuid getEditSenderID();
@ -241,7 +242,7 @@ protected:
quint64 _rotationChanged { 0 }; quint64 _rotationChanged { 0 };
mutable ReadWriteLockable _grabsLock; mutable ReadWriteLockable _grabsLock;
QSet<GrabPointer> _grabs; QSet<GrabPointer> _grabs; // upon this thing
private: private:
SpatiallyNestable() = delete; SpatiallyNestable() = delete;

View file

@ -30,11 +30,17 @@ const QStringList& FileUtils::getFileSelectors() {
static std::once_flag once; static std::once_flag once;
static QStringList extraSelectors; static QStringList extraSelectors;
std::call_once(once, [] { std::call_once(once, [] {
#if defined(Q_OS_ANDROID)
//extraSelectors << "android_" HIFI_ANDROID_APP;
#endif
#if defined(USE_GLES) #if defined(USE_GLES)
extraSelectors << "gles"; extraSelectors << "gles";
#endif #endif
#if defined(Q_OS_ANDROID)
extraSelectors << HIFI_ANDROID_APP; #ifndef Q_OS_ANDROID
extraSelectors << "webengine";
#endif #endif
}); });
return extraSelectors; return extraSelectors;

View file

@ -49,6 +49,7 @@
#include <shared/ReadWriteLockable.h> #include <shared/ReadWriteLockable.h>
#include "SecurityImageProvider.h" #include "SecurityImageProvider.h"
#include "shared/FileUtils.h"
#include "types/FileTypeProfile.h" #include "types/FileTypeProfile.h"
#include "types/HFWebEngineProfile.h" #include "types/HFWebEngineProfile.h"
#include "types/SoundEffect.h" #include "types/SoundEffect.h"
@ -237,6 +238,9 @@ void OffscreenQmlSurface::clearFocusItem() {
void OffscreenQmlSurface::initializeEngine(QQmlEngine* engine) { void OffscreenQmlSurface::initializeEngine(QQmlEngine* engine) {
Parent::initializeEngine(engine); Parent::initializeEngine(engine);
QQmlFileSelector* fileSelector = new QQmlFileSelector(engine);
fileSelector->setExtraSelectors(FileUtils::getFileSelectors());
static std::once_flag once; static std::once_flag once;
std::call_once(once, [] { std::call_once(once, [] {
qRegisterMetaType<TabletProxy*>(); qRegisterMetaType<TabletProxy*>();

View file

@ -0,0 +1,118 @@
"use strict";
/* jslint vars: true, plusplus: true */
//
// defaultScripts.js
// examples
//
// Copyright 2014 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
//
var DEFAULT_SCRIPTS_COMBINED = [
"system/request-service.js",
"system/progress.js",
//"system/away.js",
"system/hmd.js",
"system/menu.js",
"system/bubble.js",
"system/pal.js", // "system/mod.js", // older UX, if you prefer
"system/avatarapp.js",
"system/makeUserConnection.js",
"system/tablet-goto.js",
"system/notifications.js",
"system/commerce/wallet.js",
"system/dialTone.js",
"system/firstPersonHMD.js",
"system/tablet-ui/tabletUI.js",
"system/miniTablet.js"
];
var DEFAULT_SCRIPTS_SEPARATE = [
"system/controllers/controllerScripts.js",
//"system/chat.js"
];
if (Window.interstitialModeEnabled) {
// Insert interstitial scripts at front so that they're started first.
DEFAULT_SCRIPTS_COMBINED.splice(0, 0, "system/interstitialPage.js", "system/redirectOverlays.js");
}
// add a menu item for debugging
var MENU_CATEGORY = "Developer > Scripting";
var MENU_ITEM = "Debug defaultScripts.js";
var SETTINGS_KEY = '_debugDefaultScriptsIsChecked';
var previousSetting = Settings.getValue(SETTINGS_KEY);
if (previousSetting === '' || previousSetting === false || previousSetting === 'false') {
previousSetting = false;
}
if (previousSetting === true || previousSetting === 'true') {
previousSetting = true;
}
if (Menu.menuExists(MENU_CATEGORY) && !Menu.menuItemExists(MENU_CATEGORY, MENU_ITEM)) {
Menu.addMenuItem({
menuName: MENU_CATEGORY,
menuItemName: MENU_ITEM,
isCheckable: true,
isChecked: previousSetting,
});
}
function loadSeparateDefaults() {
for (var i in DEFAULT_SCRIPTS_SEPARATE) {
Script.load(DEFAULT_SCRIPTS_SEPARATE[i]);
}
}
function runDefaultsTogether() {
for (var i in DEFAULT_SCRIPTS_COMBINED) {
Script.include(DEFAULT_SCRIPTS_COMBINED[i]);
}
loadSeparateDefaults();
}
function runDefaultsSeparately() {
for (var i in DEFAULT_SCRIPTS_COMBINED) {
Script.load(DEFAULT_SCRIPTS_COMBINED[i]);
}
loadSeparateDefaults();
}
// start all scripts
if (Menu.isOptionChecked(MENU_ITEM)) {
// we're debugging individual default scripts
// so we load each into its own ScriptEngine instance
runDefaultsSeparately();
} else {
// include all default scripts into this ScriptEngine
runDefaultsTogether();
}
function menuItemEvent(menuItem) {
if (menuItem === MENU_ITEM) {
var isChecked = Menu.isOptionChecked(MENU_ITEM);
if (isChecked === true) {
Settings.setValue(SETTINGS_KEY, true);
} else if (isChecked === false) {
Settings.setValue(SETTINGS_KEY, false);
}
Menu.triggerOption("Reload All Scripts");
}
}
function removeMenuItem() {
if (!Menu.isOptionChecked(MENU_ITEM)) {
Menu.removeMenuItem(MENU_CATEGORY, MENU_ITEM);
}
}
Script.scriptEnding.connect(function() {
removeMenuItem();
});
Menu.menuItemEvent.connect(menuItemEvent);

View file

@ -1,7 +1,7 @@
# Declare dependencies # Declare dependencies
macro (setup_testcase_dependencies) macro (setup_testcase_dependencies)
# link in the shared libraries # link in the shared libraries
link_hifi_libraries(shared animation gpu fbx graphics networking test-utils) link_hifi_libraries(shared animation gpu fbx hfm graphics networking test-utils)
package_libraries_for_deployment() package_libraries_for_deployment()
endmacro () endmacro ()

View file

@ -143,7 +143,7 @@ void AnimInverseKinematicsTests::testSingleChain() {
ikDoll.setTargetVars(QString("D"), QString("positionD"), QString("rotationD"), QString("targetTypeD"), ikDoll.setTargetVars(QString("D"), QString("positionD"), QString("rotationD"), QString("targetTypeD"),
QString("weightD"), 1.0f, flexCoefficients, QString("poleVectorEnabledD"), QString("weightD"), 1.0f, flexCoefficients, QString("poleVectorEnabledD"),
QString("poleReferenceVectorD"), QString("poleVectorD")); QString("poleReferenceVectorD"), QString("poleVectorD"));
AnimNode::Triggers triggers; AnimVariantMap triggers;
// the IK solution should be: // the IK solution should be:
// //
@ -236,7 +236,7 @@ void AnimInverseKinematicsTests::testSingleChain() {
ikDoll.setTargetVars(QString("D"), QString("positionD"), QString("rotationD"), QString("targetTypeD"), ikDoll.setTargetVars(QString("D"), QString("positionD"), QString("rotationD"), QString("targetTypeD"),
QString("weightD"), 1.0f, flexCoefficients, QString("poleVectorEnabledD"), QString("weightD"), 1.0f, flexCoefficients, QString("poleVectorEnabledD"),
QString("poleReferenceVectorD"), QString("poleVectorD")); QString("poleReferenceVectorD"), QString("poleVectorD"));
AnimNode::Triggers triggers; AnimVariantMap triggers;
// the IK solution should be: // the IK solution should be:
// //

View file

@ -19,6 +19,7 @@
#include <AddressManager.h> #include <AddressManager.h>
#include <AccountManager.h> #include <AccountManager.h>
#include <ResourceManager.h> #include <ResourceManager.h>
#include <ResourceRequestObserver.h>
#include <StatTracker.h> #include <StatTracker.h>
#include <test-utils/QTestExtensions.h> #include <test-utils/QTestExtensions.h>
@ -33,6 +34,7 @@ void AnimTests::initTestCase() {
DependencyManager::set<NodeList>(NodeType::Agent); DependencyManager::set<NodeList>(NodeType::Agent);
DependencyManager::set<ResourceManager>(); DependencyManager::set<ResourceManager>();
DependencyManager::set<AnimationCache>(); DependencyManager::set<AnimationCache>();
DependencyManager::set<ResourceRequestObserver>();
DependencyManager::set<ResourceCacheSharedItems>(); DependencyManager::set<ResourceCacheSharedItems>();
DependencyManager::set<StatTracker>(); DependencyManager::set<StatTracker>();
} }
@ -84,26 +86,26 @@ void AnimTests::testClipEvaulate() {
AnimClip clip(id, url, startFrame, endFrame, timeScale, loopFlag, mirrorFlag); AnimClip clip(id, url, startFrame, endFrame, timeScale, loopFlag, mirrorFlag);
AnimNode::Triggers triggers; AnimVariantMap triggers;
clip.evaluate(vars, context, framesToSec(10.0f), triggers); clip.evaluate(vars, context, framesToSec(10.0f), triggers);
QCOMPARE_WITH_ABS_ERROR(clip._frame, 12.0f, TEST_EPSILON); QCOMPARE_WITH_ABS_ERROR(clip._frame, 12.0f, TEST_EPSILON);
// does it loop? // does it loop?
triggers.clear(); triggers.clearMap();
clip.evaluate(vars, context, framesToSec(12.0f), triggers); clip.evaluate(vars, context, framesToSec(12.0f), triggers);
QCOMPARE_WITH_ABS_ERROR(clip._frame, 3.0f, TEST_EPSILON); // Note: frame 3 and not 4, because extra frame between start and end. QCOMPARE_WITH_ABS_ERROR(clip._frame, 3.0f, TEST_EPSILON); // Note: frame 3 and not 4, because extra frame between start and end.
// did we receive a loop trigger? // did we receive a loop trigger?
QVERIFY(std::find(triggers.begin(), triggers.end(), "myClipNodeOnLoop") != triggers.end()); QVERIFY(triggers.hasKey("myClipNodeOnLoop"));
// does it pause at end? // does it pause at end?
triggers.clear(); triggers.clearMap();
clip.setLoopFlagVar("FalseVar"); clip.setLoopFlagVar("FalseVar");
clip.evaluate(vars, context, framesToSec(20.0f), triggers); clip.evaluate(vars, context, framesToSec(20.0f), triggers);
QCOMPARE_WITH_ABS_ERROR(clip._frame, 22.0f, TEST_EPSILON); QCOMPARE_WITH_ABS_ERROR(clip._frame, 22.0f, TEST_EPSILON);
// did we receive a done trigger? // did we receive a done trigger?
QVERIFY(std::find(triggers.begin(), triggers.end(), "myClipNodeOnDone") != triggers.end()); QVERIFY(triggers.hasKey("myClipNodeOnDone"));
} }
void AnimTests::testClipEvaulateWithVars() { void AnimTests::testClipEvaulateWithVars() {
@ -133,7 +135,7 @@ void AnimTests::testClipEvaulateWithVars() {
clip.setTimeScaleVar("timeScale2"); clip.setTimeScaleVar("timeScale2");
clip.setLoopFlagVar("loopFlag2"); clip.setLoopFlagVar("loopFlag2");
AnimNode::Triggers triggers; AnimVariantMap triggers;
clip.evaluate(vars, context, framesToSec(0.1f), triggers); clip.evaluate(vars, context, framesToSec(0.1f), triggers);
// verify that the values from the AnimVariantMap made it into the clipNode's // verify that the values from the AnimVariantMap made it into the clipNode's
@ -284,11 +286,11 @@ void AnimTests::testAccumulateTime() {
timeScale = 1.0f; timeScale = 1.0f;
float dt = 1.0f; float dt = 1.0f;
QString id = "testNode"; QString id = "testNode";
AnimNode::Triggers triggers; AnimVariantMap triggers;
float loopFlag = true; float loopFlag = true;
float resultFrame = accumulateTime(startFrame, endFrame, timeScale, startFrame, dt, loopFlag, id, triggers); float resultFrame = accumulateTime(startFrame, endFrame, timeScale, startFrame, dt, loopFlag, id, triggers);
// a one frame looping animation should NOT trigger onLoop events // a one frame looping animation should NOT trigger onLoop events
QVERIFY(triggers.empty()); QVERIFY(!triggers.hasKey("testNodeOnLoop"));
const uint32_t MAX_TRIGGER_COUNT = 3; const uint32_t MAX_TRIGGER_COUNT = 3;
@ -296,45 +298,45 @@ void AnimTests::testAccumulateTime() {
endFrame = 1.1f; endFrame = 1.1f;
timeScale = 10.0f; timeScale = 10.0f;
dt = 10.0f; dt = 10.0f;
triggers.clear(); triggers.clearMap();
loopFlag = true; loopFlag = true;
resultFrame = accumulateTime(startFrame, endFrame, timeScale, startFrame, dt, loopFlag, id, triggers); resultFrame = accumulateTime(startFrame, endFrame, timeScale, startFrame, dt, loopFlag, id, triggers);
// a short animation with a large dt & a large timescale, should only create a MAXIMUM of 3 loop events. // a short animation with a large dt & a large timescale, should generate a onLoop event.
QVERIFY(triggers.size() <= MAX_TRIGGER_COUNT); QVERIFY(triggers.hasKey("testNodeOnLoop"));
} }
void AnimTests::testAccumulateTimeWithParameters(float startFrame, float endFrame, float timeScale) const { void AnimTests::testAccumulateTimeWithParameters(float startFrame, float endFrame, float timeScale) const {
float dt = (1.0f / 30.0f) / timeScale; // sec float dt = (1.0f / 30.0f) / timeScale; // sec
QString id = "testNode"; QString id = "testNode";
AnimNode::Triggers triggers; AnimVariantMap triggers;
bool loopFlag = false; bool loopFlag = false;
float resultFrame = accumulateTime(startFrame, endFrame, timeScale, startFrame, dt, loopFlag, id, triggers); float resultFrame = accumulateTime(startFrame, endFrame, timeScale, startFrame, dt, loopFlag, id, triggers);
QVERIFY(resultFrame == startFrame + 1.0f); QVERIFY(resultFrame == startFrame + 1.0f);
QVERIFY(triggers.empty()); QVERIFY(!triggers.hasKey("testNodeOnLoop"));
triggers.clear(); triggers.clearMap();
resultFrame = accumulateTime(startFrame, endFrame, timeScale, resultFrame, dt, loopFlag, id, triggers); resultFrame = accumulateTime(startFrame, endFrame, timeScale, resultFrame, dt, loopFlag, id, triggers);
QVERIFY(resultFrame == startFrame + 2.0f); QVERIFY(resultFrame == startFrame + 2.0f);
QVERIFY(triggers.empty()); QVERIFY(!triggers.hasKey("testNodeOnLoop"));
triggers.clear(); triggers.clearMap();
resultFrame = accumulateTime(startFrame, endFrame, timeScale, resultFrame, dt, loopFlag, id, triggers); resultFrame = accumulateTime(startFrame, endFrame, timeScale, resultFrame, dt, loopFlag, id, triggers);
QVERIFY(resultFrame == startFrame + 3.0f); QVERIFY(resultFrame == startFrame + 3.0f);
QVERIFY(triggers.empty()); QVERIFY(!triggers.hasKey("testNodeOnLoop"));
triggers.clear(); triggers.clearMap();
// test onDone trigger and frame clamping. // test onDone trigger and frame clamping.
resultFrame = accumulateTime(startFrame, endFrame, timeScale, endFrame - 1.0f, dt, loopFlag, id, triggers); resultFrame = accumulateTime(startFrame, endFrame, timeScale, endFrame - 1.0f, dt, loopFlag, id, triggers);
QVERIFY(resultFrame == endFrame); QVERIFY(resultFrame == endFrame);
QVERIFY(!triggers.empty() && triggers[0] == "testNodeOnDone"); QVERIFY(triggers.hasKey("testNodeOnDone"));
triggers.clear(); triggers.clearMap();
resultFrame = accumulateTime(startFrame, endFrame, timeScale, endFrame - 0.5f, dt, loopFlag, id, triggers); resultFrame = accumulateTime(startFrame, endFrame, timeScale, endFrame - 0.5f, dt, loopFlag, id, triggers);
QVERIFY(resultFrame == endFrame); QVERIFY(resultFrame == endFrame);
QVERIFY(!triggers.empty() && triggers[0] == "testNodeOnDone"); QVERIFY(triggers.hasKey("testNodeOnDone"));
triggers.clear(); triggers.clearMap();
// test onLoop trigger and looping frame logic // test onLoop trigger and looping frame logic
loopFlag = true; loopFlag = true;
@ -342,26 +344,26 @@ void AnimTests::testAccumulateTimeWithParameters(float startFrame, float endFram
// should NOT trigger loop even though we stop at last frame, because there is an extra frame between end and start frames. // should NOT trigger loop even though we stop at last frame, because there is an extra frame between end and start frames.
resultFrame = accumulateTime(startFrame, endFrame, timeScale, endFrame - 1.0f, dt, loopFlag, id, triggers); resultFrame = accumulateTime(startFrame, endFrame, timeScale, endFrame - 1.0f, dt, loopFlag, id, triggers);
QVERIFY(resultFrame == endFrame); QVERIFY(resultFrame == endFrame);
QVERIFY(triggers.empty()); QVERIFY(!triggers.hasKey("testNodeOnLoop"));
triggers.clear(); triggers.clearMap();
// now we should hit loop trigger // now we should hit loop trigger
resultFrame = accumulateTime(startFrame, endFrame, timeScale, resultFrame, dt, loopFlag, id, triggers); resultFrame = accumulateTime(startFrame, endFrame, timeScale, resultFrame, dt, loopFlag, id, triggers);
QVERIFY(resultFrame == startFrame); QVERIFY(resultFrame == startFrame);
QVERIFY(!triggers.empty() && triggers[0] == "testNodeOnLoop"); QVERIFY(triggers.hasKey("testNodeOnLoop"));
triggers.clear(); triggers.clearMap();
// should NOT trigger loop, even though we move past the end frame, because of extra frame between end and start. // should NOT trigger loop, even though we move past the end frame, because of extra frame between end and start.
resultFrame = accumulateTime(startFrame, endFrame, timeScale, endFrame - 0.5f, dt, loopFlag, id, triggers); resultFrame = accumulateTime(startFrame, endFrame, timeScale, endFrame - 0.5f, dt, loopFlag, id, triggers);
QVERIFY(resultFrame == endFrame + 0.5f); QVERIFY(resultFrame == endFrame + 0.5f);
QVERIFY(triggers.empty()); QVERIFY(!triggers.hasKey("testNodeOnLoop"));
triggers.clear(); triggers.clearMap();
// now we should hit loop trigger // now we should hit loop trigger
resultFrame = accumulateTime(startFrame, endFrame, timeScale, resultFrame, dt, loopFlag, id, triggers); resultFrame = accumulateTime(startFrame, endFrame, timeScale, resultFrame, dt, loopFlag, id, triggers);
QVERIFY(resultFrame == startFrame + 0.5f); QVERIFY(resultFrame == startFrame + 0.5f);
QVERIFY(!triggers.empty() && triggers[0] == "testNodeOnLoop"); QVERIFY(triggers.hasKey("testNodeOnLoop"));
triggers.clear(); triggers.clearMap();
} }
void AnimTests::testAnimPose() { void AnimTests::testAnimPose() {