diff --git a/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java b/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java index 6428044df0..a7bda3c29b 100644 --- a/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java +++ b/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java @@ -24,6 +24,7 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Vibrator; +import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -54,6 +55,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW public static final String DOMAIN_URL = "url"; public static final String EXTRA_GOTO_USERNAME = "gotousername"; private static final String TAG = "Interface"; + public static final String EXTRA_ARGS = "args"; private static final int WEB_DRAWER_RIGHT_MARGIN = 262; private static final int WEB_DRAWER_BOTTOM_MARGIN = 150; private static final int NORMAL_DPI = 160; @@ -78,6 +80,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW private boolean nativeEnterBackgroundCallEnqueued = false; private SlidingDrawer mWebSlidingDrawer; + private boolean mStartInDomain; // private GvrApi gvrApi; // Opaque native pointer to the Application C++ object. // This object is owned by the InterfaceActivity instance and passed to the native methods. @@ -93,8 +96,14 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW public void onCreate(Bundle savedInstanceState) { super.isLoading = true; Intent intent = getIntent(); - if (intent.hasExtra(DOMAIN_URL) && !intent.getStringExtra(DOMAIN_URL).isEmpty()) { + if (intent.hasExtra(DOMAIN_URL) && !TextUtils.isEmpty(intent.getStringExtra(DOMAIN_URL))) { intent.putExtra("applicationArguments", "--url " + intent.getStringExtra(DOMAIN_URL)); + } else if (intent.hasExtra(EXTRA_ARGS)) { + String args = intent.getStringExtra(EXTRA_ARGS); + if (!TextUtils.isEmpty(args)) { + mStartInDomain = true; + intent.putExtra("applicationArguments", args); + } } super.onCreate(savedInstanceState); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); @@ -125,7 +134,10 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW getActionBar().hide(); } }); - startActivity(new Intent(this, SplashActivity.class)); + Intent splashIntent = new Intent(this, SplashActivity.class); + splashIntent.putExtra(SplashActivity.EXTRA_START_IN_DOMAIN, mStartInDomain); + startActivity(splashIntent); + mVibrator = (Vibrator) this.getSystemService(VIBRATOR_SERVICE); headsetStateReceiver = new HeadsetStateReceiver(); } diff --git a/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/PermissionChecker.java b/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/PermissionChecker.java index 78a6421746..ef9876c71a 100644 --- a/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/PermissionChecker.java +++ b/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/PermissionChecker.java @@ -9,6 +9,7 @@ import android.content.pm.PackageManager; import android.os.Bundle; import android.util.Log; import android.view.View; +import android.text.TextUtils; import org.json.JSONException; import org.json.JSONObject; @@ -27,9 +28,14 @@ public class PermissionChecker extends Activity { private static final boolean CHOOSE_AVATAR_ON_STARTUP = false; private static final String TAG = "Interface"; + private static final String EXTRA_ARGS = "args"; + private String mArgs; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + mArgs =(getIntent().getStringExtra(EXTRA_ARGS)); + Intent myIntent = new Intent(this, BreakpadUploaderService.class); startService(myIntent); if (CHOOSE_AVATAR_ON_STARTUP) { @@ -76,6 +82,11 @@ public class PermissionChecker extends Activity { private void launchActivityWithPermissions(){ Intent i = new Intent(this, InterfaceActivity.class); + + if (!TextUtils.isEmpty(mArgs)) { + i.putExtra(EXTRA_ARGS, mArgs); + } + startActivity(i); finish(); } diff --git a/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/SplashActivity.java b/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/SplashActivity.java index bb42467ace..536bf23603 100644 --- a/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/SplashActivity.java +++ b/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/SplashActivity.java @@ -7,6 +7,9 @@ import android.view.View; public class SplashActivity extends Activity { + public static final String EXTRA_START_IN_DOMAIN = "start-in-domain"; + private boolean mStartInDomain; + private native void registerLoadCompleteListener(); @Override @@ -36,13 +39,27 @@ public class SplashActivity extends Activity { } public void onAppLoadedComplete() { - if (HifiUtils.getInstance().isUserLoggedIn()) { - startActivity(new Intent(this, MainActivity.class)); - } else { - Intent menuIntent = new Intent(this, LoginMenuActivity.class); - menuIntent.putExtra(LoginMenuActivity.EXTRA_FINISH_ON_BACK, true); - startActivity(menuIntent); + if (!mStartInDomain) { + if (HifiUtils.getInstance().isUserLoggedIn()) { + startActivity(new Intent(this, MainActivity.class)); + } else { + Intent menuIntent = new Intent(this, LoginMenuActivity.class); + menuIntent.putExtra(LoginMenuActivity.EXTRA_FINISH_ON_BACK, true); + startActivity(menuIntent); + } } SplashActivity.this.finish(); } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putBoolean(EXTRA_START_IN_DOMAIN, mStartInDomain); + } + + @Override + protected void onRestoreInstanceState(Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + mStartInDomain = savedInstanceState.getBoolean(EXTRA_START_IN_DOMAIN, false); + } } diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 9ce11ca032..cc7a6929a2 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -300,6 +300,8 @@ Var substringResult SectionGetFlags ${${SecName}} $AR_SecFlags ;Reading section flags ;Checking lowest bit: IntOp $AR_SecFlags $AR_SecFlags & ${SF_SELECTED} + !insertmacro LoadVar ${SecName}_was_installed + IntOp $AR_SecFlags $AR_SecFlags | $R0 IntCmp $AR_SecFlags 1 "leave_${SecName}" ;Section is not selected: @@ -478,18 +480,6 @@ Var GAClientID ;-------------------------------- ; Installation types -Section "-Previous Install Cleanup" - ; Remove the resources folder so we don't end up including removed QML files - RMDir /r "$INSTDIR\resources" - - ; delete old assignment-client and domain-server so they're no longer present - ; in client only installs. - Delete "$INSTDIR\@DS_EXEC_NAME@" - Delete "$INSTDIR\@AC_EXEC_NAME@" - - ; delete interface so it's not there for server-only installs - Delete "$INSTDIR\@INTERFACE_WIN_EXEC_NAME@" -SectionEnd @CPACK_NSIS_INSTALLATION_TYPES@ diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 0bfed88d9c..74e25662c7 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -333,7 +333,11 @@ if (APPLE) COMMAND "${CMAKE_COMMAND}" -E copy_directory "${PROJECT_SOURCE_DIR}/resources/fonts" "${RESOURCES_DEV_DIR}/fonts" - # add redirect json to macOS builds. + #copy serverless for android + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${PROJECT_SOURCE_DIR}/resources/serverless" + "${RESOURCES_DEV_DIR}/serverless" + # add redirect json to macOS builds. COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${PROJECT_SOURCE_DIR}/resources/serverless/redirect.json" "${RESOURCES_DEV_DIR}/serverless/redirect.json" diff --git a/interface/resources/images/whitePixel.png b/interface/resources/images/whitePixel.png new file mode 100644 index 0000000000..fe2ec44cb7 Binary files /dev/null and b/interface/resources/images/whitePixel.png differ diff --git a/interface/resources/qml/+android_interface/Web3DSurface.qml b/interface/resources/qml/+android_interface/Web3DSurface.qml index d7b8306d6c..c4a613222e 100644 --- a/interface/resources/qml/+android_interface/Web3DSurface.qml +++ b/interface/resources/qml/+android_interface/Web3DSurface.qml @@ -1,5 +1,5 @@ // -// Web3DOverlay.qml +// Web3DSurface.qml // // Created by Gabriel Calero & Cristian Duarte on Jun 22, 2018 // Copyright 2016 High Fidelity, Inc. diff --git a/interface/resources/qml/+webengine/Browser.qml b/interface/resources/qml/+webengine/Browser.qml deleted file mode 100644 index 52157bdf42..0000000000 --- a/interface/resources/qml/+webengine/Browser.qml +++ /dev/null @@ -1,275 +0,0 @@ -import QtQuick 2.5 -import QtWebChannel 1.0 -import QtWebEngine 1.5 - -import controlsUit 1.0 -import stylesUit 1.0 -import "qrc:////qml//windows" - -ScrollingWindow { - id: root - HifiConstants { id: hifi } - //HifiStyles.HifiConstants { id: hifistyles } - title: "Browser" - resizable: true - destroyOnHidden: true - width: 800 - height: 600 - property variant permissionsBar: {'securityOrigin':'none','feature':'none'} - property alias url: webview.url - property alias webView: webview - - signal loadingChanged(int status) - - x: 100 - y: 100 - - Component.onCompleted: { - focus = true - shown = true - addressBar.text = webview.url - } - - function setProfile(profile) { - webview.profile = profile; - } - - function showPermissionsBar(){ - permissionsContainer.visible=true; - } - - function hidePermissionsBar(){ - permissionsContainer.visible=false; - } - - function allowPermissions(){ - webview.grantFeaturePermission(permissionsBar.securityOrigin, permissionsBar.feature, true); - hidePermissionsBar(); - } - - function setAutoAdd(auto) { - desktop.setAutoAdd(auto); - } - - Item { - id:item - width: pane.contentWidth - implicitHeight: pane.scrollHeight - - Row { - id: buttons - spacing: 4 - anchors.top: parent.top - anchors.topMargin: 8 - anchors.left: parent.left - anchors.leftMargin: 8 - HiFiGlyphs { - id: back; - enabled: webview.canGoBack; - text: hifi.glyphs.backward - color: enabled ? hifi.colors.text : hifi.colors.disabledText - size: 48 - MouseArea { anchors.fill: parent; onClicked: webview.goBack() } - } - - HiFiGlyphs { - id: forward; - enabled: webview.canGoForward; - text: hifi.glyphs.forward - color: enabled ? hifi.colors.text : hifi.colors.disabledText - size: 48 - MouseArea { anchors.fill: parent; onClicked: webview.goForward() } - } - - HiFiGlyphs { - id: reload; - enabled: webview.canGoForward; - text: webview.loading ? hifi.glyphs.close : hifi.glyphs.reload - color: enabled ? hifi.colors.text : hifi.colors.disabledText - size: 48 - MouseArea { anchors.fill: parent; onClicked: webview.goForward() } - } - - } - - Item { - id: border - height: 48 - anchors.top: parent.top - anchors.topMargin: 8 - anchors.right: parent.right - anchors.rightMargin: 8 - anchors.left: buttons.right - anchors.leftMargin: 8 - - Item { - id: barIcon - width: parent.height - height: parent.height - Image { - source: webview.icon; - x: (parent.height - height) / 2 - y: (parent.width - width) / 2 - sourceSize: Qt.size(width, height); - verticalAlignment: Image.AlignVCenter; - horizontalAlignment: Image.AlignHCenter - } - } - - TextField { - id: addressBar - anchors.right: parent.right - anchors.rightMargin: 8 - anchors.left: barIcon.right - anchors.leftMargin: 0 - anchors.verticalCenter: parent.verticalCenter - focus: true - colorScheme: hifi.colorSchemes.dark - placeholderText: "Enter URL" - Component.onCompleted: ScriptDiscoveryService.scriptsModelFilter.filterRegExp = new RegExp("^.*$", "i") - Keys.onPressed: { - switch(event.key) { - case Qt.Key_Enter: - case Qt.Key_Return: - event.accepted = true - if (text.indexOf("http") != 0) { - text = "http://" + text; - } - root.hidePermissionsBar(); - root.keyboardRaised = false; - webview.url = text; - break; - } - } - } - } - - Rectangle { - id:permissionsContainer - visible:false - color: "#000000" - width: parent.width - anchors.top: buttons.bottom - height:40 - z:100 - gradient: Gradient { - GradientStop { position: 0.0; color: "black" } - GradientStop { position: 1.0; color: "grey" } - } - - RalewayLight { - id: permissionsInfo - anchors.right:permissionsRow.left - anchors.rightMargin: 32 - anchors.topMargin:8 - anchors.top:parent.top - text: "This site wants to use your microphone/camera" - size: 18 - color: hifi.colors.white - } - - Row { - id: permissionsRow - spacing: 4 - anchors.top:parent.top - anchors.topMargin: 8 - anchors.right: parent.right - visible: true - z:101 - - Button { - id:allow - text: "Allow" - color: hifi.buttons.blue - colorScheme: root.colorScheme - width: 120 - enabled: true - onClicked: root.allowPermissions(); - z:101 - } - - Button { - id:block - text: "Block" - color: hifi.buttons.red - colorScheme: root.colorScheme - width: 120 - enabled: true - onClicked: root.hidePermissionsBar(); - z:101 - } - } - } - - WebView { - id: webview - url: "https://highfidelity.com/" - profile: FileTypeProfile; - - // Create a global EventBridge object for raiseAndLowerKeyboard. - WebEngineScript { - id: createGlobalEventBridge - sourceCode: eventBridgeJavaScriptToInject - injectionPoint: WebEngineScript.Deferred - worldId: WebEngineScript.MainWorld - } - - // Detect when may want to raise and lower keyboard. - WebEngineScript { - id: raiseAndLowerKeyboard - injectionPoint: WebEngineScript.Deferred - sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js" - worldId: WebEngineScript.MainWorld - } - - userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ] - - anchors.top: buttons.bottom - anchors.topMargin: 8 - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.right: parent.right - - onFeaturePermissionRequested: { - if (feature == 2) { // QWebEnginePage::MediaAudioCapture - grantFeaturePermission(securityOrigin, feature, true); - } else { - permissionsBar.securityOrigin = securityOrigin; - permissionsBar.feature = feature; - root.showPermissionsBar(); - } - } - - onLoadingChanged: { - if (loadRequest.status === WebEngineView.LoadSucceededStatus) { - addressBar.text = loadRequest.url - } - root.loadingChanged(loadRequest.status); - } - - onWindowCloseRequested: { - root.destroy(); - } - - Component.onCompleted: { - webChannel.registerObject("eventBridge", eventBridge); - webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper); - desktop.initWebviewProfileHandlers(webview.profile); - } - } - - } // item - - - Keys.onPressed: { - switch(event.key) { - case Qt.Key_L: - if (event.modifiers == Qt.ControlModifier) { - event.accepted = true - addressBar.selectAll() - addressBar.forceActiveFocus() - } - break; - } - } -} // dialog diff --git a/interface/resources/qml/+webengine/BrowserWebView.qml b/interface/resources/qml/+webengine/BrowserWebView.qml new file mode 100644 index 0000000000..5c5cf2cfb9 --- /dev/null +++ b/interface/resources/qml/+webengine/BrowserWebView.qml @@ -0,0 +1,58 @@ +import QtQuick 2.5 +import QtWebChannel 1.0 +import QtWebEngine 1.5 + +import controlsUit 1.0 + +WebView { + id: webview + url: "https://highfidelity.com/" + profile: FileTypeProfile; + + property var parentRoot: null + + // Create a global EventBridge object for raiseAndLowerKeyboard. + WebEngineScript { + id: createGlobalEventBridge + sourceCode: eventBridgeJavaScriptToInject + injectionPoint: WebEngineScript.Deferred + worldId: WebEngineScript.MainWorld + } + + // Detect when may want to raise and lower keyboard. + WebEngineScript { + id: raiseAndLowerKeyboard + injectionPoint: WebEngineScript.Deferred + sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js" + worldId: WebEngineScript.MainWorld + } + + userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ] + + onFeaturePermissionRequested: { + if (feature == 2) { // QWebEnginePage::MediaAudioCapture + grantFeaturePermission(securityOrigin, feature, true); + } else { + permissionsBar.securityOrigin = securityOrigin; + permissionsBar.feature = feature; + parentRoot.showPermissionsBar(); + } + } + + onLoadingChanged: { + if (loadRequest.status === WebEngineView.LoadSucceededStatus) { + addressBar.text = loadRequest.url + } + parentRoot.loadingChanged(loadRequest.status); + } + + onWindowCloseRequested: { + parentRoot.destroy(); + } + + Component.onCompleted: { + webChannel.registerObject("eventBridge", eventBridge); + webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper); + desktop.initWebviewProfileHandlers(webview.profile); + } +} diff --git a/interface/resources/qml/+webengine/InfoView.qml b/interface/resources/qml/+webengine/InfoView.qml deleted file mode 100644 index eb190c3c45..0000000000 --- a/interface/resources/qml/+webengine/InfoView.qml +++ /dev/null @@ -1,50 +0,0 @@ -// -// InfoView.qml -// -// Created by Bradley Austin Davis on 27 Apr 2015 -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -import QtQuick 2.5 -import Hifi 1.0 as Hifi - -import controlsUit 1.0 -import "qrc:////qml//windows" as Windows - -Windows.ScrollingWindow { - id: root - width: 800 - height: 800 - resizable: true - - Hifi.InfoView { - id: infoView - width: pane.contentWidth - implicitHeight: pane.scrollHeight - - WebView { - id: webview - objectName: "WebView" - anchors.fill: parent - url: infoView.url - } - } - - Component.onCompleted: { - centerWindow(root); - } - - onVisibleChanged: { - if (visible) { - centerWindow(root); - } - } - - function centerWindow() { - desktop.centerOnVisible(root); - } - -} diff --git a/interface/resources/qml/+webengine/QmlWebWindow.qml b/interface/resources/qml/+webengine/QmlWebWindow.qml deleted file mode 100644 index 2e3718f6f5..0000000000 --- a/interface/resources/qml/+webengine/QmlWebWindow.qml +++ /dev/null @@ -1,108 +0,0 @@ -// -// QmlWebWindow.qml -// -// Created by Bradley Austin Davis on 17 Dec 2015 -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -import QtQuick 2.5 -import QtWebEngine 1.1 -import QtWebChannel 1.0 - -import "qrc:////qml//windows" as Windows -import controlsUit 1.0 as Controls -import stylesUit 1.0 - -Windows.ScrollingWindow { - id: root - HifiConstants { id: hifi } - title: "WebWindow" - resizable: true - shown: false - // Don't destroy on close... otherwise the JS/C++ will have a dangling pointer - destroyOnCloseButton: false - property alias source: webview.url - property alias scriptUrl: webview.userScriptUrl - - // 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 - signal sendToScript(var message); - - signal moved(vector2d position); - signal resized(size size); - - function notifyMoved() { - moved(Qt.vector2d(x, y)); - } - - function notifyResized() { - resized(Qt.size(width, height)); - } - - onXChanged: notifyMoved(); - onYChanged: notifyMoved(); - - onWidthChanged: notifyResized(); - onHeightChanged: notifyResized(); - - onShownChanged: { - keyboardEnabled = HMD.active; - } - - Item { - width: pane.contentWidth - implicitHeight: pane.scrollHeight - - Controls.WebView { - id: webview - url: "about:blank" - anchors.fill: parent - focus: true - profile: HFWebEngineProfile; - - property string userScriptUrl: "" - - // Create a global EventBridge object for raiseAndLowerKeyboard. - WebEngineScript { - id: createGlobalEventBridge - sourceCode: eventBridgeJavaScriptToInject - injectionPoint: WebEngineScript.DocumentCreation - worldId: WebEngineScript.MainWorld - } - - // Detect when may want to raise and lower keyboard. - WebEngineScript { - id: raiseAndLowerKeyboard - injectionPoint: WebEngineScript.Deferred - sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js" - worldId: WebEngineScript.MainWorld - } - - // User script. - WebEngineScript { - id: userScript - sourceUrl: webview.userScriptUrl - injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished. - worldId: WebEngineScript.MainWorld - } - - userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ] - - function onWebEventReceived(event) { - if (event.slice(0, 17) === "CLARA.IO DOWNLOAD") { - ApplicationInterface.addAssetToWorldFromURL(event.slice(18)); - } - } - - Component.onCompleted: { - webChannel.registerObject("eventBridge", eventBridge); - webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper); - eventBridge.webEventReceived.connect(onWebEventReceived); - } - } - } -} diff --git a/interface/resources/qml/+webengine/QmlWebWindowView.qml b/interface/resources/qml/+webengine/QmlWebWindowView.qml new file mode 100644 index 0000000000..d2f1820e9a --- /dev/null +++ b/interface/resources/qml/+webengine/QmlWebWindowView.qml @@ -0,0 +1,53 @@ +import QtQuick 2.5 +import QtWebEngine 1.1 +import QtWebChannel 1.0 + +import controlsUit 1.0 as Controls +import stylesUit 1.0 +Controls.WebView { + id: webview + url: "about:blank" + anchors.fill: parent + focus: true + profile: HFWebEngineProfile; + + property string userScriptUrl: "" + + // Create a global EventBridge object for raiseAndLowerKeyboard. + WebEngineScript { + id: createGlobalEventBridge + sourceCode: eventBridgeJavaScriptToInject + injectionPoint: WebEngineScript.DocumentCreation + worldId: WebEngineScript.MainWorld + } + + // Detect when may want to raise and lower keyboard. + WebEngineScript { + id: raiseAndLowerKeyboard + injectionPoint: WebEngineScript.Deferred + sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js" + worldId: WebEngineScript.MainWorld + } + + // User script. + WebEngineScript { + id: userScript + sourceUrl: webview.userScriptUrl + injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished. + worldId: WebEngineScript.MainWorld + } + + userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ] + + function onWebEventReceived(event) { + if (event.slice(0, 17) === "CLARA.IO DOWNLOAD") { + ApplicationInterface.addAssetToWorldFromURL(event.slice(18)); + } + } + + Component.onCompleted: { + webChannel.registerObject("eventBridge", eventBridge); + webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper); + eventBridge.webEventReceived.connect(onWebEventReceived); + } +} diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index 78ffb51e67..496209a2a8 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -1,14 +1,13 @@ import QtQuick 2.5 - import controlsUit 1.0 import stylesUit 1.0 - import "windows" +import "." ScrollingWindow { id: root HifiConstants { id: hifi } - //HifiStyles.HifiConstants { id: hifistyles } + title: "Browser" resizable: true destroyOnHidden: true @@ -30,6 +29,7 @@ ScrollingWindow { } function setProfile(profile) { + webview.profile = profile; } function showPermissionsBar(){ @@ -41,6 +41,7 @@ ScrollingWindow { } function allowPermissions(){ + webview.grantFeaturePermission(permissionsBar.securityOrigin, permissionsBar.feature, true); hidePermissionsBar(); } @@ -198,10 +199,15 @@ ScrollingWindow { } } - ProxyWebView { + BrowserWebView { id: webview - anchors.centerIn: parent - url: "https://highfidelity.com/" + parentRoot: root + + anchors.top: buttons.bottom + anchors.topMargin: 8 + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right } } // item diff --git a/interface/resources/qml/BrowserWebView.qml b/interface/resources/qml/BrowserWebView.qml new file mode 100644 index 0000000000..6db016c284 --- /dev/null +++ b/interface/resources/qml/BrowserWebView.qml @@ -0,0 +1,8 @@ +import QtQuick 2.5 +import controlsUit 1.0 + +ProxyWebView { + property var parentRoot: null + + function grantFeaturePermission(origin, feature) {} +} diff --git a/interface/resources/qml/InfoView.qml b/interface/resources/qml/InfoView.qml index 5c2c7fcff9..2fd0ddf925 100644 --- a/interface/resources/qml/InfoView.qml +++ b/interface/resources/qml/InfoView.qml @@ -24,7 +24,7 @@ Windows.ScrollingWindow { width: pane.contentWidth implicitHeight: pane.scrollHeight - ProxyWebView { + BaseWebView { id: webview objectName: "WebView" anchors.fill: parent diff --git a/interface/resources/qml/QmlWebWindow.qml b/interface/resources/qml/QmlWebWindow.qml index a40168039e..7ee4b1c50c 100644 --- a/interface/resources/qml/QmlWebWindow.qml +++ b/interface/resources/qml/QmlWebWindow.qml @@ -11,6 +11,7 @@ import QtQuick 2.5 import "windows" as Windows +import "." import controlsUit 1.0 as Controls import stylesUit 1.0 @@ -55,12 +56,8 @@ Windows.ScrollingWindow { width: pane.contentWidth implicitHeight: pane.scrollHeight - Controls.WebView { + QmlWebWindowView { id: webview - url: "about:blank" - property string userScriptUrl: "" - anchors.fill: parent - focus: true } } } diff --git a/interface/resources/qml/QmlWebWindowView.qml b/interface/resources/qml/QmlWebWindowView.qml new file mode 100644 index 0000000000..9210468ae2 --- /dev/null +++ b/interface/resources/qml/QmlWebWindowView.qml @@ -0,0 +1,5 @@ +import QtQuick 2.5 +import controlsUit 1.0 + +BaseWebView { +} diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index a65170ee3b..3b703d72e6 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -129,11 +129,11 @@ Item { } StatText { visible: root.expanded - text: "Intersection calls: Entities/Overlays/Avatars/HUD\n " + - "Styluses:\t" + root.stylusPicksUpdated.x + "/" + root.stylusPicksUpdated.y + "/" + root.stylusPicksUpdated.z + "/" + root.stylusPicksUpdated.w + "\n " + - "Rays:\t" + root.rayPicksUpdated.x + "/" + root.rayPicksUpdated.y + "/" + root.rayPicksUpdated.z + "/" + root.rayPicksUpdated.w + "\n " + - "Parabolas:\t" + root.parabolaPicksUpdated.x + "/" + root.parabolaPicksUpdated.y + "/" + root.parabolaPicksUpdated.z + "/" + root.parabolaPicksUpdated.w + "\n " + - "Colliders:\t" + root.collisionPicksUpdated.x + "/" + root.collisionPicksUpdated.y + "/" + root.collisionPicksUpdated.z + "/" + root.collisionPicksUpdated.w + text: "Intersection calls: Entities/Avatars/HUD\n " + + "Styluses:\t" + root.stylusPicksUpdated.x + "/" + root.stylusPicksUpdated.y + "/" + root.stylusPicksUpdated.z + "\n " + + "Rays:\t" + root.rayPicksUpdated.x + "/" + root.rayPicksUpdated.y + "/" + root.rayPicksUpdated.z + "\n " + + "Parabolas:\t" + root.parabolaPicksUpdated.x + "/" + root.parabolaPicksUpdated.y + "/" + root.parabolaPicksUpdated.z + "\n " + + "Colliders:\t" + root.collisionPicksUpdated.x + "/" + root.collisionPicksUpdated.y + "/" + root.collisionPicksUpdated.z } } } diff --git a/interface/resources/qml/Web3DSurface.qml b/interface/resources/qml/Web3DSurface.qml index fdd5d8a7c6..32c19daf14 100644 --- a/interface/resources/qml/Web3DSurface.qml +++ b/interface/resources/qml/Web3DSurface.qml @@ -1,5 +1,5 @@ // -// Web3DOverlay.qml +// Web3DSurface.qml // // Created by David Rowe on 16 Dec 2016. // Copyright 2016 High Fidelity, Inc. diff --git a/interface/resources/qml/controlsUit/ProxyWebView.qml b/interface/resources/qml/controlsUit/ProxyWebView.qml index adcc472831..2b13760962 100644 --- a/interface/resources/qml/controlsUit/ProxyWebView.qml +++ b/interface/resources/qml/controlsUit/ProxyWebView.qml @@ -18,6 +18,7 @@ Rectangle { property bool safeLoading: false property bool loadingLatched: false + property bool loading: false property var loadingRequest: null diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index b7b412b577..1c190a2b79 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -42,6 +42,8 @@ Rectangle { property var activeTab: "nearbyTab"; property bool currentlyEditingDisplayName: false property bool punctuationMode: false; + property double loudSortTime: 0.0; + readonly property double kLOUD_SORT_PERIOD_MS: 1000.0; HifiConstants { id: hifi; } RootHttpRequest { id: http; } @@ -1247,6 +1249,11 @@ Rectangle { } } } + if (nearbyTable.sortIndicatorColumn == 0 && Date.now() - pal.loudSortTime >= pal.kLOUD_SORT_PERIOD_MS) { + // Current sort by loudness so re-sort. + sortModel(); + pal.loudSortTime = Date.now(); + } break; case 'clearLocalQMLData': ignored = {}; diff --git a/interface/resources/qml/hifi/tablet/+webengine/BlocksWebView.qml b/interface/resources/qml/hifi/tablet/+webengine/BlocksWebView.qml new file mode 100644 index 0000000000..050515da37 --- /dev/null +++ b/interface/resources/qml/hifi/tablet/+webengine/BlocksWebView.qml @@ -0,0 +1,7 @@ +import QtQuick 2.0 +import QtWebEngine 1.5 +import "../../controls" as Controls + +Controls.TabletWebView { + profile: WebEngineProfile { httpUserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36"} +} diff --git a/interface/resources/qml/hifi/tablet/BlocksWebView.qml b/interface/resources/qml/hifi/tablet/BlocksWebView.qml index 03fce0a112..eaed88ba01 100644 --- a/interface/resources/qml/hifi/tablet/BlocksWebView.qml +++ b/interface/resources/qml/hifi/tablet/BlocksWebView.qml @@ -2,7 +2,6 @@ import QtQuick 2.0 import "../../controls" as Controls Controls.TabletWebView { - profile: WebEngineProfile { httpUserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36"} } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ca7678c233..b8fb05daf4 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -152,6 +152,7 @@ #include #include #include +#include #include #include #include "recording/ClipCache.h" @@ -198,15 +199,12 @@ #include "ui/AvatarInputs.h" #include "ui/DialogsManager.h" #include "ui/LoginDialog.h" -#include "ui/overlays/Cube3DOverlay.h" -#include "ui/overlays/Web3DOverlay.h" #include "ui/Snapshot.h" #include "ui/SnapshotAnimated.h" #include "ui/StandAloneJSConsole.h" #include "ui/Stats.h" #include "ui/AnimStats.h" #include "ui/UpdateDialog.h" -#include "ui/overlays/Overlays.h" #include "ui/DomainConnectionModel.h" #include "ui/Keyboard.h" #include "Util.h" @@ -351,6 +349,8 @@ static const QString ACTIVE_DISPLAY_PLUGIN_SETTING_NAME = "activeDisplayPlugin"; static const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system"; static const QString KEEP_ME_LOGGED_IN_SETTING_NAME = "keepMeLoggedIn"; +static const float FOCUS_HIGHLIGHT_EXPANSION_FACTOR = 1.05f; + #if defined(Q_OS_ANDROID) static const QString TESTER_FILE = "/sdcard/_hifi_test_device.txt"; #endif @@ -623,7 +623,7 @@ public: case NestableType::Entity: return getEntityModelProvider(static_cast(uuid)); case NestableType::Overlay: - return getOverlayModelProvider(static_cast(uuid)); + return nullptr; case NestableType::Avatar: return getAvatarModelProvider(uuid); } @@ -647,22 +647,6 @@ private: return provider; } - scriptable::ModelProviderPointer getOverlayModelProvider(OverlayID overlayID) { - scriptable::ModelProviderPointer provider; - auto &overlays = qApp->getOverlays(); - if (auto overlay = overlays.getOverlay(overlayID)) { - if (auto base3d = std::dynamic_pointer_cast(overlay)) { - provider = std::dynamic_pointer_cast(base3d); - provider->modelProviderType = NestableType::Overlay; - } else { - qCWarning(interfaceapp) << "no renderer for overlay ID" << overlayID.toString(); - } - } else { - qCWarning(interfaceapp) << "overlay not found" << overlayID.toString(); - } - return provider; - } - scriptable::ModelProviderPointer getAvatarModelProvider(QUuid sessionUUID) { scriptable::ModelProviderPointer provider; auto avatarManager = DependencyManager::get(); @@ -947,9 +931,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { // FIXME move to header, or better yet, design some kind of UI manager // to take care of highlighting keyboard focused items, rather than // continuing to overburden Application.cpp -std::shared_ptr _keyboardFocusHighlight{ nullptr }; -OverlayID _keyboardFocusHighlightID{ UNKNOWN_OVERLAY_ID }; - +QUuid _keyboardFocusHighlightID; OffscreenGLCanvas* _qmlShareContext { nullptr }; @@ -1220,9 +1202,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo if (tabletScriptingInterface) { tabletScriptingInterface->setQmlTabletRoot(SYSTEM_TABLET, nullptr); } - getOverlays().deleteOverlay(getTabletScreenID()); - getOverlays().deleteOverlay(getTabletHomeButtonID()); - getOverlays().deleteOverlay(getTabletFrameID()); + auto entityScriptingInterface = DependencyManager::get(); + entityScriptingInterface->deleteEntity(getTabletScreenID()); + entityScriptingInterface->deleteEntity(getTabletHomeButtonID()); + entityScriptingInterface->deleteEntity(getTabletFrameID()); _failedToConnectToEntityServer = false; }); @@ -1312,10 +1295,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo if (isHMDMode()) { emit loginDialogFocusDisabled(); dialogsManager->hideLoginDialog(); - createLoginDialogOverlay(); + createLoginDialog(); } else { - getOverlays().deleteOverlay(_loginDialogOverlayID); - _loginDialogOverlayID = OverlayID(); + DependencyManager::get()->deleteEntity(_loginDialogID); + _loginDialogID = QUuid(); _loginStateManager.tearDown(); dialogsManager->showLoginDialog(); emit loginDialogFocusEnabled(); @@ -1906,25 +1889,27 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo SpacemouseManager::getInstance().init(); #endif - // If the user clicks an an entity, we will check that it's an unlocked web entity, and if so, set the focus to it - auto entityScriptingInterface = DependencyManager::get(); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::mousePressOnEntity, - [this](const EntityItemID& entityItemID, const PointerEvent& event) { + // If the user clicks on an object, we will check that it's a web surface, and if so, set the focus to it + auto pointerManager = DependencyManager::get(); + auto keyboardFocusOperator = [this](const QUuid& id, const PointerEvent& event) { if (event.shouldFocus()) { - if (getEntities()->wantsKeyboardFocus(entityItemID)) { - setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID); - setKeyboardFocusEntity(entityItemID); - } else { + auto keyboard = DependencyManager::get(); + if (getEntities()->wantsKeyboardFocus(id)) { + setKeyboardFocusEntity(id); + } else if (!keyboard->containsID(id)) { // FIXME: this is a hack to make the keyboard work for now, since the keys would otherwise steal focus setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); } } - }); + }; + connect(pointerManager.data(), &PointerManager::triggerBeginEntity, keyboardFocusOperator); + connect(pointerManager.data(), &PointerManager::triggerBeginOverlay, keyboardFocusOperator); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::deletingEntity, [this](const EntityItemID& entityItemID) { + auto entityScriptingInterface = DependencyManager::get(); + connect(entityScriptingInterface.data(), &EntityScriptingInterface::deletingEntity, this, [this](const EntityItemID& entityItemID) { if (entityItemID == _keyboardFocusedEntity.get()) { setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); } - }); + }, Qt::QueuedConnection); EntityTreeRenderer::setAddMaterialToEntityOperator([this](const QUuid& entityID, graphics::MaterialLayer material, const std::string& parentMaterialName) { if (_aboutToQuit) { @@ -1970,16 +1955,33 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo return false; }); + EntityTree::setGetEntityObjectOperator([this](const QUuid& id) -> QObject* { + auto entities = getEntities(); + if (auto entity = entities->renderableForEntityId(id)) { + return qobject_cast(entity.get()); + } + return nullptr; + }); + + EntityTree::setTextSizeOperator([this](const QUuid& id, const QString& text) { + auto entities = getEntities(); + if (auto entity = entities->renderableForEntityId(id)) { + if (auto renderable = std::dynamic_pointer_cast(entity)) { + return renderable->textSize(text); + } + } + }); + // Keyboard focus handling for Web overlays. auto overlays = &(qApp->getOverlays()); connect(overlays, &Overlays::overlayDeleted, [this](const OverlayID& overlayID) { if (overlayID == _keyboardFocusedOverlay.get()) { setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID); } + return QSizeF(0.0f, 0.0f); }); connect(this, &Application::aboutToQuit, [this]() { - setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID); setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); }); @@ -2300,8 +2302,30 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo DependencyManager::get()->setPrecisionPicking(rayPickID, value); }); - EntityTreeRenderer::setGetAvatarUpOperator([] { - return DependencyManager::get()->getMyAvatar()->getWorldOrientation() * Vectors::UP; + EntityItem::setBillboardRotationOperator([this](const glm::vec3& position, const glm::quat& rotation, BillboardMode billboardMode) { + if (billboardMode == BillboardMode::YAW) { + //rotate about vertical to face the camera + ViewFrustum frustum; + copyViewFrustum(frustum); + glm::vec3 dPosition = frustum.getPosition() - position; + // If x and z are 0, atan(x, z) is undefined, so default to 0 degrees + float yawRotation = dPosition.x == 0.0f && dPosition.z == 0.0f ? 0.0f : glm::atan(dPosition.x, dPosition.z); + return glm::quat(glm::vec3(0.0f, yawRotation, 0.0f)); + } else if (billboardMode == BillboardMode::FULL) { + ViewFrustum frustum; + copyViewFrustum(frustum); + glm::vec3 cameraPos = frustum.getPosition(); + // use the referencial from the avatar, y isn't always up + glm::vec3 avatarUP = DependencyManager::get()->getMyAvatar()->getWorldOrientation() * Vectors::UP; + // check to see if glm::lookAt will work / using glm::lookAt variable name + glm::highp_vec3 s(glm::cross(position - cameraPos, avatarUP)); + + // make sure s is not NaN for any component + if (glm::length2(s) > 0.0f) { + return glm::conjugate(glm::toQuat(glm::lookAt(cameraPos, position, avatarUP))); + } + } + return rotation; }); render::entities::WebEntityRenderer::setAcquireWebSurfaceOperator([this](const QString& url, bool htmlContent, QSharedPointer& webSurface, bool& cachedWebSurface) { @@ -2327,7 +2351,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo }); }); auto rootItemLoadedFunctor = [webSurface, url, isTablet] { - Application::setupQmlSurface(webSurface->getSurfaceContext(), isTablet || url == OVERLAY_LOGIN_DIALOG.toString()); + Application::setupQmlSurface(webSurface->getSurfaceContext(), isTablet || url == LOGIN_DIALOG.toString()); }; if (webSurface->getRootItem()) { rootItemLoadedFunctor(); @@ -2346,7 +2370,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // Fix for crash in QtWebEngineCore when rapidly switching domains // Call stop on the QWebEngineView before destroying OffscreenQMLSurface. - if (rootItem) { + if (rootItem && !cachedWebSurface) { // stop loading QMetaObject::invokeMethod(rootItem, "stop"); } @@ -2375,6 +2399,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo }); // Preload Tablet sounds + DependencyManager::get()->setEntityTree(qApp->getEntities()->getTree()); DependencyManager::get()->preloadSounds(); DependencyManager::get()->createKeyboard(); @@ -2582,11 +2607,10 @@ void Application::cleanupBeforeQuit() { _applicationStateDevice.reset(); { - if (_keyboardFocusHighlightID != UNKNOWN_OVERLAY_ID) { - getOverlays().deleteOverlay(_keyboardFocusHighlightID); - _keyboardFocusHighlightID = UNKNOWN_OVERLAY_ID; + if (_keyboardFocusHighlightID != UNKNOWN_ENTITY_ID) { + DependencyManager::get()->deleteEntity(_keyboardFocusHighlightID); + _keyboardFocusHighlightID = UNKNOWN_ENTITY_ID; } - _keyboardFocusHighlight = nullptr; } { @@ -3090,7 +3114,7 @@ void Application::initializeUi() { }); #if !defined(DISABLE_QML) - // Pre-create a couple of Web3D overlays to speed up tablet UI + // Pre-create a couple of offscreen surfaces to speed up tablet UI auto offscreenSurfaceCache = DependencyManager::get(); offscreenSurfaceCache->setOnRootContextCreated([&](const QString& rootObject, QQmlContext* surfaceContext) { if (rootObject == TabletScriptingInterface::QML) { @@ -3811,8 +3835,8 @@ static inline bool isKeyEvent(QEvent::Type type) { return type == QEvent::KeyPress || type == QEvent::KeyRelease; } -bool Application::handleKeyEventForFocusedEntityOrOverlay(QEvent* event) { - if (!_keyboardFocusedEntity.get().isInvalidID()) { +bool Application::handleKeyEventForFocusedEntity(QEvent* event) { + if (_keyboardFocusedEntity.get() != UNKNOWN_ENTITY_ID) { switch (event->type()) { case QEvent::KeyPress: case QEvent::KeyRelease: @@ -3833,28 +3857,6 @@ bool Application::handleKeyEventForFocusedEntityOrOverlay(QEvent* event) { } } - if (_keyboardFocusedOverlay.get() != UNKNOWN_OVERLAY_ID) { - switch (event->type()) { - case QEvent::KeyPress: - case QEvent::KeyRelease: { - // Only Web overlays can have focus. - auto overlay = std::dynamic_pointer_cast(getOverlays().getOverlay(_keyboardFocusedOverlay.get())); - if (overlay && overlay->getEventHandler()) { - event->setAccepted(false); - QCoreApplication::sendEvent(overlay->getEventHandler(), event); - if (event->isAccepted()) { - _lastAcceptedKeyPress = usecTimestampNow(); - return true; - } - } - } - break; - - default: - break; - } - } - return false; } @@ -3897,8 +3899,8 @@ bool Application::event(QEvent* event) { return false; } - // Allow focused Entities and Overlays to handle keyboard input - if (isKeyEvent(event->type()) && handleKeyEventForFocusedEntityOrOverlay(event)) { + // Allow focused Entities to handle keyboard input + if (isKeyEvent(event->type()) && handleKeyEventForFocusedEntity(event)) { return true; } @@ -4379,9 +4381,9 @@ void Application::mouseMoveEvent(QMouseEvent* event) { buttons, event->modifiers()); if (compositor.getReticleVisible() || !isHMDMode() || !compositor.getReticleOverDesktop() || - getOverlays().getOverlayAtPoint(glm::vec2(transformedPos.x(), transformedPos.y())) != UNKNOWN_OVERLAY_ID) { - getOverlays().mouseMoveEvent(&mappedEvent); + getOverlays().getOverlayAtPoint(glm::vec2(transformedPos.x(), transformedPos.y())) != UNKNOWN_ENTITY_ID) { getEntities()->mouseMoveEvent(&mappedEvent); + getOverlays().mouseMoveEvent(&mappedEvent); } _controllerScriptingInterface->emitMouseMoveEvent(&mappedEvent); // send events to any registered scripts @@ -4413,15 +4415,16 @@ void Application::mousePressEvent(QMouseEvent* event) { #else QPointF transformedPos; #endif - QMouseEvent mappedEvent(event->type(), - transformedPos, - event->screenPos(), event->button(), - event->buttons(), event->modifiers()); - getOverlays().mousePressEvent(&mappedEvent); + QMouseEvent mappedEvent(event->type(), transformedPos, event->screenPos(), event->button(), event->buttons(), event->modifiers()); + std::pair entityResult; if (!_controllerScriptingInterface->areEntityClicksCaptured()) { - getEntities()->mousePressEvent(&mappedEvent); + entityResult = getEntities()->mousePressEvent(&mappedEvent); } + std::pair overlayResult = getOverlays().mousePressEvent(&mappedEvent); + + QUuid focusedEntity = entityResult.first < overlayResult.first ? entityResult.second : overlayResult.second; + setKeyboardFocusEntity(getEntities()->wantsKeyboardFocus(focusedEntity) ? focusedEntity : UNKNOWN_ENTITY_ID); _controllerScriptingInterface->emitMousePressEvent(&mappedEvent); // send events to any registered scripts @@ -4461,10 +4464,10 @@ void Application::mouseDoublePressEvent(QMouseEvent* event) { event->screenPos(), event->button(), event->buttons(), event->modifiers()); - getOverlays().mouseDoublePressEvent(&mappedEvent); if (!_controllerScriptingInterface->areEntityClicksCaptured()) { getEntities()->mouseDoublePressEvent(&mappedEvent); } + getOverlays().mouseDoublePressEvent(&mappedEvent); // if one of our scripts have asked to capture this event, then stop processing it if (_controllerScriptingInterface->isMouseCaptured()) { @@ -4488,8 +4491,8 @@ void Application::mouseReleaseEvent(QMouseEvent* event) { event->screenPos(), event->button(), event->buttons(), event->modifiers()); - getOverlays().mouseReleaseEvent(&mappedEvent); getEntities()->mouseReleaseEvent(&mappedEvent); + getOverlays().mouseReleaseEvent(&mappedEvent); _controllerScriptingInterface->emitMouseReleaseEvent(&mappedEvent); // send events to any registered scripts @@ -4955,31 +4958,19 @@ void Application::idle() { update(glm::clamp(secondsSinceLastUpdate, 0.0f, BIGGEST_DELTA_TIME_SECS)); } - - // Update focus highlight for entity or overlay. - { - if (!_keyboardFocusedEntity.get().isInvalidID() || _keyboardFocusedOverlay.get() != UNKNOWN_OVERLAY_ID) { + { // Update keyboard focus highlight + if (!_keyboardFocusedEntity.get().isInvalidID()) { const quint64 LOSE_FOCUS_AFTER_ELAPSED_TIME = 30 * USECS_PER_SECOND; // if idle for 30 seconds, drop focus quint64 elapsedSinceAcceptedKeyPress = usecTimestampNow() - _lastAcceptedKeyPress; if (elapsedSinceAcceptedKeyPress > LOSE_FOCUS_AFTER_ELAPSED_TIME) { setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); - setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID); } else { - // update position of highlight overlay - if (!_keyboardFocusedEntity.get().isInvalidID()) { - auto entity = getEntities()->getTree()->findEntityByID(_keyboardFocusedEntity.get()); - if (entity && _keyboardFocusHighlight) { - _keyboardFocusHighlight->setWorldOrientation(entity->getWorldOrientation()); - _keyboardFocusHighlight->setWorldPosition(entity->getWorldPosition()); - } - } else { - // Only Web overlays can have focus. - auto overlay = - std::dynamic_pointer_cast(getOverlays().getOverlay(_keyboardFocusedOverlay.get())); - if (overlay && _keyboardFocusHighlight) { - _keyboardFocusHighlight->setWorldOrientation(overlay->getWorldOrientation()); - _keyboardFocusHighlight->setWorldPosition(overlay->getWorldPosition()); - } + if (auto entity = getEntities()->getTree()->findEntityByID(_keyboardFocusedEntity.get())) { + EntityItemProperties properties; + properties.setPosition(entity->getWorldPosition()); + properties.setRotation(entity->getWorldOrientation()); + properties.setDimensions(entity->getScaledDimensions() * FOCUS_HIGHLIGHT_EXPANSION_FACTOR); + DependencyManager::get()->editEntity(_keyboardFocusHighlightID, properties); } } } @@ -5777,118 +5768,80 @@ void Application::rotationModeChanged() const { } void Application::setKeyboardFocusHighlight(const glm::vec3& position, const glm::quat& rotation, const glm::vec3& dimensions) { - // Create focus if (qApp->getLoginDialogPoppedUp()) { return; } - if (_keyboardFocusHighlightID == UNKNOWN_OVERLAY_ID || !getOverlays().isAddedOverlay(_keyboardFocusHighlightID)) { - _keyboardFocusHighlight = std::make_shared(); - _keyboardFocusHighlight->setAlpha(1.0f); - _keyboardFocusHighlight->setColor({ 0xFF, 0xEF, 0x00 }); - _keyboardFocusHighlight->setIsSolid(false); - _keyboardFocusHighlight->setPulseMin(0.5); - _keyboardFocusHighlight->setPulseMax(1.0); - _keyboardFocusHighlight->setColorPulse(1.0); - _keyboardFocusHighlight->setIgnorePickIntersection(true); - _keyboardFocusHighlight->setDrawInFront(false); - _keyboardFocusHighlightID = getOverlays().addOverlay(_keyboardFocusHighlight); + + auto entityScriptingInterface = DependencyManager::get(); + if (_keyboardFocusHighlightID == UNKNOWN_ENTITY_ID || !entityScriptingInterface->isAddedEntity(_keyboardFocusHighlightID)) { + EntityItemProperties properties; + properties.setType(EntityTypes::Box); + properties.setAlpha(1.0f); + properties.setColor({ 0xFF, 0xEF, 0x00 }); + properties.setPrimitiveMode(PrimitiveMode::LINES); + properties.getPulse().setMin(0.5); + properties.getPulse().setMax(1.0f); + properties.getPulse().setColorMode(PulseMode::IN_PHASE); + properties.setIgnorePickIntersection(true); + _keyboardFocusHighlightID = entityScriptingInterface->addEntityInternal(properties, entity::HostType::LOCAL); } // Position focus - _keyboardFocusHighlight->setWorldOrientation(rotation); - _keyboardFocusHighlight->setWorldPosition(position); - _keyboardFocusHighlight->setDimensions(dimensions); - _keyboardFocusHighlight->setVisible(true); + EntityItemProperties properties; + properties.setPosition(position); + properties.setRotation(rotation); + properties.setDimensions(dimensions); + properties.setVisible(true); + entityScriptingInterface->editEntity(_keyboardFocusHighlightID, properties); } QUuid Application::getKeyboardFocusEntity() const { return _keyboardFocusedEntity.get(); } -static const float FOCUS_HIGHLIGHT_EXPANSION_FACTOR = 1.05f; - -void Application::setKeyboardFocusEntity(const EntityItemID& entityItemID) { - if (qApp->getLoginDialogPoppedUp()) { - return; - } - if (_keyboardFocusedEntity.get() != entityItemID) { - _keyboardFocusedEntity.set(entityItemID); - - if (_keyboardFocusHighlight && _keyboardFocusedOverlay.get() == UNKNOWN_OVERLAY_ID) { - _keyboardFocusHighlight->setVisible(false); - } - - if (entityItemID == UNKNOWN_ENTITY_ID) { - return; - } - - auto entityScriptingInterface = DependencyManager::get(); - auto properties = entityScriptingInterface->getEntityProperties(entityItemID); - if (!properties.getLocked() && properties.getVisible()) { - - auto entities = getEntities(); - auto entityId = _keyboardFocusedEntity.get(); - if (entities->wantsKeyboardFocus(entityId)) { - entities->setProxyWindow(entityId, _window->windowHandle()); - if (_keyboardMouseDevice->isActive()) { - _keyboardMouseDevice->pluginFocusOutEvent(); - } - _lastAcceptedKeyPress = usecTimestampNow(); - - auto entity = getEntities()->getEntity(entityId); - if (entity) { - setKeyboardFocusHighlight(entity->getWorldPosition(), entity->getWorldOrientation(), - entity->getScaledDimensions() * FOCUS_HIGHLIGHT_EXPANSION_FACTOR); - } - } - } - } -} - -OverlayID Application::getKeyboardFocusOverlay() { - return _keyboardFocusedOverlay.get(); -} - -void Application::setKeyboardFocusOverlay(const OverlayID& overlayID) { - if (overlayID != _keyboardFocusedOverlay.get()) { - if (qApp->getLoginDialogPoppedUp() && !_loginDialogOverlayID.isNull()) { - if (overlayID == _loginDialogOverlayID) { +void Application::setKeyboardFocusEntity(const QUuid& id) { + if (_keyboardFocusedEntity.get() != id) { + if (qApp->getLoginDialogPoppedUp() && !_loginDialogID.isNull()) { + if (id == _loginDialogID) { emit loginDialogFocusEnabled(); } else { - // that's the only overlay we want in focus; + // that's the only entity we want in focus; return; } } - _keyboardFocusedOverlay.set(overlayID); + _keyboardFocusedEntity.set(id); - if (_keyboardFocusHighlight && _keyboardFocusedEntity.get() == UNKNOWN_ENTITY_ID) { - _keyboardFocusHighlight->setVisible(false); - } + auto entityScriptingInterface = DependencyManager::get(); + if (id != UNKNOWN_ENTITY_ID) { + EntityPropertyFlags desiredProperties; + desiredProperties += PROP_VISIBLE; + desiredProperties += PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT; + auto properties = entityScriptingInterface->getEntityProperties(id); + if (properties.getVisible()) { + auto entities = getEntities(); + auto entityId = _keyboardFocusedEntity.get(); + if (entities->wantsKeyboardFocus(entityId)) { + entities->setProxyWindow(entityId, _window->windowHandle()); + if (_keyboardMouseDevice->isActive()) { + _keyboardMouseDevice->pluginFocusOutEvent(); + } + _lastAcceptedKeyPress = usecTimestampNow(); - if (overlayID == UNKNOWN_OVERLAY_ID) { - return; - } - - auto overlayType = getOverlays().getOverlayType(overlayID); - auto isVisible = getOverlays().getProperty(overlayID, "visible").value.toBool(); - if (overlayType == Web3DOverlay::TYPE && isVisible) { - auto overlay = std::dynamic_pointer_cast(getOverlays().getOverlay(overlayID)); - overlay->setProxyWindow(_window->windowHandle()); - - if (_keyboardMouseDevice->isActive()) { - _keyboardMouseDevice->pluginFocusOutEvent(); - } - _lastAcceptedKeyPress = usecTimestampNow(); - - if (overlay->getProperty("showKeyboardFocusHighlight").toBool()) { - auto size = overlay->getSize() * FOCUS_HIGHLIGHT_EXPANSION_FACTOR; - const float OVERLAY_DEPTH = 0.0105f; - setKeyboardFocusHighlight(overlay->getWorldPosition(), overlay->getWorldOrientation(), glm::vec3(size.x, size.y, OVERLAY_DEPTH)); - } else if (_keyboardFocusHighlight) { - _keyboardFocusHighlight->setVisible(false); + if (properties.getShowKeyboardFocusHighlight()) { + if (auto entity = entities->getEntity(entityId)) { + setKeyboardFocusHighlight(entity->getWorldPosition(), entity->getWorldOrientation(), + entity->getScaledDimensions() * FOCUS_HIGHLIGHT_EXPANSION_FACTOR); + return; + } + } + } } } + + EntityItemProperties properties; + properties.setVisible(false); + entityScriptingInterface->editEntity(_keyboardFocusHighlightID, properties); } } @@ -6265,6 +6218,19 @@ void Application::update(float deltaTime) { auto grabManager = DependencyManager::get(); grabManager->simulateGrabs(); + // TODO: break these out into distinct perfTimers when they prove interesting + { + PROFILE_RANGE(app, "PickManager"); + PerformanceTimer perfTimer("pickManager"); + DependencyManager::get()->update(); + } + + { + PROFILE_RANGE(app, "PointerManager"); + PerformanceTimer perfTimer("pointerManager"); + DependencyManager::get()->update(); + } + QSharedPointer avatarManager = DependencyManager::get(); { @@ -6278,40 +6244,57 @@ void Application::update(float deltaTime) { PROFILE_RANGE(simulation_physics, "PrePhysics"); PerformanceTimer perfTimer("prePhysics)"); { + PROFILE_RANGE(simulation_physics, "RemoveEntities"); const VectorOfMotionStates& motionStates = _entitySimulation->getObjectsToRemoveFromPhysics(); - _physicsEngine->removeObjects(motionStates); + { + PROFILE_RANGE_EX(simulation_physics, "NumObjs", 0xffff0000, (uint64_t)motionStates.size()); + _physicsEngine->removeObjects(motionStates); + } _entitySimulation->deleteObjectsRemovedFromPhysics(); } - VectorOfMotionStates motionStates; - getEntities()->getTree()->withReadLock([&] { - _entitySimulation->getObjectsToAddToPhysics(motionStates); - _physicsEngine->addObjects(motionStates); - - }); - getEntities()->getTree()->withReadLock([&] { - _entitySimulation->getObjectsToChange(motionStates); - VectorOfMotionStates stillNeedChange = _physicsEngine->changeObjects(motionStates); - _entitySimulation->setObjectsToChange(stillNeedChange); - }); + { + PROFILE_RANGE(simulation_physics, "AddEntities"); + VectorOfMotionStates motionStates; + getEntities()->getTree()->withReadLock([&] { + _entitySimulation->getObjectsToAddToPhysics(motionStates); + PROFILE_RANGE_EX(simulation_physics, "NumObjs", 0xffff0000, (uint64_t)motionStates.size()); + _physicsEngine->addObjects(motionStates); + }); + } + { + VectorOfMotionStates motionStates; + PROFILE_RANGE(simulation_physics, "ChangeEntities"); + getEntities()->getTree()->withReadLock([&] { + _entitySimulation->getObjectsToChange(motionStates); + VectorOfMotionStates stillNeedChange = _physicsEngine->changeObjects(motionStates); + _entitySimulation->setObjectsToChange(stillNeedChange); + }); + } _entitySimulation->applyDynamicChanges(); t1 = std::chrono::high_resolution_clock::now(); - PhysicsEngine::Transaction transaction; - avatarManager->buildPhysicsTransaction(transaction); - _physicsEngine->processTransaction(transaction); - avatarManager->handleProcessedPhysicsTransaction(transaction); - myAvatar->getCharacterController()->buildPhysicsTransaction(transaction); - _physicsEngine->processTransaction(transaction); - myAvatar->getCharacterController()->handleProcessedPhysicsTransaction(transaction); - myAvatar->prepareForPhysicsSimulation(); - _physicsEngine->enableGlobalContactAddedCallback(myAvatar->isFlying()); + { + PROFILE_RANGE(simulation_physics, "Avatars"); + PhysicsEngine::Transaction transaction; + avatarManager->buildPhysicsTransaction(transaction); + _physicsEngine->processTransaction(transaction); + avatarManager->handleProcessedPhysicsTransaction(transaction); + myAvatar->getCharacterController()->buildPhysicsTransaction(transaction); + _physicsEngine->processTransaction(transaction); + myAvatar->getCharacterController()->handleProcessedPhysicsTransaction(transaction); + myAvatar->prepareForPhysicsSimulation(); + _physicsEngine->enableGlobalContactAddedCallback(myAvatar->isFlying()); + } - _physicsEngine->forEachDynamic([&](EntityDynamicPointer dynamic) { - dynamic->prepareForPhysicsSimulation(); - }); + { + PROFILE_RANGE(simulation_physics, "PrepareActions"); + _physicsEngine->forEachDynamic([&](EntityDynamicPointer dynamic) { + dynamic->prepareForPhysicsSimulation(); + }); + } } auto t2 = std::chrono::high_resolution_clock::now(); { @@ -6414,22 +6397,9 @@ void Application::update(float deltaTime) { updateLOD(deltaTime); - if (!_loginDialogOverlayID.isNull()) { - _loginStateManager.update(getMyAvatar()->getDominantHand(), _loginDialogOverlayID); - updateLoginDialogOverlayPosition(); - } - - // TODO: break these out into distinct perfTimers when they prove interesting - { - PROFILE_RANGE(app, "PickManager"); - PerformanceTimer perfTimer("pickManager"); - DependencyManager::get()->update(); - } - - { - PROFILE_RANGE(app, "PointerManager"); - PerformanceTimer perfTimer("pointerManager"); - DependencyManager::get()->update(); + if (!_loginDialogID.isNull()) { + _loginStateManager.update(getMyAvatar()->getDominantHand(), _loginDialogID); + updateLoginDialogPosition(); } { @@ -7195,7 +7165,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe connect(scriptEngine.data(), &ScriptEngine::finished, clipboardScriptable, &ClipboardScriptingInterface::deleteLater); scriptEngine->registerGlobalObject("Overlays", &_overlays); - qScriptRegisterMetaType(scriptEngine.data(), OverlayPropertyResultToScriptValue, OverlayPropertyResultFromScriptValue); qScriptRegisterMetaType(scriptEngine.data(), RayToOverlayIntersectionResultToScriptValue, RayToOverlayIntersectionResultFromScriptValue); @@ -7312,8 +7281,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe scriptEngine->registerGlobalObject("HifiAbout", AboutUtil::getInstance()); scriptEngine->registerGlobalObject("ResourceRequestObserver", DependencyManager::get().data()); - qScriptRegisterMetaType(scriptEngine.data(), OverlayIDtoScriptValue, OverlayIDfromScriptValue); - registerInteractiveWindowMetaType(scriptEngine.data()); auto pickScriptingInterface = DependencyManager::get(); @@ -8835,45 +8802,47 @@ void Application::setShowBulletConstraintLimits(bool value) { _physicsEngine->setShowBulletConstraintLimits(value); } -void Application::createLoginDialogOverlay() { - const glm::vec2 LOGIN_OVERLAY_DIMENSIONS{ 0.89f, 0.5f }; - const auto OVERLAY_OFFSET = glm::vec2(0.7f, -0.1f); +void Application::createLoginDialog() { + const glm::vec3 LOGIN_DIMENSIONS { 0.89f, 0.5f, 0.01f }; + const auto OFFSET = glm::vec2(0.7f, -0.1f); auto cameraPosition = _myCamera.getPosition(); auto cameraOrientation = _myCamera.getOrientation(); auto upVec = getMyAvatar()->getWorldOrientation() * Vectors::UNIT_Y; auto headLookVec = (cameraOrientation * Vectors::FRONT); // DEFAULT_DPI / tablet scale percentage - const float OVERLAY_DPI = 31.0f / (75.0f / 100.0f); - auto offset = headLookVec * OVERLAY_OFFSET.x; - auto overlayPosition = (cameraPosition + offset) + (upVec * OVERLAY_OFFSET.y); - QVariantMap overlayProperties = { - { "name", "LoginDialogOverlay" }, - { "url", OVERLAY_LOGIN_DIALOG }, - { "position", vec3toVariant(overlayPosition) }, - { "orientation", quatToVariant(cameraOrientation) }, - { "isSolid", true }, - { "grabbable", false }, - { "ignorePickIntersection", false }, - { "alpha", 1.0 }, - { "dimensions", vec2ToVariant(LOGIN_OVERLAY_DIMENSIONS)}, - { "dpi", OVERLAY_DPI }, - { "visible", true } - }; - auto& overlays = getOverlays(); - _loginDialogOverlayID = overlays.addOverlay("web3d", overlayProperties); - auto loginOverlay = std::dynamic_pointer_cast(overlays.getOverlay(_loginDialogOverlayID)); + const float DPI = 31.0f / (75.0f / 100.0f); + auto offset = headLookVec * OFFSET.x; + auto position = (cameraPosition + offset) + (upVec * OFFSET.y); + + EntityItemProperties properties; + properties.setType(EntityTypes::Web); + properties.setName("LoginDialogEntity"); + properties.setSourceUrl(LOGIN_DIALOG.toString()); + properties.setPosition(position); + properties.setRotation(cameraOrientation); + properties.setDimensions(LOGIN_DIMENSIONS); + properties.setPrimitiveMode(PrimitiveMode::SOLID); + properties.getGrab().setGrabbable(false); + properties.setIgnorePickIntersection(false); + properties.setAlpha(1.0f); + properties.setDPI(DPI); + properties.setVisible(true); + + auto entityScriptingInterface = DependencyManager::get(); + _loginDialogID = entityScriptingInterface->addEntityInternal(properties, entity::HostType::LOCAL); + auto keyboard = DependencyManager::get().data(); - if (!keyboard->getAnchorID().isNull() && !_loginDialogOverlayID.isNull()) { - const auto KEYBOARD_LOCAL_ORIENTATION = glm::quat(0.0f, 0.0, 1.0f, 0.25f); + if (!keyboard->getAnchorID().isNull() && !_loginDialogID.isNull()) { auto keyboardLocalOffset = cameraOrientation * glm::vec3(-0.4f * getMyAvatar()->getSensorToWorldScale(), -0.3f, 0.2f); - QVariantMap properties { - { "position", vec3toVariant(overlayPosition + keyboardLocalOffset) }, - { "orientation", quatToVariant(cameraOrientation * KEYBOARD_LOCAL_ORIENTATION) }, - }; - overlays.editOverlay(keyboard->getAnchorID(), properties); + + EntityItemProperties properties; + properties.setPosition(position + keyboardLocalOffset); + properties.setRotation(cameraOrientation * Quaternions::Y_180); + + entityScriptingInterface->editEntity(keyboard->getAnchorID(), properties); keyboard->setResetKeyboardPositionOnRaise(false); } - setKeyboardFocusOverlay(_loginDialogOverlayID); + setKeyboardFocusEntity(_loginDialogID); emit loginDialogFocusEnabled(); getApplicationCompositor().getReticleInterface()->setAllowMouseCapture(false); getApplicationCompositor().getReticleInterface()->setVisible(false); @@ -8882,38 +8851,43 @@ void Application::createLoginDialogOverlay() { } } -void Application::updateLoginDialogOverlayPosition() { +void Application::updateLoginDialogPosition() { const float LOOK_AWAY_THRESHOLD_ANGLE = 70.0f; - const auto OVERLAY_OFFSET = glm::vec2(0.7f, -0.1f); - auto& overlays = getOverlays(); - auto loginOverlay = std::dynamic_pointer_cast(overlays.getOverlay(_loginDialogOverlayID)); - auto overlayPositionVec = loginOverlay->getWorldPosition(); + const auto OFFSET = glm::vec2(0.7f, -0.1f); + + auto entityScriptingInterface = DependencyManager::get(); + EntityPropertyFlags desiredProperties; + desiredProperties += PROP_POSITION; + auto properties = entityScriptingInterface->getEntityProperties(_loginDialogID, desiredProperties); + auto positionVec = properties.getPosition(); auto cameraPositionVec = _myCamera.getPosition(); auto cameraOrientation = cancelOutRollAndPitch(_myCamera.getOrientation()); auto headLookVec = (cameraOrientation * Vectors::FRONT); - auto overlayToHeadVec = overlayPositionVec - cameraPositionVec; - auto pointAngle = (glm::acos(glm::dot(glm::normalize(overlayToHeadVec), glm::normalize(headLookVec))) * 180.0f / PI); + auto entityToHeadVec = positionVec - cameraPositionVec; + auto pointAngle = (glm::acos(glm::dot(glm::normalize(entityToHeadVec), glm::normalize(headLookVec))) * 180.0f / PI); auto upVec = getMyAvatar()->getWorldOrientation() * Vectors::UNIT_Y; - auto offset = headLookVec * OVERLAY_OFFSET.x; - auto newOverlayPositionVec = (cameraPositionVec + offset) + (upVec * OVERLAY_OFFSET.y); - auto newOverlayOrientation = glm::inverse(glm::quat_cast(glm::lookAt(newOverlayPositionVec, cameraPositionVec, upVec))) * Quaternions::Y_180; + auto offset = headLookVec * OFFSET.x; + auto newPositionVec = (cameraPositionVec + offset) + (upVec * OFFSET.y); - bool overlayOutOfBounds = glm::distance(overlayPositionVec, cameraPositionVec) > 1.0f; + bool outOfBounds = glm::distance(positionVec, cameraPositionVec) > 1.0f; - if (pointAngle > LOOK_AWAY_THRESHOLD_ANGLE || overlayOutOfBounds) { - QVariantMap properties { - {"position", vec3toVariant(newOverlayPositionVec)}, - {"orientation", quatToVariant(newOverlayOrientation)} - }; - overlays.editOverlay(_loginDialogOverlayID, properties); - const auto KEYBOARD_LOCAL_ORIENTATION = glm::quat(0.0f, 0.0, 1.0f, 0.25f); - auto keyboardLocalOffset = newOverlayOrientation * glm::vec3(-0.4f * getMyAvatar()->getSensorToWorldScale(), -0.3f, 0.2f); - QVariantMap keyboardProperties { - { "position", vec3toVariant(newOverlayPositionVec + keyboardLocalOffset) }, - { "orientation", quatToVariant(newOverlayOrientation * KEYBOARD_LOCAL_ORIENTATION) }, - }; - auto keyboard = DependencyManager::get().data(); - overlays.editOverlay(keyboard->getAnchorID(), keyboardProperties); + if (pointAngle > LOOK_AWAY_THRESHOLD_ANGLE || outOfBounds) { + { + EntityItemProperties properties; + properties.setPosition(newPositionVec); + properties.setRotation(cameraOrientation); + entityScriptingInterface->editEntity(_loginDialogID, properties); + } + + { + glm::vec3 keyboardLocalOffset = cameraOrientation * glm::vec3(-0.4f * getMyAvatar()->getSensorToWorldScale(), -0.3f, 0.2f); + glm::quat keyboardOrientation = cameraOrientation * glm::quat(glm::radians(glm::vec3(-30.0f, 180.0f, 0.0f))); + + EntityItemProperties properties; + properties.setPosition(newPositionVec + keyboardLocalOffset); + properties.setRotation(keyboardOrientation); + entityScriptingInterface->editEntity(DependencyManager::get()->getAnchorID(), properties); + } } } @@ -8930,10 +8904,9 @@ void Application::onDismissedLoginDialog() { loginDialogPoppedUp.set(false); auto keyboard = DependencyManager::get().data(); keyboard->setResetKeyboardPositionOnRaise(true); - if (!_loginDialogOverlayID.isNull()) { - // deleting overlay. - getOverlays().deleteOverlay(_loginDialogOverlayID); - _loginDialogOverlayID = OverlayID(); + if (!_loginDialogID.isNull()) { + DependencyManager::get()->deleteEntity(_loginDialogID); + _loginDialogID = QUuid(); _loginStateManager.tearDown(); } resumeAfterLoginDialogActionTaken(); @@ -9099,12 +9072,12 @@ void Application::updateSystemTabletMode() { } } -OverlayID Application::getTabletScreenID() const { +QUuid Application::getTabletScreenID() const { auto HMD = DependencyManager::get(); return HMD->getCurrentTabletScreenID(); } -OverlayID Application::getTabletHomeButtonID() const { +QUuid Application::getTabletHomeButtonID() const { auto HMD = DependencyManager::get(); return HMD->getCurrentHomeButtonID(); } @@ -9115,7 +9088,7 @@ QUuid Application::getTabletFrameID() const { } QVector Application::getTabletIDs() const { - // Most important overlays first. + // Most important first. QVector result; auto HMD = DependencyManager::get(); result << HMD->getCurrentTabletScreenID(); diff --git a/interface/src/Application.h b/interface/src/Application.h index 2b4902f72e..afd9f5f12f 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -301,10 +301,10 @@ public: void shareSnapshot(const QString& filename, const QUrl& href = QUrl("")); - OverlayID getTabletScreenID() const; - OverlayID getTabletHomeButtonID() const; - QUuid getTabletFrameID() const; // may be an entity or an overlay - QVector getTabletIDs() const; // In order of most important IDs first. + QUuid getTabletScreenID() const; + QUuid getTabletHomeButtonID() const; + QUuid getTabletFrameID() const; + QVector getTabletIDs() const; void setAvatarOverrideUrl(const QUrl& url, bool save); void clearAvatarOverrideUrl() { _avatarOverrideUrl = QUrl(); _saveAvatarOverrideUrl = false; } @@ -327,8 +327,8 @@ public: void setOtherAvatarsReplicaCount(int count) { DependencyManager::get()->setReplicaCount(count); } bool getLoginDialogPoppedUp() const { return _loginDialogPoppedUp; } - void createLoginDialogOverlay(); - void updateLoginDialogOverlayPosition(); + void createLoginDialog(); + void updateLoginDialogPosition(); // Check if a headset is connected bool hasRiftControllers(); @@ -442,10 +442,7 @@ public slots: void setKeyboardFocusHighlight(const glm::vec3& position, const glm::quat& rotation, const glm::vec3& dimensions); QUuid getKeyboardFocusEntity() const; // thread-safe - void setKeyboardFocusEntity(const EntityItemID& entityItemID); - - OverlayID getKeyboardFocusOverlay(); - void setKeyboardFocusOverlay(const OverlayID& overlayID); + void setKeyboardFocusEntity(const QUuid& id); void addAssetToWorldMessageClose(); @@ -536,7 +533,7 @@ private: void init(); void pauseUntilLoginDetermined(); void resumeAfterLoginDialogActionTaken(); - bool handleKeyEventForFocusedEntityOrOverlay(QEvent* event); + bool handleKeyEventForFocusedEntity(QEvent* event); bool handleFileOpenEvent(QFileOpenEvent* event); void cleanupBeforeQuit(); @@ -710,7 +707,7 @@ private: QString _previousAvatarSkeletonModel; float _previousAvatarTargetScale; CameraMode _previousCameraMode; - OverlayID _loginDialogOverlayID; + QUuid _loginDialogID; LoginStateManager _loginStateManager; quint64 _lastFaceTrackerUpdate; @@ -728,7 +725,6 @@ private: DialogsManagerScriptingInterface* _dialogsManagerScriptingInterface = new DialogsManagerScriptingInterface(); ThreadSafeValueCache _keyboardFocusedEntity; - ThreadSafeValueCache _keyboardFocusedOverlay; quint64 _lastAcceptedKeyPress = 0; bool _isForeground = true; // starts out assumed to be in foreground bool _isGLInitialized { false }; diff --git a/interface/src/InterfaceParentFinder.cpp b/interface/src/InterfaceParentFinder.cpp index b9be58f04b..33328f54cc 100644 --- a/interface/src/InterfaceParentFinder.cpp +++ b/interface/src/InterfaceParentFinder.cpp @@ -50,15 +50,6 @@ SpatiallyNestableWeakPointer InterfaceParentFinder::find(QUuid parentID, bool& s return avatarManager->getMyAvatar(); } - // search overlays - auto& overlays = qApp->getOverlays(); - auto overlay = overlays.getOverlay(parentID); - parent = std::dynamic_pointer_cast(overlay); // this will return nullptr for non-3d overlays - if (!parent.expired()) { - success = true; - return parent; - } - success = false; return parent; } diff --git a/interface/src/LoginStateManager.cpp b/interface/src/LoginStateManager.cpp index 8811303f7d..0a09d33775 100644 --- a/interface/src/LoginStateManager.cpp +++ b/interface/src/LoginStateManager.cpp @@ -170,7 +170,7 @@ void LoginStateManager::setUp() { const unsigned int leftHand = 0; QVariantMap leftPointerProperties { { "joint", "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND" }, - { "filter", PickScriptingInterface::PICK_OVERLAYS() }, + { "filter", PickScriptingInterface::PICK_LOCAL_ENTITIES() }, { "triggers", leftPointerTriggerProperties }, { "posOffset", vec3toVariant(grabPointSphereOffsetLeft + malletOffset) }, { "hover", true }, @@ -197,7 +197,7 @@ void LoginStateManager::setUp() { rightPointerTriggerProperties = QList({rtClick1, rtClick2}); QVariantMap rightPointerProperties{ { "joint", "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" }, - { "filter", PickScriptingInterface::PICK_OVERLAYS() }, + { "filter", PickScriptingInterface::PICK_LOCAL_ENTITIES() }, { "triggers", rightPointerTriggerProperties }, { "posOffset", vec3toVariant(grabPointSphereOffsetRight + malletOffset) }, { "hover", true }, @@ -212,7 +212,7 @@ void LoginStateManager::setUp() { pointers->enablePointer(_rightLoginPointerID); } -void LoginStateManager::update(const QString dominantHand, const QUuid loginOverlayID) { +void LoginStateManager::update(const QString& dominantHand, const QUuid& loginEntityID) { if (!isSetUp()) { return; } @@ -224,8 +224,8 @@ void LoginStateManager::update(const QString dominantHand, const QUuid loginOver if (pointers && raypicks) { const auto rightObjectID = raypicks->getPrevRayPickResult(_rightLoginPointerID)["objectID"].toUuid(); const auto leftObjectID = raypicks->getPrevRayPickResult(_leftLoginPointerID)["objectID"].toUuid(); - const QString leftMode = (leftObjectID.isNull() || leftObjectID != loginOverlayID) ? "" : "full"; - const QString rightMode = (rightObjectID.isNull() || rightObjectID != loginOverlayID) ? "" : "full"; + const QString leftMode = (leftObjectID.isNull() || leftObjectID != loginEntityID) ? "" : "full"; + const QString rightMode = (rightObjectID.isNull() || rightObjectID != loginEntityID) ? "" : "full"; pointers->setRenderState(_leftLoginPointerID, leftMode); pointers->setRenderState(_rightLoginPointerID, rightMode); if (_dominantHand == "left" && !leftObjectID.isNull()) { diff --git a/interface/src/LoginStateManager.h b/interface/src/LoginStateManager.h index ad25e87ee6..b898303ba6 100644 --- a/interface/src/LoginStateManager.h +++ b/interface/src/LoginStateManager.h @@ -26,7 +26,7 @@ public: void setUp(); void tearDown(); - void update(const QString dominantHand, const QUuid loginOverlayID); + void update(const QString& dominantHand, const QUuid& loginObjectID); bool isSetUp() const { return (_leftLoginPointerID > PointerEvent::INVALID_POINTER_ID) && (_rightLoginPointerID > PointerEvent::INVALID_POINTER_ID); } diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index a1826076fa..5fb9c9a0ee 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -569,7 +569,7 @@ void AvatarActionHold::lateAvatarUpdate(const AnimPose& prePhysicsRoomPose, cons } btTransform worldTrans = rigidBody->getWorldTransform(); - AnimPose worldBodyPose(1.0f, bulletToGLM(worldTrans.getRotation()), bulletToGLM(worldTrans.getOrigin())); + AnimPose worldBodyPose(glm::vec3(1), bulletToGLM(worldTrans.getRotation()), bulletToGLM(worldTrans.getOrigin())); // transform the body transform into sensor space with the prePhysics sensor-to-world matrix. // then transform it back into world uisng the postAvatarUpdate sensor-to-world matrix. diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 6fef47da8e..afb7a218f6 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -2980,7 +2980,7 @@ void MyAvatar::postUpdate(float deltaTime, const render::ScenePointer& scene) { auto animSkeleton = _skeletonModel->getRig().getAnimSkeleton(); // the rig is in the skeletonModel frame - AnimPose xform(1.0f, _skeletonModel->getRotation(), _skeletonModel->getTranslation()); + AnimPose xform(glm::vec3(1), _skeletonModel->getRotation(), _skeletonModel->getTranslation()); if (_enableDebugDrawDefaultPose && animSkeleton) { glm::vec4 gray(0.2f, 0.2f, 0.2f, 0.2f); @@ -3025,7 +3025,7 @@ void MyAvatar::postUpdate(float deltaTime, const render::ScenePointer& scene) { updateHoldActions(_prePhysicsRoomPose, postUpdateRoomPose); if (_enableDebugDrawDetailedCollision) { - AnimPose rigToWorldPose(1.0f, getWorldOrientation() * Quaternions::Y_180, getWorldPosition()); + AnimPose rigToWorldPose(glm::vec3(1.0f), getWorldOrientation() * Quaternions::Y_180, getWorldPosition()); const int NUM_DEBUG_COLORS = 8; const glm::vec4 DEBUG_COLORS[NUM_DEBUG_COLORS] = { glm::vec4(1.0f, 1.0f, 1.0f, 1.0f), @@ -4821,7 +4821,7 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat swingTwistDecomposition(hipsinWorldSpace, avatarUpWorld, resultingSwingInWorld, resultingTwistInWorld); // remove scale present from sensorToWorldMatrix - followWorldPose.scale() = 1.0f; + followWorldPose.scale() = glm::vec3(1.0f); if (isActive(Rotation)) { //use the hmd reading for the hips follow @@ -5313,3 +5313,15 @@ void MyAvatar::releaseGrab(const QUuid& grabID) { } } +void MyAvatar::sendPacket(const QUuid& entityID, const EntityItemProperties& properties) const { + auto treeRenderer = DependencyManager::get(); + EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; + if (entityTree) { + entityTree->withWriteLock([&] { + // force an update packet + EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender(); + packetSender->queueEditEntityMessage(PacketType::EntityEdit, entityTree, entityID, properties); + }); + } +} + diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 1a6833b988..984d7b297b 100755 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -138,7 +138,7 @@ class MyAvatar : public Avatar { * your avatar when rolling your HMD in degrees per second. * @property {number} userHeight=1.75 - The height of the user in sensor space. * @property {number} userEyeHeight=1.65 - The estimated height of the user's eyes in sensor space. Read-only. - * @property {Uuid} SELF_ID - UUID representing "my avatar". Only use for local-only entities and overlays in situations + * @property {Uuid} SELF_ID - UUID representing "my avatar". Only use for local-only entities in situations * where MyAvatar.sessionUUID is not available (e.g., if not connected to a domain). Note: Likely to be deprecated. * Read-only. * @property {number} walkSpeed @@ -1884,6 +1884,7 @@ private: bool didTeleport(); bool getIsAway() const { return _isAway; } void setAway(bool value); + void sendPacket(const QUuid& entityID, const EntityItemProperties& properties) const override; std::mutex _pinnedJointsMutex; std::vector _pinnedJoints; diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 253cc891ee..26d69841d0 100755 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -41,7 +41,7 @@ static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) { if (myAvatar->isJointPinned(hipsIndex)) { Transform avatarTransform = myAvatar->getTransform(); AnimPose result = AnimPose(worldToSensorMat * avatarTransform.getMatrix() * Matrices::Y_180); - result.scale() = 1.0f; + result.scale() = glm::vec3(1.0f, 1.0f, 1.0f); return result; } @@ -108,7 +108,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { Rig::ControllerParameters params; - AnimPose avatarToRigPose(1.0f, Quaternions::Y_180, glm::vec3(0.0f)); + AnimPose avatarToRigPose(glm::vec3(1.0f), Quaternions::Y_180, glm::vec3(0.0f)); glm::mat4 rigToAvatarMatrix = Matrices::Y_180; glm::mat4 avatarToWorldMatrix = createMatFromQuatAndPos(myAvatar->getWorldOrientation(), myAvatar->getWorldPosition()); @@ -127,7 +127,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { // preMult 180 is necessary to convert from avatar to rig coordinates. // postMult 180 is necessary to convert head from -z forward to z forward. glm::quat headRot = Quaternions::Y_180 * head->getFinalOrientationInLocalFrame() * Quaternions::Y_180; - params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = AnimPose(1.0f, headRot, glm::vec3(0.0f)); + params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = AnimPose(glm::vec3(1.0f), headRot, glm::vec3(0.0f)); params.primaryControllerFlags[Rig::PrimaryControllerType_Head] = 0; } diff --git a/interface/src/avatar/OtherAvatar.cpp b/interface/src/avatar/OtherAvatar.cpp index 9496336ae1..40c7c01b30 100755 --- a/interface/src/avatar/OtherAvatar.cpp +++ b/interface/src/avatar/OtherAvatar.cpp @@ -55,44 +55,47 @@ OtherAvatar::~OtherAvatar() { void OtherAvatar::removeOrb() { if (!_otherAvatarOrbMeshPlaceholderID.isNull()) { - qApp->getOverlays().deleteOverlay(_otherAvatarOrbMeshPlaceholderID); - _otherAvatarOrbMeshPlaceholderID = UNKNOWN_OVERLAY_ID; + DependencyManager::get()->deleteEntity(_otherAvatarOrbMeshPlaceholderID); + _otherAvatarOrbMeshPlaceholderID = UNKNOWN_ENTITY_ID; } } void OtherAvatar::updateOrbPosition() { - if (_otherAvatarOrbMeshPlaceholder != nullptr) { - _otherAvatarOrbMeshPlaceholder->setWorldPosition(getHead()->getPosition()); - if (_otherAvatarOrbMeshPlaceholderID.isNull()) { - _otherAvatarOrbMeshPlaceholderID = qApp->getOverlays().addOverlay(_otherAvatarOrbMeshPlaceholder); - } + if (_otherAvatarOrbMeshPlaceholderID.isNull()) { + EntityItemProperties properties; + properties.setPosition(getHead()->getPosition()); + DependencyManager::get()->editEntity(_otherAvatarOrbMeshPlaceholderID, properties); } } void OtherAvatar::createOrb() { if (_otherAvatarOrbMeshPlaceholderID.isNull()) { - _otherAvatarOrbMeshPlaceholder = std::make_shared(); - _otherAvatarOrbMeshPlaceholder->setAlpha(1.0f); - _otherAvatarOrbMeshPlaceholder->setColor(getLoadingOrbColor(_loadingStatus)); - _otherAvatarOrbMeshPlaceholder->setIsSolid(false); - _otherAvatarOrbMeshPlaceholder->setPulseMin(0.5); - _otherAvatarOrbMeshPlaceholder->setPulseMax(1.0); - _otherAvatarOrbMeshPlaceholder->setColorPulse(1.0); - _otherAvatarOrbMeshPlaceholder->setIgnorePickIntersection(true); - _otherAvatarOrbMeshPlaceholder->setDrawInFront(false); - _otherAvatarOrbMeshPlaceholderID = qApp->getOverlays().addOverlay(_otherAvatarOrbMeshPlaceholder); - // Position focus - _otherAvatarOrbMeshPlaceholder->setWorldOrientation(glm::quat(0.0f, 0.0f, 0.0f, 1.0)); - _otherAvatarOrbMeshPlaceholder->setWorldPosition(getHead()->getPosition()); - _otherAvatarOrbMeshPlaceholder->setDimensions(glm::vec3(0.5f, 0.5f, 0.5f)); - _otherAvatarOrbMeshPlaceholder->setVisible(true); + EntityItemProperties properties; + properties.setType(EntityTypes::Sphere); + properties.setAlpha(1.0f); + properties.setColor(getLoadingOrbColor(_loadingStatus)); + properties.setPrimitiveMode(PrimitiveMode::LINES); + properties.getPulse().setMin(0.5f); + properties.getPulse().setMax(1.0f); + properties.getPulse().setColorMode(PulseMode::IN_PHASE); + properties.setIgnorePickIntersection(true); + + properties.setPosition(getHead()->getPosition()); + properties.setRotation(glm::quat(0.0f, 0.0f, 0.0f, 1.0)); + properties.setDimensions(glm::vec3(0.5f, 0.5f, 0.5f)); + properties.setVisible(true); + + _otherAvatarOrbMeshPlaceholderID = DependencyManager::get()->addEntityInternal(properties, entity::HostType::LOCAL); } } void OtherAvatar::indicateLoadingStatus(LoadingStatus loadingStatus) { Avatar::indicateLoadingStatus(loadingStatus); - if (_otherAvatarOrbMeshPlaceholder) { - _otherAvatarOrbMeshPlaceholder->setColor(getLoadingOrbColor(_loadingStatus)); + + if (_otherAvatarOrbMeshPlaceholderID != UNKNOWN_ENTITY_ID) { + EntityItemProperties properties; + properties.setColor(getLoadingOrbColor(_loadingStatus)); + DependencyManager::get()->editEntity(_otherAvatarOrbMeshPlaceholderID, properties); } } @@ -525,6 +528,11 @@ void OtherAvatar::handleChangedAvatarEntityData() { } } stateItr.value().success = success; + if (success) { + stateItr.value().hash = newHash; + } else { + stateItr.value().hash = 0; + } } AvatarEntityIDs recentlyRemovedAvatarEntities = getAndClearRecentlyRemovedIDs(); diff --git a/interface/src/avatar/OtherAvatar.h b/interface/src/avatar/OtherAvatar.h index 3ecd35413f..696e122b30 100644 --- a/interface/src/avatar/OtherAvatar.h +++ b/interface/src/avatar/OtherAvatar.h @@ -16,8 +16,6 @@ #include #include "InterfaceLogging.h" -#include "ui/overlays/Overlays.h" -#include "ui/overlays/Sphere3DOverlay.h" class AvatarManager; class AvatarMotionState; @@ -76,9 +74,18 @@ protected: void onAddAttachedAvatarEntity(const QUuid& id); void onRemoveAttachedAvatarEntity(const QUuid& id); + class AvatarEntityDataHash { + public: + AvatarEntityDataHash(uint32_t h) : hash(h) {}; + uint32_t hash { 0 }; + bool success { false }; + }; + + using MapOfAvatarEntityDataHashes = QMap; + MapOfAvatarEntityDataHashes _avatarEntityDataHashes; + std::vector _attachedAvatarEntities; - std::shared_ptr _otherAvatarOrbMeshPlaceholder { nullptr }; - OverlayID _otherAvatarOrbMeshPlaceholderID { UNKNOWN_OVERLAY_ID }; + QUuid _otherAvatarOrbMeshPlaceholderID; AvatarMotionState* _motionState { nullptr }; std::vector _detailedMotionStates; int32_t _spaceIndex { -1 }; diff --git a/interface/src/devices/DdeFaceTracker.cpp b/interface/src/devices/DdeFaceTracker.cpp index ed52083d77..b9dc8326e8 100644 --- a/interface/src/devices/DdeFaceTracker.cpp +++ b/interface/src/devices/DdeFaceTracker.cpp @@ -194,7 +194,6 @@ DdeFaceTracker::DdeFaceTracker(const QHostAddress& host, quint16 serverPort, qui _calibrationCount(0), _calibrationValues(), _calibrationBillboard(NULL), - _calibrationBillboardID(UNKNOWN_OVERLAY_ID), _calibrationMessage(QString()), _isCalibrated(false) { @@ -616,10 +615,6 @@ void DdeFaceTracker::setEyeClosingThreshold(float eyeClosingThreshold) { static const int CALIBRATION_BILLBOARD_WIDTH = 300; static const int CALIBRATION_BILLBOARD_HEIGHT = 120; -static const int CALIBRATION_BILLBOARD_TOP_MARGIN = 30; -static const int CALIBRATION_BILLBOARD_LEFT_MARGIN = 30; -static const int CALIBRATION_BILLBOARD_FONT_SIZE = 16; -static const float CALIBRATION_BILLBOARD_ALPHA = 0.5f; static QString CALIBRATION_INSTRUCTION_MESSAGE = "Hold still to calibrate camera"; void DdeFaceTracker::calibrate() { @@ -634,12 +629,8 @@ void DdeFaceTracker::calibrate() { _calibrationCount = 0; _calibrationMessage = CALIBRATION_INSTRUCTION_MESSAGE + "\n\n"; + // FIXME: this overlay probably doesn't work anymore _calibrationBillboard = new TextOverlay(); - _calibrationBillboard->setTopMargin(CALIBRATION_BILLBOARD_TOP_MARGIN); - _calibrationBillboard->setLeftMargin(CALIBRATION_BILLBOARD_LEFT_MARGIN); - _calibrationBillboard->setFontSize(CALIBRATION_BILLBOARD_FONT_SIZE); - _calibrationBillboard->setText(CALIBRATION_INSTRUCTION_MESSAGE); - _calibrationBillboard->setAlpha(CALIBRATION_BILLBOARD_ALPHA); glm::vec2 viewport = qApp->getCanvasSize(); _calibrationBillboard->setX((viewport.x - CALIBRATION_BILLBOARD_WIDTH) / 2); _calibrationBillboard->setY((viewport.y - CALIBRATION_BILLBOARD_HEIGHT) / 2); @@ -659,10 +650,10 @@ void DdeFaceTracker::addCalibrationDatum() { int samplesLeft = CALIBRATION_SAMPLES - _calibrationCount; if (samplesLeft % LARGE_TICK_INTERVAL == 0) { _calibrationMessage += QString::number(samplesLeft / LARGE_TICK_INTERVAL); - _calibrationBillboard->setText(_calibrationMessage); + // FIXME: set overlay text } else if (samplesLeft % SMALL_TICK_INTERVAL == 0) { _calibrationMessage += "."; - _calibrationBillboard->setText(_calibrationMessage); + // FIXME: set overlay text } for (int i = 0; i < NUM_FACESHIFT_BLENDSHAPES; i++) { diff --git a/interface/src/devices/DdeFaceTracker.h b/interface/src/devices/DdeFaceTracker.h index 282a347dcc..0ad8d85c62 100644 --- a/interface/src/devices/DdeFaceTracker.h +++ b/interface/src/devices/DdeFaceTracker.h @@ -169,7 +169,7 @@ private: int _calibrationCount; QVector _calibrationValues; TextOverlay* _calibrationBillboard; - OverlayID _calibrationBillboardID; + QUuid _calibrationBillboardID; QString _calibrationMessage; bool _isCalibrated; void addCalibrationDatum(); diff --git a/interface/src/raypick/CollisionPick.cpp b/interface/src/raypick/CollisionPick.cpp index d30f98051e..82d75257df 100644 --- a/interface/src/raypick/CollisionPick.cpp +++ b/interface/src/raypick/CollisionPick.cpp @@ -409,10 +409,6 @@ PickResultPointer CollisionPick::getEntityIntersection(const CollisionRegion& pi return std::make_shared(pick, entityIntersections, std::vector()); } -PickResultPointer CollisionPick::getOverlayIntersection(const CollisionRegion& pick) { - return std::make_shared(pick, std::vector(), std::vector()); -} - PickResultPointer CollisionPick::getAvatarIntersection(const CollisionRegion& pick) { if (!pick.loaded) { // Cannot compute result diff --git a/interface/src/raypick/CollisionPick.h b/interface/src/raypick/CollisionPick.h index 92aa415f9e..c742c089b4 100644 --- a/interface/src/raypick/CollisionPick.h +++ b/interface/src/raypick/CollisionPick.h @@ -54,7 +54,6 @@ public: return std::make_shared(pickVariant, std::vector(), std::vector()); } PickResultPointer getEntityIntersection(const CollisionRegion& pick) override; - PickResultPointer getOverlayIntersection(const CollisionRegion& pick) override; PickResultPointer getAvatarIntersection(const CollisionRegion& pick) override; PickResultPointer getHUDIntersection(const CollisionRegion& pick) override; Transform getResultTransform() const override; diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp index 236512f2fe..aeed65fbad 100644 --- a/interface/src/raypick/LaserPointer.cpp +++ b/interface/src/raypick/LaserPointer.cpp @@ -17,6 +17,8 @@ #include "PickManager.h" #include "RayPick.h" +#include "PolyLineEntityItem.h" + LaserPointer::LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, bool hover, const PointerTriggers& triggers, bool faceAvatar, bool followNormal, float followNormalTime, bool centerEndY, bool lockEnd, bool distanceScaleEnd, bool scaleWithParent, bool enabled) : @@ -28,7 +30,7 @@ LaserPointer::LaserPointer(const QVariant& rayProps, const RenderStateMap& rende void LaserPointer::editRenderStatePath(const std::string& state, const QVariant& pathProps) { auto renderState = std::static_pointer_cast(_renderStates[state]); if (renderState) { - updateRenderStateOverlay(renderState->getPathID(), pathProps); + updateRenderState(renderState->getPathID(), pathProps); QVariant lineWidth = pathProps.toMap()["lineWidth"]; if (lineWidth.isValid()) { renderState->setLineWidth(lineWidth.toFloat()); @@ -121,48 +123,62 @@ void LaserPointer::setVisualPickResultInternal(PickResultPointer pickResult, Int } } -LaserPointer::RenderState::RenderState(const OverlayID& startID, const OverlayID& pathID, const OverlayID& endID) : +LaserPointer::RenderState::RenderState(const QUuid& startID, const QUuid& pathID, const QUuid& endID) : StartEndRenderState(startID, endID), _pathID(pathID) { - if (!_pathID.isNull()) { - _pathIgnoreRays = qApp->getOverlays().getProperty(_pathID, "ignorePickIntersection").value.toBool(); - _lineWidth = qApp->getOverlays().getProperty(_pathID, "lineWidth").value.toFloat(); + if (!getPathID().isNull()) { + auto entityScriptingInterface = DependencyManager::get(); + { + EntityPropertyFlags desiredProperties; + desiredProperties += PROP_IGNORE_PICK_INTERSECTION; + _pathIgnorePicks = entityScriptingInterface->getEntityProperties(getPathID(), desiredProperties).getIgnorePickIntersection(); + } + { + EntityPropertyFlags desiredProperties; + desiredProperties += PROP_STROKE_WIDTHS; + auto widths = entityScriptingInterface->getEntityProperties(getPathID(), desiredProperties).getStrokeWidths(); + _lineWidth = widths.length() == 0 ? PolyLineEntityItem::DEFAULT_LINE_WIDTH : widths[0]; + } } } void LaserPointer::RenderState::cleanup() { StartEndRenderState::cleanup(); - if (!_pathID.isNull()) { - qApp->getOverlays().deleteOverlay(_pathID); + if (!getPathID().isNull()) { + DependencyManager::get()->deleteEntity(getPathID()); } } void LaserPointer::RenderState::disable() { StartEndRenderState::disable(); if (!getPathID().isNull()) { - QVariantMap pathProps; - pathProps.insert("visible", false); - pathProps.insert("ignorePickIntersection", true); - qApp->getOverlays().editOverlay(getPathID(), pathProps); + EntityItemProperties properties; + properties.setVisible(false); + properties.setIgnorePickIntersection(true); + DependencyManager::get()->editEntity(getPathID(), properties); } } void LaserPointer::RenderState::update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, float parentScale, bool distanceScaleEnd, bool centerEndY, bool faceAvatar, bool followNormal, float followNormalStrength, float distance, const PickResultPointer& pickResult) { StartEndRenderState::update(origin, end, surfaceNormal, parentScale, distanceScaleEnd, centerEndY, faceAvatar, followNormal, followNormalStrength, distance, pickResult); - QVariant endVariant = vec3toVariant(end); if (!getPathID().isNull()) { - QVariantMap pathProps; - pathProps.insert("start", vec3toVariant(origin)); - pathProps.insert("end", endVariant); - pathProps.insert("visible", true); - pathProps.insert("ignorePickIntersection", doesPathIgnoreRays()); - pathProps.insert("lineWidth", getLineWidth() * parentScale); - qApp->getOverlays().editOverlay(getPathID(), pathProps); + EntityItemProperties properties; + QVector points; + points.append(glm::vec3(0.0f)); + points.append(end - origin); + properties.setPosition(origin); + properties.setLinePoints(points); + properties.setVisible(true); + properties.setIgnorePickIntersection(doesPathIgnorePicks()); + QVector widths; + widths.append(getLineWidth() * parentScale); + DependencyManager::get()->editEntity(getPathID(), properties); } } std::shared_ptr LaserPointer::buildRenderState(const QVariantMap& propMap) { + // FIXME: we have to keep using the Overlays interface here, because existing scripts use overlay properties to define pointers QUuid startID; if (propMap["start"].isValid()) { QVariantMap startMap = propMap["start"].toMap(); @@ -232,9 +248,8 @@ PointerEvent LaserPointer::buildPointerEvent(const PickedObject& target, const P glm::vec3 LaserPointer::findIntersection(const PickedObject& pickedObject, const glm::vec3& origin, const glm::vec3& direction) { switch (pickedObject.type) { case ENTITY: + case LOCAL_ENTITY: return RayPick::intersectRayWithEntityXYPlane(pickedObject.objectID, origin, direction); - case OVERLAY: - return RayPick::intersectRayWithOverlayXYPlane(pickedObject.objectID, origin, direction); default: return glm::vec3(NAN); } diff --git a/interface/src/raypick/LaserPointer.h b/interface/src/raypick/LaserPointer.h index bcd4bb607d..13d108baee 100644 --- a/interface/src/raypick/LaserPointer.h +++ b/interface/src/raypick/LaserPointer.h @@ -19,10 +19,10 @@ public: class RenderState : public StartEndRenderState { public: RenderState() {} - RenderState(const OverlayID& startID, const OverlayID& pathID, const OverlayID& endID); + RenderState(const QUuid& startID, const QUuid& pathID, const QUuid& endID); - const OverlayID& getPathID() const { return _pathID; } - const bool& doesPathIgnoreRays() const { return _pathIgnoreRays; } + const QUuid& getPathID() const { return _pathID; } + const bool& doesPathIgnorePicks() const { return _pathIgnorePicks; } void setLineWidth(float width) { _lineWidth = width; } float getLineWidth() const { return _lineWidth; } @@ -33,9 +33,9 @@ public: bool faceAvatar, bool followNormal, float followNormalStrength, float distance, const PickResultPointer& pickResult) override; private: - OverlayID _pathID; - bool _pathIgnoreRays; + QUuid _pathID; + bool _pathIgnorePicks; float _lineWidth; }; diff --git a/interface/src/raypick/LaserPointerScriptingInterface.h b/interface/src/raypick/LaserPointerScriptingInterface.h index b02213340c..425ffd7de4 100644 --- a/interface/src/raypick/LaserPointerScriptingInterface.h +++ b/interface/src/raypick/LaserPointerScriptingInterface.h @@ -112,10 +112,10 @@ public: * @function LaserPointers.setLockEndUUID * @param {number} id * @param {Uuid} itemID - * @param {boolean} isOverlay + * @param {boolean} isAvatar * @param {Mat4} [offsetMat] */ - Q_INVOKABLE void setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isOverlay, const glm::mat4& offsetMat = glm::mat4()) const { DependencyManager::get()->setLockEndUUID(uid, objectID, isOverlay, offsetMat); } + Q_INVOKABLE void setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isAvatar, const glm::mat4& offsetMat = glm::mat4()) const { DependencyManager::get()->setLockEndUUID(uid, objectID, isAvatar, offsetMat); } /**jsdoc diff --git a/interface/src/raypick/ParabolaPick.cpp b/interface/src/raypick/ParabolaPick.cpp index b93ced17c6..7a0eed96a8 100644 --- a/interface/src/raypick/ParabolaPick.cpp +++ b/interface/src/raypick/ParabolaPick.cpp @@ -10,7 +10,6 @@ #include "Application.h" #include "EntityScriptingInterface.h" #include "PickScriptingInterface.h" -#include "ui/overlays/Overlays.h" #include "avatar/AvatarManager.h" #include "scripting/HMDScriptingInterface.h" #include "DependencyManager.h" @@ -68,20 +67,15 @@ PickResultPointer ParabolaPick::getEntityIntersection(const PickParabola& pick) DependencyManager::get()->evalParabolaIntersectionVector(pick, searchFilter, getIncludeItemsAs(), getIgnoreItemsAs()); if (entityRes.intersects) { - return std::make_shared(IntersectionType::ENTITY, entityRes.entityID, entityRes.distance, entityRes.parabolicDistance, entityRes.intersection, pick, entityRes.surfaceNormal, entityRes.extraInfo); - } - } - return std::make_shared(pick.toVariantMap()); -} - -PickResultPointer ParabolaPick::getOverlayIntersection(const PickParabola& pick) { - if (glm::length2(pick.acceleration) > EPSILON && glm::length2(pick.velocity) > EPSILON) { - bool precisionPicking = !(getFilter().isCoarse() || DependencyManager::get()->getForceCoarsePicking()); - ParabolaToOverlayIntersectionResult overlayRes = - qApp->getOverlays().findParabolaIntersectionVector(pick, precisionPicking, - getIncludeItemsAs(), getIgnoreItemsAs(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable()); - if (overlayRes.intersects) { - return std::make_shared(IntersectionType::OVERLAY, overlayRes.overlayID, overlayRes.distance, overlayRes.parabolicDistance, overlayRes.intersection, pick, overlayRes.surfaceNormal, overlayRes.extraInfo); + IntersectionType type = IntersectionType::ENTITY; + if (getFilter().doesPickLocalEntities()) { + EntityPropertyFlags desiredProperties; + desiredProperties += PROP_ENTITY_HOST_TYPE; + if (DependencyManager::get()->getEntityProperties(entityRes.entityID, desiredProperties).getEntityHostType() == entity::HostType::LOCAL) { + type = IntersectionType::LOCAL_ENTITY; + } + } + return std::make_shared(type, entityRes.entityID, entityRes.distance, entityRes.parabolicDistance, entityRes.intersection, pick, entityRes.surfaceNormal, entityRes.extraInfo); } } return std::make_shared(pick.toVariantMap()); diff --git a/interface/src/raypick/ParabolaPick.h b/interface/src/raypick/ParabolaPick.h index 8cbbc79bf5..0adbb01954 100644 --- a/interface/src/raypick/ParabolaPick.h +++ b/interface/src/raypick/ParabolaPick.h @@ -12,7 +12,6 @@ #include class EntityItemID; -class OverlayID; class ParabolaPickResult : public PickResult { public: @@ -80,7 +79,6 @@ public: PickResultPointer getDefaultResult(const QVariantMap& pickVariant) const override { return std::make_shared(pickVariant); } PickResultPointer getEntityIntersection(const PickParabola& pick) override; - PickResultPointer getOverlayIntersection(const PickParabola& pick) override; PickResultPointer getAvatarIntersection(const PickParabola& pick) override; PickResultPointer getHUDIntersection(const PickParabola& pick) override; Transform getResultTransform() const override; diff --git a/interface/src/raypick/ParabolaPointer.cpp b/interface/src/raypick/ParabolaPointer.cpp index e45b11b479..389f6ed286 100644 --- a/interface/src/raypick/ParabolaPointer.cpp +++ b/interface/src/raypick/ParabolaPointer.cpp @@ -149,7 +149,7 @@ void ParabolaPointer::setVisualPickResultInternal(PickResultPointer pickResult, } } -ParabolaPointer::RenderState::RenderState(const OverlayID& startID, const OverlayID& endID, const glm::vec3& pathColor, float pathAlpha, float pathWidth, +ParabolaPointer::RenderState::RenderState(const QUuid& startID, const QUuid& endID, const glm::vec3& pathColor, float pathAlpha, float pathWidth, bool isVisibleInSecondaryCamera, bool drawInFront, bool pathEnabled) : StartEndRenderState(startID, endID) { @@ -230,6 +230,7 @@ void ParabolaPointer::RenderState::update(const glm::vec3& origin, const glm::ve } std::shared_ptr ParabolaPointer::buildRenderState(const QVariantMap& propMap) { + // FIXME: we have to keep using the Overlays interface here, because existing scripts use overlay properties to define pointers QUuid startID; if (propMap["start"].isValid()) { QVariantMap startMap = propMap["start"].toMap(); @@ -321,9 +322,8 @@ glm::vec3 ParabolaPointer::findIntersection(const PickedObject& pickedObject, co // TODO: implement switch (pickedObject.type) { case ENTITY: + case LOCAL_ENTITY: //return ParabolaPick::intersectParabolaWithEntityXYPlane(pickedObject.objectID, origin, velocity, acceleration); - case OVERLAY: - //return ParabolaPick::intersectParabolaWithOverlayXYPlane(pickedObject.objectID, origin, velocity, acceleration); default: return glm::vec3(NAN); } diff --git a/interface/src/raypick/ParabolaPointer.h b/interface/src/raypick/ParabolaPointer.h index 3e9a22db4f..d4b705a7d2 100644 --- a/interface/src/raypick/ParabolaPointer.h +++ b/interface/src/raypick/ParabolaPointer.h @@ -10,6 +10,8 @@ #include "PathPointer.h" +#include + class ParabolaPointer : public PathPointer { using Parent = PathPointer; public: @@ -79,7 +81,7 @@ public: }; RenderState() {} - RenderState(const OverlayID& startID, const OverlayID& endID, const glm::vec3& pathColor, float pathAlpha, float parentScale, + RenderState(const QUuid& startID, const QUuid& endID, const glm::vec3& pathColor, float pathAlpha, float parentScale, bool isVisibleInSecondaryCamera, bool drawInFront, bool pathEnabled); void setPathWidth(float width) { _pathWidth = width; } diff --git a/interface/src/raypick/PathPointer.cpp b/interface/src/raypick/PathPointer.cpp index ae0ce4671b..8a1675cfe1 100644 --- a/interface/src/raypick/PathPointer.cpp +++ b/interface/src/raypick/PathPointer.cpp @@ -73,10 +73,10 @@ void PathPointer::setLength(float length) { }); } -void PathPointer::setLockEndUUID(const QUuid& objectID, const bool isOverlay, const glm::mat4& offsetMat) { +void PathPointer::setLockEndUUID(const QUuid& objectID, bool isAvatar, const glm::mat4& offsetMat) { withWriteLock([&] { _lockEndObject.id = objectID; - _lockEndObject.isOverlay = isOverlay; + _lockEndObject.isAvatar = isAvatar; _lockEndObject.offsetMat = offsetMat; }); } @@ -97,12 +97,8 @@ PickResultPointer PathPointer::getVisualPickResult(const PickResultPointer& pick glm::quat rot; glm::vec3 dim; glm::vec3 registrationPoint; - if (_lockEndObject.isOverlay) { - pos = vec3FromVariant(qApp->getOverlays().getProperty(_lockEndObject.id, "position").value); - rot = quatFromVariant(qApp->getOverlays().getProperty(_lockEndObject.id, "rotation").value); - dim = vec3FromVariant(qApp->getOverlays().getProperty(_lockEndObject.id, "dimensions").value); - registrationPoint = glm::vec3(0.5f); - } else { + // TODO: use isAvatar + { EntityItemProperties props = DependencyManager::get()->getEntityProperties(_lockEndObject.id); glm::mat4 entityMat = createMatFromQuatAndPos(props.getRotation(), props.getPosition()); glm::mat4 finalPosAndRotMat = entityMat * _lockEndObject.offsetMat; @@ -117,7 +113,7 @@ PickResultPointer PathPointer::getVisualPickResult(const PickResultPointer& pick distance = glm::distance(origin, endVec); glm::vec3 normalizedDirection = glm::normalize(direction); - type = _lockEndObject.isOverlay ? IntersectionType::OVERLAY : IntersectionType::ENTITY; + type = IntersectionType::ENTITY; id = _lockEndObject.id; intersection = endVec; surfaceNormal = -normalizedDirection; @@ -126,8 +122,6 @@ PickResultPointer PathPointer::getVisualPickResult(const PickResultPointer& pick id = getPickedObjectID(pickResult); if (type == IntersectionType::ENTITY) { endVec = DependencyManager::get()->getEntityTransform(id)[3]; - } else if (type == IntersectionType::OVERLAY) { - endVec = vec3FromVariant(qApp->getOverlays().getProperty(id, "position").value); } else if (type == IntersectionType::AVATAR) { endVec = DependencyManager::get()->getAvatar(id)->getPosition(); } @@ -184,8 +178,8 @@ void PathPointer::editRenderState(const std::string& state, const QVariant& star withWriteLock([&] { auto renderState = _renderStates.find(state); if (renderState != _renderStates.end()) { - updateRenderStateOverlay(renderState->second->getStartID(), startProps); - updateRenderStateOverlay(renderState->second->getEndID(), endProps); + updateRenderState(renderState->second->getStartID(), startProps); + updateRenderState(renderState->second->getEndID(), endProps); QVariant startDim = startProps.toMap()["dimensions"]; if (startDim.isValid()) { renderState->second->setStartDim(vec3FromVariant(startDim)); @@ -204,7 +198,8 @@ void PathPointer::editRenderState(const std::string& state, const QVariant& star }); } -void PathPointer::updateRenderStateOverlay(const OverlayID& id, const QVariant& props) { +void PathPointer::updateRenderState(const QUuid& id, const QVariant& props) { + // FIXME: we have to keep using the Overlays interface here, because existing scripts use overlay properties to define pointers if (!id.isNull() && props.isValid()) { QVariantMap propMap = props.toMap(); propMap.remove("visible"); @@ -249,65 +244,79 @@ Pointer::Buttons PathPointer::getPressedButtons(const PickResultPointer& pickRes return toReturn; } -StartEndRenderState::StartEndRenderState(const OverlayID& startID, const OverlayID& endID) : +StartEndRenderState::StartEndRenderState(const QUuid& startID, const QUuid& endID) : _startID(startID), _endID(endID) { + auto entityScriptingInterface = DependencyManager::get(); if (!_startID.isNull()) { - _startDim = vec3FromVariant(qApp->getOverlays().getProperty(_startID, "dimensions").value); - _startIgnoreRays = qApp->getOverlays().getProperty(_startID, "ignorePickIntersection").value.toBool(); + EntityPropertyFlags desiredProperties; + desiredProperties += PROP_DIMENSIONS; + desiredProperties += PROP_IGNORE_PICK_INTERSECTION; + auto properties = entityScriptingInterface->getEntityProperties(_startID, desiredProperties); + _startDim = properties.getDimensions(); + _startIgnorePicks = properties.getIgnorePickIntersection(); } if (!_endID.isNull()) { - _endDim = vec3FromVariant(qApp->getOverlays().getProperty(_endID, "dimensions").value); - _endRot = quatFromVariant(qApp->getOverlays().getProperty(_endID, "rotation").value); - _endIgnoreRays = qApp->getOverlays().getProperty(_endID, "ignorePickIntersection").value.toBool(); + EntityPropertyFlags desiredProperties; + desiredProperties += PROP_DIMENSIONS; + desiredProperties += PROP_ROTATION; + desiredProperties += PROP_IGNORE_PICK_INTERSECTION; + auto properties = entityScriptingInterface->getEntityProperties(_endID, desiredProperties); + _endDim = properties.getDimensions(); + _endRot = properties.getRotation(); + _endIgnorePicks = properties.getIgnorePickIntersection(); } } void StartEndRenderState::cleanup() { + auto entityScriptingInterface = DependencyManager::get(); if (!_startID.isNull()) { - qApp->getOverlays().deleteOverlay(_startID); + entityScriptingInterface->deleteEntity(_startID); } if (!_endID.isNull()) { - qApp->getOverlays().deleteOverlay(_endID); + entityScriptingInterface->deleteEntity(_endID); } } void StartEndRenderState::disable() { + auto entityScriptingInterface = DependencyManager::get(); if (!getStartID().isNull()) { - QVariantMap startProps; - startProps.insert("visible", false); - startProps.insert("ignorePickIntersection", true); - qApp->getOverlays().editOverlay(getStartID(), startProps); + EntityItemProperties properties; + properties.setVisible(false); + properties.setIgnorePickIntersection(true); + entityScriptingInterface->editEntity(getStartID(), properties); } if (!getEndID().isNull()) { - QVariantMap endProps; - endProps.insert("visible", false); - endProps.insert("ignorePickIntersection", true); - qApp->getOverlays().editOverlay(getEndID(), endProps); + EntityItemProperties properties; + properties.setVisible(false); + properties.setIgnorePickIntersection(true); + entityScriptingInterface->editEntity(getEndID(), properties); } _enabled = false; } void StartEndRenderState::update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, float parentScale, bool distanceScaleEnd, bool centerEndY, bool faceAvatar, bool followNormal, float followNormalStrength, float distance, const PickResultPointer& pickResult) { + auto entityScriptingInterface = DependencyManager::get(); if (!getStartID().isNull()) { - QVariantMap startProps; - startProps.insert("position", vec3toVariant(origin)); - startProps.insert("visible", true); - startProps.insert("dimensions", vec3toVariant(getStartDim() * parentScale)); - startProps.insert("ignorePickIntersection", doesStartIgnoreRays()); - qApp->getOverlays().editOverlay(getStartID(), startProps); + EntityItemProperties properties; + properties.setPosition(origin); + properties.setVisible(true); + properties.setDimensions(getStartDim() * parentScale); + properties.setIgnorePickIntersection(doesStartIgnorePicks()); + entityScriptingInterface->editEntity(getStartID(), properties); } if (!getEndID().isNull()) { - QVariantMap endProps; - glm::vec3 dim = vec3FromVariant(qApp->getOverlays().getProperty(getEndID(), "dimensions").value); + EntityItemProperties properties; + EntityPropertyFlags desiredProperties; + desiredProperties += PROP_DIMENSIONS; + glm::vec3 dim; if (distanceScaleEnd) { dim = getEndDim() * glm::distance(origin, end); - endProps.insert("dimensions", vec3toVariant(dim)); } else { dim = getEndDim() * parentScale; - endProps.insert("dimensions", vec3toVariant(dim)); } + properties.setDimensions(dim); glm::quat normalQuat = Quat().lookAtSimple(Vectors::ZERO, surfaceNormal); normalQuat = normalQuat * glm::quat(glm::vec3(-M_PI_2, 0, 0)); @@ -343,11 +352,11 @@ void StartEndRenderState::update(const glm::vec3& origin, const glm::vec3& end, _avgEndRot = rotation; } } - endProps.insert("position", vec3toVariant(position)); - endProps.insert("rotation", quatToVariant(rotation)); - endProps.insert("visible", true); - endProps.insert("ignorePickIntersection", doesEndIgnoreRays()); - qApp->getOverlays().editOverlay(getEndID(), endProps); + properties.setPosition(position); + properties.setRotation(rotation); + properties.setVisible(true); + properties.setIgnorePickIntersection(doesEndIgnorePicks()); + entityScriptingInterface->editEntity(getEndID(), properties); } _enabled = true; } @@ -355,9 +364,8 @@ void StartEndRenderState::update(const glm::vec3& origin, const glm::vec3& end, glm::vec2 PathPointer::findPos2D(const PickedObject& pickedObject, const glm::vec3& origin) { switch (pickedObject.type) { case ENTITY: + case LOCAL_ENTITY: return RayPick::projectOntoEntityXYPlane(pickedObject.objectID, origin); - case OVERLAY: - return RayPick::projectOntoOverlayXYPlane(pickedObject.objectID, origin); case HUD: return DependencyManager::get()->calculatePos2DFromHUD(origin); default: diff --git a/interface/src/raypick/PathPointer.h b/interface/src/raypick/PathPointer.h index 1aa4165c87..4b0625ef0f 100644 --- a/interface/src/raypick/PathPointer.h +++ b/interface/src/raypick/PathPointer.h @@ -12,27 +12,25 @@ #include #include -#include "ui/overlays/Overlay.h" - #include #include struct LockEndObject { QUuid id { QUuid() }; - bool isOverlay { false }; + bool isAvatar { false }; glm::mat4 offsetMat { glm::mat4() }; }; class StartEndRenderState { public: StartEndRenderState() {} - StartEndRenderState(const OverlayID& startID, const OverlayID& endID); + StartEndRenderState(const QUuid& startID, const QUuid& endID); virtual ~StartEndRenderState() = default; - const OverlayID& getStartID() const { return _startID; } - const OverlayID& getEndID() const { return _endID; } - const bool& doesStartIgnoreRays() const { return _startIgnoreRays; } - const bool& doesEndIgnoreRays() const { return _endIgnoreRays; } + const QUuid& getStartID() const { return _startID; } + const QUuid& getEndID() const { return _endID; } + const bool& doesStartIgnorePicks() const { return _startIgnorePicks; } + const bool& doesEndIgnorePicks() const { return _endIgnorePicks; } void setStartDim(const glm::vec3& startDim) { _startDim = startDim; } const glm::vec3& getStartDim() const { return _startDim; } @@ -51,10 +49,10 @@ public: bool isEnabled() const { return _enabled; } protected: - OverlayID _startID; - OverlayID _endID; - bool _startIgnoreRays; - bool _endIgnoreRays; + QUuid _startID; + QUuid _endID; + bool _startIgnorePicks; + bool _endIgnorePicks; glm::vec3 _startDim; glm::vec3 _endDim; @@ -78,11 +76,11 @@ public: virtual ~PathPointer(); void setRenderState(const std::string& state) override; - // You cannot use editRenderState to change the type of any part of the pointer. You can only edit the properties of the existing overlays. + // You cannot use editRenderState to change the type of any part of the pointer. You can only edit the properties of the existing parts. void editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) override; void setLength(float length) override; - void setLockEndUUID(const QUuid& objectID, bool isOverlay, const glm::mat4& offsetMat = glm::mat4()) override; + void setLockEndUUID(const QUuid& objectID, bool isAvatar, const glm::mat4& offsetMat = glm::mat4()) override; void updateVisuals(const PickResultPointer& prevRayPickResult) override; @@ -119,7 +117,7 @@ protected: bool shouldHover(const PickResultPointer& pickResult) override { return _currentRenderState != ""; } bool shouldTrigger(const PickResultPointer& pickResult) override { return _currentRenderState != ""; } - void updateRenderStateOverlay(const OverlayID& id, const QVariant& props); + void updateRenderState(const QUuid& id, const QVariant& props); virtual void editRenderStatePath(const std::string& state, const QVariant& pathProps) = 0; PickedObject getHoveredObject(const PickResultPointer& pickResult) override; diff --git a/interface/src/raypick/PickScriptingInterface.cpp b/interface/src/raypick/PickScriptingInterface.cpp index e8f84e63fe..ce01db3a56 100644 --- a/interface/src/raypick/PickScriptingInterface.cpp +++ b/interface/src/raypick/PickScriptingInterface.cpp @@ -26,7 +26,6 @@ #include "avatar/AvatarManager.h" #include "NestableTransformNode.h" #include "avatars-renderer/AvatarTransformNode.h" -#include "ui/overlays/OverlayTransformNode.h" #include "EntityTransformNode.h" #include @@ -61,7 +60,7 @@ PickFilter getPickFilter(unsigned int filter) { * @property {boolean} [enabled=false] If this Pick should start enabled or not. Disabled Picks do not updated their pick results. * @property {number} [filter=0] The filter for this Pick to use, constructed using filter flags combined using bitwise OR. * @property {number} [maxDistance=0.0] The max distance at which this Pick will intersect. 0.0 = no max. < 0.0 is invalid. - * @property {Uuid} parentID - The ID of the parent, either an avatar, an entity, an overlay, or a pick. + * @property {Uuid} parentID - The ID of the parent, either an avatar, an entity, or a pick. * @property {number} [parentJointIndex=0] - The joint of the parent to parent to, for example, the joints on the model of an avatar. (default = 0, no joint) * @property {string} joint - If "Mouse," parents the pick to the mouse. If "Avatar," parents the pick to MyAvatar's head. Otherwise, parents to the joint of the given name on MyAvatar. * @property {Vec3} [posOffset=Vec3.ZERO] Only for Joint Ray Picks. A local joint position offset, in meters. x = upward, y = forward, z = lateral @@ -161,7 +160,7 @@ unsigned int PickScriptingInterface::createStylusPick(const QVariant& properties * @property {boolean} [enabled=false] If this Pick should start enabled or not. Disabled Picks do not updated their pick results. * @property {number} [filter=0] The filter for this Pick to use, constructed using filter flags combined using bitwise OR. * @property {number} [maxDistance=0.0] The max distance at which this Pick will intersect. 0.0 = no max. < 0.0 is invalid. - * @property {Uuid} parentID - The ID of the parent, either an avatar, an entity, an overlay, or a pick. + * @property {Uuid} parentID - The ID of the parent, either an avatar, an entity, or a pick. * @property {number} [parentJointIndex=0] - The joint of the parent to parent to, for example, the joints on the model of an avatar. (default = 0, no joint) * @property {string} joint - If "Mouse," parents the pick to the mouse. If "Avatar," parents the pick to MyAvatar's head. Otherwise, parents to the joint of the given name on MyAvatar. * @property {Vec3} [posOffset=Vec3.ZERO] Only for Joint Parabola Picks. A local joint position offset, in meters. x = upward, y = forward, z = lateral @@ -264,7 +263,7 @@ unsigned int PickScriptingInterface::createParabolaPick(const QVariant& properti * The depth is measured in world space, but will scale with the parent if defined. * @property {CollisionMask} [collisionGroup=8] - The type of object this collision pick collides as. Objects whose collision masks overlap with the pick's collision group * will be considered colliding with the pick. -* @property {Uuid} parentID - The ID of the parent, either an avatar, an entity, an overlay, or a pick. +* @property {Uuid} parentID - The ID of the parent, either an avatar, an entity, or a pick. * @property {number} [parentJointIndex=0] - The joint of the parent to parent to, for example, the joints on the model of an avatar. (default = 0, no joint) * @property {string} joint - If "Mouse," parents the pick to the mouse. If "Avatar," parents the pick to MyAvatar's head. Otherwise, parents to the joint of the given name on MyAvatar. * @property {boolean} [scaleWithParent=true] If true, the collision pick's dimensions and threshold will adjust according to the scale of the parent. @@ -415,8 +414,6 @@ void PickScriptingInterface::setParentTransform(std::shared_ptr pick, NestableType nestableType = sharedNestablePointer->getNestableType(); if (nestableType == NestableType::Avatar) { pick->parentTransform = std::make_shared(std::static_pointer_cast(sharedNestablePointer), parentJointIndex); - } else if (nestableType == NestableType::Overlay) { - pick->parentTransform = std::make_shared(std::static_pointer_cast(sharedNestablePointer), parentJointIndex); } else if (nestableType == NestableType::Entity) { pick->parentTransform = std::make_shared(std::static_pointer_cast(sharedNestablePointer), parentJointIndex); } else { diff --git a/interface/src/raypick/PickScriptingInterface.h b/interface/src/raypick/PickScriptingInterface.h index 1c7c8cc6d6..6264a3e258 100644 --- a/interface/src/raypick/PickScriptingInterface.h +++ b/interface/src/raypick/PickScriptingInterface.h @@ -47,7 +47,8 @@ * * @property {number} INTERSECTED_NONE An intersection type. Intersected nothing with the given filter flags. Read-only. * @property {number} INTERSECTED_ENTITY An intersection type. Intersected an entity. Read-only. - * @property {number} INTERSECTED_OVERLAY An intersection type. Intersected an overlay. Read-only. + * @property {number} INTERSECTED_LOCAL_ENTITY An intersection type. Intersected a local entity. + * @property {number} INTERSECTED_OVERLAY An intersection type. Intersected an entity (3D Overlays no longer exist). Read-only. * @property {number} INTERSECTED_AVATAR An intersection type. Intersected an avatar. Read-only. * @property {number} INTERSECTED_HUD An intersection type. Intersected the HUD sphere. Read-only. * @property {number} perFrameTimeBudget - The max number of usec to spend per frame updating Pick results. @@ -77,6 +78,7 @@ class PickScriptingInterface : public QObject, public Dependency { Q_PROPERTY(unsigned int INTERSECTED_NONE READ INTERSECTED_NONE CONSTANT) Q_PROPERTY(unsigned int INTERSECTED_ENTITY READ INTERSECTED_ENTITY CONSTANT) + Q_PROPERTY(unsigned int INTERSECTED_LOCAL_ENTITY READ INTERSECTED_LOCAL_ENTITY CONSTANT) Q_PROPERTY(unsigned int INTERSECTED_OVERLAY READ INTERSECTED_OVERLAY CONSTANT) Q_PROPERTY(unsigned int INTERSECTED_AVATAR READ INTERSECTED_AVATAR CONSTANT) Q_PROPERTY(unsigned int INTERSECTED_HUD READ INTERSECTED_HUD CONSTANT) @@ -212,7 +214,7 @@ public: Q_INVOKABLE void setPrecisionPicking(unsigned int uid, bool precisionPicking); /**jsdoc - * Sets a list of Entity IDs, Overlay IDs, and/or Avatar IDs to ignore during intersection. Not used by Stylus Picks. + * Sets a list of Entity IDs and/or Avatar IDs to ignore during intersection. Not used by Stylus Picks. * @function Picks.setIgnoreItems * @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}. * @param {Uuid[]} ignoreItems A list of IDs to ignore. @@ -220,7 +222,7 @@ public: Q_INVOKABLE void setIgnoreItems(unsigned int uid, const QScriptValue& ignoreItems); /**jsdoc - * Sets a list of Entity IDs, Overlay IDs, and/or Avatar IDs to include during intersection, instead of intersecting with everything. Stylus + * Sets a list of Entity IDs and/or Avatar IDs to include during intersection, instead of intersecting with everything. Stylus * Picks only intersect with objects in their include list. * @function Picks.setIncludeItems * @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}. @@ -349,7 +351,13 @@ public slots: * @function Picks.INTERSECTED_OVERLAY * @returns {number} */ - static constexpr unsigned int INTERSECTED_OVERLAY() { return IntersectionType::OVERLAY; } + static constexpr unsigned int INTERSECTED_LOCAL_ENTITY() { return IntersectionType::LOCAL_ENTITY; } + + /**jsdoc + * @function Picks.INTERSECTED_OVERLAY + * @returns {number} + */ + static constexpr unsigned int INTERSECTED_OVERLAY() { return INTERSECTED_LOCAL_ENTITY(); } /**jsdoc * @function Picks.INTERSECTED_AVATAR diff --git a/interface/src/raypick/PointerScriptingInterface.cpp b/interface/src/raypick/PointerScriptingInterface.cpp index 0009536479..19c20f0c06 100644 --- a/interface/src/raypick/PointerScriptingInterface.cpp +++ b/interface/src/raypick/PointerScriptingInterface.cpp @@ -99,7 +99,7 @@ unsigned int PointerScriptingInterface::createStylus(const QVariant& properties) } } - return DependencyManager::get()->addPointer(std::make_shared(properties, StylusPointer::buildStylusOverlay(propertyMap), hover, enabled, modelPositionOffset, + return DependencyManager::get()->addPointer(std::make_shared(properties, StylusPointer::buildStylus(propertyMap), hover, enabled, modelPositionOffset, modelRotationOffset, modelDimensions)); } @@ -116,15 +116,15 @@ unsigned int PointerScriptingInterface::createStylus(const QVariant& properties) * * @typedef {object} Pointers.RayPointerRenderState * @property {string} name When using {@link Pointers.createPointer}, the name of this render state, used by {@link Pointers.setRenderState} and {@link Pointers.editRenderState} - * @property {Overlays.OverlayProperties|QUuid} [start] When using {@link Pointers.createPointer}, an optionally defined overlay to represent the beginning of the Ray Pointer, + * @property {Overlays.OverlayProperties|QUuid} [start] When using {@link Pointers.createPointer}, an optionally defined object to represent the beginning of the Ray Pointer, * using the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a type field). - * When returned from {@link Pointers.getPointerProperties}, the ID of the created overlay if it exists, or a null ID otherwise. - * @property {Overlays.OverlayProperties|QUuid} [path] When using {@link Pointers.createPointer}, an optionally defined overlay to represent the path of the Ray Pointer, + * When returned from {@link Pointers.getPointerProperties}, the ID of the created object if it exists, or a null ID otherwise. + * @property {Overlays.OverlayProperties|QUuid} [path] When using {@link Pointers.createPointer}, an optionally defined object to represent the path of the Ray Pointer, * using the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a type field), which must be "line3d". - * When returned from {@link Pointers.getPointerProperties}, the ID of the created overlay if it exists, or a null ID otherwise. - * @property {Overlays.OverlayProperties|QUuid} [end] When using {@link Pointers.createPointer}, an optionally defined overlay to represent the end of the Ray Pointer, + * When returned from {@link Pointers.getPointerProperties}, the ID of the created object if it exists, or a null ID otherwise. + * @property {Overlays.OverlayProperties|QUuid} [end] When using {@link Pointers.createPointer}, an optionally defined object to represent the end of the Ray Pointer, * using the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a type field). - * When returned from {@link Pointers.getPointerProperties}, the ID of the created overlay if it exists, or a null ID otherwise. + * When returned from {@link Pointers.getPointerProperties}, the ID of the created object if it exists, or a null ID otherwise. */ /**jsdoc * A set of properties that can be passed to {@link Pointers.createPointer} to create a new Pointer. Contains the relevant {@link Picks.PickProperties} to define the underlying Pick. @@ -271,14 +271,14 @@ unsigned int PointerScriptingInterface::createLaserPointer(const QVariant& prope * * @typedef {object} Pointers.ParabolaPointerRenderState * @property {string} name When using {@link Pointers.createPointer}, the name of this render state, used by {@link Pointers.setRenderState} and {@link Pointers.editRenderState} -* @property {Overlays.OverlayProperties|QUuid} [start] When using {@link Pointers.createPointer}, an optionally defined overlay to represent the beginning of the Parabola Pointer, +* @property {Overlays.OverlayProperties|QUuid} [start] When using {@link Pointers.createPointer}, an optionally defined object to represent the beginning of the Parabola Pointer, * using the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a type field). -* When returned from {@link Pointers.getPointerProperties}, the ID of the created overlay if it exists, or a null ID otherwise. +* When returned from {@link Pointers.getPointerProperties}, the ID of the created object if it exists, or a null ID otherwise. * @property {Pointers.ParabolaProperties} [path] When using {@link Pointers.createPointer}, the optionally defined rendering properties of the parabolic path defined by the Parabola Pointer. * Not defined in {@link Pointers.getPointerProperties}. -* @property {Overlays.OverlayProperties|QUuid} [end] When using {@link Pointers.createPointer}, an optionally defined overlay to represent the end of the Parabola Pointer, +* @property {Overlays.OverlayProperties|QUuid} [end] When using {@link Pointers.createPointer}, an optionally defined object to represent the end of the Parabola Pointer, * using the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a type field). -* When returned from {@link Pointers.getPointerProperties}, the ID of the created overlay if it exists, or a null ID otherwise. +* When returned from {@link Pointers.getPointerProperties}, the ID of the created object if it exists, or a null ID otherwise. */ /**jsdoc * A set of properties that can be passed to {@link Pointers.createPointer} to create a new Pointer. Contains the relevant {@link Picks.PickProperties} to define the underlying Pick. diff --git a/interface/src/raypick/PointerScriptingInterface.h b/interface/src/raypick/PointerScriptingInterface.h index 49b13d6ce6..b04d15d888 100644 --- a/interface/src/raypick/PointerScriptingInterface.h +++ b/interface/src/raypick/PointerScriptingInterface.h @@ -16,7 +16,7 @@ /**jsdoc * The Pointers API lets you create and manage objects for repeatedly calculating intersections in different ways, as well as the visual representation of those objects. - * Pointers can also be configured to automatically generate {@link PointerEvent}s on {@link Entities} and {@link Overlays}. + * Pointers can also be configured to automatically generate {@link PointerEvent}s on {@link Entities}. * * @namespace Pointers * @@ -40,7 +40,7 @@ public: * @typedef {object} Pointers.Trigger * @property {Controller.Standard|Controller.Actions|function} action This can be a built-in Controller action, like Controller.Standard.LTClick, or a function that evaluates to >= 1.0 when you want to trigger button. * @property {string} button Which button to trigger. "Primary", "Secondary", "Tertiary", and "Focus" are currently supported. Only "Primary" will trigger clicks on web surfaces. If "Focus" is triggered, - * it will try to set the entity or overlay focus to the object at which the Pointer is aimed. Buttons besides the first three will still trigger events, but event.button will be "None". + * it will try to set the entity focus to the object at which the Pointer is aimed. Buttons besides the first three will still trigger events, but event.button will be "None". */ /**jsdoc @@ -154,7 +154,7 @@ public: Q_INVOKABLE void setLength(unsigned int uid, float length) const { DependencyManager::get()->setLength(uid, length); } /**jsdoc - * Sets a list of Entity IDs, Overlay IDs, and/or Avatar IDs to ignore during intersection. Not used by Stylus Pointers. + * Sets a list of Entity IDs and/or Avatar IDs to ignore during intersection. Not used by Stylus Pointers. * @function Pointers.setIgnoreItems * @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}. * @param {Uuid[]} ignoreItems A list of IDs to ignore. @@ -162,7 +162,7 @@ public: Q_INVOKABLE void setIgnoreItems(unsigned int uid, const QScriptValue& ignoreEntities) const; /**jsdoc - * Sets a list of Entity IDs, Overlay IDs, and/or Avatar IDs to include during intersection, instead of intersecting with everything. Stylus + * Sets a list of Entity IDs and/or Avatar IDs to include during intersection, instead of intersecting with everything. Stylus * Pointers only intersect with objects in their include list. * @function Pointers.setIncludeItems * @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}. @@ -172,15 +172,15 @@ public: /**jsdoc - * Lock a Pointer onto a specific object (overlay, entity, or avatar). Optionally, provide an offset in object-space, otherwise the Pointer will lock on to the center of the object. + * Lock a Pointer onto a specific object (entity or avatar). Optionally, provide an offset in object-space, otherwise the Pointer will lock on to the center of the object. * Not used by Stylus Pointers. * @function Pointers.setLockEndUUID * @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}. * @param {Uuid} objectID The ID of the object to which to lock on. - * @param {boolean} isOverlay False for entities or avatars, true for overlays + * @param {boolean} isAvatar False for entities, true for avatars * @param {Mat4} [offsetMat] The offset matrix to use if you do not want to lock on to the center of the object. */ - Q_INVOKABLE void setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isOverlay, const glm::mat4& offsetMat = glm::mat4()) const { DependencyManager::get()->setLockEndUUID(uid, objectID, isOverlay, offsetMat); } + Q_INVOKABLE void setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isAvatar, const glm::mat4& offsetMat = glm::mat4()) const { DependencyManager::get()->setLockEndUUID(uid, objectID, isAvatar, offsetMat); } /**jsdoc @@ -212,7 +212,7 @@ public: * @function Pointers.getPointerProperties * @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}. * @returns {Pointers.LaserPointerProperties|Pointers.StylusPointerProperties|Pointers.ParabolaPointerProperties} The information about the Pointer. - * Currently only includes renderStates and defaultRenderStates with associated overlay IDs. + * Currently only includes renderStates and defaultRenderStates with associated entity IDs. */ Q_INVOKABLE QVariantMap getPointerProperties(unsigned int uid) const; }; diff --git a/interface/src/raypick/RayPick.cpp b/interface/src/raypick/RayPick.cpp index d476357bab..b030a67e17 100644 --- a/interface/src/raypick/RayPick.cpp +++ b/interface/src/raypick/RayPick.cpp @@ -9,7 +9,6 @@ #include "Application.h" #include "EntityScriptingInterface.h" -#include "ui/overlays/Overlays.h" #include "avatar/AvatarManager.h" #include "scripting/HMDScriptingInterface.h" #include "DependencyManager.h" @@ -37,19 +36,15 @@ PickResultPointer RayPick::getEntityIntersection(const PickRay& pick) { DependencyManager::get()->evalRayIntersectionVector(pick, searchFilter, getIncludeItemsAs(), getIgnoreItemsAs()); if (entityRes.intersects) { - return std::make_shared(IntersectionType::ENTITY, entityRes.entityID, entityRes.distance, entityRes.intersection, pick, entityRes.surfaceNormal, entityRes.extraInfo); - } else { - return std::make_shared(pick.toVariantMap()); - } -} - -PickResultPointer RayPick::getOverlayIntersection(const PickRay& pick) { - bool precisionPicking = !(getFilter().isCoarse() || DependencyManager::get()->getForceCoarsePicking()); - RayToOverlayIntersectionResult overlayRes = - qApp->getOverlays().findRayIntersectionVector(pick, precisionPicking, - getIncludeItemsAs(), getIgnoreItemsAs(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable()); - if (overlayRes.intersects) { - return std::make_shared(IntersectionType::OVERLAY, overlayRes.overlayID, overlayRes.distance, overlayRes.intersection, pick, overlayRes.surfaceNormal, overlayRes.extraInfo); + IntersectionType type = IntersectionType::ENTITY; + if (getFilter().doesPickLocalEntities()) { + EntityPropertyFlags desiredProperties; + desiredProperties += PROP_ENTITY_HOST_TYPE; + if (DependencyManager::get()->getEntityProperties(entityRes.entityID, desiredProperties).getEntityHostType() == entity::HostType::LOCAL) { + type = IntersectionType::LOCAL_ENTITY; + } + } + return std::make_shared(type, entityRes.entityID, entityRes.distance, entityRes.intersection, pick, entityRes.surfaceNormal, entityRes.extraInfo); } else { return std::make_shared(pick.toVariantMap()); } @@ -89,12 +84,6 @@ glm::vec3 RayPick::intersectRayWithXYPlane(const glm::vec3& origin, const glm::v return origin + t * direction; } -glm::vec3 RayPick::intersectRayWithOverlayXYPlane(const QUuid& overlayID, const glm::vec3& origin, const glm::vec3& direction) { - glm::vec3 position = vec3FromVariant(qApp->getOverlays().getProperty(overlayID, "position").value); - glm::quat rotation = quatFromVariant(qApp->getOverlays().getProperty(overlayID, "rotation").value); - return intersectRayWithXYPlane(origin, direction, position, rotation, ENTITY_ITEM_DEFAULT_REGISTRATION_POINT); -} - glm::vec3 RayPick::intersectRayWithEntityXYPlane(const QUuid& entityID, const glm::vec3& origin, const glm::vec3& direction) { auto props = DependencyManager::get()->getEntityProperties(entityID); return intersectRayWithXYPlane(origin, direction, props.getPosition(), props.getRotation(), props.getRegistrationPoint()); @@ -113,15 +102,12 @@ glm::vec2 RayPick::projectOntoXYPlane(const glm::vec3& worldPos, const glm::vec3 return pos2D; } -glm::vec2 RayPick::projectOntoOverlayXYPlane(const QUuid& overlayID, const glm::vec3& worldPos, bool unNormalized) { - glm::vec3 position = vec3FromVariant(qApp->getOverlays().getProperty(overlayID, "position").value); - glm::quat rotation = quatFromVariant(qApp->getOverlays().getProperty(overlayID, "rotation").value); - glm::vec3 dimensions = glm::vec3(vec2FromVariant(qApp->getOverlays().getProperty(overlayID, "dimensions").value), 0.01f); - - return projectOntoXYPlane(worldPos, position, rotation, dimensions, ENTITY_ITEM_DEFAULT_REGISTRATION_POINT, unNormalized); -} - glm::vec2 RayPick::projectOntoEntityXYPlane(const QUuid& entityID, const glm::vec3& worldPos, bool unNormalized) { - auto props = DependencyManager::get()->getEntityProperties(entityID); + EntityPropertyFlags desiredProperties; + desiredProperties += PROP_POSITION; + desiredProperties += PROP_ROTATION; + desiredProperties += PROP_DIMENSIONS; + desiredProperties += PROP_REGISTRATION_POINT; + auto props = DependencyManager::get()->getEntityProperties(entityID, desiredProperties); return projectOntoXYPlane(worldPos, props.getPosition(), props.getRotation(), props.getDimensions(), props.getRegistrationPoint(), unNormalized); } diff --git a/interface/src/raypick/RayPick.h b/interface/src/raypick/RayPick.h index ba67ceebb1..a781795e55 100644 --- a/interface/src/raypick/RayPick.h +++ b/interface/src/raypick/RayPick.h @@ -12,7 +12,6 @@ #include class EntityItemID; -class OverlayID; class RayPickResult : public PickResult { public: @@ -78,16 +77,13 @@ public: PickResultPointer getDefaultResult(const QVariantMap& pickVariant) const override { return std::make_shared(pickVariant); } PickResultPointer getEntityIntersection(const PickRay& pick) override; - PickResultPointer getOverlayIntersection(const PickRay& pick) override; PickResultPointer getAvatarIntersection(const PickRay& pick) override; PickResultPointer getHUDIntersection(const PickRay& pick) override; Transform getResultTransform() const override; // These are helper functions for projecting and intersecting rays static glm::vec3 intersectRayWithEntityXYPlane(const QUuid& entityID, const glm::vec3& origin, const glm::vec3& direction); - static glm::vec3 intersectRayWithOverlayXYPlane(const QUuid& overlayID, const glm::vec3& origin, const glm::vec3& direction); static glm::vec2 projectOntoEntityXYPlane(const QUuid& entityID, const glm::vec3& worldPos, bool unNormalized = true); - static glm::vec2 projectOntoOverlayXYPlane(const QUuid& overlayID, const glm::vec3& worldPos, bool unNormalized = true); private: static glm::vec3 intersectRayWithXYPlane(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& point, const glm::quat& rotation, const glm::vec3& registration); diff --git a/interface/src/raypick/RayPickScriptingInterface.h b/interface/src/raypick/RayPickScriptingInterface.h index 2311cd21c8..64b21ea055 100644 --- a/interface/src/raypick/RayPickScriptingInterface.h +++ b/interface/src/raypick/RayPickScriptingInterface.h @@ -54,6 +54,7 @@ class RayPickScriptingInterface : public QObject, public Dependency { Q_PROPERTY(unsigned int PICK_ALL_INTERSECTIONS READ PICK_ALL_INTERSECTIONS CONSTANT) Q_PROPERTY(unsigned int INTERSECTED_NONE READ INTERSECTED_NONE CONSTANT) Q_PROPERTY(unsigned int INTERSECTED_ENTITY READ INTERSECTED_ENTITY CONSTANT) + Q_PROPERTY(unsigned int INTERSECTED_LOCAL_ENTITY READ INTERSECTED_LOCAL_ENTITY CONSTANT) Q_PROPERTY(unsigned int INTERSECTED_OVERLAY READ INTERSECTED_OVERLAY CONSTANT) Q_PROPERTY(unsigned int INTERSECTED_AVATAR READ INTERSECTED_AVATAR CONSTANT) Q_PROPERTY(unsigned int INTERSECTED_HUD READ INTERSECTED_HUD CONSTANT) @@ -203,7 +204,13 @@ public slots: * @function RayPick.INTERSECTED_OVERLAY * @returns {number} */ - static unsigned int INTERSECTED_OVERLAY() { return PickScriptingInterface::INTERSECTED_OVERLAY(); } + static unsigned int INTERSECTED_LOCAL_ENTITY() { return PickScriptingInterface::INTERSECTED_LOCAL_ENTITY(); } + + /**jsdoc + * @function RayPick.INTERSECTED_OVERLAY + * @returns {number} + */ + static unsigned int INTERSECTED_OVERLAY() { return PickScriptingInterface::INTERSECTED_LOCAL_ENTITY(); } /**jsdoc * @function RayPick.INTERSECTED_AVATAR diff --git a/interface/src/raypick/StylusPick.cpp b/interface/src/raypick/StylusPick.cpp index 0a76180be8..0e95959566 100644 --- a/interface/src/raypick/StylusPick.cpp +++ b/interface/src/raypick/StylusPick.cpp @@ -11,8 +11,6 @@ #include -#include "ui/overlays/Base3DOverlay.h" - #include "Application.h" #include #include "avatar/AvatarManager.h" @@ -20,6 +18,8 @@ #include #include +#include + using namespace bilateral; float StylusPick::WEB_STYLUS_LENGTH = 0.2f; @@ -148,7 +148,7 @@ PickResultPointer StylusPick::getEntityIntersection(const StylusTip& pick) { continue; } - if (!entity->getVisible() && !getFilter().doesPickInvisible()) { + if (!EntityTreeElement::checkFilterSettings(entity, getFilter())) { continue; } @@ -161,47 +161,15 @@ PickResultPointer StylusPick::getEntityIntersection(const StylusTip& pick) { glm::vec2 pos2D = RayPick::projectOntoEntityXYPlane(target, intersection, false); if (pos2D == glm::clamp(pos2D, glm::vec2(0), glm::vec2(1))) { - results.push_back(StylusPickResult(IntersectionType::ENTITY, target, distance, intersection, pick, normal)); - } - } - - StylusPickResult nearestTarget(pick.toVariantMap()); - for (const auto& result : results) { - if (result.distance < nearestTarget.distance) { - nearestTarget = result; - } - } - return std::make_shared(nearestTarget); -} - -PickResultPointer StylusPick::getOverlayIntersection(const StylusTip& pick) { - std::vector results; - for (const auto& target : getIncludeItems()) { - if (target.isNull()) { - continue; - } - - auto overlay = qApp->getOverlays().getOverlay(target); - // Don't interact with non-3D or invalid overlays - if (!overlay || !overlay->is3D()) { - continue; - } - - if (!overlay->getVisible() && !getFilter().doesPickInvisible()) { - continue; - } - - auto overlay3D = std::static_pointer_cast(overlay); - const auto overlayRotation = overlay3D->getWorldOrientation(); - const auto overlayPosition = overlay3D->getWorldPosition(); - - glm::vec3 normal = overlayRotation * Vectors::UNIT_Z; - float distance = glm::dot(pick.position - overlayPosition, normal); - glm::vec3 intersection = pick.position - (normal * distance); - - glm::vec2 pos2D = RayPick::projectOntoOverlayXYPlane(target, intersection, false); - if (pos2D == glm::clamp(pos2D, glm::vec2(0), glm::vec2(1))) { - results.push_back(StylusPickResult(IntersectionType::OVERLAY, target, distance, intersection, pick, normal)); + IntersectionType type = IntersectionType::ENTITY; + if (getFilter().doesPickLocalEntities()) { + EntityPropertyFlags desiredProperties; + desiredProperties += PROP_ENTITY_HOST_TYPE; + if (DependencyManager::get()->getEntityProperties(target, desiredProperties).getEntityHostType() == entity::HostType::LOCAL) { + type = IntersectionType::LOCAL_ENTITY; + } + } + results.push_back(StylusPickResult(type, target, distance, intersection, pick, normal)); } } diff --git a/interface/src/raypick/StylusPick.h b/interface/src/raypick/StylusPick.h index 14821c0ce5..9d5dc10b67 100644 --- a/interface/src/raypick/StylusPick.h +++ b/interface/src/raypick/StylusPick.h @@ -63,7 +63,6 @@ public: StylusTip getMathematicalPick() const override; PickResultPointer getDefaultResult(const QVariantMap& pickVariant) const override; PickResultPointer getEntityIntersection(const StylusTip& pick) override; - PickResultPointer getOverlayIntersection(const StylusTip& pick) override; PickResultPointer getAvatarIntersection(const StylusTip& pick) override; PickResultPointer getHUDIntersection(const StylusTip& pick) override; Transform getResultTransform() const override; diff --git a/interface/src/raypick/StylusPointer.cpp b/interface/src/raypick/StylusPointer.cpp index 867f896763..3cdcb9c3a5 100644 --- a/interface/src/raypick/StylusPointer.cpp +++ b/interface/src/raypick/StylusPointer.cpp @@ -27,10 +27,10 @@ static const float TOUCH_HYSTERESIS = 0.001f; static const QString DEFAULT_STYLUS_MODEL_URL = PathUtils::resourcesUrl() + "/meshes/tablet-stylus-fat.fbx"; -StylusPointer::StylusPointer(const QVariant& props, const OverlayID& stylusOverlay, bool hover, bool enabled, +StylusPointer::StylusPointer(const QVariant& props, const QUuid& stylus, bool hover, bool enabled, const glm::vec3& modelPositionOffset, const glm::quat& modelRotationOffset, const glm::vec3& modelDimensions) : Pointer(DependencyManager::get()->createStylusPick(props), enabled, hover), - _stylusOverlay(stylusOverlay), + _stylus(stylus), _modelPositionOffset(modelPositionOffset), _modelDimensions(modelDimensions), _modelRotationOffset(modelRotationOffset) @@ -38,13 +38,14 @@ StylusPointer::StylusPointer(const QVariant& props, const OverlayID& stylusOverl } StylusPointer::~StylusPointer() { - if (!_stylusOverlay.isNull()) { - qApp->getOverlays().deleteOverlay(_stylusOverlay); + if (!_stylus.isNull()) { + DependencyManager::get()->deleteEntity(_stylus); } } -OverlayID StylusPointer::buildStylusOverlay(const QVariantMap& properties) { - QVariantMap overlayProperties; +QUuid StylusPointer::buildStylus(const QVariantMap& properties) { + // FIXME: we have to keep using the Overlays interface here, because existing scripts use overlay properties to define pointers + QVariantMap propertiesMap; QString modelUrl = DEFAULT_STYLUS_MODEL_URL; @@ -56,15 +57,15 @@ OverlayID StylusPointer::buildStylusOverlay(const QVariantMap& properties) { } } // TODO: make these configurable per pointer - overlayProperties["name"] = "stylus"; - overlayProperties["url"] = modelUrl; - overlayProperties["loadPriority"] = 10.0f; - overlayProperties["solid"] = true; - overlayProperties["visible"] = false; - overlayProperties["ignorePickIntersection"] = true; - overlayProperties["drawInFront"] = false; + propertiesMap["name"] = "stylus"; + propertiesMap["url"] = modelUrl; + propertiesMap["loadPriority"] = 10.0f; + propertiesMap["solid"] = true; + propertiesMap["visible"] = false; + propertiesMap["ignorePickIntersection"] = true; + propertiesMap["drawInFront"] = false; - return qApp->getOverlays().addOverlay("model", overlayProperties); + return qApp->getOverlays().addOverlay("model", propertiesMap); } void StylusPointer::updateVisuals(const PickResultPointer& pickResult) { @@ -83,25 +84,25 @@ void StylusPointer::updateVisuals(const PickResultPointer& pickResult) { } void StylusPointer::show(const StylusTip& tip) { - if (!_stylusOverlay.isNull()) { - QVariantMap props; + if (!_stylus.isNull()) { auto modelOrientation = tip.orientation * _modelRotationOffset; auto sensorToWorldScale = DependencyManager::get()->getMyAvatar()->getSensorToWorldScale(); auto modelPositionOffset = modelOrientation * (_modelPositionOffset * sensorToWorldScale); - props["position"] = vec3toVariant(tip.position + modelPositionOffset); - props["rotation"] = quatToVariant(modelOrientation); - props["dimensions"] = vec3toVariant(sensorToWorldScale * _modelDimensions); - props["visible"] = true; - qApp->getOverlays().editOverlay(_stylusOverlay, props); + EntityItemProperties properties; + properties.setPosition(tip.position + modelPositionOffset); + properties.setRotation(modelOrientation); + properties.setDimensions(sensorToWorldScale * _modelDimensions); + properties.setVisible(true); + DependencyManager::get()->editEntity(_stylus, properties); } _showing = true; } void StylusPointer::hide() { - if (!_stylusOverlay.isNull()) { - QVariantMap props; - props.insert("visible", false); - qApp->getOverlays().editOverlay(_stylusOverlay, props); + if (!_stylus.isNull()) { + EntityItemProperties properties; + properties.setVisible(false); + DependencyManager::get()->editEntity(_stylus, properties); } _showing = false; } @@ -234,9 +235,8 @@ QVariantMap StylusPointer::toVariantMap() const { glm::vec3 StylusPointer::findIntersection(const PickedObject& pickedObject, const glm::vec3& origin, const glm::vec3& direction) { switch (pickedObject.type) { case ENTITY: + case LOCAL_ENTITY: return RayPick::intersectRayWithEntityXYPlane(pickedObject.objectID, origin, direction); - case OVERLAY: - return RayPick::intersectRayWithOverlayXYPlane(pickedObject.objectID, origin, direction); default: return glm::vec3(NAN); } @@ -245,9 +245,8 @@ glm::vec3 StylusPointer::findIntersection(const PickedObject& pickedObject, cons glm::vec2 StylusPointer::findPos2D(const PickedObject& pickedObject, const glm::vec3& origin) { switch (pickedObject.type) { case ENTITY: + case LOCAL_ENTITY: return RayPick::projectOntoEntityXYPlane(pickedObject.objectID, origin); - case OVERLAY: - return RayPick::projectOntoOverlayXYPlane(pickedObject.objectID, origin); case HUD: return DependencyManager::get()->calculatePos2DFromHUD(origin); default: diff --git a/interface/src/raypick/StylusPointer.h b/interface/src/raypick/StylusPointer.h index 64e2a38bed..7d43df2379 100644 --- a/interface/src/raypick/StylusPointer.h +++ b/interface/src/raypick/StylusPointer.h @@ -12,8 +12,6 @@ #include #include -#include "ui/overlays/Overlay.h" - #include "StylusPick.h" class StylusPointer : public Pointer { @@ -21,7 +19,7 @@ class StylusPointer : public Pointer { using Ptr = std::shared_ptr; public: - StylusPointer(const QVariant& props, const OverlayID& stylusOverlay, bool hover, bool enabled, + StylusPointer(const QVariant& props, const QUuid& stylus, bool hover, bool enabled, const glm::vec3& modelPositionOffset, const glm::quat& modelRotationOffset, const glm::vec3& modelDimensions); ~StylusPointer(); @@ -36,7 +34,7 @@ public: QVariantMap toVariantMap() const override; - static OverlayID buildStylusOverlay(const QVariantMap& properties); + static QUuid buildStylus(const QVariantMap& properties); protected: PickedObject getHoveredObject(const PickResultPointer& pickResult) override; @@ -74,7 +72,7 @@ private: RenderState _renderState { EVENTS_ON }; - const OverlayID _stylusOverlay; + QUuid _stylus; static bool isWithinBounds(float distance, float min, float max, float hysteresis); static glm::vec3 findIntersection(const PickedObject& pickedObject, const glm::vec3& origin, const glm::vec3& direction); diff --git a/interface/src/scripting/HMDScriptingInterface.h b/interface/src/scripting/HMDScriptingInterface.h index f00e600858..d41e8951a2 100644 --- a/interface/src/scripting/HMDScriptingInterface.h +++ b/interface/src/scripting/HMDScriptingInterface.h @@ -54,12 +54,12 @@ class QScriptEngine; * @property {boolean} tabletContextualMode - true if the tablet has been opened in contextual mode, otherwise * false. In contextual mode, the tablet has been opened at a specific world position and orientation rather * than at a position and orientation relative to the user. Read-only. - * @property {Uuid} tabletID - The UUID of the tablet body model overlay. - * @property {Uuid} tabletScreenID - The UUID of the tablet's screen overlay. - * @property {Uuid} homeButtonID - The UUID of the tablet's "home" button overlay. - * @property {Uuid} homeButtonHighlightID - The UUID of the tablet's "home" button highlight overlay. - * @property {Uuid} miniTabletID - The UUID of the mini tablet's body model overlay. null if not in HMD mode. - * @property {Uuid} miniTabletScreenID - The UUID of the mini tablet's screen overlay. null if not in HMD mode. + * @property {Uuid} tabletID - The UUID of the tablet body model entity. + * @property {Uuid} tabletScreenID - The UUID of the tablet's screen entity. + * @property {Uuid} homeButtonID - The UUID of the tablet's "home" button entity. + * @property {Uuid} homeButtonHighlightID - The UUID of the tablet's "home" button highlight entity. + * @property {Uuid} miniTabletID - The UUID of the mini tablet's body model entity. null if not in HMD mode. + * @property {Uuid} miniTabletScreenID - The UUID of the mini tablet's screen entity. null if not in HMD mode. * @property {number} miniTabletHand - The hand that the mini tablet is displayed on: 0 for left hand, * 1 for right hand, -1 if not in HMD mode. * @property {bool} miniTabletEnabled=true - true if the mini tablet is enabled to be displayed, otherwise diff --git a/interface/src/scripting/KeyboardScriptingInterface.cpp b/interface/src/scripting/KeyboardScriptingInterface.cpp index ccf123efed..b031f2d749 100644 --- a/interface/src/scripting/KeyboardScriptingInterface.cpp +++ b/interface/src/scripting/KeyboardScriptingInterface.cpp @@ -64,6 +64,6 @@ bool KeyboardScriptingInterface::getPreferMalletsOverLasers() const { return DependencyManager::get()->getPreferMalletsOverLasers(); } -bool KeyboardScriptingInterface::containsID(OverlayID overlayID) const { - return DependencyManager::get()->containsID(overlayID); +bool KeyboardScriptingInterface::containsID(const QUuid& id) const { + return DependencyManager::get()->containsID(id); } diff --git a/interface/src/scripting/KeyboardScriptingInterface.h b/interface/src/scripting/KeyboardScriptingInterface.h index 68582d9bd9..41c5ab7644 100644 --- a/interface/src/scripting/KeyboardScriptingInterface.h +++ b/interface/src/scripting/KeyboardScriptingInterface.h @@ -16,7 +16,6 @@ #include #include "DependencyManager.h" -#include "ui/overlays/Overlay.h" /**jsdoc * The Keyboard API provides facilities to use 3D Physical keyboard. @@ -47,7 +46,7 @@ public: Q_INVOKABLE void disableRightMallet(); Q_INVOKABLE void setLeftHandLaser(unsigned int leftHandLaser); Q_INVOKABLE void setRightHandLaser(unsigned int rightHandLaser); - Q_INVOKABLE bool containsID(OverlayID overlayID) const; + Q_INVOKABLE bool containsID(const QUuid& overlayID) const; private: bool getPreferMalletsOverLasers() const; bool isRaised() const; diff --git a/interface/src/scripting/SelectionScriptingInterface.cpp b/interface/src/scripting/SelectionScriptingInterface.cpp index 4a8a72b16d..c15b5cde11 100644 --- a/interface/src/scripting/SelectionScriptingInterface.cpp +++ b/interface/src/scripting/SelectionScriptingInterface.cpp @@ -40,19 +40,6 @@ bool GameplayObjects::removeFromGameplayObjects(const EntityItemID& entityID) { return true; } -bool GameplayObjects::addToGameplayObjects(const OverlayID& overlayID) { - containsData = true; - if (std::find(_overlayIDs.begin(), _overlayIDs.end(), overlayID) == _overlayIDs.end()) { - _overlayIDs.push_back(overlayID); - } - return true; -} -bool GameplayObjects::removeFromGameplayObjects(const OverlayID& overlayID) { - _overlayIDs.erase(std::remove(_overlayIDs.begin(), _overlayIDs.end(), overlayID), _overlayIDs.end()); - return true; -} - - SelectionScriptingInterface::SelectionScriptingInterface() { } @@ -64,7 +51,6 @@ SelectionScriptingInterface::SelectionScriptingInterface() { * * "avatar" * "entity" - * "overlay" * * * @typedef {string} Selection.ItemType @@ -72,20 +58,16 @@ SelectionScriptingInterface::SelectionScriptingInterface() { bool SelectionScriptingInterface::addToSelectedItemsList(const QString& listName, const QString& itemType, const QUuid& id) { if (itemType == "avatar") { return addToGameplayObjects(listName, (QUuid)id); - } else if (itemType == "entity") { + } else if (itemType == "entity" || itemType == "overlay") { return addToGameplayObjects(listName, (EntityItemID)id); - } else if (itemType == "overlay") { - return addToGameplayObjects(listName, (OverlayID)id); } return false; } bool SelectionScriptingInterface::removeFromSelectedItemsList(const QString& listName, const QString& itemType, const QUuid& id) { if (itemType == "avatar") { return removeFromGameplayObjects(listName, (QUuid)id); - } else if (itemType == "entity") { + } else if (itemType == "entity" || itemType == "overlay") { return removeFromGameplayObjects(listName, (EntityItemID)id); - } else if (itemType == "overlay") { - return removeFromGameplayObjects(listName, (OverlayID)id); } return false; } @@ -253,12 +235,6 @@ void SelectionScriptingInterface::printList(const QString& listName) { qDebug() << j << ';'; } qDebug() << ""; - - qDebug() << "Overlay IDs:"; - for (auto k : (*currentList).getOverlayIDs()) { - qDebug() << k << ';'; - } - qDebug() << ""; } else { qDebug() << "List named " << listName << " empty"; @@ -272,7 +248,6 @@ void SelectionScriptingInterface::printList(const QString& listName) { * @typedef {object} Selection.SelectedItemsList * @property {Uuid[]} avatars - The IDs of the avatars in the selection. * @property {Uuid[]} entities - The IDs of the entities in the selection. - * @property {Uuid[]} overlays - The IDs of the overlays in the selection. */ QVariantMap SelectionScriptingInterface::getSelectedItemsList(const QString& listName) const { QReadLocker lock(&_selectionListsLock); @@ -281,7 +256,6 @@ QVariantMap SelectionScriptingInterface::getSelectedItemsList(const QString& lis if (currentList != _selectedItemsListMap.end()) { QList avatarIDs; QList entityIDs; - QList overlayIDs; if ((*currentList).getContainsData()) { if (!(*currentList).getAvatarIDs().empty()) { @@ -294,15 +268,9 @@ QVariantMap SelectionScriptingInterface::getSelectedItemsList(const QString& lis entityIDs.push_back((QUuid)j ); } } - if (!(*currentList).getOverlayIDs().empty()) { - for (auto j : (*currentList).getOverlayIDs()) { - overlayIDs.push_back((QUuid)j); - } - } } list["avatars"] = (avatarIDs); list["entities"] = (entityIDs); - list["overlays"] = (overlayIDs); return list; } @@ -379,7 +347,6 @@ void SelectionToSceneHandler::updateSceneFromSelectedList() { render::ItemIDs finalList; render::ItemID currentID; auto entityTreeRenderer = DependencyManager::get(); - auto& overlays = qApp->getOverlays(); for (QUuid& currentAvatarID : thisList.getAvatarIDs()) { auto avatar = std::static_pointer_cast(DependencyManager::get()->getAvatarBySessionID(currentAvatarID)); @@ -398,16 +365,6 @@ void SelectionToSceneHandler::updateSceneFromSelectedList() { } } - for (OverlayID& currentOverlayID : thisList.getOverlayIDs()) { - auto overlay = overlays.getOverlay(currentOverlayID); - if (overlay != NULL) { - currentID = overlay->getRenderItemID(); - if (currentID != render::Item::INVALID_ITEM_ID) { - finalList.push_back(currentID); - } - } - } - render::Selection selection(_listName.toStdString(), finalList); transaction.resetSelection(selection); diff --git a/interface/src/scripting/SelectionScriptingInterface.h b/interface/src/scripting/SelectionScriptingInterface.h index 3217efd10a..fcb4090184 100644 --- a/interface/src/scripting/SelectionScriptingInterface.h +++ b/interface/src/scripting/SelectionScriptingInterface.h @@ -19,7 +19,6 @@ #include #include "RenderableEntityItem.h" -#include "ui/overlays/Overlay.h" #include #include @@ -37,15 +36,10 @@ public: bool addToGameplayObjects(const EntityItemID& entityID); bool removeFromGameplayObjects(const EntityItemID& entityID); - std::vector getOverlayIDs() const { return _overlayIDs; } - bool addToGameplayObjects(const OverlayID& overlayID); - bool removeFromGameplayObjects(const OverlayID& overlayID); - private: bool containsData { false }; std::vector _avatarIDs; std::vector _entityIDs; - std::vector _overlayIDs; }; @@ -83,7 +77,7 @@ protected: }; /**jsdoc - * The Selection API provides a means of grouping together avatars, entities, and overlays in named lists. + * The Selection API provides a means of grouping together avatars and entities in named lists. * @namespace Selection * * @hifi-interface @@ -175,14 +169,14 @@ public: Q_INVOKABLE bool clearSelectedItemsList(const QString& listName); /**jsdoc - * Print out the list of avatars, entities, and overlays in a selection to the debug log (not the script log). + * Print out the list of avatars and entities in a selection to the debug log (not the script log). * @function Selection.printList * @param {string} listName - The name of the selection list. */ Q_INVOKABLE void printList(const QString& listName); /**jsdoc - * Get the list of avatars, entities, and overlays stored in a selection list. + * Get the list of avatars and entities stored in a selection list. * @function Selection.getSelectedItemsList * @param {string} listName - The name of the selection list. * @returns {Selection.SelectedItemsList} The content of a selection list. If the list name doesn't exist, the function diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 3579776213..e91b1d725c 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -90,7 +90,7 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { } void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) { - PROFILE_RANGE(app, __FUNCTION__); + PROFILE_RANGE(render, __FUNCTION__); if (!_uiTexture) { _uiTexture = gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda()); @@ -119,7 +119,7 @@ void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) { } void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) { - PROFILE_RANGE(app, __FUNCTION__); + PROFILE_RANGE(render, __FUNCTION__); gpu::Batch& batch = *renderArgs->_batch; auto geometryCache = DependencyManager::get(); @@ -134,9 +134,7 @@ void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) { batch.resetViewTransform(); // Render all of the Script based "HUD" aka 2D overlays. - // note: we call them HUD, as opposed to 2D, only because there are some cases of 3D HUD overlays, like the - // cameral controls for the edit.js - qApp->getOverlays().renderHUD(renderArgs); + qApp->getOverlays().render(renderArgs); } void ApplicationOverlay::renderDomainConnectionStatusBorder(RenderArgs* renderArgs) { @@ -178,7 +176,7 @@ static const auto DEFAULT_SAMPLER = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LI static const auto DEPTH_FORMAT = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH); void ApplicationOverlay::buildFramebufferObject() { - PROFILE_RANGE(app, __FUNCTION__); + PROFILE_RANGE(render, __FUNCTION__); auto uiSize = glm::uvec2(qApp->getUiSize()); if (!_overlayFramebuffer || uiSize != _overlayFramebuffer->getSize()) { diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index 24083956ff..a3a875ac40 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -57,7 +57,7 @@ void DialogsManager::showAddressBar() { if (!hmd->getShouldShowTablet()) { hmd->openTablet(); } - qApp->setKeyboardFocusOverlay(hmd->getCurrentTabletScreenID()); + qApp->setKeyboardFocusEntity(hmd->getCurrentTabletScreenID()); setAddressBarVisible(true); } @@ -70,7 +70,7 @@ void DialogsManager::hideAddressBar() { tablet->gotoHomeScreen(); hmd->closeTablet(); } - qApp->setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID); + qApp->setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); setAddressBarVisible(false); } diff --git a/interface/src/ui/Keyboard.cpp b/interface/src/ui/Keyboard.cpp index 9e9a319802..8102df6dc6 100644 --- a/interface/src/ui/Keyboard.cpp +++ b/interface/src/ui/Keyboard.cpp @@ -33,11 +33,6 @@ #include #include -#include "ui/overlays/Overlays.h" -#include "ui/overlays/Overlay.h" -#include "ui/overlays/ModelOverlay.h" -#include "ui/overlays/Cube3DOverlay.h" -#include "ui/overlays/Text3DOverlay.h" #include "avatar/AvatarManager.h" #include "avatar/MyAvatar.h" #include "avatar/AvatarManager.h" @@ -120,21 +115,21 @@ std::pair calculateKeyboardPositionAndOrientation() { float sensorToWorldScale = myAvatar->getSensorToWorldScale(); QUuid tabletID = hmd->getCurrentTabletFrameID(); if (!tabletID.isNull() && hmd->getShouldShowTablet()) { - Overlays& overlays = qApp->getOverlays(); - auto tabletOverlay = std::dynamic_pointer_cast(overlays.getOverlay(tabletID)); - if (tabletOverlay) { - auto tablet = DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system"); - bool landscapeMode = tablet->getLandscape(); - glm::vec3 keyboardOffset = landscapeMode ? KEYBOARD_TABLET_LANDSCAPE_OFFSET : KEYBOARD_TABLET_OFFSET; - glm::vec3 keyboardDegreesOffset = landscapeMode ? KEYBOARD_TABLET_LANDSCAPE_DEGREES_OFFSET : KEYBOARD_TABLET_DEGREES_OFFSET; - glm::vec3 tabletWorldPosition = tabletOverlay->getWorldPosition(); - glm::quat tabletWorldOrientation = tabletOverlay->getWorldOrientation(); - glm::vec3 scaledKeyboardTabletOffset = keyboardOffset * sensorToWorldScale; + EntityPropertyFlags desiredProperties; + desiredProperties += PROP_POSITION; + desiredProperties += PROP_ROTATION; + auto properties = DependencyManager::get()->getEntityProperties(tabletID, desiredProperties); - keyboardLocation.first = tabletWorldPosition + (tabletWorldOrientation * scaledKeyboardTabletOffset); - keyboardLocation.second = tabletWorldOrientation * glm::quat(glm::radians(keyboardDegreesOffset)); - } + auto tablet = DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system"); + bool landscapeMode = tablet->getLandscape(); + glm::vec3 keyboardOffset = landscapeMode ? KEYBOARD_TABLET_LANDSCAPE_OFFSET : KEYBOARD_TABLET_OFFSET; + glm::vec3 keyboardDegreesOffset = landscapeMode ? KEYBOARD_TABLET_LANDSCAPE_DEGREES_OFFSET : KEYBOARD_TABLET_DEGREES_OFFSET; + glm::vec3 tabletWorldPosition = properties.getPosition(); + glm::quat tabletWorldOrientation = properties.getRotation(); + glm::vec3 scaledKeyboardTabletOffset = keyboardOffset * sensorToWorldScale; + keyboardLocation.first = tabletWorldPosition + (tabletWorldOrientation * scaledKeyboardTabletOffset); + keyboardLocation.second = tabletWorldOrientation * glm::quat(glm::radians(keyboardDegreesOffset)); } else { glm::vec3 avatarWorldPosition = myAvatar->getWorldPosition(); glm::quat avatarWorldOrientation = myAvatar->getWorldOrientation(); @@ -148,31 +143,27 @@ std::pair calculateKeyboardPositionAndOrientation() { } void Key::saveDimensionsAndLocalPosition() { - Overlays& overlays = qApp->getOverlays(); - auto model3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(_keyID)); + EntityPropertyFlags desiredProperties; + desiredProperties += PROP_LOCAL_POSITION; + desiredProperties += PROP_DIMENSIONS; + auto properties = DependencyManager::get()->getEntityProperties(_keyID, desiredProperties); - if (model3DOverlay) { - _originalLocalPosition = model3DOverlay->getLocalPosition(); - _originalDimensions = model3DOverlay->getDimensions(); - _currentLocalPosition = _originalLocalPosition; - } + _originalLocalPosition = properties.getLocalPosition(); + _originalDimensions = properties.getDimensions(); + _currentLocalPosition = _originalLocalPosition; + _originalDimensionsAndLocalPositionSaved = true; } void Key::scaleKey(float sensorToWorldScale) { - Overlays& overlays = qApp->getOverlays(); - auto model3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(_keyID)); - - if (model3DOverlay) { + if (_originalDimensionsAndLocalPositionSaved) { glm::vec3 scaledLocalPosition = _originalLocalPosition * sensorToWorldScale; glm::vec3 scaledDimensions = _originalDimensions * sensorToWorldScale; _currentLocalPosition = scaledLocalPosition; - QVariantMap properties { - { "dimensions", vec3toVariant(scaledDimensions) }, - { "localPosition", vec3toVariant(scaledLocalPosition) } - }; - - overlays.editOverlay(_keyID, properties); + EntityItemProperties properties; + properties.setDimensions(scaledDimensions); + properties.setLocalPosition(scaledLocalPosition); + DependencyManager::get()->editEntity(_keyID, properties); } } @@ -241,6 +232,18 @@ void Keyboard::registerKeyboardHighlighting() { selection->enableListToScene(KEY_PRESSED_HIGHLIGHT); } +void Keyboard::disableSelectionLists() { + auto selection = DependencyManager::get(); + selection->disableListHighlight(KEY_HOVER_HIGHLIGHT); + selection->disableListHighlight(KEY_PRESSED_HIGHLIGHT); +} + +void Keyboard::enableSelectionLists() { + auto selection = DependencyManager::get(); + selection->enableListHighlight(KEY_HOVER_HIGHLIGHT, KEY_HOVERING_STYLE); + selection->enableListHighlight(KEY_PRESSED_HIGHLIGHT, KEY_PRESSING_STYLE); +} + bool Keyboard::getUse3DKeyboard() const { return _use3DKeyboardLock.resultWithReadLock([&] { return _use3DKeyboard.get(); @@ -262,21 +265,21 @@ void Keyboard::createKeyboard() { QVariantMap leftStylusProperties { { "hand", LEFT_HAND_CONTROLLER_INDEX }, - { "filter", PickScriptingInterface::PICK_OVERLAYS() }, + { "filter", PickScriptingInterface::PICK_LOCAL_ENTITIES() }, { "model", modelProperties }, { "tipOffset", vec3toVariant(MALLET_TIP_OFFSET) } }; QVariantMap rightStylusProperties { { "hand", RIGHT_HAND_CONTROLLER_INDEX }, - { "filter", PickScriptingInterface::PICK_OVERLAYS() }, + { "filter", PickScriptingInterface::PICK_LOCAL_ENTITIES() }, { "model", modelProperties }, { "tipOffset", vec3toVariant(MALLET_TIP_OFFSET) } }; - _leftHandStylus = pointerManager->addPointer(std::make_shared(leftStylusProperties, StylusPointer::buildStylusOverlay(leftStylusProperties), true, true, + _leftHandStylus = pointerManager->addPointer(std::make_shared(leftStylusProperties, StylusPointer::buildStylus(leftStylusProperties), true, true, MALLET_POSITION_OFFSET, MALLET_ROTATION_OFFSET, MALLET_MODEL_DIMENSIONS)); - _rightHandStylus = pointerManager->addPointer(std::make_shared(rightStylusProperties, StylusPointer::buildStylusOverlay(rightStylusProperties), true, true, + _rightHandStylus = pointerManager->addPointer(std::make_shared(rightStylusProperties, StylusPointer::buildStylus(rightStylusProperties), true, true, MALLET_POSITION_OFFSET, MALLET_ROTATION_OFFSET, MALLET_MODEL_DIMENSIONS)); pointerManager->disablePointer(_rightHandStylus); @@ -300,6 +303,7 @@ void Keyboard::setRaised(bool raised) { raiseKeyboardAnchor(raised); raiseKeyboard(raised); raised ? enableStylus() : disableStylus(); + raised ? enableSelectionLists() : disableSelectionLists(); withWriteLock([&] { _raised = raised; _layerIndex = 0; @@ -312,93 +316,65 @@ void Keyboard::setRaised(bool raised) { } void Keyboard::updateTextDisplay() { - Overlays& overlays = qApp->getOverlays(); - auto myAvatar = DependencyManager::get()->getMyAvatar(); + auto entityScriptingInterface = DependencyManager::get(); float sensorToWorldScale = myAvatar->getSensorToWorldScale(); - float textWidth = (float) overlays.textSize(_textDisplay.overlayID, _typedCharacters).width(); + float textWidth = (float)entityScriptingInterface->textSize(_textDisplay.entityID, _typedCharacters).width(); glm::vec3 scaledDimensions = _textDisplay.dimensions; scaledDimensions *= sensorToWorldScale; float leftMargin = (scaledDimensions.x / 2); scaledDimensions.x += textWidth; + scaledDimensions.y *= 1.25f; - - QVariantMap textDisplayProperties { - { "dimensions", vec3toVariant(scaledDimensions) }, - { "leftMargin", leftMargin }, - { "text", _typedCharacters }, - { "lineHeight", (_textDisplay.lineHeight * sensorToWorldScale) } - }; - - overlays.editOverlay(_textDisplay.overlayID, textDisplayProperties); + EntityItemProperties properties; + properties.setDimensions(scaledDimensions); + properties.setLeftMargin(leftMargin); + properties.setText(_typedCharacters); + properties.setLineHeight(_textDisplay.lineHeight * sensorToWorldScale); + entityScriptingInterface->editEntity(_textDisplay.entityID, properties); } void Keyboard::raiseKeyboardAnchor(bool raise) const { - Overlays& overlays = qApp->getOverlays(); - OverlayID anchorOverlayID = _anchor.overlayID; - auto anchorOverlay = std::dynamic_pointer_cast(overlays.getOverlay(anchorOverlayID)); - if (anchorOverlay) { + auto entityScriptingInterface = DependencyManager::get(); + + EntityItemProperties properties; + properties.setVisible(raise); + + entityScriptingInterface->editEntity(_textDisplay.entityID, properties); + entityScriptingInterface->editEntity(_backPlate.entityID, properties); + + if (_resetKeyboardPositionOnRaise) { std::pair keyboardLocation = calculateKeyboardPositionAndOrientation(); - if (_resetKeyboardPositionOnRaise) { - anchorOverlay->setWorldPosition(keyboardLocation.first); - anchorOverlay->setWorldOrientation(keyboardLocation.second); - } - anchorOverlay->setVisible(raise); - - QVariantMap textDisplayProperties { - { "visible", raise } - }; - - overlays.editOverlay(_textDisplay.overlayID, textDisplayProperties); - - auto backPlateOverlay = std::dynamic_pointer_cast(overlays.getOverlay(_backPlate.overlayID)); - - if (backPlateOverlay) { - backPlateOverlay->setVisible(raise); - } + properties.setPosition(keyboardLocation.first); + properties.setRotation(keyboardLocation.second); } + entityScriptingInterface->editEntity(_anchor.entityID, properties); } void Keyboard::scaleKeyboard(float sensorToWorldScale) { - Overlays& overlays = qApp->getOverlays(); + auto entityScriptingInterface = DependencyManager::get(); - glm::vec3 scaledDimensions = _anchor.originalDimensions * sensorToWorldScale; - auto volume3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(_anchor.overlayID)); - - if (volume3DOverlay) { - volume3DOverlay->setDimensions(scaledDimensions); + { + EntityItemProperties properties; + properties.setLocalPosition(_backPlate.localPosition * sensorToWorldScale); + properties.setDimensions(_backPlate.dimensions * sensorToWorldScale); + entityScriptingInterface->editEntity(_backPlate.entityID, properties); } - for (auto& keyboardLayer: _keyboardLayers) { + for (auto& keyboardLayer : _keyboardLayers) { for (auto iter = keyboardLayer.begin(); iter != keyboardLayer.end(); iter++) { iter.value().scaleKey(sensorToWorldScale); } } - - - glm::vec3 scaledLocalPosition = _textDisplay.localPosition * sensorToWorldScale; - glm::vec3 textDisplayScaledDimensions = _textDisplay.dimensions * sensorToWorldScale; - - QVariantMap textDisplayProperties { - { "localPosition", vec3toVariant(scaledLocalPosition) }, - { "dimensions", vec3toVariant(textDisplayScaledDimensions) }, - { "lineHeight", (_textDisplay.lineHeight * sensorToWorldScale) } - }; - - overlays.editOverlay(_textDisplay.overlayID, textDisplayProperties); - - - glm::vec3 backPlateScaledDimensions = _backPlate.dimensions * sensorToWorldScale; - glm::vec3 backPlateScaledLocalPosition = _backPlate.localPosition * sensorToWorldScale; - - QVariantMap backPlateProperties { - { "localPosition", vec3toVariant(backPlateScaledLocalPosition) }, - { "dimensions", vec3toVariant(backPlateScaledDimensions) } - }; - - overlays.editOverlay(_backPlate.overlayID, backPlateProperties); + { + EntityItemProperties properties; + properties.setLocalPosition(_textDisplay.localPosition * sensorToWorldScale); + properties.setDimensions(_textDisplay.dimensions * sensorToWorldScale); + properties.setLineHeight(_textDisplay.lineHeight * sensorToWorldScale); + entityScriptingInterface->editEntity(_textDisplay.entityID, properties); + } } void Keyboard::startLayerSwitchTimer() { @@ -419,13 +395,12 @@ void Keyboard::raiseKeyboard(bool raise) const { if (_keyboardLayers.empty()) { return; } - Overlays& overlays = qApp->getOverlays(); + const auto& keyboardLayer = _keyboardLayers[_layerIndex]; + EntityItemProperties properties; + properties.setVisible(raise); for (auto iter = keyboardLayer.begin(); iter != keyboardLayer.end(); iter++) { - auto base3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(iter.key())); - if (base3DOverlay) { - base3DOverlay->setVisible(raise); - } + DependencyManager::get()->editEntity(iter.key(), properties); } } @@ -465,19 +440,15 @@ bool Keyboard::getPreferMalletsOverLasers() const { } void Keyboard::switchToLayer(int layerIndex) { + auto entityScriptingInterface = DependencyManager::get(); if (layerIndex >= 0 && layerIndex < (int)_keyboardLayers.size()) { - Overlays& overlays = qApp->getOverlays(); + EntityPropertyFlags desiredProperties; + desiredProperties += PROP_POSITION; + desiredProperties += PROP_ROTATION; + auto oldProperties = entityScriptingInterface->getEntityProperties(_anchor.entityID, desiredProperties); - OverlayID currentAnchorOverlayID = _anchor.overlayID; - - glm::vec3 currentOverlayPosition; - glm::quat currentOverlayOrientation; - - auto currentAnchorOverlay = std::dynamic_pointer_cast(overlays.getOverlay(currentAnchorOverlayID)); - if (currentAnchorOverlay) { - currentOverlayPosition = currentAnchorOverlay->getWorldPosition(); - currentOverlayOrientation = currentAnchorOverlay->getWorldOrientation(); - } + glm::vec3 currentPosition = oldProperties.getPosition(); + glm::quat currentOrientation = oldProperties.getRotation(); raiseKeyboardAnchor(false); raiseKeyboard(false); @@ -487,19 +458,17 @@ void Keyboard::switchToLayer(int layerIndex) { raiseKeyboardAnchor(true); raiseKeyboard(true); - OverlayID newAnchorOverlayID = _anchor.overlayID; - auto newAnchorOverlay = std::dynamic_pointer_cast(overlays.getOverlay(newAnchorOverlayID)); - if (newAnchorOverlay) { - newAnchorOverlay->setWorldPosition(currentOverlayPosition); - newAnchorOverlay->setWorldOrientation(currentOverlayOrientation); - } + EntityItemProperties properties; + properties.setPosition(currentPosition); + properties.setRotation(currentOrientation); + entityScriptingInterface->editEntity(_anchor.entityID, properties); startLayerSwitchTimer(); } } -bool Keyboard::shouldProcessOverlayAndPointerEvent(const PointerEvent& event, const OverlayID& overlayID) const { - return (shouldProcessPointerEvent(event) && shouldProcessOverlay(overlayID)); +bool Keyboard::shouldProcessEntityAndPointerEvent(const PointerEvent& event) const { + return (shouldProcessPointerEvent(event) && shouldProcessEntity()); } bool Keyboard::shouldProcessPointerEvent(const PointerEvent& event) const { @@ -510,14 +479,14 @@ bool Keyboard::shouldProcessPointerEvent(const PointerEvent& event) const { return ((isStylusEvent && preferMalletsOverLasers) || (isLaserEvent && !preferMalletsOverLasers)); } -void Keyboard::handleTriggerBegin(const OverlayID& overlayID, const PointerEvent& event) { +void Keyboard::handleTriggerBegin(const QUuid& id, const PointerEvent& event) { auto buttonType = event.getButton(); - if (!shouldProcessOverlayAndPointerEvent(event, overlayID) || buttonType != PointerEvent::PrimaryButton) { + if (!shouldProcessEntityAndPointerEvent(event) || buttonType != PointerEvent::PrimaryButton) { return; } auto& keyboardLayer = _keyboardLayers[_layerIndex]; - auto search = keyboardLayer.find(overlayID); + auto search = keyboardLayer.find(id); if (search == keyboardLayer.end()) { return; @@ -533,13 +502,9 @@ void Keyboard::handleTriggerBegin(const OverlayID& overlayID, const PointerEvent auto userInputMapper = DependencyManager::get(); userInputMapper->triggerHapticPulse(PULSE_STRENGTH, PULSE_DURATION, handIndex); - Overlays& overlays = qApp->getOverlays(); - auto base3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(overlayID)); - - glm::vec3 keyWorldPosition; - if (base3DOverlay) { - keyWorldPosition = base3DOverlay->getWorldPosition(); - } + EntityPropertyFlags desiredProperties; + desiredProperties += PROP_POSITION; + glm::vec3 keyWorldPosition = DependencyManager::get()->getEntityProperties(id, desiredProperties).getPosition(); AudioInjectorOptions audioOptions; audioOptions.localOnly = true; @@ -601,7 +566,7 @@ void Keyboard::handleTriggerBegin(const OverlayID& overlayID, const PointerEvent key.startTimer(KEY_PRESS_TIMEOUT_MS); } auto selection = DependencyManager::get(); - selection->addToSelectedItemsList(KEY_PRESSED_HIGHLIGHT, "overlay", overlayID); + selection->addToSelectedItemsList(KEY_PRESSED_HIGHLIGHT, "entity", id); } } @@ -617,25 +582,23 @@ void Keyboard::setRightHandLaser(unsigned int rightHandLaser) { }); } -void Keyboard::handleTriggerEnd(const OverlayID& overlayID, const PointerEvent& event) { - if (!shouldProcessOverlayAndPointerEvent(event, overlayID)) { +void Keyboard::handleTriggerEnd(const QUuid& id, const PointerEvent& event) { + if (!shouldProcessEntityAndPointerEvent(event)) { return; } auto& keyboardLayer = _keyboardLayers[_layerIndex]; - auto search = keyboardLayer.find(overlayID); + auto search = keyboardLayer.find(id); if (search == keyboardLayer.end()) { return; } - Key& key = search.value();; - Overlays& overlays = qApp->getOverlays(); - auto base3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(overlayID)); + Key& key = search.value(); - if (base3DOverlay) { - base3DOverlay->setLocalPosition(key.getCurrentLocalPosition()); - } + EntityItemProperties properties; + properties.setLocalPosition(key.getCurrentLocalPosition()); + DependencyManager::get()->editEntity(id, properties); key.setIsPressed(false); if (key.timerFinished() && getPreferMalletsOverLasers()) { @@ -643,78 +606,79 @@ void Keyboard::handleTriggerEnd(const OverlayID& overlayID, const PointerEvent& } auto selection = DependencyManager::get(); - selection->removeFromSelectedItemsList(KEY_PRESSED_HIGHLIGHT, "overlay", overlayID); + selection->removeFromSelectedItemsList(KEY_PRESSED_HIGHLIGHT, "entity", id); } -void Keyboard::handleTriggerContinue(const OverlayID& overlayID, const PointerEvent& event) { - if (!shouldProcessOverlayAndPointerEvent(event, overlayID)) { +void Keyboard::handleTriggerContinue(const QUuid& id, const PointerEvent& event) { + if (!shouldProcessEntityAndPointerEvent(event)) { return; } auto& keyboardLayer = _keyboardLayers[_layerIndex]; - auto search = keyboardLayer.find(overlayID); + auto search = keyboardLayer.find(id); if (search == keyboardLayer.end()) { return; } Key& key = search.value(); - Overlays& overlays = qApp->getOverlays(); - if (!key.isPressed() && getPreferMalletsOverLasers()) { - auto base3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(overlayID)); + unsigned int pointerID = event.getID(); + auto pointerManager = DependencyManager::get(); + auto pickResult = pointerManager->getPrevPickResult(pointerID); + auto stylusPickResult = std::dynamic_pointer_cast(pickResult); + float distance = stylusPickResult->distance; - if (base3DOverlay) { - unsigned int pointerID = event.getID(); - auto pointerManager = DependencyManager::get(); - auto pickResult = pointerManager->getPrevPickResult(pointerID); - auto stylusPickResult = std::dynamic_pointer_cast(pickResult); - float distance = stylusPickResult->distance; + static const float PENETRATION_THRESHOLD = 0.025f; + if (distance < PENETRATION_THRESHOLD) { + static const float Z_OFFSET = 0.002f; - static const float PENATRATION_THRESHOLD = 0.025f; - if (distance < PENATRATION_THRESHOLD) { - static const float Z_OFFSET = 0.002f; - glm::quat overlayOrientation = base3DOverlay->getWorldOrientation(); - glm::vec3 overlayYAxis = overlayOrientation * Z_AXIS; - glm::vec3 overlayYOffset = overlayYAxis * Z_OFFSET; - glm::vec3 localPosition = key.getCurrentLocalPosition() - overlayYOffset; - base3DOverlay->setLocalPosition(localPosition); - key.setIsPressed(true); - } + auto entityScriptingInterface = DependencyManager::get(); + EntityPropertyFlags desiredProperties; + desiredProperties += PROP_ROTATION; + glm::quat orientation = entityScriptingInterface->getEntityProperties(id, desiredProperties).getRotation(); + glm::vec3 yAxis = orientation * Z_AXIS; + glm::vec3 yOffset = yAxis * Z_OFFSET; + glm::vec3 localPosition = key.getCurrentLocalPosition() - yOffset; + + EntityItemProperties properties; + properties.setLocalPosition(localPosition); + entityScriptingInterface->editEntity(id, properties); + key.setIsPressed(true); } } } -void Keyboard::handleHoverBegin(const OverlayID& overlayID, const PointerEvent& event) { - if (!shouldProcessOverlayAndPointerEvent(event, overlayID)) { +void Keyboard::handleHoverBegin(const QUuid& id, const PointerEvent& event) { + if (!shouldProcessEntityAndPointerEvent(event)) { return; } auto& keyboardLayer = _keyboardLayers[_layerIndex]; - auto search = keyboardLayer.find(overlayID); + auto search = keyboardLayer.find(id); if (search == keyboardLayer.end()) { return; } auto selection = DependencyManager::get(); - selection->addToSelectedItemsList(KEY_HOVER_HIGHLIGHT, "overlay", overlayID); + selection->addToSelectedItemsList(KEY_HOVER_HIGHLIGHT, "entity", id); } -void Keyboard::handleHoverEnd(const OverlayID& overlayID, const PointerEvent& event) { - if (!shouldProcessOverlayAndPointerEvent(event, overlayID)) { +void Keyboard::handleHoverEnd(const QUuid& id, const PointerEvent& event) { + if (!shouldProcessEntityAndPointerEvent(event)) { return; } auto& keyboardLayer = _keyboardLayers[_layerIndex]; - auto search = keyboardLayer.find(overlayID); + auto search = keyboardLayer.find(id); if (search == keyboardLayer.end()) { return; } auto selection = DependencyManager::get(); - selection->removeFromSelectedItemsList(KEY_HOVER_HIGHLIGHT, "overlay", overlayID); + selection->removeFromSelectedItemsList(KEY_HOVER_HIGHLIGHT, "entity", id); } void Keyboard::disableStylus() { @@ -752,7 +716,6 @@ void Keyboard::loadKeyboardFile(const QString& keyboardFile) { } clearKeyboardKeys(); - Overlays& overlays = qApp->getOverlays(); auto requestData = request->getData(); QVector includeItems; @@ -776,54 +739,60 @@ void Keyboard::loadKeyboardFile(const QString& keyboardFile) { return; } - QVariantMap anchorProperties { - { "name", "KeyboardAnchor"}, - { "isSolid", true }, - { "visible", false }, - { "grabbable", true }, - { "ignorePickIntersection", false }, - { "dimensions", anchorObject["dimensions"].toVariant() }, - { "position", anchorObject["position"].toVariant() }, - { "orientation", anchorObject["rotation"].toVariant() } - }; + auto entityScriptingInterface = DependencyManager::get(); + { + glm::vec3 dimensions = vec3FromVariant(anchorObject["dimensions"].toVariant()); - glm::vec3 dimensions = vec3FromVariant(anchorObject["dimensions"].toVariant()); + EntityItemProperties properties; + properties.setType(EntityTypes::Box); + properties.setName("KeyboardAnchor"); + properties.setVisible(false); + properties.getGrab().setGrabbable(true); + properties.setIgnorePickIntersection(false); + properties.setDimensions(dimensions); + properties.setPosition(vec3FromVariant(anchorObject["position"].toVariant())); + properties.setRotation(quatFromVariant(anchorObject["rotation"].toVariant())); - Anchor anchor; - anchor.overlayID = overlays.addOverlay("cube", anchorProperties); - anchor.originalDimensions = dimensions; - _anchor = anchor; + Anchor anchor; + anchor.entityID = entityScriptingInterface->addEntityInternal(properties, entity::HostType::LOCAL); + anchor.originalDimensions = dimensions; + _anchor = anchor; + } - QJsonObject backPlateObject = jsonObject["backPlate"].toObject(); + { + QJsonObject backPlateObject = jsonObject["backPlate"].toObject(); + glm::vec3 position = vec3FromVariant(backPlateObject["position"].toVariant()); + glm::vec3 dimensions = vec3FromVariant(backPlateObject["dimensions"].toVariant()); + glm::quat rotation = quatFromVariant(backPlateObject["rotation"].toVariant()); - QVariantMap backPlateProperties { - { "name", "backPlate"}, - { "isSolid", true }, - { "visible", true }, - { "grabbable", false }, - { "alpha", 0.0 }, - { "ignoreRayIntersection", false}, - { "dimensions", backPlateObject["dimensions"].toVariant() }, - { "position", backPlateObject["position"].toVariant() }, - { "orientation", backPlateObject["rotation"].toVariant() }, - { "parentID", _anchor.overlayID } - }; + EntityItemProperties properties; + properties.setType(EntityTypes::Box); + properties.setName("Keyboard-BackPlate"); + properties.setVisible(true); + properties.getGrab().setGrabbable(false); + properties.setAlpha(0.0f); + properties.setIgnorePickIntersection(false); + properties.setDimensions(dimensions); + properties.setPosition(position); + properties.setRotation(rotation); + properties.setParentID(_anchor.entityID); - BackPlate backPlate; - backPlate.overlayID = overlays.addOverlay("cube", backPlateProperties); - backPlate.dimensions = vec3FromVariant(backPlateObject["dimensions"].toVariant()); - backPlate.localPosition = vec3FromVariant(overlays.getProperty(backPlate.overlayID, "localPosition").value); - _backPlate = backPlate; + BackPlate backPlate; + backPlate.entityID = entityScriptingInterface->addEntityInternal(properties, entity::HostType::LOCAL); + backPlate.dimensions = dimensions; + glm::quat anchorEntityInverseWorldOrientation = glm::inverse(rotation); + glm::vec3 anchorEntityLocalTranslation = anchorEntityInverseWorldOrientation * -position; + backPlate.localPosition = (anchorEntityInverseWorldOrientation * position) + anchorEntityLocalTranslation; + _backPlate = backPlate; + } const QJsonArray& keyboardLayers = jsonObject["layers"].toArray(); int keyboardLayerCount = keyboardLayers.size(); _keyboardLayers.reserve(keyboardLayerCount); - - for (int keyboardLayerIndex = 0; keyboardLayerIndex < keyboardLayerCount; keyboardLayerIndex++) { const QJsonValue& keyboardLayer = keyboardLayers[keyboardLayerIndex].toArray(); - QHash keyboardLayerKeys; + QHash keyboardLayerKeys; foreach (const QJsonValue& keyboardKeyValue, keyboardLayer.toArray()) { QVariantMap textureMap; @@ -841,20 +810,18 @@ void Keyboard::loadKeyboardFile(const QString& keyboardFile) { QString modelUrl = keyboardKeyValue["modelURL"].toString(); QString url = (useResourcePath ? (resourcePath + modelUrl) : modelUrl); - QVariantMap properties { - { "dimensions", keyboardKeyValue["dimensions"].toVariant() }, - { "position", keyboardKeyValue["position"].toVariant() }, - { "visible", false }, - { "isSolid", true }, - { "emissive", true }, - { "parentID", _anchor.overlayID }, - { "url", url }, - { "textures", textureMap }, - { "grabbable", false }, - { "localOrientation", keyboardKeyValue["localOrientation"].toVariant() } - }; - - OverlayID overlayID = overlays.addOverlay("model", properties); + EntityItemProperties properties; + properties.setType(EntityTypes::Model); + properties.setDimensions(vec3FromVariant(keyboardKeyValue["dimensions"].toVariant())); + properties.setPosition(vec3FromVariant(keyboardKeyValue["position"].toVariant())); + properties.setVisible(false); + properties.setEmissive(true); + properties.setParentID(_anchor.entityID); + properties.setModelURL(url); + properties.setTextures(QString(QJsonDocument::fromVariant(textureMap).toJson())); + properties.getGrab().setGrabbable(false); + properties.setLocalRotation(quatFromVariant(keyboardKeyValue["localOrientation"].toVariant())); + QUuid id = entityScriptingInterface->addEntityInternal(properties, entity::HostType::LOCAL); QString keyType = keyboardKeyValue["type"].toString(); QString keyString = keyboardKeyValue["key"].toString(); @@ -869,48 +836,54 @@ void Keyboard::loadKeyboardFile(const QString& keyboardFile) { key.setSwitchToLayerIndex(switchToLayer); } } - key.setID(overlayID); + key.setID(id); key.setKeyString(keyString); key.saveDimensionsAndLocalPosition(); includeItems.append(key.getID()); - _itemsToIgnore.append(key.getID()); - keyboardLayerKeys.insert(overlayID, key); + _itemsToIgnore.insert(key.getID()); + keyboardLayerKeys.insert(id, key); } _keyboardLayers.push_back(keyboardLayerKeys); } - TextDisplay textDisplay; - QJsonObject displayTextObject = jsonObject["textDisplay"].toObject(); + { + QJsonObject displayTextObject = jsonObject["textDisplay"].toObject(); + glm::vec3 dimensions = vec3FromVariant(displayTextObject["dimensions"].toVariant()); + glm::vec3 localPosition = vec3FromVariant(displayTextObject["localPosition"].toVariant()); + float lineHeight = (float)displayTextObject["lineHeight"].toDouble(); - QVariantMap displayTextProperties { - { "dimensions", displayTextObject["dimensions"].toVariant() }, - { "localPosition", displayTextObject["localPosition"].toVariant() }, - { "localOrientation", displayTextObject["localOrientation"].toVariant() }, - { "leftMargin", displayTextObject["leftMargin"].toVariant() }, - { "rightMargin", displayTextObject["rightMargin"].toVariant() }, - { "topMargin", displayTextObject["topMargin"].toVariant() }, - { "bottomMargin", displayTextObject["bottomMargin"].toVariant() }, - { "lineHeight", displayTextObject["lineHeight"].toVariant() }, - { "visible", false }, - { "emissive", true }, - { "grabbable", false }, - { "text", ""}, - { "parentID", _anchor.overlayID } - }; + EntityItemProperties properties; + properties.setType(EntityTypes::Text); + properties.setDimensions(dimensions); + properties.setLocalPosition(localPosition); + properties.setLocalRotation(quatFromVariant(displayTextObject["localOrientation"].toVariant())); + properties.setLeftMargin((float)displayTextObject["leftMargin"].toDouble()); + properties.setRightMargin((float)displayTextObject["rightMargin"].toDouble()); + properties.setTopMargin((float)displayTextObject["topMargin"].toDouble()); + properties.setBottomMargin((float)displayTextObject["bottomMargin"].toDouble()); + properties.setLineHeight((float)displayTextObject["lineHeight"].toDouble()); + properties.setVisible(false); + properties.setEmissive(true); + properties.getGrab().setGrabbable(false); + properties.setText(""); + properties.setTextAlpha(1.0f); + properties.setBackgroundAlpha(0.7f); + properties.setParentID(_anchor.entityID); - textDisplay.overlayID = overlays.addOverlay("text3d", displayTextProperties); - textDisplay.localPosition = vec3FromVariant(displayTextObject["localPosition"].toVariant()); - textDisplay.dimensions = vec3FromVariant(displayTextObject["dimensions"].toVariant()); - textDisplay.lineHeight = (float) displayTextObject["lineHeight"].toDouble(); - - _textDisplay = textDisplay; + TextDisplay textDisplay; + textDisplay.entityID = entityScriptingInterface->addEntityInternal(properties, entity::HostType::LOCAL); + textDisplay.localPosition = localPosition; + textDisplay.dimensions = dimensions; + textDisplay.lineHeight = lineHeight; + _textDisplay = textDisplay; + } _ignoreItemsLock.withWriteLock([&] { - _itemsToIgnore.append(_textDisplay.overlayID); - _itemsToIgnore.append(_anchor.overlayID); + _itemsToIgnore.insert(_textDisplay.entityID); + _itemsToIgnore.insert(_anchor.entityID); }); _layerIndex = 0; auto pointerManager = DependencyManager::get(); @@ -922,34 +895,33 @@ void Keyboard::loadKeyboardFile(const QString& keyboardFile) { } -OverlayID Keyboard::getAnchorID() { - return _ignoreItemsLock.resultWithReadLock([&] { - return _anchor.overlayID; +QUuid Keyboard::getAnchorID() { + return _ignoreItemsLock.resultWithReadLock([&] { + return _anchor.entityID; }); } -bool Keyboard::shouldProcessOverlay(const OverlayID& overlayID) const { - return (!_keyboardLayers.empty() && isLayerSwitchTimerFinished() && overlayID != _backPlate.overlayID); +bool Keyboard::shouldProcessEntity() const { + return (!_keyboardLayers.empty() && isLayerSwitchTimerFinished()); } -QVector Keyboard::getKeysID() { - return _ignoreItemsLock.resultWithReadLock>([&] { +QSet Keyboard::getKeyIDs() { + return _ignoreItemsLock.resultWithReadLock>([&] { return _itemsToIgnore; }); } void Keyboard::clearKeyboardKeys() { - Overlays& overlays = qApp->getOverlays(); - + auto entityScriptingInterface = DependencyManager::get(); for (const auto& keyboardLayer: _keyboardLayers) { for (auto iter = keyboardLayer.begin(); iter != keyboardLayer.end(); iter++) { - overlays.deleteOverlay(iter.key()); + entityScriptingInterface->deleteEntity(iter.key()); } } - overlays.deleteOverlay(_anchor.overlayID); - overlays.deleteOverlay(_textDisplay.overlayID); - overlays.deleteOverlay(_backPlate.overlayID); + entityScriptingInterface->deleteEntity(_anchor.entityID); + entityScriptingInterface->deleteEntity(_textDisplay.entityID); + entityScriptingInterface->deleteEntity(_backPlate.entityID); _keyboardLayers.clear(); @@ -989,8 +961,8 @@ void Keyboard::disableRightMallet() { pointerManager->disablePointer(_rightHandStylus); } -bool Keyboard::containsID(OverlayID overlayID) const { +bool Keyboard::containsID(const QUuid& id) const { return resultWithReadLock([&] { - return _itemsToIgnore.contains(overlayID) || _backPlate.overlayID == overlayID; + return _itemsToIgnore.contains(id) || _backPlate.entityID == id; }); } diff --git a/interface/src/ui/Keyboard.h b/interface/src/ui/Keyboard.h index bcb6ef1a22..958c862520 100644 --- a/interface/src/ui/Keyboard.h +++ b/interface/src/ui/Keyboard.h @@ -25,8 +25,6 @@ #include #include -#include "ui/overlays/Overlay.h" - class PointerEvent; @@ -47,8 +45,8 @@ public: static Key::Type getKeyTypeFromString(const QString& keyTypeString); - OverlayID getID() const { return _keyID; } - void setID(OverlayID overlayID) { _keyID = overlayID; } + QUuid getID() const { return _keyID; } + void setID(const QUuid& id) { _keyID = id; } void startTimer(int time); bool timerFinished(); @@ -77,12 +75,13 @@ private: int _switchToLayer { 0 }; bool _pressed { false }; - OverlayID _keyID; + QUuid _keyID; QString _keyString; glm::vec3 _originalLocalPosition; glm::vec3 _originalDimensions; glm::vec3 _currentLocalPosition; + bool _originalDimensionsAndLocalPositionSaved { false }; std::shared_ptr _timer { std::make_shared() }; }; @@ -111,35 +110,35 @@ public: bool getUse3DKeyboard() const; void setUse3DKeyboard(bool use); - bool containsID(OverlayID overlayID) const; + bool containsID(const QUuid& id) const; void loadKeyboardFile(const QString& keyboardFile); - QVector getKeysID(); - OverlayID getAnchorID(); + QSet getKeyIDs(); + QUuid getAnchorID(); public slots: - void handleTriggerBegin(const OverlayID& overlayID, const PointerEvent& event); - void handleTriggerEnd(const OverlayID& overlayID, const PointerEvent& event); - void handleTriggerContinue(const OverlayID& overlayID, const PointerEvent& event); - void handleHoverBegin(const OverlayID& overlayID, const PointerEvent& event); - void handleHoverEnd(const OverlayID& overlayID, const PointerEvent& event); + void handleTriggerBegin(const QUuid& id, const PointerEvent& event); + void handleTriggerEnd(const QUuid& id, const PointerEvent& event); + void handleTriggerContinue(const QUuid& id, const PointerEvent& event); + void handleHoverBegin(const QUuid& id, const PointerEvent& event); + void handleHoverEnd(const QUuid& id, const PointerEvent& event); void scaleKeyboard(float sensorToWorldScale); private: struct Anchor { - OverlayID overlayID; + QUuid entityID; glm::vec3 originalDimensions; }; struct BackPlate { - OverlayID overlayID; + QUuid entityID; glm::vec3 dimensions; glm::vec3 localPosition; }; struct TextDisplay { float lineHeight; - OverlayID overlayID; + QUuid entityID; glm::vec3 localPosition; glm::vec3 dimensions; }; @@ -148,14 +147,16 @@ private: void raiseKeyboardAnchor(bool raise) const; void enableStylus(); void disableStylus(); + void enableSelectionLists(); + void disableSelectionLists(); void setLayerIndex(int layerIndex); void clearKeyboardKeys(); void switchToLayer(int layerIndex); void updateTextDisplay(); - bool shouldProcessOverlayAndPointerEvent(const PointerEvent& event, const OverlayID& overlayID) const; + bool shouldProcessEntityAndPointerEvent(const PointerEvent& event) const; bool shouldProcessPointerEvent(const PointerEvent& event) const; - bool shouldProcessOverlay(const OverlayID& overlayID) const; + bool shouldProcessEntity() const; void startLayerSwitchTimer(); bool isLayerSwitchTimerFinished() const; @@ -184,8 +185,8 @@ private: Anchor _anchor; BackPlate _backPlate; - QVector _itemsToIgnore; - std::vector> _keyboardLayers; + QSet _itemsToIgnore; + std::vector> _keyboardLayers; }; #endif diff --git a/interface/src/ui/LoginDialog.cpp b/interface/src/ui/LoginDialog.cpp index 4359318833..b4f504822f 100644 --- a/interface/src/ui/LoginDialog.cpp +++ b/interface/src/ui/LoginDialog.cpp @@ -34,7 +34,7 @@ HIFI_QML_DEF(LoginDialog) static const QUrl TABLET_LOGIN_DIALOG_URL("dialogs/TabletLoginDialog.qml"); -const QUrl OVERLAY_LOGIN_DIALOG = PathUtils::qmlUrl("OverlayLoginDialog.qml"); +const QUrl LOGIN_DIALOG = PathUtils::qmlUrl("OverlayLoginDialog.qml"); LoginDialog::LoginDialog(QQuickItem *parent) : OffscreenQmlDialog(parent) { auto accountManager = DependencyManager::get(); @@ -71,7 +71,7 @@ void LoginDialog::showWithSelection() { if (!qApp->getLoginDialogPoppedUp()) { tablet->initialScreen(TABLET_LOGIN_DIALOG_URL); } else { - qApp->createLoginDialogOverlay(); + qApp->createLoginDialog(); } } diff --git a/interface/src/ui/LoginDialog.h b/interface/src/ui/LoginDialog.h index 7c932932cf..e2fa8adc61 100644 --- a/interface/src/ui/LoginDialog.h +++ b/interface/src/ui/LoginDialog.h @@ -18,7 +18,7 @@ class QNetworkReply; -extern const QUrl OVERLAY_LOGIN_DIALOG; +extern const QUrl LOGIN_DIALOG; class LoginDialog : public OffscreenQmlDialog { Q_OBJECT diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index cb204c9772..e3697ee8ec 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -158,7 +158,7 @@ void Stats::updateStats(bool force) { STAT_UPDATE(rayPicksCount, totalPicks[PickQuery::Ray]); STAT_UPDATE(parabolaPicksCount, totalPicks[PickQuery::Parabola]); STAT_UPDATE(collisionPicksCount, totalPicks[PickQuery::Collision]); - std::vector updatedPicks = pickManager->getUpdatedPickCounts(); + std::vector updatedPicks = pickManager->getUpdatedPickCounts(); STAT_UPDATE(stylusPicksUpdated, updatedPicks[PickQuery::Stylus]); STAT_UPDATE(rayPicksUpdated, updatedPicks[PickQuery::Ray]); STAT_UPDATE(parabolaPicksUpdated, updatedPicks[PickQuery::Parabola]); diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index 5488da9115..36e92b00af 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -172,10 +172,10 @@ private: \ * @property {number} rayPicksCount - Read-only. * @property {number} parabolaPicksCount - Read-only. * @property {number} collisionPicksCount - Read-only. - * @property {Vec4} stylusPicksUpdated - Read-only. - * @property {Vec4} rayPicksUpdated - Read-only. - * @property {Vec4} parabolaPicksUpdated - Read-only. - * @property {Vec4} collisionPicksUpdated - Read-only. + * @property {Vec3} stylusPicksUpdated - Read-only. + * @property {Vec3} rayPicksUpdated - Read-only. + * @property {Vec3} parabolaPicksUpdated - Read-only. + * @property {Vec3} collisionPicksUpdated - Read-only. */ // Properties from x onwards are QQuickItem properties. @@ -297,10 +297,10 @@ class Stats : public QQuickItem { STATS_PROPERTY(int, rayPicksCount, 0) STATS_PROPERTY(int, parabolaPicksCount, 0) STATS_PROPERTY(int, collisionPicksCount, 0) - STATS_PROPERTY(QVector4D, stylusPicksUpdated, QVector4D(0, 0, 0, 0)) - STATS_PROPERTY(QVector4D, rayPicksUpdated, QVector4D(0, 0, 0, 0)) - STATS_PROPERTY(QVector4D, parabolaPicksUpdated, QVector4D(0, 0, 0, 0)) - STATS_PROPERTY(QVector4D, collisionPicksUpdated, QVector4D(0, 0, 0, 0)) + STATS_PROPERTY(QVector3D, stylusPicksUpdated, QVector3D(0, 0, 0)) + STATS_PROPERTY(QVector3D, rayPicksUpdated, QVector3D(0, 0, 0)) + STATS_PROPERTY(QVector3D, parabolaPicksUpdated, QVector3D(0, 0, 0)) + STATS_PROPERTY(QVector3D, collisionPicksUpdated, QVector3D(0, 0, 0)) public: static Stats* getInstance(); diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp deleted file mode 100644 index eb43e8cf45..0000000000 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ /dev/null @@ -1,392 +0,0 @@ -// -// Base3DOverlay.cpp -// interface/src/ui/overlays -// -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - - -#include "Base3DOverlay.h" - -#include -#include -#include "Application.h" - - -const bool DEFAULT_IS_SOLID = false; -const bool DEFAULT_IS_DASHED_LINE = false; - -Base3DOverlay::Base3DOverlay() : - SpatiallyNestable(NestableType::Overlay, QUuid::createUuid()), - _isSolid(DEFAULT_IS_SOLID), - _isDashedLine(DEFAULT_IS_DASHED_LINE), - _ignorePickIntersection(false), - _drawInFront(false), - _drawHUDLayer(false) -{ - // HACK: queryAACube stuff not actually relevant for 3DOverlays, and by setting _queryAACubeSet true here - // we can avoid incorrect evaluation for sending updates for entities with 3DOverlays children. - _queryAACubeSet = true; -} - -Base3DOverlay::Base3DOverlay(const Base3DOverlay* base3DOverlay) : - Overlay(base3DOverlay), - SpatiallyNestable(NestableType::Overlay, QUuid::createUuid()), - _isSolid(base3DOverlay->_isSolid), - _isDashedLine(base3DOverlay->_isDashedLine), - _ignorePickIntersection(base3DOverlay->_ignorePickIntersection), - _drawInFront(base3DOverlay->_drawInFront), - _drawHUDLayer(base3DOverlay->_drawHUDLayer), - _isGrabbable(base3DOverlay->_isGrabbable), - _isVisibleInSecondaryCamera(base3DOverlay->_isVisibleInSecondaryCamera) -{ - setTransform(base3DOverlay->getTransform()); - // HACK: queryAACube stuff not actually relevant for 3DOverlays, and by setting _queryAACubeSet true here - // we can avoid incorrect evaluation for sending updates for entities with 3DOverlays children. - _queryAACubeSet = true; -} - -QVariantMap convertOverlayLocationFromScriptSemantics(const QVariantMap& properties, bool scalesWithParent) { - // the position and rotation in _transform are relative to the parent (aka local). The versions coming from - // scripts are in world-frame, unless localPosition or localRotation are used. Patch up the properties - // so that "position" and "rotation" are relative-to-parent values. - QVariantMap result = properties; - QUuid parentID = result["parentID"].isValid() ? QUuid(result["parentID"].toString()) : QUuid(); - int parentJointIndex = result["parentJointIndex"].isValid() ? result["parentJointIndex"].toInt() : -1; - bool success; - - // make "position" and "orientation" be relative-to-parent - if (result["localPosition"].isValid()) { - result["position"] = result["localPosition"]; - } else if (result["position"].isValid()) { - glm::vec3 localPosition = SpatiallyNestable::worldToLocal(vec3FromVariant(result["position"]), - parentID, parentJointIndex, scalesWithParent, success); - if (success) { - result["position"] = vec3toVariant(localPosition); - } - } - - if (result["localOrientation"].isValid()) { - result["orientation"] = result["localOrientation"]; - } else if (result["orientation"].isValid()) { - glm::quat localOrientation = SpatiallyNestable::worldToLocal(quatFromVariant(result["orientation"]), - parentID, parentJointIndex, scalesWithParent, success); - if (success) { - result["orientation"] = quatToVariant(localOrientation); - } - } - - return result; -} - -void Base3DOverlay::setProperties(const QVariantMap& originalProperties) { - QVariantMap properties = originalProperties; - - if (properties["name"].isValid()) { - setName(properties["name"].toString()); - } - - // carry over some legacy keys - if (!properties["position"].isValid() && !properties["localPosition"].isValid()) { - if (properties["p1"].isValid()) { - properties["position"] = properties["p1"]; - } else if (properties["point"].isValid()) { - properties["position"] = properties["point"]; - } else if (properties["start"].isValid()) { - properties["position"] = properties["start"]; - } - } - if (!properties["orientation"].isValid() && properties["rotation"].isValid()) { - properties["orientation"] = properties["rotation"]; - } - if (!properties["localOrientation"].isValid() && properties["localRotation"].isValid()) { - properties["localOrientation"] = properties["localRotation"]; - } - - // All of parentID, parentJointIndex, position, orientation are needed to make sense of any of them. - // If any of these changed, pull any missing properties from the overlay. - if (properties["parentID"].isValid() || properties["parentJointIndex"].isValid() || - properties["position"].isValid() || properties["localPosition"].isValid() || - properties["orientation"].isValid() || properties["localOrientation"].isValid()) { - if (!properties["parentID"].isValid()) { - properties["parentID"] = getParentID(); - } - if (!properties["parentJointIndex"].isValid()) { - properties["parentJointIndex"] = getParentJointIndex(); - } - if (!properties["position"].isValid() && !properties["localPosition"].isValid()) { - properties["position"] = vec3toVariant(getWorldPosition()); - } - if (!properties["orientation"].isValid() && !properties["localOrientation"].isValid()) { - properties["orientation"] = quatToVariant(getWorldOrientation()); - } - } - - properties = convertOverlayLocationFromScriptSemantics(properties, getScalesWithParent()); - Overlay::setProperties(properties); - - bool needRenderItemUpdate = false; - - auto drawInFront = properties["drawInFront"]; - if (drawInFront.isValid()) { - bool value = drawInFront.toBool(); - setDrawInFront(value); - needRenderItemUpdate = true; - } - - auto drawHUDLayer = properties["drawHUDLayer"]; - if (drawHUDLayer.isValid()) { - bool value = drawHUDLayer.toBool(); - setDrawHUDLayer(value); - needRenderItemUpdate = true; - } - - auto isGrabbable = properties["grabbable"]; - if (isGrabbable.isValid()) { - setIsGrabbable(isGrabbable.toBool()); - } - - auto isVisibleInSecondaryCamera = properties["isVisibleInSecondaryCamera"]; - if (isVisibleInSecondaryCamera.isValid()) { - bool value = isVisibleInSecondaryCamera.toBool(); - setIsVisibleInSecondaryCamera(value); - needRenderItemUpdate = true; - } - - if (properties["position"].isValid()) { - setLocalPosition(vec3FromVariant(properties["position"])); - needRenderItemUpdate = true; - } - if (properties["orientation"].isValid()) { - setLocalOrientation(quatFromVariant(properties["orientation"])); - needRenderItemUpdate = true; - } - if (properties["isSolid"].isValid()) { - setIsSolid(properties["isSolid"].toBool()); - } - if (properties["isFilled"].isValid()) { - setIsSolid(properties["isSolid"].toBool()); - } - if (properties["isWire"].isValid()) { - setIsSolid(!properties["isWire"].toBool()); - } - if (properties["solid"].isValid()) { - setIsSolid(properties["solid"].toBool()); - } - if (properties["filled"].isValid()) { - setIsSolid(properties["filled"].toBool()); - } - if (properties["wire"].isValid()) { - setIsSolid(!properties["wire"].toBool()); - } - - if (properties["isDashedLine"].isValid()) { - qDebug() << "isDashed is deprecated and will be removed in RC79!"; - setIsDashedLine(properties["isDashedLine"].toBool()); - } - if (properties["dashed"].isValid()) { - qDebug() << "dashed is deprecated and will be removed in RC79!"; - setIsDashedLine(properties["dashed"].toBool()); - } - if (properties["ignorePickIntersection"].isValid()) { - setIgnorePickIntersection(properties["ignorePickIntersection"].toBool()); - } else if (properties["ignoreRayIntersection"].isValid()) { - setIgnorePickIntersection(properties["ignoreRayIntersection"].toBool()); - } - - if (properties["parentID"].isValid()) { - setParentID(QUuid(properties["parentID"].toString())); - needRenderItemUpdate = true; - } - if (properties["parentJointIndex"].isValid()) { - setParentJointIndex(properties["parentJointIndex"].toInt()); - needRenderItemUpdate = true; - } - - // Communicate changes to the renderItem if needed - if (needRenderItemUpdate) { - auto itemID = getRenderItemID(); - if (render::Item::isValidID(itemID)) { - render::ScenePointer scene = qApp->getMain3DScene(); - render::Transaction transaction; - transaction.updateItem(itemID); - scene->enqueueTransaction(transaction); - } - _queryAACubeSet = true; // HACK: just in case some SpatiallyNestable code accidentally set it false - } -} - -// JSDoc for copying to @typedefs of overlay types that inherit Base3DOverlay. -/**jsdoc - * @property {string} name="" - A friendly name for the overlay. - * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and - * start. - * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a - * parentID set, otherwise the same value as position. - * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. - * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a - * parentID set, otherwise the same value as rotation. - * @property {boolean} isSolid=false - Synonyms: solid, isFilled, and filled. - * Antonyms: isWire and wire. - * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: - * dashed. Deprecated. - * @property {boolean} ignorePickIntersection=false - If true, picks ignore the overlay. ignoreRayIntersection is a synonym. - * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't - * have drawInFront set to true, and in front of entities. - * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. - * @property {boolean} isVisibleInSecondaryCamera=false - If true, the overlay is rendered in secondary - * camera views. - * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. - * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if - * parentID is an avatar skeleton. A value of 65535 means "no joint". - */ -QVariant Base3DOverlay::getProperty(const QString& property) { - if (property == "name") { - return _nameLock.resultWithReadLock([&] { - return _name; - }); - } - if (property == "position" || property == "start" || property == "p1" || property == "point") { - return vec3toVariant(getWorldPosition()); - } - if (property == "localPosition") { - return vec3toVariant(getLocalPosition()); - } - if (property == "rotation" || property == "orientation") { - return quatToVariant(getWorldOrientation()); - } - if (property == "localRotation" || property == "localOrientation") { - return quatToVariant(getLocalOrientation()); - } - if (property == "isSolid" || property == "isFilled" || property == "solid" || property == "filled") { - return _isSolid; - } - if (property == "isWire" || property == "wire") { - return !_isSolid; - } - if (property == "isDashedLine" || property == "dashed") { - qDebug() << "isDashedLine/dashed are deprecated and will be removed in RC79!"; - return _isDashedLine; - } - if (property == "ignorePickIntersection" || property == "ignoreRayIntersection") { - return _ignorePickIntersection; - } - if (property == "drawInFront") { - return _drawInFront; - } - if (property == "grabbable") { - return _isGrabbable; - } - if (property == "isVisibleInSecondaryCamera") { - return _isVisibleInSecondaryCamera; - } - if (property == "parentID") { - return getParentID(); - } - if (property == "parentJointIndex") { - return getParentJointIndex(); - } - - return Overlay::getProperty(property); -} - -void Base3DOverlay::locationChanged(bool tellPhysics) { - SpatiallyNestable::locationChanged(tellPhysics); - - // Force the actual update of the render transform through the notify call - notifyRenderVariableChange(); -} - -// FIXME: Overlays shouldn't be deleted when their parents are -void Base3DOverlay::parentDeleted() { - qApp->getOverlays().deleteOverlay(getOverlayID()); -} - -void Base3DOverlay::update(float duration) { - // In Base3DOverlay, if its location or bound changed, the renderTrasnformDirty flag is true. - // then the correct transform used for rendering is computed in the update transaction and assigned. - if (_renderVariableDirty) { - auto itemID = getRenderItemID(); - if (render::Item::isValidID(itemID)) { - // Capture the render transform value in game loop before - auto latestTransform = evalRenderTransform(); - bool latestVisible = getVisible(); - _renderVariableDirty = false; - render::ScenePointer scene = qApp->getMain3DScene(); - render::Transaction transaction; - transaction.updateItem(itemID, [latestTransform, latestVisible](Overlay& data) { - auto overlay3D = dynamic_cast(&data); - if (overlay3D) { - // TODO: overlays need to communicate all relavent render properties through transactions - overlay3D->setRenderTransform(latestTransform); - overlay3D->setRenderVisible(latestVisible); - } - }); - scene->enqueueTransaction(transaction); - } - } -} - -void Base3DOverlay::notifyRenderVariableChange() const { - _renderVariableDirty = true; -} - -Transform Base3DOverlay::evalRenderTransform() { - return getTransform(); -} - -void Base3DOverlay::setRenderTransform(const Transform& transform) { - _renderTransform = transform; -} - -void Base3DOverlay::setRenderVisible(bool visible) { - _renderVisible = visible; -} - -SpatialParentTree* Base3DOverlay::getParentTree() const { - auto entityTreeRenderer = qApp->getEntities(); - EntityTreePointer entityTree = entityTreeRenderer ? entityTreeRenderer->getTree() : nullptr; - return entityTree.get(); -} - -void Base3DOverlay::setVisible(bool visible) { - Parent::setVisible(visible); - notifyRenderVariableChange(); -} - -QString Base3DOverlay::getName() const { - return _nameLock.resultWithReadLock([&] { - return QString("Overlay:") + _name; - }); -} - -void Base3DOverlay::setName(QString name) { - _nameLock.withWriteLock([&] { - _name = name; - }); -} - - - -render::ItemKey Base3DOverlay::getKey() { - auto builder = render::ItemKey::Builder(Overlay::getKey()); - - if (getDrawInFront()) { - builder.withLayer(render::hifi::LAYER_3D_FRONT); - } else if (getDrawHUDLayer()) { - builder.withLayer(render::hifi::LAYER_3D_HUD); - } else { - builder.withoutLayer(); - } - - builder.withoutViewSpace(); - - if (isTransparent()) { - builder.withTransparent(); - } - - return builder.build(); -} diff --git a/interface/src/ui/overlays/Base3DOverlay.h b/interface/src/ui/overlays/Base3DOverlay.h deleted file mode 100644 index daf15e676f..0000000000 --- a/interface/src/ui/overlays/Base3DOverlay.h +++ /dev/null @@ -1,112 +0,0 @@ -// -// Base3DOverlay.h -// interface/src/ui/overlays -// -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_Base3DOverlay_h -#define hifi_Base3DOverlay_h - -#include -#include -#include -#include "Overlay.h" - -class Base3DOverlay : public Overlay, public SpatiallyNestable, public scriptable::ModelProvider { - Q_OBJECT - using Parent = Overlay; - -public: - Base3DOverlay(); - Base3DOverlay(const Base3DOverlay* base3DOverlay); - - void setVisible(bool visible) override; - bool queryAACubeNeedsUpdate() const override { return false; } // HACK: queryAACube not relevant for Overlays - - virtual OverlayID getOverlayID() const override { return OverlayID(getID().toString()); } - void setOverlayID(OverlayID overlayID) override { setID(overlayID); } - - virtual QString getName() const override; - void setName(QString name); - - // getters - virtual bool is3D() const override { return true; } - - virtual render::ItemKey getKey() override; - virtual uint32_t fetchMetaSubItems(render::ItemIDs& subItems) const override { subItems.push_back(getRenderItemID()); return (uint32_t) subItems.size(); } - virtual scriptable::ScriptableModelBase getScriptableModel() override { return scriptable::ScriptableModelBase(); } - - // TODO: consider implementing registration points in this class - glm::vec3 getCenter() const { return getWorldPosition(); } - - bool getIsSolid() const { return _isSolid; } - bool getIsDashedLine() const { return _isDashedLine; } - bool getIsSolidLine() const { return !_isDashedLine; } - bool getIgnorePickIntersection() const { return _ignorePickIntersection; } - bool getDrawInFront() const { return _drawInFront; } - bool getDrawHUDLayer() const { return _drawHUDLayer; } - bool getIsGrabbable() const { return _isGrabbable; } - virtual bool getIsVisibleInSecondaryCamera() const override { return _isVisibleInSecondaryCamera; } - - void setIsSolid(bool isSolid) { _isSolid = isSolid; } - void setIsDashedLine(bool isDashedLine) { _isDashedLine = isDashedLine; } - void setIgnorePickIntersection(bool value) { _ignorePickIntersection = value; } - virtual void setDrawInFront(bool value) { _drawInFront = value; } - virtual void setDrawHUDLayer(bool value) { _drawHUDLayer = value; } - void setIsGrabbable(bool value) { _isGrabbable = value; } - virtual void setIsVisibleInSecondaryCamera(bool value) { _isVisibleInSecondaryCamera = value; } - - void update(float deltatime) override; - - void notifyRenderVariableChange() const; - - virtual void setProperties(const QVariantMap& properties) override; - virtual QVariant getProperty(const QString& property) override; - - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) { return false; } - - virtual bool findRayIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& direction, - float& distance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, bool precisionPicking = false) { - return findRayIntersection(origin, direction, distance, face, surfaceNormal, precisionPicking); - } - - virtual bool findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, float& parabolicDistance, - BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) { return false; } - - virtual bool findParabolaIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, - float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, bool precisionPicking = false) { - return findParabolaIntersection(origin, velocity, acceleration, parabolicDistance, face, surfaceNormal, precisionPicking); - } - - virtual SpatialParentTree* getParentTree() const override; - -protected: - virtual void locationChanged(bool tellPhysics = true) override; - virtual void parentDeleted() override; - - mutable Transform _renderTransform; - mutable bool _renderVisible; - virtual Transform evalRenderTransform(); - virtual void setRenderTransform(const Transform& transform); - void setRenderVisible(bool visible); - const Transform& getRenderTransform() const { return _renderTransform; } - - bool _isSolid; - bool _isDashedLine; - bool _ignorePickIntersection; - bool _drawInFront; - bool _drawHUDLayer; - bool _isGrabbable { false }; - bool _isVisibleInSecondaryCamera { false }; - mutable bool _renderVariableDirty { true }; - - QString _name; - mutable ReadWriteLockable _nameLock; -}; - -#endif // hifi_Base3DOverlay_h diff --git a/interface/src/ui/overlays/Billboard3DOverlay.cpp b/interface/src/ui/overlays/Billboard3DOverlay.cpp deleted file mode 100644 index ecade70ef8..0000000000 --- a/interface/src/ui/overlays/Billboard3DOverlay.cpp +++ /dev/null @@ -1,65 +0,0 @@ -// -// Billboard3DOverlay.cpp -// hifi/interface/src/ui/overlays -// -// Created by Zander Otavka on 8/4/15. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "Billboard3DOverlay.h" - -Billboard3DOverlay::Billboard3DOverlay(const Billboard3DOverlay* billboard3DOverlay) : - Planar3DOverlay(billboard3DOverlay), - PanelAttachable(*billboard3DOverlay), - Billboardable(*billboard3DOverlay) -{ -} - -void Billboard3DOverlay::setProperties(const QVariantMap& properties) { - Planar3DOverlay::setProperties(properties); - PanelAttachable::setProperties(properties); - Billboardable::setProperties(properties); -} - -QVariant Billboard3DOverlay::getProperty(const QString &property) { - QVariant value; - value = Billboardable::getProperty(property); - if (value.isValid()) { - return value; - } - value = PanelAttachable::getProperty(property); - if (value.isValid()) { - return value; - } - return Planar3DOverlay::getProperty(property); -} - -bool Billboard3DOverlay::applyTransformTo(Transform& transform, bool force) { - bool transformChanged = false; - if (force || usecTimestampNow() > _transformExpiry) { - transformChanged = PanelAttachable::applyTransformTo(transform, true); - transformChanged |= pointTransformAtCamera(transform, getOffsetRotation()); - } - return transformChanged; -} - -void Billboard3DOverlay::update(float duration) { - if (isFacingAvatar()) { - _renderVariableDirty = true; - } - Parent::update(duration); -} - -Transform Billboard3DOverlay::evalRenderTransform() { - Transform transform = getTransform(); - bool transformChanged = applyTransformTo(transform, true); - // If the transform is not modified, setting the transform to - // itself will cause drift over time due to floating point errors. - if (transformChanged) { - setTransform(transform); - } - return transform; -} \ No newline at end of file diff --git a/interface/src/ui/overlays/Billboard3DOverlay.h b/interface/src/ui/overlays/Billboard3DOverlay.h deleted file mode 100644 index 174bc23bc8..0000000000 --- a/interface/src/ui/overlays/Billboard3DOverlay.h +++ /dev/null @@ -1,38 +0,0 @@ -// -// Billboard3DOverlay.h -// hifi/interface/src/ui/overlays -// -// Created by Zander Otavka on 8/4/15. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_Billboard3DOverlay_h -#define hifi_Billboard3DOverlay_h - -#include "Planar3DOverlay.h" -#include "PanelAttachable.h" -#include "Billboardable.h" - -class Billboard3DOverlay : public Planar3DOverlay, public PanelAttachable, public Billboardable { - Q_OBJECT - using Parent = Planar3DOverlay; - -public: - Billboard3DOverlay() {} - Billboard3DOverlay(const Billboard3DOverlay* billboard3DOverlay); - - void setProperties(const QVariantMap& properties) override; - QVariant getProperty(const QString& property) override; - - void update(float duration) override; - -protected: - virtual bool applyTransformTo(Transform& transform, bool force = false) override; - - Transform evalRenderTransform() override; -}; - -#endif // hifi_Billboard3DOverlay_h diff --git a/interface/src/ui/overlays/Billboardable.cpp b/interface/src/ui/overlays/Billboardable.cpp deleted file mode 100644 index a125956b5a..0000000000 --- a/interface/src/ui/overlays/Billboardable.cpp +++ /dev/null @@ -1,56 +0,0 @@ -// -// Billboardable.cpp -// interface/src/ui/overlays -// -// Created by Zander Otavka on 8/7/15. -// Modified by Daniela Fontes on 24/10/17. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "Billboardable.h" - -#include -#include -#include "avatar/AvatarManager.h" - -void Billboardable::setProperties(const QVariantMap& properties) { - auto isFacingAvatar = properties["isFacingAvatar"]; - if (isFacingAvatar.isValid()) { - setIsFacingAvatar(isFacingAvatar.toBool()); - } -} - -// JSDoc for copying to @typedefs of overlay types that inherit Billboardable. -/**jsdoc - * @property {boolean} isFacingAvatar - If true, the overlay is rotated to face the user's camera about an axis - * parallel to the user's avatar's "up" direction. - */ -QVariant Billboardable::getProperty(const QString &property) { - if (property == "isFacingAvatar") { - return isFacingAvatar(); - } - return QVariant(); -} - -bool Billboardable::pointTransformAtCamera(Transform& transform, glm::quat offsetRotation) { - if (isFacingAvatar()) { - glm::vec3 billboardPos = transform.getTranslation(); - glm::vec3 cameraPos = qApp->getCamera().getPosition(); - // use the referencial from the avatar, y isn't always up - glm::vec3 avatarUP = DependencyManager::get()->getMyAvatar()->getWorldOrientation()*Vectors::UP; - // check to see if glm::lookAt will work / using glm::lookAt variable name - glm::highp_vec3 s(glm::cross(billboardPos - cameraPos, avatarUP)); - - // make sure s is not NaN for any component - if(glm::length2(s) > 0.0f) { - glm::quat rotation(conjugate(toQuat(glm::lookAt(cameraPos, billboardPos, avatarUP)))); - transform.setRotation(rotation); - transform.postRotate(offsetRotation); - return true; - } - } - return false; -} diff --git a/interface/src/ui/overlays/Billboardable.h b/interface/src/ui/overlays/Billboardable.h deleted file mode 100644 index 46d9ac6479..0000000000 --- a/interface/src/ui/overlays/Billboardable.h +++ /dev/null @@ -1,36 +0,0 @@ -// -// Billboardable.h -// interface/src/ui/overlays -// -// Created by Zander Otavka on 8/7/15. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_Billboardable_h -#define hifi_Billboardable_h - -#include -#include - -class QString; -class Transform; - -class Billboardable { -public: - bool isFacingAvatar() const { return _isFacingAvatar; } - void setIsFacingAvatar(bool isFacingAvatar) { _isFacingAvatar = isFacingAvatar; } - -protected: - void setProperties(const QVariantMap& properties); - QVariant getProperty(const QString& property); - - bool pointTransformAtCamera(Transform& transform, glm::quat offsetRotation = {1, 0, 0, 0}); - -private: - bool _isFacingAvatar = false; -}; - -#endif // hifi_Billboardable_h diff --git a/interface/src/ui/overlays/Circle3DOverlay.cpp b/interface/src/ui/overlays/Circle3DOverlay.cpp deleted file mode 100644 index 1a7d3228c7..0000000000 --- a/interface/src/ui/overlays/Circle3DOverlay.cpp +++ /dev/null @@ -1,589 +0,0 @@ -// -// Circle3DOverlay.cpp -// interface/src/ui/overlays -// -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "Circle3DOverlay.h" - -#include -#include -#include - -QString const Circle3DOverlay::TYPE = "circle3d"; - -Circle3DOverlay::Circle3DOverlay() {} - -Circle3DOverlay::Circle3DOverlay(const Circle3DOverlay* circle3DOverlay) : - Planar3DOverlay(circle3DOverlay), - _startAt(circle3DOverlay->_startAt), - _endAt(circle3DOverlay->_endAt), - _outerRadius(circle3DOverlay->_outerRadius), - _innerRadius(circle3DOverlay->_innerRadius), - _innerStartColor(circle3DOverlay->_innerStartColor), - _innerEndColor(circle3DOverlay->_innerEndColor), - _outerStartColor(circle3DOverlay->_outerStartColor), - _outerEndColor(circle3DOverlay->_outerEndColor), - _innerStartAlpha(circle3DOverlay->_innerStartAlpha), - _innerEndAlpha(circle3DOverlay->_innerEndAlpha), - _outerStartAlpha(circle3DOverlay->_outerStartAlpha), - _outerEndAlpha(circle3DOverlay->_outerEndAlpha), - _hasTickMarks(circle3DOverlay->_hasTickMarks), - _majorTickMarksAngle(circle3DOverlay->_majorTickMarksAngle), - _minorTickMarksAngle(circle3DOverlay->_minorTickMarksAngle), - _majorTickMarksLength(circle3DOverlay->_majorTickMarksLength), - _minorTickMarksLength(circle3DOverlay->_minorTickMarksLength), - _majorTickMarksColor(circle3DOverlay->_majorTickMarksColor), - _minorTickMarksColor(circle3DOverlay->_minorTickMarksColor) -{ -} - -Circle3DOverlay::~Circle3DOverlay() { - auto geometryCache = DependencyManager::get(); - if (geometryCache) { - if (_quadVerticesID) { - geometryCache->releaseID(_quadVerticesID); - } - if (_lineVerticesID) { - geometryCache->releaseID(_lineVerticesID); - } - if (_majorTicksVerticesID) { - geometryCache->releaseID(_majorTicksVerticesID); - } - if (_minorTicksVerticesID) { - geometryCache->releaseID(_minorTicksVerticesID); - } - } -} -void Circle3DOverlay::render(RenderArgs* args) { - if (!_renderVisible) { - return; // do nothing if we're not visible - } - - float alpha = getAlpha(); - if (alpha == 0.0f) { - return; // do nothing if our alpha is 0, we're not visible - } - - bool geometryChanged = _dirty; - _dirty = false; - - const float FULL_CIRCLE = 360.0f; - const float SLICES = 180.0f; // The amount of segment to create the circle - const float SLICE_ANGLE_RADIANS = glm::radians(FULL_CIRCLE / SLICES); - - auto geometryCache = DependencyManager::get(); - - Q_ASSERT(args->_batch); - auto& batch = *args->_batch; - - DependencyManager::get()->bindSimpleProgram(batch, false, isTransparent(), false, !getIsSolid(), true); - - batch.setModelTransform(getRenderTransform()); - - // for our overlay, is solid means we draw a ring between the inner and outer radius of the circle, otherwise - // we just draw a line... - if (getIsSolid()) { - if (!_quadVerticesID) { - _quadVerticesID = geometryCache->allocateID(); - } - - if (geometryChanged) { - QVector points; - QVector colors; - - float pulseLevel = updatePulse(); - vec4 pulseModifier = vec4(1); - if (_alphaPulse != 0.0f) { - pulseModifier.a = (_alphaPulse >= 0.0f) ? pulseLevel : (1.0f - pulseLevel); - } - if (_colorPulse != 0.0f) { - float pulseValue = (_colorPulse >= 0.0f) ? pulseLevel : (1.0f - pulseLevel); - pulseModifier = vec4(vec3(pulseValue), pulseModifier.a); - } - vec4 innerStartColor = vec4(toGlm(_innerStartColor), _innerStartAlpha) * pulseModifier; - vec4 outerStartColor = vec4(toGlm(_outerStartColor), _outerStartAlpha) * pulseModifier; - vec4 innerEndColor = vec4(toGlm(_innerEndColor), _innerEndAlpha) * pulseModifier; - vec4 outerEndColor = vec4(toGlm(_outerEndColor), _outerEndAlpha) * pulseModifier; - - const auto startAtRadians = glm::radians(_startAt); - const auto endAtRadians = glm::radians(_endAt); - - const auto totalRange = _endAt - _startAt; - if (_innerRadius <= 0) { - _solidPrimitive = gpu::TRIANGLE_FAN; - points << vec2(); - colors << innerStartColor; - for (float angleRadians = startAtRadians; angleRadians < endAtRadians; angleRadians += SLICE_ANGLE_RADIANS) { - float range = (angleRadians - startAtRadians) / totalRange; - points << glm::vec2(cosf(angleRadians) * _outerRadius, sinf(angleRadians) * _outerRadius); - colors << glm::mix(outerStartColor, outerEndColor, range); - } - points << glm::vec2(cosf(endAtRadians) * _outerRadius, sinf(endAtRadians) * _outerRadius); - colors << outerEndColor; - - } else { - _solidPrimitive = gpu::TRIANGLE_STRIP; - for (float angleRadians = startAtRadians; angleRadians < endAtRadians; angleRadians += SLICE_ANGLE_RADIANS) { - float range = (angleRadians - startAtRadians) / totalRange; - - points << glm::vec2(cosf(angleRadians) * _innerRadius, sinf(angleRadians) * _innerRadius); - colors << glm::mix(innerStartColor, innerEndColor, range); - - points << glm::vec2(cosf(angleRadians) * _outerRadius, sinf(angleRadians) * _outerRadius); - colors << glm::mix(outerStartColor, outerEndColor, range); - } - points << glm::vec2(cosf(endAtRadians) * _innerRadius, sinf(endAtRadians) * _innerRadius); - colors << innerEndColor; - - points << glm::vec2(cosf(endAtRadians) * _outerRadius, sinf(endAtRadians) * _outerRadius); - colors << outerEndColor; - } - geometryCache->updateVertices(_quadVerticesID, points, colors); - } - - geometryCache->renderVertices(batch, _solidPrimitive, _quadVerticesID); - - } else { - if (!_lineVerticesID) { - _lineVerticesID = geometryCache->allocateID(); - } - - if (geometryChanged) { - QVector points; - - const auto startAtRadians = glm::radians(_startAt); - const auto endAtRadians = glm::radians(_endAt); - - float angleRadians = startAtRadians; - glm::vec2 firstPoint(cosf(angleRadians) * _outerRadius, sinf(angleRadians) * _outerRadius); - points << firstPoint; - - while (angleRadians < endAtRadians) { - angleRadians += SLICE_ANGLE_RADIANS; - glm::vec2 thisPoint(cosf(angleRadians) * _outerRadius, sinf(angleRadians) * _outerRadius); - points << thisPoint; - - if (getIsDashedLine()) { - angleRadians += SLICE_ANGLE_RADIANS / 2.0f; // short gap - glm::vec2 dashStartPoint(cosf(angleRadians) * _outerRadius, sinf(angleRadians) * _outerRadius); - points << dashStartPoint; - } - } - - // get the last slice portion.... - angleRadians = endAtRadians; - glm::vec2 lastPoint(cosf(angleRadians) * _outerRadius, sinf(angleRadians) * _outerRadius); - points << lastPoint; - geometryCache->updateVertices(_lineVerticesID, points, vec4(toGlm(getColor()), getAlpha())); - } - - if (getIsDashedLine()) { - geometryCache->renderVertices(batch, gpu::LINES, _lineVerticesID); - } else { - geometryCache->renderVertices(batch, gpu::LINE_STRIP, _lineVerticesID); - } - } - - // draw our tick marks - // for our overlay, is solid means we draw a ring between the inner and outer radius of the circle, otherwise - // we just draw a line... - if (getHasTickMarks()) { - if (!_majorTicksVerticesID) { - _majorTicksVerticesID = geometryCache->allocateID(); - } - if (!_minorTicksVerticesID) { - _minorTicksVerticesID = geometryCache->allocateID(); - } - - if (geometryChanged) { - QVector majorPoints; - QVector minorPoints; - - // draw our major tick marks - if (getMajorTickMarksAngle() > 0.0f && getMajorTickMarksLength() != 0.0f) { - - float tickMarkAngle = getMajorTickMarksAngle(); - float angle = _startAt - fmodf(_startAt, tickMarkAngle) + tickMarkAngle; - float tickMarkLength = getMajorTickMarksLength(); - float startRadius = (tickMarkLength > 0.0f) ? _innerRadius : _outerRadius; - float endRadius = startRadius + tickMarkLength; - - while (angle <= _endAt) { - float angleInRadians = glm::radians(angle); - - glm::vec2 thisPointA(cosf(angleInRadians) * startRadius, sinf(angleInRadians) * startRadius); - glm::vec2 thisPointB(cosf(angleInRadians) * endRadius, sinf(angleInRadians) * endRadius); - - majorPoints << thisPointA << thisPointB; - - angle += tickMarkAngle; - } - } - - // draw our minor tick marks - if (getMinorTickMarksAngle() > 0.0f && getMinorTickMarksLength() != 0.0f) { - - float tickMarkAngle = getMinorTickMarksAngle(); - float angle = _startAt - fmodf(_startAt, tickMarkAngle) + tickMarkAngle; - float tickMarkLength = getMinorTickMarksLength(); - float startRadius = (tickMarkLength > 0.0f) ? _innerRadius : _outerRadius; - float endRadius = startRadius + tickMarkLength; - - while (angle <= _endAt) { - float angleInRadians = glm::radians(angle); - - glm::vec2 thisPointA(cosf(angleInRadians) * startRadius, sinf(angleInRadians) * startRadius); - glm::vec2 thisPointB(cosf(angleInRadians) * endRadius, sinf(angleInRadians) * endRadius); - - minorPoints << thisPointA << thisPointB; - - angle += tickMarkAngle; - } - } - - glm::vec4 majorColor(toGlm(getMajorTickMarksColor()), alpha); - geometryCache->updateVertices(_majorTicksVerticesID, majorPoints, majorColor); - glm::vec4 minorColor(toGlm(getMinorTickMarksColor()), alpha); - geometryCache->updateVertices(_minorTicksVerticesID, minorPoints, minorColor); - } - - geometryCache->renderVertices(batch, gpu::LINES, _majorTicksVerticesID); - - geometryCache->renderVertices(batch, gpu::LINES, _minorTicksVerticesID); - } -} - -const render::ShapeKey Circle3DOverlay::getShapeKey() { - auto builder = render::ShapeKey::Builder().withoutCullFace(); - if (isTransparent()) { - builder.withTranslucent(); - } - if (!getIsSolid()) { - builder.withUnlit().withDepthBias(); - } - return builder.build(); -} - -template T fromVariant(const QVariant& v, bool& valid) { - valid = v.isValid(); - return qvariant_cast(v); -} - -template<> glm::u8vec3 fromVariant(const QVariant& v, bool& valid) { - return u8vec3FromVariant(v, valid); -} - -template -bool updateIfValid(const QVariantMap& properties, const char* key, T& output) { - bool valid; - T result = fromVariant(properties[key], valid); - if (!valid) { - return false; - } - - // Don't signal updates if the value was already set - if (result == output) { - return false; - } - - output = result; - return true; -} - -// Multicast, many outputs -template -bool updateIfValid(const QVariantMap& properties, const char* key, std::initializer_list> outputs) { - bool valid; - T value = fromVariant(properties[key], valid); - if (!valid) { - return false; - } - bool updated = false; - for (T& output : outputs) { - if (output != value) { - output = value; - updated = true; - } - } - return updated; -} - -// Multicast, multiple possible inputs, in order of preference -template -bool updateIfValid(const QVariantMap& properties, const std::initializer_list keys, T& output) { - for (const char* key : keys) { - if (updateIfValid(properties, key, output)) { - return true; - } - } - return false; -} - - -void Circle3DOverlay::setProperties(const QVariantMap& properties) { - Planar3DOverlay::setProperties(properties); - _dirty |= updateIfValid(properties, "alpha", { _innerStartAlpha, _innerEndAlpha, _outerStartAlpha, _outerEndAlpha }); - _dirty |= updateIfValid(properties, "Alpha", { _innerStartAlpha, _innerEndAlpha, _outerStartAlpha, _outerEndAlpha }); - _dirty |= updateIfValid(properties, "startAlpha", { _innerStartAlpha, _outerStartAlpha }); - _dirty |= updateIfValid(properties, "endAlpha", { _innerEndAlpha, _outerEndAlpha }); - _dirty |= updateIfValid(properties, "innerAlpha", { _innerStartAlpha, _innerEndAlpha }); - _dirty |= updateIfValid(properties, "outerAlpha", { _outerStartAlpha, _outerEndAlpha }); - _dirty |= updateIfValid(properties, "innerStartAlpha", _innerStartAlpha); - _dirty |= updateIfValid(properties, "innerEndAlpha", _innerEndAlpha); - _dirty |= updateIfValid(properties, "outerStartAlpha", _outerStartAlpha); - _dirty |= updateIfValid(properties, "outerEndAlpha", _outerEndAlpha); - - _dirty |= updateIfValid(properties, "color", { _innerStartColor, _innerEndColor, _outerStartColor, _outerEndColor }); - _dirty |= updateIfValid(properties, "startColor", { _innerStartColor, _outerStartColor } ); - _dirty |= updateIfValid(properties, "endColor", { _innerEndColor, _outerEndColor } ); - _dirty |= updateIfValid(properties, "innerColor", { _innerStartColor, _innerEndColor } ); - _dirty |= updateIfValid(properties, "outerColor", { _outerStartColor, _outerEndColor } ); - _dirty |= updateIfValid(properties, "innerStartColor", _innerStartColor); - _dirty |= updateIfValid(properties, "innerEndColor", _innerEndColor); - _dirty |= updateIfValid(properties, "outerStartColor", _outerStartColor); - _dirty |= updateIfValid(properties, "outerEndColor", _outerEndColor); - - _dirty |= updateIfValid(properties, "startAt", _startAt); - _dirty |= updateIfValid(properties, "endAt", _endAt); - - _dirty |= updateIfValid(properties, { "radius", "outerRadius" }, _outerRadius); - _dirty |= updateIfValid(properties, "innerRadius", _innerRadius); - _dirty |= updateIfValid(properties, "hasTickMarks", _hasTickMarks); - _dirty |= updateIfValid(properties, "majorTickMarksAngle", _majorTickMarksAngle); - _dirty |= updateIfValid(properties, "minorTickMarksAngle", _minorTickMarksAngle); - _dirty |= updateIfValid(properties, "majorTickMarksLength", _majorTickMarksLength); - _dirty |= updateIfValid(properties, "minorTickMarksLength", _minorTickMarksLength); - _dirty |= updateIfValid(properties, "majorTickMarksColor", _majorTickMarksColor); - _dirty |= updateIfValid(properties, "minorTickMarksColor", _minorTickMarksColor); - - if (_innerStartAlpha < 1.0f || _innerEndAlpha < 1.0f || _outerStartAlpha < 1.0f || _outerEndAlpha < 1.0f) { - // Force the alpha to 0.5, since we'll ignore it in the presence of these other values, but we need - // it to be non-1 in order to get the right pipeline and non-0 in order to render at all. - _alpha = 0.5f; - } -} - -// Overlay's color and alpha properties are overridden. And the dimensions property is not used. -/**jsdoc - * These are the properties of a circle3d {@link Overlays.OverlayType|OverlayType}. - * @typedef {object} Overlays.Circle3DProperties - * - * @property {string} type=circle3d - Has the value "circle3d". Read-only. - * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. - * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. - * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from - * pulseMin to pulseMax, then pulseMax to pulseMin in one period. - * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 - * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise - * used.) - * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 - * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise - * used.) - * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. - * - * @property {string} name="" - A friendly name for the overlay. - * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and - * start. - * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a - * parentID set, otherwise the same value as position. - * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. - * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a - * parentID set, otherwise the same value as rotation. - * @property {boolean} isSolid=false - Synonyms: solid, isFilled, and filled - * Antonyms: isWire and wire. - * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: - * dashed. Deprecated. - * @property {boolean} ignorePickIntersection=false - If true, picks ignore the overlay. ignoreRayIntersection is a synonym. - * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't - * have drawInFront set to true, and in front of entities. - * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. - * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. - * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if - * parentID is an avatar skeleton. A value of 65535 means "no joint". - * - * @property {Vec2} dimensions=1,1 - The dimensions of the overlay. Synonyms: scale, size. - * Not used. - * - * @property {number} startAt=0 - The counter-clockwise angle from the overlay's x-axis that drawing starts at, in degrees. - * @property {number} endAt=360 - The counter-clockwise angle from the overlay's x-axis that drawing ends at, in degrees. - * @property {number} outerRadius=1 - The outer radius of the overlay, in meters. Synonym: radius. - * @property {number} innerRadius=0 - The inner radius of the overlay, in meters. - * @property {Color} color=255,255,255 - The color of the overlay. Setting this value also sets the values of - * innerStartColor, innerEndColor, outerStartColor, and outerEndColor. - * @property {Color} startColor - Sets the values of innerStartColor and outerStartColor. - * Write-only. - * @property {Color} endColor - Sets the values of innerEndColor and outerEndColor. - * Write-only. - * @property {Color} innerColor - Sets the values of innerStartColor and innerEndColor. - * Write-only. - * @property {Color} outerColor - Sets the values of outerStartColor and outerEndColor. - * Write-only. - * @property {Color} innerStartcolor - The color at the inner start point of the overlay. - * @property {Color} innerEndColor - The color at the inner end point of the overlay. - * @property {Color} outerStartColor - The color at the outer start point of the overlay. - * @property {Color} outerEndColor - The color at the outer end point of the overlay. - * @property {number} alpha=0.5 - The opacity of the overlay, 0.0 - 1.0. Setting this value also sets - * the values of innerStartAlpha, innerEndAlpha, outerStartAlpha, and - * outerEndAlpha. Synonym: Alpha; write-only. - * @property {number} startAlpha - Sets the values of innerStartAlpha and outerStartAlpha. - * Write-only. - * @property {number} endAlpha - Sets the values of innerEndAlpha and outerEndAlpha. - * Write-only. - * @property {number} innerAlpha - Sets the values of innerStartAlpha and innerEndAlpha. - * Write-only. - * @property {number} outerAlpha - Sets the values of outerStartAlpha and outerEndAlpha. - * Write-only. - * @property {number} innerStartAlpha=0 - The alpha at the inner start point of the overlay. - * @property {number} innerEndAlpha=0 - The alpha at the inner end point of the overlay. - * @property {number} outerStartAlpha=0 - The alpha at the outer start point of the overlay. - * @property {number} outerEndAlpha=0 - The alpha at the outer end point of the overlay. - - * @property {boolean} hasTickMarks=false - If true, tick marks are drawn. - * @property {number} majorTickMarksAngle=0 - The angle between major tick marks, in degrees. - * @property {number} minorTickMarksAngle=0 - The angle between minor tick marks, in degrees. - * @property {number} majorTickMarksLength=0 - The length of the major tick marks, in meters. A positive value draws tick marks - * outwards from the inner radius; a negative value draws tick marks inwards from the outer radius. - * @property {number} minorTickMarksLength=0 - The length of the minor tick marks, in meters. A positive value draws tick marks - * outwards from the inner radius; a negative value draws tick marks inwards from the outer radius. - * @property {Color} majorTickMarksColor=0,0,0 - The color of the major tick marks. - * @property {Color} minorTickMarksColor=0,0,0 - The color of the minor tick marks. - */ -QVariant Circle3DOverlay::getProperty(const QString& property) { - if (property == "startAt") { - return _startAt; - } - if (property == "endAt") { - return _endAt; - } - if (property == "radius") { - return _outerRadius; - } - if (property == "outerRadius") { - return _outerRadius; - } - if (property == "innerRadius") { - return _innerRadius; - } - if (property == "innerStartColor") { - return u8vec3ColortoVariant(_innerStartColor); - } - if (property == "innerEndColor") { - return u8vec3ColortoVariant(_innerEndColor); - } - if (property == "outerStartColor") { - return u8vec3ColortoVariant(_outerStartColor); - } - if (property == "outerEndColor") { - return u8vec3ColortoVariant(_outerEndColor); - } - if (property == "innerStartAlpha") { - return _innerStartAlpha; - } - if (property == "innerEndAlpha") { - return _innerEndAlpha; - } - if (property == "outerStartAlpha") { - return _outerStartAlpha; - } - if (property == "outerEndAlpha") { - return _outerEndAlpha; - } - if (property == "hasTickMarks") { - return _hasTickMarks; - } - if (property == "majorTickMarksAngle") { - return _majorTickMarksAngle; - } - if (property == "minorTickMarksAngle") { - return _minorTickMarksAngle; - } - if (property == "majorTickMarksLength") { - return _majorTickMarksLength; - } - if (property == "minorTickMarksLength") { - return _minorTickMarksLength; - } - if (property == "majorTickMarksColor") { - return u8vec3ColortoVariant(_majorTickMarksColor); - } - if (property == "minorTickMarksColor") { - return u8vec3ColortoVariant(_minorTickMarksColor); - } - - return Planar3DOverlay::getProperty(property); -} - -bool Circle3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) { - // Scale the dimensions by the diameter - glm::vec2 dimensions = getOuterRadius() * 2.0f * getDimensions(); - glm::quat rotation = getWorldOrientation(); - - if (findRayRectangleIntersection(origin, direction, rotation, getWorldPosition(), dimensions, distance)) { - glm::vec3 hitPosition = origin + (distance * direction); - glm::vec3 localHitPosition = glm::inverse(getWorldOrientation()) * (hitPosition - getWorldPosition()); - localHitPosition.x /= getDimensions().x; - localHitPosition.y /= getDimensions().y; - float distanceToHit = glm::length(localHitPosition); - - if (getInnerRadius() <= distanceToHit && distanceToHit <= getOuterRadius()) { - glm::vec3 forward = rotation * Vectors::FRONT; - if (glm::dot(forward, direction) > 0.0f) { - face = MAX_Z_FACE; - surfaceNormal = -forward; - } else { - face = MIN_Z_FACE; - surfaceNormal = forward; - } - return true; - } - } - - return false; -} - -bool Circle3DOverlay::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, - float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) { - // Scale the dimensions by the diameter - glm::vec2 xyDimensions = getOuterRadius() * 2.0f * getDimensions(); - glm::quat rotation = getWorldOrientation(); - glm::vec3 position = getWorldPosition(); - - glm::quat inverseRot = glm::inverse(rotation); - glm::vec3 localOrigin = inverseRot * (origin - position); - glm::vec3 localVelocity = inverseRot * velocity; - glm::vec3 localAcceleration = inverseRot * acceleration; - - if (findParabolaRectangleIntersection(localOrigin, localVelocity, localAcceleration, xyDimensions, parabolicDistance)) { - glm::vec3 localHitPosition = localOrigin + localVelocity * parabolicDistance + 0.5f * localAcceleration * parabolicDistance * parabolicDistance; - localHitPosition.x /= getDimensions().x; - localHitPosition.y /= getDimensions().y; - float distanceToHit = glm::length(localHitPosition); - - if (getInnerRadius() <= distanceToHit && distanceToHit <= getOuterRadius()) { - float localIntersectionVelocityZ = localVelocity.z + localAcceleration.z * parabolicDistance; - glm::vec3 forward = rotation * Vectors::FRONT; - if (localIntersectionVelocityZ > 0.0f) { - face = MIN_Z_FACE; - surfaceNormal = forward; - } else { - face = MAX_Z_FACE; - surfaceNormal = -forward; - } - return true; - } - } - - return false; -} - -Circle3DOverlay* Circle3DOverlay::createClone() const { - return new Circle3DOverlay(this); -} diff --git a/interface/src/ui/overlays/Circle3DOverlay.h b/interface/src/ui/overlays/Circle3DOverlay.h deleted file mode 100644 index ca5e05a53b..0000000000 --- a/interface/src/ui/overlays/Circle3DOverlay.h +++ /dev/null @@ -1,96 +0,0 @@ -// -// Circle3DOverlay.h -// interface/src/ui/overlays -// -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_Circle3DOverlay_h -#define hifi_Circle3DOverlay_h - -// include this before QGLWidget, which includes an earlier version of OpenGL -#include "Planar3DOverlay.h" - -class Circle3DOverlay : public Planar3DOverlay { - Q_OBJECT - -public: - static QString const TYPE; - virtual QString getType() const override { return TYPE; } - - Circle3DOverlay(); - Circle3DOverlay(const Circle3DOverlay* circle3DOverlay); - ~Circle3DOverlay(); - - virtual void render(RenderArgs* args) override; - virtual const render::ShapeKey getShapeKey() override; - void setProperties(const QVariantMap& properties) override; - QVariant getProperty(const QString& property) override; - - float getStartAt() const { return _startAt; } - float getEndAt() const { return _endAt; } - float getOuterRadius() const { return _outerRadius; } - float getInnerRadius() const { return _innerRadius; } - bool getHasTickMarks() const { return _hasTickMarks; } - float getMajorTickMarksAngle() const { return _majorTickMarksAngle; } - float getMinorTickMarksAngle() const { return _minorTickMarksAngle; } - float getMajorTickMarksLength() const { return _majorTickMarksLength; } - float getMinorTickMarksLength() const { return _minorTickMarksLength; } - glm::u8vec3 getMajorTickMarksColor() const { return _majorTickMarksColor; } - glm::u8vec3 getMinorTickMarksColor() const { return _minorTickMarksColor; } - - void setStartAt(float value) { _startAt = value; } - void setEndAt(float value) { _endAt = value; } - void setOuterRadius(float value) { _outerRadius = value; } - void setInnerRadius(float value) { _innerRadius = value; } - void setHasTickMarks(bool value) { _hasTickMarks = value; } - void setMajorTickMarksAngle(float value) { _majorTickMarksAngle = value; } - void setMinorTickMarksAngle(float value) { _minorTickMarksAngle = value; } - void setMajorTickMarksLength(float value) { _majorTickMarksLength = value; } - void setMinorTickMarksLength(float value) { _minorTickMarksLength = value; } - void setMajorTickMarksColor(const glm::u8vec3& value) { _majorTickMarksColor = value; } - void setMinorTickMarksColor(const glm::u8vec3& value) { _minorTickMarksColor = value; } - - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override; - virtual bool findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, - float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override; - - virtual Circle3DOverlay* createClone() const override; - -protected: - float _startAt { 0 }; - float _endAt { 360 }; - float _outerRadius { 1 }; - float _innerRadius { 0 }; - - glm::u8vec3 _innerStartColor { DEFAULT_OVERLAY_COLOR }; - glm::u8vec3 _innerEndColor { DEFAULT_OVERLAY_COLOR }; - glm::u8vec3 _outerStartColor { DEFAULT_OVERLAY_COLOR }; - glm::u8vec3 _outerEndColor { DEFAULT_OVERLAY_COLOR }; - float _innerStartAlpha { DEFAULT_ALPHA }; - float _innerEndAlpha { DEFAULT_ALPHA }; - float _outerStartAlpha { DEFAULT_ALPHA }; - float _outerEndAlpha { DEFAULT_ALPHA }; - - bool _hasTickMarks { false }; - float _majorTickMarksAngle { 0 }; - float _minorTickMarksAngle { 0 }; - float _majorTickMarksLength { 0 }; - float _minorTickMarksLength { 0 }; - glm::u8vec3 _majorTickMarksColor { DEFAULT_OVERLAY_COLOR }; - glm::u8vec3 _minorTickMarksColor { DEFAULT_OVERLAY_COLOR }; - gpu::Primitive _solidPrimitive { gpu::TRIANGLE_FAN }; - int _quadVerticesID { 0 }; - int _lineVerticesID { 0 }; - int _majorTicksVerticesID { 0 }; - int _minorTicksVerticesID { 0 }; - - bool _dirty { true }; -}; - - -#endif // hifi_Circle3DOverlay_h diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index aecfdba3b8..c382c3de43 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -60,11 +60,12 @@ ContextOverlayInterface::ContextOverlayInterface() { QUuid tabletFrameID = _hmdScriptingInterface->getCurrentTabletFrameID(); auto myAvatar = DependencyManager::get()->getMyAvatar(); glm::quat cameraOrientation = qApp->getCamera().getOrientation(); - QVariantMap props; + + EntityItemProperties properties; float sensorToWorldScale = myAvatar->getSensorToWorldScale(); - props.insert("position", vec3toVariant(myAvatar->getEyePosition() + glm::quat(glm::radians(glm::vec3(0.0f, CONTEXT_OVERLAY_TABLET_OFFSET, 0.0f))) * ((CONTEXT_OVERLAY_TABLET_DISTANCE * sensorToWorldScale) * (cameraOrientation * Vectors::FRONT)))); - props.insert("orientation", quatToVariant(cameraOrientation * glm::quat(glm::radians(glm::vec3(0.0f, CONTEXT_OVERLAY_TABLET_ORIENTATION, 0.0f))))); - qApp->getOverlays().editOverlay(tabletFrameID, props); + properties.setPosition(myAvatar->getEyePosition() + glm::quat(glm::radians(glm::vec3(0.0f, CONTEXT_OVERLAY_TABLET_OFFSET, 0.0f))) * ((CONTEXT_OVERLAY_TABLET_DISTANCE * sensorToWorldScale) * (cameraOrientation * Vectors::FRONT))); + properties.setRotation(cameraOrientation * glm::quat(glm::radians(glm::vec3(0.0f, CONTEXT_OVERLAY_TABLET_ORIENTATION, 0.0f)))); + DependencyManager::get()->editEntity(tabletFrameID, properties); _contextOverlayJustClicked = false; } }); @@ -93,7 +94,6 @@ static const float CONTEXT_OVERLAY_HOVERED_ALPHA = 1.0f; static const float CONTEXT_OVERLAY_UNHOVERED_PULSEMIN = 0.6f; static const float CONTEXT_OVERLAY_UNHOVERED_PULSEMAX = 1.0f; static const float CONTEXT_OVERLAY_UNHOVERED_PULSEPERIOD = 1.0f; -static const float CONTEXT_OVERLAY_UNHOVERED_COLORPULSE = 1.0f; void ContextOverlayInterface::setEnabled(bool enabled) { _enabled = enabled; @@ -192,22 +192,28 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& } // Finally, setup and draw the Context Overlay - if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { - _contextOverlay = std::make_shared(); - _contextOverlay->setAlpha(CONTEXT_OVERLAY_UNHOVERED_ALPHA); - _contextOverlay->setPulseMin(CONTEXT_OVERLAY_UNHOVERED_PULSEMIN); - _contextOverlay->setPulseMax(CONTEXT_OVERLAY_UNHOVERED_PULSEMAX); - _contextOverlay->setColorPulse(CONTEXT_OVERLAY_UNHOVERED_COLORPULSE); - _contextOverlay->setIgnorePickIntersection(false); - _contextOverlay->setDrawInFront(true); - _contextOverlay->setURL(PathUtils::resourcesUrl() + "images/inspect-icon.png"); - _contextOverlay->setIsFacingAvatar(true); - _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); + auto entityScriptingInterface = DependencyManager::get(); + if (_contextOverlayID == UNKNOWN_ENTITY_ID || !entityScriptingInterface->isAddedEntity(_contextOverlayID)) { + EntityItemProperties properties; + properties.setType(EntityTypes::Image); + properties.setAlpha(CONTEXT_OVERLAY_UNHOVERED_ALPHA); + properties.getPulse().setMin(CONTEXT_OVERLAY_UNHOVERED_PULSEMIN); + properties.getPulse().setMax(CONTEXT_OVERLAY_UNHOVERED_PULSEMAX); + properties.getPulse().setColorMode(PulseMode::IN_PHASE); + properties.setIgnorePickIntersection(false); + properties.setRenderLayer(RenderLayer::FRONT); + properties.setImageURL(PathUtils::resourcesUrl() + "images/inspect-icon.png"); + properties.setBillboardMode(BillboardMode::FULL); + + _contextOverlayID = entityScriptingInterface->addEntityInternal(properties, entity::HostType::LOCAL); } - _contextOverlay->setWorldPosition(contextOverlayPosition); - _contextOverlay->setDimensions(contextOverlayDimensions); - _contextOverlay->setWorldOrientation(entityProperties.getRotation()); - _contextOverlay->setVisible(true); + + EntityItemProperties properties; + properties.setPosition(contextOverlayPosition); + properties.setDimensions(glm::vec3(contextOverlayDimensions, 0.01f)); + properties.setRotation(entityProperties.getRotation()); + properties.setVisible(true); + entityScriptingInterface->editEntity(_contextOverlayID, properties); return true; } @@ -227,15 +233,13 @@ bool ContextOverlayInterface::contextOverlayFilterPassed(const EntityItemID& ent } bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { - if (_contextOverlayID != UNKNOWN_OVERLAY_ID) { + if (_contextOverlayID != UNKNOWN_ENTITY_ID) { qCDebug(context_overlay) << "Destroying Context Overlay on top of entity with ID: " << entityItemID; disableEntityHighlight(entityItemID); setCurrentEntityWithContextOverlay(QUuid()); _entityMarketplaceID.clear(); - // Destroy the Context Overlay - qApp->getOverlays().deleteOverlay(_contextOverlayID); - _contextOverlay = NULL; - _contextOverlayID = UNKNOWN_OVERLAY_ID; + DependencyManager::get()->deleteEntity(_contextOverlayID); + _contextOverlayID = UNKNOWN_ENTITY_ID; return true; } return false; @@ -245,45 +249,49 @@ bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt return ContextOverlayInterface::destroyContextOverlay(entityItemID, PointerEvent()); } -void ContextOverlayInterface::contextOverlays_mousePressOnOverlay(const OverlayID& overlayID, const PointerEvent& event) { - if (overlayID == _contextOverlayID && event.getButton() == PointerEvent::PrimaryButton) { - qCDebug(context_overlay) << "Clicked Context Overlay. Entity ID:" << _currentEntityWithContextOverlay << "Overlay ID:" << overlayID; +void ContextOverlayInterface::contextOverlays_mousePressOnOverlay(const QUuid& id, const PointerEvent& event) { + if (id == _contextOverlayID && event.getButton() == PointerEvent::PrimaryButton) { + qCDebug(context_overlay) << "Clicked Context Overlay. Entity ID:" << _currentEntityWithContextOverlay << "ID:" << id; emit contextOverlayClicked(_currentEntityWithContextOverlay); _contextOverlayJustClicked = true; } } -void ContextOverlayInterface::contextOverlays_hoverEnterOverlay(const OverlayID& overlayID, const PointerEvent& event) { - if (_contextOverlayID != UNKNOWN_OVERLAY_ID && _contextOverlay) { - qCDebug(context_overlay) << "Started hovering over Context Overlay. Overlay ID:" << overlayID; - _contextOverlay->setColor(CONTEXT_OVERLAY_COLOR); - _contextOverlay->setColorPulse(0.0f); // pulse off - _contextOverlay->setPulsePeriod(0.0f); // pulse off - _contextOverlay->setAlpha(CONTEXT_OVERLAY_HOVERED_ALPHA); +void ContextOverlayInterface::contextOverlays_hoverEnterOverlay(const QUuid& id, const PointerEvent& event) { + if (_contextOverlayID != UNKNOWN_ENTITY_ID) { + qCDebug(context_overlay) << "Started hovering over Context Overlay. ID:" << id; + EntityItemProperties properties; + properties.setColor(CONTEXT_OVERLAY_COLOR); + properties.getPulse().setColorMode(PulseMode::NONE); + properties.getPulse().setPeriod(0.0f); + properties.setAlpha(CONTEXT_OVERLAY_HOVERED_ALPHA); + DependencyManager::get()->editEntity(_contextOverlayID, properties); } } -void ContextOverlayInterface::contextOverlays_hoverLeaveOverlay(const OverlayID& overlayID, const PointerEvent& event) { - if (_contextOverlayID != UNKNOWN_OVERLAY_ID && _contextOverlay) { - qCDebug(context_overlay) << "Stopped hovering over Context Overlay. Overlay ID:" << overlayID; - _contextOverlay->setColor(CONTEXT_OVERLAY_COLOR); - _contextOverlay->setColorPulse(CONTEXT_OVERLAY_UNHOVERED_COLORPULSE); - _contextOverlay->setPulsePeriod(CONTEXT_OVERLAY_UNHOVERED_PULSEPERIOD); - _contextOverlay->setAlpha(CONTEXT_OVERLAY_UNHOVERED_ALPHA); +void ContextOverlayInterface::contextOverlays_hoverLeaveOverlay(const QUuid& id, const PointerEvent& event) { + if (_contextOverlayID != UNKNOWN_ENTITY_ID) { + qCDebug(context_overlay) << "Stopped hovering over Context Overlay. ID:" << id; + EntityItemProperties properties; + properties.setColor(CONTEXT_OVERLAY_COLOR); + properties.getPulse().setColorMode(PulseMode::IN_PHASE); + properties.getPulse().setPeriod(CONTEXT_OVERLAY_UNHOVERED_PULSEPERIOD); + properties.setAlpha(CONTEXT_OVERLAY_UNHOVERED_ALPHA); + DependencyManager::get()->editEntity(_contextOverlayID, properties); } } -void ContextOverlayInterface::contextOverlays_hoverEnterEntity(const EntityItemID& entityID, const PointerEvent& event) { +void ContextOverlayInterface::contextOverlays_hoverEnterEntity(const EntityItemID& id, const PointerEvent& event) { bool isMouse = event.getID() == PointerManager::MOUSE_POINTER_ID || DependencyManager::get()->isMouse(event.getID()); - if (contextOverlayFilterPassed(entityID) && _enabled && !isMouse) { - enableEntityHighlight(entityID); + if (contextOverlayFilterPassed(id) && _enabled && !isMouse) { + enableEntityHighlight(id); } } -void ContextOverlayInterface::contextOverlays_hoverLeaveEntity(const EntityItemID& entityID, const PointerEvent& event) { +void ContextOverlayInterface::contextOverlays_hoverLeaveEntity(const EntityItemID& id, const PointerEvent& event) { bool isMouse = event.getID() == PointerManager::MOUSE_POINTER_ID || DependencyManager::get()->isMouse(event.getID()); - if (_currentEntityWithContextOverlay != entityID && _enabled && !isMouse) { - disableEntityHighlight(entityID); + if (_currentEntityWithContextOverlay != id && _enabled && !isMouse) { + disableEntityHighlight(id); } } @@ -380,12 +388,12 @@ void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID } } -void ContextOverlayInterface::enableEntityHighlight(const EntityItemID& entityItemID) { - _selectionScriptingInterface->addToSelectedItemsList("contextOverlayHighlightList", "entity", entityItemID); +void ContextOverlayInterface::enableEntityHighlight(const EntityItemID& entityID) { + _selectionScriptingInterface->addToSelectedItemsList("contextOverlayHighlightList", "entity", entityID); } -void ContextOverlayInterface::disableEntityHighlight(const EntityItemID& entityItemID) { - _selectionScriptingInterface->removeFromSelectedItemsList("contextOverlayHighlightList", "entity", entityItemID); +void ContextOverlayInterface::disableEntityHighlight(const EntityItemID& entityID) { + _selectionScriptingInterface->removeFromSelectedItemsList("contextOverlayHighlightList", "entity", entityID); } void ContextOverlayInterface::deletingEntity(const EntityItemID& entityID) { diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 48b14e1a91..b87535acf2 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -22,8 +22,6 @@ #include "avatar/AvatarManager.h" #include "EntityScriptingInterface.h" -#include "ui/overlays/Image3DOverlay.h" -#include "ui/overlays/Overlays.h" #include "scripting/HMDScriptingInterface.h" #include "scripting/SelectionScriptingInterface.h" #include "scripting/WalletScriptingInterface.h" @@ -43,8 +41,7 @@ class ContextOverlayInterface : public QObject, public Dependency { QSharedPointer _hmdScriptingInterface; QSharedPointer _tabletScriptingInterface; QSharedPointer _selectionScriptingInterface; - OverlayID _contextOverlayID { UNKNOWN_OVERLAY_ID }; - std::shared_ptr _contextOverlay { nullptr }; + QUuid _contextOverlayID { UNKNOWN_ENTITY_ID }; public: ContextOverlayInterface(); Q_INVOKABLE QUuid getCurrentEntityWithContextOverlay() { return _currentEntityWithContextOverlay; } @@ -68,9 +65,9 @@ public slots: bool createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); bool destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); bool destroyContextOverlay(const EntityItemID& entityItemID); - void contextOverlays_mousePressOnOverlay(const OverlayID& overlayID, const PointerEvent& event); - void contextOverlays_hoverEnterOverlay(const OverlayID& overlayID, const PointerEvent& event); - void contextOverlays_hoverLeaveOverlay(const OverlayID& overlayID, const PointerEvent& event); + void contextOverlays_mousePressOnOverlay(const QUuid& id, const PointerEvent& event); + void contextOverlays_hoverEnterOverlay(const QUuid& id, const PointerEvent& event); + void contextOverlays_hoverLeaveOverlay(const QUuid& id, const PointerEvent& event); void contextOverlays_hoverEnterEntity(const EntityItemID& entityID, const PointerEvent& event); void contextOverlays_hoverLeaveEntity(const EntityItemID& entityID, const PointerEvent& event); bool contextOverlayFilterPassed(const EntityItemID& entityItemID); @@ -83,12 +80,12 @@ private: enum { MAX_SELECTION_COUNT = 16 }; - bool _verboseLogging{ true }; + bool _verboseLogging { true }; bool _enabled { true }; - EntityItemID _mouseDownEntity{}; + EntityItemID _mouseDownEntity; quint64 _mouseDownEntityTimestamp; - EntityItemID _currentEntityWithContextOverlay{}; - EntityItemID _lastInspectedEntity{}; + EntityItemID _currentEntityWithContextOverlay; + EntityItemID _lastInspectedEntity; QString _entityMarketplaceID; bool _contextOverlayJustClicked { false }; diff --git a/interface/src/ui/overlays/Cube3DOverlay.cpp b/interface/src/ui/overlays/Cube3DOverlay.cpp deleted file mode 100644 index 9888d696cf..0000000000 --- a/interface/src/ui/overlays/Cube3DOverlay.cpp +++ /dev/null @@ -1,196 +0,0 @@ -// -// Cube3DOverlay.cpp -// interface/src/ui/overlays -// -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -// include this before QGLWidget, which includes an earlier version of OpenGL -#include "Cube3DOverlay.h" - -#include -#include -#include -#include - -QString const Cube3DOverlay::TYPE = "cube"; - -Cube3DOverlay::Cube3DOverlay() { - auto geometryCache = DependencyManager::get(); - for (size_t i = 0; i < _geometryIds.size(); ++i) { - _geometryIds[i] = geometryCache->allocateID(); - } -} - -Cube3DOverlay::Cube3DOverlay(const Cube3DOverlay* cube3DOverlay) : - Volume3DOverlay(cube3DOverlay) -{ - auto geometryCache = DependencyManager::get(); - for (size_t i = 0; i < _geometryIds.size(); ++i) { - _geometryIds[i] = geometryCache->allocateID(); - } -} - -Cube3DOverlay::~Cube3DOverlay() { - auto geometryCache = DependencyManager::get(); - if (geometryCache) { - for (size_t i = 0; i < _geometryIds.size(); ++i) { - geometryCache->releaseID(_geometryIds[i]); - } - } -} - -void Cube3DOverlay::render(RenderArgs* args) { - if (!_renderVisible) { - return; // do nothing if we're not visible - } - - float alpha = getAlpha(); - glm::u8vec3 color = getColor(); - glm::vec4 cubeColor(toGlm(color), alpha); - - auto batch = args->_batch; - if (batch) { - Transform transform = getRenderTransform(); - auto geometryCache = DependencyManager::get(); - auto shapePipeline = args->_shapePipeline; - if (!shapePipeline) { - shapePipeline = _isSolid ? geometryCache->getOpaqueShapePipeline() : geometryCache->getWireShapePipeline(); - } - - if (_isSolid) { - batch->setModelTransform(transform); - geometryCache->renderSolidCubeInstance(args, *batch, cubeColor, shapePipeline); - } else { - geometryCache->bindSimpleProgram(*batch, false, false, false, true, true); - if (getIsDashedLine()) { - auto dimensions = transform.getScale(); - transform.setScale(1.0f); - batch->setModelTransform(transform); - - glm::vec3 halfDimensions = dimensions / 2.0f; - glm::vec3 bottomLeftNear(-halfDimensions.x, -halfDimensions.y, -halfDimensions.z); - glm::vec3 bottomRightNear(halfDimensions.x, -halfDimensions.y, -halfDimensions.z); - glm::vec3 topLeftNear(-halfDimensions.x, halfDimensions.y, -halfDimensions.z); - glm::vec3 topRightNear(halfDimensions.x, halfDimensions.y, -halfDimensions.z); - - glm::vec3 bottomLeftFar(-halfDimensions.x, -halfDimensions.y, halfDimensions.z); - glm::vec3 bottomRightFar(halfDimensions.x, -halfDimensions.y, halfDimensions.z); - glm::vec3 topLeftFar(-halfDimensions.x, halfDimensions.y, halfDimensions.z); - glm::vec3 topRightFar(halfDimensions.x, halfDimensions.y, halfDimensions.z); - - geometryCache->renderDashedLine(*batch, bottomLeftNear, bottomRightNear, cubeColor, _geometryIds[0]); - geometryCache->renderDashedLine(*batch, bottomRightNear, bottomRightFar, cubeColor, _geometryIds[1]); - geometryCache->renderDashedLine(*batch, bottomRightFar, bottomLeftFar, cubeColor, _geometryIds[2]); - geometryCache->renderDashedLine(*batch, bottomLeftFar, bottomLeftNear, cubeColor, _geometryIds[3]); - - geometryCache->renderDashedLine(*batch, topLeftNear, topRightNear, cubeColor, _geometryIds[4]); - geometryCache->renderDashedLine(*batch, topRightNear, topRightFar, cubeColor, _geometryIds[5]); - geometryCache->renderDashedLine(*batch, topRightFar, topLeftFar, cubeColor, _geometryIds[6]); - geometryCache->renderDashedLine(*batch, topLeftFar, topLeftNear, cubeColor, _geometryIds[7]); - - geometryCache->renderDashedLine(*batch, bottomLeftNear, topLeftNear, cubeColor, _geometryIds[8]); - geometryCache->renderDashedLine(*batch, bottomRightNear, topRightNear, cubeColor, _geometryIds[9]); - geometryCache->renderDashedLine(*batch, bottomLeftFar, topLeftFar, cubeColor, _geometryIds[10]); - geometryCache->renderDashedLine(*batch, bottomRightFar, topRightFar, cubeColor, _geometryIds[11]); - - } else { - batch->setModelTransform(transform); - geometryCache->renderWireCubeInstance(args, *batch, cubeColor, shapePipeline); - } - } - } -} - -const render::ShapeKey Cube3DOverlay::getShapeKey() { - auto builder = render::ShapeKey::Builder(); - if (isTransparent()) { - builder.withTranslucent(); - } - if (!getIsSolid()) { - builder.withUnlit().withDepthBias(); - } - return builder.build(); -} - -Cube3DOverlay* Cube3DOverlay::createClone() const { - return new Cube3DOverlay(this); -} - -void Cube3DOverlay::setProperties(const QVariantMap& properties) { - Volume3DOverlay::setProperties(properties); -} - -/**jsdoc - * These are the properties of a cube {@link Overlays.OverlayType|OverlayType}. - * @typedef {object} Overlays.CubeProperties - * - * @property {string} type=cube - Has the value "cube". Read-only. - * @property {Color} color=255,255,255 - The color of the overlay. - * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. - * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. - * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. - * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from - * pulseMin to pulseMax, then pulseMax to pulseMin in one period. - * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 - * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise - * used.) - * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 - * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise - * used.) - * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. - * - * @property {string} name="" - A friendly name for the overlay. - * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and - * start. - * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a - * parentID set, otherwise the same value as position. - * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. - * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a - * parentID set, otherwise the same value as rotation. - * @property {boolean} isSolid=false - Synonyms: solid, isFilled, and filled. - * Antonyms: isWire and wire. - * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: - * dashed. Deprecated. - * @property {boolean} ignorePickIntersection=false - If true, picks ignore the overlay. ignoreRayIntersection is a synonym. - * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't - * have drawInFront set to true, and in front of entities. - * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. - * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. - * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if - * parentID is an avatar skeleton. A value of 65535 means "no joint". - * - * @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: scale, size. - */ -QVariant Cube3DOverlay::getProperty(const QString& property) { - return Volume3DOverlay::getProperty(property); -} - -Transform Cube3DOverlay::evalRenderTransform() { - // TODO: handle registration point?? - glm::vec3 position = getWorldPosition(); - glm::vec3 dimensions = getDimensions(); - glm::quat rotation = getWorldOrientation(); - - Transform transform; - transform.setScale(dimensions); - transform.setTranslation(position); - transform.setRotation(rotation); - return transform; -} - -scriptable::ScriptableModelBase Cube3DOverlay::getScriptableModel() { - auto geometryCache = DependencyManager::get(); - auto vertexColor = ColorUtils::toVec3(_color); - scriptable::ScriptableModelBase result; - if (auto mesh = geometryCache->meshFromShape(GeometryCache::Cube, vertexColor)) { - result.objectID = getID(); - result.append(mesh); - } - return result; -} diff --git a/interface/src/ui/overlays/Cube3DOverlay.h b/interface/src/ui/overlays/Cube3DOverlay.h deleted file mode 100644 index d28d11920a..0000000000 --- a/interface/src/ui/overlays/Cube3DOverlay.h +++ /dev/null @@ -1,45 +0,0 @@ -// -// Cube3DOverlay.h -// interface/src/ui/overlays -// -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_Cube3DOverlay_h -#define hifi_Cube3DOverlay_h - -#include "Volume3DOverlay.h" - -class Cube3DOverlay : public Volume3DOverlay { - Q_OBJECT - -public: - static QString const TYPE; - virtual QString getType() const override { return TYPE; } - - Cube3DOverlay(); - Cube3DOverlay(const Cube3DOverlay* cube3DOverlay); - ~Cube3DOverlay(); - - virtual void render(RenderArgs* args) override; - virtual const render::ShapeKey getShapeKey() override; - - virtual Cube3DOverlay* createClone() const override; - - void setProperties(const QVariantMap& properties) override; - QVariant getProperty(const QString& property) override; - - virtual scriptable::ScriptableModelBase getScriptableModel() override; -protected: - Transform evalRenderTransform() override; - -private: - // edges on a cube - std::array _geometryIds; -}; - - -#endif // hifi_Cube3DOverlay_h diff --git a/interface/src/ui/overlays/Grid3DOverlay.cpp b/interface/src/ui/overlays/Grid3DOverlay.cpp deleted file mode 100644 index 92481b8116..0000000000 --- a/interface/src/ui/overlays/Grid3DOverlay.cpp +++ /dev/null @@ -1,200 +0,0 @@ -// -// Grid3DOverlay.cpp -// interface/src/ui/overlays -// -// Created by Ryan Huffman on 11/06/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "Grid3DOverlay.h" - -#include -#include -#include -#include -#include - - -QString const Grid3DOverlay::TYPE = "grid"; -const float DEFAULT_SCALE = 100.0f; - -Grid3DOverlay::Grid3DOverlay() { - setDimensions(DEFAULT_SCALE); - updateGrid(); - _geometryId = DependencyManager::get()->allocateID(); -} - -Grid3DOverlay::Grid3DOverlay(const Grid3DOverlay* grid3DOverlay) : - Planar3DOverlay(grid3DOverlay), - _majorGridEvery(grid3DOverlay->_majorGridEvery), - _minorGridEvery(grid3DOverlay->_minorGridEvery) -{ - updateGrid(); - _geometryId = DependencyManager::get()->allocateID(); -} - -Grid3DOverlay::~Grid3DOverlay() { - auto geometryCache = DependencyManager::get(); - if (geometryCache) { - geometryCache->releaseID(_geometryId); - } -} - -AABox Grid3DOverlay::getBounds() const { - if (_followCamera) { - // This is a UI element that should always be in view, lie to the octree to avoid culling - const AABox DOMAIN_BOX = AABox(glm::vec3(-TREE_SCALE / 2), TREE_SCALE); - return DOMAIN_BOX; - } - return Planar3DOverlay::getBounds(); -} - -void Grid3DOverlay::render(RenderArgs* args) { - if (!_renderVisible) { - return; // do nothing if we're not visible - } - - float alpha = getAlpha(); - glm::u8vec3 color = getColor(); - glm::vec4 gridColor(toGlm(color), alpha); - - auto batch = args->_batch; - - if (batch) { - auto minCorner = glm::vec2(-0.5f, -0.5f); - auto maxCorner = glm::vec2(0.5f, 0.5f); - - auto position = getWorldPosition(); - if (_followCamera) { - // Get the camera position rounded to the nearest major grid line - // This grid is for UI and should lie on worldlines - auto cameraPosition = - (float)_majorGridEvery * glm::round(args->getViewFrustum().getPosition() / (float)_majorGridEvery); - - position += glm::vec3(cameraPosition.x, 0.0f, cameraPosition.z); - } - - Transform transform = getRenderTransform(); - transform.setTranslation(position); - batch->setModelTransform(transform); - const float MINOR_GRID_EDGE = 0.0025f; - const float MAJOR_GRID_EDGE = 0.005f; - DependencyManager::get()->renderGrid(*batch, minCorner, maxCorner, - _minorGridRowDivisions, _minorGridColDivisions, MINOR_GRID_EDGE, - _majorGridRowDivisions, _majorGridColDivisions, MAJOR_GRID_EDGE, - gridColor, _geometryId); - } -} - -const render::ShapeKey Grid3DOverlay::getShapeKey() { - return render::ShapeKey::Builder().withOwnPipeline().withUnlit().withDepthBias(); -} - -void Grid3DOverlay::setProperties(const QVariantMap& properties) { - Planar3DOverlay::setProperties(properties); - if (properties["followCamera"].isValid()) { - _followCamera = properties["followCamera"].toBool(); - } - - if (properties["majorGridEvery"].isValid()) { - _majorGridEvery = properties["majorGridEvery"].toInt(); - } - - if (properties["minorGridEvery"].isValid()) { - _minorGridEvery = properties["minorGridEvery"].toFloat(); - } - - updateGrid(); -} - -/**jsdoc - * These are the properties of a grid {@link Overlays.OverlayType|OverlayType}. - * @typedef {object} Overlays.GridProperties - * - * @property {string} type=grid - Has the value "grid". Read-only. - * @property {Color} color=255,255,255 - The color of the overlay. - * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. - * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. - * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. - * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from - * pulseMin to pulseMax, then pulseMax to pulseMin in one period. - * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 - * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise - * used.) - * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 - * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise - * used.) - * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. - * - * @property {string} name="" - A friendly name for the overlay. - * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and - * start. - * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a - * parentID set, otherwise the same value as position. - * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. - * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a - * parentID set, otherwise the same value as rotation. - * @property {boolean} isSolid=false - Synonyms: solid, isFilled, and filled. - * Antonyms: isWire and wire. - * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: - * dashed. Deprecated. - * @property {boolean} ignorePickIntersection=false - If true, picks ignore the overlay. ignoreRayIntersection is a synonym. - * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't - * have drawInFront set to true, and in front of entities. - * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. - * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. - * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if - * parentID is an avatar skeleton. A value of 65535 means "no joint". - * - * @property {Vec2} dimensions=1,1 - The dimensions of the overlay. Synonyms: scale, size. - * - * @property {boolean} followCamera=true - If true, the grid is always visible even as the camera moves to another - * position. - * @property {number} majorGridEvery=5 - Integer number of minorGridEvery intervals at which to draw a thick grid - * line. Minimum value = 1. - * @property {number} minorGridEvery=1 - Real number of meters at which to draw thin grid lines. Minimum value = - * 0.001. - */ -QVariant Grid3DOverlay::getProperty(const QString& property) { - if (property == "followCamera") { - return _followCamera; - } - if (property == "majorGridEvery") { - return _majorGridEvery; - } - if (property == "minorGridEvery") { - return _minorGridEvery; - } - - return Planar3DOverlay::getProperty(property); -} - -Grid3DOverlay* Grid3DOverlay::createClone() const { - return new Grid3DOverlay(this); -} - -void Grid3DOverlay::updateGrid() { - const int MAJOR_GRID_EVERY_MIN = 1; - const float MINOR_GRID_EVERY_MIN = 0.01f; - - _majorGridEvery = std::max(_majorGridEvery, MAJOR_GRID_EVERY_MIN); - _minorGridEvery = std::max(_minorGridEvery, MINOR_GRID_EVERY_MIN); - - _majorGridRowDivisions = getDimensions().x / _majorGridEvery; - _majorGridColDivisions = getDimensions().y / _majorGridEvery; - - _minorGridRowDivisions = getDimensions().x / _minorGridEvery; - _minorGridColDivisions = getDimensions().y / _minorGridEvery; -} - -Transform Grid3DOverlay::evalRenderTransform() { - Transform transform; - transform.setRotation(getWorldOrientation()); - transform.setScale(glm::vec3(getDimensions(), 1.0f)); - return transform; -} diff --git a/interface/src/ui/overlays/Grid3DOverlay.h b/interface/src/ui/overlays/Grid3DOverlay.h deleted file mode 100644 index 64b65b3178..0000000000 --- a/interface/src/ui/overlays/Grid3DOverlay.h +++ /dev/null @@ -1,61 +0,0 @@ -// -// Grid3DOverlay.h -// interface/src/ui/overlays -// -// Created by Ryan Huffman on 11/06/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_Grid3DOverlay_h -#define hifi_Grid3DOverlay_h - -#include "Planar3DOverlay.h" - -class Grid3DOverlay : public Planar3DOverlay { - Q_OBJECT - -public: - static QString const TYPE; - virtual QString getType() const override { return TYPE; } - - Grid3DOverlay(); - Grid3DOverlay(const Grid3DOverlay* grid3DOverlay); - ~Grid3DOverlay(); - - virtual AABox getBounds() const override; - - virtual void render(RenderArgs* args) override; - virtual const render::ShapeKey getShapeKey() override; - void setProperties(const QVariantMap& properties) override; - QVariant getProperty(const QString& property) override; - - virtual Grid3DOverlay* createClone() const override; - - // Grids are UI tools, and may not be intersected (pickable) - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, - glm::vec3& surfaceNormal, bool precisionPicking = false) override { return false; } - virtual bool findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, - float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override { return false; } - -protected: - Transform evalRenderTransform() override; - -private: - void updateGrid(); - - bool _followCamera { true }; - - int _majorGridEvery { 5 }; - float _majorGridRowDivisions; - float _majorGridColDivisions; - - float _minorGridEvery { 1.0f }; - float _minorGridRowDivisions; - float _minorGridColDivisions; - int _geometryId { 0 }; -}; - -#endif // hifi_Grid3DOverlay_h diff --git a/interface/src/ui/overlays/Image3DOverlay.cpp b/interface/src/ui/overlays/Image3DOverlay.cpp deleted file mode 100644 index dcf3ca2285..0000000000 --- a/interface/src/ui/overlays/Image3DOverlay.cpp +++ /dev/null @@ -1,341 +0,0 @@ -// -// Image3DOverlay.cpp -// -// -// Created by Clement on 7/1/14. -// Modified and renamed by Zander Otavka on 8/4/15 -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "Image3DOverlay.h" - -#include -#include -#include -#include - -#include "GeometryUtil.h" - -#include "AbstractViewStateInterface.h" - -QString const Image3DOverlay::TYPE = "image3d"; - -Image3DOverlay::Image3DOverlay() { - _isLoaded = false; - _geometryId = DependencyManager::get()->allocateID(); -} - -Image3DOverlay::Image3DOverlay(const Image3DOverlay* image3DOverlay) : - Billboard3DOverlay(image3DOverlay), - _url(image3DOverlay->_url), - _texture(image3DOverlay->_texture), - _emissive(image3DOverlay->_emissive), - _fromImage(image3DOverlay->_fromImage) -{ - _geometryId = DependencyManager::get()->allocateID(); -} - -Image3DOverlay::~Image3DOverlay() { - auto geometryCache = DependencyManager::get(); - if (geometryCache) { - geometryCache->releaseID(_geometryId); - } -} - -void Image3DOverlay::update(float deltatime) { - if (!_isLoaded) { - _isLoaded = true; - _texture = DependencyManager::get()->getTexture(_url); - _textureIsLoaded = false; - } - Parent::update(deltatime); -} - -void Image3DOverlay::render(RenderArgs* args) { - if (!_renderVisible || !getParentVisible() || !_texture || !_texture->isLoaded()) { - return; - } - - // Once the texture has loaded, check if we need to update the render item because of transparency - if (!_textureIsLoaded && _texture && _texture->getGPUTexture()) { - _textureIsLoaded = true; - bool prevAlphaTexture = _alphaTexture; - _alphaTexture = _texture->getGPUTexture()->getUsage().isAlpha(); - if (_alphaTexture != prevAlphaTexture) { - auto itemID = getRenderItemID(); - if (render::Item::isValidID(itemID)) { - render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); - render::Transaction transaction; - transaction.updateItem(itemID); - scene->enqueueTransaction(transaction); - } - } - } - - Q_ASSERT(args->_batch); - gpu::Batch* batch = args->_batch; - - float imageWidth = _texture->getWidth(); - float imageHeight = _texture->getHeight(); - - QRect fromImage; - if (_fromImage.width() <= 0) { - fromImage.setX(0); - fromImage.setWidth(imageWidth); - } else { - float scaleX = imageWidth / _texture->getOriginalWidth(); - fromImage.setX(scaleX * _fromImage.x()); - fromImage.setWidth(scaleX * _fromImage.width()); - } - - if (_fromImage.height() <= 0) { - fromImage.setY(0); - fromImage.setHeight(imageHeight); - } else { - float scaleY = imageHeight / _texture->getOriginalHeight(); - fromImage.setY(scaleY * _fromImage.y()); - fromImage.setHeight(scaleY * _fromImage.height()); - } - - float maxSize = glm::max(fromImage.width(), fromImage.height()); - float x = _keepAspectRatio ? fromImage.width() / (2.0f * maxSize) : 0.5f; - float y = _keepAspectRatio ? -fromImage.height() / (2.0f * maxSize) : -0.5f; - - glm::vec2 topLeft(-x, -y); - glm::vec2 bottomRight(x, y); - glm::vec2 texCoordTopLeft((fromImage.x() + 0.5f) / imageWidth, (fromImage.y() + 0.5f) / imageHeight); - glm::vec2 texCoordBottomRight((fromImage.x() + fromImage.width() - 0.5f) / imageWidth, - (fromImage.y() + fromImage.height() - 0.5f) / imageHeight); - - float alpha = getAlpha(); - glm::u8vec3 color = getColor(); - glm::vec4 imageColor(toGlm(color), alpha); - - batch->setModelTransform(getRenderTransform()); - batch->setResourceTexture(0, _texture->getGPUTexture()); - - DependencyManager::get()->renderQuad( - *batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, - imageColor, _geometryId - ); - - batch->setResourceTexture(0, nullptr); // restore default white color after me -} - -const render::ShapeKey Image3DOverlay::getShapeKey() { - auto builder = render::ShapeKey::Builder().withoutCullFace().withDepthBias(); - if (_emissive) { - builder.withUnlit(); - } - if (isTransparent()) { - builder.withTranslucent(); - } - return builder.build(); -} - -void Image3DOverlay::setProperties(const QVariantMap& properties) { - Billboard3DOverlay::setProperties(properties); - - auto urlValue = properties["url"]; - if (urlValue.isValid()) { - QString newURL = urlValue.toString(); - if (newURL != _url) { - setURL(newURL); - } - } - - auto subImageBoundsVar = properties["subImage"]; - if (subImageBoundsVar.isValid()) { - if (subImageBoundsVar.isNull()) { - _fromImage = QRect(); - } else { - QRect oldSubImageRect = _fromImage; - QRect subImageRect = _fromImage; - auto subImageBounds = subImageBoundsVar.toMap(); - if (subImageBounds["x"].isValid()) { - subImageRect.setX(subImageBounds["x"].toInt()); - } else { - subImageRect.setX(oldSubImageRect.x()); - } - if (subImageBounds["y"].isValid()) { - subImageRect.setY(subImageBounds["y"].toInt()); - } else { - subImageRect.setY(oldSubImageRect.y()); - } - if (subImageBounds["width"].isValid()) { - subImageRect.setWidth(subImageBounds["width"].toInt()); - } else { - subImageRect.setWidth(oldSubImageRect.width()); - } - if (subImageBounds["height"].isValid()) { - subImageRect.setHeight(subImageBounds["height"].toInt()); - } else { - subImageRect.setHeight(oldSubImageRect.height()); - } - setClipFromSource(subImageRect); - } - } - - auto keepAspectRatioValue = properties["keepAspectRatio"]; - if (keepAspectRatioValue.isValid()) { - _keepAspectRatio = keepAspectRatioValue.toBool(); - } - - auto emissiveValue = properties["emissive"]; - if (emissiveValue.isValid()) { - _emissive = emissiveValue.toBool(); - } -} - -/**jsdoc - * These are the properties of an image3d {@link Overlays.OverlayType|OverlayType}. - * @typedef {object} Overlays.Image3DProperties - * - * @property {string} type=image3d - Has the value "image3d". Read-only. - * @property {Color} color=255,255,255 - The color of the overlay. - * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. - * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. - * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. - * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from - * pulseMin to pulseMax, then pulseMax to pulseMin in one period. - * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 - * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise - * used.) - * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 - * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise - * used.) - * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. - * - * @property {string} name="" - A friendly name for the overlay. - * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and - * start. - * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a - * parentID set, otherwise the same value as position. - * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. - * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a - * parentID set, otherwise the same value as rotation. - * @property {boolean} isSolid=false - Synonyms: solid, isFilled, and filled. - * Antonyms: isWire and wire. - * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: - * dashed. Deprecated. - * @property {boolean} ignorePickIntersection=false - If true, picks ignore the overlay. ignoreRayIntersection is a synonym. - * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't - * have drawInFront set to true, and in front of entities. - * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. - * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. - * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if - * parentID is an avatar skeleton. A value of 65535 means "no joint". - * - * @property {Vec2} dimensions=1,1 - The dimensions of the overlay. Synonyms: scale, size. - * - * @property {bool} keepAspectRatio=true - overlays will maintain the aspect ratio when the subImage is applied. - * - * @property {boolean} isFacingAvatar - If true, the overlay is rotated to face the user's camera about an axis - * parallel to the user's avatar's "up" direction. - * - * @property {string} url - The URL of the PNG or JPG image to display. - * @property {Rect} subImage - The portion of the image to display. Defaults to the full image. - * @property {boolean} emissive - If true, the overlay is displayed at full brightness, otherwise it is rendered - * with scene lighting. - */ -QVariant Image3DOverlay::getProperty(const QString& property) { - if (property == "url") { - return _url; - } - if (property == "subImage") { - return _fromImage; - } - if (property == "emissive") { - return _emissive; - } - if (property == "keepAspectRatio") { - return _keepAspectRatio; - } - - return Billboard3DOverlay::getProperty(property); -} - -void Image3DOverlay::setURL(const QString& url) { - _url = url; - _isLoaded = false; -} - -bool Image3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) { - if (_texture && _texture->isLoaded()) { - Transform transform = getTransform(); - - // Produce the dimensions of the overlay based on the image's aspect ratio and the overlay's scale. - bool isNull = _fromImage.isNull(); - float width = isNull ? _texture->getWidth() : _fromImage.width(); - float height = isNull ? _texture->getHeight() : _fromImage.height(); - float maxSize = glm::max(width, height); - glm::vec2 dimensions = _dimensions * glm::vec2(width / maxSize, height / maxSize); - glm::quat rotation = transform.getRotation(); - - if (findRayRectangleIntersection(origin, direction, rotation, transform.getTranslation(), dimensions, distance)) { - glm::vec3 forward = rotation * Vectors::FRONT; - if (glm::dot(forward, direction) > 0.0f) { - face = MAX_Z_FACE; - surfaceNormal = -forward; - } else { - face = MIN_Z_FACE; - surfaceNormal = forward; - } - return true; - } - } - - return false; -} - -bool Image3DOverlay::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, - float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) { - if (_texture && _texture->isLoaded()) { - Transform transform = getTransform(); - - // Produce the dimensions of the overlay based on the image's aspect ratio and the overlay's scale. - bool isNull = _fromImage.isNull(); - float width = isNull ? _texture->getWidth() : _fromImage.width(); - float height = isNull ? _texture->getHeight() : _fromImage.height(); - float maxSize = glm::max(width, height); - glm::vec2 dimensions = _dimensions * glm::vec2(width / maxSize, height / maxSize); - glm::quat rotation = transform.getRotation(); - glm::vec3 position = getWorldPosition(); - - glm::quat inverseRot = glm::inverse(rotation); - glm::vec3 localOrigin = inverseRot * (origin - position); - glm::vec3 localVelocity = inverseRot * velocity; - glm::vec3 localAcceleration = inverseRot * acceleration; - - if (findParabolaRectangleIntersection(localOrigin, localVelocity, localAcceleration, dimensions, parabolicDistance)) { - float localIntersectionVelocityZ = localVelocity.z + localAcceleration.z * parabolicDistance; - glm::vec3 forward = rotation * Vectors::FRONT; - if (localIntersectionVelocityZ > 0.0f) { - face = MIN_Z_FACE; - surfaceNormal = forward; - } else { - face = MAX_Z_FACE; - surfaceNormal = -forward; - } - return true; - } - } - - return false; -} - -Image3DOverlay* Image3DOverlay::createClone() const { - return new Image3DOverlay(this); -} - -Transform Image3DOverlay::evalRenderTransform() { - auto transform = Parent::evalRenderTransform(); - transform.postScale(glm::vec3(getDimensions(), 1.0f)); - return transform; -} diff --git a/interface/src/ui/overlays/Image3DOverlay.h b/interface/src/ui/overlays/Image3DOverlay.h deleted file mode 100644 index 1000401abb..0000000000 --- a/interface/src/ui/overlays/Image3DOverlay.h +++ /dev/null @@ -1,67 +0,0 @@ -// -// Image3DOverlay.h -// -// -// Created by Clement on 7/1/14. -// Modified and renamed by Zander Otavka on 8/4/15 -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_Image3DOverlay_h -#define hifi_Image3DOverlay_h - -#include - -#include "Billboard3DOverlay.h" - -class Image3DOverlay : public Billboard3DOverlay { - Q_OBJECT - using Parent = Billboard3DOverlay; - -public: - static QString const TYPE; - virtual QString getType() const override { return TYPE; } - - Image3DOverlay(); - Image3DOverlay(const Image3DOverlay* image3DOverlay); - ~Image3DOverlay(); - virtual void render(RenderArgs* args) override; - - virtual void update(float deltatime) override; - - virtual const render::ShapeKey getShapeKey() override; - - // setters - void setURL(const QString& url); - void setClipFromSource(const QRect& bounds) { _fromImage = bounds; } - - void setProperties(const QVariantMap& properties) override; - QVariant getProperty(const QString& property) override; - bool isTransparent() override { return Base3DOverlay::isTransparent() || _alphaTexture; } - - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override; - virtual bool findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, float& parabolicDistance, - BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override; - - virtual Image3DOverlay* createClone() const override; - -protected: - Transform evalRenderTransform() override; - -private: - QString _url; - NetworkTexturePointer _texture; - bool _textureIsLoaded { false }; - bool _alphaTexture { false }; - bool _emissive { false }; - bool _keepAspectRatio { true }; - - QRect _fromImage; // where from in the image to sample - int _geometryId { 0 }; -}; - -#endif // hifi_Image3DOverlay_h diff --git a/interface/src/ui/overlays/ImageOverlay.cpp b/interface/src/ui/overlays/ImageOverlay.cpp index 19f32511f6..228c904026 100644 --- a/interface/src/ui/overlays/ImageOverlay.cpp +++ b/interface/src/ui/overlays/ImageOverlay.cpp @@ -19,29 +19,6 @@ QString const ImageOverlay::TYPE = "image"; QUrl const ImageOverlay::URL(QString("hifi/overlays/ImageOverlay.qml")); -// ImageOverlay's properties are defined in the QML file specified above. -/**jsdoc - * These are the properties of an image {@link Overlays.OverlayType|OverlayType}. - * @typedef {object} Overlays.ImageProperties - * - * @property {Rect} bounds - The position and size of the image display area, in pixels. Write-only. - * @property {number} x - Integer left, x-coordinate value of the image display area = bounds.x. - * Write-only. - * @property {number} y - Integer top, y-coordinate value of the image display area = bounds.y. - * Write-only. - * @property {number} width - Integer width of the image display area = bounds.width. Write-only. - * @property {number} height - Integer height of the image display area = bounds.height. Write-only. - * @property {string} imageURL - The URL of the image file to display. The image is scaled to fit to the bounds. - * Write-only. - * @property {Vec2} subImage=0,0 - Integer coordinates of the top left pixel to start using image content from. - * Write-only. - * @property {Color} color=0,0,0 - The color to apply over the top of the image to colorize it. Write-only. - * @property {number} alpha=0.0 - The opacity of the color applied over the top of the image, 0.0 - - * 1.0. Write-only. - * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. - * Write-only. - */ - ImageOverlay::ImageOverlay() : QmlOverlay(URL) { } @@ -50,5 +27,4 @@ ImageOverlay::ImageOverlay(const ImageOverlay* imageOverlay) : ImageOverlay* ImageOverlay::createClone() const { return new ImageOverlay(this); -} - +} \ No newline at end of file diff --git a/interface/src/ui/overlays/Line3DOverlay.cpp b/interface/src/ui/overlays/Line3DOverlay.cpp deleted file mode 100644 index b1c316c6af..0000000000 --- a/interface/src/ui/overlays/Line3DOverlay.cpp +++ /dev/null @@ -1,366 +0,0 @@ -// -// Line3DOverlay.cpp -// interface/src/ui/overlays -// -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "Line3DOverlay.h" - -#include -#include - -#include "AbstractViewStateInterface.h" - -QString const Line3DOverlay::TYPE = "line3d"; - -Line3DOverlay::Line3DOverlay() : - _geometryCacheID(DependencyManager::get()->allocateID()) -{ -} - -Line3DOverlay::Line3DOverlay(const Line3DOverlay* line3DOverlay) : - Base3DOverlay(line3DOverlay), - _geometryCacheID(DependencyManager::get()->allocateID()) -{ - setParentID(line3DOverlay->getParentID()); - setParentJointIndex(line3DOverlay->getParentJointIndex()); - setLocalTransform(line3DOverlay->getLocalTransform()); - _direction = line3DOverlay->getDirection(); - _length = line3DOverlay->getLength(); - _endParentID = line3DOverlay->getEndParentID(); - _endParentJointIndex = line3DOverlay->getEndJointIndex(); - _lineWidth = line3DOverlay->getLineWidth(); - _glow = line3DOverlay->getGlow(); -} - -Line3DOverlay::~Line3DOverlay() { - auto geometryCache = DependencyManager::get(); - if (_geometryCacheID && geometryCache) { - geometryCache->releaseID(_geometryCacheID); - } -} - -glm::vec3 Line3DOverlay::getStart() const { - return getWorldPosition(); -} - -glm::vec3 Line3DOverlay::getEnd() const { - bool success; - glm::vec3 localEnd; - glm::vec3 worldEnd; - - if (_endParentID != QUuid()) { - glm::vec3 localOffset = _direction * _length; - bool success; - worldEnd = localToWorld(localOffset, _endParentID, _endParentJointIndex, getScalesWithParent(), success); - return worldEnd; - } - - localEnd = getLocalEnd(); - worldEnd = localToWorld(localEnd, getParentID(), getParentJointIndex(), getScalesWithParent(), success); - if (!success) { - qDebug() << "Line3DOverlay::getEnd failed, parentID = " << getParentID(); - } - return worldEnd; -} - -void Line3DOverlay::setStart(const glm::vec3& start) { - setWorldPosition(start); -} - -void Line3DOverlay::setEnd(const glm::vec3& end) { - bool success; - glm::vec3 localStart; - glm::vec3 localEnd; - glm::vec3 offset; - - if (_endParentID != QUuid()) { - offset = worldToLocal(end, _endParentID, _endParentJointIndex, getScalesWithParent(), success); - } else { - localStart = getLocalStart(); - localEnd = worldToLocal(end, getParentID(), getParentJointIndex(), getScalesWithParent(), success); - offset = localEnd - localStart; - } - if (!success) { - qDebug() << "Line3DOverlay::setEnd failed"; - return; - } - - _length = glm::length(offset); - if (_length > 0.0f) { - _direction = glm::normalize(offset); - } else { - _direction = glm::vec3(0.0f); - } - notifyRenderVariableChange(); -} - -void Line3DOverlay::setLocalEnd(const glm::vec3& localEnd) { - glm::vec3 offset; - if (_endParentID != QUuid()) { - offset = localEnd; - } else { - glm::vec3 localStart = getLocalStart(); - offset = localEnd - localStart; - } - _length = glm::length(offset); - if (_length > 0.0f) { - _direction = glm::normalize(offset); - } else { - _direction = glm::vec3(0.0f); - } -} - -AABox Line3DOverlay::getBounds() const { - auto extents = Extents{}; - extents.addPoint(getStart()); - extents.addPoint(getEnd()); - return AABox(extents); -} - -void Line3DOverlay::render(RenderArgs* args) { - if (!_renderVisible) { - return; // do nothing if we're not visible - } - - float alpha = getAlpha(); - glm::u8vec3 color = getColor(); - glm::vec4 colorv4(toGlm(color), alpha); - auto batch = args->_batch; - if (batch) { - batch->setModelTransform(Transform()); - auto& renderTransform = getRenderTransform(); - glm::vec3 start = renderTransform.getTranslation(); - glm::vec3 end = renderTransform.transform(vec3(0.0, 0.0, -1.0)); - - auto geometryCache = DependencyManager::get(); - if (getIsDashedLine()) { - // TODO: add support for color to renderDashedLine() - geometryCache->bindSimpleProgram(*batch, false, false, false, true, true); - geometryCache->renderDashedLine(*batch, start, end, colorv4, _geometryCacheID); - } else { - // renderGlowLine handles both glow = 0 and glow > 0 cases - geometryCache->renderGlowLine(*batch, start, end, colorv4, _glow, _lineWidth, _geometryCacheID); - } - } -} - -const render::ShapeKey Line3DOverlay::getShapeKey() { - auto builder = render::ShapeKey::Builder().withOwnPipeline(); - if (isTransparent()) { - builder.withTranslucent(); - } - return builder.build(); -} - -void Line3DOverlay::setProperties(const QVariantMap& originalProperties) { - QVariantMap properties = originalProperties; - glm::vec3 newStart(0.0f); - bool newStartSet { false }; - glm::vec3 newEnd(0.0f); - bool newEndSet { false }; - - auto start = properties["start"]; - // If "start" property was not there, check to see if they included aliases: startPoint, p1 - if (!start.isValid()) { - start = properties["startPoint"]; - } - if (!start.isValid()) { - start = properties["p1"]; - } - if (start.isValid()) { - newStart = vec3FromVariant(start); - newStartSet = true; - } - properties.remove("start"); // so that Base3DOverlay doesn't respond to it - properties.remove("startPoint"); - properties.remove("p1"); - - auto end = properties["end"]; - // If "end" property was not there, check to see if they included aliases: endPoint, p2 - if (!end.isValid()) { - end = properties["endPoint"]; - } - if (!end.isValid()) { - end = properties["p2"]; - } - if (end.isValid()) { - newEnd = vec3FromVariant(end); - newEndSet = true; - } - properties.remove("end"); // so that Base3DOverlay doesn't respond to it - properties.remove("endPoint"); - properties.remove("p2"); - - auto length = properties["length"]; - if (length.isValid()) { - _length = length.toFloat(); - } - - Base3DOverlay::setProperties(properties); - - auto endParentIDProp = properties["endParentID"]; - if (endParentIDProp.isValid()) { - _endParentID = QUuid(endParentIDProp.toString()); - } - auto endParentJointIndexProp = properties["endParentJointIndex"]; - if (endParentJointIndexProp.isValid()) { - _endParentJointIndex = endParentJointIndexProp.toInt(); - } - - auto localStart = properties["localStart"]; - if (localStart.isValid()) { - glm::vec3 tmpLocalEnd = getLocalEnd(); - setLocalStart(vec3FromVariant(localStart)); - setLocalEnd(tmpLocalEnd); - } - - auto localEnd = properties["localEnd"]; - if (localEnd.isValid()) { - setLocalEnd(vec3FromVariant(localEnd)); - } - - // these are saved until after Base3DOverlay::setProperties so parenting infomation can be set, first - if (newStartSet) { - setStart(newStart); - } - if (newEndSet) { - setEnd(newEnd); - } - - auto glow = properties["glow"]; - if (glow.isValid()) { - float prevGlow = _glow; - setGlow(glow.toFloat()); - // Update our payload key if necessary to handle transparency - if ((prevGlow <= 0.0f && _glow > 0.0f) || (prevGlow > 0.0f && _glow <= 0.0f)) { - auto itemID = getRenderItemID(); - if (render::Item::isValidID(itemID)) { - render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); - render::Transaction transaction; - transaction.updateItem(itemID); - scene->enqueueTransaction(transaction); - } - } - } - - auto lineWidth = properties["lineWidth"]; - if (lineWidth.isValid()) { - setLineWidth(lineWidth.toFloat()); - } -} - -/**jsdoc - * These are the properties of a line3d {@link Overlays.OverlayType|OverlayType}. - * @typedef {object} Overlays.Line3DProperties - * - * @property {string} type=line3d - Has the value "line3d". Read-only. - * @property {Color} color=255,255,255 - The color of the overlay. - * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. - * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. - * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. - * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from - * pulseMin to pulseMax, then pulseMax to pulseMin in one period. - * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 - * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise - * used.) - * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 - * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise - * used.) - * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. - * - * @property {string} name="" - A friendly name for the overlay. - * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and - * start. - * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a - * parentID set, otherwise the same value as position. - * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. - * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a - * parentID set, otherwise the same value as rotation. - * @property {boolean} isSolid=false - Synonyms: solid, isFilled, and filled. - * Antonyms: isWire and wire. - * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: - * dashed. Deprecated. - * @property {boolean} ignorePickIntersection=false - If true, picks ignore the overlay. ignoreRayIntersection is a synonym. - * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't - * have drawInFront set to true, and in front of entities. - * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. - * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. - * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if - * parentID is an avatar skeleton. A value of 65535 means "no joint". - * - * @property {Uuid} endParentID=null - The avatar, entity, or overlay that the end point of the line is parented to. - * @property {number} endParentJointIndex=65535 - Integer value specifying the skeleton joint that the end point of the line is - * attached to if parentID is an avatar skeleton. A value of 65535 means "no joint". - * @property {Vec3} start - The start point of the line. Synonyms: startPoint, p1, and - * position. - * @property {Vec3} end - The end point of the line. Synonyms: endPoint and p2. - * @property {Vec3} localStart - The local position of the overlay relative to its parent if the overlay has a - * parentID set, otherwise the same value as start. Synonym: localPosition. - * @property {Vec3} localEnd - The local position of the overlay relative to its parent if the overlay has a - * endParentID set, otherwise the same value as end. - * @property {number} length - The length of the line, in meters. This can be set after creating a line with start and end - * points. - * @property {number} glow=0 - If glow > 0, the line is rendered with a glow. - * @property {number} lineWidth=0.02 - If glow > 0, this is the width of the glow, in meters. - */ -QVariant Line3DOverlay::getProperty(const QString& property) { - if (property == "start" || property == "startPoint" || property == "p1") { - return vec3toVariant(getStart()); - } - if (property == "end" || property == "endPoint" || property == "p2") { - return vec3toVariant(getEnd()); - } - if (property == "length") { - return QVariant(getLength()); - } - if (property == "endParentID") { - return _endParentID; - } - if (property == "endParentJointIndex") { - return _endParentJointIndex; - } - if (property == "localStart") { - return vec3toVariant(getLocalStart()); - } - if (property == "localEnd") { - return vec3toVariant(getLocalEnd()); - } - if (property == "glow") { - return getGlow(); - } - if (property == "lineWidth") { - return _lineWidth; - } - - return Base3DOverlay::getProperty(property); -} - -Line3DOverlay* Line3DOverlay::createClone() const { - return new Line3DOverlay(this); -} - -Transform Line3DOverlay::evalRenderTransform() { - // Capture start and endin the renderTransform: - // start is the origin - // end is at the tip of the front axis aka -Z - Transform transform; - transform.setTranslation( getStart()); - auto endPos = getEnd(); - - auto vec = endPos - transform.getTranslation(); - const float MIN_LINE_LENGTH = 0.0001f; - auto scale = glm::max(glm::length(vec), MIN_LINE_LENGTH); - auto dir = vec / scale; - auto orientation = glm::rotation(glm::vec3(0.0f, 0.0f, -1.0f), dir); - transform.setRotation(orientation); - transform.setScale(scale); - - return transform; -} diff --git a/interface/src/ui/overlays/Line3DOverlay.h b/interface/src/ui/overlays/Line3DOverlay.h deleted file mode 100644 index 79af937f23..0000000000 --- a/interface/src/ui/overlays/Line3DOverlay.h +++ /dev/null @@ -1,79 +0,0 @@ -// -// Line3DOverlay.h -// interface/src/ui/overlays -// -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_Line3DOverlay_h -#define hifi_Line3DOverlay_h - -#include "Base3DOverlay.h" - -class Line3DOverlay : public Base3DOverlay { - Q_OBJECT - using Parent = Base3DOverlay; - -public: - static QString const TYPE; - virtual QString getType() const override { return TYPE; } - - Line3DOverlay(); - Line3DOverlay(const Line3DOverlay* line3DOverlay); - ~Line3DOverlay(); - virtual void render(RenderArgs* args) override; - virtual const render::ShapeKey getShapeKey() override; - virtual AABox getBounds() const override; - - // getters - glm::vec3 getStart() const; - glm::vec3 getEnd() const; - const float& getLineWidth() const { return _lineWidth; } - const float& getGlow() const { return _glow; } - - // setters - void setStart(const glm::vec3& start); - void setEnd(const glm::vec3& end); - - void setLocalStart(const glm::vec3& localStart) { setLocalPosition(localStart); } - void setLocalEnd(const glm::vec3& localEnd); - - void setLineWidth(const float& lineWidth) { _lineWidth = lineWidth; } - void setGlow(const float& glow) { _glow = glow; } - - void setProperties(const QVariantMap& properties) override; - QVariant getProperty(const QString& property) override; - bool isTransparent() override { return Base3DOverlay::isTransparent() || _glow > 0.0f; } - - virtual Line3DOverlay* createClone() const override; - - glm::vec3 getDirection() const { return _direction; } - float getLength() const { return _length; } - glm::vec3 getLocalStart() const { return getLocalPosition(); } - glm::vec3 getLocalEnd() const { return getLocalStart() + _direction * _length; } - QUuid getEndParentID() const { return _endParentID; } - quint16 getEndJointIndex() const { return _endParentJointIndex; } - -protected: - Transform evalRenderTransform() override; - -private: - QUuid _endParentID; - quint16 _endParentJointIndex { INVALID_JOINT_INDEX }; - - // _direction and _length are in the parent's frame. If _endParentID is set, they are - // relative to that. Otherwise, they are relative to the local-start-position (which is the - // same as localPosition) - glm::vec3 _direction; // in parent frame - float _length { 1.0 }; // in parent frame - - const float DEFAULT_LINE_WIDTH = 0.02f; - float _lineWidth { DEFAULT_LINE_WIDTH }; - float _glow { 0.0 }; - int _geometryCacheID; -}; - -#endif // hifi_Line3DOverlay_h diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp deleted file mode 100644 index 14e5cdc7f5..0000000000 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ /dev/null @@ -1,767 +0,0 @@ -// -// ModelOverlay.cpp -// -// -// Created by Clement on 6/30/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "ModelOverlay.h" - -#include -#include - -#include - -#include "Application.h" - - -QString const ModelOverlay::TYPE = "model"; - -ModelOverlay::ModelOverlay() - : _model(std::make_shared(nullptr, this)), - _modelTextures(QVariantMap()) -{ - _model->setLoadingPriority(_loadPriority); - _isLoaded = false; - render::ScenePointer scene = qApp->getMain3DScene(); - _model->setVisibleInScene(false, scene); -} - -ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) : - Volume3DOverlay(modelOverlay), - _model(std::make_shared(nullptr, this)), - _modelTextures(QVariantMap()), - _url(modelOverlay->_url), - _updateModel(false), - _scaleToFit(modelOverlay->_scaleToFit), - _loadPriority(modelOverlay->_loadPriority), - - _animationURL(modelOverlay->_animationURL), - _animationFPS(modelOverlay->_animationFPS), - _animationCurrentFrame(modelOverlay->_animationCurrentFrame), - _animationRunning(modelOverlay->_animationRunning), - _animationLoop(modelOverlay->_animationLoop), - _animationFirstFrame(modelOverlay->_animationFirstFrame), - _animationLastFrame(modelOverlay->_animationLastFrame), - _animationHold(modelOverlay->_animationHold), - _animationAllowTranslation(modelOverlay->_animationAllowTranslation) - - // Joint translations and rotations aren't copied because the model needs to load before they can be applied. -{ - _model->setLoadingPriority(_loadPriority); - if (_url.isValid()) { - _updateModel = true; - _isLoaded = false; - } -} - -void ModelOverlay::update(float deltatime) { - Base3DOverlay::update(deltatime); - - if (_updateModel) { - _updateModel = false; - _model->setSnapModelToCenter(true); - Transform transform = evalRenderTransform(); - if (_scaleToFit) { - _model->setScaleToFit(true, transform.getScale() * getDimensions()); - } else { - _model->setScale(transform.getScale()); - } - _model->setRotation(transform.getRotation()); - _model->setTranslation(transform.getTranslation()); - _model->setURL(_url); - _model->simulate(deltatime, true); - } else { - _model->simulate(deltatime); - } - _isLoaded = _model->isActive(); - - if (isAnimatingSomething()) { - if (!jointsMapped()) { - mapAnimationJoints(_model->getJointNames()); - } - animate(); - } - - // check to see if when we added our model to the scene they were ready, if they were not ready, then - // fix them up in the scene - render::ScenePointer scene = qApp->getMain3DScene(); - render::Transaction transaction; - if (_model->needsFixupInScene()) { - emit DependencyManager::get()->modelRemovedFromScene(getID(), NestableType::Overlay, _model); - _model->removeFromScene(scene, transaction); - _model->addToScene(scene, transaction); - - auto newRenderItemIDs{ _model->fetchRenderItemIDs() }; - transaction.updateItem(getRenderItemID(), [newRenderItemIDs](Overlay& data) { - auto modelOverlay = static_cast(&data); - modelOverlay->setSubRenderItemIDs(newRenderItemIDs); - }); - processMaterials(); - emit DependencyManager::get()->modelAddedToScene(getID(), NestableType::Overlay, _model); - } - bool metaDirty = false; - if (_visibleDirty && _texturesLoaded) { - _visibleDirty = false; - // don't show overlays in mirrors or spectator-cam unless _isVisibleInSecondaryCamera is true - uint8_t modelRenderTagMask = (_isVisibleInSecondaryCamera ? render::hifi::TAG_ALL_VIEWS : render::hifi::TAG_MAIN_VIEW); - - _model->setTagMask(modelRenderTagMask, scene); - _model->setVisibleInScene(getVisible(), scene); - metaDirty = true; - } - if (_renderLayerDirty) { - _renderLayerDirty = false; - _model->setHifiRenderLayer(_drawHUDLayer ? render::hifi::LAYER_3D_HUD : (_drawInFront ? render::hifi::LAYER_3D_FRONT : render::hifi::LAYER_3D), scene); - metaDirty = true; - } - if (_groupCulledDirty) { - _groupCulledDirty = false; - _model->setGroupCulled(_isGroupCulled, scene); - metaDirty = true; - } - if (metaDirty) { - transaction.updateItem(getRenderItemID(), [](Overlay& data) {}); - } - scene->enqueueTransaction(transaction); - - if (_texturesDirty && !_modelTextures.isEmpty()) { - _texturesDirty = false; - _model->setTextures(_modelTextures); - } - - if (!_texturesLoaded && _model->getGeometry() && _model->getGeometry()->areTexturesLoaded()) { - _texturesLoaded = true; - - _model->setVisibleInScene(getVisible(), scene); - _model->updateRenderItems(); - } -} - -bool ModelOverlay::addToScene(Overlay::Pointer overlay, const render::ScenePointer& scene, render::Transaction& transaction) { - Volume3DOverlay::addToScene(overlay, scene, transaction); - _model->addToScene(scene, transaction); - processMaterials(); - emit DependencyManager::get()->modelAddedToScene(getID(), NestableType::Overlay, _model); - return true; -} - -void ModelOverlay::removeFromScene(Overlay::Pointer overlay, const render::ScenePointer& scene, render::Transaction& transaction) { - Volume3DOverlay::removeFromScene(overlay, scene, transaction); - _model->removeFromScene(scene, transaction); - emit DependencyManager::get()->modelRemovedFromScene(getID(), NestableType::Overlay, _model); - transaction.updateItem(getRenderItemID(), [](Overlay& data) { - auto modelOverlay = static_cast(&data); - modelOverlay->clearSubRenderItemIDs(); - }); -} - -void ModelOverlay::setVisible(bool visible) { - if (visible != getVisible()) { - Overlay::setVisible(visible); - _visibleDirty = true; - } -} - -void ModelOverlay::setDrawInFront(bool drawInFront) { - if (drawInFront != getDrawInFront()) { - Base3DOverlay::setDrawInFront(drawInFront); - _renderLayerDirty = true; - } -} - -void ModelOverlay::setDrawHUDLayer(bool drawHUDLayer) { - if (drawHUDLayer != getDrawHUDLayer()) { - Base3DOverlay::setDrawHUDLayer(drawHUDLayer); - _renderLayerDirty = true; - } -} - -void ModelOverlay::setGroupCulled(bool groupCulled) { - if (groupCulled != _isGroupCulled) { - _isGroupCulled = groupCulled; - _groupCulledDirty = true; - } -} - -void ModelOverlay::setProperties(const QVariantMap& properties) { - auto origPosition = getWorldPosition(); - auto origRotation = getWorldOrientation(); - auto origDimensions = getDimensions(); - auto origScale = getSNScale(); - - Base3DOverlay::setProperties(properties); - - auto scale = properties["scale"]; - if (scale.isValid()) { - setSNScale(vec3FromVariant(scale)); - } - - auto dimensions = properties["dimensions"]; - if (!dimensions.isValid()) { - dimensions = properties["size"]; - } - if (dimensions.isValid()) { - _scaleToFit = true; - setDimensions(vec3FromVariant(dimensions)); - } else if (scale.isValid()) { - // if "scale" property is set but "dimensions" is not. - // do NOT scale to fit. - _scaleToFit = false; - } - - if (origPosition != getWorldPosition() || origRotation != getWorldOrientation() || origDimensions != getDimensions() || origScale != getSNScale()) { - _updateModel = true; - } - - auto loadPriorityProperty = properties["loadPriority"]; - if (loadPriorityProperty.isValid()) { - _loadPriority = loadPriorityProperty.toFloat(); - _model->setLoadingPriority(_loadPriority); - } - - auto urlValue = properties["url"]; - if (urlValue.isValid() && urlValue.canConvert()) { - _url = urlValue.toString(); - _updateModel = true; - _isLoaded = false; - _texturesLoaded = false; - } - - auto texturesValue = properties["textures"]; - if (texturesValue.isValid() && texturesValue.canConvert(QVariant::Map)) { - _texturesLoaded = false; - QVariantMap textureMap = texturesValue.toMap(); - _modelTextures = textureMap; - _texturesDirty = true; - } - - auto groupCulledValue = properties["isGroupCulled"]; - if (groupCulledValue.isValid() && groupCulledValue.canConvert(QVariant::Bool)) { - setGroupCulled(groupCulledValue.toBool()); - } - - // jointNames is read-only. - // jointPositions is read-only. - // jointOrientations is read-only. - - // relative - auto jointTranslationsValue = properties["jointTranslations"]; - if (jointTranslationsValue.canConvert(QVariant::List)) { - const QVariantList& jointTranslations = jointTranslationsValue.toList(); - int translationCount = jointTranslations.size(); - int jointCount = _model->getJointStateCount(); - if (translationCount < jointCount) { - jointCount = translationCount; - } - for (int i=0; i < jointCount; i++) { - const auto& translationValue = jointTranslations[i]; - if (translationValue.isValid()) { - _model->setJointTranslation(i, true, vec3FromVariant(translationValue), 1.0f); - } - } - _updateModel = true; - } - - // relative - auto jointRotationsValue = properties["jointRotations"]; - if (jointRotationsValue.canConvert(QVariant::List)) { - const QVariantList& jointRotations = jointRotationsValue.toList(); - int rotationCount = jointRotations.size(); - int jointCount = _model->getJointStateCount(); - if (rotationCount < jointCount) { - jointCount = rotationCount; - } - for (int i=0; i < jointCount; i++) { - const auto& rotationValue = jointRotations[i]; - if (rotationValue.isValid()) { - _model->setJointRotation(i, true, quatFromVariant(rotationValue), 1.0f); - } - } - _updateModel = true; - } - - auto animationSettings = properties["animationSettings"]; - if (animationSettings.canConvert(QVariant::Map)) { - QVariantMap animationSettingsMap = animationSettings.toMap(); - - auto animationURL = animationSettingsMap["url"]; - auto animationFPS = animationSettingsMap["fps"]; - auto animationCurrentFrame = animationSettingsMap["currentFrame"]; - auto animationFirstFrame = animationSettingsMap["firstFrame"]; - auto animationLastFrame = animationSettingsMap["lastFrame"]; - auto animationRunning = animationSettingsMap["running"]; - auto animationLoop = animationSettingsMap["loop"]; - auto animationHold = animationSettingsMap["hold"]; - auto animationAllowTranslation = animationSettingsMap["allowTranslation"]; - - if (animationURL.canConvert(QVariant::Url)) { - _animationURL = animationURL.toUrl(); - } - if (animationFPS.isValid()) { - _animationFPS = animationFPS.toFloat(); - } - if (animationCurrentFrame.isValid()) { - _animationCurrentFrame = animationCurrentFrame.toFloat(); - } - if (animationFirstFrame.isValid()) { - _animationFirstFrame = animationFirstFrame.toFloat(); - } - if (animationLastFrame.isValid()) { - _animationLastFrame = animationLastFrame.toFloat(); - } - - if (animationRunning.canConvert(QVariant::Bool)) { - _animationRunning = animationRunning.toBool(); - } - if (animationLoop.canConvert(QVariant::Bool)) { - _animationLoop = animationLoop.toBool(); - } - if (animationHold.canConvert(QVariant::Bool)) { - _animationHold = animationHold.toBool(); - } - if (animationAllowTranslation.canConvert(QVariant::Bool)) { - _animationAllowTranslation = animationAllowTranslation.toBool(); - } - - } -} - -template -vectorType ModelOverlay::mapJoints(mapFunction function) const { - vectorType result; - if (_model && _model->isActive()) { - const int jointCount = _model->getJointStateCount(); - result.reserve(jointCount); - for (int i = 0; i < jointCount; i++) { - result << function(i); - } - } - return result; -} - -// Note: ModelOverlay overrides Volume3DOverlay's "dimensions" and "scale" properties. -/**jsdoc - * These are the properties of a model {@link Overlays.OverlayType|OverlayType}. - * @typedef {object} Overlays.ModelProperties - * - * @property {string} type=sphere - Has the value "model". Read-only. - * @property {Color} color=255,255,255 - The color of the overlay. - * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. - * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. - * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. - * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from - * pulseMin to pulseMax, then pulseMax to pulseMin in one period. - * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 - * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise - * used.) - * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 - * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise - * used.) - * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. - * - * @property {string} name="" - A friendly name for the overlay. - * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and - * start. - * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a - * parentID set, otherwise the same value as position. - * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. - * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a - * parentID set, otherwise the same value as rotation. - * @property {boolean} isSolid=false - Synonyms: solid, isFilled, and filled. - * Antonyms: isWire and wire. - * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: - * dashed. Deprecated. - * @property {boolean} ignorePickIntersection=false - If true, picks ignore the overlay. ignoreRayIntersection is a synonym. - * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't - * have drawInFront set to true, and in front of entities. - * @property {boolean} isGroupCulled=false - If true, the mesh parts of the model are LOD culled as a group. - * If false, separate mesh parts will be LOD culled individually. - * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. - * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. - * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if - * parentID is an avatar skeleton. A value of 65535 means "no joint". - * - * @property {string} url - The URL of the FBX or OBJ model used for the overlay. - * @property {number} loadPriority=0.0 - The priority for loading and displaying the overlay. Overlays with higher values load - * first. - * @property {Vec3} dimensions - The dimensions of the overlay. Synonym: size. - * @property {Vec3} scale - The scale factor applied to the model's dimensions. - * @property {object.} textures - Maps the named textures in the model to the JPG or PNG images in the urls. - * @property {Array.} jointNames - The names of the joints - if any - in the model. Read-only. - * @property {Array.} jointRotations - The relative rotations of the model's joints. Not copied if overlay is - * cloned. - * @property {Array.} jointTranslations - The relative translations of the model's joints. Not copied if overlay is - * cloned. - * @property {Array.} jointOrientations - The absolute orientations of the model's joints, in world coordinates. - * Read-only. - * @property {Array.} jointPositions - The absolute positions of the model's joints, in world coordinates. - * Read-only. - * @property {string} animationSettings.url="" - The URL of an FBX file containing an animation to play. - * @property {number} animationSettings.fps=0 - The frame rate (frames/sec) to play the animation at. - * @property {number} animationSettings.firstFrame=0 - The frame to start playing at. - * @property {number} animationSettings.lastFrame=0 - The frame to finish playing at. - * @property {number} animationSettings.currentFrame=0 - The current frame being played. - * @property {boolean} animationSettings.running=false - Whether or not the animation is playing. - * @property {boolean} animationSettings.loop=false - Whether or not the animation should repeat in a loop. - * @property {boolean} animationSettings.hold=false - Whether or not when the animation finishes, the rotations and - * translations of the last frame played should be maintained. - * @property {boolean} animationSettings.allowTranslation=false - Whether or not translations contained in the animation should - * be played. - */ -QVariant ModelOverlay::getProperty(const QString& property) { - if (property == "url") { - return _url.toString(); - } - if (property == "dimensions" || property == "size") { - return vec3toVariant(getDimensions()); - } - if (property == "scale") { - return vec3toVariant(getSNScale()); - } - if (property == "textures") { - if (_modelTextures.size() > 0) { - QVariantMap textures; - foreach(const QString& key, _modelTextures.keys()) { - textures[key] = _modelTextures[key].toString(); - } - return textures; - } else { - return QVariant(); - } - } - - if (property == "loadPriority") { - return _loadPriority; - } - - if (property == "jointNames") { - if (_model && _model->isActive()) { - // note: going through Rig because Model::getJointNames() (which proxies to HFMModel) was always empty - const Rig* rig = &(_model->getRig()); - return mapJoints([rig](int jointIndex) -> QString { - return rig->nameOfJoint(jointIndex); - }); - } - } - - // relative - if (property == "jointRotations") { - return mapJoints( - [this](int jointIndex) -> QVariant { - glm::quat rotation; - _model->getJointRotation(jointIndex, rotation); - return quatToVariant(rotation); - }); - } - - // relative - if (property == "jointTranslations") { - return mapJoints( - [this](int jointIndex) -> QVariant { - glm::vec3 translation; - _model->getJointTranslation(jointIndex, translation); - return vec3toVariant(translation); - }); - } - - // absolute - if (property == "jointOrientations") { - return mapJoints( - [this](int jointIndex) -> QVariant { - glm::quat orientation; - _model->getJointRotationInWorldFrame(jointIndex, orientation); - return quatToVariant(orientation); - }); - } - - // absolute - if (property == "jointPositions") { - return mapJoints( - [this](int jointIndex) -> QVariant { - glm::vec3 position; - _model->getJointPositionInWorldFrame(jointIndex, position); - return vec3toVariant(position); - }); - } - - // animation properties - if (property == "animationSettings") { - QVariantMap animationSettingsMap; - - animationSettingsMap["url"] = _animationURL; - animationSettingsMap["fps"] = _animationFPS; - animationSettingsMap["currentFrame"] = _animationCurrentFrame; - animationSettingsMap["firstFrame"] = _animationFirstFrame; - animationSettingsMap["lastFrame"] = _animationLastFrame; - animationSettingsMap["running"] = _animationRunning; - animationSettingsMap["loop"] = _animationLoop; - animationSettingsMap["hold"]= _animationHold; - animationSettingsMap["allowTranslation"] = _animationAllowTranslation; - - return animationSettingsMap; - } - - - return Volume3DOverlay::getProperty(property); -} - -bool ModelOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) { - QVariantMap extraInfo; - return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, surfaceNormal, extraInfo, precisionPicking); -} - -bool ModelOverlay::findRayIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& direction, - float& distance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, bool precisionPicking) { - return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, surfaceNormal, extraInfo, precisionPicking); -} - -bool ModelOverlay::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, - float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) { - QVariantMap extraInfo; - return _model->findParabolaIntersectionAgainstSubMeshes(origin, velocity, acceleration, parabolicDistance, face, surfaceNormal, extraInfo, precisionPicking); -} - -bool ModelOverlay::findParabolaIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, - float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, bool precisionPicking) { - return _model->findParabolaIntersectionAgainstSubMeshes(origin, velocity, acceleration, parabolicDistance, face, surfaceNormal, extraInfo, precisionPicking); -} - -ModelOverlay* ModelOverlay::createClone() const { - return new ModelOverlay(this); -} - -Transform ModelOverlay::evalRenderTransform() { - Transform transform = getTransform(); - transform.setScale(1.0f); // disable inherited scale - return transform; -} - -void ModelOverlay::locationChanged(bool tellPhysics) { - Base3DOverlay::locationChanged(tellPhysics); - - // FIXME Start using the _renderTransform instead of calling for Transform and Dimensions from here, do the custom things needed in evalRenderTransform() - if (_model && _model->isActive()) { - _model->setRotation(getWorldOrientation()); - _model->setTranslation(getWorldPosition()); - _updateModel = true; - } -} - -QString ModelOverlay::getName() const { - if (_name != "") { - return QString("Overlay:") + getType() + ":" + _name; - } - return QString("Overlay:") + getType() + ":" + _url.toString(); -} - - -void ModelOverlay::animate() { - - if (!_animation || !_animation->isLoaded() || !_model || !_model->isLoaded()) { - return; - } - - - QVector jointsData; - - const QVector& frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy - int frameCount = frames.size(); - if (frameCount <= 0) { - return; - } - - if (!_lastAnimated) { - _lastAnimated = usecTimestampNow(); - return; - } - - auto now = usecTimestampNow(); - auto interval = now - _lastAnimated; - _lastAnimated = now; - float deltaTime = (float)interval / (float)USECS_PER_SECOND; - _animationCurrentFrame += (deltaTime * _animationFPS); - - int animationCurrentFrame = (int)(glm::floor(_animationCurrentFrame)) % frameCount; - if (animationCurrentFrame < 0 || animationCurrentFrame > frameCount) { - animationCurrentFrame = 0; - } - - if (animationCurrentFrame == _lastKnownCurrentFrame) { - return; - } - _lastKnownCurrentFrame = animationCurrentFrame; - - if (_jointMapping.size() != _model->getJointStateCount()) { - return; - } - - QStringList animationJointNames = _animation->getHFMModel().getJointNames(); - auto& hfmJoints = _animation->getHFMModel().joints; - - auto& originalHFMJoints = _model->getHFMModel().joints; - auto& originalHFMIndices = _model->getHFMModel().jointIndices; - - const QVector& rotations = frames[_lastKnownCurrentFrame].rotations; - const QVector& translations = frames[_lastKnownCurrentFrame].translations; - - jointsData.resize(_jointMapping.size()); - for (int j = 0; j < _jointMapping.size(); j++) { - int index = _jointMapping[j]; - - if (index >= 0) { - glm::mat4 translationMat; - - if (_animationAllowTranslation) { - if (index < translations.size()) { - translationMat = glm::translate(translations[index]); - } - } else if (index < animationJointNames.size()) { - QString jointName = hfmJoints[index].name; - - if (originalHFMIndices.contains(jointName)) { - // Making sure the joint names exist in the original model the animation is trying to apply onto. If they do, then remap and get its translation. - int remappedIndex = originalHFMIndices[jointName] - 1; // JointIndeces seem to always start from 1 and the found index is always 1 higher than actual. - translationMat = glm::translate(originalHFMJoints[remappedIndex].translation); - } - } - glm::mat4 rotationMat; - if (index < rotations.size()) { - rotationMat = glm::mat4_cast(hfmJoints[index].preRotation * rotations[index] * hfmJoints[index].postRotation); - } else { - rotationMat = glm::mat4_cast(hfmJoints[index].preRotation * hfmJoints[index].postRotation); - } - - glm::mat4 finalMat = (translationMat * hfmJoints[index].preTransform * - rotationMat * hfmJoints[index].postTransform); - auto& jointData = jointsData[j]; - jointData.translation = extractTranslation(finalMat); - jointData.translationIsDefaultPose = false; - jointData.rotation = glmExtractRotation(finalMat); - jointData.rotationIsDefaultPose = false; - } - } - // Set the data in the model - copyAnimationJointDataToModel(jointsData); -} - - -void ModelOverlay::mapAnimationJoints(const QStringList& modelJointNames) { - - // if we don't have animation, or we're already joint mapped then bail early - if (!hasAnimation() || jointsMapped()) { - return; - } - - if (!_animation || _animation->getURL() != _animationURL) { - _animation = DependencyManager::get()->getAnimation(_animationURL); - } - - if (_animation && _animation->isLoaded()) { - QStringList animationJointNames = _animation->getJointNames(); - - if (modelJointNames.size() > 0 && animationJointNames.size() > 0) { - _jointMapping.resize(modelJointNames.size()); - for (int i = 0; i < modelJointNames.size(); i++) { - _jointMapping[i] = animationJointNames.indexOf(modelJointNames[i]); - } - _jointMappingCompleted = true; - _jointMappingURL = _animationURL; - } - } -} - -void ModelOverlay::copyAnimationJointDataToModel(QVector jointsData) { - if (!_model || !_model->isLoaded()) { - return; - } - - // relay any inbound joint changes from scripts/animation/network to the model/rig - for (int index = 0; index < jointsData.size(); ++index) { - auto& jointData = jointsData[index]; - _model->setJointRotation(index, true, jointData.rotation, 1.0f); - _model->setJointTranslation(index, true, jointData.translation, 1.0f); - } - _updateModel = true; -} - -void ModelOverlay::clearSubRenderItemIDs() { - _subRenderItemIDs.clear(); -} - -void ModelOverlay::setSubRenderItemIDs(const render::ItemIDs& ids) { - _subRenderItemIDs = ids; -} - -uint32_t ModelOverlay::fetchMetaSubItems(render::ItemIDs& subItems) const { - if (_model) { - auto metaSubItems = _subRenderItemIDs; - subItems.insert(subItems.end(), metaSubItems.begin(), metaSubItems.end()); - return (uint32_t)metaSubItems.size(); - } - return 0; -} - -void ModelOverlay::addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) { - Overlay::addMaterial(material, parentMaterialName); - if (_model && _model->fetchRenderItemIDs().size() > 0) { - _model->addMaterial(material, parentMaterialName); - } -} - -void ModelOverlay::removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) { - Overlay::removeMaterial(material, parentMaterialName); - if (_model && _model->fetchRenderItemIDs().size() > 0) { - _model->removeMaterial(material, parentMaterialName); - } -} - -void ModelOverlay::processMaterials() { - assert(_model); - std::lock_guard lock(_materialsLock); - for (auto& shapeMaterialPair : _materials) { - auto material = shapeMaterialPair.second; - while (!material.empty()) { - _model->addMaterial(material.top(), shapeMaterialPair.first); - material.pop(); - } - } -} - -bool ModelOverlay::canReplaceModelMeshPart(int meshIndex, int partIndex) { - // TODO: bounds checking; for now just used to indicate provider generally supports mesh updates - return _model && _model->isLoaded(); -} - -bool ModelOverlay::replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointer newModel, int meshIndex, int partIndex) { - return canReplaceModelMeshPart(meshIndex, partIndex) && - _model->replaceScriptableModelMeshPart(newModel, meshIndex, partIndex); -} - -scriptable::ScriptableModelBase ModelOverlay::getScriptableModel() { - if (!_model || !_model->isLoaded()) { - return Base3DOverlay::getScriptableModel(); - } - auto result = _model->getScriptableModel(); - result.objectID = getID(); - { - std::lock_guard lock(_materialsLock); - result.appendMaterials(_materials); - } - return result; -} - -render::ItemKey ModelOverlay::getKey() { - auto builder = render::ItemKey::Builder(Base3DOverlay::getKey()); - if (_isGroupCulled) { - builder.withMetaCullGroup(); - } - return builder.build(); -} diff --git a/interface/src/ui/overlays/ModelOverlay.h b/interface/src/ui/overlays/ModelOverlay.h deleted file mode 100644 index 17a2327d02..0000000000 --- a/interface/src/ui/overlays/ModelOverlay.h +++ /dev/null @@ -1,137 +0,0 @@ -// -// ModelOverlay.h -// -// -// Created by Clement on 6/30/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_ModelOverlay_h -#define hifi_ModelOverlay_h - -#include -#include - -#include "Volume3DOverlay.h" - -class ModelOverlay : public Volume3DOverlay { - Q_OBJECT -public: - static QString const TYPE; - virtual QString getType() const override { return TYPE; } - - virtual QString getName() const override; - - ModelOverlay(); - ModelOverlay(const ModelOverlay* modelOverlay); - - virtual void update(float deltatime) override; - virtual void render(RenderArgs* args) override {}; - - virtual uint32_t fetchMetaSubItems(render::ItemIDs& subItems) const override; - - render::ItemKey getKey() override; - void clearSubRenderItemIDs(); - void setSubRenderItemIDs(const render::ItemIDs& ids); - - virtual void setIsVisibleInSecondaryCamera(bool value) override { - Base3DOverlay::setIsVisibleInSecondaryCamera(value); - _visibleDirty = true; - } - - void setProperties(const QVariantMap& properties) override; - QVariant getProperty(const QString& property) override; - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override; - virtual bool findRayIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& direction, - float& distance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, bool precisionPicking = false) override; - virtual bool findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, float& parabolicDistance, - BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override; - virtual bool findParabolaIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, float& parabolicDistance, - BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, bool precisionPicking = false) override; - - virtual ModelOverlay* createClone() const override; - - virtual bool addToScene(Overlay::Pointer overlay, const render::ScenePointer& scene, render::Transaction& transaction) override; - virtual void removeFromScene(Overlay::Pointer overlay, const render::ScenePointer& scene, render::Transaction& transaction) override; - - void locationChanged(bool tellPhysics) override; - - float getLoadPriority() const { return _loadPriority; } - - bool hasAnimation() const { return !_animationURL.isEmpty(); } - bool jointsMapped() const { return _jointMappingURL == _animationURL && _jointMappingCompleted; } - - void setVisible(bool visible) override; - void setDrawInFront(bool drawInFront) override; - void setDrawHUDLayer(bool drawHUDLayer) override; - void setGroupCulled(bool groupCulled); - - void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) override; - void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) override; - - virtual scriptable::ScriptableModelBase getScriptableModel() override; - virtual bool canReplaceModelMeshPart(int meshIndex, int partIndex) override; - virtual bool replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointer model, int meshIndex, int partIndex) override; - -protected: - Transform evalRenderTransform() override; - - // helper to extract metadata from our Model's rigged joints - template using mapFunction = std::function; - template - vectorType mapJoints(mapFunction function) const; - - void animate(); - void mapAnimationJoints(const QStringList& modelJointNames); - bool isAnimatingSomething() const { - return !_animationURL.isEmpty() && _animationRunning && _animationFPS != 0.0f; - } - void copyAnimationJointDataToModel(QVector jointsData); - - -private: - - ModelPointer _model; - QVariantMap _modelTextures; - bool _texturesLoaded { false }; - bool _texturesDirty { false }; - - render::ItemIDs _subRenderItemIDs; - - QUrl _url; - bool _updateModel { false }; - bool _scaleToFit { false }; - float _loadPriority { 0.0f }; - - AnimationPointer _animation; - - QUrl _animationURL; - float _animationFPS { 0.0f }; - float _animationCurrentFrame { 0.0f }; - bool _animationRunning { false }; - bool _animationLoop { false }; - float _animationFirstFrame { 0.0f }; - float _animationLastFrame { 0.0f }; - bool _animationHold { false }; - bool _animationAllowTranslation { false }; - uint64_t _lastAnimated { 0 }; - int _lastKnownCurrentFrame { -1 }; - - QUrl _jointMappingURL; - bool _jointMappingCompleted { false }; - QVector _jointMapping; // domain is index into model-joints, range is index into animation-joints - - bool _visibleDirty { true }; - bool _renderLayerDirty { false }; - bool _isGroupCulled { false }; - bool _groupCulledDirty { false }; - - void processMaterials(); - -}; - -#endif // hifi_ModelOverlay_h diff --git a/interface/src/ui/overlays/Overlay.cpp b/interface/src/ui/overlays/Overlay.cpp index 1bf94adfa0..714db97bc2 100644 --- a/interface/src/ui/overlays/Overlay.cpp +++ b/interface/src/ui/overlays/Overlay.cpp @@ -15,126 +15,31 @@ #include "Application.h" -const glm::u8vec3 Overlay::DEFAULT_OVERLAY_COLOR = { 255, 255, 255 }; -const float Overlay::DEFAULT_ALPHA = 0.7f; - Overlay::Overlay() : _renderItemID(render::Item::INVALID_ITEM_ID), - _isLoaded(true), - _alpha(DEFAULT_ALPHA), - _pulse(1.0f), - _pulseMax(0.0f), - _pulseMin(0.0f), - _pulsePeriod(1.0f), - _pulseDirection(1.0f), - _lastPulseUpdate(usecTimestampNow()), - _alphaPulse(0.0f), - _colorPulse(0.0f), - _color(DEFAULT_OVERLAY_COLOR), _visible(true) { } Overlay::Overlay(const Overlay* overlay) : _renderItemID(render::Item::INVALID_ITEM_ID), - _isLoaded(overlay->_isLoaded), - _alpha(overlay->_alpha), - _pulse(overlay->_pulse), - _pulseMax(overlay->_pulseMax), - _pulseMin(overlay->_pulseMin), - _pulsePeriod(overlay->_pulsePeriod), - _pulseDirection(overlay->_pulseDirection), - _lastPulseUpdate(usecTimestampNow()), - _alphaPulse(overlay->_alphaPulse), - _colorPulse(overlay->_colorPulse), - _color(overlay->_color), _visible(overlay->_visible) { } -Overlay::~Overlay() { -} - void Overlay::setProperties(const QVariantMap& properties) { - bool valid; - auto color = u8vec3FromVariant(properties["color"], valid); - if (valid) { - _color = color; - } - - if (properties["alpha"].isValid()) { - setAlpha(properties["alpha"].toFloat()); - } - - if (properties["pulseMax"].isValid()) { - setPulseMax(properties["pulseMax"].toFloat()); - } - - if (properties["pulseMin"].isValid()) { - setPulseMin(properties["pulseMin"].toFloat()); - } - - if (properties["pulsePeriod"].isValid()) { - setPulsePeriod(properties["pulsePeriod"].toFloat()); - } - - if (properties["alphaPulse"].isValid()) { - setAlphaPulse(properties["alphaPulse"].toFloat()); - } - - if (properties["colorPulse"].isValid()) { - setColorPulse(properties["colorPulse"].toFloat()); - } - if (properties["visible"].isValid()) { bool visible = properties["visible"].toBool(); setVisible(visible); } } -// JSDoc for copying to @typedefs of overlay types that inherit Overlay. -/**jsdoc - * @property {string} type=TODO - Has the value "TODO". Read-only. - * @property {Color} color=255,255,255 - The color of the overlay. - * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. - * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. - * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. - * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from - * pulseMin to pulseMax, then pulseMax to pulseMin in one period. - * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 - * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise - * used.) - * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 - * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise - * used.) - * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. - */ QVariant Overlay::getProperty(const QString& property) { if (property == "type") { return QVariant(getType()); } - if (property == "color") { - return u8vec3ColortoVariant(_color); - } - if (property == "alpha") { - return _alpha; - } - if (property == "pulseMax") { - return _pulseMax; - } - if (property == "pulseMin") { - return _pulseMin; - } - if (property == "pulsePeriod") { - return _pulsePeriod; - } - if (property == "alphaPulse") { - return _alphaPulse; - } - if (property == "colorPulse") { - return _colorPulse; + if (property == "id") { + return getID(); } if (property == "visible") { return _visible; @@ -143,67 +48,6 @@ QVariant Overlay::getProperty(const QString& property) { return QVariant(); } -glm::u8vec3 Overlay::getColor() { - if (_colorPulse == 0.0f) { - return _color; - } - - float pulseLevel = updatePulse(); - glm::u8vec3 result = _color; - if (_colorPulse < 0.0f) { - result.x *= (1.0f - pulseLevel); - result.y *= (1.0f - pulseLevel); - result.z *= (1.0f - pulseLevel); - } else { - result.x *= pulseLevel; - result.y *= pulseLevel; - result.z *= pulseLevel; - } - return result; -} - -float Overlay::getAlpha() { - if (_alphaPulse == 0.0f) { - return _alpha; - } - float pulseLevel = updatePulse(); - return (_alphaPulse >= 0.0f) ? _alpha * pulseLevel : _alpha * (1.0f - pulseLevel); -} - -// pulse travels from min to max, then max to min in one period. -float Overlay::updatePulse() { - if (_pulsePeriod <= 0.0f) { - return _pulse; - } - quint64 now = usecTimestampNow(); - quint64 elapsedUSecs = (now - _lastPulseUpdate); - float elapsedSeconds = (float)elapsedUSecs / (float)USECS_PER_SECOND; - float elapsedPeriods = elapsedSeconds / _pulsePeriod; - - // we can safely remove any "full" periods, since those just rotate us back - // to our final pulse level - elapsedPeriods = fmod(elapsedPeriods, 1.0f); - _lastPulseUpdate = now; - - float pulseDistance = (_pulseMax - _pulseMin); - float pulseDistancePerPeriod = pulseDistance * 2.0f; - - float pulseDelta = _pulseDirection * pulseDistancePerPeriod * elapsedPeriods; - float newPulse = _pulse + pulseDelta; - float limit = (_pulseDirection > 0.0f) ? _pulseMax : _pulseMin; - float passedLimit = (_pulseDirection > 0.0f) ? (newPulse >= limit) : (newPulse <= limit); - - if (passedLimit) { - float pulseDeltaToLimit = newPulse - limit; - float pulseDeltaFromLimitBack = pulseDelta - pulseDeltaToLimit; - pulseDelta = -pulseDeltaFromLimitBack; - _pulseDirection *= -1.0f; - } - _pulse += pulseDelta; - - return _pulse; -} - bool Overlay::addToScene(Overlay::Pointer overlay, const render::ScenePointer& scene, render::Transaction& transaction) { _renderItemID = scene->allocateID(); transaction.resetItem(_renderItemID, std::make_shared(overlay)); @@ -215,37 +59,6 @@ void Overlay::removeFromScene(Overlay::Pointer overlay, const render::ScenePoint render::Item::clearID(_renderItemID); } -QScriptValue OverlayIDtoScriptValue(QScriptEngine* engine, const OverlayID& id) { - return quuidToScriptValue(engine, id); -} - -void OverlayIDfromScriptValue(const QScriptValue &object, OverlayID& id) { - quuidFromScriptValue(object, id); -} - -QVector qVectorOverlayIDFromScriptValue(const QScriptValue& array) { - if (!array.isArray()) { - return QVector(); - } - QVector newVector; - int length = array.property("length").toInteger(); - newVector.reserve(length); - for (int i = 0; i < length; i++) { - newVector << OverlayID(array.property(i).toString()); - } - return newVector; -} - -void Overlay::addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) { - std::lock_guard lock(_materialsLock); - _materials[parentMaterialName].push(material); -} - -void Overlay::removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) { - std::lock_guard lock(_materialsLock); - _materials[parentMaterialName].remove(material); -} - render::ItemKey Overlay::getKey() { auto builder = render::ItemKey::Builder().withTypeShape().withTypeMeta(); @@ -256,8 +69,7 @@ render::ItemKey Overlay::getKey() { builder.withInvisible(); } - // always visible in primary view. if isVisibleInSecondaryCamera, also draw in secondary view - render::hifi::Tag viewTagBits = getIsVisibleInSecondaryCamera() ? render::hifi::TAG_ALL_VIEWS : render::hifi::TAG_MAIN_VIEW; + render::hifi::Tag viewTagBits = render::hifi::TAG_ALL_VIEWS; builder.withTagBits(viewTagBits); return builder.build(); diff --git a/interface/src/ui/overlays/Overlay.h b/interface/src/ui/overlays/Overlay.h index 8e430f7e85..ee6e281193 100644 --- a/interface/src/ui/overlays/Overlay.h +++ b/interface/src/ui/overlays/Overlay.h @@ -13,13 +13,6 @@ #include -class OverlayID : public QUuid { -public: - OverlayID() : QUuid() {} - OverlayID(QString v) : QUuid(v) {} - OverlayID(QUuid v) : QUuid(v) {} -}; - class Overlay : public QObject { Q_OBJECT @@ -30,12 +23,10 @@ public: Overlay(); Overlay(const Overlay* overlay); - ~Overlay(); - virtual OverlayID getOverlayID() const { return _overlayID; } - virtual void setOverlayID(OverlayID overlayID) { _overlayID = overlayID; } + virtual QUuid getID() const { return _id; } + virtual void setID(const QUuid& id) { _id = id; } - virtual void update(float deltatime) {} virtual void render(RenderArgs* args) = 0; virtual render::ItemKey getKey(); @@ -51,36 +42,13 @@ public: // getters virtual QString getType() const = 0; - virtual bool is3D() const = 0; - bool isLoaded() { return _isLoaded; } + bool isLoaded() { return true; } bool getVisible() const { return _visible; } - virtual bool isTransparent() { return getAlphaPulse() != 0.0f || getAlpha() != 1.0f; }; - virtual bool getIsVisibleInSecondaryCamera() const { return false; } - - glm::u8vec3 getColor(); - float getAlpha(); - - float getPulseMax() const { return _pulseMax; } - float getPulseMin() const { return _pulseMin; } - float getPulsePeriod() const { return _pulsePeriod; } - float getPulseDirection() const { return _pulseDirection; } - - float getColorPulse() const { return _colorPulse; } - float getAlphaPulse() const { return _alphaPulse; } // setters virtual void setVisible(bool visible) { _visible = visible; } - void setDrawHUDLayer(bool drawHUDLayer); - void setColor(const glm::u8vec3& color) { _color = color; } - void setAlpha(float alpha) { _alpha = alpha; } - - void setPulseMax(float value) { _pulseMax = value; } - void setPulseMin(float value) { _pulseMin = value; } - void setPulsePeriod(float value) { _pulsePeriod = value; } - void setPulseDirection(float value) { _pulseDirection = value; } - - void setColorPulse(float value) { _colorPulse = value; } - void setAlphaPulse(float value) { _alphaPulse = value; } + unsigned int getStackOrder() const { return _stackOrder; } + void setStackOrder(unsigned int stackOrder) { _stackOrder = stackOrder; } Q_INVOKABLE virtual void setProperties(const QVariantMap& properties); Q_INVOKABLE virtual Overlay* createClone() const = 0; @@ -89,43 +57,14 @@ public: render::ItemID getRenderItemID() const { return _renderItemID; } void setRenderItemID(render::ItemID renderItemID) { _renderItemID = renderItemID; } - unsigned int getStackOrder() const { return _stackOrder; } - void setStackOrder(unsigned int stackOrder) { _stackOrder = stackOrder; } - - virtual void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName); - virtual void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName); - protected: - float updatePulse(); - - render::ItemID _renderItemID{ render::Item::INVALID_ITEM_ID }; - - bool _isLoaded; - float _alpha; - - float _pulse; - float _pulseMax; - float _pulseMin; - float _pulsePeriod; - float _pulseDirection; - quint64 _lastPulseUpdate; - - float _alphaPulse; // ratio of the pulse to the alpha - float _colorPulse; // ratio of the pulse to the color - - glm::u8vec3 _color; - bool _visible; // should the overlay be drawn at all + render::ItemID _renderItemID { render::Item::INVALID_ITEM_ID }; + bool _visible; unsigned int _stackOrder { 0 }; - static const glm::u8vec3 DEFAULT_OVERLAY_COLOR; - static const float DEFAULT_ALPHA; - - std::unordered_map _materials; - std::mutex _materialsLock; - private: - OverlayID _overlayID; // only used for non-3d overlays + QUuid _id; }; namespace render { @@ -136,10 +75,4 @@ namespace render { template <> uint32_t metaFetchMetaSubItems(const Overlay::Pointer& overlay, ItemIDs& subItems); } -Q_DECLARE_METATYPE(OverlayID); -Q_DECLARE_METATYPE(QVector); -QScriptValue OverlayIDtoScriptValue(QScriptEngine* engine, const OverlayID& id); -void OverlayIDfromScriptValue(const QScriptValue& object, OverlayID& id); -QVector qVectorOverlayIDFromScriptValue(const QScriptValue& array); - #endif // hifi_Overlay_h diff --git a/interface/src/ui/overlays/Overlay2D.h b/interface/src/ui/overlays/Overlay2D.h index 3175df92f1..54ab52b469 100644 --- a/interface/src/ui/overlays/Overlay2D.h +++ b/interface/src/ui/overlays/Overlay2D.h @@ -17,15 +17,12 @@ class Overlay2D : public Overlay { Q_OBJECT - + public: Overlay2D() {} Overlay2D(const Overlay2D* overlay2D); - - virtual AABox getBounds() const override; - - virtual bool is3D() const override { return false; } + virtual AABox getBounds() const override; virtual uint32_t fetchMetaSubItems(render::ItemIDs& subItems) const override { subItems.push_back(getRenderItemID()); return 1; } // getters @@ -34,7 +31,7 @@ public: int getWidth() const { return _bounds.width(); } int getHeight() const { return _bounds.height(); } const QRect& getBoundingRect() const { return _bounds; } - + // setters void setX(int x) { _bounds.setX(x); } void setY(int y) { _bounds.setY(y); } diff --git a/interface/src/ui/overlays/OverlayTransformNode.cpp b/interface/src/ui/overlays/OverlayTransformNode.cpp deleted file mode 100644 index 817b6af305..0000000000 --- a/interface/src/ui/overlays/OverlayTransformNode.cpp +++ /dev/null @@ -1,13 +0,0 @@ -// -// Created by Sabrina Shanman 9/5/2018 -// Copyright 2018 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// -#include "OverlayTransformNode.h" - -template<> -glm::vec3 BaseNestableTransformNode::getActualScale(const std::shared_ptr& nestablePointer) const { - return nestablePointer->getBounds().getScale(); -} \ No newline at end of file diff --git a/interface/src/ui/overlays/OverlayTransformNode.h b/interface/src/ui/overlays/OverlayTransformNode.h deleted file mode 100644 index 11c3415828..0000000000 --- a/interface/src/ui/overlays/OverlayTransformNode.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// Created by Sabrina Shanman 9/5/2018 -// Copyright 2018 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// -#ifndef hifi_OverlayTransformNode_h -#define hifi_OverlayTransformNode_h - -#include "NestableTransformNode.h" - -#include "Base3DOverlay.h" - -// For 3D overlays only -class OverlayTransformNode : public BaseNestableTransformNode { -public: - OverlayTransformNode(std::weak_ptr spatiallyNestable, int jointIndex) : BaseNestableTransformNode(spatiallyNestable, jointIndex) {}; -}; - -#endif // hifi_OverlayTransformNode_h \ No newline at end of file diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 754c8d26a9..e1708c14fe 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -21,27 +21,27 @@ #include "Application.h" #include "InterfaceLogging.h" -#include "Image3DOverlay.h" -#include "Circle3DOverlay.h" -#include "Cube3DOverlay.h" -#include "Shape3DOverlay.h" + #include "ImageOverlay.h" -#include "Line3DOverlay.h" -#include "ModelOverlay.h" -#include "Rectangle3DOverlay.h" -#include "Sphere3DOverlay.h" -#include "Grid3DOverlay.h" #include "TextOverlay.h" #include "RectangleOverlay.h" -#include "Text3DOverlay.h" -#include "Web3DOverlay.h" + +#include +#include +#include +#include + +#include +#include "VariantMapToScriptValue.h" + #include "ui/Keyboard.h" #include -#include - Q_LOGGING_CATEGORY(trace_render_overlays, "trace.render.overlays") +std::unordered_map Overlays::_entityToOverlayTypes; +std::unordered_map Overlays::_overlayToEntityTypes; + Overlays::Overlays() { auto pointerManager = DependencyManager::get(); connect(pointerManager.data(), &PointerManager::hoverBeginOverlay, this, &Overlays::hoverEnterPointerEvent); @@ -50,41 +50,53 @@ Overlays::Overlays() { connect(pointerManager.data(), &PointerManager::triggerBeginOverlay, this, &Overlays::mousePressPointerEvent); connect(pointerManager.data(), &PointerManager::triggerContinueOverlay, this, &Overlays::mouseMovePointerEvent); connect(pointerManager.data(), &PointerManager::triggerEndOverlay, this, &Overlays::mouseReleasePointerEvent); + + ADD_TYPE_MAP(Box, cube); + ADD_TYPE_MAP(Sphere, sphere); + _overlayToEntityTypes["rectangle3d"] = "Shape"; + ADD_TYPE_MAP(Shape, shape); + ADD_TYPE_MAP(Model, model); + ADD_TYPE_MAP(Text, text3d); + ADD_TYPE_MAP(Image, image3d); + _overlayToEntityTypes["billboard"] = "Image"; + ADD_TYPE_MAP(Web, web3d); + ADD_TYPE_MAP(PolyLine, line3d); + ADD_TYPE_MAP(Grid, grid); + ADD_TYPE_MAP(Gizmo, circle3d); + + auto mouseRayPick = std::make_shared(Vectors::ZERO, Vectors::UP, + PickFilter(PickFilter::getBitMask(PickFilter::FlagBit::LOCAL_ENTITIES) | + PickFilter::getBitMask(PickFilter::FlagBit::VISIBLE)), 0.0f, true); + mouseRayPick->parentTransform = std::make_shared(); + mouseRayPick->setJointState(PickQuery::JOINT_STATE_MOUSE); + _mouseRayPickID = DependencyManager::get()->addPick(PickQuery::Ray, mouseRayPick); } void Overlays::cleanupAllOverlays() { _shuttingDown = true; - QMap overlaysHUD; - QMap overlaysWorld; + QMap overlays; { QMutexLocker locker(&_mutex); - overlaysHUD.swap(_overlaysHUD); - overlaysWorld.swap(_overlaysWorld); + overlays.swap(_overlays); } - foreach(Overlay::Pointer overlay, overlaysHUD) { - _overlaysToDelete.push_back(overlay); - } - foreach(Overlay::Pointer overlay, overlaysWorld) { + foreach(Overlay::Pointer overlay, overlays) { _overlaysToDelete.push_back(overlay); } cleanupOverlaysToDelete(); } void Overlays::init() { + auto entityScriptingInterface = DependencyManager::get(); + connect(this, &Overlays::hoverEnterOverlay, entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity); + connect(this, &Overlays::hoverOverOverlay, entityScriptingInterface.data(), &EntityScriptingInterface::hoverOverEntity); + connect(this, &Overlays::hoverLeaveOverlay, entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity); + connect(this, &Overlays::mousePressOnOverlay, entityScriptingInterface.data(), &EntityScriptingInterface::mousePressOnEntity); + connect(this, &Overlays::mouseMoveOnOverlay, entityScriptingInterface.data(), &EntityScriptingInterface::mouseMoveOnEntity); + connect(this, &Overlays::mouseReleaseOnOverlay, entityScriptingInterface.data(), &EntityScriptingInterface::mouseReleaseOnEntity); } void Overlays::update(float deltatime) { - { - QMutexLocker locker(&_mutex); - foreach(const auto& thisOverlay, _overlaysHUD) { - thisOverlay->update(deltatime); - } - foreach(const auto& thisOverlay, _overlaysWorld) { - thisOverlay->update(deltatime); - } - } - cleanupOverlaysToDelete(); } @@ -110,7 +122,7 @@ void Overlays::cleanupOverlaysToDelete() { } } -void Overlays::renderHUD(RenderArgs* renderArgs) { +void Overlays::render(RenderArgs* renderArgs) { PROFILE_RANGE(render_overlays, __FUNCTION__); gpu::Batch& batch = *renderArgs->_batch; @@ -123,7 +135,7 @@ void Overlays::renderHUD(RenderArgs* renderArgs) { mat4 legacyProjection = glm::ortho(0, width, height, 0, -1000, 1000); QMutexLocker locker(&_mutex); - foreach(Overlay::Pointer thisOverlay, _overlaysHUD) { + foreach(Overlay::Pointer thisOverlay, _overlays) { // Reset all batch pipeline settings between overlay geometryCache->useSimpleDrawPipeline(batch); @@ -144,203 +156,699 @@ void Overlays::enable() { _enabled = true; } -// Note, can't be invoked by scripts, but can be called by the InterfaceParentFinder -// class on packet processing threads -Overlay::Pointer Overlays::getOverlay(OverlayID id) const { +Overlay::Pointer Overlays::take2DOverlay(const QUuid& id) { if (_shuttingDown) { return nullptr; } QMutexLocker locker(&_mutex); - if (_overlaysHUD.contains(id)) { - return _overlaysHUD[id]; - } else if (_overlaysWorld.contains(id)) { - return _overlaysWorld[id]; + auto overlayIter = _overlays.find(id); + if (overlayIter != _overlays.end()) { + return _overlays.take(id); } return nullptr; } -OverlayID Overlays::addOverlay(const QString& type, const QVariant& properties) { +Overlay::Pointer Overlays::get2DOverlay(const QUuid& id) const { if (_shuttingDown) { - return UNKNOWN_OVERLAY_ID; + return nullptr; + } + + QMutexLocker locker(&_mutex); + auto overlayIter = _overlays.find(id); + if (overlayIter != _overlays.end()) { + return overlayIter.value(); + } + return nullptr; +} + +QString Overlays::entityToOverlayType(const QString& type) { + auto iter = _entityToOverlayTypes.find(type); + if (iter != _entityToOverlayTypes.end()) { + return iter->second; + } + return "unknown"; +} + +QString Overlays::overlayToEntityType(const QString& type) { + auto iter = _overlayToEntityTypes.find(type); + if (iter != _overlayToEntityTypes.end()) { + return iter->second; + } + return "Unknown"; +} + +#define SET_OVERLAY_PROP_DEFAULT(o, d) \ + { \ + if (add && !overlayProps.contains(#o)) { \ + overlayProps[#o] = d; \ + } \ + } + +#define RENAME_PROP(o, e) \ + { \ + auto iter = overlayProps.find(#o); \ + if (iter != overlayProps.end()) { \ + overlayProps[#e] = iter.value(); \ + } \ + } + +#define RENAME_PROP_CONVERT(o, e, C) \ + { \ + auto iter = overlayProps.find(#o); \ + if (iter != overlayProps.end()) { \ + overlayProps[#e] = C(iter.value()); \ + } \ + } + +#define OVERLAY_TO_GROUP_ENTITY_PROP(o, g, e) \ + { \ + auto iter = overlayProps.find(#o); \ + if (iter != overlayProps.end()) { \ + if (!overlayProps.contains(#g)) { \ + overlayProps[#g] = QVariantMap(); \ + } \ + auto map = overlayProps[#g].toMap(); \ + map[#e] = iter.value(); \ + overlayProps[#g] = map; \ + } \ + } + +#define OVERLAY_TO_GROUP_ENTITY_PROP_DEFAULT(o, g, e, d) \ + { \ + auto iter = overlayProps.find(#o); \ + if (iter != overlayProps.end()) { \ + if (!overlayProps.contains(#g)) { \ + overlayProps[#g] = QVariantMap(); \ + } \ + auto map = overlayProps[#g].toMap(); \ + map[#e] = iter.value(); \ + overlayProps[#g] = map; \ + } else if (add) { \ + if (!overlayProps.contains(#g)) { \ + overlayProps[#g] = QVariantMap(); \ + } \ + auto map = overlayProps[#g].toMap(); \ + map[#e] = d; \ + overlayProps[#g] = map; \ + } \ + } + +#define OVERLAY_TO_ENTITY_PROP_CONVERT_DEFAULT(o, e, d, C) \ + { \ + auto iter = overlayProps.find(#o); \ + if (iter != overlayProps.end()) { \ + overlayProps[#e] = C(iter.value()); \ + } else if (add) { \ + overlayProps[#e] = C(d); \ + } \ + } + +#define OVERLAY_TO_GROUP_ENTITY_PROP_CONVERT(o, g, e, C) \ + { \ + auto iter = overlayProps.find(#o); \ + if (iter != overlayProps.end()) { \ + if (!overlayProps.contains(#g)) { \ + overlayProps[#g] = QVariantMap(); \ + } \ + auto map = overlayProps[#g].toMap(); \ + map[#e] = C(iter.value()); \ + overlayProps[#g] = map; \ + } \ + } + +#define GROUP_ENTITY_TO_OVERLAY_PROP(g, e, o) \ + { \ + auto iter = overlayProps.find(#g); \ + if (iter != overlayProps.end()) { \ + auto map = iter.value().toMap(); \ + auto iter2 = map.find(#e); \ + if (iter2 != map.end()) { \ + overlayProps[#o] = iter2.value(); \ + } \ + } \ + } + +#define GROUP_ENTITY_TO_OVERLAY_PROP_CONVERT(g, e, o, C) \ + { \ + auto iter = overlayProps.find(#g); \ + if (iter != overlayProps.end()) { \ + auto map = iter.value().toMap(); \ + auto iter2 = map.find(#e); \ + if (iter2 != map.end()) { \ + overlayProps[#o] = C(iter2.value()); \ + } \ + } \ + } + +static QHash savedRotations = QHash(); + +EntityItemProperties Overlays::convertOverlayToEntityProperties(QVariantMap& overlayProps, const QString& type, bool add, const QUuid& id) { + glm::quat rotation; + return convertOverlayToEntityProperties(overlayProps, rotation, type, add, id); +} + +EntityItemProperties Overlays::convertOverlayToEntityProperties(QVariantMap& overlayProps, glm::quat& rotationToSave, const QString& type, bool add, const QUuid& id) { + overlayProps["type"] = type; + + SET_OVERLAY_PROP_DEFAULT(alpha, 0.7); + if (type != "PolyLine") { + RENAME_PROP(p1, position); + RENAME_PROP(start, position); + } + RENAME_PROP(point, position); + RENAME_PROP(scale, dimensions); + RENAME_PROP(size, dimensions); + RENAME_PROP(orientation, rotation); + RENAME_PROP(localOrientation, localRotation); + RENAME_PROP(ignoreRayIntersection, ignorePickIntersection); + + RENAME_PROP_CONVERT(drawInFront, renderLayer, [](const QVariant& v) { return v.toBool() ? "front" : "world"; }); + RENAME_PROP_CONVERT(drawHUDLayer, renderLayer, [=](const QVariant& v) { + bool f = v.toBool(); + if (f) { + return QVariant("hud"); + } else if (overlayProps.contains("renderLayer")) { + return overlayProps["renderLayer"]; + } + return QVariant("world"); + }); + + OVERLAY_TO_GROUP_ENTITY_PROP_DEFAULT(grabbable, grab, grabbable, false); + + OVERLAY_TO_GROUP_ENTITY_PROP(pulseMin, pulse, min); + OVERLAY_TO_GROUP_ENTITY_PROP(pulseMax, pulse, max); + OVERLAY_TO_GROUP_ENTITY_PROP(pulsePeriod, pulse, period); + OVERLAY_TO_GROUP_ENTITY_PROP_CONVERT(colorPulse, pulse, colorMode, [](const QVariant& v) { + float f = v.toFloat(); + if (f > 0.0f) { + return "in"; + } else if (f < 0.0f) { + return "out"; + } + return "none"; + }); + OVERLAY_TO_GROUP_ENTITY_PROP_CONVERT(alphaPulse, pulse, alphaMode, [](const QVariant& v) { + float f = v.toFloat(); + if (f > 0.0f) { + return "in"; + } else if (f < 0.0f) { + return "out"; + } + return "none"; + }); + + if (type == "Shape" || type == "Box" || type == "Sphere" || type == "Gizmo") { + RENAME_PROP(solid, isSolid); + RENAME_PROP(isFilled, isSolid); + RENAME_PROP(filled, isSolid); + OVERLAY_TO_ENTITY_PROP_CONVERT_DEFAULT(isSolid, primitiveMode, false, [](const QVariant& v) { return v.toBool() ? "solid" : "lines"; }); + + RENAME_PROP(wire, isWire); + RENAME_PROP_CONVERT(isWire, primitiveMode, [](const QVariant& v) { return v.toBool() ? "lines" : "solid"; }); + } + + if (type == "Shape") { + SET_OVERLAY_PROP_DEFAULT(shape, "Hexagon"); + } else if (type == "Model") { + RENAME_PROP(url, modelURL); + RENAME_PROP(animationSettings, animation); + } else if (type == "Image") { + RENAME_PROP(url, imageURL); + } else if (type == "Web") { + RENAME_PROP(url, sourceUrl); + RENAME_PROP_CONVERT(inputMode, inputMode, [](const QVariant& v) { return v.toString() == "Mouse" ? "mouse" : "touch"; }); + } else if (type == "Gizmo") { + RENAME_PROP(radius, outerRadius); + if (add || overlayProps.contains("outerRadius")) { + float ratio = 2.0f; + { + auto iter = overlayProps.find("outerRadius"); + if (iter != overlayProps.end()) { + ratio = iter.value().toFloat() / 0.5f; + } + } + glm::vec3 dimensions = glm::vec3(1.0f); + { + auto iter = overlayProps.find("dimensions"); + if (iter != overlayProps.end()) { + dimensions = vec3FromVariant(iter.value()); + } else if (!add) { + EntityPropertyFlags desiredProperties; + desiredProperties += PROP_DIMENSIONS; + dimensions = DependencyManager::get()->getEntityProperties(id, desiredProperties).getDimensions(); + } + } + overlayProps["dimensions"] = vec3toVariant(ratio * dimensions); + } + + if (add || overlayProps.contains("rotation")) { + glm::quat rotation; + { + auto iter = overlayProps.find("rotation"); + if (iter != overlayProps.end()) { + rotation = quatFromVariant(iter.value()); + } else if (!add) { + EntityPropertyFlags desiredProperties; + desiredProperties += PROP_ROTATION; + rotation = DependencyManager::get()->getEntityProperties(id, desiredProperties).getRotation(); + } + } + overlayProps["rotation"] = quatToVariant(glm::angleAxis(-(float)M_PI_2, rotation * Vectors::RIGHT) * rotation); + } + if (add || overlayProps.contains("localRotation")) { + glm::quat rotation; + { + auto iter = overlayProps.find("localRotation"); + if (iter != overlayProps.end()) { + rotation = quatFromVariant(iter.value()); + } else if (!add) { + EntityPropertyFlags desiredProperties; + desiredProperties += PROP_LOCAL_ROTATION; + rotation = DependencyManager::get()->getEntityProperties(id, desiredProperties).getLocalRotation(); + } + } + overlayProps["localRotation"] = quatToVariant(glm::angleAxis(-(float)M_PI_2, rotation * Vectors::RIGHT) * rotation); + } + + { + RENAME_PROP(color, innerStartColor); + RENAME_PROP(color, innerEndColor); + RENAME_PROP(color, outerStartColor); + RENAME_PROP(color, outerEndColor); + + RENAME_PROP(startColor, innerStartColor); + RENAME_PROP(startColor, outerStartColor); + + RENAME_PROP(endColor, innerEndColor); + RENAME_PROP(endColor, outerEndColor); + + RENAME_PROP(innerColor, innerStartColor); + RENAME_PROP(innerColor, innerEndColor); + + RENAME_PROP(outerColor, outerStartColor); + RENAME_PROP(outerColor, outerEndColor); + } + + { + RENAME_PROP(alpha, innerStartAlpha); + RENAME_PROP(alpha, innerEndAlpha); + RENAME_PROP(alpha, outerStartAlpha); + RENAME_PROP(alpha, outerEndAlpha); + + RENAME_PROP(startAlpha, innerStartAlpha); + RENAME_PROP(startAlpha, outerStartAlpha); + + RENAME_PROP(endAlpha, innerEndAlpha); + RENAME_PROP(endAlpha, outerEndAlpha); + + RENAME_PROP(innerAlpha, innerStartAlpha); + RENAME_PROP(innerAlpha, innerEndAlpha); + + RENAME_PROP(outerAlpha, outerStartAlpha); + RENAME_PROP(outerAlpha, outerEndAlpha); + } + + OVERLAY_TO_GROUP_ENTITY_PROP(startAt, ring, startAngle); + OVERLAY_TO_GROUP_ENTITY_PROP(endAt, ring, endAngle); + OVERLAY_TO_GROUP_ENTITY_PROP(innerRadius, ring, innerRadius); + + OVERLAY_TO_GROUP_ENTITY_PROP(innerStartColor, ring, innerStartColor); + OVERLAY_TO_GROUP_ENTITY_PROP(innerEndColor, ring, innerEndColor); + OVERLAY_TO_GROUP_ENTITY_PROP(outerStartColor, ring, outerStartColor); + OVERLAY_TO_GROUP_ENTITY_PROP(outerEndColor, ring, outerEndColor); + OVERLAY_TO_GROUP_ENTITY_PROP(innerStartAlpha, ring, innerStartAlpha); + OVERLAY_TO_GROUP_ENTITY_PROP(innerEndAlpha, ring, innerEndAlpha); + OVERLAY_TO_GROUP_ENTITY_PROP(outerStartAlpha, ring, outerStartAlpha); + OVERLAY_TO_GROUP_ENTITY_PROP(outerEndAlpha, ring, outerEndAlpha); + + OVERLAY_TO_GROUP_ENTITY_PROP(hasTickMarks, ring, hasTickMarks); + OVERLAY_TO_GROUP_ENTITY_PROP(majorTickMarksAngle, ring, majorTickMarksAngle); + OVERLAY_TO_GROUP_ENTITY_PROP(minorTickMarksAngle, ring, minorTickMarksAngle); + OVERLAY_TO_GROUP_ENTITY_PROP(majorTickMarksLength, ring, majorTickMarksLength); + OVERLAY_TO_GROUP_ENTITY_PROP(minorTickMarksLength, ring, minorTickMarksLength); + OVERLAY_TO_GROUP_ENTITY_PROP(majorTickMarksColor, ring, majorTickMarksColor); + OVERLAY_TO_GROUP_ENTITY_PROP(minorTickMarksColor, ring, minorTickMarksColor); + } else if (type == "PolyLine") { + RENAME_PROP(startPoint, p1); + RENAME_PROP(start, p1); + RENAME_PROP(endPoint, p2); + RENAME_PROP(end, p2); + + RENAME_PROP(p1, position); + RENAME_PROP_CONVERT(p1, p1, [](const QVariant& v) { return vec3toVariant(glm::vec3(0.0f)); }); + RENAME_PROP_CONVERT(p2, p2, [=](const QVariant& v) { + glm::vec3 position; + auto iter2 = overlayProps.find("position"); + if (iter2 != overlayProps.end()) { + position = vec3FromVariant(iter2.value()); + } else if (!add) { + EntityPropertyFlags desiredProperties; + desiredProperties += PROP_POSITION; + position = DependencyManager::get()->getEntityProperties(id, desiredProperties).getPosition(); + } + return vec3toVariant(vec3FromVariant(v) - position); + }); + + RENAME_PROP(localStart, p1); + RENAME_PROP(localEnd, p2); + + { + QVariantList points; + { + auto iter = overlayProps.find("p1"); + if (iter != overlayProps.end()) { + points.push_back(iter.value()); + } + } + { + auto iter = overlayProps.find("p2"); + if (iter != overlayProps.end()) { + points.push_back(iter.value()); + } + } + overlayProps["linePoints"] = points; + } + { + auto iter = overlayProps.find("lineWidth"); + if (iter != overlayProps.end()) { + QVariantList widths; + QVariant width = iter.value(); + widths.append(width); + widths.append(width); + overlayProps["strokeWidths"] = widths; + } + } + + RENAME_PROP_CONVERT(glow, glow, [](const QVariant& v) { return v.toFloat() > 0.0f ? true : false; }); + SET_OVERLAY_PROP_DEFAULT(faceCamera, true); + { + QVariantList normals; + normals.append(vec3toVariant(Vectors::UP)); + normals.append(vec3toVariant(Vectors::UP)); + SET_OVERLAY_PROP_DEFAULT(normals, normals); + } + + SET_OVERLAY_PROP_DEFAULT(textures, PathUtils::resourcesUrl() + "images/whitePixel.png"); + } + + if (type == "Text" || type == "Image" || type == "Grid" || type == "Web") { + glm::quat originalRotation = ENTITY_ITEM_DEFAULT_ROTATION; + { + auto iter = overlayProps.find("rotation"); + if (iter != overlayProps.end()) { + originalRotation = quatFromVariant(iter.value()); + } else { + iter = overlayProps.find("localRotation"); + if (iter != overlayProps.end()) { + originalRotation = quatFromVariant(iter.value()); + } else if (!add) { + auto iter2 = savedRotations.find(id); + if (iter2 != savedRotations.end()) { + originalRotation = iter2.value(); + } + } + } + } + + if (!add) { + savedRotations[id] = originalRotation; + } else { + rotationToSave = originalRotation; + } + + glm::vec3 dimensions = ENTITY_ITEM_DEFAULT_DIMENSIONS; + { + auto iter = overlayProps.find("dimensions"); + if (iter != overlayProps.end()) { + bool valid = false; + dimensions = vec3FromVariant(iter.value(), valid); + if (!valid) { + dimensions = glm::vec3(vec2FromVariant(iter.value()), 0.0f); + } + } else if (!add) { + EntityPropertyFlags desiredProperties; + desiredProperties += PROP_DIMENSIONS; + dimensions = DependencyManager::get()->getEntityProperties(id, desiredProperties).getDimensions(); + } + } + + bool rotateX = dimensions.y < 0.0f; + bool rotateY = dimensions.x < 0.0f; + + { + glm::quat rotation = originalRotation; + if (rotateX) { + rotation = glm::angleAxis((float)M_PI, rotation * Vectors::RIGHT) * rotation; + } + if (rotateY) { + rotation = glm::angleAxis((float)M_PI, rotation * Vectors::UP) * rotation; + } + + overlayProps["localRotation"] = quatToVariant(rotation); + overlayProps["dimensions"] = vec3toVariant(glm::abs(dimensions)); + } + } + + QScriptEngine scriptEngine; + QScriptValue props = variantMapToScriptValue(overlayProps, scriptEngine); + EntityItemProperties toReturn; + EntityItemPropertiesFromScriptValueHonorReadOnly(props, toReturn); + return toReturn; +} + +QVariantMap Overlays::convertEntityToOverlayProperties(const EntityItemProperties& properties) { + QScriptEngine scriptEngine; + QVariantMap overlayProps = EntityItemPropertiesToScriptValue(&scriptEngine, properties).toVariant().toMap(); + + QString type = overlayProps["type"].toString(); + overlayProps["type"] = entityToOverlayType(type); + + if (type != "PolyLine") { + RENAME_PROP(position, p1); + RENAME_PROP(position, start); + } + RENAME_PROP(position, point); + RENAME_PROP(dimensions, scale); + RENAME_PROP(dimensions, size); + RENAME_PROP(ignorePickIntersection, ignoreRayIntersection); + + { + RENAME_PROP_CONVERT(primitiveMode, isSolid, [](const QVariant& v) { return v.toString() == "solid" ? true : false; }); + RENAME_PROP(isSolid, solid); + RENAME_PROP(isSolid, isFilled); + RENAME_PROP(isSolid, filled); + + RENAME_PROP_CONVERT(primitiveMode, isWire, [](const QVariant& v) { return v.toString() == "lines" ? true : false; }); + RENAME_PROP(isWire, wire); + } + + RENAME_PROP_CONVERT(renderLayer, drawInFront, [](const QVariant& v) { return v.toString() == "front" ? true : false; }); + RENAME_PROP_CONVERT(renderLayer, drawHUDLayer, [](const QVariant& v) { return v.toString() == "hud" ? true : false; }); + + GROUP_ENTITY_TO_OVERLAY_PROP(grab, grabbable, grabbable); + + GROUP_ENTITY_TO_OVERLAY_PROP(pulse, min, pulseMin); + GROUP_ENTITY_TO_OVERLAY_PROP(pulse, max, pulseMax); + GROUP_ENTITY_TO_OVERLAY_PROP(pulse, period, pulsePeriod); + GROUP_ENTITY_TO_OVERLAY_PROP_CONVERT(pulse, colorMode, colorPulse, [](const QVariant& v) { + QString f = v.toString(); + if (f == "in") { + return 1.0f; + } else if (f == "out") { + return -1.0f; + } + return 0.0f; + }); + GROUP_ENTITY_TO_OVERLAY_PROP_CONVERT(pulse, alphaMode, alphaPulse, [](const QVariant& v) { + QString f = v.toString(); + if (f == "in") { + return 1.0f; + } else if (f == "out") { + return -1.0f; + } + return 0.0f; + }); + + if (type == "Model") { + RENAME_PROP(modelURL, url); + RENAME_PROP(animation, animationSettings); + } else if (type == "Image") { + RENAME_PROP(imageURL, url); + } else if (type == "Web") { + RENAME_PROP(sourceUrl, url); + RENAME_PROP_CONVERT(inputMode, inputMode, [](const QVariant& v) { return v.toString() == "mouse" ? "Mouse" : "Touch"; }); + } else if (type == "Gizmo") { + RENAME_PROP_CONVERT(dimensions, outerRadius, [](const QVariant& v) { return 2.0f * vec3FromVariant(v).x; }); + RENAME_PROP(outerRadius, radius); + + RENAME_PROP_CONVERT(rotation, rotation, [](const QVariant& v) { + glm::quat rot = quatFromVariant(v); + return quatToVariant(glm::angleAxis((float)M_PI_2, rot * Vectors::RIGHT) * rot); + }); + RENAME_PROP_CONVERT(localRotation, localRotation, [](const QVariant& v) { + glm::quat rot = quatFromVariant(v); + return quatToVariant(glm::angleAxis((float)M_PI_2, rot * Vectors::RIGHT) * rot); + }); + + GROUP_ENTITY_TO_OVERLAY_PROP(ring, startAngle, startAt); + GROUP_ENTITY_TO_OVERLAY_PROP(ring, endAngle, endAt); + GROUP_ENTITY_TO_OVERLAY_PROP(ring, innerRadius, innerRadius); + + GROUP_ENTITY_TO_OVERLAY_PROP(ring, innerStartColor, innerStartColor); + GROUP_ENTITY_TO_OVERLAY_PROP(ring, innerEndColor, innerEndColor); + GROUP_ENTITY_TO_OVERLAY_PROP(ring, outerStartColor, outerStartColor); + GROUP_ENTITY_TO_OVERLAY_PROP(ring, outerEndColor, outerEndColor); + GROUP_ENTITY_TO_OVERLAY_PROP(ring, innerStartAlpha, innerStartAlpha); + GROUP_ENTITY_TO_OVERLAY_PROP(ring, innerEndAlpha, innerEndAlpha); + GROUP_ENTITY_TO_OVERLAY_PROP(ring, outerStartAlpha, outerStartAlpha); + GROUP_ENTITY_TO_OVERLAY_PROP(ring, outerEndAlpha, outerEndAlpha); + + GROUP_ENTITY_TO_OVERLAY_PROP(ring, hasTickMarks, hasTickMarks); + GROUP_ENTITY_TO_OVERLAY_PROP(ring, majorTickMarksAngle, majorTickMarksAngle); + GROUP_ENTITY_TO_OVERLAY_PROP(ring, minorTickMarksAngle, minorTickMarksAngle); + GROUP_ENTITY_TO_OVERLAY_PROP(ring, majorTickMarksLength, majorTickMarksLength); + GROUP_ENTITY_TO_OVERLAY_PROP(ring, minorTickMarksLength, minorTickMarksLength); + GROUP_ENTITY_TO_OVERLAY_PROP(ring, majorTickMarksColor, majorTickMarksColor); + GROUP_ENTITY_TO_OVERLAY_PROP(ring, minorTickMarksColor, minorTickMarksColor); + } else if (type == "PolyLine") { + QVector points = qVectorVec3FromScriptValue(scriptEngine.newVariant(overlayProps["linePoints"])); + glm::vec3 position = vec3FromVariant(overlayProps["position"]); + if (points.length() > 1) { + overlayProps["p1"] = vec3toVariant(points[0] + position); + overlayProps["p2"] = vec3toVariant(points[1] + position); + + overlayProps["localStart"] = vec3toVariant(points[0]); + overlayProps["localEnd"] = vec3toVariant(points[1]); + } + + RENAME_PROP(p1, startPoint); + RENAME_PROP(p1, start); + RENAME_PROP(p2, endPoint); + RENAME_PROP(p2, end); + + QVector widths = qVectorFloatFromScriptValue(scriptEngine.newVariant(overlayProps["strokeWidths"])); + if (widths.length() > 0) { + overlayProps["lineWidth"] = widths[0]; + } + + RENAME_PROP_CONVERT(glow, glow, [](const QVariant& v) { return v.toBool() ? 1.0f : 0.0f; }); + } + + // Do at the end, in case this type was rotated above + RENAME_PROP(rotation, orientation); + RENAME_PROP(localRotation, localOrientation); + + return overlayProps; +} + +QUuid Overlays::addOverlay(const QString& type, const QVariant& properties) { + if (_shuttingDown) { + return UNKNOWN_ENTITY_ID; } if (QThread::currentThread() != thread()) { - OverlayID result; + QUuid result; PROFILE_RANGE(script, __FUNCTION__); - BLOCKING_INVOKE_METHOD(this, "addOverlay", Q_RETURN_ARG(OverlayID, result), Q_ARG(QString, type), Q_ARG(QVariant, properties)); + BLOCKING_INVOKE_METHOD(this, "addOverlay", Q_RETURN_ARG(QUuid, result), Q_ARG(const QString&, type), Q_ARG(const QVariant&, properties)); return result; } - Overlay::Pointer thisOverlay = nullptr; - - /**jsdoc - *

An overlay may be one of the following types:

- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Value2D/3DDescription
circle3d3DA circle.
cube3DA cube. Can also use a shape overlay to create a - * cube.
grid3DA grid of lines in a plane.
image2DAn image. Synonym: billboard.
image3d3DAn image.
line3d3DA line.
model3DA model.
rectangle2DA rectangle.
rectangle3d3DA rectangle.
shape3DA geometric shape, such as a cube, sphere, or cylinder.
sphere3DA sphere. Can also use a shape overlay to create a - * sphere.
text2DText.
text3d3DText.
web3d3DWeb content.
- *

2D overlays are rendered on the display surface in desktop mode and on the HUD surface in HMD mode. 3D overlays are - * rendered at a position and orientation in-world.

- *

Each overlay type has different {@link Overlays.OverlayProperties|OverlayProperties}.

- * @typedef {string} Overlays.OverlayType - */ - - /**jsdoc - *

Different overlay types have different properties:

- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
{@link Overlays.OverlayType|OverlayType}Overlay Properties
circle3d{@link Overlays.Circle3DProperties|Circle3DProperties}
cube{@link Overlays.CubeProperties|CubeProperties}
grid{@link Overlays.GridProperties|GridProperties}
image{@link Overlays.ImageProperties|ImageProperties}
image3d{@link Overlays.Image3DProperties|Image3DProperties}
line3d{@link Overlays.Line3DProperties|Line3DProperties}
model{@link Overlays.ModelProperties|ModelProperties}
rectangle{@link Overlays.RectangleProperties|RectangleProperties}
rectangle3d{@link Overlays.Rectangle3DProperties|Rectangle3DProperties}
shape{@link Overlays.ShapeProperties|ShapeProperties}
sphere{@link Overlays.SphereProperties|SphereProperties}
text{@link Overlays.TextProperties|TextProperties}
text3d{@link Overlays.Text3DProperties|Text3DProperties}
web3d{@link Overlays.Web3DProperties|Web3DProperties}
- * @typedef {object} Overlays.OverlayProperties - */ - + Overlay::Pointer overlay; if (type == ImageOverlay::TYPE) { #if !defined(DISABLE_QML) - thisOverlay = Overlay::Pointer(new ImageOverlay(), [](Overlay* ptr) { ptr->deleteLater(); }); + overlay = Overlay::Pointer(new ImageOverlay(), [](Overlay* ptr) { ptr->deleteLater(); }); #endif - } else if (type == Image3DOverlay::TYPE || type == "billboard") { // "billboard" for backwards compatibility - thisOverlay = Overlay::Pointer(new Image3DOverlay(), [](Overlay* ptr) { ptr->deleteLater(); }); } else if (type == TextOverlay::TYPE) { #if !defined(DISABLE_QML) - thisOverlay = Overlay::Pointer(new TextOverlay(), [](Overlay* ptr) { ptr->deleteLater(); }); + overlay = Overlay::Pointer(new TextOverlay(), [](Overlay* ptr) { ptr->deleteLater(); }); #endif - } else if (type == Text3DOverlay::TYPE) { - thisOverlay = Overlay::Pointer(new Text3DOverlay(), [](Overlay* ptr) { ptr->deleteLater(); }); - } else if (type == Shape3DOverlay::TYPE) { - thisOverlay = Overlay::Pointer(new Shape3DOverlay(), [](Overlay* ptr) { ptr->deleteLater(); }); - } else if (type == Cube3DOverlay::TYPE) { - thisOverlay = Overlay::Pointer(new Cube3DOverlay(), [](Overlay* ptr) { ptr->deleteLater(); }); - } else if (type == Sphere3DOverlay::TYPE) { - thisOverlay = Overlay::Pointer(new Sphere3DOverlay(), [](Overlay* ptr) { ptr->deleteLater(); }); - } else if (type == Circle3DOverlay::TYPE) { - thisOverlay = Overlay::Pointer(new Circle3DOverlay(), [](Overlay* ptr) { ptr->deleteLater(); }); - } else if (type == Rectangle3DOverlay::TYPE) { - thisOverlay = Overlay::Pointer(new Rectangle3DOverlay(), [](Overlay* ptr) { ptr->deleteLater(); }); - } else if (type == Line3DOverlay::TYPE) { - thisOverlay = Overlay::Pointer(new Line3DOverlay(), [](Overlay* ptr) { ptr->deleteLater(); }); - } else if (type == Grid3DOverlay::TYPE) { - thisOverlay = Overlay::Pointer(new Grid3DOverlay(), [](Overlay* ptr) { ptr->deleteLater(); }); - } else if (type == ModelOverlay::TYPE) { - thisOverlay = Overlay::Pointer(new ModelOverlay(), [](Overlay* ptr) { ptr->deleteLater(); }); - } else if (type == Web3DOverlay::TYPE) { - thisOverlay = Overlay::Pointer(new Web3DOverlay(), [](Overlay* ptr) { ptr->deleteLater(); }); } else if (type == RectangleOverlay::TYPE) { - thisOverlay = Overlay::Pointer(new RectangleOverlay(), [](Overlay* ptr) { ptr->deleteLater(); }); + overlay = Overlay::Pointer(new RectangleOverlay(), [](Overlay* ptr) { ptr->deleteLater(); }); } - if (thisOverlay) { - thisOverlay->setProperties(properties.toMap()); - return addOverlay(thisOverlay); + if (overlay) { + overlay->setProperties(properties.toMap()); + return add2DOverlay(overlay); } - return UNKNOWN_OVERLAY_ID; + + QString entityType = overlayToEntityType(type); + if (entityType == "Unknown") { + return UNKNOWN_ENTITY_ID; + } + + QVariantMap propertyMap = properties.toMap(); + if (type == "rectangle3d") { + propertyMap["shape"] = "Quad"; + } + glm::quat rotationToSave; + QUuid id = DependencyManager::get()->addEntityInternal(convertOverlayToEntityProperties(propertyMap, rotationToSave, entityType, true), entity::HostType::LOCAL); + if (entityType == "Text" || entityType == "Image" || entityType == "Grid" || entityType == "Web") { + savedRotations[id] = rotationToSave; + } + return id; } -OverlayID Overlays::addOverlay(const Overlay::Pointer& overlay) { +QUuid Overlays::add2DOverlay(const Overlay::Pointer& overlay) { if (_shuttingDown) { - return UNKNOWN_OVERLAY_ID; + return UNKNOWN_ENTITY_ID; } - OverlayID thisID = OverlayID(QUuid::createUuid()); - overlay->setOverlayID(thisID); + QUuid thisID = QUuid::createUuid(); + overlay->setID(thisID); overlay->setStackOrder(_stackOrder++); - if (overlay->is3D()) { - { - QMutexLocker locker(&_mutex); - _overlaysWorld[thisID] = overlay; - } - - render::ScenePointer scene = qApp->getMain3DScene(); - render::Transaction transaction; - overlay->addToScene(overlay, scene, transaction); - scene->enqueueTransaction(transaction); - } else { + { QMutexLocker locker(&_mutex); - _overlaysHUD[thisID] = overlay; + _overlays[thisID] = overlay; } return thisID; } -OverlayID Overlays::cloneOverlay(OverlayID id) { +QUuid Overlays::cloneOverlay(const QUuid& id) { if (_shuttingDown) { - return UNKNOWN_OVERLAY_ID; + return UNKNOWN_ENTITY_ID; } if (QThread::currentThread() != thread()) { - OverlayID result; + QUuid result; PROFILE_RANGE(script, __FUNCTION__); - BLOCKING_INVOKE_METHOD(this, "cloneOverlay", Q_RETURN_ARG(OverlayID, result), Q_ARG(OverlayID, id)); + BLOCKING_INVOKE_METHOD(this, "cloneOverlay", Q_RETURN_ARG(QUuid, result), Q_ARG(const QUuid&, id)); return result; } - Overlay::Pointer thisOverlay = getOverlay(id); - - if (thisOverlay) { - OverlayID cloneId = addOverlay(Overlay::Pointer(thisOverlay->createClone(), [](Overlay* ptr) { ptr->deleteLater(); })); - return cloneId; + Overlay::Pointer overlay = get2DOverlay(id); + if (overlay) { + return add2DOverlay(Overlay::Pointer(overlay->createClone(), [](Overlay* ptr) { ptr->deleteLater(); })); } - return UNKNOWN_OVERLAY_ID; // Not found + return DependencyManager::get()->cloneEntity(id); } -bool Overlays::editOverlay(OverlayID id, const QVariant& properties) { +bool Overlays::editOverlay(const QUuid& id, const QVariant& properties) { if (_shuttingDown) { return false; } - auto thisOverlay = getOverlay(id); - if (!thisOverlay) { - return false; - } + auto overlay = get2DOverlay(id); + if (overlay) { + if (QThread::currentThread() != thread()) { + // NOTE editOverlay can be called very frequently in scripts and can't afford to + // block waiting on the main thread. Additionally, no script actually + // examines the return value and does something useful with it, so use a non-blocking + // invoke and just always return true + QMetaObject::invokeMethod(this, "editOverlay", Q_ARG(const QUuid&, id), Q_ARG(const QVariant&, properties)); + return true; + } - if (!thisOverlay->is3D() && QThread::currentThread() != thread()) { - // NOTE editOverlay can be called very frequently in scripts and can't afford to - // block waiting on the main thread. Additionally, no script actually - // examines the return value and does something useful with it, so use a non-blocking - // invoke and just always return true - QMetaObject::invokeMethod(this, "editOverlay", Q_ARG(OverlayID, id), Q_ARG(QVariant, properties)); + overlay->setProperties(properties.toMap()); return true; } - thisOverlay->setProperties(properties.toMap()); - return true; + auto entityScriptingInterface = DependencyManager::get(); + auto propertyMap = properties.toMap(); + EntityItemProperties entityProperties = convertOverlayToEntityProperties(propertyMap, entityScriptingInterface->getEntityType(id), false, id); + return !entityScriptingInterface->editEntity(id, entityProperties).isNull(); } bool Overlays::editOverlays(const QVariant& propertiesById) { @@ -348,286 +856,229 @@ bool Overlays::editOverlays(const QVariant& propertiesById) { return false; } - bool defer2DOverlays = QThread::currentThread() != thread(); + bool deferOverlays = QThread::currentThread() != thread(); - QVariantMap deferrred; + QVariantMap deferred; const QVariantMap map = propertiesById.toMap(); - bool success = true; + auto entityScriptingInterface = DependencyManager::get(); for (const auto& key : map.keys()) { - OverlayID id = OverlayID(key); - Overlay::Pointer thisOverlay = getOverlay(id); - if (!thisOverlay) { - success = false; - continue; - } - + QUuid id = QUuid(key); const QVariant& properties = map[key]; - if (defer2DOverlays && !thisOverlay->is3D()) { - deferrred[key] = properties; - continue; + + Overlay::Pointer overlay = get2DOverlay(id); + if (overlay) { + if (deferOverlays) { + deferred[key] = properties; + continue; + } + overlay->setProperties(properties.toMap()); + } else { + auto propertyMap = properties.toMap(); + entityScriptingInterface->editEntity(id, convertOverlayToEntityProperties(propertyMap, entityScriptingInterface->getEntityType(id), false, id)); } - thisOverlay->setProperties(properties.toMap()); } // For 2D/QML overlays, we need to perform the edit on the main thread - if (defer2DOverlays && !deferrred.empty()) { + if (!deferred.empty()) { // NOTE see comment on editOverlay for why this is not a blocking call - QMetaObject::invokeMethod(this, "editOverlays", Q_ARG(QVariant, deferrred)); + QMetaObject::invokeMethod(this, "editOverlays", Q_ARG(const QVariant&, deferred)); } - return success; + return true; } -void Overlays::deleteOverlay(OverlayID id) { +void Overlays::deleteOverlay(const QUuid& id) { if (_shuttingDown) { return; } - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "deleteOverlay", Q_ARG(OverlayID, id)); + Overlay::Pointer overlay = take2DOverlay(id); + if (overlay) { + _overlaysToDelete.push_back(overlay); + emit overlayDeleted(id); return; } - Overlay::Pointer overlayToDelete; - - { - QMutexLocker locker(&_mutex); - if (_overlaysHUD.contains(id)) { - overlayToDelete = _overlaysHUD.take(id); - } else if (_overlaysWorld.contains(id)) { - overlayToDelete = _overlaysWorld.take(id); - } else { - return; - } - } - - _overlaysToDelete.push_back(overlayToDelete); + DependencyManager::get()->deleteEntity(id); emit overlayDeleted(id); } -QString Overlays::getOverlayType(OverlayID overlayId) { +QString Overlays::getOverlayType(const QUuid& id) { if (_shuttingDown) { return ""; } + if (QThread::currentThread() != thread()) { QString result; PROFILE_RANGE(script, __FUNCTION__); - BLOCKING_INVOKE_METHOD(this, "getOverlayType", Q_RETURN_ARG(QString, result), Q_ARG(OverlayID, overlayId)); + BLOCKING_INVOKE_METHOD(this, "getOverlayType", Q_RETURN_ARG(QString, result), Q_ARG(const QUuid&, id)); return result; } - Overlay::Pointer overlay = getOverlay(overlayId); + Overlay::Pointer overlay = get2DOverlay(id); if (overlay) { return overlay->getType(); } - return ""; + + return entityToOverlayType(DependencyManager::get()->getEntityType(id)); } -QObject* Overlays::getOverlayObject(OverlayID id) { +QObject* Overlays::getOverlayObject(const QUuid& id) { if (QThread::currentThread() != thread()) { QObject* result; PROFILE_RANGE(script, __FUNCTION__); - BLOCKING_INVOKE_METHOD(this, "getOverlayObject", Q_RETURN_ARG(QObject*, result), Q_ARG(OverlayID, id)); + BLOCKING_INVOKE_METHOD(this, "getOverlayObject", Q_RETURN_ARG(QObject*, result), Q_ARG(const QUuid&, id)); return result; } - Overlay::Pointer thisOverlay = getOverlay(id); - if (thisOverlay) { - return qobject_cast(&(*thisOverlay)); + Overlay::Pointer overlay = get2DOverlay(id); + if (overlay) { + return qobject_cast(&(*overlay)); } - return nullptr; + + return DependencyManager::get()->getEntityObject(id); } -OverlayID Overlays::getOverlayAtPoint(const glm::vec2& point) { +QUuid Overlays::getOverlayAtPoint(const glm::vec2& point) { if (_shuttingDown || !_enabled) { - return UNKNOWN_OVERLAY_ID; + return UNKNOWN_ENTITY_ID; } QMutexLocker locker(&_mutex); - QMapIterator i(_overlaysHUD); + QMapIterator i(_overlays); unsigned int bestStackOrder = 0; - OverlayID bestOverlayID = UNKNOWN_OVERLAY_ID; + QUuid bestID = UNKNOWN_ENTITY_ID; while (i.hasNext()) { i.next(); auto thisOverlay = std::dynamic_pointer_cast(i.value()); if (thisOverlay && thisOverlay->getVisible() && thisOverlay->isLoaded() && thisOverlay->getBoundingRect().contains(point.x, point.y, false)) { if (thisOverlay->getStackOrder() > bestStackOrder) { - bestOverlayID = i.key(); + bestID = i.key(); bestStackOrder = thisOverlay->getStackOrder(); } } } - return bestOverlayID; + return bestID; } -OverlayPropertyResult Overlays::getProperty(OverlayID id, const QString& property) { - Overlay::Pointer thisOverlay = getOverlay(id); - OverlayPropertyResult result; - if (thisOverlay && thisOverlay->supportsGetProperty()) { - result.value = thisOverlay->getProperty(property); - } - return result; -} - -OverlayPropertyResult Overlays::getProperties(const OverlayID& id, const QStringList& properties) { - Overlay::Pointer thisOverlay = getOverlay(id); - OverlayPropertyResult result; - if (thisOverlay && thisOverlay->supportsGetProperty()) { - QVariantMap mapResult; - for (const auto& property : properties) { - mapResult.insert(property, thisOverlay->getProperty(property)); +QVariant Overlays::getProperty(const QUuid& id, const QString& property) { + Overlay::Pointer overlay = get2DOverlay(id); + if (overlay) { + if (overlay->supportsGetProperty()) { + return overlay->getProperty(property); } - result.value = mapResult; + return QVariant(); } - return result; + + QVariantMap overlayProperties = convertEntityToOverlayProperties(DependencyManager::get()->getEntityProperties(id)); + auto propIter = overlayProperties.find(property); + if (propIter != overlayProperties.end()) { + return propIter.value(); + } + return QVariant(); } -OverlayPropertyResult Overlays::getOverlaysProperties(const QVariant& propertiesById) { - QVariantMap map = propertiesById.toMap(); - OverlayPropertyResult result; - QVariantMap resultMap; - for (const auto& key : map.keys()) { - OverlayID id = OverlayID(key); - QVariantMap overlayResult; - Overlay::Pointer thisOverlay = getOverlay(id); - if (thisOverlay && thisOverlay->supportsGetProperty()) { - QStringList propertiesToFetch = map[key].toStringList(); - for (const auto& property : propertiesToFetch) { - overlayResult[property] = thisOverlay->getProperty(property); +QVariantMap Overlays::getProperties(const QUuid& id, const QStringList& properties) { + Overlay::Pointer overlay = get2DOverlay(id); + QVariantMap result; + if (overlay) { + if (overlay->supportsGetProperty()) { + for (const auto& property : properties) { + result.insert(property, overlay->getProperty(property)); } } - resultMap[key] = overlayResult; + return result; + } + + QVariantMap overlayProperties = convertEntityToOverlayProperties(DependencyManager::get()->getEntityProperties(id)); + for (const auto& property : properties) { + auto propIter = overlayProperties.find(property); + if (propIter != overlayProperties.end()) { + result.insert(property, propIter.value()); + } } - result.value = resultMap; return result; } -OverlayPropertyResult::OverlayPropertyResult() { -} - -QScriptValue OverlayPropertyResultToScriptValue(QScriptEngine* engine, const OverlayPropertyResult& value) { - if (!value.value.isValid()) { - return QScriptValue::UndefinedValue; +QVariantMap Overlays::getOverlaysProperties(const QVariant& propertiesById) { + QVariantMap map = propertiesById.toMap(); + QVariantMap result; + for (const auto& key : map.keys()) { + result[key] = getProperties(QUuid(key), map[key].toStringList()); } - return engine->toScriptValue(value.value); + return result; } -void OverlayPropertyResultFromScriptValue(const QScriptValue& object, OverlayPropertyResult& value) { - value.value = object.toVariant(); -} - - RayToOverlayIntersectionResult Overlays::findRayIntersection(const PickRay& ray, bool precisionPicking, const QScriptValue& overlayIDsToInclude, const QScriptValue& overlayIDsToDiscard, bool visibleOnly, bool collidableOnly) { - const QVector overlaysToInclude = qVectorOverlayIDFromScriptValue(overlayIDsToInclude); - const QVector overlaysToDiscard = qVectorOverlayIDFromScriptValue(overlayIDsToDiscard); + const QVector include = qVectorEntityItemIDFromScriptValue(overlayIDsToInclude); + const QVector discard = qVectorEntityItemIDFromScriptValue(overlayIDsToDiscard); - return findRayIntersectionVector(ray, precisionPicking, - overlaysToInclude, overlaysToDiscard, visibleOnly, collidableOnly); + return findRayIntersectionVector(ray, precisionPicking, include, discard, visibleOnly, collidableOnly); } - RayToOverlayIntersectionResult Overlays::findRayIntersectionVector(const PickRay& ray, bool precisionPicking, - const QVector& overlaysToInclude, - const QVector& overlaysToDiscard, + const QVector& include, + const QVector& discard, bool visibleOnly, bool collidableOnly) { - float bestDistance = std::numeric_limits::max(); - bool bestIsFront = false; - bool bestIsTablet = false; - auto tabletIDs = qApp->getTabletIDs(); + unsigned int searchFilter = PickFilter::getBitMask(PickFilter::FlagBit::LOCAL_ENTITIES); - QMutexLocker locker(&_mutex); - RayToOverlayIntersectionResult result; - QMapIterator i(_overlaysWorld); - while (i.hasNext()) { - i.next(); - OverlayID thisID = i.key(); - auto thisOverlay = std::dynamic_pointer_cast(i.value()); - - if ((overlaysToDiscard.size() > 0 && overlaysToDiscard.contains(thisID)) || - (overlaysToInclude.size() > 0 && !overlaysToInclude.contains(thisID))) { - continue; - } - - if (thisOverlay && thisOverlay->getVisible() && !thisOverlay->getIgnorePickIntersection() && thisOverlay->isLoaded()) { - float thisDistance; - BoxFace thisFace; - glm::vec3 thisSurfaceNormal; - QVariantMap thisExtraInfo; - if (thisOverlay->findRayIntersectionExtraInfo(ray.origin, ray.direction, thisDistance, - thisFace, thisSurfaceNormal, thisExtraInfo, precisionPicking)) { - bool isDrawInFront = thisOverlay->getDrawInFront(); - bool isTablet = tabletIDs.contains(thisID); - if ((isDrawInFront && !bestIsFront && !bestIsTablet) - || ((isTablet || isDrawInFront || !bestIsFront) && thisDistance < bestDistance)) { - bestIsFront = isDrawInFront; - bestIsTablet = isTablet; - bestDistance = thisDistance; - result.intersects = true; - result.distance = thisDistance; - result.face = thisFace; - result.surfaceNormal = thisSurfaceNormal; - result.overlayID = thisID; - result.intersection = ray.origin + (ray.direction * thisDistance); - result.extraInfo = thisExtraInfo; - } - } - } + if (!precisionPicking) { + searchFilter = searchFilter | PickFilter::getBitMask(PickFilter::FlagBit::COARSE); } - return result; + + if (visibleOnly) { + searchFilter = searchFilter | PickFilter::getBitMask(PickFilter::FlagBit::VISIBLE); + } + + if (collidableOnly) { + searchFilter = searchFilter | PickFilter::getBitMask(PickFilter::FlagBit::COLLIDABLE); + } + auto result = DependencyManager::get()->evalRayIntersectionVector(ray, PickFilter(searchFilter), include, discard); + + RayToOverlayIntersectionResult overlayResult; + overlayResult.overlayID = result.entityID; + overlayResult.intersects = result.intersects; + overlayResult.intersection = result.intersection; + overlayResult.distance = result.distance; + overlayResult.surfaceNormal = result.surfaceNormal; + overlayResult.face = result.face; + overlayResult.extraInfo = result.extraInfo; + return overlayResult; } ParabolaToOverlayIntersectionResult Overlays::findParabolaIntersectionVector(const PickParabola& parabola, bool precisionPicking, - const QVector& overlaysToInclude, - const QVector& overlaysToDiscard, + const QVector& include, + const QVector& discard, bool visibleOnly, bool collidableOnly) { - float bestDistance = std::numeric_limits::max(); - bool bestIsFront = false; - const QVector keyboardKeysToDiscard = DependencyManager::get()->getKeysID(); - QMutexLocker locker(&_mutex); - ParabolaToOverlayIntersectionResult result; - QMapIterator i(_overlaysWorld); - while (i.hasNext()) { - i.next(); - OverlayID thisID = i.key(); - auto thisOverlay = std::dynamic_pointer_cast(i.value()); + unsigned int searchFilter = PickFilter::getBitMask(PickFilter::FlagBit::LOCAL_ENTITIES); - if ((overlaysToDiscard.size() > 0 && overlaysToDiscard.contains(thisID)) || - (overlaysToInclude.size() > 0 && !overlaysToInclude.contains(thisID)) || - (keyboardKeysToDiscard.size() > 0 && keyboardKeysToDiscard.contains(thisID))) { - continue; - } - - if (thisOverlay && thisOverlay->getVisible() && !thisOverlay->getIgnorePickIntersection() && thisOverlay->isLoaded()) { - float thisDistance; - BoxFace thisFace; - glm::vec3 thisSurfaceNormal; - QVariantMap thisExtraInfo; - if (thisOverlay->findParabolaIntersectionExtraInfo(parabola.origin, parabola.velocity, parabola.acceleration, thisDistance, - thisFace, thisSurfaceNormal, thisExtraInfo, precisionPicking)) { - bool isDrawInFront = thisOverlay->getDrawInFront(); - if ((bestIsFront && isDrawInFront && thisDistance < bestDistance) - || (!bestIsFront && (isDrawInFront || thisDistance < bestDistance))) { - - bestIsFront = isDrawInFront; - bestDistance = thisDistance; - result.intersects = true; - result.parabolicDistance = thisDistance; - result.face = thisFace; - result.surfaceNormal = thisSurfaceNormal; - result.overlayID = thisID; - result.intersection = parabola.origin + parabola.velocity * thisDistance + 0.5f * parabola.acceleration * thisDistance * thisDistance; - result.distance = glm::distance(result.intersection, parabola.origin); - result.extraInfo = thisExtraInfo; - } - } - } + if (!precisionPicking) { + searchFilter = searchFilter | PickFilter::getBitMask(PickFilter::FlagBit::COARSE); } - return result; + + if (visibleOnly) { + searchFilter = searchFilter | PickFilter::getBitMask(PickFilter::FlagBit::VISIBLE); + } + + if (collidableOnly) { + searchFilter = searchFilter | PickFilter::getBitMask(PickFilter::FlagBit::COLLIDABLE); + } + auto result = DependencyManager::get()->evalParabolaIntersectionVector(parabola, PickFilter(searchFilter), include, discard); + + ParabolaToOverlayIntersectionResult overlayResult; + overlayResult.overlayID = result.entityID; + overlayResult.intersects = result.intersects; + overlayResult.intersection = result.intersection; + overlayResult.parabolicDistance = result.parabolicDistance; + overlayResult.surfaceNormal = result.surfaceNormal; + overlayResult.face = result.face; + overlayResult.extraInfo = result.extraInfo; + return overlayResult; } QScriptValue RayToOverlayIntersectionResultToScriptValue(QScriptEngine* engine, const RayToOverlayIntersectionResult& value) { @@ -664,86 +1115,72 @@ void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, R value.extraInfo = object.property("extraInfo").toVariant().toMap(); } -bool Overlays::isLoaded(OverlayID id) { +bool Overlays::isLoaded(const QUuid& id) { if (QThread::currentThread() != thread()) { bool result; PROFILE_RANGE(script, __FUNCTION__); - BLOCKING_INVOKE_METHOD(this, "isLoaded", Q_RETURN_ARG(bool, result), Q_ARG(OverlayID, id)); + BLOCKING_INVOKE_METHOD(this, "isLoaded", Q_RETURN_ARG(bool, result), Q_ARG(const QUuid&, id)); return result; } - Overlay::Pointer thisOverlay = getOverlay(id); - if (!thisOverlay) { - return false; // not found + Overlay::Pointer overlay = get2DOverlay(id); + if (overlay) { + return overlay->isLoaded(); } - return thisOverlay->isLoaded(); + + return DependencyManager::get()->isLoaded(id); } -QSizeF Overlays::textSize(OverlayID id, const QString& text) { +QSizeF Overlays::textSize(const QUuid& id, const QString& text) { if (QThread::currentThread() != thread()) { QSizeF result; PROFILE_RANGE(script, __FUNCTION__); - BLOCKING_INVOKE_METHOD(this, "textSize", Q_RETURN_ARG(QSizeF, result), Q_ARG(OverlayID, id), Q_ARG(QString, text)); + BLOCKING_INVOKE_METHOD(this, "textSize", Q_RETURN_ARG(QSizeF, result), Q_ARG(const QUuid&, id), Q_ARG(QString, text)); return result; } - Overlay::Pointer thisOverlay = getOverlay(id); - if (thisOverlay) { - if (thisOverlay->is3D()) { - if (auto text3dOverlay = std::dynamic_pointer_cast(thisOverlay)) { - return text3dOverlay->textSize(text); - } - } else { - if (auto textOverlay = std::dynamic_pointer_cast(thisOverlay)) { - return textOverlay->textSize(text); - } + Overlay::Pointer overlay = get2DOverlay(id); + if (overlay) { + if (auto textOverlay = std::dynamic_pointer_cast(overlay)) { + return textOverlay->textSize(text); } + return QSizeF(0.0f, 0.0f); + } else { + return DependencyManager::get()->textSize(id, text); } - return QSizeF(0.0f, 0.0f); } -bool Overlays::isAddedOverlay(OverlayID id) { - if (QThread::currentThread() != thread()) { - bool result; - PROFILE_RANGE(script, __FUNCTION__); - BLOCKING_INVOKE_METHOD(this, "isAddedOverlay", Q_RETURN_ARG(bool, result), Q_ARG(OverlayID, id)); - return result; +bool Overlays::isAddedOverlay(const QUuid& id) { + Overlay::Pointer overlay = get2DOverlay(id); + if (overlay) { + return true; } - QMutexLocker locker(&_mutex); - return _overlaysHUD.contains(id) || _overlaysWorld.contains(id); + return DependencyManager::get()->isAddedEntity(id); } -void Overlays::sendMousePressOnOverlay(const OverlayID& overlayID, const PointerEvent& event) { - mousePressPointerEvent(overlayID, event); +void Overlays::sendMousePressOnOverlay(const QUuid& id, const PointerEvent& event) { + mousePressPointerEvent(id, event); } -void Overlays::sendMouseReleaseOnOverlay(const OverlayID& overlayID, const PointerEvent& event) { - mouseReleasePointerEvent(overlayID, event); +void Overlays::sendMouseReleaseOnOverlay(const QUuid& id, const PointerEvent& event) { + mouseReleasePointerEvent(id, event); } -void Overlays::sendMouseMoveOnOverlay(const OverlayID& overlayID, const PointerEvent& event) { - mouseMovePointerEvent(overlayID, event); +void Overlays::sendMouseMoveOnOverlay(const QUuid& id, const PointerEvent& event) { + mouseMovePointerEvent(id, event); } -void Overlays::sendHoverEnterOverlay(const OverlayID& overlayID, const PointerEvent& event) { - hoverEnterPointerEvent(overlayID, event); +void Overlays::sendHoverEnterOverlay(const QUuid& id, const PointerEvent& event) { + hoverEnterPointerEvent(id, event); } -void Overlays::sendHoverOverOverlay(const OverlayID& overlayID, const PointerEvent& event) { - hoverOverPointerEvent(overlayID, event); +void Overlays::sendHoverOverOverlay(const QUuid& id, const PointerEvent& event) { + hoverOverPointerEvent(id, event); } -void Overlays::sendHoverLeaveOverlay(const OverlayID& overlayID, const PointerEvent& event) { - hoverLeavePointerEvent(overlayID, event); -} - -OverlayID Overlays::getKeyboardFocusOverlay() { - return qApp->getKeyboardFocusOverlay(); -} - -void Overlays::setKeyboardFocusOverlay(const OverlayID& id) { - qApp->setKeyboardFocusOverlay(id); +void Overlays::sendHoverLeaveOverlay(const QUuid& id, const PointerEvent& event) { + hoverLeavePointerEvent(id, event); } float Overlays::width() { @@ -770,28 +1207,6 @@ float Overlays::height() { return offscreenUi->getWindow()->size().height(); } -static glm::vec2 projectOntoOverlayXYPlane(glm::vec3 position, glm::quat rotation, glm::vec2 dimensions, const PickRay& pickRay, - const RayToOverlayIntersectionResult& rayPickResult) { - - // Project the intersection point onto the local xy plane of the overlay. - float distance; - glm::vec3 planePosition = position; - glm::vec3 planeNormal = rotation * Vectors::UNIT_Z; - glm::vec3 overlayDimensions = glm::vec3(dimensions.x, dimensions.y, 0.0f); - glm::vec3 rayDirection = pickRay.direction; - glm::vec3 rayStart = pickRay.origin; - glm::vec3 p; - if (rayPlaneIntersection(planePosition, planeNormal, rayStart, rayDirection, distance)) { - p = rayStart + rayDirection * distance; - } else { - p = rayPickResult.intersection; - } - glm::vec3 localP = glm::inverse(rotation) * (p - position); - glm::vec3 normalizedP = (localP / overlayDimensions) + glm::vec3(0.5f); - return glm::vec2(normalizedP.x * overlayDimensions.x, - (1.0f - normalizedP.y) * overlayDimensions.y); // flip y-axis -} - static uint32_t toPointerButtons(const QMouseEvent& event) { uint32_t buttons = 0; buttons |= event.buttons().testFlag(Qt::LeftButton) ? PointerEvent::PrimaryButton : 0; @@ -813,73 +1228,76 @@ static PointerEvent::Button toPointerButton(const QMouseEvent& event) { } } -PointerEvent Overlays::calculateOverlayPointerEvent(OverlayID overlayID, PickRay ray, - RayToOverlayIntersectionResult rayPickResult, QMouseEvent* event, - PointerEvent::EventType eventType) { - auto overlay = std::dynamic_pointer_cast(getOverlay(overlayID)); - if (getOverlayType(overlayID) == "web3d") { - overlay = std::dynamic_pointer_cast(getOverlay(overlayID)); +RayToOverlayIntersectionResult getPrevPickResult(unsigned int mouseRayPickID) { + RayToOverlayIntersectionResult overlayResult; + overlayResult.intersects = false; + auto pickResult = DependencyManager::get()->getPrevPickResultTyped(mouseRayPickID); + if (pickResult) { + overlayResult.intersects = pickResult->type != IntersectionType::NONE; + if (overlayResult.intersects) { + overlayResult.intersection = pickResult->intersection; + overlayResult.distance = pickResult->distance; + overlayResult.surfaceNormal = pickResult->surfaceNormal; + overlayResult.overlayID = pickResult->objectID; + overlayResult.extraInfo = pickResult->extraInfo; + } } - if (!overlay) { - return PointerEvent(); - } - glm::vec3 position = overlay->getWorldPosition(); - glm::quat rotation = overlay->getWorldOrientation(); - glm::vec2 dimensions = overlay->getSize(); - - - glm::vec2 pos2D = projectOntoOverlayXYPlane(position, rotation, dimensions, ray, rayPickResult); - - PointerEvent pointerEvent(eventType, PointerManager::MOUSE_POINTER_ID, pos2D, rayPickResult.intersection, rayPickResult.surfaceNormal, - ray.direction, toPointerButton(*event), toPointerButtons(*event), event->modifiers()); - - return pointerEvent; + return overlayResult; } +PointerEvent Overlays::calculateOverlayPointerEvent(const QUuid& id, const PickRay& ray, + const RayToOverlayIntersectionResult& rayPickResult, QMouseEvent* event, + PointerEvent::EventType eventType) { + glm::vec2 pos2D = RayPick::projectOntoEntityXYPlane(id, rayPickResult.intersection); + return PointerEvent(eventType, PointerManager::MOUSE_POINTER_ID, pos2D, rayPickResult.intersection, rayPickResult.surfaceNormal, + ray.direction, toPointerButton(*event), toPointerButtons(*event), event->modifiers()); +} -bool Overlays::mousePressEvent(QMouseEvent* event) { +void Overlays::hoverEnterPointerEvent(const QUuid& id, const PointerEvent& event) { + auto keyboard = DependencyManager::get(); + // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed + if (!keyboard->getKeyIDs().contains(id)) { + emit hoverEnterOverlay(id, event); + } +} + +void Overlays::hoverOverPointerEvent(const QUuid& id, const PointerEvent& event) { + auto keyboard = DependencyManager::get(); + // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed + if (!keyboard->getKeyIDs().contains(id)) { + emit hoverOverOverlay(id, event); + } +} + +void Overlays::hoverLeavePointerEvent(const QUuid& id, const PointerEvent& event) { + auto keyboard = DependencyManager::get(); + // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed + if (!keyboard->getKeyIDs().contains(id)) { + emit hoverLeaveOverlay(id, event); + } +} + +std::pair Overlays::mousePressEvent(QMouseEvent* event) { PerformanceTimer perfTimer("Overlays::mousePressEvent"); PickRay ray = qApp->computePickRay(event->x(), event->y()); - RayToOverlayIntersectionResult rayPickResult = findRayIntersectionVector(ray, true, QVector(), - QVector()); + RayToOverlayIntersectionResult rayPickResult = getPrevPickResult(_mouseRayPickID); if (rayPickResult.intersects) { _currentClickingOnOverlayID = rayPickResult.overlayID; PointerEvent pointerEvent = calculateOverlayPointerEvent(_currentClickingOnOverlayID, ray, rayPickResult, event, PointerEvent::Press); mousePressPointerEvent(_currentClickingOnOverlayID, pointerEvent); - return true; + return { rayPickResult.distance, rayPickResult.overlayID }; } - // if we didn't press on an overlay, disable overlay keyboard focus - setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID); - // emit to scripts emit mousePressOffOverlay(); - return false; + return { FLT_MAX, UNKNOWN_ENTITY_ID }; } -void Overlays::mousePressPointerEvent(const OverlayID& overlayID, const PointerEvent& event) { - // TODO: generalize this to allow any overlay to recieve events - std::shared_ptr thisOverlay; - if (getOverlayType(overlayID) == "web3d") { - thisOverlay = std::static_pointer_cast(getOverlay(overlayID)); - } - if (thisOverlay) { - if (event.shouldFocus()) { - // Focus keyboard on web overlays - DependencyManager::get()->setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); - setKeyboardFocusOverlay(overlayID); - } - - // Send to web overlay - QMetaObject::invokeMethod(thisOverlay.get(), "handlePointerEvent", Q_ARG(PointerEvent, event)); - } - - +void Overlays::mousePressPointerEvent(const QUuid& id, const PointerEvent& event) { auto keyboard = DependencyManager::get(); // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed - if (!keyboard->getKeysID().contains(overlayID)) { - // emit to scripts - emit mousePressOnOverlay(overlayID, event); + if (!keyboard->getKeyIDs().contains(id)) { + emit mousePressOnOverlay(id, event); } } @@ -887,109 +1305,37 @@ bool Overlays::mouseDoublePressEvent(QMouseEvent* event) { PerformanceTimer perfTimer("Overlays::mouseDoublePressEvent"); PickRay ray = qApp->computePickRay(event->x(), event->y()); - RayToOverlayIntersectionResult rayPickResult = findRayIntersectionVector(ray, true, QVector(), - QVector()); + RayToOverlayIntersectionResult rayPickResult = getPrevPickResult(_mouseRayPickID); if (rayPickResult.intersects) { _currentClickingOnOverlayID = rayPickResult.overlayID; auto pointerEvent = calculateOverlayPointerEvent(_currentClickingOnOverlayID, ray, rayPickResult, event, PointerEvent::Press); - // emit to scripts emit mouseDoublePressOnOverlay(_currentClickingOnOverlayID, pointerEvent); return true; } - // emit to scripts emit mouseDoublePressOffOverlay(); return false; } -void Overlays::hoverEnterPointerEvent(const OverlayID& overlayID, const PointerEvent& event) { - // TODO: generalize this to allow any overlay to recieve events - std::shared_ptr thisOverlay; - if (getOverlayType(overlayID) == "web3d") { - thisOverlay = std::static_pointer_cast(getOverlay(overlayID)); - } - if (thisOverlay) { - // Send to web overlay - QMetaObject::invokeMethod(thisOverlay.get(), "hoverEnterOverlay", Q_ARG(PointerEvent, event)); - } - - auto keyboard = DependencyManager::get(); - // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed - if (!keyboard->getKeysID().contains(overlayID)) { - // emit to scripts - emit hoverEnterOverlay(overlayID, event); - } -} - -void Overlays::hoverOverPointerEvent(const OverlayID& overlayID, const PointerEvent& event) { - // TODO: generalize this to allow any overlay to recieve events - std::shared_ptr thisOverlay; - if (getOverlayType(overlayID) == "web3d") { - thisOverlay = std::static_pointer_cast(getOverlay(overlayID)); - } - if (thisOverlay) { - // Send to web overlay - QMetaObject::invokeMethod(thisOverlay.get(), "handlePointerEvent", Q_ARG(PointerEvent, event)); - } - - auto keyboard = DependencyManager::get(); - // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed - if (!keyboard->getKeysID().contains(overlayID)) { - // emit to scripts - emit hoverOverOverlay(overlayID, event); - } -} - -void Overlays::hoverLeavePointerEvent(const OverlayID& overlayID, const PointerEvent& event) { - // TODO: generalize this to allow any overlay to recieve events - std::shared_ptr thisOverlay; - if (getOverlayType(overlayID) == "web3d") { - thisOverlay = std::static_pointer_cast(getOverlay(overlayID)); - } - if (thisOverlay) { - // Send to web overlay - QMetaObject::invokeMethod(thisOverlay.get(), "hoverLeaveOverlay", Q_ARG(PointerEvent, event)); - } - - auto keyboard = DependencyManager::get(); - // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed - if (!keyboard->getKeysID().contains(overlayID)) { - // emit to scripts - emit hoverLeaveOverlay(overlayID, event); - } -} - bool Overlays::mouseReleaseEvent(QMouseEvent* event) { PerformanceTimer perfTimer("Overlays::mouseReleaseEvent"); PickRay ray = qApp->computePickRay(event->x(), event->y()); - RayToOverlayIntersectionResult rayPickResult = findRayIntersectionVector(ray, true, QVector(), - QVector()); + RayToOverlayIntersectionResult rayPickResult = getPrevPickResult(_mouseRayPickID); if (rayPickResult.intersects) { auto pointerEvent = calculateOverlayPointerEvent(rayPickResult.overlayID, ray, rayPickResult, event, PointerEvent::Release); mouseReleasePointerEvent(rayPickResult.overlayID, pointerEvent); } - _currentClickingOnOverlayID = UNKNOWN_OVERLAY_ID; + _currentClickingOnOverlayID = UNKNOWN_ENTITY_ID; return false; } -void Overlays::mouseReleasePointerEvent(const OverlayID& overlayID, const PointerEvent& event) { - // TODO: generalize this to allow any overlay to recieve events - std::shared_ptr thisOverlay; - if (getOverlayType(overlayID) == "web3d") { - thisOverlay = std::static_pointer_cast(getOverlay(overlayID)); - } - if (thisOverlay) { - // Send to web overlay - QMetaObject::invokeMethod(thisOverlay.get(), "handlePointerEvent", Q_ARG(PointerEvent, event)); - } - +void Overlays::mouseReleasePointerEvent(const QUuid& id, const PointerEvent& event) { auto keyboard = DependencyManager::get(); // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed - if (!keyboard->getKeysID().contains(overlayID)) { - // emit to scripts - emit mouseReleaseOnOverlay(overlayID, event); + if (!keyboard->getKeyIDs().contains(id)) { + emit mouseReleaseOnOverlay(id, event); } } @@ -997,14 +1343,13 @@ bool Overlays::mouseMoveEvent(QMouseEvent* event) { PerformanceTimer perfTimer("Overlays::mouseMoveEvent"); PickRay ray = qApp->computePickRay(event->x(), event->y()); - RayToOverlayIntersectionResult rayPickResult = findRayIntersectionVector(ray, true, QVector(), - QVector()); + RayToOverlayIntersectionResult rayPickResult = getPrevPickResult(_mouseRayPickID); if (rayPickResult.intersects) { auto pointerEvent = calculateOverlayPointerEvent(rayPickResult.overlayID, ray, rayPickResult, event, PointerEvent::Move); mouseMovePointerEvent(rayPickResult.overlayID, pointerEvent); // If previously hovering over a different overlay then leave hover on that overlay. - if (_currentHoverOverOverlayID != UNKNOWN_OVERLAY_ID && rayPickResult.overlayID != _currentHoverOverOverlayID) { + if (_currentHoverOverOverlayID != UNKNOWN_ENTITY_ID && rayPickResult.overlayID != _currentHoverOverOverlayID) { auto pointerEvent = calculateOverlayPointerEvent(_currentHoverOverOverlayID, ray, rayPickResult, event, PointerEvent::Move); hoverLeavePointerEvent(_currentHoverOverOverlayID, pointerEvent); } @@ -1020,69 +1365,678 @@ bool Overlays::mouseMoveEvent(QMouseEvent* event) { _currentHoverOverOverlayID = rayPickResult.overlayID; } else { // If previously hovering an overlay then leave hover. - if (_currentHoverOverOverlayID != UNKNOWN_OVERLAY_ID) { + if (_currentHoverOverOverlayID != UNKNOWN_ENTITY_ID) { auto pointerEvent = calculateOverlayPointerEvent(_currentHoverOverOverlayID, ray, rayPickResult, event, PointerEvent::Move); hoverLeavePointerEvent(_currentHoverOverOverlayID, pointerEvent); - _currentHoverOverOverlayID = UNKNOWN_OVERLAY_ID; + _currentHoverOverOverlayID = UNKNOWN_ENTITY_ID; } } return false; } -void Overlays::mouseMovePointerEvent(const OverlayID& overlayID, const PointerEvent& event) { - // TODO: generalize this to allow any overlay to recieve events - std::shared_ptr thisOverlay; - if (getOverlayType(overlayID) == "web3d") { - thisOverlay = std::static_pointer_cast(getOverlay(overlayID)); - } - if (thisOverlay) { - // Send to web overlay - QMetaObject::invokeMethod(thisOverlay.get(), "handlePointerEvent", Q_ARG(PointerEvent, event)); - } - +void Overlays::mouseMovePointerEvent(const QUuid& id, const PointerEvent& event) { auto keyboard = DependencyManager::get(); - // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed - if (!keyboard->getKeysID().contains(overlayID)) { - // emit to scripts - emit mouseMoveOnOverlay(overlayID, event); + if (!keyboard->getKeyIDs().contains(id)) { + emit mouseMoveOnOverlay(id, event); } } QVector Overlays::findOverlays(const glm::vec3& center, float radius) { + PROFILE_RANGE(script_entities, __FUNCTION__); + QVector result; - //if (QThread::currentThread() != thread()) { - // PROFILE_RANGE(script, __FUNCTION__); - // BLOCKING_INVOKE_METHOD(this, "findOverlays", Q_RETURN_ARG(QVector, result), Q_ARG(glm::vec3, center), Q_ARG(float, radius)); - // return result; - //} - - QMutexLocker locker(&_mutex); - QMapIterator i(_overlaysWorld); - int checked = 0; - while (i.hasNext()) { - checked++; - i.next(); - OverlayID thisID = i.key(); - auto overlay = std::dynamic_pointer_cast(i.value()); - - if (overlay && overlay->getVisible() && overlay->isLoaded()) { - // get AABox in frame of overlay - glm::vec3 dimensions = overlay->getDimensions(); - glm::vec3 low = dimensions * -0.5f; - AABox overlayFrameBox(low, dimensions); - - Transform overlayToWorldMatrix = overlay->getTransform(); - overlayToWorldMatrix.setScale(1.0f); // ignore inherited scale factor from parents - glm::mat4 worldToOverlayMatrix = glm::inverse(overlayToWorldMatrix.getMatrix()); - glm::vec3 overlayFrameSearchPosition = glm::vec3(worldToOverlayMatrix * glm::vec4(center, 1.0f)); - glm::vec3 penetration; - if (overlayFrameBox.findSpherePenetration(overlayFrameSearchPosition, radius, penetration)) { - result.append(thisID); - } - } + auto entityTree = DependencyManager::get()->getEntityTree(); + if (entityTree) { + unsigned int searchFilter = PickFilter::getBitMask(PickFilter::FlagBit::LOCAL_ENTITIES); + // For legacy reasons, this only finds visible objects + searchFilter = searchFilter | PickFilter::getBitMask(PickFilter::FlagBit::VISIBLE); + entityTree->withReadLock([&] { + entityTree->evalEntitiesInSphere(center, radius, PickFilter(searchFilter), result); + }); } - return result; } + +/**jsdoc + *

An overlay may be one of the following types:

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Value2D/3DDescription
image2DAn image. Synonym: billboard.
rectangle2DA rectangle.
text2DText.
cube3DA cube. Can also use a shape overlay to create a cube.
sphere3DA sphere. Can also use a shape overlay to create a sphere.
rectangle3d3DA rectangle.
shape3DA geometric shape, such as a cube, sphere, or cylinder.
model3DA model.
text3d3DText.
image3d3DAn image.
web3d3DWeb content.
line3d3DA line.
grid3DA grid of lines in a plane.
circle3d3DA circle.
+ *

2D overlays are rendered on the display surface in desktop mode and on the HUD surface in HMD mode. 3D overlays are + * rendered at a position and orientation in-world, but are deprecated (use local entities instead).

+ *

Each overlay type has different {@link Overlays.OverlayProperties|OverlayProperties}.

+ * @typedef {string} Overlays.OverlayType + */ + +/**jsdoc + * Different overlay types have different properties: some common to all overlays (listed below) and some specific to each + * {@link Overlays.OverlayType|OverlayType} (linked to below). The properties are accessed as an object of property names and + * values. 3D Overlays are local entities, internally, so they also support the corresponding entity properties. + * + * @typedef {object} Overlays.OverlayProperties + * @property {Uuid} id - The ID of the overlay. Read-only. + * @property {Overlays.OverlayType} type - The overlay type. Read-only. + * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. + * + * @see The different entity types have additional properties as follows: + * @see {@link Overlays.OverlayProperties-Image|OverlayProperties-Image} + * @see {@link Overlays.OverlayProperties-Text|OverlayProperties-Text} + * @see {@link Overlays.OverlayProperties-Rectangle|OverlayProperties-Rectangle} + * @see {@link Overlays.OverlayProperties-Cube|OverlayProperties-Cube} + * @see {@link Overlays.OverlayProperties-Sphere|OverlayProperties-Sphere} + * @see {@link Overlays.OverlayProperties-Rectangle3D|OverlayProperties-Rectangle3D} + * @see {@link Overlays.OverlayProperties-Shape|OverlayProperties-Shape} + * @see {@link Overlays.OverlayProperties-Model|OverlayProperties-Model} + * @see {@link Overlays.OverlayProperties-Text3D|OverlayProperties-Text3D} + * @see {@link Overlays.OverlayProperties-Image3D|OverlayProperties-Image3D} + * @see {@link Overlays.OverlayProperties-Web|OverlayProperties-Web} + * @see {@link Overlays.OverlayProperties-Line|OverlayProperties-Line} + * @see {@link Overlays.OverlayProperties-Grid|OverlayProperties-Grid} + * @see {@link Overlays.OverlayProperties-Circle|OverlayProperties-Circle} + */ + +/**jsdoc + * The "Image" {@link Overlays.OverlayType|OverlayType} is a 2D image. + * @typedef {object} Overlays.OverlayProperties-Image + * @property {Rect} bounds - The position and size of the image display area, in pixels. Write-only. + * @property {number} x - Integer left, x-coordinate value of the image display area = bounds.x. + * Write-only. + * @property {number} y - Integer top, y-coordinate value of the image display area = bounds.y. + * Write-only. + * @property {number} width - Integer width of the image display area = bounds.width. Write-only. + * @property {number} height - Integer height of the image display area = bounds.height. Write-only. + * @property {string} imageURL - The URL of the image file to display. The image is scaled to fit to the bounds. + * Write-only. + * @property {Vec2} subImage=0,0 - Integer coordinates of the top left pixel to start using image content from. + * Write-only. + * @property {Color} color=0,0,0 - The color to apply over the top of the image to colorize it. Write-only. + * @property {number} alpha=0.0 - The opacity of the color applied over the top of the image, 0.0 - + * 1.0. Write-only. + */ + +/**jsdoc + * The "Text" {@link Overlays.OverlayType|OverlayType} is for 2D text. + * @typedef {object} Overlays.OverlayProperties-Text + * @property {Rect} bounds - The position and size of the rectangle, in pixels. Write-only. + * @property {number} x - Integer left, x-coordinate value = bounds.x. Write-only. + * @property {number} y - Integer top, y-coordinate value = bounds.y. Write-only. + * @property {number} width - Integer width of the rectangle = bounds.width. Write-only. + * @property {number} height - Integer height of the rectangle = bounds.height. Write-only. + * + * @property {number} margin=0 - Sets the leftMargin and topMargin values, in pixels. + * Write-only. + * @property {number} leftMargin=0 - The left margin's size, in pixels. This value is also used for the right margin. + * Write-only. + * @property {number} topMargin=0 - The top margin's size, in pixels. This value is also used for the bottom margin. + * Write-only. + * @property {string} text="" - The text to display. Text does not automatically wrap; use \n for a line break. Text + * is clipped to the bounds. Write-only. + * @property {number} font.size=18 - The size of the text, in pixels. Write-only. + * @property {number} lineHeight=18 - The height of a line of text, in pixels. Write-only. + * @property {Color} color=255,255,255 - The color of the text. Synonym: textColor. Write-only. + * @property {number} alpha=1.0 - The opacity of the overlay, 0.0 - 1.0. Write-only. + * @property {Color} backgroundColor=0,0,0 - The color of the background rectangle. Write-only. + * @property {number} backgroundAlpha=0.7 - The opacity of the background rectangle. Write-only. + */ + +/**jsdoc + * The "Rectangle" {@link Overlays.OverlayType|OverlayType} is for 2D rectangles. + * @typedef {object} Overlays.OverlayProperties-Rectangle + * @property {Rect} bounds - The position and size of the rectangle, in pixels. Write-only. + * @property {number} x - Integer left, x-coordinate value = bounds.x. Write-only. + * @property {number} y - Integer top, y-coordinate value = bounds.y. Write-only. + * @property {number} width - Integer width of the rectangle = bounds.width. Write-only. + * @property {number} height - Integer height of the rectangle = bounds.height. Write-only. + * + * @property {Color} color=0,0,0 - The color of the overlay. Write-only. + * @property {number} alpha=1.0 - The opacity of the overlay, 0.0 - 1.0. Write-only. + * @property {number} borderWidth=1 - Integer width of the border, in pixels. The border is drawn within the rectangle's bounds. + * It is not drawn unless either borderColor or borderAlpha are specified. Write-only. + * @property {number} radius=0 - Integer corner radius, in pixels. Write-only. + * @property {Color} borderColor=0,0,0 - The color of the border. Write-only. + * @property {number} borderAlpha=1.0 - The opacity of the border, 0.0 - 1.0. + * Write-only. + */ + +/**jsdoc + * The "Cube" {@link Overlays.OverlayType|OverlayType} is for 3D cubes. + * @typedef {object} Overlays.OverlayProperties-Cube + * @property {string} name - The name of the overlay. + * @property {Color} color=255,255,255 - The color of the overlay. + * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. + * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. + * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. + * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from + * pulseMin to pulseMax, then pulseMax to pulseMin in one period. + * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) + * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) + * + * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and + * start. + * @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: scale, size. + * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. + * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as position. + * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as rotation. Synonym: localOrientation. + * @property {boolean} isSolid=false - Synonyms: solid, isFilled, and filled. + * Antonyms: isWire and wire. + * @property {boolean} ignorePickIntersection=false - If true, picks ignore the overlay. ignoreRayIntersection is a synonym. + * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of objects in the world, but behind the HUD. + * @property {boolean} drawHUDLayer=false - If true, the overlay is rendered in front of everything, including the HUD. + * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. + * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. + * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if + * parentID is an avatar skeleton. A value of 65535 means "no joint". + * + */ + +/**jsdoc + * The "Sphere" {@link Overlays.OverlayType|OverlayType} is for 3D spheres. + * @typedef {object} Overlays.OverlayProperties-Sphere + * @property {string} name - The name of the overlay. + * @property {Color} color=255,255,255 - The color of the overlay. + * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. + * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. + * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. + * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from + * pulseMin to pulseMax, then pulseMax to pulseMin in one period. + * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) + * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) + * + * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and + * start. + * @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: scale, size. + * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. + * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as position. + * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as rotation. Synonym: localOrientation. + * @property {boolean} isSolid=false - Synonyms: solid, isFilled, and filled. + * Antonyms: isWire and wire. + * @property {boolean} ignorePickIntersection=false - If true, picks ignore the overlay. ignoreRayIntersection is a synonym. + * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of objects in the world, but behind the HUD. + * @property {boolean} drawHUDLayer=false - If true, the overlay is rendered in front of everything, including the HUD. + * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. + * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. + * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if + * parentID is an avatar skeleton. A value of 65535 means "no joint". + * + */ + +/**jsdoc + * The "Rectangle3D" {@link Overlays.OverlayType|OverlayType} is for 3D rectangles. + * @typedef {object} Overlays.OverlayProperties-Rectangle3D + * @property {string} name - The name of the overlay. + * @property {Color} color=255,255,255 - The color of the overlay. + * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. + * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. + * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. + * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from + * pulseMin to pulseMax, then pulseMax to pulseMin in one period. + * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) + * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) + * + * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and + * start. + * @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: scale, size. + * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. + * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as position. + * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as rotation. Synonym: localOrientation. + * @property {boolean} isSolid=false - Synonyms: solid, isFilled, and filled. + * Antonyms: isWire and wire. + * @property {boolean} ignorePickIntersection=false - If true, picks ignore the overlay. ignoreRayIntersection is a synonym. + * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of objects in the world, but behind the HUD. + * @property {boolean} drawHUDLayer=false - If true, the overlay is rendered in front of everything, including the HUD. + * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. + * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. + * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if + * parentID is an avatar skeleton. A value of 65535 means "no joint". + * + */ + +/**jsdoc + *

A shape {@link Overlays.OverlayType|OverlayType} may display as one of the following geometrical shapes:

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
ValueDimensionsDescription
"Circle"2DA circle oriented in 3D.
"Cone"3D
"Cube"3D
"Cylinder"3D
"Dodecahedron"3D
"Hexagon"3DA hexagonal prism.
"Icosahedron"3D
"Line"1DA line oriented in 3D.
"Octagon"3DAn octagonal prism.
"Octahedron"3D
"Quad"2DA square oriented in 3D.
"Sphere"3D
"Tetrahedron"3D
"Torus"3DNot implemented.
"Triangle"3DA triangular prism.
+ * @typedef {string} Overlays.Shape + */ + +/**jsdoc + * The "Shape" {@link Overlays.OverlayType|OverlayType} is for 3D shapes. + * @typedef {object} Overlays.OverlayProperties-Shape + * @property {string} name - The name of the overlay. + * @property {Color} color=255,255,255 - The color of the overlay. + * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. + * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. + * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. + * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from + * pulseMin to pulseMax, then pulseMax to pulseMin in one period. + * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) + * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) + * + * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and + * start. + * @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: scale, size. + * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. + * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as position. + * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as rotation. Synonym: localOrientation. + * @property {boolean} isSolid=false - Synonyms: solid, isFilled, and filled. + * Antonyms: isWire and wire. + * @property {boolean} ignorePickIntersection=false - If true, picks ignore the overlay. ignoreRayIntersection is a synonym. + * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of objects in the world, but behind the HUD. + * @property {boolean} drawHUDLayer=false - If true, the overlay is rendered in front of everything, including the HUD. + * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. + * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. + * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if + * parentID is an avatar skeleton. A value of 65535 means "no joint". + * + * @property {Overlays.Shape} shape=Hexagon - The geometrical shape of the overlay. + */ + +/**jsdoc + * The "Model" {@link Overlays.OverlayType|OverlayType} is for 3D models. + * @typedef {object} Overlays.OverlayProperties-Model + * @property {string} name - The name of the overlay. + * + * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and + * start. + * @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: scale, size. + * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. + * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as position. + * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as rotation. Synonym: localOrientation. + * @property {boolean} ignorePickIntersection=false - If true, picks ignore the overlay. ignoreRayIntersection is a synonym. + * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of objects in the world, but behind the HUD. + * @property {boolean} drawHUDLayer=false - If true, the overlay is rendered in front of everything, including the HUD. + * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. + * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. + * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if + * parentID is an avatar skeleton. A value of 65535 means "no joint". + * + * @property {string} url - The URL of the FBX or OBJ model used for the overlay. + * @property {number} loadPriority=0.0 - The priority for loading and displaying the overlay. Overlays with higher values load + * first. + * @property {object.} textures - Maps the named textures in the model to the JPG or PNG images in the urls. + * @property {string[]} jointNames - The names of the joints - if any - in the model. Read-only. + * @property {Quat[]} jointRotations - The relative rotations of the model's joints. + * @property {Vec3[]} jointTranslations - The relative translations of the model's joints. + * @property {Quat[]} jointOrientations - The absolute orientations of the model's joints, in world coordinates. Read-only. + * @property {Vec3[]} jointPositions - The absolute positions of the model's joints, in world coordinates. Read-only. + * @property {string} animationSettings.url="" - The URL of an FBX file containing an animation to play. + * @property {number} animationSettings.fps=0 - The frame rate (frames/sec) to play the animation at. + * @property {number} animationSettings.firstFrame=0 - The frame to start playing at. + * @property {number} animationSettings.lastFrame=0 - The frame to finish playing at. + * @property {number} animationSettings.currentFrame=0 - The current frame being played. + * @property {boolean} animationSettings.running=false - Whether or not the animation is playing. + * @property {boolean} animationSettings.loop=false - Whether or not the animation should repeat in a loop. + * @property {boolean} animationSettings.hold=false - Whether or not when the animation finishes, the rotations and + * translations of the last frame played should be maintained. + * @property {boolean} animationSettings.allowTranslation=false - Whether or not translations contained in the animation should + * be played. + */ + +/**jsdoc + * The "Text3D" {@link Overlays.OverlayType|OverlayType} is for 3D text. + * @typedef {object} Overlays.OverlayProperties-Text3D + * @property {string} name - The name of the overlay. + * @property {Color} color=255,255,255 - The color of the overlay. + * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. + * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. + * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. + * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from + * pulseMin to pulseMax, then pulseMax to pulseMin in one period. + * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) + * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) + * + * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and + * start. + * @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: scale, size. + * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. + * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as position. + * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as rotation. Synonym: localOrientation. + * @property {boolean} ignorePickIntersection=false - If true, picks ignore the overlay. ignoreRayIntersection is a synonym. + * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of objects in the world, but behind the HUD. + * @property {boolean} drawHUDLayer=false - If true, the overlay is rendered in front of everything, including the HUD. + * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. + * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. + * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if + * parentID is an avatar skeleton. A value of 65535 means "no joint". + * + * @property {boolean} isFacingAvatar - If true< / code>, the overlay is rotated to face the user's camera about an axis + * parallel to the user's avatar's "up" direction. + * @property {string} text="" - The text to display.Text does not automatically wrap; use \n< / code> for a line break. + * @property {number} textAlpha=1 - The text alpha value. + * @property {Color} backgroundColor=0,0,0 - The background color. + * @property {number} backgroundAlpha=0.7 - The background alpha value. + * @property {number} lineHeight=1 - The height of a line of text in meters. + * @property {number} leftMargin=0.1 - The left margin, in meters. + * @property {number} topMargin=0.1 - The top margin, in meters. + * @property {number} rightMargin=0.1 - The right margin, in meters. + * @property {number} bottomMargin=0.1 - The bottom margin, in meters. + */ + +/**jsdoc + * The "Image3D" {@link Overlays.OverlayType|OverlayType} is for 3D images. + * @typedef {object} Overlays.OverlayProperties-Image3D + * @property {string} name - The name of the overlay. + * @property {Color} color=255,255,255 - The color of the overlay. + * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. + * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. + * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. + * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from + * pulseMin to pulseMax, then pulseMax to pulseMin in one period. + * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) + * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) + * + * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and + * start. + * @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: scale, size. + * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. + * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as position. + * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as rotation. Synonym: localOrientation. + * @property {boolean} ignorePickIntersection=false - If true, picks ignore the overlay. ignoreRayIntersection is a synonym. + * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of objects in the world, but behind the HUD. + * @property {boolean} drawHUDLayer=false - If true, the overlay is rendered in front of everything, including the HUD. + * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. + * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. + * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if + * parentID is an avatar skeleton. A value of 65535 means "no joint". + * + * @property {boolean} isFacingAvatar - If true, the overlay is rotated to face the user's camera about an axis + * parallel to the user's avatar's "up" direction. + * @property {string} url - The URL of the PNG or JPG image to display. + * @property {Rect} subImage - The portion of the image to display. Defaults to the full image. + * @property {boolean} emissive - If true, the overlay is displayed at full brightness, otherwise it is rendered + * with scene lighting. + * @property {bool} keepAspectRatio=true - overlays will maintain the aspect ratio when the subImage is applied. + */ + +/**jsdoc + * The "Web" {@link Overlays.OverlayType|OverlayType} is for 3D web surfaces. + * @typedef {object} Overlays.OverlayProperties-Web + * @property {string} name - The name of the overlay. + * @property {Color} color=255,255,255 - The color of the overlay. + * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. + * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. + * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. + * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from + * pulseMin to pulseMax, then pulseMax to pulseMin in one period. + * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) + * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) + * + * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and + * start. + * @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: scale, size. + * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. + * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as position. + * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as rotation. Synonym: localOrientation. + * @property {boolean} ignorePickIntersection=false - If true, picks ignore the overlay. ignoreRayIntersection is a synonym. + * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of objects in the world, but behind the HUD. + * @property {boolean} drawHUDLayer=false - If true, the overlay is rendered in front of everything, including the HUD. + * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. + * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. + * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if + * parentID is an avatar skeleton. A value of 65535 means "no joint". + * + * @property {boolean} isFacingAvatar - If true, the overlay is rotated to face the user's camera about an axis + * parallel to the user's avatar's "up" direction. + * @property {string} url - The URL of the Web page to display. + * @property {string} scriptURL="" - The URL of a JavaScript file to inject into the Web page. + * @property {number} dpi=30 - The dots per inch to display the Web page at, on the overlay. + * @property {number} maxFPS=10 - The maximum update rate for the Web overlay content, in frames/second. + * @property {string} inputMode=Touch - The user input mode to use - either "Touch" or "Mouse". + */ + +/**jsdoc + * The "Line" {@link Overlays.OverlayType|OverlayType} is for 3D lines. + * @typedef {object} Overlays.OverlayProperties-Line + * @property {string} name - The name of the overlay. + * @property {Color} color=255,255,255 - The color of the overlay. + * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. + * + * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and + * start. + * @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: scale, size. + * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. + * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as position. + * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as rotation. Synonym: localOrientation. + * @property {boolean} ignorePickIntersection=false - If true, picks ignore the overlay. ignoreRayIntersection is a synonym. + * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of objects in the world, but behind the HUD. + * @property {boolean} drawHUDLayer=false - If true, the overlay is rendered in front of everything, including the HUD. + * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. + * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. + * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if + * parentID is an avatar skeleton. A value of 65535 means "no joint". + * + * @property {Uuid} endParentID=null - The avatar, entity, or overlay that the end point of the line is parented to. + * @property {number} endParentJointIndex=65535 - Integer value specifying the skeleton joint that the end point of the line is + * attached to if parentID is an avatar skeleton. A value of 65535 means "no joint". + * @property {Vec3} start - The start point of the line. Synonyms: startPoint and p1. + * @property {Vec3} end - The end point of the line. Synonyms: endPoint and p2. + * @property {Vec3} localStart - The local position of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as start. Synonym: localPosition. + * @property {Vec3} localEnd - The local position of the overlay relative to its parent if the overlay has a + * endParentID set, otherwise the same value as end. + * @property {number} length - The length of the line, in meters. This can be set after creating a line with start and end + * points. + * @property {number} glow=0 - If glow > 0, the line is rendered with a glow. + * @property {number} lineWidth=0.02 - Width of the line, in meters. + */ + +/**jsdoc + * The "Grid" {@link Overlays.OverlayType|OverlayType} is for 3D grid. + * @typedef {object} Overlays.OverlayProperties-Grid + * @property {string} name - The name of the overlay. + * @property {Color} color=255,255,255 - The color of the overlay. + * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. + * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. + * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. + * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from + * pulseMin to pulseMax, then pulseMax to pulseMin in one period. + * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) + * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) + * + * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and + * start. + * @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: scale, size. + * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. + * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as position. + * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as rotation. Synonym: localOrientation. + * @property {boolean} ignorePickIntersection=false - If true, picks ignore the overlay. ignoreRayIntersection is a synonym. + * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of objects in the world, but behind the HUD. + * @property {boolean} drawHUDLayer=false - If true, the overlay is rendered in front of everything, including the HUD. + * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. + * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. + * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if + * parentID is an avatar skeleton. A value of 65535 means "no joint". + * + * @property {boolean} followCamera=true - If true, the grid is always visible even as the camera moves to another position. + * @property {number} majorGridEvery=5 - Integer number of minorGridEvery intervals at which to draw a thick grid line. Minimum value = 1. + * @property {number} minorGridEvery=1 - Real number of meters at which to draw thin grid lines. Minimum value = 0.001. + */ + +/**jsdoc + * The "Circle" {@link Overlays.OverlayType|OverlayType} is for 3D circle. + * @typedef {object} Overlays.OverlayProperties-Circle + * @property {string} name - The name of the overlay. + * @property {Color} color=255,255,255 - The color of the overlay. + * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. + * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. + * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. + * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from + * pulseMin to pulseMax, then pulseMax to pulseMin in one period. + * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) + * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) + * + * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and + * start. + * @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: scale, size. + * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. + * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as position. + * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as rotation. Synonym: localOrientation. + * @property {boolean} isSolid=false - Synonyms: solid, isFilled, and filled. + * Antonyms: isWire and wire. + * @property {boolean} ignorePickIntersection=false - If true, picks ignore the overlay. ignoreRayIntersection is a synonym. + * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of objects in the world, but behind the HUD. + * @property {boolean} drawHUDLayer=false - If true, the overlay is rendered in front of everything, including the HUD. + * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. + * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. + * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if + * parentID is an avatar skeleton. A value of 65535 means "no joint". + * + * @property {number} startAt = 0 - The counter - clockwise angle from the overlay's x-axis that drawing starts at, in degrees. + * @property {number} endAt = 360 - The counter - clockwise angle from the overlay's x-axis that drawing ends at, in degrees. + * @property {number} outerRadius = 1 - The outer radius of the overlay, in meters.Synonym: radius< / code>. + * @property {number} innerRadius = 0 - The inner radius of the overlay, in meters. + * @property {Color} color = 255, 255, 255 - The color of the overlay.Setting this value also sets the values of + * innerStartColor< / code>, innerEndColor< / code>, outerStartColor< / code>, and outerEndColor< / code>. + * @property {Color} startColor - Sets the values of innerStartColor< / code> and outerStartColor< / code>. + * Write - only.< / em> + * @property {Color} endColor - Sets the values of innerEndColor< / code> and outerEndColor< / code>. + * Write - only.< / em> + * @property {Color} innerColor - Sets the values of innerStartColor< / code> and innerEndColor< / code>. + * Write - only.< / em> + * @property {Color} outerColor - Sets the values of outerStartColor< / code> and outerEndColor< / code>. + * Write - only.< / em> + * @property {Color} innerStartcolor - The color at the inner start point of the overlay. + * @property {Color} innerEndColor - The color at the inner end point of the overlay. + * @property {Color} outerStartColor - The color at the outer start point of the overlay. + * @property {Color} outerEndColor - The color at the outer end point of the overlay. + * @property {number} alpha = 0.5 - The opacity of the overlay, 0.0< / code> -1.0< / code>.Setting this value also sets + * the values of innerStartAlpha< / code>, innerEndAlpha< / code>, outerStartAlpha< / code>, and + * outerEndAlpha< / code>.Synonym: Alpha< / code>; write - only< / em>. + * @property {number} startAlpha - Sets the values of innerStartAlpha< / code> and outerStartAlpha< / code>. + * Write - only.< / em> + * @property {number} endAlpha - Sets the values of innerEndAlpha< / code> and outerEndAlpha< / code>. + * Write - only.< / em> + * @property {number} innerAlpha - Sets the values of innerStartAlpha< / code> and innerEndAlpha< / code>. + * Write - only.< / em> + * @property {number} outerAlpha - Sets the values of outerStartAlpha< / code> and outerEndAlpha< / code>. + * Write - only.< / em> + * @property {number} innerStartAlpha = 0 - The alpha at the inner start point of the overlay. + * @property {number} innerEndAlpha = 0 - The alpha at the inner end point of the overlay. + * @property {number} outerStartAlpha = 0 - The alpha at the outer start point of the overlay. + * @property {number} outerEndAlpha = 0 - The alpha at the outer end point of the overlay. + * + * @property {boolean} hasTickMarks = false - If true< / code>, tick marks are drawn. + * @property {number} majorTickMarksAngle = 0 - The angle between major tick marks, in degrees. + * @property {number} minorTickMarksAngle = 0 - The angle between minor tick marks, in degrees. + * @property {number} majorTickMarksLength = 0 - The length of the major tick marks, in meters.A positive value draws tick marks + * outwards from the inner radius; a negative value draws tick marks inwards from the outer radius. + * @property {number} minorTickMarksLength = 0 - The length of the minor tick marks, in meters.A positive value draws tick marks + * outwards from the inner radius; a negative value draws tick marks inwards from the outer radius. + * @property {Color} majorTickMarksColor = 0, 0, 0 - The color of the major tick marks. + * @property {Color} minorTickMarksColor = 0, 0, 0 - The color of the minor tick marks. + */ diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 14e30b3b22..7612779099 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -26,29 +26,16 @@ #include "Overlay.h" -#include "PanelAttachable.h" +#include class PickRay; -class OverlayPropertyResult { -public: - OverlayPropertyResult(); - QVariant value; -}; - -Q_DECLARE_METATYPE(OverlayPropertyResult); - -QScriptValue OverlayPropertyResultToScriptValue(QScriptEngine* engine, const OverlayPropertyResult& value); -void OverlayPropertyResultFromScriptValue(const QScriptValue& object, OverlayPropertyResult& value); - -const OverlayID UNKNOWN_OVERLAY_ID = OverlayID(); - /**jsdoc * The result of a {@link PickRay} search using {@link Overlays.findRayIntersection|findRayIntersection}. * @typedef {object} Overlays.RayToOverlayIntersectionResult * @property {boolean} intersects - true if the {@link PickRay} intersected with a 3D overlay, otherwise * false. - * @property {Uuid} overlayID - The UUID of the overlay that was intersected. + * @property {Uuid} overlayID - The UUID of the local entity that was intersected. * @property {number} distance - The distance from the {@link PickRay} origin to the intersection point. * @property {Vec3} surfaceNormal - The normal of the overlay surface at the intersection point. * @property {Vec3} intersection - The position of the intersection point. @@ -57,7 +44,7 @@ const OverlayID UNKNOWN_OVERLAY_ID = OverlayID(); class RayToOverlayIntersectionResult { public: bool intersects { false }; - OverlayID overlayID { UNKNOWN_OVERLAY_ID }; + QUuid overlayID; float distance { 0.0f }; BoxFace face { UNKNOWN_FACE }; glm::vec3 surfaceNormal; @@ -71,7 +58,7 @@ void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, R class ParabolaToOverlayIntersectionResult { public: bool intersects { false }; - OverlayID overlayID { UNKNOWN_OVERLAY_ID }; + QUuid overlayID; float distance { 0.0f }; float parabolicDistance { 0.0f }; BoxFace face { UNKNOWN_FACE }; @@ -81,7 +68,7 @@ public: }; /**jsdoc - * The Overlays API provides facilities to create and interact with overlays. Overlays are 2D and 3D objects visible only to + * (Note: 3D Overlays are deprecated. Use local entities instead.) The Overlays API provides facilities to create and interact with overlays. Overlays are 2D and 3D objects visible only to * yourself and that aren't persisted to the domain. They are used for UI. * @namespace Overlays * @@ -89,48 +76,51 @@ public: * @hifi-client-entity * @hifi-avatar * - * @property {Uuid} keyboardFocusOverlay - Get or set the {@link Overlays.OverlayType|web3d} overlay that has keyboard focus. - * If no overlay has keyboard focus, get returns null; set to null or {@link Uuid|Uuid.NULL} to + * @property {Uuid} keyboardFocusOverlay - Get or set the {@link Entities.EntityTypes|Web} entity that has keyboard focus. + * If no entity has keyboard focus, get returns null; set to null or {@link Uuid|Uuid.NULL} to * clear keyboard focus. */ class Overlays : public QObject { Q_OBJECT - Q_PROPERTY(OverlayID keyboardFocusOverlay READ getKeyboardFocusOverlay WRITE setKeyboardFocusOverlay) + Q_PROPERTY(QUuid keyboardFocusOverlay READ getKeyboardFocusOverlay WRITE setKeyboardFocusOverlay) public: Overlays(); void init(); void update(float deltatime); - void renderHUD(RenderArgs* renderArgs); + void render(RenderArgs* renderArgs); void disable(); void enable(); - Overlay::Pointer getOverlay(OverlayID id) const; + Overlay::Pointer take2DOverlay(const QUuid& id); + Overlay::Pointer get2DOverlay(const QUuid& id) const; /// adds an overlay that's already been created - OverlayID addOverlay(Overlay* overlay) { return addOverlay(Overlay::Pointer(overlay)); } - OverlayID addOverlay(const Overlay::Pointer& overlay); + QUuid addOverlay(Overlay* overlay) { return add2DOverlay(Overlay::Pointer(overlay)); } + QUuid add2DOverlay(const Overlay::Pointer& overlay); RayToOverlayIntersectionResult findRayIntersectionVector(const PickRay& ray, bool precisionPicking, - const QVector& overlaysToInclude, - const QVector& overlaysToDiscard, + const QVector& include, + const QVector& discard, bool visibleOnly = false, bool collidableOnly = false); ParabolaToOverlayIntersectionResult findParabolaIntersectionVector(const PickParabola& parabola, bool precisionPicking, - const QVector& overlaysToInclude, - const QVector& overlaysToDiscard, + const QVector& include, + const QVector& discard, bool visibleOnly = false, bool collidableOnly = false); - bool mousePressEvent(QMouseEvent* event); + std::pair mousePressEvent(QMouseEvent* event); bool mouseDoublePressEvent(QMouseEvent* event); bool mouseReleaseEvent(QMouseEvent* event); bool mouseMoveEvent(QMouseEvent* event); void cleanupAllOverlays(); + mutable QScriptEngine _scriptEngine; + public slots: /**jsdoc * Add an overlay to the scene. @@ -146,34 +136,20 @@ public slots: * solid: true * }); */ - OverlayID addOverlay(const QString& type, const QVariant& properties); + QUuid addOverlay(const QString& type, const QVariant& properties); /**jsdoc - * Create a clone of an existing overlay. + * Create a clone of an existing entity (or 2D overlay). * @function Overlays.cloneOverlay - * @param {Uuid} overlayID - The ID of the overlay to clone. - * @returns {Uuid} The ID of the new overlay if successful, otherwise {@link Uuid|Uuid.NULL}. - * @example Add an overlay in front of your avatar, clone it, and move the clone to be above the - * original. - * var position = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })); - * var original = Overlays.addOverlay("cube", { - * position: position, - * rotation: MyAvatar.orientation, - * dimensions: { x: 0.3, y: 0.3, z: 0.3 }, - * solid: true - * }); - * - * var clone = Overlays.cloneOverlay(original); - * Overlays.editOverlay(clone, { - * position: Vec3.sum({ x: 0, y: 0.5, z: 0}, position) - * }); + * @param {Uuid} id - The ID of the entity/2D overlay to clone. + * @returns {Uuid} The ID of the new object if successful, otherwise {@link Uuid|Uuid.NULL}. */ - OverlayID cloneOverlay(OverlayID id); + QUuid cloneOverlay(const QUuid& id); /**jsdoc * Edit an overlay's properties. * @function Overlays.editOverlay - * @param {Uuid} overlayID - The ID of the overlay to edit. + * @param {Uuid} id - The ID of the overlay to edit. * @param {Overlays.OverlayProperties} properties - The properties changes to make. * @returns {boolean} true if the overlay was found and edited, otherwise false. * @example Add an overlay in front of your avatar then change its color. @@ -189,7 +165,7 @@ public slots: * }); * print("Success: " + success); */ - bool editOverlay(OverlayID id, const QVariant& properties); + bool editOverlay(const QUuid& id, const QVariant& properties); /**jsdoc * Edit multiple overlays' properties. @@ -221,27 +197,18 @@ public slots: bool editOverlays(const QVariant& propertiesById); /**jsdoc - * Delete an overlay. + * Delete an entity or 2D overlay. * @function Overlays.deleteOverlay - * @param {Uuid} overlayID - The ID of the overlay to delete. - * @example Create an overlay in front of your avatar then delete it. - * var overlay = Overlays.addOverlay("cube", { - * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })), - * rotation: MyAvatar.orientation, - * dimensions: { x: 0.3, y: 0.3, z: 0.3 }, - * solid: true - * }); - * print("Overlay: " + overlay); - * Overlays.deleteOverlay(overlay); + * @param {Uuid} id - The ID of the object to delete. */ - void deleteOverlay(OverlayID id); + void deleteOverlay(const QUuid& id); /**jsdoc - * Get the type of an overlay. + * Get the type of an entity or 2D overlay. * @function Overlays.getOverlayType - * @param {Uuid} overlayID - The ID of the overlay to get the type of. - * @returns {Overlays.OverlayType} The type of the overlay if found, otherwise an empty string. - * @example Create an overlay in front of your avatar then get and report its type. + * @param {Uuid} id - The ID of the object to get the type of. + * @returns {string} The type of the object if found, otherwise an empty string. + * @example Create an object in front of your avatar then get and report its type. * var overlay = Overlays.addOverlay("cube", { * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })), * rotation: MyAvatar.orientation, @@ -251,7 +218,7 @@ public slots: * var type = Overlays.getOverlayType(overlay); * print("Type: " + type); */ - QString getOverlayType(OverlayID overlayId); + QString getOverlayType(const QUuid& id); /**jsdoc * Get the overlay script object. In particular, this is useful for accessing the event bridge for a web3d @@ -295,7 +262,7 @@ public slots: * Overlays.deleteOverlay(web3dOverlay); * }); */ - QObject* getOverlayObject(OverlayID id); + QObject* getOverlayObject(const QUuid& id); /**jsdoc * Get the ID of the 2D overlay at a particular point on the screen or HUD. @@ -314,12 +281,12 @@ public slots: * print("Clicked: " + overlay); * }); */ - OverlayID getOverlayAtPoint(const glm::vec2& point); + QUuid getOverlayAtPoint(const glm::vec2& point); /**jsdoc * Get the value of a 3D overlay's property. * @function Overlays.getProperty - * @param {Uuid} overlayID - The ID of the overlay. Must be for a 3D {@link Overlays.OverlayType|OverlayType}. + * @param {Uuid} id - The ID of the overlay. Must be for a 3D {@link Overlays.OverlayType|OverlayType}. * @param {string} property - The name of the property value to get. * @returns {object} The value of the property if the 3D overlay and property can be found, otherwise * undefined. @@ -333,12 +300,12 @@ public slots: * var alpha = Overlays.getProperty(overlay, "alpha"); * print("Overlay alpha: " + alpha); */ - OverlayPropertyResult getProperty(OverlayID id, const QString& property); + QVariant getProperty(const QUuid& id, const QString& property); /**jsdoc * Get the values of an overlay's properties. * @function Overlays.getProperties - * @param {Uuid} overlayID - The ID of the overlay. + * @param {Uuid} id - The ID of the overlay. * @param {Array.} properties - An array of names of properties to get the values of. * @returns {Overlays.OverlayProperties} The values of valid properties if the overlay can be found, otherwise * undefined. @@ -352,7 +319,7 @@ public slots: * var properties = Overlays.getProperties(overlay, ["color", "alpha", "grabbable"]); * print("Overlay properties: " + JSON.stringify(properties)); */ - OverlayPropertyResult getProperties(const OverlayID& id, const QStringList& properties); + QVariantMap getProperties(const QUuid& id, const QStringList& properties); /**jsdoc * Get the values of multiple overlays' properties. @@ -380,7 +347,7 @@ public slots: * var properties = Overlays.getOverlaysProperties(propertiesToGet); * print("Overlays properties: " + JSON.stringify(properties)); */ - OverlayPropertyResult getOverlaysProperties(const QVariant& overlaysProperties); + QVariantMap getOverlaysProperties(const QVariant& overlaysProperties); /**jsdoc * Find the closest 3D overlay intersected by a {@link PickRay}. Overlays with their drawInFront property set @@ -391,10 +358,12 @@ public slots: * @function Overlays.findRayIntersection * @param {PickRay} pickRay - The PickRay to use for finding overlays. * @param {boolean} [precisionPicking=false] - Unused; exists to match Entity API. - * @param {Array.} [overlayIDsToInclude=[]] - If not empty then the search is restricted to these overlays. - * @param {Array.} [overlayIDsToExclude=[]] - Overlays to ignore during the search. - * @param {boolean} [visibleOnly=false] - Unused; exists to match Entity API. - * @param {boolean} [collidableOnly=false] - Unused; exists to match Entity API. + * @param {Array.} [include=[]] - If not empty then the search is restricted to these overlays. + * @param {Array.} [discard=[]] - Overlays to ignore during the search. + * @param {boolean} [visibleOnly=false] - If true then only entities that are + * {@link Entities.EntityProperties|visible} are searched. + * @param {boolean} [collideableOnly=false] - If true then only entities that are not + * {@link Entities.EntityProperties|collisionless} are searched. * @returns {Overlays.RayToOverlayIntersectionResult} The closest 3D overlay intersected by pickRay, taking * into account overlayIDsToInclude and overlayIDsToExclude if they're not empty. * @example Create a cube overlay in front of your avatar. Report 3D overlay intersection details for mouse @@ -414,18 +383,18 @@ public slots: */ RayToOverlayIntersectionResult findRayIntersection(const PickRay& ray, bool precisionPicking = false, - const QScriptValue& overlayIDsToInclude = QScriptValue(), - const QScriptValue& overlayIDsToDiscard = QScriptValue(), + const QScriptValue& include = QScriptValue(), + const QScriptValue& discard = QScriptValue(), bool visibleOnly = false, bool collidableOnly = false); /**jsdoc - * Return a list of 3D overlays with bounding boxes that touch a search sphere. + * Return a list of local entities with bounding boxes that touch a search sphere. * @function Overlays.findOverlays * @param {Vec3} center - The center of the search sphere. * @param {number} radius - The radius of the search sphere. - * @returns {Uuid[]} An array of overlay IDs with bounding boxes that touch a search sphere. - * @example Create two cube overlays in front of your avatar then search for overlays near your avatar. + * @returns {Uuid[]} An array of entity IDs with bounding boxes that touch a search sphere. + * @example Create two cube entities in front of your avatar then search for entities near your avatar. * var overlayA = Overlays.addOverlay("cube", { * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: -0.3, y: 0, z: -3 })), * rotation: MyAvatar.orientation, @@ -450,7 +419,7 @@ public slots: * Check whether an overlay's assets have been loaded. For example, for an image overlay the result indicates * whether its image has been loaded. * @function Overlays.isLoaded - * @param {Uuid} overlayID - The ID of the overlay to check. + * @param {Uuid} id - The ID of the overlay to check. * @returns {boolean} true if the overlay's assets have been loaded, otherwise false. * @example Create an image overlay and report whether its image is loaded after 1s. * var overlay = Overlays.addOverlay("image", { @@ -462,17 +431,17 @@ public slots: * print("Image loaded: " + isLoaded); * }, 1000); */ - bool isLoaded(OverlayID id); + bool isLoaded(const QUuid& id); /**jsdoc - * Calculates the size of the given text in the specified overlay if it is a text overlay. + * Calculates the size of the given text in the specified object if it is a text entity or overlay. * @function Overlays.textSize - * @param {Uuid} overlayID - The ID of the overlay to use for calculation. + * @param {Uuid} id - The ID of the object to use for calculation. * @param {string} text - The string to calculate the size of. - * @returns {Size} The size of the text if the overlay is a text overlay, otherwise - * { height: 0, width : 0 }. If the overlay is a 2D overlay, the size is in pixels; if the overlay is a 3D - * overlay, the size is in meters. - * @example Calculate the size of "hello" in a 3D text overlay. + * @returns {Size} The size of the text if the object is a text entity or overlay, otherwise + * { height: 0, width : 0 }. If the object is a 2D overlay, the size is in pixels; if the object is an entity, + * the size is in meters. + * @example Calculate the size of "hello" in a 3D text entity. * var overlay = Overlays.addOverlay("text3d", { * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -2 })), * rotation: MyAvatar.orientation, @@ -482,7 +451,7 @@ public slots: * var textSize = Overlays.textSize(overlay, "hello"); * print("Size of \"hello\": " + JSON.stringify(textSize)); */ - QSizeF textSize(OverlayID id, const QString& text); + QSizeF textSize(const QUuid& id, const QString& text); /**jsdoc * Get the width of the window or HUD. @@ -499,17 +468,17 @@ public slots: float height(); /**jsdoc - * Check if there is an overlay of a given ID. + * Check if there is an object of a given ID. * @function Overlays.isAddedOverlay - * @param {Uuid} overlayID - The ID to check. - * @returns {boolean} true if an overlay with the given ID exists, false otherwise. + * @param {Uuid} id - The ID to check. + * @returns {boolean} true if an object with the given ID exists, false otherwise. */ - bool isAddedOverlay(OverlayID id); + bool isAddedOverlay(const QUuid& id); /**jsdoc * Generate a mouse press event on an overlay. * @function Overlays.sendMousePressOnOverlay - * @param {Uuid} overlayID - The ID of the overlay to generate a mouse press event on. + * @param {Uuid} id - The ID of the overlay to generate a mouse press event on. * @param {PointerEvent} event - The mouse press event details. * @example Create a 2D rectangle overlay plus a 3D cube overlay and generate mousePressOnOverlay events for the 2D * overlay. @@ -544,23 +513,23 @@ public slots: * } * }); */ - void sendMousePressOnOverlay(const OverlayID& overlayID, const PointerEvent& event); + void sendMousePressOnOverlay(const QUuid& id, const PointerEvent& event); /**jsdoc * Generate a mouse release event on an overlay. * @function Overlays.sendMouseReleaseOnOverlay - * @param {Uuid} overlayID - The ID of the overlay to generate a mouse release event on. + * @param {Uuid} id - The ID of the overlay to generate a mouse release event on. * @param {PointerEvent} event - The mouse release event details. */ - void sendMouseReleaseOnOverlay(const OverlayID& overlayID, const PointerEvent& event); + void sendMouseReleaseOnOverlay(const QUuid& id, const PointerEvent& event); /**jsdoc * Generate a mouse move event on an overlay. * @function Overlays.sendMouseMoveOnOverlay - * @param {Uuid} overlayID - The ID of the overlay to generate a mouse move event on. + * @param {Uuid} id - The ID of the overlay to generate a mouse move event on. * @param {PointerEvent} event - The mouse move event details. */ - void sendMouseMoveOnOverlay(const OverlayID& overlayID, const PointerEvent& event); + void sendMouseMoveOnOverlay(const QUuid& id, const PointerEvent& event); /**jsdoc * Generate a hover enter event on an overlay. @@ -568,45 +537,45 @@ public slots: * @param {Uuid} id - The ID of the overlay to generate a hover enter event on. * @param {PointerEvent} event - The hover enter event details. */ - void sendHoverEnterOverlay(const OverlayID& overlayID, const PointerEvent& event); + void sendHoverEnterOverlay(const QUuid& id, const PointerEvent& event); /**jsdoc * Generate a hover over event on an overlay. * @function Overlays.sendHoverOverOverlay - * @param {Uuid} overlayID - The ID of the overlay to generate a hover over event on. + * @param {Uuid} id - The ID of the overlay to generate a hover over event on. * @param {PointerEvent} event - The hover over event details. */ - void sendHoverOverOverlay(const OverlayID& overlayID, const PointerEvent& event); + void sendHoverOverOverlay(const QUuid& id, const PointerEvent& event); /**jsdoc * Generate a hover leave event on an overlay. * @function Overlays.sendHoverLeaveOverlay - * @param {Uuid} overlayID - The ID of the overlay to generate a hover leave event on. + * @param {Uuid} id - The ID of the overlay to generate a hover leave event on. * @param {PointerEvent} event - The hover leave event details. */ - void sendHoverLeaveOverlay(const OverlayID& overlayID, const PointerEvent& event); + void sendHoverLeaveOverlay(const QUuid& id, const PointerEvent& event); /**jsdoc - * Get the ID of the Web3D overlay that has keyboard focus. + * Get the ID of the Web3D entity that has keyboard focus. * @function Overlays.getKeyboardFocusOverlay - * @returns {Uuid} The ID of the {@link Overlays.OverlayType|web3d} overlay that has focus, if any, otherwise + * @returns {Uuid} The ID of the {@link Entities.EntityTypes|Web} overlay that has focus, if any, otherwise * null. */ - OverlayID getKeyboardFocusOverlay(); + QUuid getKeyboardFocusOverlay() { return DependencyManager::get()->getKeyboardFocusEntity(); } /**jsdoc - * Set the Web3D overlay that has keyboard focus. + * Set the Web3D entity that has keyboard focus. * @function Overlays.setKeyboardFocusOverlay - * @param {Uuid} overlayID - The ID of the {@link Overlays.OverlayType|web3d} overlay to set keyboard focus to. Use + * @param {Uuid} id - The ID of the {@link Entities.EntityTypes|Web} entity to set keyboard focus to. Use * null or {@link Uuid|Uuid.NULL} to unset keyboard focus from an overlay. */ - void setKeyboardFocusOverlay(const OverlayID& id); + void setKeyboardFocusOverlay(const QUuid& id) { DependencyManager::get()->setKeyboardFocusEntity(id); } signals: /**jsdoc * Triggered when an overlay is deleted. * @function Overlays.overlayDeleted - * @param {Uuid} overlayID - The ID of the overlay that was deleted. + * @param {Uuid} id - The ID of the overlay that was deleted. * @returns {Signal} * @example Create an overlay then delete it after 1s. * var overlay = Overlays.addOverlay("cube", { @@ -624,13 +593,13 @@ signals: * Overlays.deleteOverlay(overlay); * }, 1000); */ - void overlayDeleted(OverlayID id); + void overlayDeleted(const QUuid& id); /**jsdoc * Triggered when a mouse press event occurs on an overlay. Only occurs for 3D overlays (unless you use * {@link Overlays.sendMousePressOnOverlay|sendMousePressOnOverlay} for a 2D overlay). * @function Overlays.mousePressOnOverlay - * @param {Uuid} overlayID - The ID of the overlay the mouse press event occurred on. + * @param {Uuid} id - The ID of the overlay the mouse press event occurred on. * @param {PointerEvent} event - The mouse press event details. * @returns {Signal} * @example Create a cube overlay in front of your avatar and report mouse clicks on it. @@ -648,36 +617,36 @@ signals: * } * }); */ - void mousePressOnOverlay(OverlayID overlayID, const PointerEvent& event); + void mousePressOnOverlay(const QUuid& id, const PointerEvent& event); /**jsdoc * Triggered when a mouse double press event occurs on an overlay. Only occurs for 3D overlays. * @function Overlays.mouseDoublePressOnOverlay - * @param {Uuid} overlayID - The ID of the overlay the mouse double press event occurred on. + * @param {Uuid} id - The ID of the overlay the mouse double press event occurred on. * @param {PointerEvent} event - The mouse double press event details. * @returns {Signal} */ - void mouseDoublePressOnOverlay(OverlayID overlayID, const PointerEvent& event); + void mouseDoublePressOnOverlay(const QUuid& id, const PointerEvent& event); /**jsdoc * Triggered when a mouse release event occurs on an overlay. Only occurs for 3D overlays (unless you use * {@link Overlays.sendMouseReleaseOnOverlay|sendMouseReleaseOnOverlay} for a 2D overlay). * @function Overlays.mouseReleaseOnOverlay - * @param {Uuid} overlayID - The ID of the overlay the mouse release event occurred on. + * @param {Uuid} id - The ID of the overlay the mouse release event occurred on. * @param {PointerEvent} event - The mouse release event details. * @returns {Signal} */ - void mouseReleaseOnOverlay(OverlayID overlayID, const PointerEvent& event); + void mouseReleaseOnOverlay(const QUuid& id, const PointerEvent& event); /**jsdoc * Triggered when a mouse move event occurs on an overlay. Only occurs for 3D overlays (unless you use * {@link Overlays.sendMouseMoveOnOverlay|sendMouseMoveOnOverlay} for a 2D overlay). * @function Overlays.mouseMoveOnOverlay - * @param {Uuid} overlayID - The ID of the overlay the mouse moved event occurred on. + * @param {Uuid} id - The ID of the overlay the mouse moved event occurred on. * @param {PointerEvent} event - The mouse move event details. * @returns {Signal} */ - void mouseMoveOnOverlay(OverlayID overlayID, const PointerEvent& event); + void mouseMoveOnOverlay(const QUuid& id, const PointerEvent& event); /**jsdoc * Triggered when a mouse press event occurs on something other than a 3D overlay. @@ -697,7 +666,7 @@ signals: * Triggered when a mouse cursor starts hovering over an overlay. Only occurs for 3D overlays (unless you use * {@link Overlays.sendHoverEnterOverlay|sendHoverEnterOverlay} for a 2D overlay). * @function Overlays.hoverEnterOverlay - * @param {Uuid} overlayID - The ID of the overlay the mouse moved event occurred on. + * @param {Uuid} id - The ID of the overlay the mouse moved event occurred on. * @param {PointerEvent} event - The mouse move event details. * @returns {Signal} * @example Create a cube overlay in front of your avatar and report when you start hovering your mouse over @@ -713,54 +682,67 @@ signals: * print("Hover enter: " + overlayID); * }); */ - void hoverEnterOverlay(OverlayID overlayID, const PointerEvent& event); + void hoverEnterOverlay(const QUuid& id, const PointerEvent& event); /**jsdoc * Triggered when a mouse cursor continues hovering over an overlay. Only occurs for 3D overlays (unless you use * {@link Overlays.sendHoverOverOverlay|sendHoverOverOverlay} for a 2D overlay). * @function Overlays.hoverOverOverlay - * @param {Uuid} overlayID - The ID of the overlay the hover over event occurred on. + * @param {Uuid} id - The ID of the overlay the hover over event occurred on. * @param {PointerEvent} event - The hover over event details. * @returns {Signal} */ - void hoverOverOverlay(OverlayID overlayID, const PointerEvent& event); + void hoverOverOverlay(const QUuid& id, const PointerEvent& event); /**jsdoc * Triggered when a mouse cursor finishes hovering over an overlay. Only occurs for 3D overlays (unless you use * {@link Overlays.sendHoverLeaveOverlay|sendHoverLeaveOverlay} for a 2D overlay). * @function Overlays.hoverLeaveOverlay - * @param {Uuid} overlayID - The ID of the overlay the hover leave event occurred on. + * @param {Uuid} id - The ID of the overlay the hover leave event occurred on. * @param {PointerEvent} event - The hover leave event details. * @returns {Signal} */ - void hoverLeaveOverlay(OverlayID overlayID, const PointerEvent& event); + void hoverLeaveOverlay(const QUuid& id, const PointerEvent& event); private: void cleanupOverlaysToDelete(); mutable QMutex _mutex { QMutex::Recursive }; - QMap _overlaysHUD; - QMap _overlaysWorld; - + QMap _overlays; QList _overlaysToDelete; + unsigned int _stackOrder { 1 }; - bool _enabled = true; - std::atomic _shuttingDown{ false }; + bool _enabled { true }; + std::atomic _shuttingDown { false }; - PointerEvent calculateOverlayPointerEvent(OverlayID overlayID, PickRay ray, RayToOverlayIntersectionResult rayPickResult, + PointerEvent calculateOverlayPointerEvent(const QUuid& id, const PickRay& ray, const RayToOverlayIntersectionResult& rayPickResult, QMouseEvent* event, PointerEvent::EventType eventType); - OverlayID _currentClickingOnOverlayID { UNKNOWN_OVERLAY_ID }; - OverlayID _currentHoverOverOverlayID { UNKNOWN_OVERLAY_ID }; + unsigned int _mouseRayPickID; + QUuid _currentClickingOnOverlayID; + QUuid _currentHoverOverOverlayID; + + static QString entityToOverlayType(const QString& type); + static QString overlayToEntityType(const QString& type); + static std::unordered_map _entityToOverlayTypes; + static std::unordered_map _overlayToEntityTypes; + + QVariantMap convertEntityToOverlayProperties(const EntityItemProperties& entityProps); + EntityItemProperties convertOverlayToEntityProperties(QVariantMap& overlayProps, const QString& type, bool add, const QUuid& id); + EntityItemProperties convertOverlayToEntityProperties(QVariantMap& overlayProps, glm::quat& rotationToSave, const QString& type, bool add, const QUuid& id = QUuid()); private slots: - void mousePressPointerEvent(const OverlayID& overlayID, const PointerEvent& event); - void mouseMovePointerEvent(const OverlayID& overlayID, const PointerEvent& event); - void mouseReleasePointerEvent(const OverlayID& overlayID, const PointerEvent& event); - void hoverEnterPointerEvent(const OverlayID& overlayID, const PointerEvent& event); - void hoverOverPointerEvent(const OverlayID& overlayID, const PointerEvent& event); - void hoverLeavePointerEvent(const OverlayID& overlayID, const PointerEvent& event); + void mousePressPointerEvent(const QUuid& id, const PointerEvent& event); + void mouseMovePointerEvent(const QUuid& id, const PointerEvent& event); + void mouseReleasePointerEvent(const QUuid& id, const PointerEvent& event); + void hoverEnterPointerEvent(const QUuid& id, const PointerEvent& event); + void hoverOverPointerEvent(const QUuid& id, const PointerEvent& event); + void hoverLeavePointerEvent(const QUuid& id, const PointerEvent& event); }; +#define ADD_TYPE_MAP(entity, overlay) \ + _entityToOverlayTypes[#entity] = #overlay; \ + _overlayToEntityTypes[#overlay] = #entity; + #endif // hifi_Overlays_h diff --git a/interface/src/ui/overlays/OverlaysPayload.cpp b/interface/src/ui/overlays/OverlaysPayload.cpp index 37fadef0b4..0d1bcdd071 100644 --- a/interface/src/ui/overlays/OverlaysPayload.cpp +++ b/interface/src/ui/overlays/OverlaysPayload.cpp @@ -8,27 +8,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include -#include - -#include -#include -#include -#include - -#include "Image3DOverlay.h" -#include "Circle3DOverlay.h" -#include "Cube3DOverlay.h" -#include "ImageOverlay.h" -#include "Line3DOverlay.h" -#include "ModelOverlay.h" -#include "Overlays.h" -#include "Rectangle3DOverlay.h" -#include "Sphere3DOverlay.h" -#include "Grid3DOverlay.h" -#include "TextOverlay.h" -#include "Text3DOverlay.h" - +#include "Overlay.h" namespace render { template <> const ItemKey payloadGetKey(const Overlay::Pointer& overlay) { diff --git a/interface/src/ui/overlays/PanelAttachable.cpp b/interface/src/ui/overlays/PanelAttachable.cpp deleted file mode 100644 index b53474390c..0000000000 --- a/interface/src/ui/overlays/PanelAttachable.cpp +++ /dev/null @@ -1,62 +0,0 @@ -// -// PanelAttachable.cpp -// interface/src/ui/overlays -// -// Created by Zander Otavka on 7/15/15. -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "PanelAttachable.h" - -#include - -bool PanelAttachable::getParentVisible() const { - return true; -} - -// JSDoc for copying to @typedefs of overlay types that inherit PanelAttachable. -// No JSDoc because these properties are not actually used. -QVariant PanelAttachable::getProperty(const QString& property) { - if (property == "offsetPosition") { - return vec3toVariant(getOffsetPosition()); - } - if (property == "offsetRotation") { - return quatToVariant(getOffsetRotation()); - } - if (property == "offsetScale") { - return vec3toVariant(getOffsetScale()); - } - return QVariant(); -} - -void PanelAttachable::setProperties(const QVariantMap& properties) { - auto offsetPosition = properties["offsetPosition"]; - bool valid; - if (offsetPosition.isValid()) { - glm::vec3 newPosition = vec3FromVariant(offsetPosition, valid); - if (valid) { - setOffsetPosition(newPosition); - } - } - - auto offsetRotation = properties["offsetRotation"]; - if (offsetRotation.isValid()) { - setOffsetRotation(quatFromVariant(offsetRotation)); - } - - auto offsetScale = properties["offsetScale"]; - if (offsetScale.isValid()) { - setOffsetScale(vec3FromVariant(offsetScale)); - } -} - -bool PanelAttachable::applyTransformTo(Transform& transform, bool force) { - if (force || usecTimestampNow() > _transformExpiry) { - const quint64 TRANSFORM_UPDATE_PERIOD = 100000; // frequency is 10 Hz - _transformExpiry = usecTimestampNow() + TRANSFORM_UPDATE_PERIOD; - } - return false; -} diff --git a/interface/src/ui/overlays/PanelAttachable.h b/interface/src/ui/overlays/PanelAttachable.h deleted file mode 100644 index 95faf38cf2..0000000000 --- a/interface/src/ui/overlays/PanelAttachable.h +++ /dev/null @@ -1,69 +0,0 @@ -// -// PanelAttachable.h -// interface/src/ui/overlays -// -// Created by Zander Otavka on 7/1/15. -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// -// Base class for anything that can attach itself to an `OverlayPanel` as a child. -// `PanelAttachable keeps an `std::shared_ptr` to it's parent panel, and sets its -// transformations and visibility based on the parent. -// -// When subclassing `PanelAttachable`, make sure `applyTransformTo`, `getProperty`, and -// `setProperties are all called in the appropriate places. Look through `Image3DOverlay` and -// `Billboard3DOverlay` for examples. Pay special attention to `applyTransformTo`; it should -// be called in three places for `Overlay`s: `render`, `update`, and `findRayIntersection`. -// -// When overriding `applyTransformTo`, make sure to wrap all of your code, including the call -// to the superclass method, with the following `if` block. Then call the superclass method -// with force = true. -// -// if (force || usecTimestampNow() > _transformExpiry) { -// PanelAttachable::applyTransformTo(transform, true); -// ... -// } -// - -#ifndef hifi_PanelAttachable_h -#define hifi_PanelAttachable_h - -#include - -#include -#include -#include -#include -#include - -class OverlayPanel; -class PanelAttachable { -public: - // getters - glm::vec3 getOffsetPosition() const { return _offset.getTranslation(); } - glm::quat getOffsetRotation() const { return _offset.getRotation(); } - glm::vec3 getOffsetScale() const { return _offset.getScale(); } - bool getParentVisible() const; - - // setters - void setOffsetPosition(const glm::vec3& position) { _offset.setTranslation(position); } - void setOffsetRotation(const glm::quat& rotation) { _offset.setRotation(rotation); } - void setOffsetScale(float scale) { _offset.setScale(scale); } - void setOffsetScale(const glm::vec3& scale) { _offset.setScale(scale); } - -protected: - void setProperties(const QVariantMap& properties); - QVariant getProperty(const QString& property); - - /// set position, rotation and scale on transform based on offsets, and parent panel offsets - /// if force is false, only apply transform if it hasn't been applied in the last .1 seconds - virtual bool applyTransformTo(Transform& transform, bool force = false); - quint64 _transformExpiry = 0; - -private: - Transform _offset; -}; - -#endif // hifi_PanelAttachable_h diff --git a/interface/src/ui/overlays/Planar3DOverlay.cpp b/interface/src/ui/overlays/Planar3DOverlay.cpp deleted file mode 100644 index c33d3a0c39..0000000000 --- a/interface/src/ui/overlays/Planar3DOverlay.cpp +++ /dev/null @@ -1,126 +0,0 @@ -// -// Planar3DOverlay.cpp -// interface/src/ui/overlays -// -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "Planar3DOverlay.h" - -#include -#include -#include - -Planar3DOverlay::Planar3DOverlay() : - Base3DOverlay(), - _dimensions{1.0f, 1.0f} -{ -} - -Planar3DOverlay::Planar3DOverlay(const Planar3DOverlay* planar3DOverlay) : - Base3DOverlay(planar3DOverlay), - _dimensions(planar3DOverlay->_dimensions) -{ -} - -AABox Planar3DOverlay::getBounds() const { - auto halfDimensions = glm::vec3{_dimensions / 2.0f, 0.01f}; - - auto extents = Extents{-halfDimensions, halfDimensions}; - extents.transform(getTransform()); - - return AABox(extents); -} - -void Planar3DOverlay::setDimensions(const glm::vec2& value) { - _dimensions = value; - notifyRenderVariableChange(); -} - -void Planar3DOverlay::setProperties(const QVariantMap& properties) { - Base3DOverlay::setProperties(properties); - - auto dimensions = properties["dimensions"]; - - // if "dimensions" property was not there, check to see if they included aliases: scale - if (!dimensions.isValid()) { - dimensions = properties["scale"]; - if (!dimensions.isValid()) { - dimensions = properties["size"]; - } - } - - if (dimensions.isValid()) { - setDimensions(vec2FromVariant(dimensions)); - } -} - -// JSDoc for copying to @typedefs of overlay types that inherit Planar3DOverlay. -/**jsdoc - * @property {Vec2} dimensions=1,1 - The dimensions of the overlay. Synonyms: scale, size. - */ -QVariant Planar3DOverlay::getProperty(const QString& property) { - if (property == "dimensions" || property == "scale" || property == "size") { - return vec2ToVariant(getDimensions()); - } - - return Base3DOverlay::getProperty(property); -} - -bool Planar3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) { - glm::vec2 xyDimensions = getDimensions(); - glm::quat rotation = getWorldOrientation(); - glm::vec3 position = getWorldPosition(); - - if (findRayRectangleIntersection(origin, direction, rotation, position, xyDimensions, distance)) { - glm::vec3 forward = rotation * Vectors::FRONT; - if (glm::dot(forward, direction) > 0.0f) { - face = MAX_Z_FACE; - surfaceNormal = -forward; - } else { - face = MIN_Z_FACE; - surfaceNormal = forward; - } - return true; - } - return false; -} - -bool Planar3DOverlay::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, - float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) { - glm::vec2 xyDimensions = getDimensions(); - glm::quat rotation = getWorldOrientation(); - glm::vec3 position = getWorldPosition(); - - glm::quat inverseRot = glm::inverse(rotation); - glm::vec3 localOrigin = inverseRot * (origin - position); - glm::vec3 localVelocity = inverseRot * velocity; - glm::vec3 localAcceleration = inverseRot * acceleration; - - if (findParabolaRectangleIntersection(localOrigin, localVelocity, localAcceleration, xyDimensions, parabolicDistance)) { - float localIntersectionVelocityZ = localVelocity.z + localAcceleration.z * parabolicDistance; - glm::vec3 forward = rotation * Vectors::FRONT; - if (localIntersectionVelocityZ > 0.0f) { - face = MIN_Z_FACE; - surfaceNormal = forward; - } else { - face = MAX_Z_FACE; - surfaceNormal = -forward; - } - return true; - } - return false; -} - -Transform Planar3DOverlay::evalRenderTransform() { - auto transform = getTransform(); - transform.setScale(1.0f); // ignore inherited scale factor from parents - if (glm::length2(getDimensions()) != 1.0f) { - transform.postScale(vec3(getDimensions(), 1.0f)); - } - return transform; -} diff --git a/interface/src/ui/overlays/Planar3DOverlay.h b/interface/src/ui/overlays/Planar3DOverlay.h deleted file mode 100644 index 0054b0baf1..0000000000 --- a/interface/src/ui/overlays/Planar3DOverlay.h +++ /dev/null @@ -1,45 +0,0 @@ -// -// Planar3DOverlay.h -// interface/src/ui/overlays -// -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_Planar3DOverlay_h -#define hifi_Planar3DOverlay_h - -#include "Base3DOverlay.h" - -class Planar3DOverlay : public Base3DOverlay { - Q_OBJECT - -public: - Planar3DOverlay(); - Planar3DOverlay(const Planar3DOverlay* planar3DOverlay); - - virtual AABox getBounds() const override; - virtual glm::vec2 getSize() const { return _dimensions; }; - - glm::vec2 getDimensions() const { return _dimensions; } - void setDimensions(float value) { setDimensions(glm::vec2(value)); } - void setDimensions(const glm::vec2& value); - - virtual void setProperties(const QVariantMap& properties) override; - virtual QVariant getProperty(const QString& property) override; - - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override; - virtual bool findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, - float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override; - -protected: - glm::vec2 _dimensions; - - Transform evalRenderTransform() override; -}; - - -#endif // hifi_Planar3DOverlay_h diff --git a/interface/src/ui/overlays/QmlOverlay.cpp b/interface/src/ui/overlays/QmlOverlay.cpp index 2a583e0450..537c421ca7 100644 --- a/interface/src/ui/overlays/QmlOverlay.cpp +++ b/interface/src/ui/overlays/QmlOverlay.cpp @@ -26,8 +26,8 @@ QmlOverlay::QmlOverlay(const QUrl& url) { buildQmlElement(url); } -QmlOverlay::QmlOverlay(const QUrl& url, const QmlOverlay* textOverlay) - : Overlay2D(textOverlay) { +QmlOverlay::QmlOverlay(const QUrl& url, const QmlOverlay* overlay) + : Overlay2D(overlay) { buildQmlElement(url); } diff --git a/interface/src/ui/overlays/QmlOverlay.h b/interface/src/ui/overlays/QmlOverlay.h index 7f2cf5a918..0951a04772 100644 --- a/interface/src/ui/overlays/QmlOverlay.h +++ b/interface/src/ui/overlays/QmlOverlay.h @@ -22,10 +22,9 @@ class QmlOverlay : public Overlay2D { public: QmlOverlay(const QUrl& url); - QmlOverlay(const QUrl& url, const QmlOverlay* textOverlay); + QmlOverlay(const QUrl& url, const QmlOverlay* overlay); ~QmlOverlay(); - // Cannot fetch properties from QML based overlays due to race conditions bool supportsGetProperty() const override { return false; } void setProperties(const QVariantMap& properties) override; diff --git a/interface/src/ui/overlays/Rectangle3DOverlay.cpp b/interface/src/ui/overlays/Rectangle3DOverlay.cpp deleted file mode 100644 index e30171bd50..0000000000 --- a/interface/src/ui/overlays/Rectangle3DOverlay.cpp +++ /dev/null @@ -1,158 +0,0 @@ -// -// Rectangle3DOverlay.cpp -// interface/src/ui/overlays -// -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "Rectangle3DOverlay.h" - -#include -#include - - -QString const Rectangle3DOverlay::TYPE = "rectangle3d"; - -Rectangle3DOverlay::Rectangle3DOverlay() : - _geometryCacheID(DependencyManager::get()->allocateID()) -{ - auto geometryCache = DependencyManager::get(); - for (size_t i = 0; i < _rectGeometryIds.size(); ++i) { - _rectGeometryIds[i] = geometryCache->allocateID(); - } -} - -Rectangle3DOverlay::Rectangle3DOverlay(const Rectangle3DOverlay* rectangle3DOverlay) : - Planar3DOverlay(rectangle3DOverlay), - _geometryCacheID(DependencyManager::get()->allocateID()) -{ - auto geometryCache = DependencyManager::get(); - for (size_t i = 0; i < _rectGeometryIds.size(); ++i) { - _rectGeometryIds[i] = geometryCache->allocateID(); - } -} - -Rectangle3DOverlay::~Rectangle3DOverlay() { - auto geometryCache = DependencyManager::get(); - if (geometryCache) { - geometryCache->releaseID(_geometryCacheID); - for (size_t i = 0; i < _rectGeometryIds.size(); ++i) { - geometryCache->releaseID(_rectGeometryIds[i]); - } - } -} - -void Rectangle3DOverlay::render(RenderArgs* args) { - if (!_renderVisible) { - return; // do nothing if we're not visible - } - - float alpha = getAlpha(); - glm::u8vec3 color = getColor(); - glm::vec4 rectangleColor(toGlm(color), alpha); - - auto batch = args->_batch; - if (batch) { - Transform transform = getRenderTransform(); - glm::vec2 halfDimensions = transform.getScale() * 0.5f; - transform.setScale(1.0f); - - batch->setModelTransform(transform); - auto geometryCache = DependencyManager::get(); - - if (getIsSolid()) { - glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, 0.0f); - glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, 0.0f); - geometryCache->bindSimpleProgram(*batch); - geometryCache->renderQuad(*batch, topLeft, bottomRight, rectangleColor, _geometryCacheID); - } else { - geometryCache->bindSimpleProgram(*batch, false, false, false, true, true); - if (getIsDashedLine()) { - glm::vec3 point1(-halfDimensions.x, -halfDimensions.y, 0.0f); - glm::vec3 point2(halfDimensions.x, -halfDimensions.y, 0.0f); - glm::vec3 point3(halfDimensions.x, halfDimensions.y, 0.0f); - glm::vec3 point4(-halfDimensions.x, halfDimensions.y, 0.0f); - - geometryCache->renderDashedLine(*batch, point1, point2, rectangleColor, _rectGeometryIds[0]); - geometryCache->renderDashedLine(*batch, point2, point3, rectangleColor, _rectGeometryIds[1]); - geometryCache->renderDashedLine(*batch, point3, point4, rectangleColor, _rectGeometryIds[2]); - geometryCache->renderDashedLine(*batch, point4, point1, rectangleColor, _rectGeometryIds[3]); - } else { - if (halfDimensions != _previousHalfDimensions) { - QVector border; - border << glm::vec3(-halfDimensions.x, -halfDimensions.y, 0.0f); - border << glm::vec3(halfDimensions.x, -halfDimensions.y, 0.0f); - border << glm::vec3(halfDimensions.x, halfDimensions.y, 0.0f); - border << glm::vec3(-halfDimensions.x, halfDimensions.y, 0.0f); - border << glm::vec3(-halfDimensions.x, -halfDimensions.y, 0.0f); - geometryCache->updateVertices(_geometryCacheID, border, rectangleColor); - - _previousHalfDimensions = halfDimensions; - } - geometryCache->renderVertices(*batch, gpu::LINE_STRIP, _geometryCacheID); - } - } - } -} - -const render::ShapeKey Rectangle3DOverlay::getShapeKey() { - auto builder = render::ShapeKey::Builder().withOwnPipeline(); - if (isTransparent()) { - builder.withTranslucent(); - } - return builder.build(); -} - -/**jsdoc - * These are the properties of a rectangle3d {@link Overlays.OverlayType|OverlayType}. - * @typedef {object} Overlays.Rectangle3DProperties - * - * @property {string} type=rectangle3d - Has the value "rectangle3d". Read-only. - * @property {Color} color=255,255,255 - The color of the overlay. - * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. - * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. - * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. - * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from - * pulseMin to pulseMax, then pulseMax to pulseMin in one period. - * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 - * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise - * used.) - * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 - * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise - * used.) - * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. - * - * @property {string} name="" - A friendly name for the overlay. - * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and - * start. - * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a - * parentID set, otherwise the same value as position. - * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. - * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a - * parentID set, otherwise the same value as rotation. - * @property {boolean} isSolid=false - Synonyms: solid, isFilled, and filled. - * Antonyms: isWire and wire. - * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: - * dashed. Deprecated. - * @property {boolean} ignorePickIntersection=false - If true, picks ignore the overlay. ignoreRayIntersection is a synonym. - * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't - * have drawInFront set to true, and in front of entities. - * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. - * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. - * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if - * parentID is an avatar skeleton. A value of 65535 means "no joint". - * - * @property {Vec2} dimensions=1,1 - The dimensions of the overlay. Synonyms: scale, size. - */ -void Rectangle3DOverlay::setProperties(const QVariantMap& properties) { - Planar3DOverlay::setProperties(properties); -} - -Rectangle3DOverlay* Rectangle3DOverlay::createClone() const { - return new Rectangle3DOverlay(this); -} diff --git a/interface/src/ui/overlays/Rectangle3DOverlay.h b/interface/src/ui/overlays/Rectangle3DOverlay.h deleted file mode 100644 index 645553ed38..0000000000 --- a/interface/src/ui/overlays/Rectangle3DOverlay.h +++ /dev/null @@ -1,39 +0,0 @@ -// -// Rectangle3DOverlay.h -// interface/src/ui/overlays -// -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_Rectangle3DOverlay_h -#define hifi_Rectangle3DOverlay_h - -#include "Planar3DOverlay.h" - -class Rectangle3DOverlay : public Planar3DOverlay { - Q_OBJECT - -public: - static QString const TYPE; - virtual QString getType() const override { return TYPE; } - - Rectangle3DOverlay(); - Rectangle3DOverlay(const Rectangle3DOverlay* rectangle3DOverlay); - ~Rectangle3DOverlay(); - virtual void render(RenderArgs* args) override; - virtual const render::ShapeKey getShapeKey() override; - void setProperties(const QVariantMap& properties) override; - - virtual Rectangle3DOverlay* createClone() const override; - -private: - int _geometryCacheID; - std::array _rectGeometryIds; - glm::vec2 _previousHalfDimensions; -}; - - -#endif // hifi_Rectangle3DOverlay_h diff --git a/interface/src/ui/overlays/RectangleOverlay.cpp b/interface/src/ui/overlays/RectangleOverlay.cpp index af37a4ac02..1487a4cb63 100644 --- a/interface/src/ui/overlays/RectangleOverlay.cpp +++ b/interface/src/ui/overlays/RectangleOverlay.cpp @@ -11,29 +11,6 @@ QString const RectangleOverlay::TYPE = "rectangle"; QUrl const RectangleOverlay::URL(QString("hifi/overlays/RectangleOverlay.qml")); -// RectangleOverlay's properties are defined in the QML file specified above. -/**jsdoc - * These are the properties of a rectangle {@link Overlays.OverlayType|OverlayType}. - * @typedef {object} Overlays.RectangleProperties - * - * @property {Rect} bounds - The position and size of the rectangle, in pixels. Write-only. - * @property {number} x - Integer left, x-coordinate value = bounds.x. Write-only. - * @property {number} y - Integer top, y-coordinate value = bounds.y. Write-only. - * @property {number} width - Integer width of the rectangle = bounds.width. Write-only. - * @property {number} height - Integer height of the rectangle = bounds.height. Write-only. - * - * @property {Color} color=0,0,0 - The color of the overlay. Write-only. - * @property {number} alpha=1.0 - The opacity of the overlay, 0.0 - 1.0. Write-only. - * @property {number} borderWidth=1 - Integer width of the border, in pixels. The border is drawn within the rectangle's bounds. - * It is not drawn unless either borderColor or borderAlpha are specified. Write-only. - * @property {number} radius=0 - Integer corner radius, in pixels. Write-only. - * @property {Color} borderColor=0,0,0 - The color of the border. Write-only. - * @property {number} borderAlpha=1.0 - The opacity of the border, 0.0 - 1.0. - * Write-only. - * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. - * Write-only. - */ - RectangleOverlay::RectangleOverlay() : QmlOverlay(URL) {} RectangleOverlay::RectangleOverlay(const RectangleOverlay* rectangleOverlay) diff --git a/interface/src/ui/overlays/Shape3DOverlay.cpp b/interface/src/ui/overlays/Shape3DOverlay.cpp deleted file mode 100644 index 4adbbf3792..0000000000 --- a/interface/src/ui/overlays/Shape3DOverlay.cpp +++ /dev/null @@ -1,204 +0,0 @@ -// -// Shape3DOverlay.cpp -// interface/src/ui/overlays -// -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -// include this before QGLWidget, which includes an earlier version of OpenGL -#include "Shape3DOverlay.h" - -#include -#include -#include -#include - -QString const Shape3DOverlay::TYPE = "shape"; - -Shape3DOverlay::Shape3DOverlay(const Shape3DOverlay* shape3DOverlay) : - Volume3DOverlay(shape3DOverlay), - _shape(shape3DOverlay->_shape) -{ -} - -void Shape3DOverlay::render(RenderArgs* args) { - if (!_renderVisible) { - return; // do nothing if we're not visible - } - - float alpha = getAlpha(); - glm::u8vec3 color = getColor(); - glm::vec4 shapeColor(toGlm(color), alpha); - - auto batch = args->_batch; - if (batch) { - auto geometryCache = DependencyManager::get(); - auto shapePipeline = args->_shapePipeline; - if (!shapePipeline) { - shapePipeline = _isSolid ? geometryCache->getOpaqueShapePipeline() : geometryCache->getWireShapePipeline(); - } - - batch->setModelTransform(getRenderTransform()); - if (_isSolid) { - geometryCache->renderSolidShapeInstance(args, *batch, _shape, shapeColor, shapePipeline); - } else { - geometryCache->renderWireShapeInstance(args, *batch, _shape, shapeColor, shapePipeline); - } - } -} - -const render::ShapeKey Shape3DOverlay::getShapeKey() { - auto builder = render::ShapeKey::Builder(); - if (isTransparent()) { - builder.withTranslucent(); - } - if (!getIsSolid()) { - builder.withUnlit().withDepthBias(); - } - return builder.build(); -} - -Shape3DOverlay* Shape3DOverlay::createClone() const { - return new Shape3DOverlay(this); -} - - -/**jsdoc - *

A shape {@link Overlays.OverlayType|OverlayType} may display as one of the following geometrical shapes:

- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
ValueDimensionsDescription
"Circle"2DA circle oriented in 3D.
"Cone"3D
"Cube"3D
"Cylinder"3D
"Dodecahedron"3D
"Hexagon"3DA hexagonal prism.
"Icosahedron"3D
"Line"1DA line oriented in 3D.
"Octagon"3DAn octagonal prism.
"Octahedron"3D
"Quad"2DA square oriented in 3D.
"Sphere"3D
"Tetrahedron"3D
"Torus"3DNot implemented.
"Triangle"3DA triangular prism.
- * @typedef {string} Overlays.Shape - */ -static const std::array shapeStrings { { - "Line", - "Triangle", - "Quad", - "Hexagon", - "Octagon", - "Circle", - "Cube", - "Sphere", - "Tetrahedron", - "Octahedron", - "Dodecahedron", - "Icosahedron", - "Torus", // Not implemented yet. - "Cone", - "Cylinder" -} }; - - -void Shape3DOverlay::setProperties(const QVariantMap& properties) { - Volume3DOverlay::setProperties(properties); - - auto shape = properties["shape"]; - if (shape.isValid()) { - const QString shapeStr = shape.toString(); - for (size_t i = 0; i < shapeStrings.size(); ++i) { - if (shapeStr == shapeStrings[i]) { - this->_shape = static_cast(i); - break; - } - } - } -} - -/**jsdoc - * These are the properties of a shape {@link Overlays.OverlayType|OverlayType}. - * @typedef {object} Overlays.ShapeProperties - * - * @property {string} type=shape - Has the value "shape". Read-only. - * @property {Color} color=255,255,255 - The color of the overlay. - * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. - * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. - * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. - * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from - * pulseMin to pulseMax, then pulseMax to pulseMin in one period. - * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 - * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise - * used.) - * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 - * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise - * used.) - * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. - * - * @property {string} name="" - A friendly name for the overlay. - * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and - * start. - * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a - * parentID set, otherwise the same value as position. - * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. - * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a - * parentID set, otherwise the same value as rotation. - * @property {boolean} isSolid=false - Synonyms: solid
, isFilled, and filled. - * Antonyms: isWire and wire. - * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: - * dashed. Deprecated. - * @property {boolean} ignorePickIntersection=false - If true, picks ignore the overlay. ignoreRayIntersection is a synonym. - * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't - * have drawInFront set to true, and in front of entities. - * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. - * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. - * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if - * parentID is an avatar skeleton. A value of 65535 means "no joint". - * - * @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: scale, size. - * - * @property {Overlays.Shape} shape=Hexagon - The geometrical shape of the overlay. - */ -QVariant Shape3DOverlay::getProperty(const QString& property) { - if (property == "shape") { - return shapeStrings[_shape]; - } - - return Volume3DOverlay::getProperty(property); -} - -Transform Shape3DOverlay::evalRenderTransform() { - // TODO: handle registration point?? - glm::vec3 position = getWorldPosition(); - glm::vec3 dimensions = getDimensions(); - glm::quat rotation = getWorldOrientation(); - - Transform transform; - transform.setScale(dimensions); - transform.setTranslation(position); - transform.setRotation(rotation); - return transform; -} - -scriptable::ScriptableModelBase Shape3DOverlay::getScriptableModel() { - auto geometryCache = DependencyManager::get(); - auto vertexColor = ColorUtils::toVec3(_color); - scriptable::ScriptableModelBase result; - result.objectID = getID(); - if (auto mesh = geometryCache->meshFromShape(_shape, vertexColor)) { - result.append(mesh); - } - return result; -} diff --git a/interface/src/ui/overlays/Shape3DOverlay.h b/interface/src/ui/overlays/Shape3DOverlay.h deleted file mode 100644 index 60af287af0..0000000000 --- a/interface/src/ui/overlays/Shape3DOverlay.h +++ /dev/null @@ -1,45 +0,0 @@ -// -// Shape3DOverlay.h -// interface/src/ui/overlays -// -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_Shape3DOverlay_h -#define hifi_Shape3DOverlay_h - -#include "Volume3DOverlay.h" - -#include - -class Shape3DOverlay : public Volume3DOverlay { - Q_OBJECT - -public: - static QString const TYPE; - virtual QString getType() const override { return TYPE; } - - Shape3DOverlay() {} - Shape3DOverlay(const Shape3DOverlay* shape3DOverlay); - - virtual void render(RenderArgs* args) override; - virtual const render::ShapeKey getShapeKey() override; - - virtual Shape3DOverlay* createClone() const override; - - void setProperties(const QVariantMap& properties) override; - QVariant getProperty(const QString& property) override; - - virtual scriptable::ScriptableModelBase getScriptableModel() override; -protected: - Transform evalRenderTransform() override; - -private: - GeometryCache::Shape _shape { GeometryCache::Hexagon }; -}; - - -#endif // hifi_Shape3DOverlay_h diff --git a/interface/src/ui/overlays/Sphere3DOverlay.cpp b/interface/src/ui/overlays/Sphere3DOverlay.cpp deleted file mode 100644 index b1d5c878c4..0000000000 --- a/interface/src/ui/overlays/Sphere3DOverlay.cpp +++ /dev/null @@ -1,135 +0,0 @@ -// -// Sphere3DOverlay.cpp -// interface/src/ui/overlays -// -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "Sphere3DOverlay.h" - -#include -#include -#include -#include - -QString const Sphere3DOverlay::TYPE = "sphere"; - -// Sphere overlays should fit inside a cube of the specified dimensions, hence it needs to be a half unit sphere. -// However, the geometry cache renders a UNIT sphere, so we need to scale down. -static const float SPHERE_OVERLAY_SCALE = 0.5f; - -Sphere3DOverlay::Sphere3DOverlay(const Sphere3DOverlay* Sphere3DOverlay) : - Volume3DOverlay(Sphere3DOverlay) -{ -} - -// If Sphere3DOverlay had a getProperty() method then it would go here; do JSDoc here. -/**jsdoc - * These are the properties of a sphere {@link Overlays.OverlayType|OverlayType}. - * @typedef {object} Overlays.SphereProperties - * - * @property {string} type=sphere - Has the value "sphere". Read-only. - * @property {Color} color=255,255,255 - The color of the overlay. - * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. - * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. - * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. - * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from - * pulseMin to pulseMax, then pulseMax to pulseMin in one period. - * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 - * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise - * used.) - * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 - * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise - * used.) - * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. - * - * @property {string} name="" - A friendly name for the overlay. - * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and - * start. - * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a - * parentID set, otherwise the same value as position. - * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. - * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a - * parentID set, otherwise the same value as rotation. - * @property {boolean} isSolid=false - Synonyms: solid, isFilled, and filled. - * Antonyms: isWire and wire. - * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: - * dashed. Deprecated. - * @property {boolean} ignorePickIntersection=false - If true, picks ignore the overlay. ignoreRayIntersection is a synonym. - * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't - * have drawInFront set to true, and in front of entities. - * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. - * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. - * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if - * parentID is an avatar skeleton. A value of 65535 means "no joint". - * - * @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: scale, size. - */ - -void Sphere3DOverlay::render(RenderArgs* args) { - if (!_renderVisible) { - return; // do nothing if we're not visible - } - - float alpha = getAlpha(); - glm::u8vec3 color = getColor(); - glm::vec4 sphereColor(toGlm(color), alpha); - - auto batch = args->_batch; - - if (batch) { - batch->setModelTransform(getRenderTransform()); - - auto geometryCache = DependencyManager::get(); - auto shapePipeline = args->_shapePipeline; - if (!shapePipeline) { - shapePipeline = _isSolid ? geometryCache->getOpaqueShapePipeline() : geometryCache->getWireShapePipeline(); - } - - if (_isSolid) { - geometryCache->renderSolidSphereInstance(args, *batch, sphereColor, shapePipeline); - } else { - geometryCache->renderWireSphereInstance(args, *batch, sphereColor, shapePipeline); - } - } -} - -const render::ShapeKey Sphere3DOverlay::getShapeKey() { - auto builder = render::ShapeKey::Builder(); - if (isTransparent()) { - builder.withTranslucent(); - } - if (!getIsSolid()) { - builder.withUnlit().withDepthBias(); - } - return builder.build(); -} - -Sphere3DOverlay* Sphere3DOverlay::createClone() const { - return new Sphere3DOverlay(this); -} - -Transform Sphere3DOverlay::evalRenderTransform() { - Transform transform = getTransform(); - transform.setScale(1.0f); // ignore inherited scale from SpatiallyNestable - transform.postScale(getDimensions() * SPHERE_OVERLAY_SCALE); - - return transform; -} - - -scriptable::ScriptableModelBase Sphere3DOverlay::getScriptableModel() { - auto geometryCache = DependencyManager::get(); - auto vertexColor = ColorUtils::toVec3(_color); - scriptable::ScriptableModelBase result; - if (auto mesh = geometryCache->meshFromShape(GeometryCache::Sphere, vertexColor)) { - result.objectID = getID(); - result.append(mesh); - } - return result; -} diff --git a/interface/src/ui/overlays/Sphere3DOverlay.h b/interface/src/ui/overlays/Sphere3DOverlay.h deleted file mode 100644 index 9a434e7182..0000000000 --- a/interface/src/ui/overlays/Sphere3DOverlay.h +++ /dev/null @@ -1,37 +0,0 @@ -// -// Sphere3DOverlay.h -// interface/src/ui/overlays -// -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_Sphere3DOverlay_h -#define hifi_Sphere3DOverlay_h - -#include "Volume3DOverlay.h" - -class Sphere3DOverlay : public Volume3DOverlay { - Q_OBJECT - -public: - static QString const TYPE; - virtual QString getType() const override { return TYPE; } - - Sphere3DOverlay() {} - Sphere3DOverlay(const Sphere3DOverlay* Sphere3DOverlay); - - virtual void render(RenderArgs* args) override; - virtual const render::ShapeKey getShapeKey() override; - - virtual Sphere3DOverlay* createClone() const override; - - virtual scriptable::ScriptableModelBase getScriptableModel() override; -protected: - Transform evalRenderTransform() override; -}; - - -#endif // hifi_Sphere3DOverlay_h diff --git a/interface/src/ui/overlays/Text3DOverlay.cpp b/interface/src/ui/overlays/Text3DOverlay.cpp deleted file mode 100644 index 58ce16a9fc..0000000000 --- a/interface/src/ui/overlays/Text3DOverlay.cpp +++ /dev/null @@ -1,295 +0,0 @@ -// -// Text3DOverlay.cpp -// interface/src/ui/overlays -// -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - - -#include "Text3DOverlay.h" - -#include -#include -#include -#include -#include - -#include - -const int FIXED_FONT_POINT_SIZE = 40; -const int FIXED_FONT_SCALING_RATIO = FIXED_FONT_POINT_SIZE * 92.0f; // Determined through experimentation to fit font to line - // height. -const float LINE_SCALE_RATIO = 1.2f; - -QString const Text3DOverlay::TYPE = "text3d"; - -Text3DOverlay::Text3DOverlay() { - _textRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE); - _geometryId = DependencyManager::get()->allocateID(); -} - -Text3DOverlay::Text3DOverlay(const Text3DOverlay* text3DOverlay) : - Billboard3DOverlay(text3DOverlay), - _text(text3DOverlay->_text), - _backgroundColor(text3DOverlay->_backgroundColor), - _textAlpha(text3DOverlay->_textAlpha), - _lineHeight(text3DOverlay->_lineHeight), - _leftMargin(text3DOverlay->_leftMargin), - _topMargin(text3DOverlay->_topMargin), - _rightMargin(text3DOverlay->_rightMargin), - _bottomMargin(text3DOverlay->_bottomMargin) -{ - _textRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE); - _geometryId = DependencyManager::get()->allocateID(); -} - -Text3DOverlay::~Text3DOverlay() { - delete _textRenderer; - auto geometryCache = DependencyManager::get(); - if (geometryCache) { - geometryCache->releaseID(_geometryId); - } -} - -const QString Text3DOverlay::getText() const { - QMutexLocker lock(&_mutex); - return _text; -} - -void Text3DOverlay::setText(const QString& text) { - QMutexLocker lock(&_mutex); - _text = text; -} - -glm::u8vec3 Text3DOverlay::getBackgroundColor() { - if (_colorPulse == 0.0f) { - return _backgroundColor; - } - - float pulseLevel = updatePulse(); - glm::u8vec3 result = _backgroundColor; - if (_colorPulse < 0.0f) { - result.x *= (1.0f - pulseLevel); - result.y *= (1.0f - pulseLevel); - result.z *= (1.0f - pulseLevel); - } else { - result.x *= pulseLevel; - result.y *= pulseLevel; - result.z *= pulseLevel; - } - return result; -} - -void Text3DOverlay::render(RenderArgs* args) { - if (!_renderVisible || !getParentVisible()) { - return; // do nothing if we're not visible - } - - Q_ASSERT(args->_batch); - auto& batch = *args->_batch; - - auto transform = getRenderTransform(); - batch.setModelTransform(transform); - - glm::u8vec3 backgroundColor = getBackgroundColor(); - glm::vec4 quadColor(toGlm(backgroundColor), getBackgroundAlpha()); - - glm::vec2 dimensions = getDimensions(); - glm::vec2 halfDimensions = dimensions * 0.5f; - - const float SLIGHTLY_BEHIND = -0.001f; - - glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, SLIGHTLY_BEHIND); - glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, SLIGHTLY_BEHIND); - DependencyManager::get()->bindSimpleProgram(batch, false, quadColor.a < 1.0f, false, false, false); - DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, quadColor, _geometryId); - - // Same font properties as textSize() - float maxHeight = (float)_textRenderer->computeExtent("Xy").y * LINE_SCALE_RATIO; - - float scaleFactor = (maxHeight / FIXED_FONT_SCALING_RATIO) * _lineHeight; - - glm::vec2 clipDimensions((dimensions.x - (_leftMargin + _rightMargin)) / scaleFactor, - (dimensions.y - (_topMargin + _bottomMargin)) / scaleFactor); - - transform.postTranslate(glm::vec3(-(halfDimensions.x - _leftMargin), - halfDimensions.y - _topMargin, 0.001f)); - transform.setScale(scaleFactor); - batch.setModelTransform(transform); - - glm::vec4 textColor = { toGlm(_color), getTextAlpha() }; - - // FIXME: Factor out textRenderer so that Text3DOverlay overlay parts can be grouped by pipeline for a gpu performance increase. - _textRenderer->draw(batch, 0, 0, getText(), textColor, glm::vec2(-1.0f)); -} - -const render::ShapeKey Text3DOverlay::getShapeKey() { - auto builder = render::ShapeKey::Builder(); - if (isTransparent()) { - builder.withTranslucent(); - } - return builder.build(); -} - -void Text3DOverlay::setProperties(const QVariantMap& properties) { - Billboard3DOverlay::setProperties(properties); - - auto text = properties["text"]; - if (text.isValid()) { - setText(text.toString()); - } - - auto textAlpha = properties["textAlpha"]; - if (textAlpha.isValid()) { - float prevTextAlpha = getTextAlpha(); - setTextAlpha(textAlpha.toFloat()); - // Update our payload key if necessary to handle transparency - if ((prevTextAlpha < 1.0f && _textAlpha >= 1.0f) || (prevTextAlpha >= 1.0f && _textAlpha < 1.0f)) { - auto itemID = getRenderItemID(); - if (render::Item::isValidID(itemID)) { - render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); - render::Transaction transaction; - transaction.updateItem(itemID); - scene->enqueueTransaction(transaction); - } - } - } - - bool valid; - auto backgroundColor = properties["backgroundColor"]; - if (backgroundColor.isValid()) { - auto color = u8vec3FromVariant(backgroundColor, valid); - if (valid) { - _backgroundColor = color; - } - } - - if (properties["backgroundAlpha"].isValid()) { - setAlpha(properties["backgroundAlpha"].toFloat()); - } - - if (properties["lineHeight"].isValid()) { - setLineHeight(properties["lineHeight"].toFloat()); - } - - if (properties["leftMargin"].isValid()) { - setLeftMargin(properties["leftMargin"].toFloat()); - } - - if (properties["topMargin"].isValid()) { - setTopMargin(properties["topMargin"].toFloat()); - } - - if (properties["rightMargin"].isValid()) { - setRightMargin(properties["rightMargin"].toFloat()); - } - - if (properties["bottomMargin"].isValid()) { - setBottomMargin(properties["bottomMargin"].toFloat()); - } -} - -/**jsdoc - * These are the properties of a text3d {@link Overlays.OverlayType|OverlayType}. - * @typedef {object} Overlays.Text3DProperties - * - * @property {string} type=text3d - Has the value "text3d". Read-only. - * @property {Color} color=255,255,255 - The color of the overlay. - * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. - * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. - * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. - * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from - * pulseMin to pulseMax, then pulseMax to pulseMin in one period. - * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 - * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise - * used.) - * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 - * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise - * used.) - * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. - * - * @property {string} name="" - A friendly name for the overlay. - * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and - * start. - * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a - * parentID set, otherwise the same value as position. - * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. - * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a - * parentID set, otherwise the same value as rotation. - * @property {boolean} isSolid=false - Synonyms: solid, isFilled, and filled. - * Antonyms: isWire and wire. - * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: - * dashed. Deprecated. - * @property {boolean} ignorePickIntersection=false - If true, picks ignore the overlay. ignoreRayIntersection is a synonym. - * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't - * have drawInFront set to true, and in front of entities. - * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. - * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. - * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if - * parentID is an avatar skeleton. A value of 65535 means "no joint". - * - * @property {Vec2} dimensions=1,1 - The dimensions of the overlay. Synonyms: scale, size. - * - * @property {boolean} isFacingAvatar - If true, the overlay is rotated to face the user's camera about an axis - * parallel to the user's avatar's "up" direction. - * - * @property {string} text="" - The text to display. Text does not automatically wrap; use \n for a line break. - * @property {number} textAlpha=1 - The text alpha value. - * @property {Color} backgroundColor=0,0,0 - The background color. - * @property {number} backgroundAlpha=0.7 - The background alpha value. - * @property {number} lineHeight=1 - The height of a line of text in meters. - * @property {number} leftMargin=0.1 - The left margin, in meters. - * @property {number} topMargin=0.1 - The top margin, in meters. - * @property {number} rightMargin=0.1 - The right margin, in meters. - * @property {number} bottomMargin=0.1 - The bottom margin, in meters. - */ - -QVariant Text3DOverlay::getProperty(const QString& property) { - if (property == "text") { - return getText(); - } - if (property == "textAlpha") { - return _textAlpha; - } - if (property == "backgroundColor") { - return u8vec3ColortoVariant(_backgroundColor); - } - if (property == "backgroundAlpha") { - return Billboard3DOverlay::getProperty("alpha"); - } - if (property == "lineHeight") { - return _lineHeight; - } - if (property == "leftMargin") { - return _leftMargin; - } - if (property == "topMargin") { - return _topMargin; - } - if (property == "rightMargin") { - return _rightMargin; - } - if (property == "bottomMargin") { - return _bottomMargin; - } - - return Billboard3DOverlay::getProperty(property); -} - -Text3DOverlay* Text3DOverlay::createClone() const { - return new Text3DOverlay(this);; -} - -QSizeF Text3DOverlay::textSize(const QString& text) const { - auto extents = _textRenderer->computeExtent(text); - - float maxHeight = (float)_textRenderer->computeExtent("Xy").y * LINE_SCALE_RATIO; - float pointToWorldScale = (maxHeight / FIXED_FONT_SCALING_RATIO) * _lineHeight; - - return QSizeF(extents.x, extents.y) * pointToWorldScale; -} diff --git a/interface/src/ui/overlays/Text3DOverlay.h b/interface/src/ui/overlays/Text3DOverlay.h deleted file mode 100644 index 16bbdcb4c4..0000000000 --- a/interface/src/ui/overlays/Text3DOverlay.h +++ /dev/null @@ -1,78 +0,0 @@ -// -// Text3DOverlay.h -// interface/src/ui/overlays -// -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_Text3DOverlay_h -#define hifi_Text3DOverlay_h - -#include -#include -#include "Billboard3DOverlay.h" - -class TextRenderer3D; - -class Text3DOverlay : public Billboard3DOverlay { - Q_OBJECT - using Parent = Billboard3DOverlay; - -public: - static QString const TYPE; - virtual QString getType() const override { return TYPE; } - - Text3DOverlay(); - Text3DOverlay(const Text3DOverlay* text3DOverlay); - ~Text3DOverlay(); - virtual void render(RenderArgs* args) override; - - virtual const render::ShapeKey getShapeKey() override; - - // getters - const QString getText() const; - float getLineHeight() const { return _lineHeight; } - float getLeftMargin() const { return _leftMargin; } - float getTopMargin() const { return _topMargin; } - float getRightMargin() const { return _rightMargin; } - float getBottomMargin() const { return _bottomMargin; } - glm::u8vec3 getBackgroundColor(); - float getTextAlpha() { return _textAlpha; } - float getBackgroundAlpha() { return getAlpha(); } - bool isTransparent() override { return Overlay::isTransparent() || _textAlpha < 1.0f; } - - // setters - void setText(const QString& text); - void setTextAlpha(float alpha) { _textAlpha = alpha; } - void setLineHeight(float value) { _lineHeight = value; } - void setLeftMargin(float margin) { _leftMargin = margin; } - void setTopMargin(float margin) { _topMargin = margin; } - void setRightMargin(float margin) { _rightMargin = margin; } - void setBottomMargin(float margin) { _bottomMargin = margin; } - - void setProperties(const QVariantMap& properties) override; - QVariant getProperty(const QString& property) override; - - QSizeF textSize(const QString& test) const; // Meters - - virtual Text3DOverlay* createClone() const override; - -private: - TextRenderer3D* _textRenderer = nullptr; - - QString _text; - mutable QMutex _mutex; // used to make get/setText threadsafe, mutable so can be used in const functions - glm::u8vec3 _backgroundColor { 0, 0, 0 }; - float _textAlpha { 1.0f }; - float _lineHeight { 1.0f }; - float _leftMargin { 0.1f }; - float _topMargin { 0.1f }; - float _rightMargin { 0.1f }; - float _bottomMargin { 0.1f }; - int _geometryId { 0 }; -}; - -#endif // hifi_Text3DOverlay_h diff --git a/interface/src/ui/overlays/TextOverlay.cpp b/interface/src/ui/overlays/TextOverlay.cpp index e7641ab2c2..2f4353dae8 100644 --- a/interface/src/ui/overlays/TextOverlay.cpp +++ b/interface/src/ui/overlays/TextOverlay.cpp @@ -27,43 +27,12 @@ QString const TextOverlay::TYPE = "text"; QUrl const TextOverlay::URL(QString("hifi/overlays/TextOverlay.qml")); -// TextOverlay's properties are defined in the QML file specified above. -/**jsdoc - * These are the properties of a text {@link Overlays.OverlayType|OverlayType}. - * @typedef {object} Overlays.TextProperties - * - * @property {Rect} bounds - The position and size of the rectangle, in pixels. Write-only. - * @property {number} x - Integer left, x-coordinate value = bounds.x. Write-only. - * @property {number} y - Integer top, y-coordinate value = bounds.y. Write-only. - * @property {number} width - Integer width of the rectangle = bounds.width. Write-only. - * @property {number} height - Integer height of the rectangle = bounds.height. Write-only. - * - * @property {number} margin=0 - Sets the leftMargin and topMargin values, in pixels. - * Write-only. - * @property {number} leftMargin=0 - The left margin's size, in pixels. This value is also used for the right margin. - * Write-only. - * @property {number} topMargin=0 - The top margin's size, in pixels. This value is also used for the bottom margin. - * Write-only. - * @property {string} text="" - The text to display. Text does not automatically wrap; use \n for a line break. Text - * is clipped to the bounds. Write-only. - * @property {number} font.size=18 - The size of the text, in pixels. Write-only. - * @property {number} lineHeight=18 - The height of a line of text, in pixels. Write-only. - * @property {Color} color=255,255,255 - The color of the text. Synonym: textColor. Write-only. - * @property {number} alpha=1.0 - The opacity of the overlay, 0.0 - 1.0. Write-only. - * @property {Color} backgroundColor=0,0,0 - The color of the background rectangle. Write-only. - * @property {number} backgroundAlpha=0.7 - The opacity of the background rectangle. Write-only. - * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. - * Write-only. - */ - TextOverlay::TextOverlay() : QmlOverlay(URL) { } TextOverlay::TextOverlay(const TextOverlay* textOverlay) : QmlOverlay(URL, textOverlay) { } -TextOverlay::~TextOverlay() { } - TextOverlay* TextOverlay::createClone() const { return new TextOverlay(this); } @@ -80,10 +49,4 @@ QSizeF TextOverlay::textSize(const QString& text) const { QFontMetrics fm(font); QSizeF result = QSizeF(fm.width(text), 18 * lines); return result; -} - - -void TextOverlay::setTopMargin(float margin) {} -void TextOverlay::setLeftMargin(float margin) {} -void TextOverlay::setFontSize(float size) {} -void TextOverlay::setText(const QString& text) {} +} \ No newline at end of file diff --git a/interface/src/ui/overlays/TextOverlay.h b/interface/src/ui/overlays/TextOverlay.h index 53c1805345..e78e2afa04 100644 --- a/interface/src/ui/overlays/TextOverlay.h +++ b/interface/src/ui/overlays/TextOverlay.h @@ -19,16 +19,8 @@ public: QString getType() const override { return TYPE; } static QUrl const URL; - TextOverlay(); TextOverlay(const TextOverlay* textOverlay); - ~TextOverlay(); - - void setTopMargin(float margin); - void setLeftMargin(float margin); - void setFontSize(float size); - void setText(const QString& text); - TextOverlay* createClone() const override; QSizeF textSize(const QString& text) const; // Pixels diff --git a/interface/src/ui/overlays/Volume3DOverlay.cpp b/interface/src/ui/overlays/Volume3DOverlay.cpp deleted file mode 100644 index 0cceb44a36..0000000000 --- a/interface/src/ui/overlays/Volume3DOverlay.cpp +++ /dev/null @@ -1,117 +0,0 @@ -// -// Volume3DOverlay.cpp -// interface/src/ui/overlays -// -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "Volume3DOverlay.h" - -#include -#include - -Volume3DOverlay::Volume3DOverlay(const Volume3DOverlay* volume3DOverlay) : - Base3DOverlay(volume3DOverlay), - _localBoundingBox(volume3DOverlay->_localBoundingBox) -{ -} - -AABox Volume3DOverlay::getBounds() const { - AABox bounds = _localBoundingBox; - bounds.transform(getTransform()); - return bounds; -} - -void Volume3DOverlay::setDimensions(const glm::vec3& value) { - _localBoundingBox.setBox(-value / 2.0f, value); - notifyRenderVariableChange(); -} - -void Volume3DOverlay::setProperties(const QVariantMap& properties) { - Base3DOverlay::setProperties(properties); - - auto dimensions = properties["dimensions"]; - - // if "dimensions" property was not there, check to see if they included aliases: scale, size - if (!dimensions.isValid()) { - dimensions = properties["scale"]; - if (!dimensions.isValid()) { - dimensions = properties["size"]; - } - } - - if (dimensions.isValid()) { - glm::vec3 scale = vec3FromVariant(dimensions); - // don't allow a zero or negative dimension component to reach the renderTransform - const float MIN_DIMENSION = 0.0001f; - scale = glm::max(scale, MIN_DIMENSION); - setDimensions(scale); - } -} - -// JSDoc for copying to @typedefs of overlay types that inherit Volume3DOverlay. -/**jsdoc - * @typedef - * @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: scale, size. - */ -QVariant Volume3DOverlay::getProperty(const QString& property) { - if (property == "dimensions" || property == "scale" || property == "size") { - return vec3toVariant(getDimensions()); - } - - return Base3DOverlay::getProperty(property); -} - -bool Volume3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) { - // extents is the entity relative, scaled, centered extents of the entity - glm::mat4 worldToEntityMatrix; - Transform transform = getTransform(); - transform.setScale(1.0f); // ignore any inherited scale from SpatiallyNestable - transform.getInverseMatrix(worldToEntityMatrix); - - glm::vec3 overlayFrameOrigin = glm::vec3(worldToEntityMatrix * glm::vec4(origin, 1.0f)); - glm::vec3 overlayFrameDirection = glm::vec3(worldToEntityMatrix * glm::vec4(direction, 0.0f)); - - // we can use the AABox's ray intersection by mapping our origin and direction into the overlays frame - // and testing intersection there. - bool hit = _localBoundingBox.findRayIntersection(overlayFrameOrigin, overlayFrameDirection, 1.0f / overlayFrameDirection, distance, face, surfaceNormal); - - if (hit) { - surfaceNormal = transform.getRotation() * surfaceNormal; - } - return hit; -} - -bool Volume3DOverlay::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, - float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) { - // extents is the entity relative, scaled, centered extents of the entity - glm::mat4 worldToEntityMatrix; - Transform transform = getTransform(); - transform.setScale(1.0f); // ignore any inherited scale from SpatiallyNestable - transform.getInverseMatrix(worldToEntityMatrix); - - glm::vec3 overlayFrameOrigin = glm::vec3(worldToEntityMatrix * glm::vec4(origin, 1.0f)); - glm::vec3 overlayFrameVelocity = glm::vec3(worldToEntityMatrix * glm::vec4(velocity, 0.0f)); - glm::vec3 overlayFrameAcceleration = glm::vec3(worldToEntityMatrix * glm::vec4(acceleration, 0.0f)); - - // we can use the AABox's ray intersection by mapping our origin and direction into the overlays frame - // and testing intersection there. - bool hit = _localBoundingBox.findParabolaIntersection(overlayFrameOrigin, overlayFrameVelocity, overlayFrameAcceleration, parabolicDistance, face, surfaceNormal); - - if (hit) { - surfaceNormal = transform.getRotation() * surfaceNormal; - } - return hit; -} - -Transform Volume3DOverlay::evalRenderTransform() { - Transform transform = getTransform(); -#ifndef USE_SN_SCALE - transform.setScale(1.0f); // ignore any inherited scale from SpatiallyNestable -#endif - return transform; -} diff --git a/interface/src/ui/overlays/Volume3DOverlay.h b/interface/src/ui/overlays/Volume3DOverlay.h deleted file mode 100644 index 2083f7344a..0000000000 --- a/interface/src/ui/overlays/Volume3DOverlay.h +++ /dev/null @@ -1,45 +0,0 @@ -// -// Volume3DOverlay.h -// interface/src/ui/overlays -// -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_Volume3DOverlay_h -#define hifi_Volume3DOverlay_h - -#include "Base3DOverlay.h" - -class Volume3DOverlay : public Base3DOverlay { - Q_OBJECT - using Parent = Base3DOverlay; - -public: - Volume3DOverlay() {} - Volume3DOverlay(const Volume3DOverlay* volume3DOverlay); - - virtual AABox getBounds() const override; - - const glm::vec3& getDimensions() const { return _localBoundingBox.getDimensions(); } - void setDimensions(const glm::vec3& value); - - void setProperties(const QVariantMap& properties) override; - QVariant getProperty(const QString& property) override; - - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override; - virtual bool findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, float& parabolicDistance, - BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override; - -protected: - // Centered local bounding box - AABox _localBoundingBox { vec3(-0.5), 1.0f }; - - Transform evalRenderTransform() override; -}; - - -#endif // hifi_Volume3DOverlay_h diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp deleted file mode 100644 index eb61ca7281..0000000000 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ /dev/null @@ -1,490 +0,0 @@ -// -// Web3DOverlay.cpp -// -// Created by Clement on 7/1/14. -// Modified and renamed by Zander Otavka on 8/4/15 -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "Web3DOverlay.h" - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "scripting/HMDScriptingInterface.h" -#include "scripting/AssetMappingsScriptingInterface.h" -#include "scripting/MenuScriptingInterface.h" -#include "scripting/SettingsScriptingInterface.h" -#include "scripting/KeyboardScriptingInterface.h" -#include -#include -#include -#include "FileDialogHelper.h" -#include "avatar/AvatarManager.h" -#include "AudioClient.h" -#include "LODManager.h" -#include "ui/LoginDialog.h" -#include "ui/OctreeStatsProvider.h" -#include "ui/DomainConnectionModel.h" -#include "ui/AvatarInputs.h" -#include "avatar/AvatarManager.h" -#include "scripting/AccountServicesScriptingInterface.h" -#include "scripting/WalletScriptingInterface.h" -#include -#include "ui/Snapshot.h" -#include "SoundCacheScriptingInterface.h" -#include "raypick/PointerScriptingInterface.h" -#include -#include "AboutUtil.h" -#include "ResourceRequestObserver.h" - -#include - -static int MAX_WINDOW_SIZE = 4096; -static const float METERS_TO_INCHES = 39.3701f; -static const float OPAQUE_ALPHA_THRESHOLD = 0.99f; - -const QString Web3DOverlay::TYPE = "web3d"; - -Web3DOverlay::Web3DOverlay() { - _touchDevice.setCapabilities(QTouchDevice::Position); - _touchDevice.setType(QTouchDevice::TouchScreen); - _touchDevice.setName("Web3DOverlayTouchDevice"); - _touchDevice.setMaximumTouchPoints(4); - - _geometryId = DependencyManager::get()->allocateID(); - connect(this, &Web3DOverlay::requestWebSurface, this, &Web3DOverlay::buildWebSurface); - connect(this, &Web3DOverlay::resizeWebSurface, this, &Web3DOverlay::onResizeWebSurface); - - buildWebSurface(true); -} - -Web3DOverlay::Web3DOverlay(const Web3DOverlay* Web3DOverlay) : - Billboard3DOverlay(Web3DOverlay), - _url(Web3DOverlay->_url), - _scriptURL(Web3DOverlay->_scriptURL), - _dpi(Web3DOverlay->_dpi) -{ - _geometryId = DependencyManager::get()->allocateID(); -} - -Web3DOverlay::~Web3DOverlay() { - destroyWebSurface(); - auto geometryCache = DependencyManager::get(); - if (geometryCache) { - geometryCache->releaseID(_geometryId); - } -} - -void Web3DOverlay::rebuildWebSurface() { - destroyWebSurface(); - buildWebSurface(); -} - -void Web3DOverlay::destroyWebSurface() { - if (_webSurface) { - render::entities::WebEntityRenderer::releaseWebSurface(_webSurface, _cachedWebSurface, _connections); - } -} - -void Web3DOverlay::buildWebSurface(bool overrideWeb) { - if (_webSurface) { - return; - } - - render::entities::WebEntityRenderer::acquireWebSurface(_url, overrideWeb || isWebContent(), _webSurface, _cachedWebSurface); - onResizeWebSurface(); - _webSurface->resume(); - - _connections.push_back(QObject::connect(this, &Web3DOverlay::scriptEventReceived, _webSurface.data(), &OffscreenQmlSurface::emitScriptEvent)); - _connections.push_back(QObject::connect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, this, &Web3DOverlay::webEventReceived)); -} - -void Web3DOverlay::update(float deltatime) { - if (_webSurface) { - // update globalPosition - _webSurface->getSurfaceContext()->setContextProperty("globalPosition", vec3toVariant(getWorldPosition())); - } - Parent::update(deltatime); -} - -bool Web3DOverlay::isWebContent() const { - QUrl sourceUrl(_url); - if (sourceUrl.scheme() == "http" || sourceUrl.scheme() == "https" || - _url.toLower().endsWith(".htm") || _url.toLower().endsWith(".html")) { - return true; - } - return false; -} - -void Web3DOverlay::setMaxFPS(uint8_t maxFPS) { - // FIXME, the max FPS could be better managed by being dynamic (based on the number of current surfaces and the current rendering load) - _desiredMaxFPS = maxFPS; - if (_webSurface) { - _webSurface->setMaxFps(_desiredMaxFPS); - _currentMaxFPS = _desiredMaxFPS; - } -} - -void Web3DOverlay::onResizeWebSurface() { - glm::vec2 dims = glm::vec2(getDimensions()); - dims *= METERS_TO_INCHES * _dpi; - - // ensure no side is never larger then MAX_WINDOW_SIZE - float max = (dims.x > dims.y) ? dims.x : dims.y; - if (max > MAX_WINDOW_SIZE) { - dims *= MAX_WINDOW_SIZE / max; - } - - _webSurface->resize(QSize(dims.x, dims.y)); -} - -void Web3DOverlay::render(RenderArgs* args) { - if (!_renderVisible || !getParentVisible()) { - return; - } - - if (!_webSurface) { - emit requestWebSurface(false); - return; - } - - if (_mayNeedResize) { - emit resizeWebSurface(); - } - - if (_currentMaxFPS != _desiredMaxFPS) { - setMaxFPS(_desiredMaxFPS); - } - - vec4 color(toGlm(getColor()), getAlpha()); - - if (!_texture) { - _texture = gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda()); - _texture->setSource(__FUNCTION__); - } - OffscreenQmlSurface::TextureAndFence newTextureAndFence; - bool newTextureAvailable = _webSurface->fetchTexture(newTextureAndFence); - if (newTextureAvailable) { - _texture->setExternalTexture(newTextureAndFence.first, newTextureAndFence.second); - } - - Q_ASSERT(args->_batch); - gpu::Batch& batch = *args->_batch; - batch.setResourceTexture(0, _texture); - - auto renderTransform = getRenderTransform(); - auto size = renderTransform.getScale(); - renderTransform.setScale(1.0f); - batch.setModelTransform(renderTransform); - - // Turn off jitter for these entities - batch.pushProjectionJitter(); - - auto geometryCache = DependencyManager::get(); - if (color.a < OPAQUE_ALPHA_THRESHOLD) { - geometryCache->bindWebBrowserProgram(batch, true); - } else { - geometryCache->bindWebBrowserProgram(batch); - } - vec2 halfSize = vec2(size.x, size.y) / 2.0f; - geometryCache->renderQuad(batch, halfSize * -1.0f, halfSize, vec2(0), vec2(1), color, _geometryId); - batch.popProjectionJitter(); // Restore jitter - batch.setResourceTexture(0, nullptr); // restore default white color after me - -} - -Transform Web3DOverlay::evalRenderTransform() { - Transform transform = Parent::evalRenderTransform(); - transform.setScale(1.0f); - transform.postScale(glm::vec3(getDimensions(), 1.0f)); - return transform; -} - -const render::ShapeKey Web3DOverlay::getShapeKey() { - auto builder = render::ShapeKey::Builder().withoutCullFace().withDepthBias().withOwnPipeline(); - if (isTransparent()) { - builder.withTranslucent(); - } - return builder.build(); -} - -QObject* Web3DOverlay::getEventHandler() { - if (!_webSurface) { - return nullptr; - } - return _webSurface->getEventHandler(); -} - -void Web3DOverlay::setProxyWindow(QWindow* proxyWindow) { - if (!_webSurface) { - return; - } - - _webSurface->setProxyWindow(proxyWindow); -} - -void Web3DOverlay::hoverEnterOverlay(const PointerEvent& event) { - if (_inputMode == Mouse) { - handlePointerEvent(event); - } else if (_webSurface) { - PointerEvent webEvent = event; - webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _dpi)); - _webSurface->hoverBeginEvent(webEvent, _touchDevice); - } -} - -void Web3DOverlay::hoverLeaveOverlay(const PointerEvent& event) { - if (_inputMode == Mouse) { - PointerEvent endEvent(PointerEvent::Release, event.getID(), event.getPos2D(), event.getPos3D(), event.getNormal(), event.getDirection(), - event.getButton(), event.getButtons(), event.getKeyboardModifiers()); - handlePointerEvent(endEvent); - // QML onReleased is only triggered if a click has happened first. We need to send this "fake" mouse move event to properly trigger an onExited. - PointerEvent endMoveEvent(PointerEvent::Move, event.getID()); - handlePointerEvent(endMoveEvent); - } else if (_webSurface) { - PointerEvent webEvent = event; - webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _dpi)); - _webSurface->hoverEndEvent(webEvent, _touchDevice); - } -} - -void Web3DOverlay::handlePointerEvent(const PointerEvent& event) { - if (_inputMode == Touch) { - handlePointerEventAsTouch(event); - } else { - handlePointerEventAsMouse(event); - } -} - -void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) { - if (_webSurface) { - PointerEvent webEvent = event; - webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _dpi)); - _webSurface->handlePointerEvent(webEvent, _touchDevice); - } -} - -void Web3DOverlay::handlePointerEventAsMouse(const PointerEvent& event) { - if (!_webSurface) { - return; - } - - glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _dpi); - QPointF windowPoint(windowPos.x, windowPos.y); - - Qt::MouseButtons buttons = Qt::NoButton; - if (event.getButtons() & PointerEvent::PrimaryButton) { - buttons |= Qt::LeftButton; - } - - Qt::MouseButton button = Qt::NoButton; - if (event.getButton() == PointerEvent::PrimaryButton) { - button = Qt::LeftButton; - } - - QEvent::Type type; - switch (event.getType()) { - case PointerEvent::Press: - type = QEvent::MouseButtonPress; - break; - case PointerEvent::Release: - type = QEvent::MouseButtonRelease; - break; - case PointerEvent::Move: - type = QEvent::MouseMove; - break; - default: - return; - } - - QMouseEvent mouseEvent(type, windowPoint, windowPoint, windowPoint, button, buttons, event.getKeyboardModifiers()); - QCoreApplication::sendEvent(_webSurface->getWindow(), &mouseEvent); -} - -void Web3DOverlay::setProperties(const QVariantMap& properties) { - Billboard3DOverlay::setProperties(properties); - - auto urlValue = properties["url"]; - if (urlValue.isValid()) { - QString newURL = urlValue.toString(); - if (newURL != _url) { - setURL(newURL); - } - } - - auto scriptURLValue = properties["scriptURL"]; - if (scriptURLValue.isValid()) { - QString newScriptURL = scriptURLValue.toString(); - if (newScriptURL != _scriptURL) { - setScriptURL(newScriptURL); - } - } - - auto dpi = properties["dpi"]; - if (dpi.isValid()) { - _dpi = dpi.toFloat(); - _mayNeedResize = true; - } - - auto maxFPS = properties["maxFPS"]; - if (maxFPS.isValid()) { - _desiredMaxFPS = maxFPS.toInt(); - } - - auto inputModeValue = properties["inputMode"]; - if (inputModeValue.isValid()) { - QString inputModeStr = inputModeValue.toString(); - if (inputModeStr == "Mouse") { - _inputMode = Mouse; - } else { - _inputMode = Touch; - } - } -} - -// Web3DOverlay overrides the meaning of Planar3DOverlay's dimensions property. -/**jsdoc - * These are the properties of a web3d {@link Overlays.OverlayType|OverlayType}. - * @typedef {object} Overlays.Web3DProperties - * - * @property {string} type=web3d - Has the value "web3d". Read-only. - * @property {Color} color=255,255,255 - The color of the overlay. - * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. - * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. - * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. - * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from - * pulseMin to pulseMax, then pulseMax to pulseMin in one period. - * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 - * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise - * used.) - * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the - * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 - * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise - * used.) - * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. - * - * @property {string} name="" - A friendly name for the overlay. - * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and - * start. - * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a - * parentID set, otherwise the same value as position. - * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. - * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a - * parentID set, otherwise the same value as rotation. - * @property {boolean} isSolid=false - Synonyms: solid, isFilled, and filled. - * Antonyms: isWire and wire. - * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: - * dashed. Deprecated. - * @property {boolean} ignorePickIntersection=false - If true, picks ignore the overlay. ignoreRayIntersection is a synonym. - * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't - * have drawInFront set to true, and in front of entities. - * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. - * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. - * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if - * parentID is an avatar skeleton. A value of 65535 means "no joint". - * - * @property {boolean} isFacingAvatar - If true, the overlay is rotated to face the user's camera about an axis - * parallel to the user's avatar's "up" direction. - * - * @property {string} url - The URL of the Web page to display. - * @property {string} scriptURL="" - The URL of a JavaScript file to inject into the Web page. - * @property {number} dpi=30 - The dots per inch to display the Web page at, on the overlay. - * @property {Vec2} dimensions=1,1 - The size of the overlay to display the Web page on, in meters. Synonyms: - * scale, size. - * @property {number} maxFPS=10 - The maximum update rate for the Web overlay content, in frames/second. - * @property {string} inputMode=Touch - The user input mode to use - either "Touch" or "Mouse". - */ -QVariant Web3DOverlay::getProperty(const QString& property) { - if (property == "url") { - return _url; - } - if (property == "scriptURL") { - return _scriptURL; - } - if (property == "dpi") { - return _dpi; - } - if (property == "maxFPS") { - return _desiredMaxFPS; - } - - if (property == "inputMode") { - if (_inputMode == Mouse) { - return QVariant("Mouse"); - } else { - return QVariant("Touch"); - } - } - return Billboard3DOverlay::getProperty(property); -} - -void Web3DOverlay::setURL(const QString& url) { - if (url != _url) { - bool wasWebContent = isWebContent(); - _url = url; - if (_webSurface) { - if (wasWebContent && isWebContent()) { - // If we're just targeting a new web URL, then switch to that without messing around - // with the underlying QML - AbstractViewStateInterface::instance()->postLambdaEvent([this] { - _webSurface->getRootItem()->setProperty(render::entities::WebEntityRenderer::URL_PROPERTY, _url); - _webSurface->getRootItem()->setProperty("scriptURL", _scriptURL); - }); - } else { - // If we're switching to or from web content, or between different QML content - // we need to destroy and rebuild the entire QML surface - AbstractViewStateInterface::instance()->postLambdaEvent([this] { - rebuildWebSurface(); - }); - } - } - } -} - -void Web3DOverlay::setScriptURL(const QString& scriptURL) { - _scriptURL = scriptURL; - if (_webSurface) { - AbstractViewStateInterface::instance()->postLambdaEvent([this, scriptURL] { - if (!_webSurface) { - return; - } - _webSurface->getRootItem()->setProperty("scriptURL", scriptURL); - }); - } -} - -Web3DOverlay* Web3DOverlay::createClone() const { - return new Web3DOverlay(this); -} - -void Web3DOverlay::emitScriptEvent(const QVariant& message) { - QMetaObject::invokeMethod(this, "scriptEventReceived", Q_ARG(QVariant, message)); -} diff --git a/interface/src/ui/overlays/Web3DOverlay.h b/interface/src/ui/overlays/Web3DOverlay.h deleted file mode 100644 index 4265c35699..0000000000 --- a/interface/src/ui/overlays/Web3DOverlay.h +++ /dev/null @@ -1,99 +0,0 @@ -// -// Created by Bradley Austin Davis on 2015/08/31 -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_Web3DOverlay_h -#define hifi_Web3DOverlay_h - -#include "Billboard3DOverlay.h" - -#include - -#include - -class OffscreenQmlSurface; - -class Web3DOverlay : public Billboard3DOverlay { - Q_OBJECT - using Parent = Billboard3DOverlay; - -public: - static QString const TYPE; - virtual QString getType() const override { return TYPE; } - - Web3DOverlay(); - Web3DOverlay(const Web3DOverlay* Web3DOverlay); - virtual ~Web3DOverlay(); - - void setMaxFPS(uint8_t maxFPS); - virtual void render(RenderArgs* args) override; - virtual const render::ShapeKey getShapeKey() override; - - virtual void update(float deltatime) override; - - QObject* getEventHandler(); - void setProxyWindow(QWindow* proxyWindow); - Q_INVOKABLE void hoverEnterOverlay(const PointerEvent& event); - Q_INVOKABLE void hoverLeaveOverlay(const PointerEvent& event); - Q_INVOKABLE void handlePointerEvent(const PointerEvent& event); - void handlePointerEventAsTouch(const PointerEvent& event); - void handlePointerEventAsMouse(const PointerEvent& event); - - // setters - void setURL(const QString& url); - void setScriptURL(const QString& script); - - void setProperties(const QVariantMap& properties) override; - QVariant getProperty(const QString& property) override; - - virtual Web3DOverlay* createClone() const override; - - enum InputMode { - Touch, - Mouse - }; - - void buildWebSurface(bool overrideWeb = false); - void destroyWebSurface(); - void onResizeWebSurface(); - -public slots: - void emitScriptEvent(const QVariant& scriptMessage); - -signals: - void scriptEventReceived(const QVariant& message); - void webEventReceived(const QVariant& message); - void resizeWebSurface(); - void requestWebSurface(bool overrideWeb); - -protected: - Transform evalRenderTransform() override; - -private: - void rebuildWebSurface(); - bool isWebContent() const; - - InputMode _inputMode { Touch }; - QSharedPointer _webSurface; - bool _cachedWebSurface { false }; - gpu::TexturePointer _texture; - QString _url; - QString _scriptURL; - float _dpi { 30.0f }; - int _geometryId { 0 }; - - QTouchDevice _touchDevice; - - uint8_t _desiredMaxFPS { 10 }; - uint8_t _currentMaxFPS { 0 }; - - bool _mayNeedResize { false }; - - std::vector _connections; -}; - -#endif // hifi_Web3DOverlay_h diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 71b876ff8c..1adc04ee1b 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -140,10 +140,10 @@ void AnimClip::copyFromNetworkAnim() { postRot = animSkeleton.getPostRotationPose(animJoint); // cancel out scale - preRot.scale() = 1.0f; - postRot.scale() = 1.0f; + preRot.scale() = glm::vec3(1.0f); + postRot.scale() = glm::vec3(1.0f); - AnimPose rot(1.0f, hfmAnimRot, glm::vec3()); + AnimPose rot(glm::vec3(1.0f), hfmAnimRot, glm::vec3()); // adjust translation offsets, so large translation animatons on the reference skeleton // will be adjusted when played on a skeleton with short limbs. @@ -155,7 +155,7 @@ void AnimClip::copyFromNetworkAnim() { boneLengthScale = glm::length(relDefaultPose.trans()) / glm::length(hfmZeroTrans); } - AnimPose trans = AnimPose(1.0f, glm::quat(), relDefaultPose.trans() + boneLengthScale * (hfmAnimTrans - hfmZeroTrans)); + AnimPose trans = AnimPose(glm::vec3(1.0f), glm::quat(), relDefaultPose.trans() + boneLengthScale * (hfmAnimTrans - hfmZeroTrans)); _anim[frame][skeletonJoint] = trans * preRot * rot * postRot; } diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 7af9e81889..a1809f3438 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -552,7 +552,7 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const AnimPose accum = absolutePoses[_hipsIndex]; AnimPose baseParentPose = absolutePoses[_hipsIndex]; for (int i = (int)chainDepth - 1; i >= 0; i--) { - accum = accum * AnimPose(1.0f, jointChainInfoOut.jointInfoVec[i].rot, jointChainInfoOut.jointInfoVec[i].trans); + accum = accum * AnimPose(glm::vec3(1.0f), jointChainInfoOut.jointInfoVec[i].rot, jointChainInfoOut.jointInfoVec[i].trans); postAbsPoses[i] = accum; if (jointChainInfoOut.jointInfoVec[i].jointIndex == topJointIndex) { topChainIndex = i; @@ -734,7 +734,7 @@ void AnimInverseKinematics::computeAndCacheSplineJointInfosForIKTarget(const Ani glm::mat3 m(u, v, glm::cross(u, v)); glm::quat rot = glm::normalize(glm::quat_cast(m)); - AnimPose pose(1.0f, rot, spline(t)); + AnimPose pose(glm::vec3(1.0f), rot, spline(t)); AnimPose offsetPose = pose.inverse() * defaultPose; SplineJointInfo splineJointInfo = { index, ratio, offsetPose }; @@ -767,7 +767,7 @@ void AnimInverseKinematics::solveTargetWithSpline(const AnimContext& context, co const int baseIndex = _hipsIndex; // build spline from tip to base - AnimPose tipPose = AnimPose(1.0f, target.getRotation(), target.getTranslation()); + AnimPose tipPose = AnimPose(glm::vec3(1.0f), target.getRotation(), target.getTranslation()); AnimPose basePose = absolutePoses[baseIndex]; CubicHermiteSplineFunctorWithArcLength spline; if (target.getIndex() == _headIndex) { @@ -815,7 +815,7 @@ void AnimInverseKinematics::solveTargetWithSpline(const AnimContext& context, co glm::mat3 m(u, v, glm::cross(u, v)); glm::quat rot = glm::normalize(glm::quat_cast(m)); - AnimPose desiredAbsPose = AnimPose(1.0f, rot, trans) * splineJointInfo.offsetPose; + AnimPose desiredAbsPose = AnimPose(glm::vec3(1.0f), rot, trans) * splineJointInfo.offsetPose; // apply flex coefficent AnimPose flexedAbsPose; @@ -965,7 +965,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars } _relativePoses[_hipsIndex] = parentAbsPose.inverse() * absPose; - _relativePoses[_hipsIndex].scale() = 1.0f; + _relativePoses[_hipsIndex].scale() = glm::vec3(1.0f); } // if there is an active jointChainInfo for the hips store the post shifted hips into it. @@ -1753,7 +1753,7 @@ void AnimInverseKinematics::setSecondaryTargets(const AnimContext& context) { AnimPose rigToGeometryPose = AnimPose(glm::inverse(context.getGeometryToRigMatrix())); for (auto& iter : _secondaryTargetsInRigFrame) { AnimPose absPose = rigToGeometryPose * iter.second; - absPose.scale() = 1.0f; + absPose.scale() = glm::vec3(1.0f); AnimPose parentAbsPose; int parentIndex = _skeleton->getParentIndex(iter.first); @@ -1825,7 +1825,7 @@ void AnimInverseKinematics::debugDrawSpineSplines(const AnimContext& context, co const int baseIndex = _hipsIndex; // build spline - AnimPose tipPose = AnimPose(1.0f, target.getRotation(), target.getTranslation()); + AnimPose tipPose = AnimPose(glm::vec3(1.0f), target.getRotation(), target.getTranslation()); AnimPose basePose = _skeleton->getAbsolutePose(baseIndex, _relativePoses); CubicHermiteSplineFunctorWithArcLength spline; diff --git a/libraries/animation/src/AnimManipulator.cpp b/libraries/animation/src/AnimManipulator.cpp index c75c9865bb..1146cbb19a 100644 --- a/libraries/animation/src/AnimManipulator.cpp +++ b/libraries/animation/src/AnimManipulator.cpp @@ -172,5 +172,5 @@ AnimPose AnimManipulator::computeRelativePoseFromJointVar(const AnimVariantMap& break; } - return AnimPose(1.0f, relRot, relTrans); + return AnimPose(glm::vec3(1), relRot, relTrans); } diff --git a/libraries/animation/src/AnimPoleVectorConstraint.cpp b/libraries/animation/src/AnimPoleVectorConstraint.cpp index 96e4e67261..f017fe2348 100644 --- a/libraries/animation/src/AnimPoleVectorConstraint.cpp +++ b/libraries/animation/src/AnimPoleVectorConstraint.cpp @@ -95,7 +95,7 @@ const AnimPoseVec& AnimPoleVectorConstraint::evaluate(const AnimVariantMap& anim AnimPose tipPose = ikChain.getAbsolutePoseFromJointIndex(_tipJointIndex); // Look up refVector from animVars, make sure to convert into geom space. - glm::vec3 refVector = midPose.xformVector(_referenceVector); + glm::vec3 refVector = midPose.xformVectorFast(_referenceVector); float refVectorLength = glm::length(refVector); glm::vec3 axis = basePose.trans() - tipPose.trans(); diff --git a/libraries/animation/src/AnimPose.cpp b/libraries/animation/src/AnimPose.cpp index 366d863c3d..d77514e691 100644 --- a/libraries/animation/src/AnimPose.cpp +++ b/libraries/animation/src/AnimPose.cpp @@ -14,14 +14,15 @@ #include #include "AnimUtil.h" -const AnimPose AnimPose::identity = AnimPose(1.0f, glm::quat(), glm::vec3(0.0f)); +const AnimPose AnimPose::identity = AnimPose(glm::vec3(1.0f), + glm::quat(), + glm::vec3(0.0f)); AnimPose::AnimPose(const glm::mat4& mat) { static const float EPSILON = 0.0001f; - glm::vec3 scale = extractScale(mat); + _scale = extractScale(mat); // quat_cast doesn't work so well with scaled matrices, so cancel it out. - glm::mat4 tmp = glm::scale(mat, 1.0f / scale); - _scale = extractUniformScale(scale); + glm::mat4 tmp = glm::scale(mat, 1.0f / _scale); _rot = glm::quat_cast(tmp); float lengthSquared = glm::length2(_rot); if (glm::abs(lengthSquared - 1.0f) > EPSILON) { @@ -39,22 +40,29 @@ glm::vec3 AnimPose::xformPoint(const glm::vec3& rhs) const { return *this * rhs; } +// really slow, but accurate for transforms with non-uniform scale glm::vec3 AnimPose::xformVector(const glm::vec3& rhs) const { + glm::vec3 xAxis = _rot * glm::vec3(_scale.x, 0.0f, 0.0f); + glm::vec3 yAxis = _rot * glm::vec3(0.0f, _scale.y, 0.0f); + glm::vec3 zAxis = _rot * glm::vec3(0.0f, 0.0f, _scale.z); + glm::mat3 mat(xAxis, yAxis, zAxis); + glm::mat3 transInvMat = glm::inverse(glm::transpose(mat)); + return transInvMat * rhs; +} + +// faster, but does not handle non-uniform scale correctly. +glm::vec3 AnimPose::xformVectorFast(const glm::vec3& rhs) const { return _rot * (_scale * rhs); } AnimPose AnimPose::operator*(const AnimPose& rhs) const { - float scale = _scale * rhs._scale; - glm::quat rot = _rot * rhs._rot; - glm::vec3 trans = _trans + (_rot * (_scale * rhs._trans)); - return AnimPose(scale, rot, trans); + glm::mat4 result; + glm_mat4u_mul(*this, rhs, result); + return AnimPose(result); } AnimPose AnimPose::inverse() const { - float invScale = 1.0f / _scale; - glm::quat invRot = glm::inverse(_rot); - glm::vec3 invTrans = invScale * (invRot * -_trans); - return AnimPose(invScale, invRot, invTrans); + return AnimPose(glm::inverse(static_cast(*this))); } // mirror about x-axis without applying negative scale. @@ -63,10 +71,11 @@ AnimPose AnimPose::mirror() const { } AnimPose::operator glm::mat4() const { - glm::vec3 xAxis = _rot * glm::vec3(_scale, 0.0f, 0.0f); - glm::vec3 yAxis = _rot * glm::vec3(0.0f, _scale, 0.0f); - glm::vec3 zAxis = _rot * glm::vec3(0.0f, 0.0f, _scale); - return glm::mat4(glm::vec4(xAxis, 0.0f), glm::vec4(yAxis, 0.0f), glm::vec4(zAxis, 0.0f), glm::vec4(_trans, 1.0f)); + glm::vec3 xAxis = _rot * glm::vec3(_scale.x, 0.0f, 0.0f); + glm::vec3 yAxis = _rot * glm::vec3(0.0f, _scale.y, 0.0f); + glm::vec3 zAxis = _rot * glm::vec3(0.0f, 0.0f, _scale.z); + return glm::mat4(glm::vec4(xAxis, 0.0f), glm::vec4(yAxis, 0.0f), + glm::vec4(zAxis, 0.0f), glm::vec4(_trans, 1.0f)); } void AnimPose::blend(const AnimPose& srcPose, float alpha) { diff --git a/libraries/animation/src/AnimPose.h b/libraries/animation/src/AnimPose.h index 4d6dee1987..fb693dc31d 100644 --- a/libraries/animation/src/AnimPose.h +++ b/libraries/animation/src/AnimPose.h @@ -21,13 +21,16 @@ class AnimPose { public: AnimPose() {} explicit AnimPose(const glm::mat4& mat); - explicit AnimPose(const glm::quat& rotIn) : _rot(rotIn), _trans(0.0f), _scale(1.0f) {} - AnimPose(const glm::quat& rotIn, const glm::vec3& transIn) : _rot(rotIn), _trans(transIn), _scale(1.0f) {} - AnimPose(float scaleIn, const glm::quat& rotIn, const glm::vec3& transIn) : _rot(rotIn), _trans(transIn), _scale(scaleIn) {} + + explicit AnimPose(const glm::quat& rotIn) : _scale(1.0f), _rot(rotIn), _trans(0.0f) {} + AnimPose(const glm::quat& rotIn, const glm::vec3& transIn) : _scale(1.0f), _rot(rotIn), _trans(transIn) {} + AnimPose(const glm::vec3& scaleIn, const glm::quat& rotIn, const glm::vec3& transIn) : _scale(scaleIn), _rot(rotIn), _trans(transIn) {} + static const AnimPose identity; glm::vec3 xformPoint(const glm::vec3& rhs) const; glm::vec3 xformVector(const glm::vec3& rhs) const; // really slow, but accurate for transforms with non-uniform scale + glm::vec3 xformVectorFast(const glm::vec3& rhs) const; // faster, but does not handle non-uniform scale correctly. glm::vec3 operator*(const glm::vec3& rhs) const; // same as xformPoint AnimPose operator*(const AnimPose& rhs) const; @@ -36,8 +39,8 @@ public: AnimPose mirror() const; operator glm::mat4() const; - float scale() const { return _scale; } - float& scale() { return _scale; } + const glm::vec3& scale() const { return _scale; } + glm::vec3& scale() { return _scale; } const glm::quat& rot() const { return _rot; } glm::quat& rot() { return _rot; } @@ -49,13 +52,13 @@ public: private: friend QDebug operator<<(QDebug debug, const AnimPose& pose); + glm::vec3 _scale { 1.0f }; glm::quat _rot; glm::vec3 _trans; - float _scale { 1.0f }; // uniform scale only. }; inline QDebug operator<<(QDebug debug, const AnimPose& pose) { - debug << "AnimPose, trans = (" << pose.trans().x << pose.trans().y << pose.trans().z << "), rot = (" << pose.rot().x << pose.rot().y << pose.rot().z << pose.rot().w << "), scale =" << pose.scale(); + debug << "AnimPose, trans = (" << pose.trans().x << pose.trans().y << pose.trans().z << "), rot = (" << pose.rot().x << pose.rot().y << pose.rot().z << pose.rot().w << "), scale = (" << pose.scale().x << pose.scale().y << pose.scale().z << ")"; return debug; } diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index d09de36a14..a9c57a4a15 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1326,7 +1326,7 @@ static bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& sh } if (slabCount == (DOP14_COUNT / 2) && minDisplacementLen != FLT_MAX) { // we are within the k-dop so push the point along the minimum displacement found - displacementOut = shapePose.xformVector(minDisplacement); + displacementOut = shapePose.xformVectorFast(minDisplacement); return true; } else { // point is outside of kdop @@ -1335,7 +1335,7 @@ static bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& sh } else { // point is directly on top of shapeInfo.avgPoint. // push the point out along the x axis. - displacementOut = shapePose.xformVector(shapeInfo.points[0]); + displacementOut = shapePose.xformVectorFast(shapeInfo.points[0]); return true; } } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index daef0e411a..7d0fc3409a 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -305,11 +305,6 @@ void Avatar::setTargetScale(float targetScale) { } } -void Avatar::setAvatarEntityDataChanged(bool value) { - AvatarData::setAvatarEntityDataChanged(value); - _avatarEntityDataHashes.clear(); -} - void Avatar::removeAvatarEntitiesFromTree() { auto treeRenderer = DependencyManager::get(); EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; @@ -368,6 +363,13 @@ bool Avatar::applyGrabChanges() { target->removeGrab(grab); _avatarGrabs.erase(itr); grabAddedOrRemoved = true; + if (isMyAvatar()) { + const EntityItemPointer& entity = std::dynamic_pointer_cast(target); + if (entity && entity->getEntityHostType() == entity::HostType::AVATAR && entity->getSimulationOwner().getID() == getID()) { + EntityItemProperties properties = entity->getProperties(); + sendPacket(entity->getID(), properties); + } + } } else { undeleted.push_back(id); } @@ -1977,12 +1979,12 @@ float Avatar::getUnscaledEyeHeightFromSkeleton() const { auto& rig = _skeletonModel->getRig(); // Normally the model offset transform will contain the avatar scale factor, we explicitly remove it here. - AnimPose modelOffsetWithoutAvatarScale(1.0f, rig.getModelOffsetPose().rot(), rig.getModelOffsetPose().trans()); + AnimPose modelOffsetWithoutAvatarScale(glm::vec3(1.0f), rig.getModelOffsetPose().rot(), rig.getModelOffsetPose().trans()); AnimPose geomToRigWithoutAvatarScale = modelOffsetWithoutAvatarScale * rig.getGeometryOffsetPose(); // This factor can be used to scale distances in the geometry frame into the unscaled rig frame. // Typically it will be the unit conversion from cm to m. - float scaleFactor = geomToRigWithoutAvatarScale.scale(); + float scaleFactor = geomToRigWithoutAvatarScale.scale().x; // in practice this always a uniform scale factor. int headTopJoint = rig.indexOfJoint("HeadTop_End"); int headJoint = rig.indexOfJoint("Head"); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index b43fe012b7..06942a13d8 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -480,8 +481,6 @@ public: virtual void setModelScale(float scale) { _modelScale = scale; } virtual glm::vec3 scaleForChildren() const override { return glm::vec3(getModelScale()); } - virtual void setAvatarEntityDataChanged(bool value) override; - // Show hide the model representation of the avatar virtual void setEnableMeshVisible(bool isEnabled); virtual bool getEnableMeshVisible() const; @@ -603,6 +602,7 @@ protected: // protected methods... bool isLookingAtMe(AvatarSharedPointer avatar) const; + virtual void sendPacket(const QUuid& entityID, const EntityItemProperties& properties) const { } bool applyGrabChanges(); void relayJointDataToChildren(); @@ -647,9 +647,6 @@ protected: bool success { false }; }; - using MapOfAvatarEntityDataHashes = QMap; - MapOfAvatarEntityDataHashes _avatarEntityDataHashes; - uint64_t _lastRenderUpdateTime { 0 }; int _leftPointerGeometryID { 0 }; int _rightPointerGeometryID { 0 }; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index d071b74d6e..63396a59ac 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -1147,7 +1147,7 @@ public: */ Q_INVOKABLE virtual void setAvatarEntityData(const AvatarEntityMap& avatarEntityData); - virtual void setAvatarEntityDataChanged(bool value) { _avatarEntityDataChanged = value; } + void setAvatarEntityDataChanged(bool value) { _avatarEntityDataChanged = value; } AvatarEntityIDs getAndClearRecentlyRemovedIDs(); /**jsdoc diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index c0774c795c..c0440b8cbe 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -42,7 +42,6 @@ #include std::function EntityTreeRenderer::_entitiesShouldFadeFunction = []() { return true; }; -std::function EntityTreeRenderer::_getAvatarUpOperator = []() { return Vectors::UP; }; QString resolveScriptURL(const QString& scriptUrl) { auto normalizedScriptUrl = DependencyManager::get()->normalizeURL(scriptUrl); @@ -84,38 +83,38 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf connect(pointerManager.data(), &PointerManager::triggerEndEntity, entityScriptingInterface.data(), &EntityScriptingInterface::mouseReleaseOnEntity); // Forward mouse events to web entities - auto handlePointerEvent = [&](const EntityItemID& entityID, const PointerEvent& event) { + auto handlePointerEvent = [&](const QUuid& entityID, const PointerEvent& event) { std::shared_ptr thisEntity; auto entity = getEntity(entityID); if (entity && entity->getType() == EntityTypes::Web) { thisEntity = std::static_pointer_cast(renderableForEntityId(entityID)); } if (thisEntity) { - QMetaObject::invokeMethod(thisEntity.get(), "handlePointerEvent", Q_ARG(PointerEvent, event)); + QMetaObject::invokeMethod(thisEntity.get(), "handlePointerEvent", Q_ARG(const PointerEvent&, event)); } }; connect(entityScriptingInterface.data(), &EntityScriptingInterface::mousePressOnEntity, this, handlePointerEvent); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseReleaseOnEntity, this, handlePointerEvent); connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseMoveOnEntity, this, handlePointerEvent); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity, this, [&](const EntityItemID& entityID, const PointerEvent& event) { + connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseReleaseOnEntity, this, handlePointerEvent); + connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity, this, [&](const QUuid& entityID, const PointerEvent& event) { std::shared_ptr thisEntity; auto entity = getEntity(entityID); if (entity && entity->getType() == EntityTypes::Web) { thisEntity = std::static_pointer_cast(renderableForEntityId(entityID)); } if (thisEntity) { - QMetaObject::invokeMethod(thisEntity.get(), "hoverEnterEntity", Q_ARG(PointerEvent, event)); + QMetaObject::invokeMethod(thisEntity.get(), "hoverEnterEntity", Q_ARG(const PointerEvent&, event)); } }); connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverOverEntity, this, handlePointerEvent); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity, this, [&](const EntityItemID& entityID, const PointerEvent& event) { + connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity, this, [&](const QUuid& entityID, const PointerEvent& event) { std::shared_ptr thisEntity; auto entity = getEntity(entityID); if (entity && entity->getType() == EntityTypes::Web) { thisEntity = std::static_pointer_cast(renderableForEntityId(entityID)); } if (thisEntity) { - QMetaObject::invokeMethod(thisEntity.get(), "hoverLeaveEntity", Q_ARG(PointerEvent, event)); + QMetaObject::invokeMethod(thisEntity.get(), "hoverLeaveEntity", Q_ARG(const PointerEvent&, event)); } }); } @@ -798,11 +797,11 @@ static PointerEvent::Button toPointerButton(const QMouseEvent& event) { } } -void EntityTreeRenderer::mousePressEvent(QMouseEvent* event) { +std::pair EntityTreeRenderer::mousePressEvent(QMouseEvent* event) { // If we don't have a tree, or we're in the process of shutting down, then don't // process these events. if (!_tree || _shuttingDown) { - return; + return { FLT_MAX, UNKNOWN_ENTITY_ID }; } PerformanceTimer perfTimer("EntityTreeRenderer::mousePressEvent"); @@ -833,9 +832,10 @@ void EntityTreeRenderer::mousePressEvent(QMouseEvent* event) { _lastPointerEvent = pointerEvent; _lastPointerEventValid = true; - } else { - emit entityScriptingInterface->mousePressOffEntity(); + return { rayPickResult.distance, rayPickResult.entityID }; } + emit entityScriptingInterface->mousePressOffEntity(); + return { FLT_MAX, UNKNOWN_ENTITY_ID }; } void EntityTreeRenderer::mouseDoublePressEvent(QMouseEvent* event) { diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 00a78bf9e4..154ad08811 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -93,8 +93,8 @@ public: void reloadEntityScripts(); // event handles which may generate entity related events + std::pair mousePressEvent(QMouseEvent* event); void mouseReleaseEvent(QMouseEvent* event); - void mousePressEvent(QMouseEvent* event); void mouseDoublePressEvent(QMouseEvent* event); void mouseMoveEvent(QMouseEvent* event); @@ -118,9 +118,6 @@ public: // Access the workload Space workload::SpacePointer getWorkloadSpace() const { return _space; } - static void setGetAvatarUpOperator(std::function getAvatarUpOperator) { _getAvatarUpOperator = getAvatarUpOperator; } - static glm::vec3 getAvatarUp() { return _getAvatarUpOperator(); } - EntityEditPacketSender* getPacketSender(); static void setAddMaterialToEntityOperator(std::function addMaterialToEntityOperator) { _addMaterialToEntityOperator = addMaterialToEntityOperator; } @@ -274,6 +271,7 @@ private: static std::function _removeMaterialFromEntityOperator; static std::function _addMaterialToAvatarOperator; static std::function _removeMaterialFromAvatarOperator; + }; diff --git a/libraries/entities-renderer/src/RenderableImageEntityItem.cpp b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp index c565eb1f0a..96dd1733e7 100644 --- a/libraries/entities-renderer/src/RenderableImageEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp @@ -96,12 +96,12 @@ void ImageEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce _emissive = entity->getEmissive(); _keepAspectRatio = entity->getKeepAspectRatio(); - _billboardMode = entity->getBillboardMode(); _subImage = entity->getSubImage(); _color = entity->getColor(); _alpha = entity->getAlpha(); _pulseProperties = entity->getPulseProperties(); + _billboardMode = entity->getBillboardMode(); if (!_textureIsLoaded && _texture && _texture->isLoaded()) { _textureIsLoaded = true; @@ -118,6 +118,17 @@ void ImageEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce }); } +Item::Bound ImageEntityRenderer::getBound() { + auto bound = Parent::getBound(); + if (_billboardMode != BillboardMode::NONE) { + glm::vec3 dimensions = bound.getScale(); + float max = glm::max(dimensions.x, glm::max(dimensions.y, dimensions.z)); + const float SQRT_2 = 1.41421356237f; + bound.setScaleStayCentered(glm::vec3(SQRT_2 * max)); + } + return bound; +} + ShapeKey ImageEntityRenderer::getShapeKey() { auto builder = render::ShapeKey::Builder().withoutCullFace().withDepthBias(); if (isTransparent()) { @@ -159,27 +170,7 @@ void ImageEntityRenderer::doRender(RenderArgs* args) { Q_ASSERT(args->_batch); gpu::Batch* batch = args->_batch; - if (_billboardMode == BillboardMode::YAW) { - //rotate about vertical to face the camera - glm::vec3 dPosition = args->getViewFrustum().getPosition() - transform.getTranslation(); - // If x and z are 0, atan(x, z) is undefined, so default to 0 degrees - float yawRotation = dPosition.x == 0.0f && dPosition.z == 0.0f ? 0.0f : glm::atan(dPosition.x, dPosition.z); - glm::quat orientation = glm::quat(glm::vec3(0.0f, yawRotation, 0.0f)); - transform.setRotation(orientation); - } else if (_billboardMode == BillboardMode::FULL) { - glm::vec3 billboardPos = transform.getTranslation(); - glm::vec3 cameraPos = args->getViewFrustum().getPosition(); - // use the referencial from the avatar, y isn't always up - glm::vec3 avatarUP = EntityTreeRenderer::getAvatarUp(); - // check to see if glm::lookAt will work / using glm::lookAt variable name - glm::highp_vec3 s(glm::cross(billboardPos - cameraPos, avatarUP)); - - // make sure s is not NaN for any component - if (glm::length2(s) > 0.0f) { - glm::quat rotation(conjugate(toQuat(glm::lookAt(cameraPos, billboardPos, avatarUP)))); - transform.setRotation(rotation); - } - } + transform.setRotation(EntityItem::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode)); transform.postScale(dimensions); batch->setModelTransform(transform); diff --git a/libraries/entities-renderer/src/RenderableImageEntityItem.h b/libraries/entities-renderer/src/RenderableImageEntityItem.h index a20533cc8c..d60b38fe65 100644 --- a/libraries/entities-renderer/src/RenderableImageEntityItem.h +++ b/libraries/entities-renderer/src/RenderableImageEntityItem.h @@ -23,6 +23,7 @@ public: ~ImageEntityRenderer(); protected: + Item::Bound getBound() override; ShapeKey getShapeKey() override; bool isTransparent() const override; @@ -39,12 +40,12 @@ private: bool _emissive; bool _keepAspectRatio; - BillboardMode _billboardMode; QRect _subImage; glm::u8vec3 _color; float _alpha; PulsePropertyGroup _pulseProperties; + BillboardMode _billboardMode; glm::vec3 _dimensions; diff --git a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp index 6cc342f68a..b986702f5a 100644 --- a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp @@ -25,37 +25,43 @@ bool MaterialEntityRenderer::needsRenderUpdate() const { } bool MaterialEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const { - // Could require material re-apply - if (entity->getMaterialURL() != _materialURL) { - return true; - } - if (entity->getMaterialData() != _materialData) { - return true; - } - if (entity->getParentMaterialName() != _parentMaterialName) { - return true; - } - if (entity->getParentID() != _parentID) { - return true; - } - if (entity->getPriority() != _priority) { - return true; - } + if (resultWithReadLock([&] { + // Won't cause material re-apply + if (entity->getMaterialMappingMode() != _materialMappingMode) { + return true; + } + if (entity->getMaterialRepeat() != _materialRepeat) { + return true; + } + if (entity->getMaterialMappingPos() != _materialMappingPos || entity->getMaterialMappingScale() != _materialMappingScale || entity->getMaterialMappingRot() != _materialMappingRot) { + return true; + } + if (entity->getTransform() != _transform) { + return true; + } + if (entity->getUnscaledDimensions() != _dimensions) { + return true; + } - // Won't cause material re-apply - if (entity->getMaterialMappingMode() != _materialMappingMode) { - return true; - } - if (entity->getMaterialRepeat() != _materialRepeat) { - return true; - } - if (entity->getMaterialMappingPos() != _materialMappingPos || entity->getMaterialMappingScale() != _materialMappingScale || entity->getMaterialMappingRot() != _materialMappingRot) { - return true; - } - if (entity->getTransform() != _transform) { - return true; - } - if (entity->getUnscaledDimensions() != _dimensions) { + // Could require material re-apply + if (entity->getMaterialURL() != _materialURL) { + return true; + } + if (entity->getMaterialData() != _materialData) { + return true; + } + if (entity->getParentMaterialName() != _parentMaterialName) { + return true; + } + if (entity->getParentID() != _parentID) { + return true; + } + if (entity->getPriority() != _priority) { + return true; + } + + return false; + })) { return true; } return false; @@ -63,24 +69,86 @@ bool MaterialEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityP void MaterialEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) { withWriteLock([&] { + bool transformChanged = false; + { + MaterialMappingMode mode = entity->getMaterialMappingMode(); + if (mode != _materialMappingMode) { + _materialMappingMode = mode; + transformChanged = true; + } + } + { + bool repeat = entity->getMaterialRepeat(); + if (repeat != _materialRepeat) { + _materialRepeat = repeat; + transformChanged = true; + } + } + { + glm::vec2 mappingPos = entity->getMaterialMappingPos(); + glm::vec2 mappingScale = entity->getMaterialMappingScale(); + float mappingRot = entity->getMaterialMappingRot(); + if (mappingPos != _materialMappingPos || mappingScale != _materialMappingScale || mappingRot != _materialMappingRot) { + _materialMappingPos = mappingPos; + _materialMappingScale = mappingScale; + _materialMappingRot = mappingRot; + transformChanged |= _materialMappingMode == MaterialMappingMode::UV; + } + } + { + Transform transform = entity->getTransform(); + glm::vec3 dimensions = entity->getUnscaledDimensions(); + if (transform != _transform || dimensions != _dimensions) { + _transform = transform; + _dimensions = dimensions; + transformChanged |= _materialMappingMode == MaterialMappingMode::PROJECTED; + } + } + + auto& material = getMaterial(); + // Update the old material regardless of if it's going to change + if (transformChanged && material && !_parentID.isNull()) { + Transform textureTransform; + if (_materialMappingMode == MaterialMappingMode::UV) { + textureTransform.setTranslation(glm::vec3(_materialMappingPos, 0.0f)); + textureTransform.setRotation(glm::vec3(0.0f, 0.0f, glm::radians(_materialMappingRot))); + textureTransform.setScale(glm::vec3(_materialMappingScale, 1.0f)); + } else if (_materialMappingMode == MaterialMappingMode::PROJECTED) { + textureTransform = _transform; + textureTransform.postScale(_dimensions); + // Pass the inverse transform here so we don't need to compute it in the shaders + textureTransform.evalFromRawMatrix(textureTransform.getInverseMatrix()); + } + material->setTextureTransforms(textureTransform, _materialMappingMode, _materialRepeat); + } + + bool deleteNeeded = false; + bool addNeeded = _retryApply; + + { + QString materialURL = entity->getMaterialURL(); + if (materialURL != _materialURL) { + _materialURL = materialURL; + deleteNeeded = true; + addNeeded = true; + } + } + if (_drawMaterial != entity->getMaterial()) { _texturesLoaded = false; _drawMaterial = entity->getMaterial(); } - _parentID = entity->getParentID(); - _materialMappingPos = entity->getMaterialMappingPos(); - _materialMappingScale = entity->getMaterialMappingScale(); - _materialMappingRot = entity->getMaterialMappingRot(); - _renderTransform = getModelTransform(); - const float MATERIAL_ENTITY_SCALE = 0.5f; - _renderTransform.postScale(MATERIAL_ENTITY_SCALE); - _renderTransform.postScale(ENTITY_ITEM_DEFAULT_DIMENSIONS); bool newTexturesLoaded = _drawMaterial ? !_drawMaterial->isMissingTexture() : false; if (!_texturesLoaded && newTexturesLoaded) { _drawMaterial->checkResetOpacityMap(); } _texturesLoaded = newTexturesLoaded; + + _renderTransform = getModelTransform(); + const float MATERIAL_ENTITY_SCALE = 0.5f; + _renderTransform.postScale(MATERIAL_ENTITY_SCALE); + _renderTransform.postScale(ENTITY_ITEM_DEFAULT_DIMENSIONS); }); } diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 4ede5e5057..c139fbf320 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -104,8 +104,8 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi }); _emitting = entity->getIsEmitting(); - bool hasTexture = resultWithReadLock([&]{ return _particleProperties.textures.isEmpty(); }); - if (hasTexture) { + bool textureEmpty = resultWithReadLock([&]{ return _particleProperties.textures.isEmpty(); }); + if (textureEmpty) { if (_networkTexture) { withWriteLock([&] { _networkTexture.reset(); diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index 68371e4e13..64c05b576b 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -25,6 +25,7 @@ using namespace render; using namespace render::entities; gpu::PipelinePointer PolyLineEntityRenderer::_pipeline = nullptr; +gpu::PipelinePointer PolyLineEntityRenderer::_glowPipeline = nullptr; static const QUrl DEFAULT_POLYLINE_TEXTURE = PathUtils::resourcesUrl("images/paintStroke.png"); @@ -44,14 +45,26 @@ PolyLineEntityRenderer::PolyLineEntityRenderer(const EntityItemPointer& entity) void PolyLineEntityRenderer::buildPipeline() { // FIXME: opaque pipeline gpu::ShaderPointer program = gpu::Shader::createProgram(shader::entities_renderer::program::paintStroke); - gpu::StatePointer state = gpu::StatePointer(new gpu::State()); - state->setCullMode(gpu::State::CullMode::CULL_NONE); - state->setDepthTest(true, true, gpu::LESS_EQUAL); - PrepareStencil::testMask(*state); - state->setBlendFunction(true, - gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, - gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); - _pipeline = gpu::Pipeline::create(program, state); + { + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + state->setCullMode(gpu::State::CullMode::CULL_NONE); + state->setDepthTest(true, true, gpu::LESS_EQUAL); + PrepareStencil::testMask(*state); + state->setBlendFunction(true, + gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + _pipeline = gpu::Pipeline::create(program, state); + } + { + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + state->setCullMode(gpu::State::CullMode::CULL_NONE); + state->setDepthTest(true, false, gpu::LESS_EQUAL); + PrepareStencil::testMask(*state); + state->setBlendFunction(true, + gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + _glowPipeline = gpu::Pipeline::create(program, state); + } } ItemKey PolyLineEntityRenderer::getKey() { @@ -128,7 +141,8 @@ void PolyLineEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPo } // Data - if (faceCamera != _faceCamera || glow != _glow) { + bool faceCameraChanged = faceCamera != _faceCamera; + if (faceCameraChanged || glow != _glow) { _faceCamera = faceCamera; _glow = glow; updateData(); @@ -148,7 +162,7 @@ void PolyLineEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPo _colors = entity->getStrokeColors(); _color = toGlm(entity->getColor()); } - if (_isUVModeStretch != isUVModeStretch || pointsChanged || widthsChanged || normalsChanged || colorsChanged || textureChanged) { + if (_isUVModeStretch != isUVModeStretch || pointsChanged || widthsChanged || normalsChanged || colorsChanged || textureChanged || faceCameraChanged) { _isUVModeStretch = isUVModeStretch; updateGeometry(); } @@ -220,11 +234,17 @@ void PolyLineEntityRenderer::updateGeometry() { // For last point we can assume binormals are the same since it represents the last two vertices of quad if (i < maxNumVertices - 1) { glm::vec3 tangent = _points[i + 1] - point; - binormal = glm::normalize(glm::cross(tangent, normal)); - // Check to make sure binormal is not a NAN. If it is, don't add to vertices vector - if (binormal.x != binormal.x) { - continue; + if (_faceCamera) { + // In faceCamera mode, we actually pass the tangent, and recompute the binormal in the shader + binormal = tangent; + } else { + binormal = glm::normalize(glm::cross(tangent, normal)); + + // Check to make sure binormal is not a NAN. If it is, don't add to vertices vector + if (glm::any(glm::isnan(binormal))) { + continue; + } } } @@ -250,11 +270,11 @@ void PolyLineEntityRenderer::doRender(RenderArgs* args) { Q_ASSERT(args->_batch); gpu::Batch& batch = *args->_batch; - if (!_pipeline) { + if (!_pipeline || !_glowPipeline) { buildPipeline(); } - batch.setPipeline(_pipeline); + batch.setPipeline(_glow ? _glowPipeline : _pipeline); batch.setModelTransform(_renderTransform); batch.setResourceTexture(0, _textureLoaded ? _texture->getGPUTexture() : DependencyManager::get()->getWhiteTexture()); batch.setResourceBuffer(0, _polylineGeometryBuffer); diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h index fd37a49598..b8a3ad3b3e 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h @@ -59,6 +59,7 @@ protected: gpu::BufferPointer _polylineDataBuffer; gpu::BufferPointer _polylineGeometryBuffer; static gpu::PipelinePointer _pipeline; + static gpu::PipelinePointer _glowPipeline; }; } } // namespace diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index 5614e976b5..99912e9d91 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -23,6 +23,9 @@ using namespace render; using namespace render::entities; static const int FIXED_FONT_POINT_SIZE = 40; +const int FIXED_FONT_SCALING_RATIO = FIXED_FONT_POINT_SIZE * 92.0f; // Determined through experimentation to fit font to line + // height. +const float LINE_SCALE_RATIO = 1.2f; TextEntityRenderer::TextEntityRenderer(const EntityItemPointer& entity) : Parent(entity), @@ -44,6 +47,17 @@ bool TextEntityRenderer::isTransparent() const { return Parent::isTransparent() || _textAlpha < 1.0f || _backgroundAlpha < 1.0f || _pulseProperties.getAlphaMode() != PulseMode::NONE; } +Item::Bound TextEntityRenderer::getBound() { + auto bound = Parent::getBound(); + if (_billboardMode != BillboardMode::NONE) { + glm::vec3 dimensions = bound.getScale(); + float max = glm::max(dimensions.x, glm::max(dimensions.y, dimensions.z)); + const float SQRT_2 = 1.41421356237f; + bound.setScaleStayCentered(glm::vec3(SQRT_2 * max)); + } + return bound; +} + ShapeKey TextEntityRenderer::getShapeKey() { auto builder = render::ShapeKey::Builder().withOwnPipeline(); if (isTransparent()) { @@ -167,41 +181,34 @@ void TextEntityRenderer::doRender(RenderArgs* args) { gpu::Batch& batch = *args->_batch; auto transformToTopLeft = modelTransform; - if (_billboardMode == BillboardMode::YAW) { - //rotate about vertical to face the camera - glm::vec3 dPosition = args->getViewFrustum().getPosition() - modelTransform.getTranslation(); - // If x and z are 0, atan(x, z) is undefined, so default to 0 degrees - float yawRotation = dPosition.x == 0.0f && dPosition.z == 0.0f ? 0.0f : glm::atan(dPosition.x, dPosition.z); - glm::quat orientation = glm::quat(glm::vec3(0.0f, yawRotation, 0.0f)); - transformToTopLeft.setRotation(orientation); - } else if (_billboardMode == BillboardMode::FULL) { - glm::vec3 billboardPos = transformToTopLeft.getTranslation(); - glm::vec3 cameraPos = args->getViewFrustum().getPosition(); - // use the referencial from the avatar, y isn't always up - glm::vec3 avatarUP = EntityTreeRenderer::getAvatarUp(); - // check to see if glm::lookAt will work / using glm::lookAt variable name - glm::highp_vec3 s(glm::cross(billboardPos - cameraPos, avatarUP)); - - // make sure s is not NaN for any component - if (glm::length2(s) > 0.0f) { - glm::quat rotation(conjugate(toQuat(glm::lookAt(cameraPos, billboardPos, avatarUP)))); - transformToTopLeft.setRotation(rotation); - } - } + transformToTopLeft.setRotation(EntityItem::getBillboardRotation(transformToTopLeft.getTranslation(), transformToTopLeft.getRotation(), _billboardMode)); transformToTopLeft.postTranslate(dimensions * glm::vec3(-0.5f, 0.5f, 0.0f)); // Go to the top left transformToTopLeft.setScale(1.0f); // Use a scale of one so that the text is not deformed - batch.setModelTransform(transformToTopLeft); - auto geometryCache = DependencyManager::get(); - geometryCache->bindSimpleProgram(batch, false, backgroundColor.a < 1.0f, false, false, false); - geometryCache->renderQuad(batch, minCorner, maxCorner, backgroundColor, _geometryID); + if (backgroundColor.a > 0.0f) { + batch.setModelTransform(transformToTopLeft); + auto geometryCache = DependencyManager::get(); + geometryCache->bindSimpleProgram(batch, false, backgroundColor.a < 1.0f, false, false, false); + geometryCache->renderQuad(batch, minCorner, maxCorner, backgroundColor, _geometryID); + } - // FIXME: Factor out textRenderer so that Text3DOverlay overlay parts can be grouped by pipeline for a gpu performance increase. - float scale = _lineHeight / _textRenderer->getFontSize(); - transformToTopLeft.setScale(scale); // Scale to have the correct line height - batch.setModelTransform(transformToTopLeft); + if (textColor.a > 0.0f) { + // FIXME: Factor out textRenderer so that text parts can be grouped by pipeline for a gpu performance increase. + float scale = _lineHeight / _textRenderer->getFontSize(); + transformToTopLeft.setScale(scale); // Scale to have the correct line height + batch.setModelTransform(transformToTopLeft); - glm::vec2 bounds = glm::vec2(dimensions.x - (_leftMargin + _rightMargin), - dimensions.y - (_topMargin + _bottomMargin)); - _textRenderer->draw(batch, _leftMargin / scale, -_topMargin / scale, _text, textColor, bounds / scale); + glm::vec2 bounds = glm::vec2(dimensions.x - (_leftMargin + _rightMargin), dimensions.y - (_topMargin + _bottomMargin)); + _textRenderer->draw(batch, _leftMargin / scale, -_topMargin / scale, _text, textColor, bounds / scale); + } } + +QSizeF TextEntityRenderer::textSize(const QString& text) const { + auto extents = _textRenderer->computeExtent(text); + extents.y *= 2.0f; + + float maxHeight = (float)_textRenderer->computeExtent("Xy").y * LINE_SCALE_RATIO; + float pointToWorldScale = (maxHeight / FIXED_FONT_SCALING_RATIO) * _lineHeight; + + return QSizeF(extents.x, extents.y) * pointToWorldScale; +} \ No newline at end of file diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.h b/libraries/entities-renderer/src/RenderableTextEntityItem.h index 6c9e8324f8..e0306375a0 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.h +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.h @@ -26,7 +26,11 @@ public: TextEntityRenderer(const EntityItemPointer& entity); ~TextEntityRenderer(); + QSizeF textSize(const QString& text) const; + +protected: bool isTransparent() const override; + Item::Bound getBound() override; ShapeKey getShapeKey() override; private: diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 2510e41780..bf7820fecd 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -115,35 +115,45 @@ bool WebEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointe } } - if (_color != entity->getColor()) { - return true; - } + if(resultWithReadLock([&] { + if (_color != entity->getColor()) { + return true; + } - if (_alpha != entity->getAlpha()) { - return true; - } + if (_alpha != entity->getAlpha()) { + return true; + } - if (_sourceURL != entity->getSourceUrl()) { - return true; - } + if (_billboardMode != entity->getBillboardMode()) { + return true; + } - if (_dpi != entity->getDPI()) { - return true; - } + if (_sourceURL != entity->getSourceUrl()) { + return true; + } - if (_scriptURL != entity->getScriptURL()) { - return true; - } + if (_dpi != entity->getDPI()) { + return true; + } - if (_maxFPS != entity->getMaxFPS()) { - return true; - } + if (_scriptURL != entity->getScriptURL()) { + return true; + } - if (_inputMode != entity->getInputMode()) { - return true; - } + if (_maxFPS != entity->getMaxFPS()) { + return true; + } - if (_pulseProperties != entity->getPulseProperties()) { + if (_inputMode != entity->getInputMode()) { + return true; + } + + if (_pulseProperties != entity->getPulseProperties()) { + return true; + } + + return false; + })) { return true; } @@ -185,17 +195,14 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene ContentType currentContentType; withReadLock([&] { urlChanged = _sourceURL != newSourceURL; - currentContentType = _contentType; }); + currentContentType = _contentType; if (urlChanged) { - withWriteLock([&] { - _contentType = newContentType; - }); - if (newContentType != ContentType::HtmlContent || currentContentType != ContentType::HtmlContent) { destroyWebSurface(); } + _contentType = newContentType; } } @@ -206,6 +213,7 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene _color = entity->getColor(); _alpha = entity->getAlpha(); _pulseProperties = entity->getPulseProperties(); + _billboardMode = entity->getBillboardMode(); if (_contentType == ContentType::NoContent) { return; @@ -216,12 +224,12 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene buildWebSurface(entity, newSourceURL); } - if (_webSurface && _webSurface->getRootItem()) { + if (_webSurface) { if (_webSurface->getRootItem()) { if (_contentType == ContentType::HtmlContent && urlChanged) { _webSurface->getRootItem()->setProperty(URL_PROPERTY, newSourceURL); - _sourceURL = newSourceURL; } + _sourceURL = newSourceURL; { auto scriptURL = entity->getScriptURL(); @@ -268,6 +276,17 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene }); } +Item::Bound WebEntityRenderer::getBound() { + auto bound = Parent::getBound(); + if (_billboardMode != BillboardMode::NONE) { + glm::vec3 dimensions = bound.getScale(); + float max = glm::max(dimensions.x, glm::max(dimensions.y, dimensions.z)); + const float SQRT_2 = 1.41421356237f; + bound.setScaleStayCentered(glm::vec3(SQRT_2 * max)); + } + return bound; +} + void WebEntityRenderer::doRender(RenderArgs* args) { PerformanceTimer perfTimer("WebEntityRenderer::render"); withWriteLock([&] { @@ -295,14 +314,18 @@ void WebEntityRenderer::doRender(RenderArgs* args) { gpu::Batch& batch = *args->_batch; glm::vec4 color; + Transform transform; withReadLock([&] { float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; color = glm::vec4(toGlm(_color), _alpha * fadeRatio); color = EntityRenderer::calculatePulseColor(color, _pulseProperties, _created); - batch.setModelTransform(_renderTransform); + transform = _renderTransform; }); batch.setResourceTexture(0, _texture); + transform.setRotation(EntityItem::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode)); + batch.setModelTransform(transform); + // Turn off jitter for these entities batch.pushProjectionJitter(); DependencyManager::get()->bindWebBrowserProgram(batch, color.a < OPAQUE_ALPHA_THRESHOLD); @@ -361,7 +384,6 @@ void WebEntityRenderer::hoverEnterEntity(const PointerEvent& event) { if (_inputMode == WebInputMode::MOUSE) { handlePointerEvent(event); } else if (_webSurface) { - qDebug() << "boop5" << this << _webSurface << _webSurface->getRootItem(); PointerEvent webEvent = event; webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _dpi)); _webSurface->hoverBeginEvent(webEvent, _touchDevice); @@ -450,5 +472,5 @@ QObject* WebEntityRenderer::getEventHandler() { } void WebEntityRenderer::emitScriptEvent(const QVariant& message) { - QMetaObject::invokeMethod(this, "scriptEventReceived", Q_ARG(QVariant, message)); + emit scriptEventReceived(message); } \ No newline at end of file diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index 5d97eb5b8e..30b63a72df 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -47,19 +47,20 @@ public: } } + virtual void setProxyWindow(QWindow* proxyWindow) override; + virtual QObject* getEventHandler() override; + protected: virtual bool needsRenderUpdate() const override; virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override; virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override; virtual void doRender(RenderArgs* args) override; virtual bool isTransparent() const override; + Item::Bound getBound() override; virtual bool wantsHandControllerPointerEvents() const override { return true; } virtual bool wantsKeyboardFocus() const override { return true; } - virtual void setProxyWindow(QWindow* proxyWindow) override; - virtual QObject* getEventHandler() override; - void handlePointerEventAsTouch(const PointerEvent& event); void handlePointerEventAsMouse(const PointerEvent& event); @@ -85,6 +86,7 @@ private: glm::u8vec3 _color; float _alpha { 1.0f }; PulsePropertyGroup _pulseProperties; + BillboardMode _billboardMode; QString _sourceURL; uint16_t _dpi; diff --git a/libraries/entities-renderer/src/paintStroke.slv b/libraries/entities-renderer/src/paintStroke.slv index c033d2c247..f591370186 100644 --- a/libraries/entities-renderer/src/paintStroke.slv +++ b/libraries/entities-renderer/src/paintStroke.slv @@ -36,24 +36,23 @@ void main(void) { TransformObject obj = getTransformObject(); _distanceFromCenter = -1.0 + 2.0 * evenVertex; vec4 position = vec4(vertex.positionAndUCoord.xyz, 1.0); - vec3 normal = vertex.normal.xyz; vec3 binormal = vertex.binormalAndHalfWidth.xyz; if (_polylineData.faceCameraGlow.x != 0.0) { vec4 posEye; - vec3 normalEye; - vec3 binormalEye; + vec3 tangentEye; <$transformModelToEyePos(cam, obj, position, posEye)$> - <$transformModelToEyeDir(cam, obj, normal, normalEye)$> - <$transformModelToEyeDir(cam, obj, binormal, binormalEye)$> + // See comment in RenderablePolyLineEntityItem.cpp: we actually pass in the tangent in faceCamera mode + <$transformModelToEyeDir(cam, obj, binormal, tangentEye)$> - vec3 tangentEye = cross(binormalEye, normalEye); // new normal faces the camera - normalEye = normalize(posEye.xyz); - binormalEye = normalize(cross(normalEye, tangentEye)); + vec3 normalEye = normalize(posEye.xyz); + + vec3 binormalEye = normalize(cross(normalEye, tangentEye)); posEye.xyz += _distanceFromCenter * vertex.binormalAndHalfWidth.w * binormalEye; <$transformEyeToClipPos(cam, posEye, gl_Position)$> <$transformEyeToWorldDir(cam, normalEye, _normalWS)$> } else { + vec3 normal = vertex.normal.xyz; position.xyz += _distanceFromCenter * vertex.binormalAndHalfWidth.w * binormal; <$transformModelToClipPos(cam, obj, position, gl_Position)$> <$transformModelToWorldDir(cam, obj, normal, _normalWS)$> diff --git a/libraries/entities/src/EntityEditPacketSender.h b/libraries/entities/src/EntityEditPacketSender.h index a4ec2c45f9..99a5202986 100644 --- a/libraries/entities/src/EntityEditPacketSender.h +++ b/libraries/entities/src/EntityEditPacketSender.h @@ -56,6 +56,5 @@ private: private: std::mutex _mutex; AvatarData* _myAvatar { nullptr }; - QScriptEngine _scriptEngine; }; #endif // hifi_EntityEditPacketSender_h diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index ad9acb3775..c0b12c4d1f 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -49,6 +49,8 @@ int EntityItem::_maxActionsDataSize = 800; quint64 EntityItem::_rememberDeletedActionTime = 20 * USECS_PER_SECOND; QString EntityItem::_marketplacePublicKey; +std::function EntityItem::_getBillboardRotationOperator = [](const glm::vec3&, const glm::quat& rotation, BillboardMode) { return rotation; }; + EntityItem::EntityItem(const EntityItemID& entityItemID) : SpatiallyNestable(NestableType::Entity, entityItemID) { @@ -2170,6 +2172,12 @@ void EntityItem::enableNoBootstrap() { if (!(bool)(_flags & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING)) { _flags |= Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING; _flags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar + + // NOTE: unlike disableNoBootstrap() below, we do not call simulation->changeEntity() here + // because most enableNoBootstrap() cases are already correctly handled outside this scope + // and I didn't want to add redundant work. + // TODO: cleanup Grabs & dirtySimulationFlags to be more efficient and make more sense. + forEachDescendant([&](SpatiallyNestablePointer child) { if (child->getNestableType() == NestableType::Entity) { EntityItemPointer entity = std::static_pointer_cast(child); @@ -2184,11 +2192,19 @@ void EntityItem::disableNoBootstrap() { if (!stillHasGrabActions()) { _flags &= ~Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING; _flags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar + + EntityTreePointer entityTree = getTree(); + assert(entityTree); + EntitySimulationPointer simulation = entityTree->getSimulation(); + assert(simulation); + simulation->changeEntity(getThisPointer()); + forEachDescendant([&](SpatiallyNestablePointer child) { if (child->getNestableType() == NestableType::Entity) { EntityItemPointer entity = std::static_pointer_cast(child); entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP); entity->clearSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING); + simulation->changeEntity(entity); } }); } @@ -3419,7 +3435,7 @@ void EntityItem::addGrab(GrabPointer grab) { if (useAction) { EntityTreePointer entityTree = getTree(); assert(entityTree); - EntitySimulationPointer simulation = entityTree ? entityTree->getSimulation() : nullptr; + EntitySimulationPointer simulation = entityTree->getSimulation(); assert(simulation); auto actionFactory = DependencyManager::get(); @@ -3468,7 +3484,6 @@ void EntityItem::removeGrab(GrabPointer grab) { setLocalVelocity(glm::vec3(0.0f)); setAngularVelocity(glm::vec3(0.0f)); } - markDirtyFlags(Simulation::DIRTY_MOTION_TYPE); QUuid actionID = grab->getActionID(); if (!actionID.isNull()) { diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 75fa30b3a3..824261c022 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -557,6 +557,9 @@ public: virtual void removeGrab(GrabPointer grab) override; virtual void disableGrab(GrabPointer grab) override; + static void setBillboardRotationOperator(std::function getBillboardRotationOperator) { _getBillboardRotationOperator = getBillboardRotationOperator; } + static glm::quat getBillboardRotation(const glm::vec3& position, const glm::quat& rotation, BillboardMode billboardMode) { return _getBillboardRotationOperator(position, rotation, billboardMode); } + signals: void requestRenderUpdate(); void spaceUpdate(std::pair data); @@ -744,6 +747,8 @@ protected: QHash _grabActions; +private: + static std::function _getBillboardRotationOperator; }; #endif // hifi_EntityItem_h diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index c1488a5893..6738b1cedd 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -543,6 +543,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_ALPHA, alpha); changedProperties += _pulse.getChangedProperties(); CHECK_PROPERTY_CHANGE(PROP_TEXTURES, textures); + CHECK_PROPERTY_CHANGE(PROP_BILLBOARD_MODE, billboardMode); // Particles CHECK_PROPERTY_CHANGE(PROP_MAX_PARTICLES, maxParticles); @@ -601,7 +602,6 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_TEXT_ALPHA, textAlpha); CHECK_PROPERTY_CHANGE(PROP_BACKGROUND_COLOR, backgroundColor); CHECK_PROPERTY_CHANGE(PROP_BACKGROUND_ALPHA, backgroundAlpha); - CHECK_PROPERTY_CHANGE(PROP_BILLBOARD_MODE, billboardMode); CHECK_PROPERTY_CHANGE(PROP_LEFT_MARGIN, leftMargin); CHECK_PROPERTY_CHANGE(PROP_RIGHT_MARGIN, rightMargin); CHECK_PROPERTY_CHANGE(PROP_TOP_MARGIN, topMargin); @@ -642,6 +642,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_SCRIPT_URL, scriptURL); CHECK_PROPERTY_CHANGE(PROP_MAX_FPS, maxFPS); CHECK_PROPERTY_CHANGE(PROP_INPUT_MODE, inputMode); + CHECK_PROPERTY_CHANGE(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, showKeyboardFocusHighlight); // Polyline CHECK_PROPERTY_CHANGE(PROP_LINE_POINTS, linePoints); @@ -945,12 +946,9 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { */ /**jsdoc - * The "Material" {@link Entities.EntityType|EntityType} modifies the existing materials on - * {@link Entities.EntityType|Model} entities, {@link Entities.EntityType|Shape} entities (albedo only), - * {@link Overlays.OverlayType|model overlays}, and avatars. + * The "Material" {@link Entities.EntityType|EntityType} modifies the existing materials on entities and avatars. * It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}.
- * To apply a material to an entity or overlay, set the material entity's parentID property to the entity or - * overlay's ID. + * To apply a material to an entity, set the material entity's parentID property to the entity ID. * To apply a material to an avatar, set the material entity's parentID property to the avatar's session UUID. * To apply a material to your avatar such that it persists across domains and log-ins, create the material as an avatar entity * by setting the entityHostType parameter in {@link Entities.addEntity} to "avatar". @@ -1339,6 +1337,10 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @property {Vec3} dimensions=0.1,0.1,0.01 - The dimensions of the entity. * @property {Color} color=255,255,255 - The color of the web surface. * @property {number} alpha=1 - The alpha of the web surface. + * @property {BillboardMode} billboardMode="none" - If "none", the entity is not billboarded. If "yaw", the entity will be + * oriented to follow your camera around the y-axis. If "full" the entity will be oriented to face your camera. The following deprecated + * behavior is also supported: you can also set "faceCamera" to true to set billboardMode to "yaw", and you can set + * "isFacingAvatar" to true to set billboardMode to "full". Setting either to false sets the mode to "none" * @property {string} sourceUrl="" - The URL of the Web page to display. This value does not change as you or others navigate * on the Web entity. * @property {number} dpi=30 - The resolution to display the page at, in dots per inch. If you convert this to dots per meter @@ -1348,6 +1350,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @property {number} maxFPS=10 - The maximum update rate for the Web content, in frames/second. * @property {WebInputMode} inputMode="touch" - The user input mode to use. * @property {Entities.Pulse} pulse - The pulse-related properties. Deprecated. + * @property {boolean} showKeyboardFocusHighlight - Whether or not to show the keyboard focus highlight when this entity has focus. * @example Create a Web entity displaying at 1920 x 1080 resolution. * var METERS_TO_INCHES = 39.3701; * var entity = Entities.addEntity({ @@ -1719,6 +1722,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool // Text only if (_type == EntityTypes::Text) { _pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BILLBOARD_MODE, billboardMode, getBillboardModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXT, text); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LINE_HEIGHT, lineHeight); @@ -1726,7 +1730,6 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXT_ALPHA, textAlpha); COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_BACKGROUND_COLOR, backgroundColor, u8vec3Color); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_BACKGROUND_ALPHA, backgroundAlpha); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BILLBOARD_MODE, billboardMode, getBillboardModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LEFT_MARGIN, leftMargin); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RIGHT_MARGIN, rightMargin); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TOP_MARGIN, topMargin); @@ -1762,12 +1765,14 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha); _pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BILLBOARD_MODE, billboardMode, getBillboardModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOURCE_URL, sourceUrl); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_DPI, dpi); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SCRIPT_URL, scriptURL); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MAX_FPS, maxFPS); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_INPUT_MODE, inputMode, getInputModeAsString()); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, showKeyboardFocusHighlight); } // PolyVoxel only @@ -1827,11 +1832,11 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha); _pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BILLBOARD_MODE, billboardMode, getBillboardModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_IMAGE_URL, imageURL); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMISSIVE, emissive); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_KEEP_ASPECT_RATIO, keepAspectRatio); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BILLBOARD_MODE, billboardMode, getBillboardModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SUB_IMAGE, subImage); // Handle conversions to old 'textures' property from "imageURL" @@ -1927,7 +1932,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool properties.setProperty("localEntity", convertScriptValue(engine, getEntityHostType() == entity::HostType::LOCAL)); } - if (!psuedoPropertyFlagsActive || psueudoPropertyFlags.test(EntityPsuedoPropertyFlag::FaceCamera)) { + if (_type != EntityTypes::PolyLine && (!psuedoPropertyFlagsActive || psueudoPropertyFlags.test(EntityPsuedoPropertyFlag::FaceCamera))) { properties.setProperty("faceCamera", convertScriptValue(engine, getBillboardMode() == BillboardMode::YAW)); } if (!psuedoPropertyFlagsActive || psueudoPropertyFlags.test(EntityPsuedoPropertyFlag::IsFacingAvatar)) { @@ -2036,6 +2041,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(alpha, float, setAlpha); _pulse.copyFromScriptValue(object, _defaultSettings); COPY_PROPERTY_FROM_QSCRIPTVALUE(textures, QString, setTextures); + COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(billboardMode, BillboardMode); // Particles COPY_PROPERTY_FROM_QSCRIPTVALUE(maxParticles, quint32, setMaxParticles); @@ -2094,7 +2100,6 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(textAlpha, float, setTextAlpha); COPY_PROPERTY_FROM_QSCRIPTVALUE(backgroundColor, u8vec3Color, setBackgroundColor); COPY_PROPERTY_FROM_QSCRIPTVALUE(backgroundAlpha, float, setBackgroundAlpha); - COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(billboardMode, BillboardMode); COPY_PROPERTY_FROM_QSCRIPTVALUE(leftMargin, float, setLeftMargin); COPY_PROPERTY_FROM_QSCRIPTVALUE(rightMargin, float, setRightMargin); COPY_PROPERTY_FROM_QSCRIPTVALUE(topMargin, float, setTopMargin); @@ -2135,6 +2140,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(scriptURL, QString, setScriptURL); COPY_PROPERTY_FROM_QSCRIPTVALUE(maxFPS, uint8_t, setMaxFPS); COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(inputMode, InputMode); + COPY_PROPERTY_FROM_QSCRIPTVALUE(showKeyboardFocusHighlight, bool, setShowKeyboardFocusHighlight); // Polyline COPY_PROPERTY_FROM_QSCRIPTVALUE(linePoints, qVectorVec3, setLinePoints); @@ -2194,7 +2200,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool } // Handle old "faceCamera" and "isFacingAvatar" props - { + if (_type != EntityTypes::PolyLine) { QScriptValue P = object.property("faceCamera"); if (P.isValid() && !object.property("billboardMode").isValid()) { bool newValue = P.toVariant().toBool(); @@ -2314,6 +2320,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(alpha); _pulse.merge(other._pulse); COPY_PROPERTY_IF_CHANGED(textures); + COPY_PROPERTY_IF_CHANGED(billboardMode); // Particles COPY_PROPERTY_IF_CHANGED(maxParticles); @@ -2372,7 +2379,6 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(textAlpha); COPY_PROPERTY_IF_CHANGED(backgroundColor); COPY_PROPERTY_IF_CHANGED(backgroundAlpha); - COPY_PROPERTY_IF_CHANGED(billboardMode); COPY_PROPERTY_IF_CHANGED(leftMargin); COPY_PROPERTY_IF_CHANGED(rightMargin); COPY_PROPERTY_IF_CHANGED(topMargin); @@ -2413,6 +2419,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(scriptURL); COPY_PROPERTY_IF_CHANGED(maxFPS); COPY_PROPERTY_IF_CHANGED(inputMode); + COPY_PROPERTY_IF_CHANGED(showKeyboardFocusHighlight); // Polyline COPY_PROPERTY_IF_CHANGED(linePoints); @@ -2632,6 +2639,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_GROUP_PROPERTY_TO_MAP(PROP_PULSE_ALPHA_MODE, Pulse, pulse, AlphaMode, alphaMode); } ADD_PROPERTY_TO_MAP(PROP_TEXTURES, Textures, textures, QString); + ADD_PROPERTY_TO_MAP(PROP_BILLBOARD_MODE, BillboardMode, billboardMode, BillboardMode); // Particles ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_MAX_PARTICLES, MaxParticles, maxParticles, quint32, @@ -2725,7 +2733,6 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_PROPERTY_TO_MAP(PROP_TEXT_ALPHA, TextAlpha, textAlpha, float); ADD_PROPERTY_TO_MAP(PROP_BACKGROUND_COLOR, BackgroundColor, backgroundColor, u8vec3Color); ADD_PROPERTY_TO_MAP(PROP_BACKGROUND_ALPHA, BackgroundAlpha, backgroundAlpha, float); - ADD_PROPERTY_TO_MAP(PROP_BILLBOARD_MODE, BillboardMode, billboardMode, BillboardMode); ADD_PROPERTY_TO_MAP(PROP_LEFT_MARGIN, LeftMargin, leftMargin, float); ADD_PROPERTY_TO_MAP(PROP_RIGHT_MARGIN, RightMargin, rightMargin, float); ADD_PROPERTY_TO_MAP(PROP_TOP_MARGIN, TopMargin, topMargin, float); @@ -2797,6 +2804,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_PROPERTY_TO_MAP(PROP_SCRIPT_URL, ScriptURL, scriptURL, QString); ADD_PROPERTY_TO_MAP(PROP_MAX_FPS, MaxFPS, maxFPS, uint8_t); ADD_PROPERTY_TO_MAP(PROP_INPUT_MODE, InputMode, inputMode, WebInputMode); + ADD_PROPERTY_TO_MAP(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, ShowKeyboardFocusHighlight, showKeyboardFocusHighlight, bool); // Polyline ADD_PROPERTY_TO_MAP(PROP_LINE_POINTS, LinePoints, linePoints, QVector); @@ -3134,6 +3142,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy _staticPulse.setProperties(properties); _staticPulse.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + APPEND_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, (uint32_t)properties.getBillboardMode()); APPEND_ENTITY_PROPERTY(PROP_TEXT, properties.getText()); APPEND_ENTITY_PROPERTY(PROP_LINE_HEIGHT, properties.getLineHeight()); @@ -3141,7 +3150,6 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_TEXT_ALPHA, properties.getTextAlpha()); APPEND_ENTITY_PROPERTY(PROP_BACKGROUND_COLOR, properties.getBackgroundColor()); APPEND_ENTITY_PROPERTY(PROP_BACKGROUND_ALPHA, properties.getBackgroundAlpha()); - APPEND_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, (uint32_t)properties.getBillboardMode()); APPEND_ENTITY_PROPERTY(PROP_LEFT_MARGIN, properties.getLeftMargin()); APPEND_ENTITY_PROPERTY(PROP_RIGHT_MARGIN, properties.getRightMargin()); APPEND_ENTITY_PROPERTY(PROP_TOP_MARGIN, properties.getTopMargin()); @@ -3199,12 +3207,14 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy _staticPulse.setProperties(properties); _staticPulse.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + APPEND_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, (uint32_t)properties.getBillboardMode()); APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, properties.getSourceUrl()); APPEND_ENTITY_PROPERTY(PROP_DPI, properties.getDPI()); APPEND_ENTITY_PROPERTY(PROP_SCRIPT_URL, properties.getScriptURL()); APPEND_ENTITY_PROPERTY(PROP_MAX_FPS, properties.getMaxFPS()); APPEND_ENTITY_PROPERTY(PROP_INPUT_MODE, (uint32_t)properties.getInputMode()); + APPEND_ENTITY_PROPERTY(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, properties.getShowKeyboardFocusHighlight()); } if (properties.getType() == EntityTypes::Line) { @@ -3259,11 +3269,11 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy _staticPulse.setProperties(properties); _staticPulse.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + APPEND_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, (uint32_t)properties.getBillboardMode()); APPEND_ENTITY_PROPERTY(PROP_IMAGE_URL, properties.getImageURL()); APPEND_ENTITY_PROPERTY(PROP_EMISSIVE, properties.getEmissive()); APPEND_ENTITY_PROPERTY(PROP_KEEP_ASPECT_RATIO, properties.getKeepAspectRatio()); - APPEND_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, (uint32_t)properties.getBillboardMode()); APPEND_ENTITY_PROPERTY(PROP_SUB_IMAGE, properties.getSubImage()); } @@ -3605,6 +3615,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int if (properties.getType() == EntityTypes::Text) { properties.getPulse().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BILLBOARD_MODE, BillboardMode, setBillboardMode); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT, QString, setText); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_HEIGHT, float, setLineHeight); @@ -3612,7 +3623,6 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT_ALPHA, float, setTextAlpha); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BACKGROUND_COLOR, u8vec3Color, setBackgroundColor); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BACKGROUND_ALPHA, float, setBackgroundAlpha); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BILLBOARD_MODE, BillboardMode, setBillboardMode); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LEFT_MARGIN, float, setLeftMargin); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RIGHT_MARGIN, float, setRightMargin); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TOP_MARGIN, float, setTopMargin); @@ -3659,12 +3669,14 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA, float, setAlpha); properties.getPulse().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BILLBOARD_MODE, BillboardMode, setBillboardMode); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOURCE_URL, QString, setSourceUrl); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DPI, uint16_t, setDPI); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SCRIPT_URL, QString, setScriptURL); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MAX_FPS, uint8_t, setMaxFPS); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_INPUT_MODE, WebInputMode, setInputMode); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, bool, setShowKeyboardFocusHighlight); } if (properties.getType() == EntityTypes::Line) { @@ -3716,11 +3728,11 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA, float, setAlpha); properties.getPulse().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BILLBOARD_MODE, BillboardMode, setBillboardMode); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_IMAGE_URL, QString, setImageURL); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMISSIVE, bool, setEmissive); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_KEEP_ASPECT_RATIO, bool, setKeepAspectRatio); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BILLBOARD_MODE, BillboardMode, setBillboardMode); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SUB_IMAGE, QRect, setSubImage); } @@ -3935,11 +3947,12 @@ void EntityItemProperties::markAllChanged() { // Common _shapeTypeChanged = true; + _compoundShapeURLChanged = true; _colorChanged = true; _alphaChanged = true; _pulse.markAllChanged(); _texturesChanged = true; - _compoundShapeURLChanged = true; + _billboardModeChanged = true; // Particles _maxParticlesChanged = true; @@ -3998,7 +4011,6 @@ void EntityItemProperties::markAllChanged() { _textAlphaChanged = true; _backgroundColorChanged = true; _backgroundAlphaChanged = true; - _billboardModeChanged = true; _leftMarginChanged = true; _rightMarginChanged = true; _topMarginChanged = true; @@ -4039,6 +4051,7 @@ void EntityItemProperties::markAllChanged() { _scriptURLChanged = true; _maxFPSChanged = true; _inputModeChanged = true; + _showKeyboardFocusHighlightChanged = true; // Polyline _linePointsChanged = true; @@ -4410,6 +4423,9 @@ QList EntityItemProperties::listChangedProperties() { if (texturesChanged()) { out += "textures"; } + if (billboardModeChanged()) { + out += "billboardMode"; + } // Particles if (maxParticlesChanged()) { @@ -4566,9 +4582,6 @@ QList EntityItemProperties::listChangedProperties() { if (backgroundAlphaChanged()) { out += "backgroundAlpha"; } - if (billboardModeChanged()) { - out += "billboardMode"; - } if (leftMarginChanged()) { out += "leftMargin"; } @@ -4690,6 +4703,9 @@ QList EntityItemProperties::listChangedProperties() { if (faceCameraChanged()) { out += "faceCamera"; } + if (showKeyboardFocusHighlightChanged()) { + out += "showKeyboardFocusHighlight"; + } // Shape if (shapeChanged()) { diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index dcba60b004..712f2d120f 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -242,6 +242,7 @@ public: DEFINE_PROPERTY(PROP_ALPHA, Alpha, alpha, float, ENTITY_ITEM_DEFAULT_ALPHA); DEFINE_PROPERTY_GROUP(Pulse, pulse, PulsePropertyGroup); DEFINE_PROPERTY_REF(PROP_TEXTURES, Textures, textures, QString, ""); + DEFINE_PROPERTY_REF_ENUM(PROP_BILLBOARD_MODE, BillboardMode, billboardMode, BillboardMode, BillboardMode::NONE); // Particles DEFINE_PROPERTY(PROP_MAX_PARTICLES, MaxParticles, maxParticles, quint32, particle::DEFAULT_MAX_PARTICLES); @@ -300,7 +301,6 @@ public: DEFINE_PROPERTY_REF(PROP_TEXT_ALPHA, TextAlpha, textAlpha, float, TextEntityItem::DEFAULT_TEXT_ALPHA); DEFINE_PROPERTY_REF(PROP_BACKGROUND_COLOR, BackgroundColor, backgroundColor, u8vec3Color, TextEntityItem::DEFAULT_BACKGROUND_COLOR); DEFINE_PROPERTY_REF(PROP_BACKGROUND_ALPHA, BackgroundAlpha, backgroundAlpha, float, TextEntityItem::DEFAULT_TEXT_ALPHA); - DEFINE_PROPERTY_REF_ENUM(PROP_BILLBOARD_MODE, BillboardMode, billboardMode, BillboardMode, BillboardMode::NONE); DEFINE_PROPERTY_REF(PROP_LEFT_MARGIN, LeftMargin, leftMargin, float, TextEntityItem::DEFAULT_MARGIN); DEFINE_PROPERTY_REF(PROP_RIGHT_MARGIN, RightMargin, rightMargin, float, TextEntityItem::DEFAULT_MARGIN); DEFINE_PROPERTY_REF(PROP_TOP_MARGIN, TopMargin, topMargin, float, TextEntityItem::DEFAULT_MARGIN); @@ -341,6 +341,7 @@ public: DEFINE_PROPERTY_REF(PROP_SCRIPT_URL, ScriptURL, scriptURL, QString, ""); DEFINE_PROPERTY_REF(PROP_MAX_FPS, MaxFPS, maxFPS, uint8_t, WebEntityItem::DEFAULT_MAX_FPS); DEFINE_PROPERTY_REF_ENUM(PROP_INPUT_MODE, InputMode, inputMode, WebInputMode, WebInputMode::TOUCH); + DEFINE_PROPERTY_REF(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, ShowKeyboardFocusHighlight, showKeyboardFocusHighlight, bool, true); // Polyline DEFINE_PROPERTY_REF(PROP_LINE_POINTS, LinePoints, linePoints, QVector, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index b11ecff5bb..093df92dc1 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -118,6 +118,7 @@ enum EntityPropertyList { PROP_PULSE_COLOR_MODE, PROP_PULSE_ALPHA_MODE, PROP_TEXTURES, + PROP_BILLBOARD_MODE, //////////////////////////////////////////////////////////////////////////////////////////////////// // ATTENTION: add new shared EntityItem properties to the list ABOVE this line @@ -232,11 +233,10 @@ enum EntityPropertyList { PROP_TEXT_ALPHA = PROP_DERIVED_3, PROP_BACKGROUND_COLOR = PROP_DERIVED_4, PROP_BACKGROUND_ALPHA = PROP_DERIVED_5, - PROP_BILLBOARD_MODE = PROP_DERIVED_6, - PROP_LEFT_MARGIN = PROP_DERIVED_7, - PROP_RIGHT_MARGIN = PROP_DERIVED_8, - PROP_TOP_MARGIN = PROP_DERIVED_9, - PROP_BOTTOM_MARGIN = PROP_DERIVED_10, + PROP_LEFT_MARGIN = PROP_DERIVED_6, + PROP_RIGHT_MARGIN = PROP_DERIVED_7, + PROP_TOP_MARGIN = PROP_DERIVED_8, + PROP_BOTTOM_MARGIN = PROP_DERIVED_9, // Zone // Keylight @@ -296,6 +296,7 @@ enum EntityPropertyList { PROP_SCRIPT_URL = PROP_DERIVED_2, PROP_MAX_FPS = PROP_DERIVED_3, PROP_INPUT_MODE = PROP_DERIVED_4, + PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT = PROP_DERIVED_5, // Polyline PROP_LINE_POINTS = PROP_DERIVED_0, diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 286f0dd650..150aa6b0cf 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -57,11 +57,6 @@ EntityScriptingInterface::EntityScriptingInterface(bool bidOnSimulationOwnership connect(nodeList.data(), &NodeList::canRezTmpCertifiedChanged, this, &EntityScriptingInterface::canRezTmpCertifiedChanged); connect(nodeList.data(), &NodeList::canWriteAssetsChanged, this, &EntityScriptingInterface::canWriteAssetsChanged); - // If the user clicks somewhere where there is no entity at all, we will release focus - connect(this, &EntityScriptingInterface::mousePressOffEntity, [=]() { - setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); - }); - auto& packetReceiver = nodeList->getPacketReceiver(); packetReceiver.registerListener(PacketType::EntityScriptCallMethod, this, "handleEntityScriptCallMethodPacket"); } @@ -474,7 +469,7 @@ void synchronizeEditedGrabProperties(EntityItemProperties& properties, const QSt } -QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties, const QString& entityHostTypeString) { +QUuid EntityScriptingInterface::addEntityInternal(const EntityItemProperties& properties, entity::HostType entityHostType) { PROFILE_RANGE(script_entities, __FUNCTION__); _activityTracking.addedEntityCount++; @@ -483,10 +478,10 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties const auto sessionID = nodeList->getSessionUUID(); EntityItemProperties propertiesWithSimID = properties; - propertiesWithSimID.setEntityHostTypeFromString(entityHostTypeString); - if (propertiesWithSimID.getEntityHostType() == entity::HostType::AVATAR) { + propertiesWithSimID.setEntityHostType(entityHostType); + if (entityHostType == entity::HostType::AVATAR) { propertiesWithSimID.setOwningAvatarID(sessionID); - } else if (propertiesWithSimID.getEntityHostType() == entity::HostType::LOCAL) { + } else if (entityHostType == entity::HostType::LOCAL) { // For now, local entities are always collisionless // TODO: create a separate, local physics simulation that just handles local entities (and MyAvatar?) propertiesWithSimID.setCollisionless(true); @@ -571,7 +566,7 @@ QUuid EntityScriptingInterface::addModelEntity(const QString& name, const QStrin return addEntity(properties); } -QUuid EntityScriptingInterface::cloneEntity(QUuid entityIDToClone) { +QUuid EntityScriptingInterface::cloneEntity(const QUuid& entityIDToClone) { EntityItemID newEntityID; EntityItemProperties properties = getEntityProperties(entityIDToClone); bool cloneAvatarEntity = properties.getCloneAvatarEntity(); @@ -579,9 +574,9 @@ QUuid EntityScriptingInterface::cloneEntity(QUuid entityIDToClone) { if (properties.getEntityHostType() == entity::HostType::LOCAL) { // Local entities are only cloned locally - return addEntity(properties, "local"); + return addEntityInternal(properties, entity::HostType::LOCAL); } else if (cloneAvatarEntity) { - return addEntity(properties, "avatar"); + return addEntityInternal(properties, entity::HostType::AVATAR); } else { // setLastEdited timestamp to 0 to ensure this entity gets updated with the properties // from the server-created entity, don't change this unless you know what you are doing @@ -596,12 +591,12 @@ QUuid EntityScriptingInterface::cloneEntity(QUuid entityIDToClone) { } } -EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid entityID) { +EntityItemProperties EntityScriptingInterface::getEntityProperties(const QUuid& entityID) { const EntityPropertyFlags noSpecificProperties; return getEntityProperties(entityID, noSpecificProperties); } -EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid entityID, EntityPropertyFlags desiredProperties) { +EntityItemProperties EntityScriptingInterface::getEntityProperties(const QUuid& entityID, EntityPropertyFlags desiredProperties) { PROFILE_RANGE(script_entities, __FUNCTION__); bool scalesWithParent { false }; @@ -775,7 +770,7 @@ QScriptValue EntityScriptingInterface::getMultipleEntityPropertiesInternal(QScri return finalResult; } -QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& scriptSideProperties) { +QUuid EntityScriptingInterface::editEntity(const QUuid& id, const EntityItemProperties& scriptSideProperties) { PROFILE_RANGE(script_entities, __FUNCTION__); _activityTracking.editedEntityCount++; @@ -960,7 +955,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& return id; } -void EntityScriptingInterface::deleteEntity(QUuid id) { +void EntityScriptingInterface::deleteEntity(const QUuid& id) { PROFILE_RANGE(script_entities, __FUNCTION__); _activityTracking.deletedEntityCount++; @@ -1005,12 +1000,51 @@ void EntityScriptingInterface::deleteEntity(QUuid id) { } } +QString EntityScriptingInterface::getEntityType(const QUuid& entityID) { + QString toReturn; + _entityTree->withReadLock([&] { + EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID); + if (entity) { + toReturn = EntityTypes::getEntityTypeName(entity->getType()); + } + }); + return toReturn; +} + +QObject* EntityScriptingInterface::getEntityObject(const QUuid& id) { + return EntityTree::getEntityObject(id); +} + +bool EntityScriptingInterface::isLoaded(const QUuid& id) { + bool toReturn = false; + _entityTree->withReadLock([&] { + EntityItemPointer entity = _entityTree->findEntityByEntityItemID(id); + if (entity) { + toReturn = entity->isVisuallyReady(); + } + }); + return toReturn; +} + +bool EntityScriptingInterface::isAddedEntity(const QUuid& id) { + bool toReturn = false; + _entityTree->withReadLock([&] { + EntityItemPointer entity = _entityTree->findEntityByEntityItemID(id); + toReturn = (bool)entity; + }); + return toReturn; +} + +QSizeF EntityScriptingInterface::textSize(const QUuid& id, const QString& text) { + return EntityTree::textSize(id, text); +} + void EntityScriptingInterface::setEntitiesScriptEngine(QSharedPointer engine) { std::lock_guard lock(_entitiesScriptEngineLock); _entitiesScriptEngine = engine; } -void EntityScriptingInterface::callEntityMethod(QUuid id, const QString& method, const QStringList& params) { +void EntityScriptingInterface::callEntityMethod(const QUuid& id, const QString& method, const QStringList& params) { PROFILE_RANGE(script_entities, __FUNCTION__); std::lock_guard lock(_entitiesScriptEngineLock); @@ -1020,12 +1054,12 @@ void EntityScriptingInterface::callEntityMethod(QUuid id, const QString& method, } } -void EntityScriptingInterface::callEntityServerMethod(QUuid id, const QString& method, const QStringList& params) { +void EntityScriptingInterface::callEntityServerMethod(const QUuid& id, const QString& method, const QStringList& params) { PROFILE_RANGE(script_entities, __FUNCTION__); DependencyManager::get()->callEntityServerMethod(id, method, params); } -void EntityScriptingInterface::callEntityClientMethod(QUuid clientSessionID, QUuid entityID, const QString& method, const QStringList& params) { +void EntityScriptingInterface::callEntityClientMethod(const QUuid& clientSessionID, const QUuid& entityID, const QString& method, const QStringList& params) { PROFILE_RANGE(script_entities, __FUNCTION__); auto scriptServerServices = DependencyManager::get(); @@ -1262,7 +1296,7 @@ ParabolaToEntityIntersectionResult EntityScriptingInterface::evalParabolaInterse return result; } -bool EntityScriptingInterface::reloadServerScripts(QUuid entityID) { +bool EntityScriptingInterface::reloadServerScripts(const QUuid& entityID) { auto client = DependencyManager::get(); return client->reloadServerScript(entityID); } @@ -1335,7 +1369,7 @@ bool EntityPropertyMetadataRequest::serverScripts(EntityItemID entityID, QScript return true; } -bool EntityScriptingInterface::queryPropertyMetadata(QUuid entityID, QScriptValue property, QScriptValue scopeOrCallback, QScriptValue methodOrName) { +bool EntityScriptingInterface::queryPropertyMetadata(const QUuid& entityID, QScriptValue property, QScriptValue scopeOrCallback, QScriptValue methodOrName) { auto name = property.toString(); auto handler = makeScopedHandlerObject(scopeOrCallback, methodOrName); QPointer engine = dynamic_cast(handler.engine()); @@ -1379,7 +1413,7 @@ bool EntityScriptingInterface::queryPropertyMetadata(QUuid entityID, QScriptValu } } -bool EntityScriptingInterface::getServerScriptStatus(QUuid entityID, QScriptValue callback) { +bool EntityScriptingInterface::getServerScriptStatus(const QUuid& entityID, QScriptValue callback) { auto client = DependencyManager::get(); auto request = client->createScriptStatusRequest(entityID); connect(request, &GetScriptStatusRequest::finished, callback.engine(), [callback](GetScriptStatusRequest* request) mutable { @@ -1518,14 +1552,14 @@ bool EntityScriptingInterface::setPoints(QUuid entityID, std::function& points) { +bool EntityScriptingInterface::setAllPoints(const QUuid& entityID, const QVector& points) { PROFILE_RANGE(script_entities, __FUNCTION__); EntityItemPointer entity = static_cast(_entityTree->findEntityByEntityItemID(entityID)); @@ -1580,7 +1614,7 @@ bool EntityScriptingInterface::setAllPoints(QUuid entityID, const QVector(_entityTree->findEntityByEntityItemID(entityID)); @@ -2040,7 +2074,7 @@ QVector EntityScriptingInterface::getChildrenIDs(const QUuid& parentID) { return result; } -bool EntityScriptingInterface::isChildOfParent(QUuid childID, QUuid parentID) { +bool EntityScriptingInterface::isChildOfParent(const QUuid& childID, const QUuid& parentID) { bool isChild = false; if (!_entityTree) { @@ -2062,7 +2096,7 @@ bool EntityScriptingInterface::isChildOfParent(QUuid childID, QUuid parentID) { return isChild; } -QString EntityScriptingInterface::getNestableType(QUuid id) { +QString EntityScriptingInterface::getNestableType(const QUuid& id) { QSharedPointer parentFinder = DependencyManager::get(); if (!parentFinder) { return "unknown"; @@ -2114,8 +2148,8 @@ QUuid EntityScriptingInterface::getKeyboardFocusEntity() const { return result; } -void EntityScriptingInterface::setKeyboardFocusEntity(const EntityItemID& id) { - QMetaObject::invokeMethod(qApp, "setKeyboardFocusEntity", Qt::DirectConnection, Q_ARG(EntityItemID, id)); +void EntityScriptingInterface::setKeyboardFocusEntity(const QUuid& id) { + QMetaObject::invokeMethod(qApp, "setKeyboardFocusEntity", Qt::DirectConnection, Q_ARG(const QUuid&, id)); } void EntityScriptingInterface::sendMousePressOnEntity(const EntityItemID& id, const PointerEvent& event) { @@ -2154,7 +2188,7 @@ void EntityScriptingInterface::sendHoverLeaveEntity(const EntityItemID& id, cons emit hoverLeaveEntity(id, event); } -bool EntityScriptingInterface::wantsHandControllerPointerEvents(QUuid id) { +bool EntityScriptingInterface::wantsHandControllerPointerEvents(const QUuid& id) { bool result = false; if (_entityTree) { _entityTree->withReadLock([&] { @@ -2186,7 +2220,7 @@ bool EntityScriptingInterface::AABoxIntersectsCapsule(const glm::vec3& low, cons return aaBox.findCapsulePenetration(start, end, radius, penetration); } -void EntityScriptingInterface::getMeshes(QUuid entityID, QScriptValue callback) { +void EntityScriptingInterface::getMeshes(const QUuid& entityID, QScriptValue callback) { PROFILE_RANGE(script_entities, __FUNCTION__); EntityItemPointer entity = static_cast(_entityTree->findEntityByEntityItemID(entityID)); diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 7950025ff1..0cf9070b08 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -164,6 +164,9 @@ public: */ static QScriptValue getMultipleEntityProperties(QScriptContext* context, QScriptEngine* engine); QScriptValue getMultipleEntityPropertiesInternal(QScriptEngine* engine, QVector entityIDs, const QScriptValue& extendedDesiredProperties); + + QUuid addEntityInternal(const EntityItemProperties& properties, entity::HostType entityHostType); + public slots: /**jsdoc @@ -266,7 +269,17 @@ public slots: * }); * print("Entity created: " + entityID); */ - Q_INVOKABLE QUuid addEntity(const EntityItemProperties& properties, const QString& entityHostTypeString); + Q_INVOKABLE QUuid addEntity(const EntityItemProperties& properties, const QString& entityHostTypeString) { + entity::HostType entityHostType; + if (entityHostTypeString == "local") { + entityHostType = entity::HostType::LOCAL; + } else if (entityHostTypeString == "avatar") { + entityHostType = entity::HostType::AVATAR; + } else { + entityHostType = entity::HostType::DOMAIN; + } + return addEntityInternal(properties, entityHostType); + } /**jsdoc * Add a new entity with specified properties. @@ -276,8 +289,8 @@ public slots: * @returns {Uuid} The ID of the entity if successfully created, otherwise {@link Uuid|Uuid.NULL}. */ Q_INVOKABLE QUuid addEntity(const EntityItemProperties& properties, bool avatarEntity = false) { - QString entityHostType = avatarEntity ? "avatar" : "domain"; - return addEntity(properties, entityHostType); + entity::HostType entityHostType = avatarEntity ? entity::HostType::AVATAR : entity::HostType::DOMAIN; + return addEntityInternal(properties, entityHostType); } /// temporary method until addEntity can be used from QJSEngine @@ -293,7 +306,7 @@ public slots: * @param {Uuid} entityID - The ID of the entity to clone. * @returns {Uuid} The ID of the new entity if successfully cloned, otherwise {@link Uuid|Uuid.NULL}. */ - Q_INVOKABLE QUuid cloneEntity(QUuid entityIDToClone); + Q_INVOKABLE QUuid cloneEntity(const QUuid& entityID); /**jsdoc * Get the properties of an entity. @@ -312,8 +325,8 @@ public slots: * var properties = Entities.getEntityProperties(entityID, ["color"]); * print("Entity color: " + JSON.stringify(properties.color)); */ - Q_INVOKABLE EntityItemProperties getEntityProperties(QUuid entityID); - Q_INVOKABLE EntityItemProperties getEntityProperties(QUuid entityID, EntityPropertyFlags desiredProperties); + Q_INVOKABLE EntityItemProperties getEntityProperties(const QUuid& entityID); + Q_INVOKABLE EntityItemProperties getEntityProperties(const QUuid& entityID, EntityPropertyFlags desiredProperties); /**jsdoc * Update an entity with specified properties. @@ -337,7 +350,7 @@ public slots: * properties = Entities.getEntityProperties(entityID, ["color"]); * print("Entity color: " + JSON.stringify(properties.color)); */ - Q_INVOKABLE QUuid editEntity(QUuid entityID, const EntityItemProperties& properties); + Q_INVOKABLE QUuid editEntity(const QUuid& entityID, const EntityItemProperties& properties); /**jsdoc * Delete an entity. @@ -355,8 +368,50 @@ public slots: * Entities.deleteEntity(entityID); * }, 3000); */ - Q_INVOKABLE void deleteEntity(QUuid entityID); + Q_INVOKABLE void deleteEntity(const QUuid& entityID); + /**jsdoc + * Get an entities type as a string. + * @function Entities.deleteEntity + * @param {Uuid} id - The id of the entity to get the type of. + */ + Q_INVOKABLE QString getEntityType(const QUuid& entityID); + + /**jsdoc + * Get the entity script object. In particular, this is useful for accessing the event bridge for a Web + * entity. + * @function Entities.getEntityObject + * @param {Uuid} id - The ID of the entity to get the script object of. + * @returns {object} The script object for the entity if found. + */ + Q_INVOKABLE QObject* getEntityObject(const QUuid& id); + + /**jsdoc + * Check whether an entities's assets have been loaded. For example, for an Model entity the result indicates + * whether its textures have been loaded. + * @function Entities.isLoaded + * @param {Uuid} id - The ID of the entity to check. + * @returns {boolean} true if the entity's assets have been loaded, otherwise false. + */ + Q_INVOKABLE bool isLoaded(const QUuid& id); + + /**jsdoc + * Check if there is an object of a given ID. + * @function Entities.isAddedEntity + * @param {Uuid} id - The ID to check. + * @returns {boolean} true if an object with the given ID exists, false otherwise. + */ + Q_INVOKABLE bool isAddedEntity(const QUuid& id); + + /**jsdoc + * Calculates the size of the given text in the specified object if it is a text entity. + * @function Entities.textSize + * @param {Uuid} id - The ID of the entity to use for calculation. + * @param {string} text - The string to calculate the size of. + * @returns {Size} The size of the text in meters if the object is a text entity, otherwise + * { height: 0, width : 0 }. + */ + Q_INVOKABLE QSizeF textSize(const QUuid& id, const QString& text); /**jsdoc * Call a method in a client entity script from a client script or client entity script, or call a method in a server @@ -368,7 +423,7 @@ public slots: * @param {string} method - The name of the method to call. * @param {string[]} [parameters=[]] - The parameters to call the specified method with. */ - Q_INVOKABLE void callEntityMethod(QUuid entityID, const QString& method, const QStringList& params = QStringList()); + Q_INVOKABLE void callEntityMethod(const QUuid& entityID, const QString& method, const QStringList& params = QStringList()); /**jsdoc * Call a method in a server entity script from a client script or client entity script. The entity script method must be @@ -380,7 +435,7 @@ public slots: * @param {string} method - The name of the method to call. * @param {string[]} [parameters=[]] - The parameters to call the specified method with. */ - Q_INVOKABLE void callEntityServerMethod(QUuid entityID, const QString& method, const QStringList& params = QStringList()); + Q_INVOKABLE void callEntityServerMethod(const QUuid& entityID, const QString& method, const QStringList& params = QStringList()); /**jsdoc * Call a method in a specific user's client entity script from a server entity script. The entity script method must be @@ -391,7 +446,7 @@ public slots: * @param {string} method - The name of the method to call. * @param {string[]} [parameters=[]] - The parameters to call the specified method with. */ - Q_INVOKABLE void callEntityClientMethod(QUuid clientSessionID, QUuid entityID, const QString& method, + Q_INVOKABLE void callEntityClientMethod(const QUuid& clientSessionID, const QUuid& entityID, const QString& method, const QStringList& params = QStringList()); /**jsdoc @@ -521,7 +576,7 @@ public slots: * @returns {boolean} true if the reload request was successfully sent to the server, otherwise * false. */ - Q_INVOKABLE bool reloadServerScripts(QUuid entityID); + Q_INVOKABLE bool reloadServerScripts(const QUuid& entityID); /**jsdoc * Gets the status of server entity script attached to an entity @@ -540,7 +595,7 @@ public slots: * @param {string} errorInfo - "" if there is a server entity script running, otherwise it may contain extra * information on the error. */ - Q_INVOKABLE bool getServerScriptStatus(QUuid entityID, QScriptValue callback); + Q_INVOKABLE bool getServerScriptStatus(const QUuid& entityID, QScriptValue callback); /**jsdoc * Get metadata for certain entity properties such as script and serverScripts. @@ -570,7 +625,7 @@ public slots: * @param {object} result - The metadata for the requested entity property if there was no error, otherwise * undefined. */ - Q_INVOKABLE bool queryPropertyMetadata(QUuid entityID, QScriptValue property, QScriptValue scopeOrCallback, + Q_INVOKABLE bool queryPropertyMetadata(const QUuid& entityID, QScriptValue property, QScriptValue scopeOrCallback, QScriptValue methodOrName = QScriptValue()); @@ -654,7 +709,7 @@ public slots: * Entities.setVoxelSphere(polyVox, position, 0.9, 255); */ // FIXME move to a renderable entity interface - Q_INVOKABLE bool setVoxelSphere(QUuid entityID, const glm::vec3& center, float radius, int value); + Q_INVOKABLE bool setVoxelSphere(const QUuid& entityID, const glm::vec3& center, float radius, int value); /**jsdoc * Set the values of all voxels in a capsule-shaped portion of a {@link Entities.EntityType|PolyVox} entity. @@ -678,7 +733,7 @@ public slots: * Entities.setVoxelCapsule(polyVox, startPosition, endPosition, 0.5, 255); */ // FIXME move to a renderable entity interface - Q_INVOKABLE bool setVoxelCapsule(QUuid entityID, const glm::vec3& start, const glm::vec3& end, float radius, int value); + Q_INVOKABLE bool setVoxelCapsule(const QUuid& entityID, const glm::vec3& start, const glm::vec3& end, float radius, int value); /**jsdoc * Set the value of a particular voxels in a {@link Entities.EntityType|PolyVox} entity. @@ -700,7 +755,7 @@ public slots: * Entities.setVoxel(entity, { x: 0, y: 0, z: 0 }, 0); */ // FIXME move to a renderable entity interface - Q_INVOKABLE bool setVoxel(QUuid entityID, const glm::vec3& position, int value); + Q_INVOKABLE bool setVoxel(const QUuid& entityID, const glm::vec3& position, int value); /**jsdoc * Set the values of all voxels in a {@link Entities.EntityType|PolyVox} entity. @@ -718,7 +773,7 @@ public slots: * Entities.setAllVoxels(entity, 1); */ // FIXME move to a renderable entity interface - Q_INVOKABLE bool setAllVoxels(QUuid entityID, int value); + Q_INVOKABLE bool setAllVoxels(const QUuid& entityID, int value); /**jsdoc * Set the values of all voxels in a cubic portion of a {@link Entities.EntityType|PolyVox} entity. @@ -743,7 +798,7 @@ public slots: * Entities.setVoxelsInCuboid(polyVox, cuboidPosition, cuboidSize, 0); */ // FIXME move to a renderable entity interface - Q_INVOKABLE bool setVoxelsInCuboid(QUuid entityID, const glm::vec3& lowPosition, const glm::vec3& cuboidSize, int value); + Q_INVOKABLE bool setVoxelsInCuboid(const QUuid& entityID, const glm::vec3& lowPosition, const glm::vec3& cuboidSize, int value); /**jsdoc * Convert voxel coordinates in a {@link Entities.EntityType|PolyVox} entity to world coordinates. Voxel coordinates are @@ -859,7 +914,7 @@ public slots: * ]); * }, 2000); */ - Q_INVOKABLE bool setAllPoints(QUuid entityID, const QVector& points); + Q_INVOKABLE bool setAllPoints(const QUuid& entityID, const QVector& points); /**jsdoc * Append a point to a {@link Entities.EntityType|Line} entity. @@ -887,7 +942,7 @@ public slots: * // Add a third point to create a "V". * Entities.appendPoint(entity, { x: 1, y: 1, z: 0 }); */ - Q_INVOKABLE bool appendPoint(QUuid entityID, const glm::vec3& point); + Q_INVOKABLE bool appendPoint(const QUuid& entityID, const glm::vec3& point); /**jsdoc * Dumps debug information about all entities in Interface's local in-memory tree of entities it knows about to the program log. @@ -1261,11 +1316,11 @@ public slots: /**jsdoc - * Get the IDs of entities, overlays, and avatars that are directly parented to an entity, overlay, or avatar model. Recurse on the IDs returned by the function to get all descendants of an entity, overlay, or avatar. + * Get the IDs of entities and avatars that are directly parented to an entity or avatar model. Recurse on the IDs returned by the function to get all descendants of an entity or avatar. * @function Entities.getChildrenIDs - * @param {Uuid} parentID - The ID of the entity, overlay, or avatar to get the children IDs of. - * @returns {Uuid[]} An array of entity, overlay, and avatar IDs that are parented directly to the parentID - * entity, overlay, or avatar. Does not include children's children, etc. The array is empty if no children can be found or + * @param {Uuid} parentID - The ID of the entity or avatar to get the children IDs of. + * @returns {Uuid[]} An array of entity and avatar IDs that are parented directly to the parentID + * entity or avatar. Does not include children's children, etc. The array is empty if no children can be found or * parentID cannot be found. * @example Report the children of an entity. * function createEntity(description, position, parent) { @@ -1291,12 +1346,12 @@ public slots: Q_INVOKABLE QVector getChildrenIDs(const QUuid& parentID); /**jsdoc - * Get the IDs of entities, overlays, and avatars that are directly parented to an entity, overlay, or avatar model's joint. + * Get the IDs of entities and avatars that are directly parented to an entity or avatar model's joint. * @function Entities.getChildrenIDsOfJoint - * @param {Uuid} parentID - The ID of the entity, overlay, or avatar to get the children IDs of. + * @param {Uuid} parentID - The ID of the entity or avatar to get the children IDs of. * @param {number} jointIndex - Integer number of the model joint to get the children IDs of. - * @returns {Uuid[]} An array of entity, overlay, and avatar IDs that are parented directly to the parentID - * entity, overlay, or avatar at the jointIndex joint. Does not include children's children, etc. The + * @returns {Uuid[]} An array of entity and avatar IDs that are parented directly to the parentID + * entity or avatar at the jointIndex joint. Does not include children's children, etc. The * array is empty if no children can be found or parentID cannot be found. * @example Report the children of your avatar's right hand. * function createEntity(description, position, parent) { @@ -1326,11 +1381,11 @@ public slots: Q_INVOKABLE QVector getChildrenIDsOfJoint(const QUuid& parentID, int jointIndex); /**jsdoc - * Check whether an entity or overlay has an entity as an ancestor (parent, parent's parent, etc.). + * Check whether an entity has an entity as an ancestor (parent, parent's parent, etc.). * @function Entities.isChildOfParent - * @param {Uuid} childID - The ID of the child entity or overlay to test for being a child, grandchild, etc. + * @param {Uuid} childID - The ID of the child entity to test for being a child, grandchild, etc. * @param {Uuid} parentID - The ID of the parent entity to test for being a parent, grandparent, etc. - * @returns {boolean} true if the childID entity or overlay has the parentID entity + * @returns {boolean} true if the childID entity has the parentID entity * as a parent or grandparent etc., otherwise false. * @example Check that a grandchild entity is a child of its grandparent. * function createEntity(description, position, parent) { @@ -1352,15 +1407,14 @@ public slots: * * print("grandChild has root as parent: " + Entities.isChildOfParent(grandChild, root)); // true */ - Q_INVOKABLE bool isChildOfParent(QUuid childID, QUuid parentID); + Q_INVOKABLE bool isChildOfParent(const QUuid& childID, const QUuid& parentID); /**jsdoc - * Get the type — entity, overlay, or avatar — of an in-world item. + * Get the type — entity or avatar — of an in-world item. * @function Entities.getNestableType - * @param {Uuid} entityID - The ID of the item to get the type of. - * @returns {string} The type of the item: "entity" if the item is an entity, "overlay" if the - * the item is an overlay, "avatar" if the item is an avatar; otherwise "unknown" if the item - * cannot be found. + * @param {Uuid} id - The ID of the item to get the type of. + * @returns {string} The type of the item: "entity" if the item is an entity, "avatar" + * if the item is an avatar; otherwise "unknown" if the item cannot be found. * @example Print some nestable types. * var entity = Entities.addEntity({ * type: "Sphere", @@ -1371,7 +1425,7 @@ public slots: * print(Entities.getNestableType(entity)); // "entity" * print(Entities.getNestableType(Uuid.generate())); // "unknown" */ - Q_INVOKABLE QString getNestableType(QUuid entityID); + Q_INVOKABLE QString getNestableType(const QUuid& id); /**jsdoc * Get the ID of the {@link Entities.EntityType|Web} entity that has keyboard focus. @@ -1383,11 +1437,10 @@ public slots: /**jsdoc * Set the {@link Entities.EntityType|Web} entity that has keyboard focus. * @function Entities.setKeyboardFocusEntity - * @param {Uuid} entityID - The ID of the {@link Entities.EntityType|Web} entity to set keyboard focus to. Use + * @param {Uuid} id - The ID of the {@link Entities.EntityType|Web} entity to set keyboard focus to. Use * null or {@link Uuid|Uuid.NULL} to unset keyboard focus from an entity. */ - Q_INVOKABLE void setKeyboardFocusEntity(const EntityItemID& id); - + Q_INVOKABLE void setKeyboardFocusEntity(const QUuid& id); /**jsdoc * Emit a {@link Entities.mousePressOnEntity|mousePressOnEntity} event. @@ -1469,7 +1522,7 @@ public slots: * @returns {boolean} true if the entity can be found and it wants hand controller pointer events, otherwise * false. */ - Q_INVOKABLE bool wantsHandControllerPointerEvents(QUuid id); + Q_INVOKABLE bool wantsHandControllerPointerEvents(const QUuid& id); /**jsdoc * Send a script event over a {@link Entities.EntityType|Web} entity's EventBridge to the Web page's scripts. @@ -1510,7 +1563,7 @@ public slots: * @deprecated Use the {@link Graphics} API instead. */ // FIXME move to a renderable entity interface - Q_INVOKABLE void getMeshes(QUuid entityID, QScriptValue callback); + Q_INVOKABLE void getMeshes(const QUuid& entityID, QScriptValue callback); /**jsdoc * Get the object to world transform, excluding scale, of an entity. diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index bf91d71050..53b4fb4fe4 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -2953,6 +2953,23 @@ QStringList EntityTree::getJointNames(const QUuid& entityID) const { return entity->getJointNames(); } +std::function EntityTree::_getEntityObjectOperator = nullptr; +std::function EntityTree::_textSizeOperator = nullptr; + +QObject* EntityTree::getEntityObject(const QUuid& id) { + if (_getEntityObjectOperator) { + return _getEntityObjectOperator(id); + } + return nullptr; +} + +QSizeF EntityTree::textSize(const QUuid& id, const QString& text) { + if (_textSizeOperator) { + return _textSizeOperator(id, text); + } + return QSizeF(0.0f, 0.0f); +} + void EntityTree::updateEntityQueryAACubeWorker(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender, MovingEntitiesOperator& moveOperator, bool force, bool tellServer) { // if the queryBox has changed, tell the entity-server diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 367af7ba1e..dcce0e4b99 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -262,6 +262,12 @@ public: void setIsServerlessMode(bool value) { _serverlessDomain = value; } bool isServerlessMode() const { return _serverlessDomain; } + static void setGetEntityObjectOperator(std::function getEntityObjectOperator) { _getEntityObjectOperator = getEntityObjectOperator; } + static QObject* getEntityObject(const QUuid& id); + + static void setTextSizeOperator(std::function textSizeOperator) { _textSizeOperator = textSizeOperator; } + static QSizeF textSize(const QUuid& id, const QString& text); + std::map getNamedPaths() const { return _namedPaths; } void updateEntityQueryAACube(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender, @@ -370,6 +376,9 @@ private: std::shared_ptr _myAvatar{ nullptr }; + static std::function _getEntityObjectOperator; + static std::function _textSizeOperator; + std::vector _staleProxies; bool _serverlessDomain { false }; diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 3b085457d1..ce6f20262f 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -139,16 +139,22 @@ bool EntityTreeElement::bestFitBounds(const glm::vec3& minPoint, const glm::vec3 return false; } -bool checkFilterSettings(const EntityItemPointer& entity, PickFilter searchFilter) { +bool EntityTreeElement::checkFilterSettings(const EntityItemPointer& entity, PickFilter searchFilter) { bool visible = entity->isVisible(); - bool collidable = !entity->getCollisionless() && (entity->getShapeType() != SHAPE_TYPE_NONE); + entity::HostType hostType = entity->getEntityHostType(); if ((!searchFilter.doesPickVisible() && visible) || (!searchFilter.doesPickInvisible() && !visible) || - (!searchFilter.doesPickCollidable() && collidable) || (!searchFilter.doesPickNonCollidable() && !collidable) || - (!searchFilter.doesPickDomainEntities() && entity->isDomainEntity()) || - (!searchFilter.doesPickAvatarEntities() && entity->isAvatarEntity()) || - (!searchFilter.doesPickLocalEntities() && entity->isLocalEntity())) { + (!searchFilter.doesPickDomainEntities() && hostType == entity::HostType::DOMAIN) || + (!searchFilter.doesPickAvatarEntities() && hostType == entity::HostType::AVATAR) || + (!searchFilter.doesPickLocalEntities() && hostType == entity::HostType::LOCAL)) { return false; } + // We only check the collidable filters for non-local entities, because local entities are always collisionless + bool collidable = !entity->getCollisionless() && (entity->getShapeType() != SHAPE_TYPE_NONE); + if (hostType != entity::HostType::LOCAL) { + if ((collidable && !searchFilter.doesPickCollidable()) || (!collidable && !searchFilter.doesPickNonCollidable())) { + return false; + } + } return true; } diff --git a/libraries/entities/src/EntityTreeElement.h b/libraries/entities/src/EntityTreeElement.h index 3f1fda57bd..f82eaa7fb1 100644 --- a/libraries/entities/src/EntityTreeElement.h +++ b/libraries/entities/src/EntityTreeElement.h @@ -134,6 +134,7 @@ public: virtual bool isRendered() const override { return getShouldRender(); } virtual bool deleteApproved() const override { return !hasEntities(); } + static bool checkFilterSettings(const EntityItemPointer& entity, PickFilter searchFilter); virtual bool canPickIntersect() const override { return hasEntities(); } virtual EntityItemID evalRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h index 8ad654c638..91b71513dc 100644 --- a/libraries/entities/src/EntityTypes.h +++ b/libraries/entities/src/EntityTypes.h @@ -84,8 +84,7 @@ public: * {@link Entities.EntityProperties-Light|EntityProperties-Light} * "Zone"A volume of lighting effects and avatar permissions. * {@link Entities.EntityProperties-Zone|EntityProperties-Zone} - * "Material"Modifies the existing materials on Model entities, Shape entities, - * {@link Overlays.OverlayType|model overlays}, and avatars. + * "Material"Modifies the existing materials on entities and avatars. * {@link Entities.EntityProperties-Material|EntityProperties-Material} * * diff --git a/libraries/entities/src/GizmoEntityItem.cpp b/libraries/entities/src/GizmoEntityItem.cpp index c9f205d289..f2bef0cb80 100644 --- a/libraries/entities/src/GizmoEntityItem.cpp +++ b/libraries/entities/src/GizmoEntityItem.cpp @@ -10,6 +10,8 @@ #include "EntityItemProperties.h" +#include + EntityItemPointer GizmoEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { Pointer entity(new GizmoEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); }); entity->setProperties(properties); @@ -109,15 +111,14 @@ bool GizmoEntityItem::supportsDetailedIntersection() const { return _gizmoType == GizmoType::RING; } -#include - bool GizmoEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, bool precisionPicking) const { glm::vec3 dimensions = getScaledDimensions(); glm::vec2 xyDimensions(dimensions.x, dimensions.z); - glm::quat rotation = glm::angleAxis((float)M_PI_2, Vectors::RIGHT) * getWorldOrientation(); + glm::quat rotation = getWorldOrientation(); + rotation = glm::angleAxis(-(float)M_PI_2, rotation * Vectors::RIGHT) * rotation; glm::vec3 position = getWorldPosition() + rotation * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint())); if (findRayRectangleIntersection(origin, direction, rotation, position, xyDimensions, distance)) { @@ -150,7 +151,8 @@ bool GizmoEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin, //// Scale the dimensions by the diameter glm::vec3 dimensions = getScaledDimensions(); glm::vec2 xyDimensions(dimensions.x, dimensions.z); - glm::quat rotation = glm::angleAxis((float)M_PI_2, Vectors::RIGHT) * getWorldOrientation(); + glm::quat rotation = getWorldOrientation(); + rotation = glm::angleAxis(-(float)M_PI_2, rotation * Vectors::RIGHT) * rotation; glm::vec3 position = getWorldPosition(); glm::quat inverseRot = glm::inverse(rotation); diff --git a/libraries/entities/src/GrabPropertyGroup.h b/libraries/entities/src/GrabPropertyGroup.h index 33550fef3d..d76ac46a81 100644 --- a/libraries/entities/src/GrabPropertyGroup.h +++ b/libraries/entities/src/GrabPropertyGroup.h @@ -64,9 +64,9 @@ static const glm::vec3 INITIAL_EQUIPPABLE_INDICATOR_OFFSET { glm::vec3(0.0f) }; * @property {string} equippableIndicatorURL="" - If non-empty, this model will be used to indicate that an * entity is equippable, rather than the default. * @property {Vec3} equippableIndicatorScale=1,1,1 - If equippableIndicatorURL is non-empty, this controls the - scale of the displayed overlay. + scale of the displayed indicator. * @property {Vec3} equippableIndicatorOffset=0,0,0 - If equippableIndicatorURL is non-empty, this controls the - relative offset of the displayed overlay from the equippable entity. + relative offset of the displayed object from the equippable entity. */ diff --git a/libraries/entities/src/ImageEntityItem.cpp b/libraries/entities/src/ImageEntityItem.cpp index 1e8e4511cf..837e824f4a 100644 --- a/libraries/entities/src/ImageEntityItem.cpp +++ b/libraries/entities/src/ImageEntityItem.cpp @@ -35,11 +35,11 @@ EntityItemProperties ImageEntityItem::getProperties(const EntityPropertyFlags& d withReadLock([&] { _pulseProperties.getProperties(properties); }); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(billboardMode, getBillboardMode); COPY_ENTITY_PROPERTY_TO_PROPERTIES(imageURL, getImageURL); COPY_ENTITY_PROPERTY_TO_PROPERTIES(emissive, getEmissive); COPY_ENTITY_PROPERTY_TO_PROPERTIES(keepAspectRatio, getKeepAspectRatio); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(billboardMode, getBillboardMode); COPY_ENTITY_PROPERTY_TO_PROPERTIES(subImage, getSubImage); return properties; @@ -54,11 +54,11 @@ bool ImageEntityItem::setProperties(const EntityItemProperties& properties) { bool pulsePropertiesChanged = _pulseProperties.setProperties(properties); somethingChanged |= pulsePropertiesChanged; }); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(billboardMode, setBillboardMode); SET_ENTITY_PROPERTY_FROM_PROPERTIES(imageURL, setImageURL); SET_ENTITY_PROPERTY_FROM_PROPERTIES(emissive, setEmissive); SET_ENTITY_PROPERTY_FROM_PROPERTIES(keepAspectRatio, setKeepAspectRatio); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(billboardMode, setBillboardMode); SET_ENTITY_PROPERTY_FROM_PROPERTIES(subImage, setSubImage); if (somethingChanged) { @@ -91,11 +91,11 @@ int ImageEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, bytesRead += bytesFromPulse; dataAt += bytesFromPulse; }); + READ_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, BillboardMode, setBillboardMode); READ_ENTITY_PROPERTY(PROP_IMAGE_URL, QString, setImageURL); READ_ENTITY_PROPERTY(PROP_EMISSIVE, bool, setEmissive); READ_ENTITY_PROPERTY(PROP_KEEP_ASPECT_RATIO, bool, setKeepAspectRatio); - READ_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, BillboardMode, setBillboardMode); READ_ENTITY_PROPERTY(PROP_SUB_IMAGE, QRect, setSubImage); return bytesRead; @@ -107,11 +107,11 @@ EntityPropertyFlags ImageEntityItem::getEntityProperties(EncodeBitstreamParams& requestedProperties += PROP_COLOR; requestedProperties += PROP_ALPHA; requestedProperties += _pulseProperties.getEntityProperties(params); + requestedProperties += PROP_BILLBOARD_MODE; requestedProperties += PROP_IMAGE_URL; requestedProperties += PROP_EMISSIVE; requestedProperties += PROP_KEEP_ASPECT_RATIO; - requestedProperties += PROP_BILLBOARD_MODE; requestedProperties += PROP_SUB_IMAGE; return requestedProperties; @@ -133,14 +133,24 @@ void ImageEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit _pulseProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); }); + APPEND_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, (uint32_t)getBillboardMode()); APPEND_ENTITY_PROPERTY(PROP_IMAGE_URL, getImageURL()); APPEND_ENTITY_PROPERTY(PROP_EMISSIVE, getEmissive()); APPEND_ENTITY_PROPERTY(PROP_KEEP_ASPECT_RATIO, getKeepAspectRatio()); - APPEND_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, (uint32_t)getBillboardMode()); APPEND_ENTITY_PROPERTY(PROP_SUB_IMAGE, getSubImage()); } +glm::vec3 ImageEntityItem::getRaycastDimensions() const { + glm::vec3 dimensions = getScaledDimensions(); + if (getBillboardMode() != BillboardMode::NONE) { + float max = glm::max(dimensions.x, glm::max(dimensions.y, dimensions.z)); + const float SQRT_2 = 1.41421356237f; + return glm::vec3(SQRT_2 * max); + } + return dimensions; +} + bool ImageEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, @@ -149,6 +159,7 @@ bool ImageEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec2 xyDimensions(dimensions.x, dimensions.y); glm::quat rotation = getWorldOrientation(); glm::vec3 position = getWorldPosition() + rotation * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint())); + rotation = EntityItem::getBillboardRotation(position, rotation, _billboardMode); if (findRayRectangleIntersection(origin, direction, rotation, position, xyDimensions, distance)) { glm::vec3 forward = rotation * Vectors::FRONT; diff --git a/libraries/entities/src/ImageEntityItem.h b/libraries/entities/src/ImageEntityItem.h index 05218016b3..a1be5a0554 100644 --- a/libraries/entities/src/ImageEntityItem.h +++ b/libraries/entities/src/ImageEntityItem.h @@ -43,6 +43,7 @@ public: EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) override; + glm::vec3 getRaycastDimensions() const override; virtual bool supportsDetailedIntersection() const override { return true; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& element, float& distance, @@ -77,15 +78,15 @@ public: PulsePropertyGroup getPulseProperties() const; protected: - QString _imageURL; - bool _emissive { false }; - bool _keepAspectRatio { true }; - BillboardMode _billboardMode; - QRect _subImage; - glm::u8vec3 _color; float _alpha; PulsePropertyGroup _pulseProperties; + BillboardMode _billboardMode; + + QString _imageURL; + bool _emissive { false }; + bool _keepAspectRatio { true }; + QRect _subImage; }; #endif // hifi_ImageEntityItem_h diff --git a/libraries/entities/src/LineEntityItem.cpp b/libraries/entities/src/LineEntityItem.cpp index 12d1178690..acb5bc08f6 100644 --- a/libraries/entities/src/LineEntityItem.cpp +++ b/libraries/entities/src/LineEntityItem.cpp @@ -23,7 +23,6 @@ const int LineEntityItem::MAX_POINTS_PER_LINE = 70; - EntityItemPointer LineEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItemPointer entity(new LineEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); }); entity->setProperties(properties); diff --git a/libraries/entities/src/TextEntityItem.cpp b/libraries/entities/src/TextEntityItem.cpp index a743d0a7a9..bc98c61ff7 100644 --- a/libraries/entities/src/TextEntityItem.cpp +++ b/libraries/entities/src/TextEntityItem.cpp @@ -52,6 +52,7 @@ EntityItemProperties TextEntityItem::getProperties(const EntityPropertyFlags& de withReadLock([&] { _pulseProperties.getProperties(properties); }); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(billboardMode, getBillboardMode); COPY_ENTITY_PROPERTY_TO_PROPERTIES(text, getText); COPY_ENTITY_PROPERTY_TO_PROPERTIES(lineHeight, getLineHeight); @@ -59,7 +60,6 @@ EntityItemProperties TextEntityItem::getProperties(const EntityPropertyFlags& de COPY_ENTITY_PROPERTY_TO_PROPERTIES(textAlpha, getTextAlpha); COPY_ENTITY_PROPERTY_TO_PROPERTIES(backgroundColor, getBackgroundColor); COPY_ENTITY_PROPERTY_TO_PROPERTIES(backgroundAlpha, getBackgroundAlpha); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(billboardMode, getBillboardMode); COPY_ENTITY_PROPERTY_TO_PROPERTIES(leftMargin, getLeftMargin); COPY_ENTITY_PROPERTY_TO_PROPERTIES(rightMargin, getRightMargin); COPY_ENTITY_PROPERTY_TO_PROPERTIES(topMargin, getTopMargin); @@ -75,6 +75,7 @@ bool TextEntityItem::setProperties(const EntityItemProperties& properties) { bool pulsePropertiesChanged = _pulseProperties.setProperties(properties); somethingChanged |= pulsePropertiesChanged; }); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(billboardMode, setBillboardMode); SET_ENTITY_PROPERTY_FROM_PROPERTIES(text, setText); SET_ENTITY_PROPERTY_FROM_PROPERTIES(lineHeight, setLineHeight); @@ -82,7 +83,6 @@ bool TextEntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(textAlpha, setTextAlpha); SET_ENTITY_PROPERTY_FROM_PROPERTIES(backgroundColor, setBackgroundColor); SET_ENTITY_PROPERTY_FROM_PROPERTIES(backgroundAlpha, setBackgroundAlpha); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(billboardMode, setBillboardMode); SET_ENTITY_PROPERTY_FROM_PROPERTIES(leftMargin, setLeftMargin); SET_ENTITY_PROPERTY_FROM_PROPERTIES(rightMargin, setRightMargin); SET_ENTITY_PROPERTY_FROM_PROPERTIES(topMargin, setTopMargin); @@ -117,6 +117,7 @@ int TextEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, bytesRead += bytesFromPulse; dataAt += bytesFromPulse; }); + READ_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, BillboardMode, setBillboardMode); READ_ENTITY_PROPERTY(PROP_TEXT, QString, setText); READ_ENTITY_PROPERTY(PROP_LINE_HEIGHT, float, setLineHeight); @@ -124,7 +125,6 @@ int TextEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, READ_ENTITY_PROPERTY(PROP_TEXT_ALPHA, float, setTextAlpha); READ_ENTITY_PROPERTY(PROP_BACKGROUND_COLOR, glm::u8vec3, setBackgroundColor); READ_ENTITY_PROPERTY(PROP_BACKGROUND_ALPHA, float, setBackgroundAlpha); - READ_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, BillboardMode, setBillboardMode); READ_ENTITY_PROPERTY(PROP_LEFT_MARGIN, float, setLeftMargin); READ_ENTITY_PROPERTY(PROP_RIGHT_MARGIN, float, setRightMargin); READ_ENTITY_PROPERTY(PROP_TOP_MARGIN, float, setTopMargin); @@ -137,13 +137,14 @@ EntityPropertyFlags TextEntityItem::getEntityProperties(EncodeBitstreamParams& p EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); requestedProperties += _pulseProperties.getEntityProperties(params); + requestedProperties += PROP_BILLBOARD_MODE; + requestedProperties += PROP_TEXT; requestedProperties += PROP_LINE_HEIGHT; requestedProperties += PROP_TEXT_COLOR; requestedProperties += PROP_TEXT_ALPHA; requestedProperties += PROP_BACKGROUND_COLOR; requestedProperties += PROP_BACKGROUND_ALPHA; - requestedProperties += PROP_BILLBOARD_MODE; requestedProperties += PROP_LEFT_MARGIN; requestedProperties += PROP_RIGHT_MARGIN; requestedProperties += PROP_TOP_MARGIN; @@ -166,6 +167,7 @@ void TextEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits _pulseProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); }); + APPEND_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, (uint32_t)getBillboardMode()); APPEND_ENTITY_PROPERTY(PROP_TEXT, getText()); APPEND_ENTITY_PROPERTY(PROP_LINE_HEIGHT, getLineHeight()); @@ -173,12 +175,20 @@ void TextEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits APPEND_ENTITY_PROPERTY(PROP_TEXT_ALPHA, getTextAlpha()); APPEND_ENTITY_PROPERTY(PROP_BACKGROUND_COLOR, getBackgroundColor()); APPEND_ENTITY_PROPERTY(PROP_BACKGROUND_ALPHA, getBackgroundAlpha()); - APPEND_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, (uint32_t)getBillboardMode()); APPEND_ENTITY_PROPERTY(PROP_LEFT_MARGIN, getLeftMargin()); APPEND_ENTITY_PROPERTY(PROP_RIGHT_MARGIN, getRightMargin()); APPEND_ENTITY_PROPERTY(PROP_TOP_MARGIN, getTopMargin()); APPEND_ENTITY_PROPERTY(PROP_BOTTOM_MARGIN, getBottomMargin()); - +} + +glm::vec3 TextEntityItem::getRaycastDimensions() const { + glm::vec3 dimensions = getScaledDimensions(); + if (getBillboardMode() != BillboardMode::NONE) { + float max = glm::max(dimensions.x, glm::max(dimensions.y, dimensions.z)); + const float SQRT_2 = 1.41421356237f; + return glm::vec3(SQRT_2 * max); + } + return dimensions; } bool TextEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, @@ -189,6 +199,7 @@ bool TextEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec2 xyDimensions(dimensions.x, dimensions.y); glm::quat rotation = getWorldOrientation(); glm::vec3 position = getWorldPosition() + rotation * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint())); + rotation = EntityItem::getBillboardRotation(position, rotation, _billboardMode); if (findRayRectangleIntersection(origin, direction, rotation, position, xyDimensions, distance)) { glm::vec3 forward = rotation * Vectors::FRONT; diff --git a/libraries/entities/src/TextEntityItem.h b/libraries/entities/src/TextEntityItem.h index 5ca6823052..1ead9d3e15 100644 --- a/libraries/entities/src/TextEntityItem.h +++ b/libraries/entities/src/TextEntityItem.h @@ -47,6 +47,7 @@ public: EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) override; + glm::vec3 getRaycastDimensions() const override; virtual bool supportsDetailedIntersection() const override { return true; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& element, float& distance, @@ -99,6 +100,8 @@ public: PulsePropertyGroup getPulseProperties() const; private: + BillboardMode _billboardMode; + QString _text; float _lineHeight; glm::u8vec3 _textColor; @@ -106,7 +109,6 @@ private: glm::u8vec3 _backgroundColor; float _backgroundAlpha; PulsePropertyGroup _pulseProperties; - BillboardMode _billboardMode; float _leftMargin; float _rightMargin; float _topMargin; diff --git a/libraries/entities/src/WebEntityItem.cpp b/libraries/entities/src/WebEntityItem.cpp index 459d512311..5a948fbfd4 100644 --- a/libraries/entities/src/WebEntityItem.cpp +++ b/libraries/entities/src/WebEntityItem.cpp @@ -48,12 +48,14 @@ EntityItemProperties WebEntityItem::getProperties(const EntityPropertyFlags& des withReadLock([&] { _pulseProperties.getProperties(properties); }); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(billboardMode, getBillboardMode); COPY_ENTITY_PROPERTY_TO_PROPERTIES(sourceUrl, getSourceUrl); COPY_ENTITY_PROPERTY_TO_PROPERTIES(dpi, getDPI); COPY_ENTITY_PROPERTY_TO_PROPERTIES(scriptURL, getScriptURL); COPY_ENTITY_PROPERTY_TO_PROPERTIES(maxFPS, getMaxFPS); COPY_ENTITY_PROPERTY_TO_PROPERTIES(inputMode, getInputMode); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(showKeyboardFocusHighlight, getShowKeyboardFocusHighlight); return properties; } @@ -67,12 +69,14 @@ bool WebEntityItem::setProperties(const EntityItemProperties& properties) { bool pulsePropertiesChanged = _pulseProperties.setProperties(properties); somethingChanged |= pulsePropertiesChanged; }); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(billboardMode, setBillboardMode); SET_ENTITY_PROPERTY_FROM_PROPERTIES(sourceUrl, setSourceUrl); SET_ENTITY_PROPERTY_FROM_PROPERTIES(dpi, setDPI); SET_ENTITY_PROPERTY_FROM_PROPERTIES(scriptURL, setScriptURL); SET_ENTITY_PROPERTY_FROM_PROPERTIES(maxFPS, setMaxFPS); SET_ENTITY_PROPERTY_FROM_PROPERTIES(inputMode, setInputMode); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(showKeyboardFocusHighlight, setShowKeyboardFocusHighlight); if (somethingChanged) { bool wantDebug = false; @@ -105,12 +109,14 @@ int WebEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, i bytesRead += bytesFromPulse; dataAt += bytesFromPulse; }); + READ_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, BillboardMode, setBillboardMode); READ_ENTITY_PROPERTY(PROP_SOURCE_URL, QString, setSourceUrl); READ_ENTITY_PROPERTY(PROP_DPI, uint16_t, setDPI); READ_ENTITY_PROPERTY(PROP_SCRIPT_URL, QString, setScriptURL); READ_ENTITY_PROPERTY(PROP_MAX_FPS, uint8_t, setMaxFPS); READ_ENTITY_PROPERTY(PROP_INPUT_MODE, WebInputMode, setInputMode); + READ_ENTITY_PROPERTY(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, bool, setShowKeyboardFocusHighlight); return bytesRead; } @@ -120,12 +126,14 @@ EntityPropertyFlags WebEntityItem::getEntityProperties(EncodeBitstreamParams& pa requestedProperties += PROP_COLOR; requestedProperties += PROP_ALPHA; requestedProperties += _pulseProperties.getEntityProperties(params); + requestedProperties += PROP_BILLBOARD_MODE; requestedProperties += PROP_SOURCE_URL; requestedProperties += PROP_DPI; requestedProperties += PROP_SCRIPT_URL; requestedProperties += PROP_MAX_FPS; requestedProperties += PROP_INPUT_MODE; + requestedProperties += PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT; return requestedProperties; } @@ -144,12 +152,24 @@ void WebEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitst _pulseProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); }); + APPEND_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, (uint32_t)getBillboardMode()); APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, getSourceUrl()); APPEND_ENTITY_PROPERTY(PROP_DPI, getDPI()); APPEND_ENTITY_PROPERTY(PROP_SCRIPT_URL, getScriptURL()); APPEND_ENTITY_PROPERTY(PROP_MAX_FPS, getMaxFPS()); APPEND_ENTITY_PROPERTY(PROP_INPUT_MODE, (uint32_t)getInputMode()); + APPEND_ENTITY_PROPERTY(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, getShowKeyboardFocusHighlight()); +} + +glm::vec3 WebEntityItem::getRaycastDimensions() const { + glm::vec3 dimensions = getScaledDimensions(); + if (getBillboardMode() != BillboardMode::NONE) { + float max = glm::max(dimensions.x, glm::max(dimensions.y, dimensions.z)); + const float SQRT_2 = 1.41421356237f; + return glm::vec3(SQRT_2 * max); + } + return dimensions; } bool WebEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, @@ -160,6 +180,7 @@ bool WebEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const g glm::vec2 xyDimensions(dimensions.x, dimensions.y); glm::quat rotation = getWorldOrientation(); glm::vec3 position = getWorldPosition() + rotation * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint())); + rotation = EntityItem::getBillboardRotation(position, rotation, _billboardMode); if (findRayRectangleIntersection(origin, direction, rotation, position, xyDimensions, distance)) { glm::vec3 forward = rotation * Vectors::FRONT; @@ -230,17 +251,21 @@ float WebEntityItem::getAlpha() const { }); } +BillboardMode WebEntityItem::getBillboardMode() const { + return resultWithReadLock([&] { + return _billboardMode; + }); +} + +void WebEntityItem::setBillboardMode(BillboardMode value) { + withWriteLock([&] { + _billboardMode = value; + }); +} + void WebEntityItem::setSourceUrl(const QString& value) { withWriteLock([&] { - if (_sourceUrl != value) { - auto newURL = QUrl::fromUserInput(value); - - if (newURL.isValid()) { - _sourceUrl = newURL.toDisplayString(); - } else { - qCDebug(entities) << "Clearing web entity source URL since" << value << "cannot be parsed to a valid URL."; - } - } + _sourceUrl = value; }); } @@ -270,7 +295,7 @@ void WebEntityItem::setScriptURL(const QString& value) { if (newURL.isValid()) { _scriptURL = newURL.toDisplayString(); } else { - qCDebug(entities) << "Clearing web entity source URL since" << value << "cannot be parsed to a valid URL."; + qCDebug(entities) << "Not setting web entity script URL since" << value << "cannot be parsed to a valid URL."; } } }); @@ -306,6 +331,14 @@ WebInputMode WebEntityItem::getInputMode() const { }); } +void WebEntityItem::setShowKeyboardFocusHighlight(bool value) { + _showKeyboardFocusHighlight = value; +} + +bool WebEntityItem::getShowKeyboardFocusHighlight() const { + return _showKeyboardFocusHighlight; +} + PulsePropertyGroup WebEntityItem::getPulseProperties() const { return resultWithReadLock([&] { return _pulseProperties; diff --git a/libraries/entities/src/WebEntityItem.h b/libraries/entities/src/WebEntityItem.h index a0a2d65253..bb1e527712 100644 --- a/libraries/entities/src/WebEntityItem.h +++ b/libraries/entities/src/WebEntityItem.h @@ -44,6 +44,7 @@ public: EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) override; + glm::vec3 getRaycastDimensions() const override; virtual bool supportsDetailedIntersection() const override { return true; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& element, float& distance, @@ -60,6 +61,9 @@ public: float getAlpha() const; void setAlpha(float alpha); + void setBillboardMode(BillboardMode value); + BillboardMode getBillboardMode() const; + static const QString DEFAULT_SOURCE_URL; void setSourceUrl(const QString& value); QString getSourceUrl() const; @@ -77,18 +81,23 @@ public: void setInputMode(const WebInputMode& value); WebInputMode getInputMode() const; + bool getShowKeyboardFocusHighlight() const; + void setShowKeyboardFocusHighlight(bool value); + PulsePropertyGroup getPulseProperties() const; protected: glm::u8vec3 _color; float _alpha { 1.0f }; PulsePropertyGroup _pulseProperties; + BillboardMode _billboardMode; QString _sourceUrl; uint16_t _dpi; QString _scriptURL; uint8_t _maxFPS; WebInputMode _inputMode; + bool _showKeyboardFocusHighlight; }; #endif // hifi_WebEntityItem_h diff --git a/libraries/graphics-scripting/src/graphics-scripting/Forward.h b/libraries/graphics-scripting/src/graphics-scripting/Forward.h index 43b0daf407..747788aef8 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/Forward.h +++ b/libraries/graphics-scripting/src/graphics-scripting/Forward.h @@ -157,7 +157,7 @@ namespace scriptable { // QVariantMap armature; }; - // mixin class for Avatar/Entity/Overlay Rendering that expose their in-memory graphics::Meshes + // mixin class for Avatar + Entity Rendering that expose their in-memory graphics::Meshes class ModelProvider { public: NestableType modelProviderType; diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp index 3293d294d8..848f9d42ac 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp @@ -36,12 +36,12 @@ void GraphicsScriptingInterface::jsThrowError(const QString& error) { } } -bool GraphicsScriptingInterface::canUpdateModel(QUuid uuid, int meshIndex, int partNumber) { +bool GraphicsScriptingInterface::canUpdateModel(const QUuid& uuid, int meshIndex, int partNumber) { auto provider = getModelProvider(uuid); return provider && provider->canReplaceModelMeshPart(meshIndex, partNumber); } -bool GraphicsScriptingInterface::updateModel(QUuid uuid, const scriptable::ScriptableModelPointer& model) { +bool GraphicsScriptingInterface::updateModel(const QUuid& uuid, const scriptable::ScriptableModelPointer& model) { if (!model) { jsThrowError("null model argument"); } @@ -69,7 +69,7 @@ bool GraphicsScriptingInterface::updateModel(QUuid uuid, const scriptable::Scrip return provider->replaceScriptableModelMeshPart(base, -1, -1); } -scriptable::ModelProviderPointer GraphicsScriptingInterface::getModelProvider(QUuid uuid) { +scriptable::ModelProviderPointer GraphicsScriptingInterface::getModelProvider(const QUuid& uuid) { QString error; if (auto appProvider = DependencyManager::get()) { if (auto provider = appProvider->lookupModelProvider(uuid)) { @@ -107,7 +107,7 @@ scriptable::ScriptableModelPointer GraphicsScriptingInterface::newModel(const sc return modelWrapper; } -scriptable::ScriptableModelPointer GraphicsScriptingInterface::getModel(QUuid uuid) { +scriptable::ScriptableModelPointer GraphicsScriptingInterface::getModel(const QUuid& uuid) { QString error; bool success; QString providerType = "unknown"; diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h index 163e317ffa..a72c3be14b 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h @@ -39,13 +39,13 @@ public: public slots: /**jsdoc - * Returns a model reference object associated with the specified UUID ({@link EntityID}, {@link OverlayID}, or {@link AvatarID}). + * Returns a model reference object associated with the specified UUID ({@link EntityID} or {@link AvatarID}). * * @function Graphics.getModel * @param {UUID} entityID - The objectID of the model whose meshes are to be retrieved. * @returns {Graphics.Model} the resulting Model object */ - scriptable::ScriptableModelPointer getModel(QUuid uuid); + scriptable::ScriptableModelPointer getModel(const QUuid& uuid); /**jsdoc * @function Graphics.updateModel @@ -53,7 +53,7 @@ public slots: * @param {Graphics.Model} model * @returns {boolean} */ - bool updateModel(QUuid uuid, const scriptable::ScriptableModelPointer& model); + bool updateModel(const QUuid& uuid, const scriptable::ScriptableModelPointer& model); /**jsdoc * @function Graphics.canUpdateModel @@ -62,7 +62,7 @@ public slots: * @param {number} [partNumber=-1] * @returns {boolean} */ - bool canUpdateModel(QUuid uuid, int meshIndex = -1, int partNumber = -1); + bool canUpdateModel(const QUuid& uuid, int meshIndex = -1, int partNumber = -1); /**jsdoc * @function Graphics.newModel @@ -95,7 +95,7 @@ public slots: QString exportModelToOBJ(const scriptable::ScriptableModel& in); private: - scriptable::ModelProviderPointer getModelProvider(QUuid uuid); + scriptable::ModelProviderPointer getModelProvider(const QUuid& uuid); void jsThrowError(const QString& error); scriptable::MeshPointer getMeshPointer(scriptable::ScriptableMeshPointer meshProxy); scriptable::MeshPointer getMeshPointer(scriptable::ScriptableMesh& meshProxy); diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index d898c03597..5f55c189ce 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -260,6 +260,8 @@ enum class EntityVersion : PacketVersion { MissingWebEntityProperties, PulseProperties, RingGizmoEntities, + ShowKeyboardFocusHighlight, + WebBillboardMode, // Add new versions above here NUM_PACKET_TYPE, diff --git a/libraries/physics/src/ShapeManager.cpp b/libraries/physics/src/ShapeManager.cpp index ef7a4a1749..8acbe51540 100644 --- a/libraries/physics/src/ShapeManager.cpp +++ b/libraries/physics/src/ShapeManager.cpp @@ -17,7 +17,10 @@ #include "ShapeFactory.h" +const int MAX_RING_SIZE = 256; + ShapeManager::ShapeManager() { + _garbageRing.reserve(MAX_RING_SIZE); } ShapeManager::~ShapeManager() { @@ -33,8 +36,8 @@ const btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) { if (info.getType() == SHAPE_TYPE_NONE) { return nullptr; } - HashKey key = info.getHash(); - ShapeReference* shapeRef = _shapeMap.find(key); + HashKey hashKey(info.getHash()); + ShapeReference* shapeRef = _shapeMap.find(hashKey); if (shapeRef) { shapeRef->refCount++; return shapeRef->shape; @@ -44,23 +47,43 @@ const btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) { ShapeReference newRef; newRef.refCount = 1; newRef.shape = shape; - newRef.key = key; - _shapeMap.insert(key, newRef); + newRef.key = info.getHash(); + _shapeMap.insert(hashKey, newRef); } return shape; } // private helper method -bool ShapeManager::releaseShapeByKey(const HashKey& key) { - ShapeReference* shapeRef = _shapeMap.find(key); +bool ShapeManager::releaseShapeByKey(uint64_t key) { + HashKey hashKey(key); + ShapeReference* shapeRef = _shapeMap.find(hashKey); if (shapeRef) { if (shapeRef->refCount > 0) { shapeRef->refCount--; if (shapeRef->refCount == 0) { - _pendingGarbage.push_back(key); - const int MAX_SHAPE_GARBAGE_CAPACITY = 255; - if (_pendingGarbage.size() > MAX_SHAPE_GARBAGE_CAPACITY) { - collectGarbage(); + // look for existing entry in _garbageRing + int32_t ringSize = (int32_t)(_garbageRing.size()); + for (int32_t i = 0; i < ringSize; ++i) { + int32_t j = (_ringIndex + ringSize) % ringSize; + if (_garbageRing[j] == key) { + // already on the list, don't add it again + return true; + } + } + if (ringSize == MAX_RING_SIZE) { + // remove one + HashKey hashKeyToRemove(_garbageRing[_ringIndex]); + ShapeReference* shapeRef = _shapeMap.find(hashKeyToRemove); + if (shapeRef && shapeRef->refCount == 0) { + ShapeFactory::deleteShape(shapeRef->shape); + _shapeMap.remove(hashKeyToRemove); + } + // replace at _ringIndex and advance + _garbageRing[_ringIndex] = key; + _ringIndex = (_ringIndex + 1) % ringSize; + } else { + // add one + _garbageRing.push_back(key); } } return true; @@ -87,21 +110,22 @@ bool ShapeManager::releaseShape(const btCollisionShape* shape) { } void ShapeManager::collectGarbage() { - int numShapes = _pendingGarbage.size(); + int numShapes = (int32_t)(_garbageRing.size()); for (int i = 0; i < numShapes; ++i) { - HashKey& key = _pendingGarbage[i]; + HashKey key(_garbageRing[i]); ShapeReference* shapeRef = _shapeMap.find(key); if (shapeRef && shapeRef->refCount == 0) { ShapeFactory::deleteShape(shapeRef->shape); _shapeMap.remove(key); } } - _pendingGarbage.clear(); + _ringIndex = 0; + _garbageRing.clear(); } int ShapeManager::getNumReferences(const ShapeInfo& info) const { - HashKey key = info.getHash(); - const ShapeReference* shapeRef = _shapeMap.find(key); + HashKey hashKey(info.getHash()); + const ShapeReference* shapeRef = _shapeMap.find(hashKey); if (shapeRef) { return shapeRef->refCount; } diff --git a/libraries/physics/src/ShapeManager.h b/libraries/physics/src/ShapeManager.h index d75bb1dc4a..c1fb57e017 100644 --- a/libraries/physics/src/ShapeManager.h +++ b/libraries/physics/src/ShapeManager.h @@ -12,6 +12,8 @@ #ifndef hifi_ShapeManager_h #define hifi_ShapeManager_h +#include + #include #include @@ -41,6 +43,7 @@ // later. When that list grows big enough the ShapeManager will remove any matching // entries that still have zero ref-count. + class ShapeManager { public: @@ -63,19 +66,20 @@ public: bool hasShape(const btCollisionShape* shape) const; private: - bool releaseShapeByKey(const HashKey& key); + bool releaseShapeByKey(uint64_t key); class ShapeReference { public: int refCount; const btCollisionShape* shape; - HashKey key; + uint64_t key { 0 }; ShapeReference() : refCount(0), shape(nullptr) {} }; // btHashMap is required because it supports memory alignment of the btCollisionShapes btHashMap _shapeMap; - btAlignedObjectArray _pendingGarbage; + std::vector _garbageRing; + uint32_t _ringIndex { 0 }; }; #endif // hifi_ShapeManager_h diff --git a/libraries/pointers/src/Pick.h b/libraries/pointers/src/Pick.h index 1a9d3fd32c..dda35a53a2 100644 --- a/libraries/pointers/src/Pick.h +++ b/libraries/pointers/src/Pick.h @@ -23,7 +23,7 @@ enum IntersectionType { NONE = 0, ENTITY, - OVERLAY, + LOCAL_ENTITY, AVATAR, HUD }; @@ -177,7 +177,6 @@ public: virtual T getMathematicalPick() const = 0; virtual PickResultPointer getDefaultResult(const QVariantMap& pickVariant) const = 0; virtual PickResultPointer getEntityIntersection(const T& pick) = 0; - virtual PickResultPointer getOverlayIntersection(const T& pick) = 0; virtual PickResultPointer getAvatarIntersection(const T& pick) = 0; virtual PickResultPointer getHUDIntersection(const T& pick) = 0; diff --git a/libraries/pointers/src/PickCacheOptimizer.h b/libraries/pointers/src/PickCacheOptimizer.h index e91283f02c..0bbdfea8e4 100644 --- a/libraries/pointers/src/PickCacheOptimizer.h +++ b/libraries/pointers/src/PickCacheOptimizer.h @@ -37,7 +37,7 @@ template class PickCacheOptimizer { public: - QVector4D update(std::unordered_map>& picks, uint32_t& nextToUpdate, uint64_t expiry, bool shouldPickHUD); + QVector3D update(std::unordered_map>& picks, uint32_t& nextToUpdate, uint64_t expiry, bool shouldPickHUD); protected: typedef std::unordered_map> PickCache; @@ -67,9 +67,9 @@ void PickCacheOptimizer::cacheResult(const bool needToCompareResults, const P } template -QVector4D PickCacheOptimizer::update(std::unordered_map>& picks, +QVector3D PickCacheOptimizer::update(std::unordered_map>& picks, uint32_t& nextToUpdate, uint64_t expiry, bool shouldPickHUD) { - QVector4D numIntersectionsComputed; + QVector3D numIntersectionsComputed; PickCache results; const uint32_t INVALID_PICK_ID = 0; auto itr = picks.begin(); @@ -88,7 +88,7 @@ QVector4D PickCacheOptimizer::update(std::unordered_mapisEnabled() || pick->getMaxDistance() < 0.0f || !mathematicalPick) { pick->setPickResult(res); } else { - if (pick->getFilter().doesPickDomainEntities() || pick->getFilter().doesPickAvatarEntities()) { + if (pick->getFilter().doesPickDomainEntities() || pick->getFilter().doesPickAvatarEntities() || pick->getFilter().doesPickLocalEntities()) { PickCacheKey entityKey = { pick->getFilter().getEntityFlags(), pick->getIncludeItems(), pick->getIgnoreItems() }; if (!checkAndCompareCachedResults(mathematicalPick, results, res, entityKey)) { PickResultPointer entityRes = pick->getEntityIntersection(mathematicalPick); @@ -99,22 +99,11 @@ QVector4D PickCacheOptimizer::update(std::unordered_mapgetFilter().doesPickLocalEntities()) { - PickCacheKey overlayKey = { pick->getFilter().getOverlayFlags(), pick->getIncludeItems(), pick->getIgnoreItems() }; - if (!checkAndCompareCachedResults(mathematicalPick, results, res, overlayKey)) { - PickResultPointer overlayRes = pick->getOverlayIntersection(mathematicalPick); - numIntersectionsComputed[1]++; - if (overlayRes) { - cacheResult(overlayRes->doesIntersect(), overlayRes, overlayKey, res, mathematicalPick, results, pick); - } - } - } - if (pick->getFilter().doesPickAvatars()) { PickCacheKey avatarKey = { pick->getFilter().getAvatarFlags(), pick->getIncludeItems(), pick->getIgnoreItems() }; if (!checkAndCompareCachedResults(mathematicalPick, results, res, avatarKey)) { PickResultPointer avatarRes = pick->getAvatarIntersection(mathematicalPick); - numIntersectionsComputed[2]++; + numIntersectionsComputed[1]++; if (avatarRes) { cacheResult(avatarRes->doesIntersect(), avatarRes, avatarKey, res, mathematicalPick, results, pick); } @@ -126,7 +115,7 @@ QVector4D PickCacheOptimizer::update(std::unordered_mapgetFilter().getHUDFlags(), QVector(), QVector() }; if (!checkAndCompareCachedResults(mathematicalPick, results, res, hudKey)) { PickResultPointer hudRes = pick->getHUDIntersection(mathematicalPick); - numIntersectionsComputed[3]++; + numIntersectionsComputed[2]++; if (hudRes) { cacheResult(true, hudRes, hudKey, res, mathematicalPick, results, pick); } diff --git a/libraries/pointers/src/PickManager.h b/libraries/pointers/src/PickManager.h index 9d5971078d..8f5aa5caf8 100644 --- a/libraries/pointers/src/PickManager.h +++ b/libraries/pointers/src/PickManager.h @@ -61,14 +61,14 @@ public: bool getForceCoarsePicking() { return _forceCoarsePicking; } - const std::vector& getUpdatedPickCounts() { return _updatedPickCounts; } + const std::vector& getUpdatedPickCounts() { return _updatedPickCounts; } const std::vector& getTotalPickCounts() { return _totalPickCounts; } public slots: void setForceCoarsePicking(bool forceCoarsePicking) { _forceCoarsePicking = forceCoarsePicking; } protected: - std::vector _updatedPickCounts { PickQuery::NUM_PICK_TYPES }; + std::vector _updatedPickCounts { PickQuery::NUM_PICK_TYPES }; std::vector _totalPickCounts { 0, 0, 0, 0 }; bool _forceCoarsePicking { false }; diff --git a/libraries/pointers/src/Pointer.cpp b/libraries/pointers/src/Pointer.cpp index 26460cbdd7..c08a3c2be6 100644 --- a/libraries/pointers/src/Pointer.cpp +++ b/libraries/pointers/src/Pointer.cpp @@ -109,14 +109,14 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin if (_enabled && _hover && doHover && !_prevDoHover) { if (hoveredObject.type == ENTITY) { emit pointerManager->hoverBeginEntity(hoveredObject.objectID, hoveredEvent); - } else if (hoveredObject.type == OVERLAY) { + } else if (hoveredObject.type == LOCAL_ENTITY) { emit pointerManager->hoverBeginOverlay(hoveredObject.objectID, hoveredEvent); } else if (hoveredObject.type == HUD) { emit pointerManager->hoverBeginHUD(hoveredEvent); } } else if (_enabled && _hover && doHover) { - if (hoveredObject.type == OVERLAY) { - if (_prevHoveredObject.type == OVERLAY) { + if (hoveredObject.type == LOCAL_ENTITY) { + if (_prevHoveredObject.type == LOCAL_ENTITY) { if (hoveredObject.objectID == _prevHoveredObject.objectID) { emit pointerManager->hoverContinueOverlay(hoveredObject.objectID, hoveredEvent); } else { @@ -150,7 +150,7 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin } } else { emit pointerManager->hoverBeginEntity(hoveredObject.objectID, hoveredEvent); - if (_prevHoveredObject.type == OVERLAY) { + if (_prevHoveredObject.type == LOCAL_ENTITY) { emit pointerManager->hoverEndOverlay(_prevHoveredObject.objectID, hoveredEvent); } else if (_prevHoveredObject.type == HUD) { emit pointerManager->hoverEndHUD(hoveredEvent); @@ -166,7 +166,7 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin emit pointerManager->hoverBeginHUD(hoveredEvent); if (_prevHoveredObject.type == ENTITY) { emit pointerManager->hoverEndEntity(_prevHoveredObject.objectID, hoveredEvent); - } else if (_prevHoveredObject.type == OVERLAY) { + } else if (_prevHoveredObject.type == LOCAL_ENTITY) { emit pointerManager->hoverEndOverlay(_prevHoveredObject.objectID, hoveredEvent); } } @@ -175,7 +175,7 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin if (hoveredObject.type == NONE) { if (_prevHoveredObject.type == ENTITY) { emit pointerManager->hoverEndEntity(_prevHoveredObject.objectID, hoveredEvent); - } else if (_prevHoveredObject.type == OVERLAY) { + } else if (_prevHoveredObject.type == LOCAL_ENTITY) { emit pointerManager->hoverEndOverlay(_prevHoveredObject.objectID, hoveredEvent); } else if (_prevHoveredObject.type == HUD) { emit pointerManager->hoverEndHUD(hoveredEvent); @@ -191,7 +191,7 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin hoveredEvent.setShouldFocus(button == SHOULD_FOCUS_BUTTON); if (hoveredObject.type == ENTITY) { emit pointerManager->triggerBeginEntity(hoveredObject.objectID, hoveredEvent); - } else if (hoveredObject.type == OVERLAY) { + } else if (hoveredObject.type == LOCAL_ENTITY) { emit pointerManager->triggerBeginOverlay(hoveredObject.objectID, hoveredEvent); } else if (hoveredObject.type == HUD) { emit pointerManager->triggerBeginHUD(hoveredEvent); @@ -207,7 +207,7 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin triggeredEvent.setButton(chooseButton(button)); if (_triggeredObjects[button].type == ENTITY) { emit pointerManager->triggerContinueEntity(_triggeredObjects[button].objectID, triggeredEvent); - } else if (_triggeredObjects[button].type == OVERLAY) { + } else if (_triggeredObjects[button].type == LOCAL_ENTITY) { emit pointerManager->triggerContinueOverlay(_triggeredObjects[button].objectID, triggeredEvent); } else if (_triggeredObjects[button].type == HUD) { emit pointerManager->triggerContinueHUD(triggeredEvent); @@ -222,7 +222,7 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin triggeredEvent.setButton(chooseButton(button)); if (_triggeredObjects[button].type == ENTITY) { emit pointerManager->triggerEndEntity(_triggeredObjects[button].objectID, triggeredEvent); - } else if (_triggeredObjects[button].type == OVERLAY) { + } else if (_triggeredObjects[button].type == LOCAL_ENTITY) { emit pointerManager->triggerEndOverlay(_triggeredObjects[button].objectID, triggeredEvent); } else if (_triggeredObjects[button].type == HUD) { emit pointerManager->triggerEndHUD(triggeredEvent); @@ -234,7 +234,7 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin if (_hover && ((!_enabled && _prevEnabled) || (!doHover && _prevDoHover))) { if (_prevHoveredObject.type == ENTITY) { emit pointerManager->hoverEndEntity(_prevHoveredObject.objectID, hoveredEvent); - } else if (_prevHoveredObject.type == OVERLAY) { + } else if (_prevHoveredObject.type == LOCAL_ENTITY) { emit pointerManager->hoverEndOverlay(_prevHoveredObject.objectID, hoveredEvent); } else if (_prevHoveredObject.type == HUD) { emit pointerManager->hoverEndHUD(hoveredEvent); diff --git a/libraries/pointers/src/Pointer.h b/libraries/pointers/src/Pointer.h index 173163374f..28d7e42e8f 100644 --- a/libraries/pointers/src/Pointer.h +++ b/libraries/pointers/src/Pointer.h @@ -62,7 +62,7 @@ public: // Pointers can choose to implement these virtual void setLength(float length) {} - virtual void setLockEndUUID(const QUuid& objectID, bool isOverlay, const glm::mat4& offsetMat = glm::mat4()) {} + virtual void setLockEndUUID(const QUuid& objectID, bool isAvatar, const glm::mat4& offsetMat = glm::mat4()) {} void update(unsigned int pointerID); virtual void updateVisuals(const PickResultPointer& pickResult) = 0; diff --git a/libraries/pointers/src/PointerManager.cpp b/libraries/pointers/src/PointerManager.cpp index 72e4b417cd..ca8dfb3f26 100644 --- a/libraries/pointers/src/PointerManager.cpp +++ b/libraries/pointers/src/PointerManager.cpp @@ -124,10 +124,10 @@ void PointerManager::setLength(unsigned int uid, float length) const { } } -void PointerManager::setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isOverlay, const glm::mat4& offsetMat) const { +void PointerManager::setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isAvatar, const glm::mat4& offsetMat) const { auto pointer = find(uid); if (pointer) { - pointer->setLockEndUUID(objectID, isOverlay, offsetMat); + pointer->setLockEndUUID(objectID, isAvatar, offsetMat); } } diff --git a/libraries/pointers/src/PointerManager.h b/libraries/pointers/src/PointerManager.h index 2d0b2a107e..6c1581b09b 100644 --- a/libraries/pointers/src/PointerManager.h +++ b/libraries/pointers/src/PointerManager.h @@ -37,7 +37,7 @@ public: void setIncludeItems(unsigned int uid, const QVector& includeEntities) const; void setLength(unsigned int uid, float length) const; - void setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isOverlay, const glm::mat4& offsetMat = glm::mat4()) const; + void setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isAvatar, const glm::mat4& offsetMat = glm::mat4()) const; void update(); diff --git a/libraries/render-utils/src/AnimDebugDraw.cpp b/libraries/render-utils/src/AnimDebugDraw.cpp index 8944ae7996..bf528ee5f0 100644 --- a/libraries/render-utils/src/AnimDebugDraw.cpp +++ b/libraries/render-utils/src/AnimDebugDraw.cpp @@ -374,7 +374,7 @@ void AnimDebugDraw::update() { glm::vec4 color = std::get<3>(iter.second); for (int i = 0; i < skeleton->getNumJoints(); i++) { - const float radius = BONE_RADIUS / (absPoses[i].scale() * rootPose.scale()); + const float radius = BONE_RADIUS / (absPoses[i].scale().x * rootPose.scale().x); // draw bone addBone(rootPose, absPoses[i], radius, color, v); @@ -394,16 +394,16 @@ void AnimDebugDraw::update() { glm::vec3 pos = std::get<1>(iter.second); glm::vec4 color = std::get<2>(iter.second); const float radius = POSE_RADIUS; - addBone(AnimPose::identity, AnimPose(1.0f, rot, pos), radius, color, v); + addBone(AnimPose::identity, AnimPose(glm::vec3(1), rot, pos), radius, color, v); } - AnimPose myAvatarPose(1.0f, DebugDraw::getInstance().getMyAvatarRot(), DebugDraw::getInstance().getMyAvatarPos()); + AnimPose myAvatarPose(glm::vec3(1), DebugDraw::getInstance().getMyAvatarRot(), DebugDraw::getInstance().getMyAvatarPos()); for (auto& iter : myAvatarMarkerMap) { glm::quat rot = std::get<0>(iter.second); glm::vec3 pos = std::get<1>(iter.second); glm::vec4 color = std::get<2>(iter.second); const float radius = POSE_RADIUS; - addBone(myAvatarPose, AnimPose(1.0f, rot, pos), radius, color, v); + addBone(myAvatarPose, AnimPose(glm::vec3(1), rot, pos), radius, color, v); } // draw rays from shared DebugDraw singleton diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index b70925201a..81a81c5602 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -122,7 +122,7 @@ void CauterizedModel::updateClusterMatrices() { if (_useDualQuaternionSkinning) { auto jointPose = _rig.getJointPose(cluster.jointIndex); - Transform jointTransform(jointPose.rot(), glm::vec3(jointPose.scale()), jointPose.trans()); + Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans()); Transform clusterTransform; Transform::mult(clusterTransform, jointTransform, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindTransform); state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(clusterTransform); @@ -138,7 +138,7 @@ void CauterizedModel::updateClusterMatrices() { if (!_cauterizeBoneSet.empty()) { AnimPose cauterizePose = _rig.getJointPose(_rig.indexOfJoint("Neck")); - cauterizePose.scale() = 0.0001f; + cauterizePose.scale() = glm::vec3(0.0001f, 0.0001f, 0.0001f); static const glm::mat4 zeroScale( glm::vec4(0.0001f, 0.0f, 0.0f, 0.0f), @@ -161,7 +161,7 @@ void CauterizedModel::updateClusterMatrices() { // not cauterized so just copy the value from the non-cauterized version. state.clusterDualQuaternions[j] = _meshStates[i].clusterDualQuaternions[j]; } else { - Transform jointTransform(cauterizePose.rot(), glm::vec3(cauterizePose.scale()), cauterizePose.trans()); + Transform jointTransform(cauterizePose.rot(), cauterizePose.scale(), cauterizePose.trans()); Transform clusterTransform; Transform::mult(clusterTransform, jointTransform, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindTransform); state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(clusterTransform); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 260e25009e..0206bd6963 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1364,7 +1364,7 @@ void Model::updateClusterMatrices() { if (_useDualQuaternionSkinning) { auto jointPose = _rig.getJointPose(cluster.jointIndex); - Transform jointTransform(jointPose.rot(), glm::vec3(jointPose.scale()), jointPose.trans()); + Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans()); Transform clusterTransform; Transform::mult(clusterTransform, jointTransform, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindTransform); state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(clusterTransform); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index c55178a21a..aadfca78ba 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -298,9 +298,9 @@ public: TransformDualQuaternion() {} TransformDualQuaternion(const glm::mat4& m) { AnimPose p(m); - _scale.x = p.scale(); - _scale.y = p.scale(); - _scale.z = p.scale(); + _scale.x = p.scale().x; + _scale.y = p.scale().y; + _scale.z = p.scale().z; _scale.w = 0.0f; _dq = DualQuaternion(p.rot(), p.trans()); } diff --git a/libraries/render-utils/src/RenderCommonTask.cpp b/libraries/render-utils/src/RenderCommonTask.cpp index 40724cbf5a..64037d64b6 100644 --- a/libraries/render-utils/src/RenderCommonTask.cpp +++ b/libraries/render-utils/src/RenderCommonTask.cpp @@ -45,13 +45,13 @@ void EndGPURangeTimer::run(const render::RenderContextPointer& renderContext, co config->setGPUBatchRunTime(timer->getGPUAverage(), timer->getBatchAverage()); } -DrawOverlay3D::DrawOverlay3D(bool opaque) : +DrawLayered3D::DrawLayered3D(bool opaque) : _shapePlumber(std::make_shared()), _opaquePass(opaque) { initForwardPipelines(*_shapePlumber); } -void DrawOverlay3D::run(const RenderContextPointer& renderContext, const Inputs& inputs) { +void DrawLayered3D::run(const RenderContextPointer& renderContext, const Inputs& inputs) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); @@ -70,7 +70,7 @@ void DrawOverlay3D::run(const RenderContextPointer& renderContext, const Inputs& // Needs to be distinct from the other batch because using the clear call // while stereo is enabled triggers a warning if (_opaquePass) { - gpu::doInBatch("DrawOverlay3D::run::clear", args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("DrawLayered3D::run::clear", args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0, false); }); @@ -78,7 +78,7 @@ void DrawOverlay3D::run(const RenderContextPointer& renderContext, const Inputs& if (!inItems.empty()) { // Render the items - gpu::doInBatch("DrawOverlay3D::main", args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("DrawLayered3D::main", args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; batch.setViewportTransform(args->_viewport); batch.setStateScissorRect(args->_viewport); diff --git a/libraries/render-utils/src/RenderCommonTask.h b/libraries/render-utils/src/RenderCommonTask.h index 29f195ffff..4f8f53257d 100644 --- a/libraries/render-utils/src/RenderCommonTask.h +++ b/libraries/render-utils/src/RenderCommonTask.h @@ -40,7 +40,7 @@ public: protected: }; -class DrawOverlay3DConfig : public render::Job::Config { +class DrawLayered3DConfig : public render::Job::Config { Q_OBJECT Q_PROPERTY(int numDrawn READ getNumDrawn NOTIFY numDrawnChanged) Q_PROPERTY(int maxDrawn MEMBER maxDrawn NOTIFY dirty) @@ -58,13 +58,13 @@ protected: int numDrawn{ 0 }; }; -class DrawOverlay3D { +class DrawLayered3D { public: using Inputs = render::VaryingSet3; - using Config = DrawOverlay3DConfig; - using JobModel = render::Job::ModelI; + using Config = DrawLayered3DConfig; + using JobModel = render::Job::ModelI; - DrawOverlay3D(bool opaque); + DrawLayered3D(bool opaque); void configure(const Config& config) { _maxDrawn = config.maxDrawn; } void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index a685f3998e..673a165105 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -116,13 +116,13 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren const auto& items = fetchedItems.get0(); - // Extract opaques / transparents / lights / metas / overlays / background + // Extract opaques / transparents / lights / metas / layered / background const auto& opaques = items[RenderFetchCullSortTask::OPAQUE_SHAPE]; const auto& transparents = items[RenderFetchCullSortTask::TRANSPARENT_SHAPE]; - const auto& overlaysInFrontOpaque = items[RenderFetchCullSortTask::LAYER_FRONT_OPAQUE_SHAPE]; - const auto& overlaysInFrontTransparent = items[RenderFetchCullSortTask::LAYER_FRONT_TRANSPARENT_SHAPE]; - const auto& overlaysHUDOpaque = items[RenderFetchCullSortTask::LAYER_HUD_OPAQUE_SHAPE]; - const auto& overlaysHUDTransparent = items[RenderFetchCullSortTask::LAYER_HUD_TRANSPARENT_SHAPE]; + const auto& inFrontOpaque = items[RenderFetchCullSortTask::LAYER_FRONT_OPAQUE_SHAPE]; + const auto& inFrontTransparent = items[RenderFetchCullSortTask::LAYER_FRONT_TRANSPARENT_SHAPE]; + const auto& hudOpaque = items[RenderFetchCullSortTask::LAYER_HUD_OPAQUE_SHAPE]; + const auto& hudTransparent = items[RenderFetchCullSortTask::LAYER_HUD_TRANSPARENT_SHAPE]; // Lighting model comes next, the big configuration of the view const auto& lightingModel = inputs[1]; @@ -227,12 +227,12 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren task.addJob("HighlightRangeTimer", outlineRangeTimer); // Layered Over (in front) - const auto overlayInFrontOpaquesInputs = DrawOverlay3D::Inputs(overlaysInFrontOpaque, lightingModel, jitter).asVarying(); - const auto overlayInFrontTransparentsInputs = DrawOverlay3D::Inputs(overlaysInFrontTransparent, lightingModel, jitter).asVarying(); - task.addJob("DrawOverlayInFrontOpaque", overlayInFrontOpaquesInputs, true); - task.addJob("DrawOverlayInFrontTransparent", overlayInFrontTransparentsInputs, false); + const auto inFrontOpaquesInputs = DrawLayered3D::Inputs(inFrontOpaque, lightingModel, jitter).asVarying(); + const auto inFrontTransparentsInputs = DrawLayered3D::Inputs(inFrontTransparent, lightingModel, jitter).asVarying(); + task.addJob("DrawInFrontOpaque", inFrontOpaquesInputs, true); + task.addJob("DrawInFrontTransparent", inFrontTransparentsInputs, false); - const auto toneAndPostRangeTimer = task.addJob("BeginToneAndPostRangeTimer", "PostToneOverlaysAntialiasing"); + const auto toneAndPostRangeTimer = task.addJob("BeginToneAndPostRangeTimer", "PostToneLayeredAntialiasing"); // AA job before bloom to limit flickering const auto antialiasingInputs = Antialiasing::Inputs(deferredFrameTransform, lightingFramebuffer, linearDepthTarget, velocityBuffer).asVarying(); @@ -257,14 +257,14 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren // Upscale to finale resolution const auto primaryFramebuffer = task.addJob("PrimaryBufferUpscale", scaledPrimaryFramebuffer); - // Composite the HUD and HUD overlays + // Composite the HUD and HUD layered objects task.addJob("HUD"); const auto nullJitter = Varying(glm::vec2(0.0f, 0.0f)); - const auto overlayHUDOpaquesInputs = DrawOverlay3D::Inputs(overlaysHUDOpaque, lightingModel, nullJitter).asVarying(); - const auto overlayHUDTransparentsInputs = DrawOverlay3D::Inputs(overlaysHUDTransparent, lightingModel, nullJitter).asVarying(); - task.addJob("DrawOverlayHUDOpaque", overlayHUDOpaquesInputs, true); - task.addJob("DrawOverlayHUDTransparent", overlayHUDTransparentsInputs, false); + const auto hudOpaquesInputs = DrawLayered3D::Inputs(hudOpaque, lightingModel, nullJitter).asVarying(); + const auto hudTransparentsInputs = DrawLayered3D::Inputs(hudTransparent, lightingModel, nullJitter).asVarying(); + task.addJob("DrawHUDOpaque", hudOpaquesInputs, true); + task.addJob("DrawHUDTransparent", hudTransparentsInputs, false); task.addJob("ToneAndPostRangeTimer", toneAndPostRangeTimer); @@ -283,15 +283,15 @@ void RenderDeferredTaskDebug::build(JobModel& task, const render::Varying& input const auto& fetchCullSortTaskOut = inputs.get0(); const auto& items = fetchCullSortTaskOut.get0(); - // Extract opaques / transparents / lights / metas / overlays InFront and HUD / background + // Extract opaques / transparents / lights / metas / layered / background const auto& opaques = items[RenderFetchCullSortTask::OPAQUE_SHAPE]; const auto& transparents = items[RenderFetchCullSortTask::TRANSPARENT_SHAPE]; const auto& lights = items[RenderFetchCullSortTask::LIGHT]; const auto& metas = items[RenderFetchCullSortTask::META]; - const auto& overlaysInFrontOpaque = items[RenderFetchCullSortTask::LAYER_FRONT_OPAQUE_SHAPE]; - const auto& overlaysInFrontTransparent = items[RenderFetchCullSortTask::LAYER_FRONT_TRANSPARENT_SHAPE]; - const auto& overlaysHUDOpaque = items[RenderFetchCullSortTask::LAYER_HUD_OPAQUE_SHAPE]; - const auto& overlaysHUDTransparent = items[RenderFetchCullSortTask::LAYER_HUD_TRANSPARENT_SHAPE]; + const auto& inFrontOpaque = items[RenderFetchCullSortTask::LAYER_FRONT_OPAQUE_SHAPE]; + const auto& inFrontTransparent = items[RenderFetchCullSortTask::LAYER_FRONT_TRANSPARENT_SHAPE]; + const auto& hudOpaque = items[RenderFetchCullSortTask::LAYER_HUD_OPAQUE_SHAPE]; + const auto& hudTransparent = items[RenderFetchCullSortTask::LAYER_HUD_TRANSPARENT_SHAPE]; const auto& spatialSelection = fetchCullSortTaskOut[1]; @@ -388,14 +388,14 @@ void RenderDeferredTaskDebug::build(JobModel& task, const render::Varying& input task.addJob("DrawSelectionBounds", selectedItems); } - { // Debug the bounds of the rendered Overlay items that are marked drawInFront, still look at the zbuffer - task.addJob("DrawOverlayInFrontOpaqueBounds", overlaysInFrontOpaque); - task.addJob("DrawOverlayInFrontTransparentBounds", overlaysInFrontTransparent); + { // Debug the bounds of the layered objects, still look at the zbuffer + task.addJob("DrawInFrontOpaqueBounds", inFrontOpaque); + task.addJob("DrawInFrontTransparentBounds", inFrontTransparent); } - { // Debug the bounds of the rendered Overlay items that are marked drawHUDLayer, still look at the zbuffer - task.addJob("DrawOverlayHUDOpaqueBounds", overlaysHUDOpaque); - task.addJob("DrawOverlayHUDTransparentBounds", overlaysHUDTransparent); + { // Debug the bounds of the layered objects, still look at the zbuffer + task.addJob("DrawHUDOpaqueBounds", hudOpaque); + task.addJob("DrawHUDTransparentBounds", hudTransparent); } // Debugging stages diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index ffdbc1c4b1..b7078a00a7 100755 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -61,15 +61,14 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend const auto& items = fetchedItems.get0(); - // Extract opaques / transparents / lights / metas / overlays / background + // Extract opaques / transparents / lights / metas / layered / background const auto& opaques = items[RenderFetchCullSortTask::OPAQUE_SHAPE]; const auto& transparents = items[RenderFetchCullSortTask::TRANSPARENT_SHAPE]; const auto& metas = items[RenderFetchCullSortTask::META]; - const auto& overlaysInFrontOpaque = items[RenderFetchCullSortTask::LAYER_FRONT_OPAQUE_SHAPE]; - const auto& overlaysInFrontTransparent = items[RenderFetchCullSortTask::LAYER_FRONT_TRANSPARENT_SHAPE]; - // TODO: Re enable the rendering of the HUD overlayes - // const auto& overlaysHUDOpaque = items[RenderFetchCullSortTask::LAYER_HUD_OPAQUE_SHAPE]; - // const auto& overlaysHUDTransparent = items[RenderFetchCullSortTask::LAYER_HUD_TRANSPARENT_SHAPE]; + const auto& inFrontOpaque = items[RenderFetchCullSortTask::LAYER_FRONT_OPAQUE_SHAPE]; + const auto& inFrontTransparent = items[RenderFetchCullSortTask::LAYER_FRONT_TRANSPARENT_SHAPE]; + const auto& hudOpaque = items[RenderFetchCullSortTask::LAYER_HUD_OPAQUE_SHAPE]; + const auto& hudTransparent = items[RenderFetchCullSortTask::LAYER_HUD_TRANSPARENT_SHAPE]; // Lighting model comes next, the big configuration of the view const auto& lightingModel = inputs[1]; @@ -97,14 +96,12 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend // draw a stencil mask in hidden regions of the framebuffer. task.addJob("PrepareStencil", framebuffer); - // Layered Overlays + // Layered const auto nullJitter = Varying(glm::vec2(0.0f, 0.0f)); - - // Layered Over (in front) - const auto overlayInFrontOpaquesInputs = DrawOverlay3D::Inputs(overlaysInFrontOpaque, lightingModel, nullJitter).asVarying(); - const auto overlayInFrontTransparentsInputs = DrawOverlay3D::Inputs(overlaysInFrontTransparent, lightingModel, nullJitter).asVarying(); - task.addJob("DrawOverlayInFrontOpaque", overlayInFrontOpaquesInputs, true); - task.addJob("DrawOverlayInFrontTransparent", overlayInFrontTransparentsInputs, false); + const auto inFrontOpaquesInputs = DrawLayered3D::Inputs(inFrontOpaque, lightingModel, nullJitter).asVarying(); + const auto inFrontTransparentsInputs = DrawLayered3D::Inputs(inFrontTransparent, lightingModel, nullJitter).asVarying(); + task.addJob("DrawInFrontOpaque", inFrontOpaquesInputs, true); + task.addJob("DrawInFrontTransparent", inFrontTransparentsInputs, false); // Draw opaques forward const auto opaqueInputs = DrawForward::Inputs(opaques, lightingModel).asVarying(); @@ -135,10 +132,14 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend const auto toneMappingInputs = ToneMappingDeferred::Inputs(framebuffer, static_cast(nullptr) ).asVarying(); task.addJob("ToneMapping", toneMappingInputs); - // Layered Overlays - // Composite the HUD and HUD overlays + // Composite the HUD and HUD layered objects task.addJob("HUD"); + const auto hudOpaquesInputs = DrawLayered3D::Inputs(hudOpaque, lightingModel, nullJitter).asVarying(); + const auto hudTransparentsInputs = DrawLayered3D::Inputs(hudTransparent, lightingModel, nullJitter).asVarying(); + task.addJob("DrawHUDOpaque", hudOpaquesInputs, true); + task.addJob("DrawHUDTransparent", hudTransparentsInputs, false); + // Disable blit because we do tonemapping and compositing directly to the blit FBO // Blit! // task.addJob("Blit", framebuffer); diff --git a/libraries/render/src/render/HighlightStage.h b/libraries/render/src/render/HighlightStage.h index 5e6574840f..91d8cc3f81 100644 --- a/libraries/render/src/render/HighlightStage.h +++ b/libraries/render/src/render/HighlightStage.h @@ -107,7 +107,7 @@ namespace render { float getOccludedFillOpacity() const { return getStyle()._fillOccluded.alpha; } void setOccludedFillOpacity(float value); - std::string _selectionName{ "contextOverlayHighlightList" }; + std::string _selectionName { "contextOverlayHighlightList" }; mutable SelectionStyles _styles; const HighlightStyle& getStyle() const; diff --git a/libraries/render/src/render/RenderFetchCullSortTask.cpp b/libraries/render/src/render/RenderFetchCullSortTask.cpp index 324f1d879f..d82fdef258 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.cpp +++ b/libraries/render/src/render/RenderFetchCullSortTask.cpp @@ -29,10 +29,10 @@ void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varyin const auto cullInputs = CullSpatialSelection::Inputs(spatialSelection, spatialFilter).asVarying(); const auto culledSpatialSelection = task.addJob("CullSceneSelection", cullInputs, cullFunctor, RenderDetails::ITEM); - // Overlays are not culled - const ItemFilter overlayfilter = ItemFilter::Builder().withVisible().withoutSubMetaCulled().withTagBits(tagBits, tagMask); - const auto nonspatialFilter = render::Varying(overlayfilter); - const auto nonspatialSelection = task.addJob("FetchOverlaySelection", nonspatialFilter); + // Layered objects are not culled + const ItemFilter layeredFilter = ItemFilter::Builder().withVisible().withoutSubMetaCulled().withTagBits(tagBits, tagMask); + const auto nonspatialFilter = render::Varying(layeredFilter); + const auto nonspatialSelection = task.addJob("FetchLayeredSelection", nonspatialFilter); // Multi filter visible items into different buckets const int NUM_SPATIAL_FILTERS = 4; @@ -57,26 +57,26 @@ void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varyin task.addJob>("FilterSceneSelection", culledSpatialSelection, spatialFilters) .get::ItemBoundsArray>(); const auto filteredNonspatialBuckets = - task.addJob>("FilterOverlaySelection", nonspatialSelection, nonspatialFilters) + task.addJob>("FilterLayeredSelection", nonspatialSelection, nonspatialFilters) .get::ItemBoundsArray>(); - // Extract opaques / transparents / lights / overlays + // Extract opaques / transparents / lights / layered const auto opaques = task.addJob("DepthSortOpaque", filteredSpatialBuckets[OPAQUE_SHAPE_BUCKET]); const auto transparents = task.addJob("DepthSortTransparent", filteredSpatialBuckets[TRANSPARENT_SHAPE_BUCKET], DepthSortItems(false)); const auto lights = filteredSpatialBuckets[LIGHT_BUCKET]; const auto metas = filteredSpatialBuckets[META_BUCKET]; - const auto overlayOpaques = task.addJob("DepthSortOverlayOpaque", filteredNonspatialBuckets[OPAQUE_SHAPE_BUCKET]); - const auto overlayTransparents = task.addJob("DepthSortOverlayTransparent", filteredNonspatialBuckets[TRANSPARENT_SHAPE_BUCKET], DepthSortItems(false)); const auto background = filteredNonspatialBuckets[BACKGROUND_BUCKET]; - // split up the overlays into 3D front, hud - const auto filteredOverlaysOpaque = task.addJob("FilterOverlaysLayeredOpaque", overlayOpaques, ItemKey::Layer::LAYER_1); - const auto filteredOverlaysTransparent = task.addJob("FilterOverlaysLayeredTransparent", overlayTransparents, ItemKey::Layer::LAYER_1); + // split up the layered objects into 3D front, hud + const auto layeredOpaques = task.addJob("DepthSortLayaredOpaque", filteredNonspatialBuckets[OPAQUE_SHAPE_BUCKET]); + const auto layeredTransparents = task.addJob("DepthSortLayeredTransparent", filteredNonspatialBuckets[TRANSPARENT_SHAPE_BUCKET], DepthSortItems(false)); + const auto filteredLayeredOpaque = task.addJob("FilterLayeredOpaque", layeredOpaques, ItemKey::Layer::LAYER_1); + const auto filteredLayeredTransparent = task.addJob("FilterLayeredTransparent", layeredTransparents, ItemKey::Layer::LAYER_1); - output = Output(BucketList{ opaques, transparents, lights, metas, overlayOpaques, overlayTransparents, - filteredOverlaysOpaque.getN(0), filteredOverlaysTransparent.getN(0), - filteredOverlaysOpaque.getN(1), filteredOverlaysTransparent.getN(1), + output = Output(BucketList{ opaques, transparents, lights, metas, + filteredLayeredOpaque.getN(0), filteredLayeredTransparent.getN(0), + filteredLayeredOpaque.getN(1), filteredLayeredTransparent.getN(1), background }, spatialSelection); } diff --git a/libraries/render/src/render/RenderFetchCullSortTask.h b/libraries/render/src/render/RenderFetchCullSortTask.h index a75c814d91..1b1e4a5d8f 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.h +++ b/libraries/render/src/render/RenderFetchCullSortTask.h @@ -23,8 +23,6 @@ public: TRANSPARENT_SHAPE, LIGHT, META, - OVERLAY_OPAQUE_SHAPE, - OVERLAY_TRANSPARENT_SHAPE, LAYER_FRONT_OPAQUE_SHAPE, LAYER_FRONT_TRANSPARENT_SHAPE, LAYER_HUD_OPAQUE_SHAPE, diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h index e05cb04532..f00c74775d 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -167,11 +167,9 @@ public: // Access the spatialized items const ItemSpatialTree& getSpatialTree() const { return _masterSpatialTree; } - // Access non-spatialized items (overlays, backgrounds) + // Access non-spatialized items (layered objects, backgrounds) const ItemIDSet& getNonspatialSet() const { return _masterNonspatialSet; } - - // Access a particular Stage (empty if doesn't exist) // Thread safe StagePointer getStage(const Stage::Name& name) const; diff --git a/libraries/script-engine/src/ScriptUUID.h b/libraries/script-engine/src/ScriptUUID.h index 7387cefeff..548bc6a6c8 100644 --- a/libraries/script-engine/src/ScriptUUID.h +++ b/libraries/script-engine/src/ScriptUUID.h @@ -19,7 +19,7 @@ #include /**jsdoc - * A UUID (Universally Unique IDentifier) is used to uniquely identify entities, overlays, avatars, and the like. It is + * A UUID (Universally Unique IDentifier) is used to uniquely identify entities, avatars, and the like. It is * represented in JavaScript as a string in the format, {nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn}, where the "n"s are * hexadecimal digits. * diff --git a/libraries/shared/src/HashKey.h b/libraries/shared/src/HashKey.h index 5fce182084..446eb4c25f 100644 --- a/libraries/shared/src/HashKey.h +++ b/libraries/shared/src/HashKey.h @@ -32,17 +32,19 @@ class HashKey { public: static float getNumQuantizedValuesPerMeter(); + HashKey() {} + HashKey(uint64_t hash) : _hash(hash) {} + // These two methods are required by btHashMap. bool equals(const HashKey& other) const { return _hash == other._hash; } int32_t getHash() const { return (int32_t)((uint32_t)_hash); } - void clear() { _hash = _hashCount = 0; } - bool isNull() const { return _hash == 0 && _hashCount == 0; } + // These methods for accumulating a hash. void hashUint64(uint64_t data); void hashFloat(float data); void hashVec3(const glm::vec3& data); - uint64_t getHash64() const { return _hash; } // for debug/test purposes + uint64_t getHash64() const { return _hash; } private: uint64_t _hash { 0 }; diff --git a/libraries/shared/src/PickFilter.h b/libraries/shared/src/PickFilter.h index 2efd408e4f..33f6e7278a 100644 --- a/libraries/shared/src/PickFilter.h +++ b/libraries/shared/src/PickFilter.h @@ -68,7 +68,7 @@ public: // Helpers for RayPickManager Flags getEntityFlags() const { unsigned int toReturn = 0; - for (int i = DOMAIN_ENTITIES; i < LOCAL_ENTITIES; i++) { + for (int i = DOMAIN_ENTITIES; i <= LOCAL_ENTITIES; i++) { if (_flags[i]) { toReturn |= getBitMask(FlagBit(i)); } @@ -80,15 +80,6 @@ public: } return Flags(toReturn); } - Flags getOverlayFlags() const { - unsigned int toReturn = getBitMask(LOCAL_ENTITIES); - for (int i = HUD + 1; i < NUM_FLAGS; i++) { - if (_flags[i]) { - toReturn |= getBitMask(FlagBit(i)); - } - } - return Flags(toReturn); - } Flags getAvatarFlags() const { return Flags(getBitMask(AVATARS)); } Flags getHUDFlags() const { return Flags(getBitMask(HUD)); } diff --git a/libraries/shared/src/PointerEvent.cpp b/libraries/shared/src/PointerEvent.cpp index 422f2dfb20..69d3f28d80 100644 --- a/libraries/shared/src/PointerEvent.cpp +++ b/libraries/shared/src/PointerEvent.cpp @@ -72,8 +72,8 @@ void PointerEvent::setButton(Button button) { * "Move". * @property {number} id - Integer number used to identify the pointer: 0 = hardware mouse, 1 = left * controller, 2 = right controller. - * @property {Vec2} pos2D - The 2D position of the event on the intersected overlay or entity XY plane, where applicable. - * @property {Vec3} pos3D - The 3D position of the event on the intersected overlay or entity, where applicable. + * @property {Vec2} pos2D - The 2D position of the event on the intersected object XY plane, where applicable. + * @property {Vec3} pos3D - The 3D position of the event on the intersected object, where applicable. * @property {Vec3} normal - The surface normal at the intersection point. * @property {Vec3} direction - The direction of the intersection ray. * @property {string} button - The name of the button pressed: None, Primary, Secondary, diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index 729816cf82..1edb303455 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -232,7 +232,7 @@ public: }; /**jsdoc - * A PickRay defines a vector with a starting point. It is used, for example, when finding entities or overlays that lie under a + * A PickRay defines a vector with a starting point. It is used, for example, when finding entities or avatars that lie under a * mouse click or intersect a laser beam. * * @typedef {object} PickRay @@ -351,7 +351,7 @@ public: * The depth is measured in world space, but will scale with the parent if defined. * @property {CollisionMask} [collisionGroup=8] - The type of object this collision pick collides as. Objects whose collision masks overlap with the pick's collision group * will be considered colliding with the pick. -* @property {Uuid} parentID - The ID of the parent, either an avatar, an entity, or an overlay. +* @property {Uuid} parentID - The ID of the parent, either an avatar or an entity. * @property {number} parentJointIndex - The joint of the parent to parent to, for example, the joints on the model of an avatar. (default = 0, no joint) * @property {string} joint - If "Mouse," parents the pick to the mouse. If "Avatar," parents the pick to MyAvatar's head. Otherwise, parents to the joint of the given name on MyAvatar. */ diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index c256cf2b76..bf51e455c5 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -13,6 +13,7 @@ #include +#include "HashKey.h" #include "NumericalConstants.h" // for MILLIMETERS_PER_METER /**jsdoc @@ -96,7 +97,7 @@ void ShapeInfo::clear() { _sphereCollection.clear(); _halfExtents = glm::vec3(0.0f); _offset = glm::vec3(0.0f); - _hashKey.clear(); + _hash64 = 0; _type = SHAPE_TYPE_NONE; } @@ -131,14 +132,14 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString default: break; } - _hashKey.clear(); + _hash64 = 0; } void ShapeInfo::setBox(const glm::vec3& halfExtents) { _url = ""; _type = SHAPE_TYPE_BOX; setHalfExtents(halfExtents); - _hashKey.clear(); + _hash64 = 0; } void ShapeInfo::setSphere(float radius) { @@ -146,7 +147,7 @@ void ShapeInfo::setSphere(float radius) { _type = SHAPE_TYPE_SPHERE; radius = glm::max(radius, MIN_HALF_EXTENT); _halfExtents = glm::vec3(radius); - _hashKey.clear(); + _hash64 = 0; } void ShapeInfo::setMultiSphere(const std::vector& centers, const std::vector& radiuses) { @@ -158,12 +159,12 @@ void ShapeInfo::setMultiSphere(const std::vector& centers, const std: SphereData sphere = SphereData(centers[i], radiuses[i]); _sphereCollection.push_back(sphere); } - _hashKey.clear(); + _hash64 = 0; } void ShapeInfo::setPointCollection(const ShapeInfo::PointCollection& pointCollection) { _pointCollection = pointCollection; - _hashKey.clear(); + _hash64 = 0; } void ShapeInfo::setCapsuleY(float radius, float cylinderHalfHeight) { @@ -172,12 +173,12 @@ void ShapeInfo::setCapsuleY(float radius, float cylinderHalfHeight) { radius = glm::max(radius, MIN_HALF_EXTENT); cylinderHalfHeight = glm::max(cylinderHalfHeight, 0.0f); _halfExtents = glm::vec3(radius, cylinderHalfHeight + radius, radius); - _hashKey.clear(); + _hash64 = 0; } void ShapeInfo::setOffset(const glm::vec3& offset) { _offset = offset; - _hashKey.clear(); + _hash64 = 0; } uint32_t ShapeInfo::getNumSubShapes() const { @@ -269,20 +270,21 @@ float ShapeInfo::computeVolume() const { return volume; } -const HashKey& ShapeInfo::getHash() const { +uint64_t ShapeInfo::getHash() const { // NOTE: we cache the key so we only ever need to compute it once for any valid ShapeInfo instance. - if (_hashKey.isNull() && _type != SHAPE_TYPE_NONE) { + if (_hash64 == 0 && _type != SHAPE_TYPE_NONE) { + HashKey hashKey; // The key is not yet cached therefore we must compute it. - _hashKey.hashUint64((uint64_t)_type); + hashKey.hashUint64((uint64_t)_type); if (_type == SHAPE_TYPE_MULTISPHERE) { for (auto &sphereData : _sphereCollection) { - _hashKey.hashVec3(glm::vec3(sphereData)); - _hashKey.hashFloat(sphereData.w); + hashKey.hashVec3(glm::vec3(sphereData)); + hashKey.hashFloat(sphereData.w); } } else if (_type != SHAPE_TYPE_SIMPLE_HULL) { - _hashKey.hashVec3(_halfExtents); - _hashKey.hashVec3(_offset); + hashKey.hashVec3(_halfExtents); + hashKey.hashVec3(_offset); } else { // TODO: we could avoid hashing all of these points if we were to supply the ShapeInfo with a unique // descriptive string. Shapes that are uniquely described by their type and URL could just put their @@ -292,7 +294,7 @@ const HashKey& ShapeInfo::getHash() const { const int numPoints = (int)points.size(); for (int i = 0; i < numPoints; ++i) { - _hashKey.hashVec3(points[i]); + hashKey.hashVec3(points[i]); } } @@ -300,23 +302,24 @@ const HashKey& ShapeInfo::getHash() const { if (!url.isEmpty()) { QByteArray baUrl = url.toLocal8Bit(); uint32_t urlHash = qChecksum(baUrl.data(), baUrl.size()); - _hashKey.hashUint64((uint64_t)urlHash); + hashKey.hashUint64((uint64_t)urlHash); } if (_type == SHAPE_TYPE_COMPOUND || _type == SHAPE_TYPE_SIMPLE_COMPOUND) { uint64_t numHulls = (uint64_t)_pointCollection.size(); - _hashKey.hashUint64(numHulls); + hashKey.hashUint64(numHulls); } else if (_type == SHAPE_TYPE_MULTISPHERE) { uint64_t numSpheres = (uint64_t)_sphereCollection.size(); - _hashKey.hashUint64(numSpheres); + hashKey.hashUint64(numSpheres); } else if (_type == SHAPE_TYPE_SIMPLE_HULL) { - _hashKey.hashUint64(1); - } + hashKey.hashUint64(1); + } + _hash64 = hashKey.getHash64(); } - return _hashKey; + return _hash64; } void ShapeInfo::setHalfExtents(const glm::vec3& halfExtents) { _halfExtents = glm::max(halfExtents, glm::vec3(MIN_HALF_EXTENT)); - _hashKey.clear(); + _hash64 = 0; } diff --git a/libraries/shared/src/ShapeInfo.h b/libraries/shared/src/ShapeInfo.h index d838d7b214..6b0f981b24 100644 --- a/libraries/shared/src/ShapeInfo.h +++ b/libraries/shared/src/ShapeInfo.h @@ -18,8 +18,6 @@ #include #include -#include "HashKey.h" - const float MIN_SHAPE_OFFSET = 0.001f; // offsets less than 1mm will be ignored // Bullet has a mesh generation util for convex shapes that we used to @@ -91,7 +89,7 @@ public: float computeVolume() const; - const HashKey& getHash() const; + uint64_t getHash() const; protected: void setHalfExtents(const glm::vec3& halfExtents); @@ -102,7 +100,7 @@ protected: TriangleIndices _triangleIndices; glm::vec3 _halfExtents = glm::vec3(0.0f); glm::vec3 _offset = glm::vec3(0.0f); - mutable HashKey _hashKey; + mutable uint64_t _hash64; ShapeType _type = SHAPE_TYPE_NONE; }; diff --git a/libraries/shared/src/VariantMapToScriptValue.cpp b/libraries/shared/src/VariantMapToScriptValue.cpp index 008c3a5d9b..1a747a4e5b 100644 --- a/libraries/shared/src/VariantMapToScriptValue.cpp +++ b/libraries/shared/src/VariantMapToScriptValue.cpp @@ -41,6 +41,9 @@ QScriptValue variantToScriptValue(QVariant& qValue, QScriptEngine& scriptEngine) break; } default: + if (qValue.canConvert()) { + return qValue.toFloat(); + } qCDebug(shared) << "unhandled QScript type" << qValue.type(); break; } diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index 8e938ce25b..ec0a011bd0 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -719,11 +719,7 @@ void OffscreenQmlSurface::setKeyboardRaised(QObject* object, bool raised, bool n } void OffscreenQmlSurface::emitScriptEvent(const QVariant& message) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "emitScriptEvent", Qt::QueuedConnection, Q_ARG(QVariant, message)); - } else { - emit scriptEventReceived(message); - } + emit scriptEventReceived(message); } void OffscreenQmlSurface::emitWebEvent(const QVariant& message) { diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 14a0031f1f..c1a8f363b5 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -3015,6 +3015,13 @@ function toggleDropdown(event) { element.setAttribute("dropped", isDropped !== "true" ? "true" : "false"); } +function closeAllDropdowns() { + elDropdowns = document.querySelectorAll("div.dropdown > dl"); + for (let i = 0; i < elDropdowns.length; ++i) { + elDropdowns[i].setAttribute('dropped', 'false'); + } +} + function setDropdownValue(event) { let dt = event.target.parentNode.parentNode.previousSibling; dt.value = event.target.getAttribute("value"); @@ -3777,6 +3784,8 @@ function loaded() { property.elInput = dt; dt.addEventListener('change', createEmitTextPropertyUpdateFunction(property)); } + + document.addEventListener('click', function(ev) { closeAllDropdowns() }, true); elDropdowns = document.getElementsByTagName("select"); while (elDropdowns.length > 0) { diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index f29edd8ff9..c0e3178521 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -23,7 +23,6 @@ var SENSOR_TO_ROOM_MATRIX = -2; var CAMERA_MATRIX = -7; var ROT_Y_180 = {x: 0.0, y: 1.0, z: 0, w: 0}; var ROT_LANDSCAPE = {x: 1.0, y: 1.0, z: 0, w: 0}; -var ROT_LANDSCAPE_WINDOW = {x: 0.0, y: 0.0, z: 0.0, w: 0}; var TABLET_TEXTURE_RESOLUTION = { x: 480, y: 706 }; var INCHES_TO_METERS = 1 / 39.3701; @@ -170,8 +169,7 @@ WebTablet = function (url, width, dpi, hand, location, visible) { visible: visible }); - // FIXME: Circle3D overlays currently at the wrong dimensions, so we need to account for that here - var homeButtonDim = 4.0 * tabletScaleFactor / 3.0; + var homeButtonDim = 4.0 * tabletScaleFactor / 1.5; var HOME_BUTTON_X_OFFSET = 0.00079 * sensorScaleFactor; var HOME_BUTTON_Y_OFFSET = -1 * ((tabletHeight / 2) - (4.0 * tabletScaleFactor / 2)); var HOME_BUTTON_Z_OFFSET = (tabletDepth / 1.9) * sensorScaleFactor; @@ -196,7 +194,7 @@ WebTablet = function (url, width, dpi, hand, location, visible) { color: {red: 255, green: 255, blue: 255}, solid: true, innerRadius: 0.9, - ignoreIntersection: true, + ignorePickIntersection: true, alpha: 0.0, visible: visible, drawInFront: false, @@ -224,23 +222,6 @@ WebTablet = function (url, width, dpi, hand, location, visible) { } }; - this.myOnHoverEnterOverlay = function (overlayID, pointerEvent) { - _this.onHoverEnterOverlay(overlayID, pointerEvent); - }; - - Overlays.hoverEnterOverlay.connect(this.myOnHoverEnterOverlay); - - this.myOnHoverLeaveOverlay = function (overlayID, pointerEvent) { - _this.onHoverLeaveOverlay(overlayID, pointerEvent); - }; - - Overlays.hoverLeaveOverlay.connect(this.myOnHoverLeaveOverlay); - - this.myOnHoverOverOverlay = function (overlayID, pointerEvent) { - _this.onHoverOverOverlay(overlayID, pointerEvent); - }; - Overlays.hoverOverOverlay.connect(this.myOnHoverOverOverlay); - this.state = "idle"; this.getRoot = function() { @@ -304,16 +285,19 @@ WebTablet.prototype.setLandscape = function(newLandscapeValue) { this.landscape = newLandscapeValue; var cameraOrientation = Quat.cancelOutRollAndPitch(Camera.orientation); - Overlays.editOverlay(this.tabletEntityID, - { rotation: Quat.multiply(cameraOrientation, this.landscape ? ROT_LANDSCAPE : ROT_Y_180) }); + var tabletRotation = Quat.multiply(cameraOrientation, this.landscape ? ROT_LANDSCAPE : ROT_Y_180); + Overlays.editOverlay(this.tabletEntityID, { + rotation: tabletRotation + }); var tabletWidth = getTabletWidthFromSettings() * MyAvatar.sensorToWorldScale; var tabletScaleFactor = tabletWidth / TABLET_NATURAL_DIMENSIONS.x; var tabletHeight = TABLET_NATURAL_DIMENSIONS.y * tabletScaleFactor; var screenWidth = 0.9275 * tabletWidth; var screenHeight = 0.8983 * tabletHeight; + var screenRotation = Quat.angleAxis(180, Vec3.UP); Overlays.editOverlay(this.webOverlayID, { - rotation: Quat.multiply(cameraOrientation, ROT_LANDSCAPE_WINDOW), + localRotation: this.landscape ? Quat.multiply(screenRotation, Quat.angleAxis(-90, Vec3.FRONT)) : screenRotation, dimensions: {x: this.landscape ? screenHeight : screenWidth, y: this.landscape ? screenWidth : screenHeight, z: 0.1} }); }; @@ -350,10 +334,6 @@ WebTablet.prototype.setWidth = function (width) { }; WebTablet.prototype.destroy = function () { - Overlays.hoverEnterOverlay.disconnect(this.myOnHoverEnterOverlay); - Overlays.hoverLeaveOverlay.disconnect(this.myOnHoverLeaveOverlay); - Overlays.hoverOverOverlay.disconnect(this.myOnHoverOverOverlay); - Overlays.deleteOverlay(this.webOverlayID); Overlays.deleteOverlay(this.tabletEntityID); Overlays.deleteOverlay(this.homeButtonID); @@ -449,24 +429,6 @@ WebTablet.prototype.calculateWorldAttitudeRelativeToCamera = function (windowPos }; }; -WebTablet.prototype.onHoverEnterOverlay = function (overlayID, pointerEvent) { - if (overlayID === this.homeButtonID) { - Overlays.editOverlay(this.homeButtonHighlightID, { alpha: 1.0 }); - } -}; - -WebTablet.prototype.onHoverOverOverlay = function (overlayID, pointerEvent) { - if (overlayID !== this.homeButtonID) { - Overlays.editOverlay(this.homeButtonHighlightID, { alpha: 0.0 }); - } -}; - -WebTablet.prototype.onHoverLeaveOverlay = function (overlayID, pointerEvent) { - if (overlayID === this.homeButtonID) { - Overlays.editOverlay(this.homeButtonHighlightID, { alpha: 0.0 }); - } -}; - // compute position, rotation & parentJointIndex of the tablet WebTablet.prototype.calculateTabletAttachmentProperties = function (hand, useMouse, tabletProperties) { if (HMD.active) { @@ -595,17 +557,8 @@ WebTablet.prototype.scheduleMouseMoveProcessor = function() { WebTablet.prototype.handleHomeButtonHover = function(x, y) { var pickRay = Camera.computePickRay(x, y); - var entityPickResults; - var homebuttonHovered = false; - entityPickResults = Overlays.findRayIntersection(pickRay, true, [this.tabletEntityID]); - if (entityPickResults.intersects && (entityPickResults.entityID === this.tabletEntityID || - entityPickResults.overlayID === this.tabletEntityID)) { - var overlayPickResults = Overlays.findRayIntersection(pickRay, true, [this.homeButtonID], []); - if (overlayPickResults.intersects && overlayPickResults.overlayID === this.homeButtonID) { - homebuttonHovered = true; - } - } - Overlays.editOverlay(this.homeButtonHighlightID, { alpha: homebuttonHovered ? 1.0 : 0.0 }); + var homePickResult = Overlays.findRayIntersection(pickRay, true, [this.homeButtonID]); + Overlays.editOverlay(this.homeButtonHighlightID, { alpha: homePickResult.intersects ? 1.0 : 0.0 }); }; WebTablet.prototype.mouseMoveEvent = function (event) { diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 01e5f6e22b..39796311a0 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -822,26 +822,14 @@ SelectionDisplay = (function() { borderSize: 1.4 }); - var handlePropertiesBoundingEdge = { + var handleBoundingBox = Overlays.addOverlay("cube", { alpha: 1, color: COLOR_BOUNDING_EDGE, visible: false, - ignoreRayIntersection: true, + ignorePickIntersection: true, drawInFront: true, - lineWidth: 0.2 - }; - var handleBoundingTREdge = Overlays.addOverlay("line3d", handlePropertiesBoundingEdge); - var handleBoundingTLEdge = Overlays.addOverlay("line3d", handlePropertiesBoundingEdge); - var handleBoundingTFEdge = Overlays.addOverlay("line3d", handlePropertiesBoundingEdge); - var handleBoundingTNEdge = Overlays.addOverlay("line3d", handlePropertiesBoundingEdge); - var handleBoundingBREdge = Overlays.addOverlay("line3d", handlePropertiesBoundingEdge); - var handleBoundingBLEdge = Overlays.addOverlay("line3d", handlePropertiesBoundingEdge); - var handleBoundingBFEdge = Overlays.addOverlay("line3d", handlePropertiesBoundingEdge); - var handleBoundingBNEdge = Overlays.addOverlay("line3d", handlePropertiesBoundingEdge); - var handleBoundingNREdge = Overlays.addOverlay("line3d", handlePropertiesBoundingEdge); - var handleBoundingNLEdge = Overlays.addOverlay("line3d", handlePropertiesBoundingEdge); - var handleBoundingFREdge = Overlays.addOverlay("line3d", handlePropertiesBoundingEdge); - var handleBoundingFLEdge = Overlays.addOverlay("line3d", handlePropertiesBoundingEdge); + isSolid: false + }); var handleDuplicator = Overlays.addOverlay("cube", { alpha: 1, @@ -929,26 +917,17 @@ SelectionDisplay = (function() { handleStretchYPanel, handleStretchZPanel, handleScaleCube, - handleBoundingTREdge, - handleBoundingTLEdge, - handleBoundingTFEdge, - handleBoundingTNEdge, - handleBoundingBREdge, - handleBoundingBLEdge, - handleBoundingBFEdge, - handleBoundingBNEdge, - handleBoundingNREdge, - handleBoundingNLEdge, - handleBoundingFREdge, - handleBoundingFLEdge, + handleBoundingBox, handleDuplicator, selectionBox, iconSelectionBox, xRailOverlay, yRailOverlay, zRailOverlay - ]; + + const nonLayeredOverlays = [selectionBox, iconSelectionBox]; + var maximumHandleInAllOverlays = handleDuplicator; overlayNames[handleTranslateXCone] = "handleTranslateXCone"; @@ -973,18 +952,7 @@ SelectionDisplay = (function() { overlayNames[handleScaleCube] = "handleScaleCube"; - overlayNames[handleBoundingTREdge] = "handleBoundingTREdge"; - overlayNames[handleBoundingTLEdge] = "handleBoundingTLEdge"; - overlayNames[handleBoundingTFEdge] = "handleBoundingTFEdge"; - overlayNames[handleBoundingTNEdge] = "handleBoundingTNEdge"; - overlayNames[handleBoundingBREdge] = "handleBoundingBREdge"; - overlayNames[handleBoundingBLEdge] = "handleBoundingBLEdge"; - overlayNames[handleBoundingBFEdge] = "handleBoundingBFEdge"; - overlayNames[handleBoundingBNEdge] = "handleBoundingBNEdge"; - overlayNames[handleBoundingNREdge] = "handleBoundingNREdge"; - overlayNames[handleBoundingNLEdge] = "handleBoundingNLEdge"; - overlayNames[handleBoundingFREdge] = "handleBoundingFREdge"; - overlayNames[handleBoundingFLEdge] = "handleBoundingFLEdge"; + overlayNames[handleBoundingBox] = "handleBoundingBox"; overlayNames[handleDuplicator] = "handleDuplicator"; overlayNames[selectionBox] = "selectionBox"; @@ -1074,7 +1042,31 @@ SelectionDisplay = (function() { return null; } - var intersectObj = Overlays.findRayIntersection(queryRay, true, overlayIncludes, overlayExcludes); + // We want to first check the drawInFront overlays (i.e. the handles, but really everything except the selectionBoxes) + // so that you can click on them even when they're behind things + var overlayIncludesLayered = []; + var overlayIncludesNonLayered = []; + for (var i = 0; i < overlayIncludes.length; i++) { + var value = overlayIncludes[i]; + var contains = false; + for (var j = 0; j < nonLayeredOverlays.length; j++) { + if (nonLayeredOverlays[j] === value) { + contains = true; + break; + } + } + if (contains) { + overlayIncludesNonLayered.push(value); + } else { + overlayIncludesLayered.push(value); + } + } + + var intersectObj = Overlays.findRayIntersection(queryRay, true, overlayIncludesLayered, overlayExcludes); + + if (!intersectObj.intersects && overlayIncludesNonLayered.length > 0) { + intersectObj = Overlays.findRayIntersection(queryRay, true, overlayIncludesNonLayered, overlayExcludes); + } if (wantDebug) { if (!overlayIncludes) { @@ -1200,7 +1192,7 @@ SelectionDisplay = (function() { that.updateHighlight = function(event) { // if no tool is active, then just look for handles to highlight... var pickRay = generalComputePickRay(event.x, event.y); - var result = Overlays.findRayIntersection(pickRay); + var result = testRayIntersect(pickRay, allOverlays); var pickedColor; var highlightNeeded = false; @@ -1703,40 +1695,26 @@ SelectionDisplay = (function() { dimensions: scaleCubeDimensions }); - // UPDATE BOUNDING BOX EDGES + // UPDATE BOUNDING BOX + Overlays.editOverlay(handleBoundingBox, { + position: position, + rotation: rotation, + dimensions: dimensions + }); + + // UPDATE STRETCH HIGHLIGHT PANELS var edgeOffsetX = BOUNDING_EDGE_OFFSET * dimensions.x; var edgeOffsetY = BOUNDING_EDGE_OFFSET * dimensions.y; var edgeOffsetZ = BOUNDING_EDGE_OFFSET * dimensions.z; - var LBNPosition = { x: -edgeOffsetX, y: -edgeOffsetY, z: -edgeOffsetZ }; - LBNPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, LBNPosition)); - var RBNPosition = { x: edgeOffsetX, y: -edgeOffsetY, z: -edgeOffsetZ }; - RBNPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, RBNPosition)); - var LBFPosition = { x: -edgeOffsetX, y: -edgeOffsetY, z: edgeOffsetZ }; - LBFPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, LBFPosition)); var RBFPosition = { x: edgeOffsetX, y: -edgeOffsetY, z: edgeOffsetZ }; RBFPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, RBFPosition)); + var RTFPosition = { x: edgeOffsetX, y: edgeOffsetY, z: edgeOffsetZ }; + RTFPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, RTFPosition)); var LTNPosition = { x: -edgeOffsetX, y: edgeOffsetY, z: -edgeOffsetZ }; LTNPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, LTNPosition)); var RTNPosition = { x: edgeOffsetX, y: edgeOffsetY, z: -edgeOffsetZ }; RTNPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, RTNPosition)); - var LTFPosition = { x: -edgeOffsetX, y: edgeOffsetY, z: edgeOffsetZ }; - LTFPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, LTFPosition)); - var RTFPosition = { x: edgeOffsetX, y: edgeOffsetY, z: edgeOffsetZ }; - RTFPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, RTFPosition)); - Overlays.editOverlay(handleBoundingTREdge, { start: RTNPosition, end: RTFPosition }); - Overlays.editOverlay(handleBoundingTLEdge, { start: LTNPosition, end: LTFPosition }); - Overlays.editOverlay(handleBoundingTFEdge, { start: LTFPosition, end: RTFPosition }); - Overlays.editOverlay(handleBoundingTNEdge, { start: LTNPosition, end: RTNPosition }); - Overlays.editOverlay(handleBoundingBREdge, { start: RBNPosition, end: RBFPosition }); - Overlays.editOverlay(handleBoundingBLEdge, { start: LBNPosition, end: LBFPosition }); - Overlays.editOverlay(handleBoundingBFEdge, { start: LBFPosition, end: RBFPosition }); - Overlays.editOverlay(handleBoundingBNEdge, { start: LBNPosition, end: RBNPosition }); - Overlays.editOverlay(handleBoundingNREdge, { start: RTNPosition, end: RBNPosition }); - Overlays.editOverlay(handleBoundingNLEdge, { start: LTNPosition, end: LBNPosition }); - Overlays.editOverlay(handleBoundingFREdge, { start: RTFPosition, end: RBFPosition }); - Overlays.editOverlay(handleBoundingFLEdge, { start: LTFPosition, end: LBFPosition }); - - // UPDATE STRETCH HIGHLIGHT PANELS + var RBFPositionRotated = Vec3.multiplyQbyV(rotationInverse, RBFPosition); var RTFPositionRotated = Vec3.multiplyQbyV(rotationInverse, RTFPosition); var LTNPositionRotated = Vec3.multiplyQbyV(rotationInverse, LTNPosition); @@ -1867,7 +1845,7 @@ SelectionDisplay = (function() { var showOutlineForZone = (SelectionManager.selections.length === 1 && typeof SelectionManager.savedProperties[SelectionManager.selections[0]] !== "undefined" && SelectionManager.savedProperties[SelectionManager.selections[0]].type === "Zone"); - that.setHandleBoundingEdgeVisible(showOutlineForZone || (!isActiveTool(handleRotatePitchRing) && + that.setHandleBoundingBoxVisible(showOutlineForZone || (!isActiveTool(handleRotatePitchRing) && !isActiveTool(handleRotateYawRing) && !isActiveTool(handleRotateRollRing))); @@ -1967,26 +1945,15 @@ SelectionDisplay = (function() { // FUNCTION: SET HANDLE SCALE VISIBLE that.setHandleScaleVisible = function(isVisible) { that.setHandleScaleVisible(isVisible); - that.setHandleBoundingEdgeVisible(isVisible); + that.setHandleBoundingBoxVisible(isVisible); }; that.setHandleScaleVisible = function(isVisible) { Overlays.editOverlay(handleScaleCube, { visible: isVisible }); }; - that.setHandleBoundingEdgeVisible = function(isVisible) { - Overlays.editOverlay(handleBoundingTREdge, { visible: isVisible }); - Overlays.editOverlay(handleBoundingTLEdge, { visible: isVisible }); - Overlays.editOverlay(handleBoundingTFEdge, { visible: isVisible }); - Overlays.editOverlay(handleBoundingTNEdge, { visible: isVisible }); - Overlays.editOverlay(handleBoundingBREdge, { visible: isVisible }); - Overlays.editOverlay(handleBoundingBLEdge, { visible: isVisible }); - Overlays.editOverlay(handleBoundingBFEdge, { visible: isVisible }); - Overlays.editOverlay(handleBoundingBNEdge, { visible: isVisible }); - Overlays.editOverlay(handleBoundingNREdge, { visible: isVisible }); - Overlays.editOverlay(handleBoundingNLEdge, { visible: isVisible }); - Overlays.editOverlay(handleBoundingFREdge, { visible: isVisible }); - Overlays.editOverlay(handleBoundingFLEdge, { visible: isVisible }); + that.setHandleBoundingBoxVisible = function(isVisible) { + Overlays.editOverlay(handleBoundingBox, { visible: isVisible }); }; // FUNCTION: SET HANDLE DUPLICATOR VISIBLE diff --git a/scripts/system/libraries/utils.js b/scripts/system/libraries/utils.js index c0a52a45c6..931c346299 100644 --- a/scripts/system/libraries/utils.js +++ b/scripts/system/libraries/utils.js @@ -412,8 +412,7 @@ resizeTablet = function (width, newParentJointIndex, sensorToWorldScaleOverride) }); // update homeButton - // FIXME: Circle3D overlays currently at the wrong dimensions, so we need to account for that here - var homeButtonDim = 4.0 * tabletScaleFactor / 3.0; + var homeButtonDim = 4.0 * tabletScaleFactor / 1.5; var HOME_BUTTON_X_OFFSET = 0.00079 * sensorScaleOffsetOverride * sensorScaleFactor; var HOME_BUTTON_Y_OFFSET = -1 * ((tabletHeight / 2) - (4.0 * tabletScaleFactor / 2)) * sensorScaleOffsetOverride; var HOME_BUTTON_Z_OFFSET = (tabletDepth / 1.9) * sensorScaleOffsetOverride; diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index 780dacf85e..449921514c 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -114,7 +114,7 @@ uiHand = LEFT_HAND, miniUIOverlay = null, MINI_UI_HTML = Script.resolvePath("./html/miniTablet.html"), - MINI_UI_DIMENSIONS = { x: 0.059, y: 0.0865 }, + MINI_UI_DIMENSIONS = { x: 0.059, y: 0.0865, z: 0.001 }, MINI_UI_WIDTH_PIXELS = 150, METERS_TO_INCHES = 39.3701, MINI_UI_DPI = MINI_UI_WIDTH_PIXELS / (MINI_UI_DIMENSIONS.x * METERS_TO_INCHES), @@ -171,19 +171,23 @@ function updateMutedStatus() { - var isMuted = Audio.muted; - miniOverlayObject.emitScriptEvent(JSON.stringify({ - type: MUTE_MESSAGE, - on: isMuted, - icon: isMuted ? MUTE_ON_ICON : MUTE_OFF_ICON - })); + if (miniOverlayObject) { + var isMuted = Audio.muted; + miniOverlayObject.emitScriptEvent(JSON.stringify({ + type: MUTE_MESSAGE, + on: isMuted, + icon: isMuted ? MUTE_ON_ICON : MUTE_OFF_ICON + })); + } } function setGotoIcon() { - miniOverlayObject.emitScriptEvent(JSON.stringify({ - type: GOTO_MESSAGE, - icon: GOTO_ICON - })); + if (miniOverlayObject) { + miniOverlayObject.emitScriptEvent(JSON.stringify({ + type: GOTO_MESSAGE, + icon: GOTO_ICON + })); + } } function onWebEventReceived(data) { @@ -445,6 +449,19 @@ }); } + function checkEventBridge() { + // The miniUIOverlay overlay's overlay object is not available immediately the overlay is created so we have to + // provide a means to check for and connect it when it does become available. + if (miniOverlayObject) { + return; + } + + miniOverlayObject = Overlays.getOverlayObject(miniUIOverlay); + if (miniOverlayObject) { + miniOverlayObject.webEventReceived.connect(onWebEventReceived); + } + } + function create() { miniOverlay = Overlays.addOverlay("model", { url: MINI_MODEL, @@ -452,7 +469,7 @@ solid: true, grabbable: true, showKeyboardFocusHighlight: false, - displayInFront: true, + drawInFront: true, visible: false }); miniUIOverlay = Overlays.addOverlay("web3d", { @@ -465,14 +482,13 @@ alpha: 0, // Hide overlay while its content is being created. grabbable: false, showKeyboardFocusHighlight: false, - displayInFront: true, + drawInFront: true, visible: false }); miniUIOverlayEnabled = false; // This and alpha = 0 hides overlay while its content is being created. - miniOverlayObject = Overlays.getOverlayObject(miniUIOverlay); - miniOverlayObject.webEventReceived.connect(onWebEventReceived); + checkEventBridge(); } function destroy() { @@ -502,6 +518,7 @@ updateRotation: updateRotation, release: release, hide: hide, + checkEventBridge: checkEventBridge, destroy: destroy }; @@ -978,6 +995,8 @@ } function updateState() { + ui.checkEventBridge(); + if (STATE_MACHINE[STATE_STRINGS[miniState]].update) { STATE_MACHINE[STATE_STRINGS[miniState]].update(); } @@ -1125,4 +1144,4 @@ setUp(); Script.scriptEnding.connect(tearDown); -}()); +}()); \ No newline at end of file diff --git a/tests/animation/src/AnimTests.cpp b/tests/animation/src/AnimTests.cpp index b1926efb71..0cd9571e22 100644 --- a/tests/animation/src/AnimTests.cpp +++ b/tests/animation/src/AnimTests.cpp @@ -22,11 +22,9 @@ #include #include #include -#include QTEST_MAIN(AnimTests) - const float TEST_EPSILON = 0.001f; void AnimTests::initTestCase() { @@ -374,10 +372,16 @@ void AnimTests::testAnimPose() { const glm::quat ROT_Y_180 = glm::angleAxis(PI, glm::vec3(0.0f, 1.0, 0.0f)); const glm::quat ROT_Z_30 = glm::angleAxis(PI / 6.0f, glm::vec3(1.0f, 0.0f, 0.0f)); - std::vector scaleVec = { - 1.0f, - 2.0f, - 0.5f + std::vector scaleVec = { + glm::vec3(1), + glm::vec3(2.0f, 1.0f, 1.0f), + glm::vec3(1.0f, 0.5f, 1.0f), + glm::vec3(1.0f, 1.0f, 1.5f), + glm::vec3(2.0f, 0.5f, 1.5f), + glm::vec3(-2.0f, 0.5f, 1.5f), + glm::vec3(2.0f, -0.5f, 1.5f), + glm::vec3(2.0f, 0.5f, -1.5f), + glm::vec3(-2.0f, -0.5f, -1.5f), }; std::vector rotVec = { @@ -407,7 +411,7 @@ void AnimTests::testAnimPose() { for (auto& trans : transVec) { // build a matrix the old fashioned way. - glm::mat4 scaleMat = glm::scale(glm::mat4(), glm::vec3(scale)); + glm::mat4 scaleMat = glm::scale(glm::mat4(), scale); glm::mat4 rotTransMat = createMatFromQuatAndPos(rot, trans); glm::mat4 rawMat = rotTransMat * scaleMat; @@ -425,7 +429,7 @@ void AnimTests::testAnimPose() { for (auto& trans : transVec) { // build a matrix the old fashioned way. - glm::mat4 scaleMat = glm::scale(glm::mat4(), glm::vec3(scale)); + glm::mat4 scaleMat = glm::scale(glm::mat4(), scale); glm::mat4 rotTransMat = createMatFromQuatAndPos(rot, trans); glm::mat4 rawMat = rotTransMat * scaleMat; @@ -441,145 +445,6 @@ void AnimTests::testAnimPose() { } } -void AnimTests::testAnimPoseMultiply() { - const float PI = (float)M_PI; - const glm::quat ROT_X_90 = glm::angleAxis(PI / 2.0f, glm::vec3(1.0f, 0.0f, 0.0f)); - const glm::quat ROT_Y_180 = glm::angleAxis(PI, glm::vec3(0.0f, 1.0, 0.0f)); - const glm::quat ROT_Z_30 = glm::angleAxis(PI / 6.0f, glm::vec3(1.0f, 0.0f, 0.0f)); - - std::vector scaleVec = { - 1.0f, - 2.0f, - 0.5f, - }; - - std::vector rotVec = { - glm::quat(), - ROT_X_90, - ROT_Y_180, - ROT_Z_30, - ROT_X_90 * ROT_Y_180 * ROT_Z_30, - -ROT_Y_180 - }; - - std::vector transVec = { - glm::vec3(), - glm::vec3(10.0f, 0.0f, 0.0f), - glm::vec3(0.0f, 5.0f, 0.0f), - glm::vec3(0.0f, 0.0f, 7.5f), - glm::vec3(10.0f, 5.0f, 7.5f), - glm::vec3(-10.0f, 5.0f, 7.5f), - glm::vec3(10.0f, -5.0f, 7.5f), - glm::vec3(10.0f, 5.0f, -7.5f) - }; - - const float TEST_EPSILON = 0.001f; - - std::vector matrixVec; - std::vector poseVec; - - for (auto& scale : scaleVec) { - for (auto& rot : rotVec) { - for (auto& trans : transVec) { - - // build a matrix the old fashioned way. - glm::mat4 scaleMat = glm::scale(glm::mat4(), glm::vec3(scale)); - glm::mat4 rotTransMat = createMatFromQuatAndPos(rot, trans); - glm::mat4 rawMat = rotTransMat * scaleMat; - - matrixVec.push_back(rawMat); - - // use an anim pose to build a matrix by parts. - AnimPose pose(scale, rot, trans); - poseVec.push_back(pose); - } - } - } - - for (int i = 0; i < matrixVec.size(); i++) { - for (int j = 0; j < matrixVec.size(); j++) { - - // multiply the matrices together - glm::mat4 matrix = matrixVec[i] * matrixVec[j]; - - // convert to matrix (note this will remove sheer from the matrix) - AnimPose resultA(matrix); - - // multiply the poses together directly - AnimPose resultB = poseVec[i] * poseVec[j]; - - /* - qDebug() << "matrixVec[" << i << "] =" << matrixVec[i]; - qDebug() << "matrixVec[" << j << "] =" << matrixVec[j]; - qDebug() << "matrixResult =" << resultA; - - qDebug() << "poseVec[" << i << "] =" << poseVec[i]; - qDebug() << "poseVec[" << j << "] =" << poseVec[j]; - qDebug() << "poseResult =" << resultB; - */ - - // compare results. - QCOMPARE_WITH_ABS_ERROR(resultA.scale(), resultB.scale(), TEST_EPSILON); - QCOMPARE_WITH_ABS_ERROR(resultA.rot(), resultB.rot(), TEST_EPSILON); - QCOMPARE_WITH_ABS_ERROR(resultA.trans(), resultB.trans(), TEST_EPSILON); - } - } -} - -void AnimTests::testAnimPoseInverse() { - const float PI = (float)M_PI; - const glm::quat ROT_X_90 = glm::angleAxis(PI / 2.0f, glm::vec3(1.0f, 0.0f, 0.0f)); - const glm::quat ROT_Y_180 = glm::angleAxis(PI, glm::vec3(0.0f, 1.0, 0.0f)); - const glm::quat ROT_Z_30 = glm::angleAxis(PI / 6.0f, glm::vec3(1.0f, 0.0f, 0.0f)); - - std::vector scaleVec = { - 1.0f, - 2.0f, - 0.5f - }; - - std::vector rotVec = { - glm::quat(), - ROT_X_90, - ROT_Y_180, - ROT_Z_30, - ROT_X_90 * ROT_Y_180 * ROT_Z_30, - -ROT_Y_180 - }; - - std::vector transVec = { - glm::vec3(), - glm::vec3(10.0f, 0.0f, 0.0f), - glm::vec3(0.0f, 5.0f, 0.0f), - glm::vec3(0.0f, 0.0f, 7.5f), - glm::vec3(10.0f, 5.0f, 7.5f), - glm::vec3(-10.0f, 5.0f, 7.5f), - glm::vec3(10.0f, -5.0f, 7.5f), - glm::vec3(10.0f, 5.0f, -7.5f) - }; - - const float TEST_EPSILON = 0.001f; - - for (auto& scale : scaleVec) { - for (auto& rot : rotVec) { - for (auto& trans : transVec) { - - // build a matrix the old fashioned way. - glm::mat4 scaleMat = glm::scale(glm::mat4(), glm::vec3(scale)); - glm::mat4 rotTransMat = createMatFromQuatAndPos(rot, trans); - glm::mat4 rawMat = glm::inverse(rotTransMat * scaleMat); - - // use an anim pose to build a matrix by parts. - AnimPose pose(scale, rot, trans); - glm::mat4 poseMat = pose.inverse(); - - QCOMPARE_WITH_ABS_ERROR(rawMat, poseMat, TEST_EPSILON); - } - } - } -} - - void AnimTests::testExpressionTokenizer() { QString str = "(10 + x) >= 20.1 && (y != !z)"; AnimExpression e("x"); diff --git a/tests/animation/src/AnimTests.h b/tests/animation/src/AnimTests.h index 326545b0a9..439793f21d 100644 --- a/tests/animation/src/AnimTests.h +++ b/tests/animation/src/AnimTests.h @@ -27,8 +27,6 @@ private slots: void testVariant(); void testAccumulateTime(); void testAnimPose(); - void testAnimPoseMultiply(); - void testAnimPoseInverse(); void testExpressionTokenizer(); void testExpressionParser(); void testExpressionEvaluator(); diff --git a/tests/physics/src/ShapeInfoTests.cpp b/tests/physics/src/ShapeInfoTests.cpp index efc88a4032..0116eb027f 100644 --- a/tests/physics/src/ShapeInfoTests.cpp +++ b/tests/physics/src/ShapeInfoTests.cpp @@ -55,20 +55,20 @@ void ShapeInfoTests::testHashFunctions() { // test sphere info.setSphere(radiusX); ++testCount; - HashKey key = info.getHash(); - hashPtr = hashes.find(key); + HashKey hashKey(info.getHash()); + hashPtr = hashes.find(hashKey); if (hashPtr) { std::cout << testCount << " hash collision sphere radius = " << radiusX - << " h = 0x" << std::hex << key.getHash() << " : 0x" << *hashPtr + << " h = 0x" << std::hex << hashKey.getHash() << " : 0x" << *hashPtr << std::dec << std::endl; ++numCollisions; assert(false); } else { - hashes.insert(key, key.getHash()); + hashes.insert(hashKey, hashKey.getHash()); } // track bit distribution counts to evaluate hash function randomness for (int j = 0; j < NUM_HASH_BITS; ++j) { - if (masks[j] & key.getHash()) { + if (masks[j] & hashKey.getHash()) { ++bits[j]; } } @@ -80,21 +80,21 @@ void ShapeInfoTests::testHashFunctions() { // test box info.setBox(glm::vec3(radiusX, radiusY, radiusZ)); ++testCount; - HashKey key = info.getHash(); - hashPtr = hashes.find(key); + HashKey hashKey(info.getHash()); + hashPtr = hashes.find(hashKey); if (hashPtr) { std::cout << testCount << " hash collision box dimensions = < " << radiusX << ", " << radiusY << ", " << radiusZ << " >" - << " h = 0x" << std::hex << key.getHash() << " : 0x" << *hashPtr << " : 0x" << key.getHash64() + << " h = 0x" << std::hex << hashKey.getHash() << " : 0x" << *hashPtr << " : 0x" << hashKey.getHash64() << std::dec << std::endl; ++numCollisions; assert(false); } else { - hashes.insert(key, key.getHash()); + hashes.insert(hashKey, hashKey.getHash()); } // track bit distribution counts to evaluate hash function randomness for (int k = 0; k < NUM_HASH_BITS; ++k) { - if (masks[k] & key.getHash()) { + if (masks[k] & hashKey.getHash()) { ++bits[k]; } } @@ -117,14 +117,14 @@ void ShapeInfoTests::testBoxShape() { ShapeInfo info; glm::vec3 halfExtents(1.23f, 4.56f, 7.89f); info.setBox(halfExtents); - HashKey key = info.getHash(); + HashKey hashKey(info.getHash()); const btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info); QCOMPARE(shape != nullptr, true); ShapeInfo otherInfo = info; HashKey otherKey = otherInfo.getHash(); - QCOMPARE(key.getHash(), otherKey.getHash()); + QCOMPARE(hashKey.getHash(), otherKey.getHash()); delete shape; } @@ -133,14 +133,14 @@ void ShapeInfoTests::testSphereShape() { ShapeInfo info; float radius = 1.23f; info.setSphere(radius); - HashKey key = info.getHash(); + HashKey hashKey = info.getHash(); const btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info); QCOMPARE(shape != nullptr, true); ShapeInfo otherInfo = info; HashKey otherKey = otherInfo.getHash(); - QCOMPARE(key.getHash(), otherKey.getHash()); + QCOMPARE(hashKey.getHash(), otherKey.getHash()); delete shape; } @@ -151,14 +151,14 @@ void ShapeInfoTests::testCylinderShape() { float radius = 1.23f; float height = 4.56f; info.setCylinder(radius, height); - HashKey key = info.getHash(); + HashKey hashKey(info.getHash()); btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info); QCOMPARE(shape != nullptr, true); ShapeInfo otherInfo = info; HashKey otherKey = otherInfo.getHash(); - QCOMPARE(key.getHash(), otherKey.getHash()); + QCOMPARE(hashKey.getHash(), otherKey.getHash()); delete shape; */ @@ -170,14 +170,14 @@ void ShapeInfoTests::testCapsuleShape() { float radius = 1.23f; float height = 4.56f; info.setCapsule(radius, height); - HashKey key = info.getHash(); + HashKey hashKey(info.getHash()); btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info); QCOMPARE(shape != nullptr, true); ShapeInfo otherInfo = info; HashKey otherKey = otherInfo.getHash(); - QCOMPARE(key.getHash(), otherKey.getHash()); + QCOMPARE(hashKey.getHash(), otherKey.getHash()); delete shape; */ diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 4d7a594d92..b9ae635a4f 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -19,8 +19,8 @@ function(check_test name) endfunction() if (BUILD_TOOLS) - # Allow different tools for production builds - if (RELEASE_TYPE STREQUAL "PRODUCTION") + # Allow different tools for stable builds + if (STABLE_BUILD) set(ALL_TOOLS udt-test vhacd-util diff --git a/tools/nitpick/src/AWSInterface.cpp b/tools/nitpick/src/AWSInterface.cpp index 59be26c383..63d5af9272 100644 --- a/tools/nitpick/src/AWSInterface.cpp +++ b/tools/nitpick/src/AWSInterface.cpp @@ -27,13 +27,30 @@ void AWSInterface::createWebPageFromResults(const QString& testResults, const QString& workingDirectory, QCheckBox* updateAWSCheckBox, QLineEdit* urlLineEdit) { - _testResults = testResults; _workingDirectory = workingDirectory; + // Verify filename is in correct format + // For example `D:/tt/TestResults--2019-02-10_17-30-57(local)[DESKTOP-6BO62Q9].zip` + QStringList parts = testResults.split('/'); + QString zipFilename = parts[parts.length() - 1]; + + QStringList zipFolderNameParts = zipFilename.split(QRegExp("[\\(\\)\\[\\]]"), QString::SkipEmptyParts); + + if (!QRegularExpression("TestResults--\\d{4}(-\\d\\d){2}_\\d\\d(-\\d\\d){2}").match(zipFolderNameParts[0]).hasMatch() || + !QRegularExpression("\\w").match(zipFolderNameParts[1]).hasMatch() || // build (local, build number or PR number) + !QRegularExpression("\\w").match(zipFolderNameParts[2]).hasMatch() // machine name + ) { + QMessageBox::critical(0, "Filename is in wrong format", "'" + zipFilename + "' is not in nitpick format"); + return; + } + + _testResults = testResults; + _urlLineEdit = urlLineEdit; _urlLineEdit->setEnabled(false); - extractTestFailuresFromZippedFolder(); + QString zipFilenameWithoutExtension = zipFilename.split('.')[0]; + extractTestFailuresFromZippedFolder(_workingDirectory + "/" + zipFilenameWithoutExtension); createHTMLFile(); if (updateAWSCheckBox->isChecked()) { @@ -44,14 +61,12 @@ void AWSInterface::createWebPageFromResults(const QString& testResults, } } -void AWSInterface::extractTestFailuresFromZippedFolder() { +void AWSInterface::extractTestFailuresFromZippedFolder(const QString& folderName) { // For a test results zip file called `D:/tt/TestResults--2018-10-02_16-54-11(9426)[DESKTOP-PMKNLSQ].zip` // the folder will be called `TestResults--2018-10-02_16-54-11(9426)[DESKTOP-PMKNLSQ]` // and, this folder will be in the working directory - QStringList parts = _testResults.split('/'); - QString zipFolderName = _workingDirectory + "/" + parts[parts.length() - 1].split('.')[0]; - if (QDir(zipFolderName).exists()) { - QDir dir = zipFolderName; + if (QDir(folderName).exists()) { + QDir dir = folderName; dir.removeRecursively(); } diff --git a/tools/nitpick/src/AWSInterface.h b/tools/nitpick/src/AWSInterface.h index 63c48580f5..d95b8ecf2f 100644 --- a/tools/nitpick/src/AWSInterface.h +++ b/tools/nitpick/src/AWSInterface.h @@ -30,7 +30,7 @@ public: QCheckBox* updateAWSCheckBox, QLineEdit* urlLineEdit); - void extractTestFailuresFromZippedFolder(); + void extractTestFailuresFromZippedFolder(const QString& folderName); void createHTMLFile(); void startHTMLpage(QTextStream& stream); diff --git a/tools/nitpick/src/Nitpick.cpp b/tools/nitpick/src/Nitpick.cpp index 3a799ce3c2..d5bc6f6e5a 100644 --- a/tools/nitpick/src/Nitpick.cpp +++ b/tools/nitpick/src/Nitpick.cpp @@ -40,7 +40,7 @@ Nitpick::Nitpick(QWidget* parent) : QMainWindow(parent) { _ui.plainTextEdit->setReadOnly(true); - setWindowTitle("Nitpick - v2.1.1"); + setWindowTitle("Nitpick - v2.1.2"); } Nitpick::~Nitpick() { diff --git a/tools/nitpick/src/Test.cpp b/tools/nitpick/src/Test.cpp index f618118289..f1e950db88 100644 --- a/tools/nitpick/src/Test.cpp +++ b/tools/nitpick/src/Test.cpp @@ -835,11 +835,16 @@ void Test::createRecursiveScript(const QString& directory, bool interactiveMode) << endl; textStream << "Script.include(PATH_TO_THE_REPO_PATH_UTILS_FILE);" << endl << endl; - textStream << "if (typeof nitpick === 'undefined') nitpick = createNitpick(Script.resolvePath(\".\"));" << endl; - textStream << "if (typeof testsRootPath === 'undefined') testsRootPath = nitpick.getTestsRootPath();" << endl << endl; - - textStream << "nitpick.enableRecursive();" << endl; - textStream << "nitpick.enableAuto();" << endl << endl; + // The 'depth' variable is used to signal when to start running the recursive scripts + textStream << "if (typeof depth === 'undefined') {" << endl; + textStream << " depth = 0;" << endl; + textStream << " nitpick = createNitpick(Script.resolvePath(\".\"));" << endl; + textStream << " testsRootPath = nitpick.getTestsRootPath();" << endl << endl; + textStream << " nitpick.enableRecursive();" << endl; + textStream << " nitpick.enableAuto();" << endl; + textStream << "} else {" << endl; + textStream << " depth++" << endl; + textStream << "}" << endl << endl; // Now include the test scripts for (int i = 0; i < directories.length(); ++i) { @@ -847,8 +852,9 @@ void Test::createRecursiveScript(const QString& directory, bool interactiveMode) } textStream << endl; - textStream << "if (typeof runningRecursive === 'undefined') {" << endl; - textStream << " runningRecursive = true;" << endl; + textStream << "if (depth > 0) {" << endl; + textStream << " depth--;" << endl; + textStream << "} else {" << endl; textStream << " nitpick.runRecursive();" << endl; textStream << "}" << endl << endl; @@ -1091,7 +1097,7 @@ void Test::setTestRailCreateMode(TestRailCreateMode testRailCreateMode) { void Test::createWebPage(QCheckBox* updateAWSCheckBox, QLineEdit* urlLineEdit) { QString testResults = QFileDialog::getOpenFileName(nullptr, "Please select the zipped test results to update from", nullptr, - "Zipped Test Results (*.zip)"); + "Zipped Test Results (TestResults--*.zip)"); if (testResults.isNull()) { return; } diff --git a/tools/nitpick/src/TestRunnerDesktop.cpp b/tools/nitpick/src/TestRunnerDesktop.cpp index 50cb6f9a07..e45d895886 100644 --- a/tools/nitpick/src/TestRunnerDesktop.cpp +++ b/tools/nitpick/src/TestRunnerDesktop.cpp @@ -554,7 +554,7 @@ void TestRunnerDesktop::evaluateResults() { nitpick->startTestsEvaluation(false, true, _snapshotFolder, _branch, _user); } -void TestRunnerDesktop::automaticTestRunEvaluationComplete(QString zippedFolder, int numberOfFailures) { +void TestRunnerDesktop::automaticTestRunEvaluationComplete(const QString& zippedFolder, int numberOfFailures) { addBuildNumberToResults(zippedFolder); restoreHighFidelityAppDataFolder(); @@ -580,14 +580,19 @@ void TestRunnerDesktop::automaticTestRunEvaluationComplete(QString zippedFolder, _runNow->setEnabled(true); } -void TestRunnerDesktop::addBuildNumberToResults(QString zippedFolderName) { - QString augmentedFilename; +void TestRunnerDesktop::addBuildNumberToResults(const QString& zippedFolderName) { + QString augmentedFilename { zippedFolderName }; if (!_runLatest->isChecked()) { - augmentedFilename = zippedFolderName.replace("local", getPRNumberFromURL(_url->text())); + augmentedFilename.replace("local", getPRNumberFromURL(_url->text())); } else { - augmentedFilename = zippedFolderName.replace("local", _buildInformation.build); + augmentedFilename.replace("local", _buildInformation.build); + } + + if (!QFile::rename(zippedFolderName, augmentedFilename)) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Could not rename '" + zippedFolderName + "' to '" + augmentedFilename); + exit(-1); + } - QFile::rename(zippedFolderName, augmentedFilename); } void TestRunnerDesktop::restoreHighFidelityAppDataFolder() { diff --git a/tools/nitpick/src/TestRunnerDesktop.h b/tools/nitpick/src/TestRunnerDesktop.h index a8f828b9d4..140a81f465 100644 --- a/tools/nitpick/src/TestRunnerDesktop.h +++ b/tools/nitpick/src/TestRunnerDesktop.h @@ -61,8 +61,8 @@ public: void runInterfaceWithTestScript(); void evaluateResults(); - void automaticTestRunEvaluationComplete(QString zippedFolderName, int numberOfFailures); - void addBuildNumberToResults(QString zippedFolderName); + void automaticTestRunEvaluationComplete(const QString& zippedFolderName, int numberOfFailures); + void addBuildNumberToResults(const QString& zippedFolderName); void copyFolder(const QString& source, const QString& destination);