diff --git a/interface/resources/config/render.json b/interface/resources/config/render.json index 414d94e11e..b5b72d7d07 100644 --- a/interface/resources/config/render.json +++ b/interface/resources/config/render.json @@ -1,14 +1,16 @@ { - "RenderShadowTask": { - "Enabled": { - "enabled": true - } - }, - "RenderDeferredTask": { - "AmbientOcclusion": { + "RenderMainView": { + "RenderShadowTask": { "Enabled": { "enabled": true } + }, + "RenderDeferredTask": { + "AmbientOcclusion": { + "Enabled": { + "enabled": true + } + } } } } diff --git a/interface/resources/html/createGlobalEventBridge.js b/interface/resources/html/createGlobalEventBridge.js index 027d6fe8db..4a0de464c3 100644 --- a/interface/resources/html/createGlobalEventBridge.js +++ b/interface/resources/html/createGlobalEventBridge.js @@ -32,7 +32,7 @@ var EventBridge; var webChannel = new QWebChannel(qt.webChannelTransport, function (channel) { // replace the TempEventBridge with the real one. var tempEventBridge = EventBridge; - EventBridge = channel.objects.eventBridgeWrapper.eventBridge; + EventBridge = channel.objects.eventBridge; tempEventBridge._callbacks.forEach(function (callback) { EventBridge.scriptEventReceived.connect(callback); }); diff --git a/interface/resources/meshes/Jointy3/Jointy3.fbx b/interface/resources/meshes/Jointy3/Jointy3.fbx deleted file mode 100644 index 9df7b17eac..0000000000 Binary files a/interface/resources/meshes/Jointy3/Jointy3.fbx and /dev/null differ diff --git a/interface/resources/meshes/defaultAvatar_full.fst b/interface/resources/meshes/defaultAvatar_full.fst index 3abbaf9ff7..eb8e356196 100644 --- a/interface/resources/meshes/defaultAvatar_full.fst +++ b/interface/resources/meshes/defaultAvatar_full.fst @@ -1,85 +1,89 @@ -name = Jointy3 +name = mannequin type = body+head scale = 1 -filename = Jointy3/Jointy3.fbx -texdir = Jointy3/textures +filename = mannequin/mannequin.baked.fbx +joint = jointEyeLeft = LeftEye +joint = jointRightHand = RightHand +joint = jointHead = Head +joint = jointEyeRight = RightEye +joint = jointLean = Spine joint = jointNeck = Neck joint = jointLeftHand = LeftHand -joint = jointEyeRight = RightEye -joint = jointHead = Head -joint = jointRightHand = RightHand joint = jointRoot = Hips -joint = jointLean = Spine -joint = jointEyeLeft = LeftEye freeJoint = LeftArm freeJoint = LeftForeArm freeJoint = RightArm freeJoint = RightForeArm -jointIndex = RightHand = 17 -jointIndex = LeftHandIndex3 = 56 -jointIndex = Hips = 0 -jointIndex = LeftHandRing2 = 47 -jointIndex = LeftHandThumb3 = 60 -jointIndex = RightShoulder = 14 -jointIndex = RightHandRing1 = 30 -jointIndex = RightHandRing3 = 32 -jointIndex = LeftHandPinky4 = 45 -jointIndex = LeftHandRing1 = 46 -jointIndex = LeftFoot = 8 -jointIndex = RightHandIndex2 = 23 -jointIndex = RightToeBase = 4 -jointIndex = RightHandMiddle4 = 29 -jointIndex = RightHandPinky4 = 37 -jointIndex = LeftToe_End = 10 -jointIndex = RightEye = 66 -jointIndex = RightHandPinky2 = 35 -jointIndex = RightHandRing2 = 31 -jointIndex = LeftHand = 41 -jointIndex = RightToe_End = 5 -jointIndex = LeftEye = 65 -jointIndex = LeftHandThumb2 = 59 -jointIndex = pCylinder73Shape1 = 67 -jointIndex = LeftShoulder = 38 -jointIndex = LeftHandIndex2 = 55 -jointIndex = RightForeArm = 16 -jointIndex = LeftHandMiddle2 = 51 -jointIndex = RightHandRing4 = 33 -jointIndex = LeftLeg = 7 -jointIndex = LeftHandThumb4 = 61 -jointIndex = LeftForeArm = 40 -jointIndex = HeadTop_End = 64 -jointIndex = RightHandPinky1 = 34 -jointIndex = RightHandIndex1 = 22 -jointIndex = LeftHandIndex1 = 54 -jointIndex = RightLeg = 2 -jointIndex = RightHandIndex4 = 25 -jointIndex = Neck = 62 -jointIndex = LeftHandMiddle1 = 50 -jointIndex = RightHandPinky3 = 36 -jointIndex = LeftHandPinky2 = 43 -jointIndex = RightHandMiddle3 = 28 -jointIndex = RightHandThumb4 = 21 -jointIndex = LeftUpLeg = 6 -jointIndex = RightFoot = 3 -jointIndex = LeftHandThumb1 = 58 -jointIndex = LeftArm = 39 -jointIndex = RightHandMiddle1 = 26 -jointIndex = LeftHandRing3 = 48 -jointIndex = LeftHandMiddle4 = 53 -jointIndex = RightUpLeg = 1 -jointIndex = RightHandMiddle2 = 27 -jointIndex = LeftToeBase = 9 -jointIndex = RightHandThumb2 = 19 -jointIndex = Spine2 = 13 -jointIndex = Spine = 11 -jointIndex = LeftHandRing4 = 49 -jointIndex = Head = 63 -jointIndex = LeftHandPinky3 = 44 +bs = EyeBlink_L = blink = 1 +bs = JawOpen = mouth_Open = 1 +bs = LipsFunnel = Oo = 1 +bs = BrowsU_L = brow_Up = 1 +jointIndex = RightHandIndex2 = 27 +jointIndex = LeftHandIndex2 = 51 +jointIndex = RightUpLeg = 6 +jointIndex = RightToe_End = 10 +jointIndex = RightEye = 65 jointIndex = LeftHandPinky1 = 42 -jointIndex = RightHandThumb1 = 18 -jointIndex = LeftHandIndex4 = 57 -jointIndex = LeftHandMiddle3 = 52 -jointIndex = RightHandIndex3 = 24 -jointIndex = Spine1 = 12 +jointIndex = RightHandRing1 = 22 +jointIndex = face = 67 +jointIndex = LeftUpLeg = 1 +jointIndex = LeftHand = 41 +jointIndex = LeftHandMiddle1 = 58 +jointIndex = LeftHandIndex1 = 50 +jointIndex = LeftEye = 64 +jointIndex = RightHandIndex1 = 26 +jointIndex = LeftHandPinky4 = 45 jointIndex = RightArm = 15 -jointIndex = RightHandThumb3 = 20 +jointIndex = LeftShoulder = 38 +jointIndex = RightHandPinky2 = 19 +jointIndex = RightHandThumb1 = 30 +jointIndex = RightForeArm = 16 +jointIndex = LeftHandMiddle3 = 60 +jointIndex = Neck = 62 +jointIndex = LeftHandThumb1 = 54 +jointIndex = RightHandMiddle2 = 35 +jointIndex = LeftHandMiddle4 = 61 +jointIndex = mannequin = 68 +jointIndex = Spine1 = 12 +jointIndex = RightFoot = 8 +jointIndex = RightHand = 17 +jointIndex = LeftHandIndex3 = 52 +jointIndex = RightHandIndex3 = 28 +jointIndex = RightHandMiddle4 = 37 +jointIndex = LeftLeg = 2 +jointIndex = RightHandMiddle1 = 34 +jointIndex = Spine2 = 13 +jointIndex = LeftHandMiddle2 = 59 +jointIndex = LeftHandPinky3 = 44 +jointIndex = LeftHandThumb3 = 56 +jointIndex = LeftHandRing4 = 49 +jointIndex = RightHandThumb2 = 31 +jointIndex = LeftHandRing3 = 48 +jointIndex = HeadTop_End = 66 +jointIndex = LeftHandThumb4 = 57 +jointIndex = RightHandThumb3 = 32 +jointIndex = RightHandPinky1 = 18 +jointIndex = RightLeg = 7 +jointIndex = RightHandMiddle3 = 36 +jointIndex = RightHandPinky3 = 20 +jointIndex = LeftToeBase = 4 +jointIndex = LeftForeArm = 40 +jointIndex = RightShoulder = 14 +jointIndex = LeftHandRing2 = 47 +jointIndex = LeftHandThumb2 = 55 +jointIndex = Head = 63 +jointIndex = RightHandRing4 = 25 +jointIndex = LeftHandRing1 = 46 +jointIndex = LeftFoot = 3 +jointIndex = RightHandRing3 = 24 +jointIndex = RightHandThumb4 = 33 +jointIndex = LeftArm = 39 +jointIndex = LeftToe_End = 5 +jointIndex = RightToeBase = 9 +jointIndex = RightHandPinky4 = 21 +jointIndex = Spine = 11 +jointIndex = LeftHandIndex4 = 53 +jointIndex = LeftHandPinky2 = 43 +jointIndex = RightHandIndex4 = 29 +jointIndex = Hips = 0 +jointIndex = RightHandRing2 = 23 diff --git a/interface/resources/meshes/mannequin/Eyes.ktx b/interface/resources/meshes/mannequin/Eyes.ktx new file mode 100755 index 0000000000..4da922936b Binary files /dev/null and b/interface/resources/meshes/mannequin/Eyes.ktx differ diff --git a/interface/resources/meshes/mannequin/lambert1_Base_Color.ktx b/interface/resources/meshes/mannequin/lambert1_Base_Color.ktx new file mode 100755 index 0000000000..fcca382445 Binary files /dev/null and b/interface/resources/meshes/mannequin/lambert1_Base_Color.ktx differ diff --git a/interface/resources/meshes/mannequin/lambert1_Normal_OpenGL.ktx b/interface/resources/meshes/mannequin/lambert1_Normal_OpenGL.ktx new file mode 100755 index 0000000000..3ae717c5d7 Binary files /dev/null and b/interface/resources/meshes/mannequin/lambert1_Normal_OpenGL.ktx differ diff --git a/interface/resources/meshes/mannequin/lambert1_Roughness.ktx b/interface/resources/meshes/mannequin/lambert1_Roughness.ktx new file mode 100755 index 0000000000..fe9b42a54b Binary files /dev/null and b/interface/resources/meshes/mannequin/lambert1_Roughness.ktx differ diff --git a/interface/resources/meshes/mannequin/mannequin.baked.fbx b/interface/resources/meshes/mannequin/mannequin.baked.fbx new file mode 100755 index 0000000000..b405b4cfbb Binary files /dev/null and b/interface/resources/meshes/mannequin/mannequin.baked.fbx differ diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index 4f7639dd0e..55927fda24 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -21,8 +21,6 @@ ScrollingWindow { property alias url: webview.url property alias webView: webview - property alias eventBridge: eventBridgeWrapper.eventBridge - signal loadingChanged(int status) x: 100 @@ -210,17 +208,6 @@ ScrollingWindow { url: "https://highfidelity.com/" profile: FileTypeProfile; - property alias eventBridgeWrapper: eventBridgeWrapper - - QtObject { - id: eventBridgeWrapper - WebChannel.id: "eventBridgeWrapper" - property var eventBridge; - } - - - webChannel.registeredObjects: [eventBridgeWrapper] - // Create a global EventBridge object for raiseAndLowerKeyboard. WebEngineScript { id: createGlobalEventBridge @@ -267,6 +254,8 @@ ScrollingWindow { } Component.onCompleted: { + webChannel.registerObject("eventBridge", eventBridge); + webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper); desktop.initWebviewProfileHandlers(webview.profile); } } diff --git a/interface/resources/qml/QmlWebWindow.qml b/interface/resources/qml/QmlWebWindow.qml index d40b1595ba..d2daf0fa1d 100644 --- a/interface/resources/qml/QmlWebWindow.qml +++ b/interface/resources/qml/QmlWebWindow.qml @@ -26,15 +26,8 @@ Windows.ScrollingWindow { // Don't destroy on close... otherwise the JS/C++ will have a dangling pointer destroyOnCloseButton: false property alias source: webview.url - property alias eventBridge: eventBridgeWrapper.eventBridge; property alias scriptUrl: webview.userScriptUrl - QtObject { - id: eventBridgeWrapper - WebChannel.id: "eventBridgeWrapper" - property var eventBridge; - } - // This is for JS/QML communication, which is unused in a WebWindow, // but not having this here results in spurious warnings about a // missing signal @@ -70,7 +63,6 @@ Windows.ScrollingWindow { url: "about:blank" anchors.fill: parent focus: true - webChannel.registeredObjects: [eventBridgeWrapper] property string userScriptUrl: "" @@ -107,6 +99,8 @@ Windows.ScrollingWindow { } Component.onCompleted: { + webChannel.registerObject("eventBridge", eventBridge); + webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper); eventBridge.webEventReceived.connect(onWebEventReceived); } } diff --git a/interface/resources/qml/QmlWindow.qml b/interface/resources/qml/QmlWindow.qml index ac18d36ce6..9ef151b32e 100644 --- a/interface/resources/qml/QmlWindow.qml +++ b/interface/resources/qml/QmlWindow.qml @@ -30,15 +30,6 @@ Windows.Window { property bool keyboardRaised: false property bool punctuationMode: false - // JavaScript event bridge object in case QML content includes Web content. - property alias eventBridge: eventBridgeWrapper.eventBridge; - - QtObject { - id: eventBridgeWrapper - WebChannel.id: "eventBridgeWrapper" - property var eventBridge; - } - onSourceChanged: { if (dynamicContent) { dynamicContent.destroy(); diff --git a/interface/resources/qml/TabletBrowser.qml b/interface/resources/qml/TabletBrowser.qml index d89aa8626f..c3d879c513 100644 --- a/interface/resources/qml/TabletBrowser.qml +++ b/interface/resources/qml/TabletBrowser.qml @@ -18,7 +18,6 @@ Item { property variant permissionsBar: {'securityOrigin':'none','feature':'none'} property alias url: webview.url property WebEngineView webView: webview - property alias eventBridge: eventBridgeWrapper.eventBridge property bool canGoBack: webview.canGoBack property bool canGoForward: webview.canGoForward @@ -32,12 +31,6 @@ Item { webview.profile = profile; } - QtObject { - id: eventBridgeWrapper - WebChannel.id: "eventBridgeWrapper" - property var eventBridge; - } - WebEngineView { id: webview objectName: "webEngineView" @@ -78,9 +71,10 @@ Item { property string newUrl: "" - webChannel.registeredObjects: [eventBridgeWrapper] - 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); diff --git a/interface/resources/qml/ToolWindow.qml b/interface/resources/qml/ToolWindow.qml index 9c0b0a8c1a..b1120058f9 100644 --- a/interface/resources/qml/ToolWindow.qml +++ b/interface/resources/qml/ToolWindow.qml @@ -79,15 +79,11 @@ ScrollingWindow { id: webView anchors.fill: parent enabled: false - property alias eventBridgeWrapper: eventBridgeWrapper - - QtObject { - id: eventBridgeWrapper - WebChannel.id: "eventBridgeWrapper" - property var eventBridge + Component.onCompleted: { + webChannel.registerObject("eventBridge", eventBridge); + webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper); } - webChannel.registeredObjects: [eventBridgeWrapper] onEnabledChanged: toolWindow.updateVisiblity() } } @@ -251,12 +247,9 @@ ScrollingWindow { tab.enabled = true; tab.originalUrl = properties.source; - var eventBridge = properties.eventBridge; - var result = tab.item; result.enabled = true; tabView.tabCount++; - result.eventBridgeWrapper.eventBridge = eventBridge; result.url = properties.source; return result; } diff --git a/interface/resources/qml/controls/TabletWebScreen.qml b/interface/resources/qml/controls/TabletWebScreen.qml index 0b265f6fbb..68f8226e21 100644 --- a/interface/resources/qml/controls/TabletWebScreen.qml +++ b/interface/resources/qml/controls/TabletWebScreen.qml @@ -6,7 +6,6 @@ import "../controls-uit" as HiFiControls Item { property alias url: root.url property alias scriptURL: root.userScriptUrl - property alias eventBridge: eventBridgeWrapper.eventBridge property alias canGoBack: root.canGoBack; property var goBack: root.goBack; property alias urlTag: root.urlTag @@ -22,12 +21,6 @@ Item { } */ - QtObject { - id: eventBridgeWrapper - WebChannel.id: "eventBridgeWrapper" - property var eventBridge; - } - property alias viewProfile: root.profile WebEngineView { @@ -71,10 +64,11 @@ Item { userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ] property string newUrl: "" - - webChannel.registeredObjects: [eventBridgeWrapper] + Component.onCompleted: { + webChannel.registerObject("eventBridge", eventBridge); + webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper); // Ensure the JS from the web-engine makes it to our logging root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) { console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message); diff --git a/interface/resources/qml/controls/TabletWebView.qml b/interface/resources/qml/controls/TabletWebView.qml index 3b23cbc19e..0a5a68717e 100644 --- a/interface/resources/qml/controls/TabletWebView.qml +++ b/interface/resources/qml/controls/TabletWebView.qml @@ -17,7 +17,6 @@ Item { property int headerHeight: 70 property string url property string scriptURL - property alias eventBridge: eventBridgeWrapper.eventBridge property bool keyboardEnabled: false property bool keyboardRaised: false property bool punctuationMode: false @@ -135,12 +134,6 @@ Item { loadUrl(url); } - QtObject { - id: eventBridgeWrapper - WebChannel.id: "eventBridgeWrapper" - property var eventBridge; - } - WebEngineView { id: webview objectName: "webEngineView" @@ -182,9 +175,9 @@ Item { property string newUrl: "" - webChannel.registeredObjects: [eventBridgeWrapper] - 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); diff --git a/interface/resources/qml/controls/WebView.qml b/interface/resources/qml/controls/WebView.qml index d08562eea3..38136c7eec 100644 --- a/interface/resources/qml/controls/WebView.qml +++ b/interface/resources/qml/controls/WebView.qml @@ -6,7 +6,6 @@ import "../controls-uit" as HiFiControls Item { property alias url: root.url property alias scriptURL: root.userScriptUrl - property alias eventBridge: eventBridgeWrapper.eventBridge property alias canGoBack: root.canGoBack; property var goBack: root.goBack; property alias urlTag: root.urlTag @@ -22,12 +21,6 @@ Item { } */ - QtObject { - id: eventBridgeWrapper - WebChannel.id: "eventBridgeWrapper" - property var eventBridge; - } - property alias viewProfile: root.profile WebEngineView { @@ -72,9 +65,9 @@ Item { property string newUrl: "" - webChannel.registeredObjects: [eventBridgeWrapper] - Component.onCompleted: { + webChannel.registerObject("eventBridge", eventBridge); + webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper); // Ensure the JS from the web-engine makes it to our logging root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) { console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message); diff --git a/interface/resources/qml/dialogs/TabletLoginDialog.qml b/interface/resources/qml/dialogs/TabletLoginDialog.qml index 30c3e678b4..36ca480b24 100644 --- a/interface/resources/qml/dialogs/TabletLoginDialog.qml +++ b/interface/resources/qml/dialogs/TabletLoginDialog.qml @@ -20,7 +20,6 @@ TabletModalWindow { id: loginDialogRoot objectName: "LoginDialog" - property var eventBridge; signal sendToScript(var message); property bool isHMD: false property bool gotoPreviousApp: false; diff --git a/interface/resources/qml/dialogs/preferences/AvatarBrowser.qml b/interface/resources/qml/dialogs/preferences/AvatarBrowser.qml index 652e02b6b9..e2a012ad46 100644 --- a/interface/resources/qml/dialogs/preferences/AvatarBrowser.qml +++ b/interface/resources/qml/dialogs/preferences/AvatarBrowser.qml @@ -24,8 +24,6 @@ Window { resizable: true modality: Qt.ApplicationModal - property alias eventBridge: eventBridgeWrapper.eventBridge - Item { anchors.fill: parent @@ -45,16 +43,6 @@ Window { bottom: keyboard.top } - property alias eventBridgeWrapper: eventBridgeWrapper - - QtObject { - id: eventBridgeWrapper - WebChannel.id: "eventBridgeWrapper" - property var eventBridge; - } - - webChannel.registeredObjects: [eventBridgeWrapper] - // Create a global EventBridge object for raiseAndLowerKeyboard. WebEngineScript { id: createGlobalEventBridge @@ -73,6 +61,10 @@ Window { userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ] + Component.onCompleted: { + webChannel.registerObject("eventBridge", eventBridge); + webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper); + } } Keyboard { diff --git a/interface/resources/qml/dialogs/preferences/AvatarPreference.qml b/interface/resources/qml/dialogs/preferences/AvatarPreference.qml index bcc5a1d9e6..b27827d9d7 100644 --- a/interface/resources/qml/dialogs/preferences/AvatarPreference.qml +++ b/interface/resources/qml/dialogs/preferences/AvatarPreference.qml @@ -116,9 +116,7 @@ Preference { Component { id: tabletAvatarBrowserBuilder; - TabletAvatarBrowser { - eventBridge: tabletRoot.eventBridge - } + TabletAvatarBrowser { } } } diff --git a/interface/resources/qml/hifi/Audio.qml b/interface/resources/qml/hifi/Audio.qml index 66760ff290..48de891733 100644 --- a/interface/resources/qml/hifi/Audio.qml +++ b/interface/resources/qml/hifi/Audio.qml @@ -31,7 +31,6 @@ Rectangle { HifiConstants { id: hifi; } objectName: "AudioWindow" - property var eventBridge; property string title: "Audio Options" signal sendToScript(var message); @@ -161,7 +160,12 @@ Rectangle { text.text: devicename onCheckBoxClicked: { if (checked) { - AudioDevice.setInputDeviceAsync(devicename) + if (devicename.length > 0) { + console.log("Audio.qml about to call AudioDevice.setInputDeviceAsync().devicename:" + devicename); + AudioDevice.setInputDeviceAsync(devicename); + } else { + console.log("Audio.qml attempted to set input device to empty device name."); + } } } } @@ -217,7 +221,13 @@ Rectangle { text.text: devicename onCheckBoxClicked: { if (checked) { - AudioDevice.setOutputDeviceAsync(devicename) + if (devicename.length > 0) { + console.log("Audio.qml about to call AudioDevice.setOutputDeviceAsync().devicename:" + devicename); + AudioDevice.setOutputDeviceAsync(devicename); + } else { + console.log("Audio.qml attempted to set output device to empty device name."); + } + } } } diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 2d6b21b219..250015bab9 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -44,7 +44,6 @@ Rectangle { property var activeTab: "nearbyTab"; property bool currentlyEditingDisplayName: false property bool punctuationMode: false; - property var eventBridge; HifiConstants { id: hifi; } @@ -1043,7 +1042,6 @@ Rectangle { } // Keyboard HifiControls.TabletWebView { - eventBridge: pal.eventBridge; id: userInfoViewer; anchors { top: parent.top; diff --git a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml index 6e0263787b..47c9af1f57 100644 --- a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml +++ b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml @@ -24,7 +24,6 @@ Rectangle { property string title: "Asset Browser" property bool keyboardRaised: false - property var eventBridge; signal sendToScript(var message); property bool isHMD: false diff --git a/interface/resources/qml/hifi/dialogs/TabletDCDialog.qml b/interface/resources/qml/hifi/dialogs/TabletDCDialog.qml index b33b957e15..0f363d1be9 100644 --- a/interface/resources/qml/hifi/dialogs/TabletDCDialog.qml +++ b/interface/resources/qml/hifi/dialogs/TabletDCDialog.qml @@ -20,7 +20,6 @@ Rectangle { id: root objectName: "DCConectionTiming" - property var eventBridge; signal sendToScript(var message); property bool isHMD: false diff --git a/interface/resources/qml/hifi/dialogs/TabletDebugWindow.qml b/interface/resources/qml/hifi/dialogs/TabletDebugWindow.qml index d4bbe0af04..22e9dc07a2 100644 --- a/interface/resources/qml/hifi/dialogs/TabletDebugWindow.qml +++ b/interface/resources/qml/hifi/dialogs/TabletDebugWindow.qml @@ -19,7 +19,6 @@ Rectangle { id: root objectName: "DebugWindow" - property var eventBridge; property var title: "Debug Window" property bool isHMD: false diff --git a/interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml b/interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml index 35ee58be0c..da295917a0 100644 --- a/interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml +++ b/interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml @@ -20,7 +20,6 @@ Rectangle { id: root objectName: "EntityStatistics" - property var eventBridge; signal sendToScript(var message); property bool isHMD: false diff --git a/interface/resources/qml/hifi/dialogs/TabletLODTools.qml b/interface/resources/qml/hifi/dialogs/TabletLODTools.qml index 26e9759e0d..2291a42bf6 100644 --- a/interface/resources/qml/hifi/dialogs/TabletLODTools.qml +++ b/interface/resources/qml/hifi/dialogs/TabletLODTools.qml @@ -20,7 +20,6 @@ Rectangle { id: root objectName: "LODTools" - property var eventBridge; signal sendToScript(var message); property bool isHMD: false diff --git a/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml b/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml index d826b40ad1..11643ae1f1 100644 --- a/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml +++ b/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml @@ -23,7 +23,6 @@ Rectangle { property string title: "Running Scripts" HifiConstants { id: hifi } signal sendToScript(var message); - property var eventBridge; property var scripts: ScriptDiscoveryService; property var scriptsModel: scripts.scriptsModelFilter property var runningScriptsModel: ListModel { } diff --git a/interface/resources/qml/hifi/tablet/Edit.qml b/interface/resources/qml/hifi/tablet/Edit.qml index ea31eb26d8..e2e8c4362e 100644 --- a/interface/resources/qml/hifi/tablet/Edit.qml +++ b/interface/resources/qml/hifi/tablet/Edit.qml @@ -7,14 +7,12 @@ StackView { objectName: "stack" initialItem: Qt.resolvedUrl('EditTabView.qml') - property var eventBridge; signal sendToScript(var message); HifiConstants { id: hifi } function pushSource(path) { editRoot.push(Qt.resolvedUrl(path)); - editRoot.currentItem.eventBridge = editRoot.eventBridge; editRoot.currentItem.sendToScript.connect(editRoot.sendToScript); } diff --git a/interface/resources/qml/hifi/tablet/EditTabView.qml b/interface/resources/qml/hifi/tablet/EditTabView.qml index d084f1c7b3..e4a20a0316 100644 --- a/interface/resources/qml/hifi/tablet/EditTabView.qml +++ b/interface/resources/qml/hifi/tablet/EditTabView.qml @@ -181,7 +181,6 @@ TabView { WebView { id: entityListToolWebView url: "../../../../../scripts/system/html/entityList.html" - eventBridge: editRoot.eventBridge anchors.fill: parent enabled: true } @@ -196,7 +195,6 @@ TabView { WebView { id: entityPropertiesWebView url: "../../../../../scripts/system/html/entityProperties.html" - eventBridge: editRoot.eventBridge anchors.fill: parent enabled: true } @@ -211,7 +209,6 @@ TabView { WebView { id: gridControlsWebView url: "../../../../../scripts/system/html/gridControls.html" - eventBridge: editRoot.eventBridge anchors.fill: parent enabled: true } @@ -226,7 +223,6 @@ TabView { WebView { id: particleExplorerWebView url: "../../../../../scripts/system/particle_explorer/particleExplorer.html" - eventBridge: editRoot.eventBridge anchors.fill: parent enabled: true } @@ -289,7 +285,7 @@ TabView { editTabView.currentIndex = id; } else { console.warn('Attempt to switch to invalid tab:', id); - } + } } else if (typeof id === 'string'){ switch (id.toLowerCase()) { case 'create': diff --git a/interface/resources/qml/hifi/tablet/InputRecorder.qml b/interface/resources/qml/hifi/tablet/InputRecorder.qml index 76b122d07d..292deb751e 100644 --- a/interface/resources/qml/hifi/tablet/InputRecorder.qml +++ b/interface/resources/qml/hifi/tablet/InputRecorder.qml @@ -18,7 +18,6 @@ import "../../dialogs" Rectangle { id: inputRecorder - property var eventBridge; HifiConstants { id: hifi } signal sendToScript(var message); color: hifi.colors.baseGray; diff --git a/interface/resources/qml/hifi/tablet/NewModelDialog.qml b/interface/resources/qml/hifi/tablet/NewModelDialog.qml index 2d9d121209..5040c043f4 100644 --- a/interface/resources/qml/hifi/tablet/NewModelDialog.qml +++ b/interface/resources/qml/hifi/tablet/NewModelDialog.qml @@ -20,7 +20,6 @@ Rectangle { // height: parent.height HifiConstants { id: hifi } color: hifi.colors.baseGray; - property var eventBridge; signal sendToScript(var message); property bool keyboardEnabled: false property bool punctuationMode: false diff --git a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml index 7159b078ee..073f143dbe 100644 --- a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml +++ b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml @@ -29,7 +29,6 @@ StackView { initialItem: addressBarDialog width: parent !== null ? parent.width : undefined height: parent !== null ? parent.height : undefined - property var eventBridge; property int cardWidth: 212; property int cardHeight: 152; property string metaverseBase: addressBarDialog.metaverseServerUrl + "/api/v1/"; @@ -80,7 +79,6 @@ StackView { var card = tabletWebView.createObject(); card.url = addressBarDialog.metaverseServerUrl + targetString; card.parentStackItem = root; - card.eventBridge = root.eventBridge; root.push(card); return; } diff --git a/interface/resources/qml/hifi/tablet/TabletAttachmentsDialog.qml b/interface/resources/qml/hifi/tablet/TabletAttachmentsDialog.qml index 634c9d41ec..19548365aa 100644 --- a/interface/resources/qml/hifi/tablet/TabletAttachmentsDialog.qml +++ b/interface/resources/qml/hifi/tablet/TabletAttachmentsDialog.qml @@ -25,7 +25,6 @@ Item { property bool keyboardRaised: false property bool punctuationMode: false - property var eventBridge; signal sendToScript(var message); anchors.fill: parent diff --git a/interface/resources/qml/hifi/tablet/TabletAudioPreferences.qml b/interface/resources/qml/hifi/tablet/TabletAudioPreferences.qml index 2046071e4c..b30e741be9 100644 --- a/interface/resources/qml/hifi/tablet/TabletAudioPreferences.qml +++ b/interface/resources/qml/hifi/tablet/TabletAudioPreferences.qml @@ -19,7 +19,6 @@ StackView { objectName: "stack" property string title: "Audio Settings" - property var eventBridge; signal sendToScript(var message); function pushSource(path) { diff --git a/interface/resources/qml/hifi/tablet/TabletAvatarPreferences.qml b/interface/resources/qml/hifi/tablet/TabletAvatarPreferences.qml index 75973f32ae..94fb29c6a1 100644 --- a/interface/resources/qml/hifi/tablet/TabletAvatarPreferences.qml +++ b/interface/resources/qml/hifi/tablet/TabletAvatarPreferences.qml @@ -19,7 +19,6 @@ StackView { objectName: "stack" property string title: "Avatar Settings" - property var eventBridge; signal sendToScript(var message); function pushSource(path) { diff --git a/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml b/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml index 17d3f1b959..fe043f6ac7 100644 --- a/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml +++ b/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml @@ -19,7 +19,6 @@ StackView { objectName: "stack" property string title: "General Settings" property alias gotoPreviousApp: root.gotoPreviousApp; - property var eventBridge; signal sendToScript(var message); function pushSource(path) { diff --git a/interface/resources/qml/hifi/tablet/TabletGraphicsPreferences.qml b/interface/resources/qml/hifi/tablet/TabletGraphicsPreferences.qml index 95ee2c3a72..25b5be05f2 100644 --- a/interface/resources/qml/hifi/tablet/TabletGraphicsPreferences.qml +++ b/interface/resources/qml/hifi/tablet/TabletGraphicsPreferences.qml @@ -19,7 +19,6 @@ StackView { objectName: "stack" property string title: "Graphics Settings" - property var eventBridge; signal sendToScript(var message); function pushSource(path) { diff --git a/interface/resources/qml/hifi/tablet/TabletLodPreferences.qml b/interface/resources/qml/hifi/tablet/TabletLodPreferences.qml index 6f38fee8b9..b502c26245 100644 --- a/interface/resources/qml/hifi/tablet/TabletLodPreferences.qml +++ b/interface/resources/qml/hifi/tablet/TabletLodPreferences.qml @@ -19,7 +19,6 @@ StackView { objectName: "stack" property string title: "LOD Settings" - property var eventBridge; signal sendToScript(var message); function pushSource(path) { diff --git a/interface/resources/qml/hifi/tablet/TabletMenu.qml b/interface/resources/qml/hifi/tablet/TabletMenu.qml index 62b61d129b..457fe84c3a 100644 --- a/interface/resources/qml/hifi/tablet/TabletMenu.qml +++ b/interface/resources/qml/hifi/tablet/TabletMenu.qml @@ -21,7 +21,6 @@ FocusScope { property var point: Qt.point(50, 50); TabletMenuStack { id: menuPopperUpper } property string subMenu: "" - property var eventBridge; signal sendToScript(var message); Rectangle { diff --git a/interface/resources/qml/hifi/tablet/TabletMenuStack.qml b/interface/resources/qml/hifi/tablet/TabletMenuStack.qml index bacc11228e..2fd33e9cbc 100644 --- a/interface/resources/qml/hifi/tablet/TabletMenuStack.qml +++ b/interface/resources/qml/hifi/tablet/TabletMenuStack.qml @@ -49,7 +49,6 @@ Item { function pushSource(path) { d.push(Qt.resolvedUrl(path)); - d.currentItem.eventBridge = tabletMenu.eventBridge d.currentItem.sendToScript.connect(tabletMenu.sendToScript); d.currentItem.focus = true; d.currentItem.forceActiveFocus(); diff --git a/interface/resources/qml/hifi/tablet/TabletNetworkingPreferences.qml b/interface/resources/qml/hifi/tablet/TabletNetworkingPreferences.qml index 7184d91044..91d6140fc3 100644 --- a/interface/resources/qml/hifi/tablet/TabletNetworkingPreferences.qml +++ b/interface/resources/qml/hifi/tablet/TabletNetworkingPreferences.qml @@ -19,7 +19,6 @@ StackView { objectName: "stack" property var title: "Networking Settings" - property var eventBridge; signal sendToScript(var message); function pushSource(path) { diff --git a/interface/resources/qml/hifi/tablet/TabletRoot.qml b/interface/resources/qml/hifi/tablet/TabletRoot.qml index e7654d9ff1..b6cdce0853 100644 --- a/interface/resources/qml/hifi/tablet/TabletRoot.qml +++ b/interface/resources/qml/hifi/tablet/TabletRoot.qml @@ -8,7 +8,6 @@ Item { id: tabletRoot objectName: "tabletRoot" property string username: "Unknown user" - property var eventBridge; property var rootMenu; property var openModal: null; property var openMessage: null; @@ -111,7 +110,6 @@ Item { function openBrowserWindow(request, profile) { var component = Qt.createComponent("../../controls/TabletWebView.qml"); var newWindow = component.createObject(tabletRoot); - newWindow.eventBridge = tabletRoot.eventBridge; newWindow.remove = true; newWindow.profile = profile; request.openIn(newWindow.webView); @@ -175,7 +173,7 @@ Item { // Hook up callback for clara.io download from the marketplace. Connections { id: eventBridgeConnection - target: null + target: eventBridge onWebEventReceived: { if (message.slice(0, 17) === "CLARA.IO DOWNLOAD") { ApplicationInterface.addAssetToWorldFromURL(message.slice(18)); @@ -184,10 +182,6 @@ Item { } onLoaded: { - if (loader.item.hasOwnProperty("eventBridge")) { - loader.item.eventBridge = eventBridge; - eventBridgeConnection.target = eventBridge - } if (loader.item.hasOwnProperty("sendToScript")) { loader.item.sendToScript.connect(tabletRoot.sendToScript); } diff --git a/interface/resources/qml/hifi/tablet/WindowRoot.qml b/interface/resources/qml/hifi/tablet/WindowRoot.qml index ee8dbbff59..12f302d60a 100644 --- a/interface/resources/qml/hifi/tablet/WindowRoot.qml +++ b/interface/resources/qml/hifi/tablet/WindowRoot.qml @@ -18,7 +18,6 @@ Windows.ScrollingWindow { id: tabletRoot objectName: "tabletRoot" property string username: "Unknown user" - property var eventBridge; property var rootMenu; property string subMenu: "" @@ -93,7 +92,7 @@ Windows.ScrollingWindow { // Hook up callback for clara.io download from the marketplace. Connections { id: eventBridgeConnection - target: null + target: eventBridge onWebEventReceived: { if (message.slice(0, 17) === "CLARA.IO DOWNLOAD") { ApplicationInterface.addAssetToWorldFromURL(message.slice(18)); @@ -102,10 +101,6 @@ Windows.ScrollingWindow { } onLoaded: { - if (loader.item.hasOwnProperty("eventBridge")) { - loader.item.eventBridge = eventBridge; - eventBridgeConnection.target = eventBridge - } if (loader.item.hasOwnProperty("sendToScript")) { loader.item.sendToScript.connect(tabletRoot.sendToScript); } diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletAvatarBrowser.qml b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletAvatarBrowser.qml index 029cf7d46b..cab76bf818 100644 --- a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletAvatarBrowser.qml +++ b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletAvatarBrowser.qml @@ -27,8 +27,6 @@ Item { property bool keyboardRaised: false property bool punctuationMode: false - property alias eventBridge: eventBridgeWrapper.eventBridge - anchors.fill: parent BaseWebView { @@ -43,14 +41,6 @@ Item { bottom: footer.top } - QtObject { - id: eventBridgeWrapper - WebChannel.id: "eventBridgeWrapper" - property var eventBridge; - } - - webChannel.registeredObjects: [eventBridgeWrapper] - // Create a global EventBridge object for raiseAndLowerKeyboard. WebEngineScript { id: createGlobalEventBridge @@ -68,6 +58,11 @@ Item { } userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ] + + Component.onCompleted: { + webChannel.registerObject("eventBridge", eventBridge); + webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper); + } } Rectangle { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index cd8b42af1a..ee1292f4bf 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -114,6 +114,7 @@ #include #include #include +#include #include #include #include @@ -1866,27 +1867,11 @@ void Application::initializeGL() { qCDebug(interfaceapp, "Initialized Display."); // Set up the render engine - render::CullFunctor cullFunctor = LODManager::shouldRender; - static const QString RENDER_FORWARD = "HIFI_RENDER_FORWARD"; - bool isDeferred = true; - if (QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD)) { - isDeferred = false; - } - - _renderEngine->addJob("SecondaryCameraFrame", cullFunctor); - _renderEngine->addJob("MainFrame", cullFunctor, isDeferred); - - -/* _renderEngine->addJob("RenderShadowTask", cullFunctor); - const auto items = _renderEngine->addJob("FetchCullSort", cullFunctor); - assert(items.canCast()); + render::CullFunctor cullFunctor = LODManager::shouldRender; static const QString RENDER_FORWARD = "HIFI_RENDER_FORWARD"; - if (QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD)) { - _renderEngine->addJob("Forward", items); - } else { - _renderEngine->addJob("RenderDeferredTask", items); - } - */ + bool isDeferred = !QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD); + _renderEngine->addJob("SecondaryCameraFrame", cullFunctor); + _renderEngine->addJob("RenderMainView", cullFunctor, isDeferred); _renderEngine->load(); _renderEngine->registerScene(_main3DScene); @@ -4138,10 +4123,10 @@ void Application::updateMyAvatarLookAtPosition() { } } else { // I am not looking at anyone else, so just look forward - if (isHMD) { - glm::mat4 worldHeadMat = myAvatar->getSensorToWorldMatrix() * - myAvatar->getHeadControllerPoseInSensorFrame().getMatrix(); - lookAtSpot = transformPoint(worldHeadMat, glm::vec3(0.0f, 0.0f, -TREE_SCALE)); + auto headPose = myAvatar->getHeadControllerPoseInSensorFrame(); + if (headPose.isValid()) { + glm::mat4 worldHeadMat = myAvatar->getSensorToWorldMatrix() * headPose.getMatrix(); + lookAtSpot = transformPoint(worldHeadMat, glm::vec3(0.0f, 0.0f, TREE_SCALE)); } else { lookAtSpot = myAvatar->getHead()->getEyePosition() + (myAvatar->getHead()->getFinalOrientationInWorldFrame() * glm::vec3(0.0f, 0.0f, -TREE_SCALE)); @@ -5088,7 +5073,7 @@ namespace render { template <> const ItemKey payloadGetKey(const WorldBoxRenderData::Pointer& stuff) { return ItemKey::Builder::opaqueShape(); } template <> const Item::Bound payloadGetBound(const WorldBoxRenderData::Pointer& stuff) { return Item::Bound(); } template <> void payloadRender(const WorldBoxRenderData::Pointer& stuff, RenderArgs* args) { - if (args->_renderMode != RenderArgs::MIRROR_RENDER_MODE && Menu::getInstance()->isOptionChecked(MenuOption::WorldAxes)) { + if (Menu::getInstance()->isOptionChecked(MenuOption::WorldAxes)) { PerformanceTimer perfTimer("worldBox"); auto& batch = *args->_batch; diff --git a/interface/src/AvatarBookmarks.cpp b/interface/src/AvatarBookmarks.cpp index 5cdfc8213f..db2a240b92 100644 --- a/interface/src/AvatarBookmarks.cpp +++ b/interface/src/AvatarBookmarks.cpp @@ -21,15 +21,35 @@ #include "MainWindow.h" #include "Menu.h" - #include "AvatarBookmarks.h" +#include "InterfaceLogging.h" + #include AvatarBookmarks::AvatarBookmarks() { - _bookmarksFilename = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + AVATARBOOKMARKS_FILENAME; + _bookmarksFilename = PathUtils::getAppDataPath() + "/" + AVATARBOOKMARKS_FILENAME; readFromFile(); } +void AvatarBookmarks::readFromFile() { + // migrate old avatarbookmarks.json, used to be in 'local' folder on windows + QString oldConfigPath = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + AVATARBOOKMARKS_FILENAME; + QFile oldConfig(oldConfigPath); + + // I imagine that in a year from now, this code for migrating (as well as the two lines above) + // may be removed since all bookmarks should have been migrated by then + // - Robbie Uvanni (6.8.2017) + if (oldConfig.exists()) { + if (QDir().rename(oldConfigPath, _bookmarksFilename)) { + qCDebug(interfaceapp) << "Successfully migrated" << AVATARBOOKMARKS_FILENAME; + } else { + qCDebug(interfaceapp) << "Failed to migrate" << AVATARBOOKMARKS_FILENAME; + } + } + + Bookmarks::readFromFile(); +} + void AvatarBookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) { // Add menus/actions auto bookmarkAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::BookmarkAvatar); diff --git a/interface/src/AvatarBookmarks.h b/interface/src/AvatarBookmarks.h index 725af88b0d..dc5a0aee6e 100644 --- a/interface/src/AvatarBookmarks.h +++ b/interface/src/AvatarBookmarks.h @@ -29,6 +29,7 @@ public slots: protected: void addBookmarkToMenu(Menu* menubar, const QString& name, const QString& address) override; + void readFromFile(); private: const QString AVATARBOOKMARKS_FILENAME = "avatarbookmarks.json"; diff --git a/interface/src/CrashHandler.cpp b/interface/src/CrashHandler.cpp index 19e887eb6e..f7d71de3fe 100644 --- a/interface/src/CrashHandler.cpp +++ b/interface/src/CrashHandler.cpp @@ -73,7 +73,7 @@ CrashHandler::Action CrashHandler::promptUserForAction(bool showCrashMessage) { layout->addWidget(label); QRadioButton* option1 = new QRadioButton("Reset all my settings"); - QRadioButton* option2 = new QRadioButton("Reset my settings but retain avatar info."); + QRadioButton* option2 = new QRadioButton("Reset my settings but keep essential info"); QRadioButton* option3 = new QRadioButton("Continue with my current settings"); option3->setChecked(true); layout->addWidget(option1); @@ -95,7 +95,7 @@ CrashHandler::Action CrashHandler::promptUserForAction(bool showCrashMessage) { return CrashHandler::DELETE_INTERFACE_INI; } if (option2->isChecked()) { - return CrashHandler::RETAIN_AVATAR_INFO; + return CrashHandler::RETAIN_IMPORTANT_INFO; } } @@ -104,7 +104,7 @@ CrashHandler::Action CrashHandler::promptUserForAction(bool showCrashMessage) { } void CrashHandler::handleCrash(CrashHandler::Action action) { - if (action != CrashHandler::DELETE_INTERFACE_INI && action != CrashHandler::RETAIN_AVATAR_INFO) { + if (action != CrashHandler::DELETE_INTERFACE_INI && action != CrashHandler::RETAIN_IMPORTANT_INFO) { // CrashHandler::DO_NOTHING or unexpected value return; } @@ -116,12 +116,15 @@ void CrashHandler::handleCrash(CrashHandler::Action action) { const QString DISPLAY_NAME_KEY = "displayName"; const QString FULL_AVATAR_URL_KEY = "fullAvatarURL"; const QString FULL_AVATAR_MODEL_NAME_KEY = "fullAvatarModelName"; + const QString TUTORIAL_COMPLETE_FLAG_KEY = "tutorialComplete"; + QString displayName; QUrl fullAvatarURL; QString fullAvatarModelName; QUrl address; + bool tutorialComplete = false; - if (action == CrashHandler::RETAIN_AVATAR_INFO) { + if (action == CrashHandler::RETAIN_IMPORTANT_INFO) { // Read avatar info // Location and orientation @@ -135,6 +138,9 @@ void CrashHandler::handleCrash(CrashHandler::Action action) { fullAvatarURL = settings.value(FULL_AVATAR_URL_KEY).toUrl(); fullAvatarModelName = settings.value(FULL_AVATAR_MODEL_NAME_KEY).toString(); settings.endGroup(); + + // Tutorial complete + tutorialComplete = settings.value(TUTORIAL_COMPLETE_FLAG_KEY).toBool(); } // Delete Interface.ini @@ -143,7 +149,7 @@ void CrashHandler::handleCrash(CrashHandler::Action action) { settingsFile.remove(); } - if (action == CrashHandler::RETAIN_AVATAR_INFO) { + if (action == CrashHandler::RETAIN_IMPORTANT_INFO) { // Write avatar info // Location and orientation @@ -157,6 +163,9 @@ void CrashHandler::handleCrash(CrashHandler::Action action) { settings.setValue(FULL_AVATAR_URL_KEY, fullAvatarURL); settings.setValue(FULL_AVATAR_MODEL_NAME_KEY, fullAvatarModelName); settings.endGroup(); + + // Tutorial complete + settings.setValue(TUTORIAL_COMPLETE_FLAG_KEY, tutorialComplete); } } diff --git a/interface/src/CrashHandler.h b/interface/src/CrashHandler.h index da2e1575db..bff8bba6dd 100644 --- a/interface/src/CrashHandler.h +++ b/interface/src/CrashHandler.h @@ -22,7 +22,7 @@ public: private: enum Action { DELETE_INTERFACE_INI, - RETAIN_AVATAR_INFO, + RETAIN_IMPORTANT_INFO, DO_NOTHING }; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index bc621543e3..24a25f314d 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -794,6 +794,77 @@ controller::Pose MyAvatar::getRightHandTipPose() const { return pose; } +glm::vec3 MyAvatar::worldToJointPoint(const glm::vec3& position, const int jointIndex) const { + glm::vec3 jointPos = getPosition();//default value if no or invalid joint specified + glm::quat jointRot = getRotation();//default value if no or invalid joint specified + if (jointIndex != -1) { + if (_skeletonModel->getJointPositionInWorldFrame(jointIndex, jointPos)) { + _skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot); + } else { + qWarning() << "Invalid joint index specified: " << jointIndex; + } + } + glm::vec3 modelOffset = position - jointPos; + glm::vec3 jointSpacePosition = glm::inverse(jointRot) * modelOffset; + + return jointSpacePosition; +} + +glm::vec3 MyAvatar::worldToJointDirection(const glm::vec3& worldDir, const int jointIndex) const { + glm::quat jointRot = getRotation();//default value if no or invalid joint specified + if ((jointIndex != -1) && (!_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot))) { + qWarning() << "Invalid joint index specified: " << jointIndex; + } + + glm::vec3 jointSpaceDir = glm::inverse(jointRot) * worldDir; + return jointSpaceDir; +} + +glm::quat MyAvatar::worldToJointRotation(const glm::quat& worldRot, const int jointIndex) const { + glm::quat jointRot = getRotation();//default value if no or invalid joint specified + if ((jointIndex != -1) && (!_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot))) { + qWarning() << "Invalid joint index specified: " << jointIndex; + } + glm::quat jointSpaceRot = glm::inverse(jointRot) * worldRot; + return jointSpaceRot; +} + +glm::vec3 MyAvatar::jointToWorldPoint(const glm::vec3& jointSpacePos, const int jointIndex) const { + glm::vec3 jointPos = getPosition();//default value if no or invalid joint specified + glm::quat jointRot = getRotation();//default value if no or invalid joint specified + + if (jointIndex != -1) { + if (_skeletonModel->getJointPositionInWorldFrame(jointIndex, jointPos)) { + _skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot); + } else { + qWarning() << "Invalid joint index specified: " << jointIndex; + } + } + + glm::vec3 worldOffset = jointRot * jointSpacePos; + glm::vec3 worldPos = jointPos + worldOffset; + + return worldPos; +} + +glm::vec3 MyAvatar::jointToWorldDirection(const glm::vec3& jointSpaceDir, const int jointIndex) const { + glm::quat jointRot = getRotation();//default value if no or invalid joint specified + if ((jointIndex != -1) && (!_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot))) { + qWarning() << "Invalid joint index specified: " << jointIndex; + } + glm::vec3 worldDir = jointRot * jointSpaceDir; + return worldDir; +} + +glm::quat MyAvatar::jointToWorldRotation(const glm::quat& jointSpaceRot, const int jointIndex) const { + glm::quat jointRot = getRotation();//default value if no or invalid joint specified + if ((jointIndex != -1) && (!_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot))) { + qWarning() << "Invalid joint index specified: " << jointIndex; + } + glm::quat worldRot = jointRot * jointSpaceRot; + return worldRot; +} + // virtual void MyAvatar::render(RenderArgs* renderArgs) { // don't render if we've been asked to disable local rendering @@ -2664,8 +2735,8 @@ bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, co return (offset.y > CYLINDER_TOP) || (offset.y < CYLINDER_BOTTOM); } -void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix, bool hasDriveInput) { - _desiredBodyMatrix = desiredBodyMatrix; +void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, + const glm::mat4& currentBodyMatrix, bool hasDriveInput) { if (myAvatar.getHMDLeanRecenterEnabled()) { if (!isActive(Rotation) && shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix)) { @@ -2679,7 +2750,7 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat } } - glm::mat4 desiredWorldMatrix = myAvatar.getSensorToWorldMatrix() * _desiredBodyMatrix; + glm::mat4 desiredWorldMatrix = myAvatar.getSensorToWorldMatrix() * desiredBodyMatrix; glm::mat4 currentWorldMatrix = myAvatar.getSensorToWorldMatrix() * currentBodyMatrix; AnimPose followWorldPose(currentWorldMatrix); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 3e2581382d..cfe66eb10e 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -378,6 +378,15 @@ public: Q_INVOKABLE controller::Pose getLeftHandTipPose() const; Q_INVOKABLE controller::Pose getRightHandTipPose() const; + // world-space to avatar-space rigconversion functions + Q_INVOKABLE glm::vec3 worldToJointPoint(const glm::vec3& position, const int jointIndex = -1) const; + Q_INVOKABLE glm::vec3 worldToJointDirection(const glm::vec3& direction, const int jointIndex = -1) const; + Q_INVOKABLE glm::quat worldToJointRotation(const glm::quat& rotation, const int jointIndex = -1) const; + + Q_INVOKABLE glm::vec3 jointToWorldPoint(const glm::vec3& position, const int jointIndex = -1) const; + Q_INVOKABLE glm::vec3 jointToWorldDirection(const glm::vec3& direction, const int jointIndex = -1) const; + Q_INVOKABLE glm::quat jointToWorldRotation(const glm::quat& rotation, const int jointIndex = -1) const; + AvatarWeakPointer getLookAtTargetAvatar() const { return _lookAtTargetAvatar; } void updateLookAtTargetAvatar(); void clearLookAtTargetAvatar(); @@ -702,7 +711,6 @@ private: Vertical, NumFollowTypes }; - glm::mat4 _desiredBodyMatrix; float _timeRemaining[NumFollowTypes]; void deactivate(); diff --git a/interface/src/scripting/AudioDeviceScriptingInterface.cpp b/interface/src/scripting/AudioDeviceScriptingInterface.cpp index 05168b0d4c..d22f948344 100644 --- a/interface/src/scripting/AudioDeviceScriptingInterface.cpp +++ b/interface/src/scripting/AudioDeviceScriptingInterface.cpp @@ -9,7 +9,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "AudioClient.h" +#include +#include + #include "AudioDeviceScriptingInterface.h" #include "SettingsScriptingInterface.h" @@ -44,17 +46,23 @@ AudioDeviceScriptingInterface::AudioDeviceScriptingInterface(): QAbstractListMod onDeviceChanged(); //set up previously saved device SettingsScriptingInterface* settings = SettingsScriptingInterface::getInstance(); - const QString inDevice = settings->getValue("audio_input_device").toString(); + const QString inDevice = settings->getValue("audio_input_device", _currentInputDevice).toString(); if (inDevice != _currentInputDevice) { + qCDebug(audioclient) << __FUNCTION__ << "about to call setInputDeviceAsync() device: [" << inDevice << "] _currentInputDevice:" << _currentInputDevice; setInputDeviceAsync(inDevice); } - const QString outDevice = settings->getValue("audio_output_device").toString(); + + // If the audio_output_device setting is not available, use the _currentOutputDevice + auto outDevice = settings->getValue("audio_output_device", _currentOutputDevice).toString(); if (outDevice != _currentOutputDevice) { + qCDebug(audioclient) << __FUNCTION__ << "about to call setOutputDeviceAsync() outDevice: [" << outDevice << "] _currentOutputDevice:" << _currentOutputDevice; setOutputDeviceAsync(outDevice); } } bool AudioDeviceScriptingInterface::setInputDevice(const QString& deviceName) { + qCDebug(audioclient) << __FUNCTION__ << "deviceName:" << deviceName; + bool result; QMetaObject::invokeMethod(DependencyManager::get().data(), "switchInputToAudioDevice", Qt::BlockingQueuedConnection, @@ -64,6 +72,9 @@ bool AudioDeviceScriptingInterface::setInputDevice(const QString& deviceName) { } bool AudioDeviceScriptingInterface::setOutputDevice(const QString& deviceName) { + + qCDebug(audioclient) << __FUNCTION__ << "deviceName:" << deviceName; + bool result; QMetaObject::invokeMethod(DependencyManager::get().data(), "switchOutputToAudioDevice", Qt::BlockingQueuedConnection, @@ -86,8 +97,10 @@ bool AudioDeviceScriptingInterface::setDeviceFromMenu(const QString& deviceMenuN for (ScriptingAudioDeviceInfo di: _devices) { if (mode == di.mode && deviceMenuName.contains(di.name)) { if (mode == QAudio::AudioOutput) { + qCDebug(audioclient) << __FUNCTION__ << "about to call setOutputDeviceAsync() device: [" << di.name << "]"; setOutputDeviceAsync(di.name); } else { + qCDebug(audioclient) << __FUNCTION__ << "about to call setInputDeviceAsync() device: [" << di.name << "]"; setInputDeviceAsync(di.name); } return true; @@ -98,12 +111,26 @@ bool AudioDeviceScriptingInterface::setDeviceFromMenu(const QString& deviceMenuN } void AudioDeviceScriptingInterface::setInputDeviceAsync(const QString& deviceName) { + qCDebug(audioclient) << __FUNCTION__ << "deviceName:" << deviceName; + + if (deviceName.isEmpty()) { + qCDebug(audioclient) << __FUNCTION__ << "attempt to set empty deviceName:" << deviceName << "... ignoring!"; + return; + } + QMetaObject::invokeMethod(DependencyManager::get().data(), "switchInputToAudioDevice", Qt::QueuedConnection, Q_ARG(const QString&, deviceName)); } void AudioDeviceScriptingInterface::setOutputDeviceAsync(const QString& deviceName) { + qCDebug(audioclient) << __FUNCTION__ << "deviceName:" << deviceName; + + if (deviceName.isEmpty()) { + qCDebug(audioclient) << __FUNCTION__ << "attempt to set empty deviceName:" << deviceName << "... ignoring!"; + return; + } + QMetaObject::invokeMethod(DependencyManager::get().data(), "switchOutputToAudioDevice", Qt::QueuedConnection, Q_ARG(const QString&, deviceName)); @@ -241,8 +268,11 @@ void AudioDeviceScriptingInterface::onCurrentInputDeviceChanged(const QString& n void AudioDeviceScriptingInterface::onCurrentOutputDeviceChanged(const QString& name) { currentDeviceUpdate(name, QAudio::AudioOutput); + + // FIXME - this is kinda janky to set the setting on the result of a signal //we got a signal that device changed. Save it now SettingsScriptingInterface* settings = SettingsScriptingInterface::getInstance(); + qCDebug(audioclient) << __FUNCTION__ << "about to call settings->setValue('audio_output_device', name); name:" << name; settings->setValue("audio_output_device", name); emit currentOutputDeviceChanged(name); } diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 6caa4fb159..2f812724c5 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -166,7 +166,10 @@ AudioClient::AudioClient() : connect(&_receivedAudioStream, &MixedProcessedAudioStream::processSamples, this, &AudioClient::processReceivedSamples, Qt::DirectConnection); - connect(this, &AudioClient::changeDevice, this, [=](const QAudioDeviceInfo& outputDeviceInfo) { switchOutputToAudioDevice(outputDeviceInfo); }); + connect(this, &AudioClient::changeDevice, this, [=](const QAudioDeviceInfo& outputDeviceInfo) { + qCDebug(audioclient) << "got AudioClient::changeDevice signal, about to call switchOutputToAudioDevice() outputDeviceInfo: [" << outputDeviceInfo.deviceName() << "]"; + switchOutputToAudioDevice(outputDeviceInfo); + }); connect(&_receivedAudioStream, &InboundAudioStream::mismatchedAudioCodec, this, &AudioClient::handleMismatchAudioFormat); @@ -379,7 +382,8 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { CoUninitialize(); } - qCDebug(audioclient) << "[" << deviceName << "] [" << getNamedAudioDeviceForMode(mode, deviceName).deviceName() << "]"; + qCDebug(audioclient) << "defaultAudioDeviceForMode mode: " << (mode == QAudio::AudioOutput ? "Output" : "Input") + << " [" << deviceName << "] [" << getNamedAudioDeviceForMode(mode, deviceName).deviceName() << "]"; return getNamedAudioDeviceForMode(mode, deviceName); #endif @@ -555,8 +559,12 @@ void AudioClient::start() { } void AudioClient::stop() { + // "switch" to invalid devices in order to shut down the state + qCDebug(audioclient) << "AudioClient::stop(), about to call switchInputToAudioDevice(null)"; switchInputToAudioDevice(QAudioDeviceInfo()); + + qCDebug(audioclient) << "AudioClient::stop(), about to call switchOutputToAudioDevice(null)"; switchOutputToAudioDevice(QAudioDeviceInfo()); } @@ -748,12 +756,12 @@ QVector AudioClient::getDeviceNames(QAudio::Mode mode) { } bool AudioClient::switchInputToAudioDevice(const QString& inputDeviceName) { - qCDebug(audioclient) << "[" << inputDeviceName << "] [" << getNamedAudioDeviceForMode(QAudio::AudioInput, inputDeviceName).deviceName() << "]"; + qCDebug(audioclient) << "switchInputToAudioDevice [" << inputDeviceName << "] [" << getNamedAudioDeviceForMode(QAudio::AudioInput, inputDeviceName).deviceName() << "]"; return switchInputToAudioDevice(getNamedAudioDeviceForMode(QAudio::AudioInput, inputDeviceName)); } bool AudioClient::switchOutputToAudioDevice(const QString& outputDeviceName) { - qCDebug(audioclient) << "[" << outputDeviceName << "] [" << getNamedAudioDeviceForMode(QAudio::AudioOutput, outputDeviceName).deviceName() << "]"; + qCDebug(audioclient) << "switchOutputToAudioDevice [" << outputDeviceName << "] [" << getNamedAudioDeviceForMode(QAudio::AudioOutput, outputDeviceName).deviceName() << "]"; return switchOutputToAudioDevice(getNamedAudioDeviceForMode(QAudio::AudioOutput, outputDeviceName)); } @@ -1298,6 +1306,7 @@ void AudioClient::setIsStereoInput(bool isStereoInput) { } // change in channel count for desired input format, restart the input device + qCDebug(audioclient) << __FUNCTION__ << "about to call switchInputToAudioDevice:" << _inputAudioDeviceName; switchInputToAudioDevice(_inputAudioDeviceName); } } @@ -1334,6 +1343,7 @@ void AudioClient::outputFormatChanged() { } bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceInfo) { + qCDebug(audioclient) << __FUNCTION__ << "inputDeviceInfo: [" << inputDeviceInfo.deviceName() << "]"; bool supportedFormat = false; // cleanup any previously initialized device @@ -1448,6 +1458,8 @@ void AudioClient::outputNotify() { } bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo) { + qCDebug(audioclient) << "AudioClient::switchOutputToAudioDevice() outputDeviceInfo: [" << outputDeviceInfo.deviceName() << "]"; + bool supportedFormat = false; Lock localAudioLock(_localAudioMutex); @@ -1582,7 +1594,11 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice } int AudioClient::setOutputBufferSize(int numFrames, bool persist) { + qCDebug(audioclient) << __FUNCTION__ << "numFrames:" << numFrames << "persist:" << persist; + numFrames = std::min(std::max(numFrames, MIN_BUFFER_FRAMES), MAX_BUFFER_FRAMES); + qCDebug(audioclient) << __FUNCTION__ << "clamped numFrames:" << numFrames << "_sessionOutputBufferSizeFrames:" << _sessionOutputBufferSizeFrames; + if (numFrames != _sessionOutputBufferSizeFrames) { qCInfo(audioclient, "Audio output buffer set to %d frames", numFrames); _sessionOutputBufferSizeFrames = numFrames; @@ -1594,6 +1610,7 @@ int AudioClient::setOutputBufferSize(int numFrames, bool persist) { // The buffer size can't be adjusted after QAudioOutput::start() has been called, so // recreate the device by switching to the default. QAudioDeviceInfo outputDeviceInfo = defaultAudioDeviceForMode(QAudio::AudioOutput); + qCDebug(audioclient) << __FUNCTION__ << "about to send changeDevice signal outputDeviceInfo: [" << outputDeviceInfo.deviceName() << "]"; emit changeDevice(outputDeviceInfo); // On correct thread, please, as setOutputBufferSize can be called from main thread. } } diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index b3797853f0..ea91890f33 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -230,6 +230,12 @@ void HmdDisplayPlugin::internalPresent() { float shiftLeftBy = getLeftCenterPixel() - (sourceSize.x / 2); float newWidth = sourceSize.x - shiftLeftBy; + // Experimentally adjusted the region presented in preview to avoid seeing the masked pixels and recenter the center... + static float SCALE_WIDTH = 0.9f; + static float SCALE_OFFSET = 2.0f; + newWidth *= SCALE_WIDTH; + shiftLeftBy *= SCALE_OFFSET; + const unsigned int RATIO_Y = 9; const unsigned int RATIO_X = 16; glm::uvec2 originalClippedSize { newWidth, newWidth * RATIO_Y / RATIO_X }; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 236daf6443..6d4f586c52 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -460,17 +460,11 @@ FBXLight extractLight(const FBXNode& object) { } QByteArray fileOnUrl(const QByteArray& filepath, const QString& url) { - QString path = QFileInfo(url).path(); - QByteArray filename = filepath; - QFileInfo checkFile(path + "/" + filepath); + // in order to match the behaviour when loading models from remote URLs + // we assume that all external textures are right beside the loaded model + // ignoring any relative paths or absolute paths inside of models - // check if the file exists at the RelativeFilename - if (!(checkFile.exists() && checkFile.isFile())) { - // if not, assume it is in the fbx directory - filename = filename.mid(filename.lastIndexOf('/') + 1); - } - - return filename; + return filepath.mid(filepath.lastIndexOf('/') + 1); } FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QString& url) { diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index 65c311424f..9dcc1d7991 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -277,6 +277,23 @@ QString getEventBridgeJavascript() { return javaScriptToInject; } +class EventBridgeWrapper : public QObject { + Q_OBJECT + Q_PROPERTY(QObject* eventBridge READ getEventBridge CONSTANT); + +public: + EventBridgeWrapper(QObject* eventBridge, QObject* parent = nullptr) : QObject(parent), _eventBridge(eventBridge) { + } + + QObject* getEventBridge() { + return _eventBridge; + } + +private: + QObject* _eventBridge; +}; + + QQmlEngine* acquireEngine(QQuickWindow* window) { Q_ASSERT(QThread::currentThread() == qApp->thread()); @@ -430,7 +447,6 @@ OffscreenQmlSurface::~OffscreenQmlSurface() { _canvas->deleteLater(); _rootItem->deleteLater(); - _qmlComponent->deleteLater(); _quickWindow->deleteLater(); releaseEngine(); } @@ -473,11 +489,12 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) { _qmlContext = new QQmlContext(qmlEngine->rootContext()); _qmlContext->setContextProperty("offscreenWindow", QVariant::fromValue(getWindow())); - _qmlContext->setContextProperty("globalEventBridge", this); + _qmlContext->setContextProperty("eventBridge", this); _qmlContext->setContextProperty("webEntity", this); - _qmlComponent = new QQmlComponent(qmlEngine); - + // FIXME Compatibility mechanism for existing HTML and JS that uses eventBridgeWrapper + // Find a way to flag older scripts using this mechanism and wanr that this is deprecated + _qmlContext->setContextProperty("eventBridgeWrapper", new EventBridgeWrapper(this, _qmlContext)); if (!_canvas->makeCurrent()) { qWarning("Failed to make context current for QML Renderer"); @@ -577,71 +594,79 @@ void OffscreenQmlSurface::setBaseUrl(const QUrl& baseUrl) { _qmlContext->setBaseUrl(baseUrl); } -QObject* OffscreenQmlSurface::load(const QUrl& qmlSource, std::function f) { +QObject* OffscreenQmlSurface::load(const QUrl& qmlSource, bool createNewContext, std::function f) { // Synchronous loading may take a while; restart the deadlock timer QMetaObject::invokeMethod(qApp, "updateHeartbeat", Qt::DirectConnection); - if ((qmlSource.isRelative() && !qmlSource.isEmpty()) || qmlSource.scheme() == QLatin1String("file")) { - _qmlComponent->loadUrl(_qmlContext->resolvedUrl(qmlSource), QQmlComponent::PreferSynchronous); - } else { - _qmlComponent->loadUrl(qmlSource, QQmlComponent::PreferSynchronous); + QQmlContext* targetContext = _qmlContext; + if (_rootItem && createNewContext) { + targetContext = new QQmlContext(targetContext); } + QUrl finalQmlSource = qmlSource; + if ((qmlSource.isRelative() && !qmlSource.isEmpty()) || qmlSource.scheme() == QLatin1String("file")) { + finalQmlSource = _qmlContext->resolvedUrl(qmlSource); + } - if (_qmlComponent->isLoading()) { - connect(_qmlComponent, &QQmlComponent::statusChanged, this, - [this, f](QQmlComponent::Status){ - finishQmlLoad(f); - }); + auto qmlComponent = new QQmlComponent(_qmlContext->engine(), finalQmlSource, QQmlComponent::PreferSynchronous); + if (qmlComponent->isLoading()) { + connect(qmlComponent, &QQmlComponent::statusChanged, this, + [this, qmlComponent, targetContext, f](QQmlComponent::Status) { + finishQmlLoad(qmlComponent, targetContext, f); + }); return nullptr; } - return finishQmlLoad(f); + return finishQmlLoad(qmlComponent, targetContext, f); +} + +QObject* OffscreenQmlSurface::loadInNewContext(const QUrl& qmlSource, std::function f) { + return load(qmlSource, true, f); +} + +QObject* OffscreenQmlSurface::load(const QUrl& qmlSource, std::function f) { + return load(qmlSource, false, f); } void OffscreenQmlSurface::clearCache() { _qmlContext->engine()->clearComponentCache(); } -QObject* OffscreenQmlSurface::finishQmlLoad(std::function f) { -#if 0 - if (!_rootItem) { - QQmlComponent component(_qmlContext->engine()); - component.setData(R"QML( -import QtQuick 2.0 -import QtWebChannel 1.0 -Item { Component.onCompleted: globalEventBridge.WebChannel.id = "globalEventBridge"; } -)QML", QUrl()); - QObject *helper = component.create(_qmlContext); - qDebug() << "Created helper"; - } -#endif - disconnect(_qmlComponent, &QQmlComponent::statusChanged, this, 0); - if (_qmlComponent->isError()) { - QList errorList = _qmlComponent->errors(); - foreach(const QQmlError& error, errorList) { - qWarning() << error.url() << error.line() << error; +QObject* OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, std::function f) { + disconnect(qmlComponent, &QQmlComponent::statusChanged, this, 0); + if (qmlComponent->isError()) { + for (const auto& error : qmlComponent->errors()) { + qCWarning(glLogging) << error.url() << error.line() << error; } + qmlComponent->deleteLater(); return nullptr; } - QObject* newObject = _qmlComponent->beginCreate(_qmlContext); - if (_qmlComponent->isError()) { - QList errorList = _qmlComponent->errors(); - foreach(const QQmlError& error, errorList) + QObject* newObject = qmlComponent->beginCreate(qmlContext); + if (qmlComponent->isError()) { + for (const auto& error : qmlComponent->errors()) { qCWarning(glLogging) << error.url() << error.line() << error; + } if (!_rootItem) { qFatal("Unable to finish loading QML root"); } + qmlComponent->deleteLater(); return nullptr; } - _qmlContext->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership); - newObject->setProperty("eventBridge", QVariant::fromValue(this)); + qmlContext->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership); + f(qmlContext, newObject); - f(_qmlContext, newObject); - _qmlComponent->completeCreate(); + QObject* eventBridge = qmlContext->contextProperty("eventBridge").value(); + if (qmlContext != _qmlContext && eventBridge && eventBridge != this) { + // FIXME Compatibility mechanism for existing HTML and JS that uses eventBridgeWrapper + // Find a way to flag older scripts using this mechanism and wanr that this is deprecated + qmlContext->setContextProperty("eventBridgeWrapper", new EventBridgeWrapper(eventBridge, qmlContext)); + } + + qmlComponent->completeCreate(); + qmlComponent->deleteLater(); // All quick items should be focusable diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.h b/libraries/gl/src/gl/OffscreenQmlSurface.h index 2a078d2b4f..ae81ae48b4 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.h +++ b/libraries/gl/src/gl/OffscreenQmlSurface.h @@ -48,6 +48,8 @@ public: void resize(const QSize& size, bool forceResize = false); QSize size() const; + Q_INVOKABLE QObject* load(const QUrl& qmlSource, bool createNewContext, std::function f = [](QQmlContext*, QObject*) {}); + Q_INVOKABLE QObject* loadInNewContext(const QUrl& qmlSource, std::function f = [](QQmlContext*, QObject*) {}); Q_INVOKABLE QObject* load(const QUrl& qmlSource, std::function f = [](QQmlContext*, QObject*) {}); Q_INVOKABLE QObject* load(const QString& qmlSourceFile, std::function f = [](QQmlContext*, QObject*) {}) { return load(QUrl(qmlSourceFile), f); @@ -118,7 +120,7 @@ protected: void setFocusText(bool newFocusText); private: - QObject* finishQmlLoad(std::function f); + QObject* finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, std::function f); QPointF mapWindowToUi(const QPointF& sourcePosition, QObject* sourceObject); void setupFbo(); bool allowNewFrame(uint8_t fps); @@ -134,7 +136,6 @@ private: QQuickWindow* _quickWindow { nullptr }; QMyQuickRenderControl* _renderControl{ nullptr }; QQmlContext* _qmlContext { nullptr }; - QQmlComponent* _qmlComponent { nullptr }; QQuickItem* _rootItem { nullptr }; OffscreenGLCanvas* _canvas { nullptr }; diff --git a/libraries/networking/src/UserActivityLogger.cpp b/libraries/networking/src/UserActivityLogger.cpp index 28117c0933..0cfd1e09e7 100644 --- a/libraries/networking/src/UserActivityLogger.cpp +++ b/libraries/networking/src/UserActivityLogger.cpp @@ -20,6 +20,10 @@ #include #include "AddressManager.h" +UserActivityLogger::UserActivityLogger() { + _timer.start(); +} + UserActivityLogger& UserActivityLogger::getInstance() { static UserActivityLogger sharedInstance; return sharedInstance; @@ -42,6 +46,12 @@ void UserActivityLogger::logAction(QString action, QJsonObject details, JSONCall actionPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"action_name\""); actionPart.setBody(QByteArray().append(action)); multipart->append(actionPart); + + // Log the local-time that this event was logged + QHttpPart elapsedPart; + elapsedPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"elapsed_ms\""); + elapsedPart.setBody(QString::number(_timer.elapsed()).toLocal8Bit()); + multipart->append(elapsedPart); // If there are action details, add them to the multipart if (!details.isEmpty()) { diff --git a/libraries/networking/src/UserActivityLogger.h b/libraries/networking/src/UserActivityLogger.h index 9fad498b86..179e8e6e66 100644 --- a/libraries/networking/src/UserActivityLogger.h +++ b/libraries/networking/src/UserActivityLogger.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include "AddressManager.h" @@ -51,8 +52,10 @@ private slots: void requestError(QNetworkReply& errorReply); private: - UserActivityLogger() {}; + UserActivityLogger(); Setting::Handle _disabled { "UserActivityLoggerDisabled", false }; + + QElapsedTimer _timer; }; #endif // hifi_UserActivityLogger_h diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 9d970fa318..d59da2f726 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -56,7 +56,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::AvatarData: case PacketType::BulkAvatarData: case PacketType::KillAvatar: - return static_cast(AvatarMixerPacketVersion::AvatarIdentitySequenceId); + return static_cast(AvatarMixerPacketVersion::MannequinDefaultAvatar); case PacketType::MessagesData: return static_cast(MessageDataVersion::TextOrBinaryData); case PacketType::ICEServerHeartbeat: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 2cc3a2c42e..fa1151e0a6 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -236,7 +236,8 @@ enum class AvatarMixerPacketVersion : PacketVersion { AvatarAsChildFixes, StickAndBallDefaultAvatar, IdentityPacketsIncludeUpdateTime, - AvatarIdentitySequenceId + AvatarIdentitySequenceId, + MannequinDefaultAvatar }; enum class DomainConnectRequestVersion : PacketVersion { diff --git a/libraries/render-utils/src/AntialiasingEffect.cpp b/libraries/render-utils/src/AntialiasingEffect.cpp index 139f1ae091..f7881b0333 100644 --- a/libraries/render-utils/src/AntialiasingEffect.cpp +++ b/libraries/render-utils/src/AntialiasingEffect.cpp @@ -110,10 +110,6 @@ void Antialiasing::run(const render::RenderContextPointer& renderContext, const RenderArgs* args = renderContext->args; - if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { - return; - } - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); batch.setViewportTransform(args->_viewport); diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 36a9401d00..4b3ee9fec7 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -504,10 +504,7 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext, { // Framebuffer copy operations cannot function as multipass stereo operations. batch.enableStereo(false); - - // perform deferred lighting, rendering to free fbo - auto framebufferCache = DependencyManager::get(); - + auto textureCache = DependencyManager::get(); auto deferredLightingEffect = DependencyManager::get(); diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index cd87cfdb3d..dcf90012c1 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -69,7 +69,7 @@ std::vector polygon() { std::vector result; result.reserve(SIDES); double angleIncrement = 2.0 * M_PI / SIDES; - for (size_t i = 0; i < SIDES; ++i) { + for (size_t i = 0; i < SIDES; i++) { double angle = (double)i * angleIncrement; result.push_back(vec3{ cos(angle) * 0.5, 0.0, sin(angle) * 0.5 }); } @@ -172,20 +172,20 @@ void setupFlatShape(GeometryCache::ShapeData& shapeData, const geometry::Solid& face = shape.faces[f]; // Compute the face normal vec3 faceNormal = shape.getFaceNormal(f); // Create the vertices for the face - for (Index i = 0; i < N; ++i) { + for (Index i = 0; i < N; i++) { Index originalIndex = face[i]; vertices.push_back(shape.vertices[originalIndex]); vertices.push_back(faceNormal); } // Create the wire indices for unseen edges - for (Index i = 0; i < N; ++i) { + for (Index i = 0; i < N; i++) { Index a = i; Index b = (i + 1) % N; auto token = indexToken(face[a], face[b]); @@ -197,7 +197,7 @@ void setupFlatShape(GeometryCache::ShapeData& shapeData, const geometry::Solid& face = shape.faces[f]; // Create the wire indices for unseen edges - for (Index i = 0; i < N; ++i) { + for (Index i = 0; i < N; i++) { Index a = face[i]; Index b = face[(i + 1) % N]; auto token = indexToken(a, b); @@ -244,7 +244,7 @@ void setupSmoothShape(GeometryCache::ShapeData& shapeData, const geometry::Solid } // Create the solid face indices - for (Index i = 0; i < N - 2; ++i) { + for (Index i = 0; i < N - 2; i++) { solidIndices.push_back(face[i] + baseVertex); solidIndices.push_back(face[i + 1] + baseVertex); solidIndices.push_back(face[i + 2] + baseVertex); @@ -256,23 +256,30 @@ void setupSmoothShape(GeometryCache::ShapeData& shapeData, const geometry::Solid } template -void extrudePolygon(GeometryCache::ShapeData& shapeData, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer) { +void extrudePolygon(GeometryCache::ShapeData& shapeData, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer, bool isConical = false) { using namespace geometry; Index baseVertex = (Index)(vertexBuffer->getSize() / SHAPE_VERTEX_STRIDE); VertexVector vertices; IndexVector solidIndices, wireIndices; - // Top and bottom faces + // Top (if not conical) and bottom faces std::vector shape = polygon(); - for (const vec3& v : shape) { - vertices.push_back(vec3(v.x, 0.5f, v.z)); - vertices.push_back(vec3(0, 1, 0)); + if (isConical) { + for (uint32_t i = 0; i < N; i++) { + vertices.push_back(vec3(0.0f, 0.5f, 0.0f)); + vertices.push_back(vec3(0.0f, 1.0f, 0.0f)); + } + } else { + for (const vec3& v : shape) { + vertices.push_back(vec3(v.x, 0.5f, v.z)); + vertices.push_back(vec3(0.0f, 1.0f, 0.0f)); + } } for (const vec3& v : shape) { vertices.push_back(vec3(v.x, -0.5f, v.z)); - vertices.push_back(vec3(0, -1, 0)); + vertices.push_back(vec3(0.0f, -1.0f, 0.0f)); } - for (uint32_t i = 2; i < N; ++i) { + for (uint32_t i = 2; i < N; i++) { solidIndices.push_back(baseVertex + 0); solidIndices.push_back(baseVertex + i); solidIndices.push_back(baseVertex + i - 1); @@ -280,7 +287,7 @@ void extrudePolygon(GeometryCache::ShapeData& shapeData, gpu::BufferPointer& ver solidIndices.push_back(baseVertex + i + N - 1); solidIndices.push_back(baseVertex + i + N); } - for (uint32_t i = 1; i <= N; ++i) { + for (uint32_t i = 1; i <= N; i++) { wireIndices.push_back(baseVertex + (i % N)); wireIndices.push_back(baseVertex + i - 1); wireIndices.push_back(baseVertex + (i % N) + N); @@ -290,12 +297,12 @@ void extrudePolygon(GeometryCache::ShapeData& shapeData, gpu::BufferPointer& ver // Now do the sides baseVertex += 2 * N; - for (uint32_t i = 0; i < N; ++i) { + for (uint32_t i = 0; i < N; i++) { vec3 left = shape[i]; vec3 right = shape[(i + 1) % N]; vec3 normal = glm::normalize(left + right); - vec3 topLeft = vec3(left.x, 0.5f, left.z); - vec3 topRight = vec3(right.x, 0.5f, right.z); + vec3 topLeft = (isConical ? vec3(0.0f, 0.5f, 0.0f) : vec3(left.x, 0.5f, left.z)); + vec3 topRight = (isConical ? vec3(0.0f, 0.5f, 0.0f) : vec3(right.x, 0.5f, right.z)); vec3 bottomLeft = vec3(left.x, -0.5f, left.z); vec3 bottomRight = vec3(right.x, -0.5f, right.z); @@ -325,6 +332,41 @@ void extrudePolygon(GeometryCache::ShapeData& shapeData, gpu::BufferPointer& ver shapeData.setupIndices(indexBuffer, solidIndices, wireIndices); } +void drawCircle(GeometryCache::ShapeData& shapeData, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer) { + // Draw a circle with radius 1/4th the size of the bounding box + using namespace geometry; + + Index baseVertex = (Index)(vertexBuffer->getSize() / SHAPE_VERTEX_STRIDE); + VertexVector vertices; + IndexVector solidIndices, wireIndices; + const int NUM_CIRCLE_VERTICES = 64; + + std::vector shape = polygon(); + for (const vec3& v : shape) { + vertices.push_back(vec3(v.x, 0.0f, v.z)); + vertices.push_back(vec3(0.0f, 0.0f, 0.0f)); + } + + for (uint32_t i = 2; i < NUM_CIRCLE_VERTICES; i++) { + solidIndices.push_back(baseVertex + 0); + solidIndices.push_back(baseVertex + i); + solidIndices.push_back(baseVertex + i - 1); + solidIndices.push_back(baseVertex + NUM_CIRCLE_VERTICES); + solidIndices.push_back(baseVertex + i + NUM_CIRCLE_VERTICES - 1); + solidIndices.push_back(baseVertex + i + NUM_CIRCLE_VERTICES); + } + + for (uint32_t i = 1; i <= NUM_CIRCLE_VERTICES; i++) { + wireIndices.push_back(baseVertex + (i % NUM_CIRCLE_VERTICES)); + wireIndices.push_back(baseVertex + i - 1); + wireIndices.push_back(baseVertex + (i % NUM_CIRCLE_VERTICES) + NUM_CIRCLE_VERTICES); + wireIndices.push_back(baseVertex + (i - 1) + NUM_CIRCLE_VERTICES); + } + + shapeData.setupVertices(vertexBuffer, vertices); + shapeData.setupIndices(indexBuffer, solidIndices, wireIndices); +} + // FIXME solids need per-face vertices, but smooth shaded // components do not. Find a way to support using draw elements // or draw arrays as appropriate @@ -357,8 +399,8 @@ void GeometryCache::buildShapes() { Index baseVertex = (Index)(_shapeVertices->getSize() / SHAPE_VERTEX_STRIDE); ShapeData& shapeData = _shapes[Line]; shapeData.setupVertices(_shapeVertices, VertexVector { - vec3(-0.5, 0, 0), vec3(-0.5f, 0, 0), - vec3(0.5f, 0, 0), vec3(0.5f, 0, 0) + vec3(-0.5f, 0.0f, 0.0f), vec3(-0.5f, 0.0f, 0.0f), + vec3(0.5f, 0.0f, 0.0f), vec3(0.5f, 0.0f, 0.0f) }); IndexVector wireIndices; // Only two indices @@ -367,20 +409,22 @@ void GeometryCache::buildShapes() { shapeData.setupIndices(_shapeIndices, IndexVector(), wireIndices); } - // Not implememented yet: - //Triangle, extrudePolygon<3>(_shapes[Triangle], _shapeVertices, _shapeIndices); //Hexagon, extrudePolygon<6>(_shapes[Hexagon], _shapeVertices, _shapeIndices); //Octagon, extrudePolygon<8>(_shapes[Octagon], _shapeVertices, _shapeIndices); - - //Quad, - //Circle, - //Torus, - //Cone, //Cylinder, + extrudePolygon<64>(_shapes[Cylinder], _shapeVertices, _shapeIndices); + //Cone, + extrudePolygon<64>(_shapes[Cone], _shapeVertices, _shapeIndices, true); + //Circle + drawCircle(_shapes[Circle], _shapeVertices, _shapeIndices); + // Not implememented yet: + //Quad, + //Torus, + } gpu::Stream::FormatPointer& getSolidStreamFormat() { @@ -597,7 +641,7 @@ void GeometryCache::updateVertices(int id, const QVector& points, con auto pointCount = points.size(); auto colorCount = colors.size(); int compactColor = 0; - for (auto i = 0; i < pointCount; ++i) { + for (auto i = 0; i < pointCount; i++) { const auto& point = points[i]; *(vertex++) = point.x; *(vertex++) = point.y; @@ -674,7 +718,7 @@ void GeometryCache::updateVertices(int id, const QVector& points, con const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f); auto pointCount = points.size(); auto colorCount = colors.size(); - for (auto i = 0; i < pointCount; ++i) { + for (auto i = 0; i < pointCount; i++) { const glm::vec3& point = points[i]; if (i < colorCount) { const glm::vec4& color = colors[i]; diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index e0a610a095..9853269280 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -142,8 +142,8 @@ public: Dodecahedron, Icosahedron, Torus, // not yet implemented - Cone, // not yet implemented - Cylinder, // not yet implemented + Cone, + Cylinder, NUM_SHAPES, }; diff --git a/libraries/render-utils/src/LightClusters.h b/libraries/render-utils/src/LightClusters.h index 105d6fb139..f495dabebb 100644 --- a/libraries/render-utils/src/LightClusters.h +++ b/libraries/render-utils/src/LightClusters.h @@ -152,9 +152,9 @@ public: int numInputLights { 0 }; int numClusteredLights { 0 }; - void setNumClusteredLightReferences(int numRefs) { numClusteredLightReferences = numRefs; emit dirty(); } - void setNumInputLights(int numLights) { numInputLights = numLights; emit dirty(); } - void setNumClusteredLights(int numLights) { numClusteredLights = numLights; emit dirty(); } + void setNumClusteredLightReferences(int numRefs) { numClusteredLightReferences = numRefs; } + void setNumInputLights(int numLights) { numInputLights = numLights; } + void setNumClusteredLights(int numLights) { numClusteredLights = numLights; } int numSceneLights { 0 }; int numFreeSceneLights { 0 }; diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index d7ec087174..ddb64bc69e 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -48,6 +48,7 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, RenderArgs* args = renderContext->args; gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; + batch.enableStereo(false); glm::ivec4 viewport{0, 0, fbo->getWidth(), fbo->getHeight()}; batch.setViewportTransform(viewport); @@ -114,7 +115,7 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende skinProgram, state); } - const auto cachedMode = task.addJob("Setup"); + const auto cachedMode = task.addJob("ShadowSetup"); // CPU jobs: // Fetch and cull the items from the scene @@ -129,7 +130,7 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende // GPU jobs: Render to shadow map task.addJob("RenderShadowMap", sortedShapes, shapePlumber); - task.addJob("Teardown", cachedMode); + task.addJob("ShadowTeardown", cachedMode); } void RenderShadowTask::configure(const Config& configuration) { diff --git a/libraries/render-utils/src/RenderViewTask.cpp b/libraries/render-utils/src/RenderViewTask.cpp new file mode 100644 index 0000000000..fceaf7b5b9 --- /dev/null +++ b/libraries/render-utils/src/RenderViewTask.cpp @@ -0,0 +1,33 @@ +// +// RenderViewTask.cpp +// render-utils/src/ +// +// Created by Sam Gateau on 5/25/2017. +// Copyright 2017 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 "RenderViewTask.h" + +#include "RenderShadowTask.h" +#include "RenderDeferredTask.h" +#include "RenderForwardTask.h" + + + +void RenderViewTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, bool isDeferred) { + // auto items = input.get(); + + task.addJob("RenderShadowTask", cullFunctor); + + const auto items = task.addJob("FetchCullSort", cullFunctor); + assert(items.canCast()); + + if (isDeferred) { + task.addJob("RenderDeferredTask", items); + } else { + task.addJob("Forward", items); + } +} + diff --git a/libraries/render-utils/src/RenderViewTask.h b/libraries/render-utils/src/RenderViewTask.h new file mode 100644 index 0000000000..eb61f56eab --- /dev/null +++ b/libraries/render-utils/src/RenderViewTask.h @@ -0,0 +1,31 @@ +// +// RenderViewTask.h +// render-utils/src/ +// +// Created by Sam Gateau on 5/25/2017. +// Copyright 2017 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 +// +#pragma once +#ifndef hifi_RenderViewTask_h +#define hifi_RenderViewTask_h + +#include +#include + + +class RenderViewTask { +public: + using Input = RenderFetchCullSortTask::Output; + using JobModel = render::Task::ModelI; + + RenderViewTask() {} + + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred); + +}; + + +#endif // hifi_RenderViewTask_h diff --git a/libraries/render-utils/src/text/Font.cpp b/libraries/render-utils/src/text/Font.cpp index c6a7da3a1a..f51a779066 100644 --- a/libraries/render-utils/src/text/Font.cpp +++ b/libraries/render-utils/src/text/Font.cpp @@ -14,6 +14,8 @@ #include "../RenderUtilsLogging.h" #include "FontFamilies.h" +static std::mutex fontMutex; + struct TextureVertex { glm::vec2 pos; glm::vec2 tex; @@ -56,6 +58,7 @@ Font::Pointer Font::load(QIODevice& fontFile) { } Font::Pointer Font::load(const QString& family) { + std::lock_guard lock(fontMutex); if (!LOADED_FONTS.contains(family)) { static const QString SDFF_COURIER_PRIME_FILENAME{ ":/CourierPrime.sdff" }; diff --git a/libraries/render-utils/src/text/Font.h b/libraries/render-utils/src/text/Font.h index 5b6b4f2a43..2b61f19492 100644 --- a/libraries/render-utils/src/text/Font.h +++ b/libraries/render-utils/src/text/Font.h @@ -31,10 +31,10 @@ public: const glm::vec4* color, EffectType effectType, const glm::vec2& bound, bool layered = false); - static Pointer load(QIODevice& fontFile); static Pointer load(const QString& family); private: + static Pointer load(QIODevice& fontFile); QStringList tokenizeForWrapping(const QString& str) const; QStringList splitLines(const QString& str) const; glm::vec2 computeTokenExtent(const QString& str) const; diff --git a/libraries/render/src/render/EngineStats.cpp b/libraries/render/src/render/EngineStats.cpp index 9e45be5dbd..ae1467ac0f 100644 --- a/libraries/render/src/render/EngineStats.cpp +++ b/libraries/render/src/render/EngineStats.cpp @@ -63,6 +63,4 @@ void EngineStats::run(const RenderContextPointer& renderContext) { config->frameSetPipelineCount = _gpuStats._PSNumSetPipelines; config->frameSetInputFormatCount = _gpuStats._ISNumFormatChanges; - - config->emitDirty(); } diff --git a/libraries/render/src/task/Config.cpp b/libraries/render/src/task/Config.cpp index cb2c4f1e3c..0e630311f6 100644 --- a/libraries/render/src/task/Config.cpp +++ b/libraries/render/src/task/Config.cpp @@ -34,6 +34,7 @@ void TaskConfig::connectChildConfig(QConfigPointer childConfig, const std::strin if (childConfig->metaObject()->indexOfSignal(DIRTY_SIGNAL) != -1) { // Connect dirty->refresh if defined QObject::connect(childConfig.get(), SIGNAL(dirty()), this, SLOT(refresh())); + QObject::connect(childConfig.get(), SIGNAL(dirtyEnabled()), this, SLOT(refresh())); } } @@ -50,6 +51,7 @@ void TaskConfig::transferChildrenConfigs(QConfigPointer source) { if (child->metaObject()->indexOfSignal(DIRTY_SIGNAL) != -1) { // Connect dirty->refresh if defined QObject::connect(child, SIGNAL(dirty()), this, SLOT(refresh())); + QObject::connect(child, SIGNAL(dirtyEnabled()), this, SLOT(refresh())); } } } diff --git a/libraries/render/src/task/Config.h b/libraries/render/src/task/Config.h index c78a3f3bfe..40a3abbd18 100644 --- a/libraries/render/src/task/Config.h +++ b/libraries/render/src/task/Config.h @@ -89,7 +89,7 @@ protected: class JobConfig : public QObject { Q_OBJECT Q_PROPERTY(double cpuRunTime READ getCPURunTime NOTIFY newStats()) //ms - Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled) + Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY dirtyEnabled()) double _msCPURunTime{ 0.0 }; public: @@ -99,7 +99,7 @@ public: JobConfig(bool enabled) : alwaysEnabled{ false }, enabled{ enabled } {} bool isEnabled() { return alwaysEnabled || enabled; } - void setEnabled(bool enable) { enabled = alwaysEnabled || enable; } + void setEnabled(bool enable) { enabled = alwaysEnabled || enable; emit dirtyEnabled(); } bool alwaysEnabled{ true }; bool enabled{ true }; @@ -121,6 +121,7 @@ public slots: signals: void loaded(); void newStats(); + void dirtyEnabled(); }; class TaskConfig : public JobConfig { diff --git a/libraries/render/src/task/Task.h b/libraries/render/src/task/Task.h index ed335150a7..f76ba92546 100644 --- a/libraries/render/src/task/Task.h +++ b/libraries/render/src/task/Task.h @@ -170,6 +170,7 @@ protected: std::string _name = ""; }; + // A task is a specialized job to run a collection of other jobs // It can be created on any type T by aliasing the type JobModel in the class T // using JobModel = Task::Model diff --git a/libraries/script-engine/src/TabletScriptingInterface.cpp b/libraries/script-engine/src/TabletScriptingInterface.cpp index 644f1e6f0c..dadf436ea5 100644 --- a/libraries/script-engine/src/TabletScriptingInterface.cpp +++ b/libraries/script-engine/src/TabletScriptingInterface.cpp @@ -540,7 +540,7 @@ void TabletProxy::gotoWebScreen(const QString& url, const QString& injectedJavaS QObject* TabletProxy::addButton(const QVariant& properties) { auto tabletButtonProxy = QSharedPointer(new TabletButtonProxy(properties.toMap())); - std::lock_guard guard(_tabletMutex); + std::unique_lock guard(_tabletMutex); _tabletButtonProxies.push_back(tabletButtonProxy); if (!_toolbarMode && _qmlTabletRoot) { auto tablet = getQmlTablet(); @@ -550,7 +550,6 @@ QObject* TabletProxy::addButton(const QVariant& properties) { qCCritical(scriptengine) << "Could not find tablet in TabletRoot.qml"; } } else if (_toolbarMode) { - auto tabletScriptingInterface = DependencyManager::get(); QObject* toolbarProxy = tabletScriptingInterface->getSystemToolbarProxy(); @@ -559,6 +558,8 @@ QObject* TabletProxy::addButton(const QVariant& properties) { connectionType = Qt::BlockingQueuedConnection; } + guard.unlock(); + // copy properties from tablet button proxy to toolbar button proxy. QObject* toolbarButtonProxy = nullptr; bool hasResult = QMetaObject::invokeMethod(toolbarProxy, "addButton", connectionType, Q_RETURN_ARG(QObject*, toolbarButtonProxy), Q_ARG(QVariant, tabletButtonProxy->getProperties())); @@ -576,31 +577,38 @@ bool TabletProxy::onHomeScreen() { } void TabletProxy::removeButton(QObject* tabletButtonProxy) { - std::lock_guard guard(_tabletMutex); + std::unique_lock guard(_tabletMutex); auto tablet = getQmlTablet(); if (!tablet) { qCCritical(scriptengine) << "Could not find tablet in TabletRoot.qml"; } - auto iter = std::find(_tabletButtonProxies.begin(), _tabletButtonProxies.end(), tabletButtonProxy); - if (iter != _tabletButtonProxies.end()) { - if (!_toolbarMode && _qmlTabletRoot) { - (*iter)->setQmlButton(nullptr); - if (tablet) { - QMetaObject::invokeMethod(tablet, "removeButtonProxy", Qt::AutoConnection, Q_ARG(QVariant, (*iter)->getProperties())); - } - } else if (_toolbarMode) { - auto tabletScriptingInterface = DependencyManager::get(); - QObject* toolbarProxy = tabletScriptingInterface->getSystemToolbarProxy(); - - // remove button from toolbarProxy - QMetaObject::invokeMethod(toolbarProxy, "removeButton", Qt::AutoConnection, Q_ARG(QVariant, (*iter)->getUuid().toString())); - (*iter)->setToolbarButtonProxy(nullptr); + QSharedPointer buttonProxy; + { + auto iter = std::find(_tabletButtonProxies.begin(), _tabletButtonProxies.end(), tabletButtonProxy); + if (iter == _tabletButtonProxies.end()) { + qCWarning(scriptengine) << "TabletProxy::removeButton() could not find button " << tabletButtonProxy; + return; } + buttonProxy = *iter; _tabletButtonProxies.erase(iter); - } else { - qCWarning(scriptengine) << "TabletProxy::removeButton() could not find button " << tabletButtonProxy; + } + + if (!_toolbarMode && _qmlTabletRoot) { + buttonProxy->setQmlButton(nullptr); + if (tablet) { + guard.unlock(); + QMetaObject::invokeMethod(tablet, "removeButtonProxy", Qt::AutoConnection, Q_ARG(QVariant, buttonProxy->getProperties())); + } + } else if (_toolbarMode) { + auto tabletScriptingInterface = DependencyManager::get(); + QObject* toolbarProxy = tabletScriptingInterface->getSystemToolbarProxy(); + + // remove button from toolbarProxy + guard.unlock(); + QMetaObject::invokeMethod(toolbarProxy, "removeButton", Qt::AutoConnection, Q_ARG(QVariant, buttonProxy->getUuid().toString())); + buttonProxy->setToolbarButtonProxy(nullptr); } } diff --git a/libraries/ui/src/QmlWindowClass.cpp b/libraries/ui/src/QmlWindowClass.cpp index c0e94058ae..58d39448ac 100644 --- a/libraries/ui/src/QmlWindowClass.cpp +++ b/libraries/ui/src/QmlWindowClass.cpp @@ -104,9 +104,9 @@ void QmlWindowClass::initQml(QVariantMap properties) { Q_ASSERT(invokeResult); } else { // Build the event bridge and wrapper on the main thread - offscreenUi->load(qmlSource(), [&](QQmlContext* context, QObject* object) { + offscreenUi->loadInNewContext(qmlSource(), [&](QQmlContext* context, QObject* object) { _qmlWindow = object; - _qmlWindow->setProperty("eventBridge", QVariant::fromValue(this)); + context->setContextProperty("eventBridge", this); context->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership); context->engine()->setObjectOwnership(object, QQmlEngine::CppOwnership); if (properties.contains(TITLE_PROPERTY)) { diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index b5fa7cadad..7330daf228 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -123,15 +123,18 @@ bool ViveControllerManager::isSupported() const { bool ViveControllerManager::activate() { InputPlugin::activate(); - _container->addMenu(MENU_PATH); - _container->addMenuItem(PluginType::INPUT_PLUGIN, MENU_PATH, RENDER_CONTROLLERS, - [this] (bool clicked) { this->setRenderControllers(clicked); }, - true, true); - if (!_system) { _system = acquireOpenVrSystem(); } - Q_ASSERT(_system); + + if (!_system) { + return false; + } + + _container->addMenu(MENU_PATH); + _container->addMenuItem(PluginType::INPUT_PLUGIN, MENU_PATH, RENDER_CONTROLLERS, + [this](bool clicked) { this->setRenderControllers(clicked); }, + true, true); enableOpenVrKeyboard(_container); diff --git a/scripts/developer/tests/avatarToWorldTests.js b/scripts/developer/tests/avatarToWorldTests.js new file mode 100644 index 0000000000..c6e23fc81b --- /dev/null +++ b/scripts/developer/tests/avatarToWorldTests.js @@ -0,0 +1,127 @@ +var AVATAR_SELF_ID = "{00000000-0000-0000-0000-000000000001}"; + +var debugSphereBaseProperties = { + type: "Sphere", + dimensions: { x: 0.2, y: 0.2, z: 0.2 }, + dynamic: false, + collisionless: true, + gravity: { x: 0, y: 0, z: 0 }, + lifetime: 10.0, + userData: "{ \"grabbableKey\": { \"grabbable\": false, \"kinematic\": false } }" +}; + +var debugBoxBaseProperties = { + type: "Box", + dimensions: { x: 0.2, y: 0.2, z: 0.2 }, + dynamic: false, + collisionless: true, + gravity: { x: 0, y: 0, z: 0 }, + lifetime: 10.0, + userData: "{ \"grabbableKey\": { \"grabbable\": false, \"kinematic\": false } }" +}; + +//jointToWorldPoint +// create sphere for finger on left hand +// each frame, calculate world position of finger, with some offset +// update sphere to match this position +var jointToWorldPointTest_sphereEntity; +function jointToWorldPointTest() { + var jointIndex = MyAvatar.getJointIndex("LeftHandPinky4"); + var jointOffset_WorldSpace = { x: 0.1, y: 0, z: 0 }; + var worldPos = MyAvatar.jointToWorldPoint(jointOffset_WorldSpace, jointIndex); + + var jointSphereProps = Object.create(debugSphereBaseProperties); + jointSphereProps.name = "jointToWorldPointTest_Sphere"; + jointSphereProps.color = { blue: 240, green: 150, red: 150 }; + jointSphereProps.position = worldPos; + jointToWorldPointTest_sphereEntity = Entities.addEntity(jointSphereProps); +} +function jointToWorldPointTest_update(deltaTime) { + var jointIndex = MyAvatar.getJointIndex("LeftHandPinky4"); + var jointOffset_WorldSpace = { x: 0.1, y: 0, z: 0 }; + var worldPos = MyAvatar.jointToWorldPoint(jointOffset_WorldSpace, jointIndex); + var newProperties = { position: worldPos }; + Entities.editEntity(jointToWorldPointTest_sphereEntity, newProperties); +} + +//jointToWorldDirection +// create line in world space +// each frame calculate world space direction of players head z axis +// update line to match +var jointToWorldDirectionTest_lineEntity; +function jointToWorldDirectionTest() { + var jointIndex = MyAvatar.getJointIndex("Head"); + var avatarPos = MyAvatar.getJointPosition(jointIndex); + + var jointDir = { x: 1, y: 0, z: 1 }; + var worldDir = MyAvatar.jointToWorldDirection(jointDir, jointIndex); + print(worldDir.x); + print(worldDir.y); + print(worldDir.z); + jointToWorldDirectionTest_lineEntity = Entities.addEntity({ + type: "Line", + color: {red: 250, green: 0, blue: 0}, + dimensions: {x: 5, y: 5, z: 5}, + lifetime: 10.0, + linePoints: [{ + x: 0, + y: 0, + z: 0 + }, worldDir + ], + position : avatarPos, + }); +} +function jointToWorldDirection_update(deltaTime) { + var jointIndex = MyAvatar.getJointIndex("Head"); + var avatarPos = MyAvatar.getJointPosition(jointIndex); + var jointDir = { x: 1, y: 0, z: 0 }; + var worldDir = MyAvatar.jointToWorldDirection(jointDir, jointIndex); + var newProperties = { + linePoints: [{ + x: 0, + y: 0, + z: 0 + }, worldDir + ], + position : avatarPos + }; + + Entities.editEntity(jointToWorldDirectionTest_lineEntity, newProperties); +} + +//jointToWorldRotation +// create box in world space +// each frame calculate world space rotation of players head +// update box rotation to match +var jointToWorldRotationTest_boxEntity; +function jointToWorldRotationTest() { + var jointIndex = MyAvatar.getJointIndex("Head"); + var jointPosition_WorldSpace = MyAvatar.getJointPosition(jointIndex); + var jointRot = MyAvatar.getJointRotation(jointIndex); + var jointRot_WorldSpace = MyAvatar.jointToWorldRotation(jointRot, jointIndex); + + var boxProps = Object.create(debugBoxBaseProperties); + boxProps.name = "jointToWorldRotationTest_Box"; + boxProps.color = { blue: 250, green: 250, red: 250 }; + boxProps.position = jointPosition_WorldSpace; + boxProps.rotation = jointRot_WorldSpace; + jointToWorldRotationTest_boxEntity = Entities.addEntity(boxProps); +} +function jointToWorldRotationTest_update(deltaTime) { + var jointIndex = MyAvatar.getJointIndex("Head"); + var jointPosition_WorldSpace = MyAvatar.getJointPosition(jointIndex); + var jointRot = MyAvatar.getJointRotation(jointIndex); + var jointRot_WorldSpace = MyAvatar.jointToWorldRotation(jointRot, jointIndex); + var newProperties = { position: jointPosition_WorldSpace, rotation: jointRot_WorldSpace }; + Entities.editEntity(jointToWorldRotationTest_boxEntity, newProperties); +} + +jointToWorldPointTest(); +Script.update.connect(jointToWorldPointTest_update); + +jointToWorldDirectionTest(); +Script.update.connect(jointToWorldDirection_update); + +jointToWorldRotationTest(); +Script.update.connect(jointToWorldRotationTest_update); diff --git a/scripts/developer/tests/worldToAvatarTests.js b/scripts/developer/tests/worldToAvatarTests.js new file mode 100644 index 0000000000..6f0b19dc2d --- /dev/null +++ b/scripts/developer/tests/worldToAvatarTests.js @@ -0,0 +1,134 @@ +var AVATAR_SELF_ID = "{00000000-0000-0000-0000-000000000001}"; + +var debugSphereBaseProperties = { + type: "Sphere", + dimensions: { x: 0.2, y: 0.2, z: 0.2 }, + dynamic: false, + collisionless: true, + gravity: { x: 0, y: 0, z: 0 }, + lifetime: 10.0, + userData: "{ \"grabbableKey\": { \"grabbable\": false, \"kinematic\": false } }" +}; + +var debugBoxBaseProperties = { + type: "Box", + dimensions: { x: 0.2, y: 0.2, z: 0.2 }, + dynamic: false, + collisionless: true, + gravity: { x: 0, y: 0, z: 0 }, + lifetime: 10.0, + userData: "{ \"grabbableKey\": { \"grabbable\": false, \"kinematic\": false } }" +}; + +//worldToJointPoint +// calculate position offset from joint using getJointPosition +// pass through worldToJointPoint to get offset in joint space of players joint +// create a blue sphere and attach it to players joint using the joint space offset +// The two spheres should appear in the same place, but the blue sphere will follow the avatar +function worldToJointPointTest() { + var jointIndex = MyAvatar.getJointIndex("LeftHandPinky4"); + var avatarPos = MyAvatar.position; + + var jointPosition_WorldSpace = MyAvatar.getJointPosition(jointIndex); + var jointOffset_WorldSpace = { x: 0.1, y: 0, z: 0 }; + var jointPosition_WorldSpaceOffset = Vec3.sum(jointPosition_WorldSpace, jointOffset_WorldSpace); + var jointPosition_JointSpaceOffset = MyAvatar.worldToJointPoint(jointPosition_WorldSpaceOffset, jointIndex); + + var jointSphereProps = Object.create(debugSphereBaseProperties); + jointSphereProps.name = "worldToJointPointTest_Joint"; + jointSphereProps.color = { blue: 240, green: 150, red: 150 }; + jointSphereProps.localPosition = jointPosition_JointSpaceOffset; + jointSphereProps.parentID = AVATAR_SELF_ID; + jointSphereProps.parentJointIndex = jointIndex; + Entities.addEntity(jointSphereProps); + + var worldSphereProps = Object.create(debugSphereBaseProperties); + worldSphereProps.name = "worldToJointPointTest_World"; + worldSphereProps.color = { blue: 150, green: 250, red: 150 }; + worldSphereProps.position = jointPosition_WorldSpaceOffset; + Entities.addEntity(worldSphereProps); +} + +//worldToJointDirection +// create line and attach to avatars head +// each frame calculate direction of world x axis in joint space of players head +// update arrow orientation to match +var worldToJointDirectionTest_lineEntity; +function worldToJointDirectionTest() { + var jointIndex = MyAvatar.getJointIndex("Head"); + + var jointPosition_WorldSpace = MyAvatar.getJointPosition(jointIndex); + var jointOffset_WorldSpace = { x: 0, y: 0, z: 0 }; + var jointPosition_WorldSpaceOffset = Vec3.sum(jointPosition_WorldSpace, jointOffset_WorldSpace); + var jointPosition_JointSpaceOffset = MyAvatar.worldToJointPoint(jointPosition_WorldSpaceOffset, jointIndex); + + var worldDir = { x: 1, y: 0, z: 0 }; + var avatarDir = MyAvatar.worldToJointDirection(worldDir, jointIndex); + + worldToJointDirectionTest_lineEntity = Entities.addEntity({ + type: "Line", + color: {red: 200, green: 250, blue: 0}, + dimensions: {x: 5, y: 5, z: 5}, + lifetime: 10.0, + linePoints: [{ + x: 0, + y: 0, + z: 0 + }, avatarDir + ], + localPosition : jointOffset_WorldSpace, + parentID : AVATAR_SELF_ID, + parentJointIndex : jointIndex + }); +} + +function worldToJointDirectionTest_update(deltaTime) { + var jointIndex = MyAvatar.getJointIndex("Head"); + var worldDir = { x: 1, y: 0, z: 0 }; + var avatarDir = MyAvatar.worldToJointDirection(worldDir, jointIndex); + var newProperties = { linePoints: [{ + x: 0, + y: 0, + z: 0 + }, avatarDir + ]}; + + Entities.editEntity(worldToJointDirectionTest_lineEntity, newProperties); +} + +//worldToJointRotation +// create box and parent to some player joint +// convert world identity rotation to joint space rotation +// each frame, update box with new orientation +var worldToJointRotationTest_boxEntity; +function worldToJointRotationTest() { + var jointIndex = MyAvatar.getJointIndex("RightHandPinky4"); + var avatarPos = MyAvatar.position; + + var jointPosition_WorldSpace = MyAvatar.getJointPosition(jointIndex); + var jointOffset_WorldSpace = { x: 0.0, y: 0, z: 0 }; + var jointPosition_WorldSpaceOffset = Vec3.sum(jointPosition_WorldSpace, jointOffset_WorldSpace); + var jointPosition_JointSpaceOffset = MyAvatar.worldToJointPoint(jointPosition_WorldSpaceOffset, jointIndex); + + var jointBoxProps = Object.create(debugBoxBaseProperties); + jointBoxProps.name = "worldToJointRotationTest_Box"; + jointBoxProps.color = { blue: 0, green: 0, red: 250 }; + jointBoxProps.localPosition = jointPosition_JointSpaceOffset; + jointBoxProps.parentID = AVATAR_SELF_ID; + jointBoxProps.parentJointIndex = jointIndex; + worldToJointRotationTest_boxEntity = Entities.addEntity(jointBoxProps); +} +function worldToJointRotationTest_update(deltaTime) { + var jointIndex = MyAvatar.getJointIndex("RightHandPinky4"); + var worldRot = Quat.fromPitchYawRollDegrees(0,0,0); + var avatarRot = MyAvatar.worldToJointRotation(worldRot, jointIndex); + var newProperties = { localRotation: avatarRot }; + Entities.editEntity(worldToJointRotationTest_boxEntity, newProperties); +} + +worldToJointPointTest(); +worldToJointDirectionTest(); +worldToJointRotationTest(); + +Script.update.connect(worldToJointDirectionTest_update); +Script.update.connect(worldToJointRotationTest_update); diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 1348734f53..2c3cc496dc 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -618,7 +618,6 @@ var toolBar = (function () { that.toggle = function () { that.setActive(!isActive); - activeButton.editProperties({isActive: isActive}); if (!isActive) { tablet.gotoHomeScreen(); } @@ -642,6 +641,8 @@ var toolBar = (function () { enabled: active })); isActive = active; + activeButton.editProperties({isActive: isActive}); + var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); if (!isActive) { diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index fcb1815ca4..40f3d682cd 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -610,6 +610,7 @@ hr { .dropdown dl[dropped="true"] { color: #404040; background: linear-gradient(#afafaf, #afafaf); + z-index: 998; } .dropdown dt { @@ -657,7 +658,8 @@ hr { font-family: FiraSans-SemiBold; font-size: 15px; color: #404040; - background-color: #afafaf + background-color: #afafaf; + z-index: 999; } .dropdown li:hover { background-color: #00b4ef; diff --git a/scripts/system/html/css/jsoneditor.css b/scripts/system/html/css/jsoneditor.css index 4d63380a62..ce83b45ff3 100644 --- a/scripts/system/html/css/jsoneditor.css +++ b/scripts/system/html/css/jsoneditor.css @@ -503,7 +503,7 @@ div.jsoneditor-contextmenu-root { div.jsoneditor-contextmenu { position: absolute; box-sizing: content-box; - z-index: 99999; + z-index: 998; } div.jsoneditor-contextmenu ul, diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index 35accdd0df..bf65bdad32 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -51,6 +51,9 @@ + + +
diff --git a/scripts/system/html/js/eventBridgeLoader.js b/scripts/system/html/js/eventBridgeLoader.js index 0e95345b40..411780853b 100644 --- a/scripts/system/html/js/eventBridgeLoader.js +++ b/scripts/system/html/js/eventBridgeLoader.js @@ -13,7 +13,7 @@ var WebChannel; openEventBridge = function(callback) { WebChannel = new QWebChannel(qt.webChannelTransport, function (channel) { - EventBridge = WebChannel.objects.eventBridgeWrapper.eventBridge; + EventBridge = WebChannel.objects.eventBridge; callback(EventBridge); }); } diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index 8c3f5d03bb..b429a9f3ae 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -43,7 +43,7 @@ var LOCAL_TABLET_MODEL_PATH = Script.resourcesPath() + "meshes/tablet-with-home- // returns object with two fields: // * position - position in front of the user // * rotation - rotation of entity so it faces the user. -function calcSpawnInfo(hand, height) { +function calcSpawnInfo(hand, tabletHeight) { var finalPosition; var headPos = (HMD.active && Camera.mode === "first person") ? HMD.position : Camera.position; @@ -53,30 +53,35 @@ function calcSpawnInfo(hand, height) { hand = NO_HANDS; } + var handController = null; if (HMD.active && hand !== NO_HANDS) { - var handController = getControllerWorldLocation(hand, true); + handController = getControllerWorldLocation(hand, true); + } - var TABLET_UP_OFFSET = 0.1; - var TABLET_FORWARD_OFFSET = 0.1; - var normal = Vec3.multiplyQbyV(handController.rotation, {x: 0, y: -1, z: 0}); - var pitch = Math.asin(normal.y); - var MAX_PITCH = Math.PI / 4; - if (pitch < -MAX_PITCH) { - pitch = -MAX_PITCH; - } else if (pitch > MAX_PITCH) { - pitch = MAX_PITCH; + if (handController && handController.valid) { + // Orient tablet per hand pitch and yaw. + // Angle it back similar to holding it like a book. + // Move tablet up so that hand is at bottom. + // Move tablet back so that hand is in front. + + var position = handController.position; + var rotation = handController.rotation; + + if (hand === Controller.Standard.LeftHand) { + rotation = Quat.multiply(rotation, Quat.fromPitchYawRollDegrees(0, 90, 0)); + } else { + rotation = Quat.multiply(rotation, Quat.fromPitchYawRollDegrees(0, -90, 0)); } + var normal = Vec3.multiplyQbyV(rotation, Vec3.UNIT_NEG_Y); + var lookAt = Quat.lookAt(Vec3.ZERO, normal, Vec3.multiplyQbyV(MyAvatar.orientation, Vec3.UNIT_Y)); + var TABLET_RAKE_ANGLE = 30; + rotation = Quat.multiply(Quat.angleAxis(TABLET_RAKE_ANGLE, Vec3.multiplyQbyV(lookAt, Vec3.UNIT_X)), lookAt); - // rebuild normal from pitch and heading. - var heading = Math.atan2(normal.z, normal.x); - normal = {x: Math.cos(heading), y: Math.sin(pitch), z: Math.sin(heading)}; - - var position = Vec3.sum(handController.position, {x: 0, y: TABLET_UP_OFFSET, z: 0}); - var rotation = Quat.lookAt({x: 0, y: 0, z: 0}, normal, Y_AXIS); - var offset = Vec3.multiplyQbyV(rotation, {x: 0, y: height / 2, z: TABLET_FORWARD_OFFSET}); + var RELATIVE_SPAWN_OFFSET = { x: 0, y: 0.4, z: 0.05 }; + position = Vec3.sum(position, Vec3.multiplyQbyV(rotation, Vec3.multiply(tabletHeight, RELATIVE_SPAWN_OFFSET))); return { - position: Vec3.sum(offset, position), + position: position, rotation: rotation }; } else { diff --git a/tools/oven/src/BakerCLI.cpp b/tools/oven/src/BakerCLI.cpp index 7bdd221514..14eb9de150 100644 --- a/tools/oven/src/BakerCLI.cpp +++ b/tools/oven/src/BakerCLI.cpp @@ -48,7 +48,7 @@ void BakerCLI::bakeFile(QUrl inputUrl, const QString outputPath) { _baker->moveToThread(qApp->getNextWorkerThread()); } else { qCDebug(model_baking) << "Failed to determine baker type for file" << inputUrl; - return; + QApplication::exit(1); } // invoke the bake method on the baker thread @@ -60,5 +60,5 @@ void BakerCLI::bakeFile(QUrl inputUrl, const QString outputPath) { void BakerCLI::handleFinishedBaker() { qCDebug(model_baking) << "Finished baking file."; - QApplication::quit(); + QApplication::exit(_baker.get()->hasErrors()); } \ No newline at end of file