mirror of
https://github.com/overte-org/overte.git
synced 2025-04-23 11:13:29 +02:00
merge with master
This commit is contained in:
commit
25b31cfc72
215 changed files with 3979 additions and 9328 deletions
android/apps/interface/src/main/java/io/highfidelity/hifiinterface
cmake/templates
interface
CMakeLists.txt
resources
images
qml
+android_interface
+webengine
Browser.qmlBrowserWebView.qmlInfoView.qmlQmlWebWindow.qmlQmlWebWindowView.qmlStats.qmlWeb3DSurface.qmlcontrolsUit
hifi
src
Application.cppApplication.hInterfaceParentFinder.cppLoginStateManager.cppLoginStateManager.h
avatar
devices
raypick
CollisionPick.cppCollisionPick.hLaserPointer.cppLaserPointer.hLaserPointerScriptingInterface.hParabolaPick.cppParabolaPick.hParabolaPointer.cppParabolaPointer.hPathPointer.cppPathPointer.hPickScriptingInterface.cppPickScriptingInterface.hPointerScriptingInterface.cppPointerScriptingInterface.hRayPick.cppRayPick.hRayPickScriptingInterface.hStylusPick.cppStylusPick.hStylusPointer.cppStylusPointer.h
scripting
HMDScriptingInterface.hKeyboardScriptingInterface.cppKeyboardScriptingInterface.hSelectionScriptingInterface.cppSelectionScriptingInterface.h
ui
ApplicationOverlay.cppDialogsManager.cppKeyboard.cppKeyboard.hLoginDialog.cppLoginDialog.hStats.cppStats.h
overlays
Base3DOverlay.cppBase3DOverlay.hBillboard3DOverlay.cppBillboard3DOverlay.hBillboardable.cppBillboardable.hCircle3DOverlay.cppCircle3DOverlay.hContextOverlayInterface.cppContextOverlayInterface.hCube3DOverlay.cppCube3DOverlay.hGrid3DOverlay.cppGrid3DOverlay.hImage3DOverlay.cppImage3DOverlay.hImageOverlay.cppLine3DOverlay.cppLine3DOverlay.hModelOverlay.cppModelOverlay.hOverlay.cppOverlay.hOverlay2D.hOverlayTransformNode.cppOverlayTransformNode.hOverlays.cppOverlays.hOverlaysPayload.cpp
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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@
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
BIN
interface/resources/images/whitePixel.png
Normal file
BIN
interface/resources/images/whitePixel.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 155 B |
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// Web3DOverlay.qml
|
||||
// Web3DSurface.qml
|
||||
//
|
||||
// Created by Gabriel Calero & Cristian Duarte on Jun 22, 2018
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
|
|
|
@ -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
|
58
interface/resources/qml/+webengine/BrowserWebView.qml
Normal file
58
interface/resources/qml/+webengine/BrowserWebView.qml
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
53
interface/resources/qml/+webengine/QmlWebWindowView.qml
Normal file
53
interface/resources/qml/+webengine/QmlWebWindowView.qml
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
8
interface/resources/qml/BrowserWebView.qml
Normal file
8
interface/resources/qml/BrowserWebView.qml
Normal file
|
@ -0,0 +1,8 @@
|
|||
import QtQuick 2.5
|
||||
import controlsUit 1.0
|
||||
|
||||
ProxyWebView {
|
||||
property var parentRoot: null
|
||||
|
||||
function grantFeaturePermission(origin, feature) {}
|
||||
}
|
|
@ -24,7 +24,7 @@ Windows.ScrollingWindow {
|
|||
width: pane.contentWidth
|
||||
implicitHeight: pane.scrollHeight
|
||||
|
||||
ProxyWebView {
|
||||
BaseWebView {
|
||||
id: webview
|
||||
objectName: "WebView"
|
||||
anchors.fill: parent
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
5
interface/resources/qml/QmlWebWindowView.qml
Normal file
5
interface/resources/qml/QmlWebWindowView.qml
Normal file
|
@ -0,0 +1,5 @@
|
|||
import QtQuick 2.5
|
||||
import controlsUit 1.0
|
||||
|
||||
BaseWebView {
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// Web3DOverlay.qml
|
||||
// Web3DSurface.qml
|
||||
//
|
||||
// Created by David Rowe on 16 Dec 2016.
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
|
|
|
@ -18,6 +18,7 @@ Rectangle {
|
|||
|
||||
property bool safeLoading: false
|
||||
property bool loadingLatched: false
|
||||
property bool loading: false
|
||||
property var loadingRequest: null
|
||||
|
||||
|
||||
|
|
|
@ -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 = {};
|
||||
|
|
|
@ -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"}
|
||||
}
|
|
@ -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"}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -152,6 +152,7 @@
|
|||
#include <trackers/EyeTracker.h>
|
||||
#include <avatars-renderer/ScriptAvatar.h>
|
||||
#include <RenderableEntityItem.h>
|
||||
#include <RenderableTextEntityItem.h>
|
||||
#include <RenderableWebEntityItem.h>
|
||||
#include <model-networking/MaterialCache.h>
|
||||
#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<EntityItemID>(uuid));
|
||||
case NestableType::Overlay:
|
||||
return getOverlayModelProvider(static_cast<OverlayID>(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<Base3DOverlay>(overlay)) {
|
||||
provider = std::dynamic_pointer_cast<scriptable::ModelProvider>(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<AvatarManager>();
|
||||
|
@ -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<Cube3DOverlay> _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>();
|
||||
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<EntityScriptingInterface>()->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<EntityScriptingInterface>();
|
||||
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<PointerManager>();
|
||||
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<Keyboard>();
|
||||
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<EntityScriptingInterface>();
|
||||
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<QObject*>(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<render::entities::TextEntityRenderer>(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<PickManager>()->setPrecisionPicking(rayPickID, value);
|
||||
});
|
||||
|
||||
EntityTreeRenderer::setGetAvatarUpOperator([] {
|
||||
return DependencyManager::get<AvatarManager>()->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<AvatarManager>()->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<OffscreenQmlSurface>& 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<EntityScriptingInterface>()->setEntityTree(qApp->getEntities()->getTree());
|
||||
DependencyManager::get<TabletScriptingInterface>()->preloadSounds();
|
||||
DependencyManager::get<Keyboard>()->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<EntityScriptingInterface>()->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<OffscreenQmlSurfaceCache>();
|
||||
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<Web3DOverlay>(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<float, QUuid> entityResult;
|
||||
if (!_controllerScriptingInterface->areEntityClicksCaptured()) {
|
||||
getEntities()->mousePressEvent(&mappedEvent);
|
||||
entityResult = getEntities()->mousePressEvent(&mappedEvent);
|
||||
}
|
||||
std::pair<float, QUuid> 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<Web3DOverlay>(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<EntityScriptingInterface>()->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<Cube3DOverlay>();
|
||||
_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<EntityScriptingInterface>();
|
||||
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<EntityScriptingInterface>();
|
||||
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<EntityScriptingInterface>();
|
||||
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<Web3DOverlay>(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>();
|
||||
grabManager->simulateGrabs();
|
||||
|
||||
// TODO: break these out into distinct perfTimers when they prove interesting
|
||||
{
|
||||
PROFILE_RANGE(app, "PickManager");
|
||||
PerformanceTimer perfTimer("pickManager");
|
||||
DependencyManager::get<PickManager>()->update();
|
||||
}
|
||||
|
||||
{
|
||||
PROFILE_RANGE(app, "PointerManager");
|
||||
PerformanceTimer perfTimer("pointerManager");
|
||||
DependencyManager::get<PointerManager>()->update();
|
||||
}
|
||||
|
||||
QSharedPointer<AvatarManager> avatarManager = DependencyManager::get<AvatarManager>();
|
||||
|
||||
{
|
||||
|
@ -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<PickManager>()->update();
|
||||
}
|
||||
|
||||
{
|
||||
PROFILE_RANGE(app, "PointerManager");
|
||||
PerformanceTimer perfTimer("pointerManager");
|
||||
DependencyManager::get<PointerManager>()->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<ResourceRequestObserver>().data());
|
||||
|
||||
qScriptRegisterMetaType(scriptEngine.data(), OverlayIDtoScriptValue, OverlayIDfromScriptValue);
|
||||
|
||||
registerInteractiveWindowMetaType(scriptEngine.data());
|
||||
|
||||
auto pickScriptingInterface = DependencyManager::get<PickScriptingInterface>();
|
||||
|
@ -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<Web3DOverlay>(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<EntityScriptingInterface>();
|
||||
_loginDialogID = entityScriptingInterface->addEntityInternal(properties, entity::HostType::LOCAL);
|
||||
|
||||
auto keyboard = DependencyManager::get<Keyboard>().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<Web3DOverlay>(overlays.getOverlay(_loginDialogOverlayID));
|
||||
auto overlayPositionVec = loginOverlay->getWorldPosition();
|
||||
const auto OFFSET = glm::vec2(0.7f, -0.1f);
|
||||
|
||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||
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<Keyboard>().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<Keyboard>()->getAnchorID(), properties);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8930,10 +8904,9 @@ void Application::onDismissedLoginDialog() {
|
|||
loginDialogPoppedUp.set(false);
|
||||
auto keyboard = DependencyManager::get<Keyboard>().data();
|
||||
keyboard->setResetKeyboardPositionOnRaise(true);
|
||||
if (!_loginDialogOverlayID.isNull()) {
|
||||
// deleting overlay.
|
||||
getOverlays().deleteOverlay(_loginDialogOverlayID);
|
||||
_loginDialogOverlayID = OverlayID();
|
||||
if (!_loginDialogID.isNull()) {
|
||||
DependencyManager::get<EntityScriptingInterface>()->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<HMDScriptingInterface>();
|
||||
return HMD->getCurrentTabletScreenID();
|
||||
}
|
||||
|
||||
OverlayID Application::getTabletHomeButtonID() const {
|
||||
QUuid Application::getTabletHomeButtonID() const {
|
||||
auto HMD = DependencyManager::get<HMDScriptingInterface>();
|
||||
return HMD->getCurrentHomeButtonID();
|
||||
}
|
||||
|
@ -9115,7 +9088,7 @@ QUuid Application::getTabletFrameID() const {
|
|||
}
|
||||
|
||||
QVector<QUuid> Application::getTabletIDs() const {
|
||||
// Most important overlays first.
|
||||
// Most important first.
|
||||
QVector<QUuid> result;
|
||||
auto HMD = DependencyManager::get<HMDScriptingInterface>();
|
||||
result << HMD->getCurrentTabletScreenID();
|
||||
|
|
|
@ -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<QUuid> getTabletIDs() const; // In order of most important IDs first.
|
||||
QUuid getTabletScreenID() const;
|
||||
QUuid getTabletHomeButtonID() const;
|
||||
QUuid getTabletFrameID() const;
|
||||
QVector<QUuid> 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<AvatarHashMap>()->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<EntityItemID> _keyboardFocusedEntity;
|
||||
ThreadSafeValueCache<OverlayID> _keyboardFocusedOverlay;
|
||||
quint64 _lastAcceptedKeyPress = 0;
|
||||
bool _isForeground = true; // starts out assumed to be in foreground
|
||||
bool _isGLInitialized { false };
|
||||
|
|
|
@ -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<SpatiallyNestable>(overlay); // this will return nullptr for non-3d overlays
|
||||
if (!parent.expired()) {
|
||||
success = true;
|
||||
return parent;
|
||||
}
|
||||
|
||||
success = false;
|
||||
return parent;
|
||||
}
|
||||
|
|
|
@ -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<QVariant>({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()) {
|
||||
|
|
|
@ -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); }
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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<EntityTreeRenderer>();
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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. <em>Read-only.</em>
|
||||
* @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.
|
||||
* <em>Read-only.</em>
|
||||
* @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<int> _pinnedJoints;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -55,44 +55,47 @@ OtherAvatar::~OtherAvatar() {
|
|||
|
||||
void OtherAvatar::removeOrb() {
|
||||
if (!_otherAvatarOrbMeshPlaceholderID.isNull()) {
|
||||
qApp->getOverlays().deleteOverlay(_otherAvatarOrbMeshPlaceholderID);
|
||||
_otherAvatarOrbMeshPlaceholderID = UNKNOWN_OVERLAY_ID;
|
||||
DependencyManager::get<EntityScriptingInterface>()->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<EntityScriptingInterface>()->editEntity(_otherAvatarOrbMeshPlaceholderID, properties);
|
||||
}
|
||||
}
|
||||
|
||||
void OtherAvatar::createOrb() {
|
||||
if (_otherAvatarOrbMeshPlaceholderID.isNull()) {
|
||||
_otherAvatarOrbMeshPlaceholder = std::make_shared<Sphere3DOverlay>();
|
||||
_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<EntityScriptingInterface>()->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<EntityScriptingInterface>()->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();
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
#include <workload/Space.h>
|
||||
|
||||
#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<QUuid, AvatarEntityDataHash>;
|
||||
MapOfAvatarEntityDataHashes _avatarEntityDataHashes;
|
||||
|
||||
std::vector<QUuid> _attachedAvatarEntities;
|
||||
std::shared_ptr<Sphere3DOverlay> _otherAvatarOrbMeshPlaceholder { nullptr };
|
||||
OverlayID _otherAvatarOrbMeshPlaceholderID { UNKNOWN_OVERLAY_ID };
|
||||
QUuid _otherAvatarOrbMeshPlaceholderID;
|
||||
AvatarMotionState* _motionState { nullptr };
|
||||
std::vector<DetailedMotionState*> _detailedMotionStates;
|
||||
int32_t _spaceIndex { -1 };
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -169,7 +169,7 @@ private:
|
|||
int _calibrationCount;
|
||||
QVector<float> _calibrationValues;
|
||||
TextOverlay* _calibrationBillboard;
|
||||
OverlayID _calibrationBillboardID;
|
||||
QUuid _calibrationBillboardID;
|
||||
QString _calibrationMessage;
|
||||
bool _isCalibrated;
|
||||
void addCalibrationDatum();
|
||||
|
|
|
@ -409,10 +409,6 @@ PickResultPointer CollisionPick::getEntityIntersection(const CollisionRegion& pi
|
|||
return std::make_shared<CollisionPickResult>(pick, entityIntersections, std::vector<ContactTestResult>());
|
||||
}
|
||||
|
||||
PickResultPointer CollisionPick::getOverlayIntersection(const CollisionRegion& pick) {
|
||||
return std::make_shared<CollisionPickResult>(pick, std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
|
||||
}
|
||||
|
||||
PickResultPointer CollisionPick::getAvatarIntersection(const CollisionRegion& pick) {
|
||||
if (!pick.loaded) {
|
||||
// Cannot compute result
|
||||
|
|
|
@ -54,7 +54,6 @@ public:
|
|||
return std::make_shared<CollisionPickResult>(pickVariant, std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
|
||||
}
|
||||
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;
|
||||
|
|
|
@ -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<RenderState>(_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<EntityScriptingInterface>();
|
||||
{
|
||||
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<EntityScriptingInterface>()->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<EntityScriptingInterface>()->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<glm::vec3> points;
|
||||
points.append(glm::vec3(0.0f));
|
||||
points.append(end - origin);
|
||||
properties.setPosition(origin);
|
||||
properties.setLinePoints(points);
|
||||
properties.setVisible(true);
|
||||
properties.setIgnorePickIntersection(doesPathIgnorePicks());
|
||||
QVector<float> widths;
|
||||
widths.append(getLineWidth() * parentScale);
|
||||
DependencyManager::get<EntityScriptingInterface>()->editEntity(getPathID(), properties);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<StartEndRenderState> 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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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<PointerManager>()->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<PointerManager>()->setLockEndUUID(uid, objectID, isAvatar, offsetMat); }
|
||||
|
||||
|
||||
/**jsdoc
|
||||
|
|
|
@ -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<EntityScriptingInterface>()->evalParabolaIntersectionVector(pick, searchFilter,
|
||||
getIncludeItemsAs<EntityItemID>(), getIgnoreItemsAs<EntityItemID>());
|
||||
if (entityRes.intersects) {
|
||||
return std::make_shared<ParabolaPickResult>(IntersectionType::ENTITY, entityRes.entityID, entityRes.distance, entityRes.parabolicDistance, entityRes.intersection, pick, entityRes.surfaceNormal, entityRes.extraInfo);
|
||||
}
|
||||
}
|
||||
return std::make_shared<ParabolaPickResult>(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<PickManager>()->getForceCoarsePicking());
|
||||
ParabolaToOverlayIntersectionResult overlayRes =
|
||||
qApp->getOverlays().findParabolaIntersectionVector(pick, precisionPicking,
|
||||
getIncludeItemsAs<OverlayID>(), getIgnoreItemsAs<OverlayID>(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable());
|
||||
if (overlayRes.intersects) {
|
||||
return std::make_shared<ParabolaPickResult>(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<EntityScriptingInterface>()->getEntityProperties(entityRes.entityID, desiredProperties).getEntityHostType() == entity::HostType::LOCAL) {
|
||||
type = IntersectionType::LOCAL_ENTITY;
|
||||
}
|
||||
}
|
||||
return std::make_shared<ParabolaPickResult>(type, entityRes.entityID, entityRes.distance, entityRes.parabolicDistance, entityRes.intersection, pick, entityRes.surfaceNormal, entityRes.extraInfo);
|
||||
}
|
||||
}
|
||||
return std::make_shared<ParabolaPickResult>(pick.toVariantMap());
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include <Pick.h>
|
||||
|
||||
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<ParabolaPickResult>(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;
|
||||
|
|
|
@ -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<StartEndRenderState> 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);
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
#include "PathPointer.h"
|
||||
|
||||
#include <render/Item.h>
|
||||
|
||||
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; }
|
||||
|
|
|
@ -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<EntityScriptingInterface>()->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<EntityScriptingInterface>()->getEntityTransform(id)[3];
|
||||
} else if (type == IntersectionType::OVERLAY) {
|
||||
endVec = vec3FromVariant(qApp->getOverlays().getProperty(id, "position").value);
|
||||
} else if (type == IntersectionType::AVATAR) {
|
||||
endVec = DependencyManager::get<AvatarHashMap>()->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<EntityScriptingInterface>();
|
||||
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<EntityScriptingInterface>();
|
||||
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<EntityScriptingInterface>();
|
||||
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<EntityScriptingInterface>();
|
||||
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<PickManager>()->calculatePos2DFromHUD(origin);
|
||||
default:
|
||||
|
|
|
@ -12,27 +12,25 @@
|
|||
#include <QString>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "ui/overlays/Overlay.h"
|
||||
|
||||
#include <Pointer.h>
|
||||
#include <Pick.h>
|
||||
|
||||
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;
|
||||
|
|
|
@ -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 <ScriptEngine.h>
|
||||
|
@ -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<PickQuery> pick,
|
|||
NestableType nestableType = sharedNestablePointer->getNestableType();
|
||||
if (nestableType == NestableType::Avatar) {
|
||||
pick->parentTransform = std::make_shared<AvatarTransformNode>(std::static_pointer_cast<Avatar>(sharedNestablePointer), parentJointIndex);
|
||||
} else if (nestableType == NestableType::Overlay) {
|
||||
pick->parentTransform = std::make_shared<OverlayTransformNode>(std::static_pointer_cast<Base3DOverlay>(sharedNestablePointer), parentJointIndex);
|
||||
} else if (nestableType == NestableType::Entity) {
|
||||
pick->parentTransform = std::make_shared<EntityTransformNode>(std::static_pointer_cast<EntityItem>(sharedNestablePointer), parentJointIndex);
|
||||
} else {
|
||||
|
|
|
@ -47,7 +47,8 @@
|
|||
*
|
||||
* @property {number} INTERSECTED_NONE An intersection type. Intersected nothing with the given filter flags. <em>Read-only.</em>
|
||||
* @property {number} INTERSECTED_ENTITY An intersection type. Intersected an entity. <em>Read-only.</em>
|
||||
* @property {number} INTERSECTED_OVERLAY An intersection type. Intersected an overlay. <em>Read-only.</em>
|
||||
* @property {number} INTERSECTED_LOCAL_ENTITY An intersection type. Intersected a local entity.</em>
|
||||
* @property {number} INTERSECTED_OVERLAY An intersection type. Intersected an entity (3D Overlays no longer exist). <em>Read-only.</em>
|
||||
* @property {number} INTERSECTED_AVATAR An intersection type. Intersected an avatar. <em>Read-only.</em>
|
||||
* @property {number} INTERSECTED_HUD An intersection type. Intersected the HUD sphere. <em>Read-only.</em>
|
||||
* @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 <b>only</b> 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
|
||||
|
|
|
@ -99,7 +99,7 @@ unsigned int PointerScriptingInterface::createStylus(const QVariant& properties)
|
|||
}
|
||||
}
|
||||
|
||||
return DependencyManager::get<PointerManager>()->addPointer(std::make_shared<StylusPointer>(properties, StylusPointer::buildStylusOverlay(propertyMap), hover, enabled, modelPositionOffset,
|
||||
return DependencyManager::get<PointerManager>()->addPointer(std::make_shared<StylusPointer>(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 <code>type</code> 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 <code>type</code> field), which <b>must</b> be <code>"line3d"</code>.
|
||||
* 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 <code>type</code> 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 <code>type</code> 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 <code>type</code> 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.
|
||||
|
|
|
@ -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 <code>button</code>.
|
||||
* @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<PointerManager>()->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 <b>only</b> 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<PointerManager>()->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<PointerManager>()->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;
|
||||
};
|
||||
|
|
|
@ -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<EntityScriptingInterface>()->evalRayIntersectionVector(pick, searchFilter,
|
||||
getIncludeItemsAs<EntityItemID>(), getIgnoreItemsAs<EntityItemID>());
|
||||
if (entityRes.intersects) {
|
||||
return std::make_shared<RayPickResult>(IntersectionType::ENTITY, entityRes.entityID, entityRes.distance, entityRes.intersection, pick, entityRes.surfaceNormal, entityRes.extraInfo);
|
||||
} else {
|
||||
return std::make_shared<RayPickResult>(pick.toVariantMap());
|
||||
}
|
||||
}
|
||||
|
||||
PickResultPointer RayPick::getOverlayIntersection(const PickRay& pick) {
|
||||
bool precisionPicking = !(getFilter().isCoarse() || DependencyManager::get<PickManager>()->getForceCoarsePicking());
|
||||
RayToOverlayIntersectionResult overlayRes =
|
||||
qApp->getOverlays().findRayIntersectionVector(pick, precisionPicking,
|
||||
getIncludeItemsAs<OverlayID>(), getIgnoreItemsAs<OverlayID>(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable());
|
||||
if (overlayRes.intersects) {
|
||||
return std::make_shared<RayPickResult>(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<EntityScriptingInterface>()->getEntityProperties(entityRes.entityID, desiredProperties).getEntityHostType() == entity::HostType::LOCAL) {
|
||||
type = IntersectionType::LOCAL_ENTITY;
|
||||
}
|
||||
}
|
||||
return std::make_shared<RayPickResult>(type, entityRes.entityID, entityRes.distance, entityRes.intersection, pick, entityRes.surfaceNormal, entityRes.extraInfo);
|
||||
} else {
|
||||
return std::make_shared<RayPickResult>(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<EntityScriptingInterface>()->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<EntityScriptingInterface>()->getEntityProperties(entityID);
|
||||
EntityPropertyFlags desiredProperties;
|
||||
desiredProperties += PROP_POSITION;
|
||||
desiredProperties += PROP_ROTATION;
|
||||
desiredProperties += PROP_DIMENSIONS;
|
||||
desiredProperties += PROP_REGISTRATION_POINT;
|
||||
auto props = DependencyManager::get<EntityScriptingInterface>()->getEntityProperties(entityID, desiredProperties);
|
||||
return projectOntoXYPlane(worldPos, props.getPosition(), props.getRotation(), props.getDimensions(), props.getRegistrationPoint(), unNormalized);
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include <Pick.h>
|
||||
|
||||
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<RayPickResult>(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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -11,8 +11,6 @@
|
|||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "ui/overlays/Base3DOverlay.h"
|
||||
|
||||
#include "Application.h"
|
||||
#include <DependencyManager.h>
|
||||
#include "avatar/AvatarManager.h"
|
||||
|
@ -20,6 +18,8 @@
|
|||
#include <controllers/StandardControls.h>
|
||||
#include <controllers/UserInputMapper.h>
|
||||
|
||||
#include <EntityTreeElement.h>
|
||||
|
||||
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<StylusPickResult>(nearestTarget);
|
||||
}
|
||||
|
||||
PickResultPointer StylusPick::getOverlayIntersection(const StylusTip& pick) {
|
||||
std::vector<StylusPickResult> 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<Base3DOverlay>(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<EntityScriptingInterface>()->getEntityProperties(target, desiredProperties).getEntityHostType() == entity::HostType::LOCAL) {
|
||||
type = IntersectionType::LOCAL_ENTITY;
|
||||
}
|
||||
}
|
||||
results.push_back(StylusPickResult(type, target, distance, intersection, pick, normal));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<PickScriptingInterface>()->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<EntityScriptingInterface>()->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<AvatarManager>()->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<EntityScriptingInterface>()->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<EntityScriptingInterface>()->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<PickManager>()->calculatePos2DFromHUD(origin);
|
||||
default:
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
#include <shared/Bilateral.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
|
||||
#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<StylusPointer>;
|
||||
|
||||
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);
|
||||
|
|
|
@ -54,12 +54,12 @@ class QScriptEngine;
|
|||
* @property {boolean} tabletContextualMode - <code>true</code> if the tablet has been opened in contextual mode, otherwise
|
||||
* <code>false</code>. 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. <em>Read-only.</em>
|
||||
* @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. <code>null</code> if not in HMD mode.
|
||||
* @property {Uuid} miniTabletScreenID - The UUID of the mini tablet's screen overlay. <code>null</code> 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. <code>null</code> if not in HMD mode.
|
||||
* @property {Uuid} miniTabletScreenID - The UUID of the mini tablet's screen entity. <code>null</code> if not in HMD mode.
|
||||
* @property {number} miniTabletHand - The hand that the mini tablet is displayed on: <code>0</code> for left hand,
|
||||
* <code>1</code> for right hand, <code>-1</code> if not in HMD mode.
|
||||
* @property {bool} miniTabletEnabled=true - <code>true</code> if the mini tablet is enabled to be displayed, otherwise
|
||||
|
|
|
@ -64,6 +64,6 @@ bool KeyboardScriptingInterface::getPreferMalletsOverLasers() const {
|
|||
return DependencyManager::get<Keyboard>()->getPreferMalletsOverLasers();
|
||||
}
|
||||
|
||||
bool KeyboardScriptingInterface::containsID(OverlayID overlayID) const {
|
||||
return DependencyManager::get<Keyboard>()->containsID(overlayID);
|
||||
bool KeyboardScriptingInterface::containsID(const QUuid& id) const {
|
||||
return DependencyManager::get<Keyboard>()->containsID(id);
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
#include <QtCore/QUuid>
|
||||
|
||||
#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;
|
||||
|
|
|
@ -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() {
|
|||
* <tbody>
|
||||
* <tr><td><code>"avatar"</code></td><td></td></tr>
|
||||
* <tr><td><code>"entity"</code></td><td></td></tr>
|
||||
* <tr><td><code>"overlay"</code></td><td></td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @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<QVariant> avatarIDs;
|
||||
QList<QVariant> entityIDs;
|
||||
QList<QVariant> 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<EntityTreeRenderer>();
|
||||
auto& overlays = qApp->getOverlays();
|
||||
|
||||
for (QUuid& currentAvatarID : thisList.getAvatarIDs()) {
|
||||
auto avatar = std::static_pointer_cast<Avatar>(DependencyManager::get<AvatarManager>()->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);
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include <AbstractViewStateInterface.h>
|
||||
|
||||
#include "RenderableEntityItem.h"
|
||||
#include "ui/overlays/Overlay.h"
|
||||
#include <avatar/AvatarManager.h>
|
||||
#include <render/HighlightStyle.h>
|
||||
|
||||
|
@ -37,15 +36,10 @@ public:
|
|||
bool addToGameplayObjects(const EntityItemID& entityID);
|
||||
bool removeFromGameplayObjects(const EntityItemID& entityID);
|
||||
|
||||
std::vector<OverlayID> getOverlayIDs() const { return _overlayIDs; }
|
||||
bool addToGameplayObjects(const OverlayID& overlayID);
|
||||
bool removeFromGameplayObjects(const OverlayID& overlayID);
|
||||
|
||||
private:
|
||||
bool containsData { false };
|
||||
std::vector<QUuid> _avatarIDs;
|
||||
std::vector<EntityItemID> _entityIDs;
|
||||
std::vector<OverlayID> _overlayIDs;
|
||||
};
|
||||
|
||||
|
||||
|
@ -83,7 +77,7 @@ protected:
|
|||
};
|
||||
|
||||
/**jsdoc
|
||||
* The <code>Selection</code> API provides a means of grouping together avatars, entities, and overlays in named lists.
|
||||
* The <code>Selection</code> 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 <em>debug log</em> (not the script log).
|
||||
* Print out the list of avatars and entities in a selection to the <em>debug log</em> (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
|
||||
|
|
|
@ -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<GeometryCache>();
|
||||
|
@ -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()) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -33,11 +33,6 @@
|
|||
#include <RegisteredMetaTypes.h>
|
||||
#include <ui/TabletScriptingInterface.h>
|
||||
|
||||
#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<glm::vec3, glm::quat> calculateKeyboardPositionAndOrientation() {
|
|||
float sensorToWorldScale = myAvatar->getSensorToWorldScale();
|
||||
QUuid tabletID = hmd->getCurrentTabletFrameID();
|
||||
if (!tabletID.isNull() && hmd->getShouldShowTablet()) {
|
||||
Overlays& overlays = qApp->getOverlays();
|
||||
auto tabletOverlay = std::dynamic_pointer_cast<Base3DOverlay>(overlays.getOverlay(tabletID));
|
||||
if (tabletOverlay) {
|
||||
auto tablet = DependencyManager::get<TabletScriptingInterface>()->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<EntityScriptingInterface>()->getEntityProperties(tabletID, desiredProperties);
|
||||
|
||||
keyboardLocation.first = tabletWorldPosition + (tabletWorldOrientation * scaledKeyboardTabletOffset);
|
||||
keyboardLocation.second = tabletWorldOrientation * glm::quat(glm::radians(keyboardDegreesOffset));
|
||||
}
|
||||
auto tablet = DependencyManager::get<TabletScriptingInterface>()->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<glm::vec3, glm::quat> calculateKeyboardPositionAndOrientation() {
|
|||
}
|
||||
|
||||
void Key::saveDimensionsAndLocalPosition() {
|
||||
Overlays& overlays = qApp->getOverlays();
|
||||
auto model3DOverlay = std::dynamic_pointer_cast<ModelOverlay>(overlays.getOverlay(_keyID));
|
||||
EntityPropertyFlags desiredProperties;
|
||||
desiredProperties += PROP_LOCAL_POSITION;
|
||||
desiredProperties += PROP_DIMENSIONS;
|
||||
auto properties = DependencyManager::get<EntityScriptingInterface>()->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<ModelOverlay>(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<EntityScriptingInterface>()->editEntity(_keyID, properties);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -241,6 +232,18 @@ void Keyboard::registerKeyboardHighlighting() {
|
|||
selection->enableListToScene(KEY_PRESSED_HIGHLIGHT);
|
||||
}
|
||||
|
||||
void Keyboard::disableSelectionLists() {
|
||||
auto selection = DependencyManager::get<SelectionScriptingInterface>();
|
||||
selection->disableListHighlight(KEY_HOVER_HIGHLIGHT);
|
||||
selection->disableListHighlight(KEY_PRESSED_HIGHLIGHT);
|
||||
}
|
||||
|
||||
void Keyboard::enableSelectionLists() {
|
||||
auto selection = DependencyManager::get<SelectionScriptingInterface>();
|
||||
selection->enableListHighlight(KEY_HOVER_HIGHLIGHT, KEY_HOVERING_STYLE);
|
||||
selection->enableListHighlight(KEY_PRESSED_HIGHLIGHT, KEY_PRESSING_STYLE);
|
||||
}
|
||||
|
||||
bool Keyboard::getUse3DKeyboard() const {
|
||||
return _use3DKeyboardLock.resultWithReadLock<bool>([&] {
|
||||
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<StylusPointer>(leftStylusProperties, StylusPointer::buildStylusOverlay(leftStylusProperties), true, true,
|
||||
_leftHandStylus = pointerManager->addPointer(std::make_shared<StylusPointer>(leftStylusProperties, StylusPointer::buildStylus(leftStylusProperties), true, true,
|
||||
MALLET_POSITION_OFFSET, MALLET_ROTATION_OFFSET, MALLET_MODEL_DIMENSIONS));
|
||||
_rightHandStylus = pointerManager->addPointer(std::make_shared<StylusPointer>(rightStylusProperties, StylusPointer::buildStylusOverlay(rightStylusProperties), true, true,
|
||||
_rightHandStylus = pointerManager->addPointer(std::make_shared<StylusPointer>(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<AvatarManager>()->getMyAvatar();
|
||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||
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<Cube3DOverlay>(overlays.getOverlay(anchorOverlayID));
|
||||
if (anchorOverlay) {
|
||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||
|
||||
EntityItemProperties properties;
|
||||
properties.setVisible(raise);
|
||||
|
||||
entityScriptingInterface->editEntity(_textDisplay.entityID, properties);
|
||||
entityScriptingInterface->editEntity(_backPlate.entityID, properties);
|
||||
|
||||
if (_resetKeyboardPositionOnRaise) {
|
||||
std::pair<glm::vec3, glm::quat> 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<Cube3DOverlay>(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<EntityScriptingInterface>();
|
||||
|
||||
glm::vec3 scaledDimensions = _anchor.originalDimensions * sensorToWorldScale;
|
||||
auto volume3DOverlay = std::dynamic_pointer_cast<Volume3DOverlay>(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<Base3DOverlay>(overlays.getOverlay(iter.key()));
|
||||
if (base3DOverlay) {
|
||||
base3DOverlay->setVisible(raise);
|
||||
}
|
||||
DependencyManager::get<EntityScriptingInterface>()->editEntity(iter.key(), properties);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -465,19 +440,15 @@ bool Keyboard::getPreferMalletsOverLasers() const {
|
|||
}
|
||||
|
||||
void Keyboard::switchToLayer(int layerIndex) {
|
||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||
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<Cube3DOverlay>(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<Cube3DOverlay>(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>();
|
||||
userInputMapper->triggerHapticPulse(PULSE_STRENGTH, PULSE_DURATION, handIndex);
|
||||
|
||||
Overlays& overlays = qApp->getOverlays();
|
||||
auto base3DOverlay = std::dynamic_pointer_cast<Base3DOverlay>(overlays.getOverlay(overlayID));
|
||||
|
||||
glm::vec3 keyWorldPosition;
|
||||
if (base3DOverlay) {
|
||||
keyWorldPosition = base3DOverlay->getWorldPosition();
|
||||
}
|
||||
EntityPropertyFlags desiredProperties;
|
||||
desiredProperties += PROP_POSITION;
|
||||
glm::vec3 keyWorldPosition = DependencyManager::get<EntityScriptingInterface>()->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<SelectionScriptingInterface>();
|
||||
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<Base3DOverlay>(overlays.getOverlay(overlayID));
|
||||
Key& key = search.value();
|
||||
|
||||
if (base3DOverlay) {
|
||||
base3DOverlay->setLocalPosition(key.getCurrentLocalPosition());
|
||||
}
|
||||
EntityItemProperties properties;
|
||||
properties.setLocalPosition(key.getCurrentLocalPosition());
|
||||
DependencyManager::get<EntityScriptingInterface>()->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<SelectionScriptingInterface>();
|
||||
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<Base3DOverlay>(overlays.getOverlay(overlayID));
|
||||
unsigned int pointerID = event.getID();
|
||||
auto pointerManager = DependencyManager::get<PointerManager>();
|
||||
auto pickResult = pointerManager->getPrevPickResult(pointerID);
|
||||
auto stylusPickResult = std::dynamic_pointer_cast<StylusPickResult>(pickResult);
|
||||
float distance = stylusPickResult->distance;
|
||||
|
||||
if (base3DOverlay) {
|
||||
unsigned int pointerID = event.getID();
|
||||
auto pointerManager = DependencyManager::get<PointerManager>();
|
||||
auto pickResult = pointerManager->getPrevPickResult(pointerID);
|
||||
auto stylusPickResult = std::dynamic_pointer_cast<StylusPickResult>(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<EntityScriptingInterface>();
|
||||
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<SelectionScriptingInterface>();
|
||||
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<SelectionScriptingInterface>();
|
||||
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<QUuid> 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<EntityScriptingInterface>();
|
||||
{
|
||||
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<OverlayID, Key> keyboardLayerKeys;
|
||||
QHash<QUuid, Key> 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<PointerManager>();
|
||||
|
@ -922,34 +895,33 @@ void Keyboard::loadKeyboardFile(const QString& keyboardFile) {
|
|||
}
|
||||
|
||||
|
||||
OverlayID Keyboard::getAnchorID() {
|
||||
return _ignoreItemsLock.resultWithReadLock<OverlayID>([&] {
|
||||
return _anchor.overlayID;
|
||||
QUuid Keyboard::getAnchorID() {
|
||||
return _ignoreItemsLock.resultWithReadLock<QUuid>([&] {
|
||||
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<OverlayID> Keyboard::getKeysID() {
|
||||
return _ignoreItemsLock.resultWithReadLock<QVector<OverlayID>>([&] {
|
||||
QSet<QUuid> Keyboard::getKeyIDs() {
|
||||
return _ignoreItemsLock.resultWithReadLock<QSet<QUuid>>([&] {
|
||||
return _itemsToIgnore;
|
||||
});
|
||||
}
|
||||
|
||||
void Keyboard::clearKeyboardKeys() {
|
||||
Overlays& overlays = qApp->getOverlays();
|
||||
|
||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||
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<bool>([&] {
|
||||
return _itemsToIgnore.contains(overlayID) || _backPlate.overlayID == overlayID;
|
||||
return _itemsToIgnore.contains(id) || _backPlate.entityID == id;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -25,8 +25,6 @@
|
|||
#include <shared/ReadWriteLockable.h>
|
||||
#include <SettingHandle.h>
|
||||
|
||||
#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<QTimer> _timer { std::make_shared<QTimer>() };
|
||||
};
|
||||
|
@ -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<OverlayID> getKeysID();
|
||||
OverlayID getAnchorID();
|
||||
QSet<QUuid> 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<OverlayID> _itemsToIgnore;
|
||||
std::vector<QHash<OverlayID, Key>> _keyboardLayers;
|
||||
QSet<QUuid> _itemsToIgnore;
|
||||
std::vector<QHash<QUuid, Key>> _keyboardLayers;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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<AccountManager>();
|
||||
|
@ -71,7 +71,7 @@ void LoginDialog::showWithSelection() {
|
|||
if (!qApp->getLoginDialogPoppedUp()) {
|
||||
tablet->initialScreen(TABLET_LOGIN_DIALOG_URL);
|
||||
} else {
|
||||
qApp->createLoginDialogOverlay();
|
||||
qApp->createLoginDialog();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
class QNetworkReply;
|
||||
|
||||
extern const QUrl OVERLAY_LOGIN_DIALOG;
|
||||
extern const QUrl LOGIN_DIALOG;
|
||||
|
||||
class LoginDialog : public OffscreenQmlDialog {
|
||||
Q_OBJECT
|
||||
|
|
|
@ -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<QVector4D> updatedPicks = pickManager->getUpdatedPickCounts();
|
||||
std::vector<QVector3D> updatedPicks = pickManager->getUpdatedPickCounts();
|
||||
STAT_UPDATE(stylusPicksUpdated, updatedPicks[PickQuery::Stylus]);
|
||||
STAT_UPDATE(rayPicksUpdated, updatedPicks[PickQuery::Ray]);
|
||||
STAT_UPDATE(parabolaPicksUpdated, updatedPicks[PickQuery::Parabola]);
|
||||
|
|
|
@ -172,10 +172,10 @@ private: \
|
|||
* @property {number} rayPicksCount - <em>Read-only.</em>
|
||||
* @property {number} parabolaPicksCount - <em>Read-only.</em>
|
||||
* @property {number} collisionPicksCount - <em>Read-only.</em>
|
||||
* @property {Vec4} stylusPicksUpdated - <em>Read-only.</em>
|
||||
* @property {Vec4} rayPicksUpdated - <em>Read-only.</em>
|
||||
* @property {Vec4} parabolaPicksUpdated - <em>Read-only.</em>
|
||||
* @property {Vec4} collisionPicksUpdated - <em>Read-only.</em>
|
||||
* @property {Vec3} stylusPicksUpdated - <em>Read-only.</em>
|
||||
* @property {Vec3} rayPicksUpdated - <em>Read-only.</em>
|
||||
* @property {Vec3} parabolaPicksUpdated - <em>Read-only.</em>
|
||||
* @property {Vec3} collisionPicksUpdated - <em>Read-only.</em>
|
||||
*/
|
||||
// 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();
|
||||
|
|
|
@ -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 <RegisteredMetaTypes.h>
|
||||
#include <SharedUtil.h>
|
||||
#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: <code>p1</code>, <code>point</code>, and
|
||||
* <code>start</code>.
|
||||
* @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>position</code>.
|
||||
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
|
||||
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
|
||||
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>, and <code>filled</code>.
|
||||
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>. Deprecated.
|
||||
* @property {boolean} ignorePickIntersection=false - If <code>true</code>, picks ignore the overlay. <code>ignoreRayIntersection</code> is a synonym.
|
||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||
* have <code>drawInFront</code> set to <code>true</code>, 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 <code>true</code>, 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
|
||||
* <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> means "no joint".
|
||||
*/
|
||||
QVariant Base3DOverlay::getProperty(const QString& property) {
|
||||
if (property == "name") {
|
||||
return _nameLock.resultWithReadLock<QString>([&] {
|
||||
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<Overlay>(itemID, [latestTransform, latestVisible](Overlay& data) {
|
||||
auto overlay3D = dynamic_cast<Base3DOverlay*>(&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<QString>([&] {
|
||||
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();
|
||||
}
|
|
@ -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 <Transform.h>
|
||||
#include <SpatiallyNestable.h>
|
||||
#include <graphics-scripting/Forward.h>
|
||||
#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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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 <Application.h>
|
||||
#include <Transform.h>
|
||||
#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 <code>true</code>, 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<AvatarManager>()->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;
|
||||
}
|
|
@ -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 <glm/gtc/quaternion.hpp>
|
||||
#include <QVariant>
|
||||
|
||||
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
|
|
@ -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 <GeometryUtil.h>
|
||||
#include <GeometryCache.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
|
||||
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<GeometryCache>();
|
||||
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<GeometryCache>();
|
||||
|
||||
Q_ASSERT(args->_batch);
|
||||
auto& batch = *args->_batch;
|
||||
|
||||
DependencyManager::get<GeometryCache>()->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<glm::vec2> points;
|
||||
QVector<glm::vec4> 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<glm::vec2> 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<glm::vec2> majorPoints;
|
||||
QVector<glm::vec2> 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<typename T> T fromVariant(const QVariant& v, bool& valid) {
|
||||
valid = v.isValid();
|
||||
return qvariant_cast<T>(v);
|
||||
}
|
||||
|
||||
template<> glm::u8vec3 fromVariant(const QVariant& v, bool& valid) {
|
||||
return u8vec3FromVariant(v, valid);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool updateIfValid(const QVariantMap& properties, const char* key, T& output) {
|
||||
bool valid;
|
||||
T result = fromVariant<T>(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<typename T>
|
||||
bool updateIfValid(const QVariantMap& properties, const char* key, std::initializer_list<std::reference_wrapper<T>> outputs) {
|
||||
bool valid;
|
||||
T value = fromVariant<T>(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<typename T>
|
||||
bool updateIfValid(const QVariantMap& properties, const std::initializer_list<const char*> keys, T& output) {
|
||||
for (const char* key : keys) {
|
||||
if (updateIfValid<T>(properties, key, output)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void Circle3DOverlay::setProperties(const QVariantMap& properties) {
|
||||
Planar3DOverlay::setProperties(properties);
|
||||
_dirty |= updateIfValid<float>(properties, "alpha", { _innerStartAlpha, _innerEndAlpha, _outerStartAlpha, _outerEndAlpha });
|
||||
_dirty |= updateIfValid<float>(properties, "Alpha", { _innerStartAlpha, _innerEndAlpha, _outerStartAlpha, _outerEndAlpha });
|
||||
_dirty |= updateIfValid<float>(properties, "startAlpha", { _innerStartAlpha, _outerStartAlpha });
|
||||
_dirty |= updateIfValid<float>(properties, "endAlpha", { _innerEndAlpha, _outerEndAlpha });
|
||||
_dirty |= updateIfValid<float>(properties, "innerAlpha", { _innerStartAlpha, _innerEndAlpha });
|
||||
_dirty |= updateIfValid<float>(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<glm::u8vec3>(properties, "color", { _innerStartColor, _innerEndColor, _outerStartColor, _outerEndColor });
|
||||
_dirty |= updateIfValid<glm::u8vec3>(properties, "startColor", { _innerStartColor, _outerStartColor } );
|
||||
_dirty |= updateIfValid<glm::u8vec3>(properties, "endColor", { _innerEndColor, _outerEndColor } );
|
||||
_dirty |= updateIfValid<glm::u8vec3>(properties, "innerColor", { _innerStartColor, _innerEndColor } );
|
||||
_dirty |= updateIfValid<glm::u8vec3>(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 <code>circle3d</code> {@link Overlays.OverlayType|OverlayType}.
|
||||
* @typedef {object} Overlays.Circle3DProperties
|
||||
*
|
||||
* @property {string} type=circle3d - Has the value <code>"circle3d"</code>. <em>Read-only.</em>
|
||||
* @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
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> 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 <code>true</code>, 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: <code>p1</code>, <code>point</code>, and
|
||||
* <code>start</code>.
|
||||
* @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>position</code>.
|
||||
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
|
||||
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
|
||||
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>, and <code>filled</code>
|
||||
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>. Deprecated.
|
||||
* @property {boolean} ignorePickIntersection=false - If <code>true</code>, picks ignore the overlay. <code>ignoreRayIntersection</code> is a synonym.
|
||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||
* have <code>drawInFront</code> set to <code>true</code>, 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
|
||||
* <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> means "no joint".
|
||||
*
|
||||
* @property {Vec2} dimensions=1,1 - The dimensions of the overlay. Synonyms: <code>scale</code>, <code>size</code>.
|
||||
* <em>Not used.</em>
|
||||
*
|
||||
* @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: <code>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
|
||||
* <code>innerStartColor</code>, <code>innerEndColor</code>, <code>outerStartColor</code>, and <code>outerEndColor</code>.
|
||||
* @property {Color} startColor - Sets the values of <code>innerStartColor</code> and <code>outerStartColor</code>.
|
||||
* <em>Write-only.</em>
|
||||
* @property {Color} endColor - Sets the values of <code>innerEndColor</code> and <code>outerEndColor</code>.
|
||||
* <em>Write-only.</em>
|
||||
* @property {Color} innerColor - Sets the values of <code>innerStartColor</code> and <code>innerEndColor</code>.
|
||||
* <em>Write-only.</em>
|
||||
* @property {Color} outerColor - Sets the values of <code>outerStartColor</code> and <code>outerEndColor</code>.
|
||||
* <em>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, <code>0.0</code> - <code>1.0</code>. Setting this value also sets
|
||||
* the values of <code>innerStartAlpha</code>, <code>innerEndAlpha</code>, <code>outerStartAlpha</code>, and
|
||||
* <code>outerEndAlpha</code>. Synonym: <code>Alpha</code>; <em>write-only</em>.
|
||||
* @property {number} startAlpha - Sets the values of <code>innerStartAlpha</code> and <code>outerStartAlpha</code>.
|
||||
* <em>Write-only.</em>
|
||||
* @property {number} endAlpha - Sets the values of <code>innerEndAlpha</code> and <code>outerEndAlpha</code>.
|
||||
* <em>Write-only.</em>
|
||||
* @property {number} innerAlpha - Sets the values of <code>innerStartAlpha</code> and <code>innerEndAlpha</code>.
|
||||
* <em>Write-only.</em>
|
||||
* @property {number} outerAlpha - Sets the values of <code>outerStartAlpha</code> and <code>outerEndAlpha</code>.
|
||||
* <em>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 <code>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.
|
||||
*/
|
||||
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);
|
||||
}
|
|
@ -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
|
|
@ -60,11 +60,12 @@ ContextOverlayInterface::ContextOverlayInterface() {
|
|||
QUuid tabletFrameID = _hmdScriptingInterface->getCurrentTabletFrameID();
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->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<EntityScriptingInterface>()->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<Image3DOverlay>();
|
||||
_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<EntityScriptingInterface>();
|
||||
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<EntityScriptingInterface>()->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<EntityScriptingInterface>()->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<EntityScriptingInterface>()->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<PointerManager>()->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<PointerManager>()->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) {
|
||||
|
|
|
@ -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> _hmdScriptingInterface;
|
||||
QSharedPointer<TabletScriptingInterface> _tabletScriptingInterface;
|
||||
QSharedPointer<SelectionScriptingInterface> _selectionScriptingInterface;
|
||||
OverlayID _contextOverlayID { UNKNOWN_OVERLAY_ID };
|
||||
std::shared_ptr<Image3DOverlay> _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 };
|
||||
|
||||
|
|
|
@ -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 <SharedUtil.h>
|
||||
#include <StreamUtils.h>
|
||||
#include <GeometryCache.h>
|
||||
#include <DependencyManager.h>
|
||||
|
||||
QString const Cube3DOverlay::TYPE = "cube";
|
||||
|
||||
Cube3DOverlay::Cube3DOverlay() {
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
for (size_t i = 0; i < _geometryIds.size(); ++i) {
|
||||
_geometryIds[i] = geometryCache->allocateID();
|
||||
}
|
||||
}
|
||||
|
||||
Cube3DOverlay::Cube3DOverlay(const Cube3DOverlay* cube3DOverlay) :
|
||||
Volume3DOverlay(cube3DOverlay)
|
||||
{
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
for (size_t i = 0; i < _geometryIds.size(); ++i) {
|
||||
_geometryIds[i] = geometryCache->allocateID();
|
||||
}
|
||||
}
|
||||
|
||||
Cube3DOverlay::~Cube3DOverlay() {
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
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<GeometryCache>();
|
||||
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 <code>cube</code> {@link Overlays.OverlayType|OverlayType}.
|
||||
* @typedef {object} Overlays.CubeProperties
|
||||
*
|
||||
* @property {string} type=cube - Has the value <code>"cube"</code>. <em>Read-only.</em>
|
||||
* @property {Color} color=255,255,255 - The color of the overlay.
|
||||
* @property {number} alpha=0.7 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>.
|
||||
* @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
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> 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 <code>true</code>, 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: <code>p1</code>, <code>point</code>, and
|
||||
* <code>start</code>.
|
||||
* @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>position</code>.
|
||||
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
|
||||
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
|
||||
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>, and <code>filled</code>.
|
||||
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>. Deprecated.
|
||||
* @property {boolean} ignorePickIntersection=false - If <code>true</code>, picks ignore the overlay. <code>ignoreRayIntersection</code> is a synonym.
|
||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||
* have <code>drawInFront</code> set to <code>true</code>, 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
|
||||
* <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> means "no joint".
|
||||
*
|
||||
* @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: <code>scale</code>, <code>size</code>.
|
||||
*/
|
||||
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<GeometryCache>();
|
||||
auto vertexColor = ColorUtils::toVec3(_color);
|
||||
scriptable::ScriptableModelBase result;
|
||||
if (auto mesh = geometryCache->meshFromShape(GeometryCache::Cube, vertexColor)) {
|
||||
result.objectID = getID();
|
||||
result.append(mesh);
|
||||
}
|
||||
return result;
|
||||
}
|
|
@ -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<int, 12> _geometryIds;
|
||||
};
|
||||
|
||||
|
||||
#endif // hifi_Cube3DOverlay_h
|
|
@ -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 <OctreeConstants.h>
|
||||
#include <DependencyManager.h>
|
||||
#include <GeometryCache.h>
|
||||
#include <PathUtils.h>
|
||||
#include <ViewFrustum.h>
|
||||
|
||||
|
||||
QString const Grid3DOverlay::TYPE = "grid";
|
||||
const float DEFAULT_SCALE = 100.0f;
|
||||
|
||||
Grid3DOverlay::Grid3DOverlay() {
|
||||
setDimensions(DEFAULT_SCALE);
|
||||
updateGrid();
|
||||
_geometryId = DependencyManager::get<GeometryCache>()->allocateID();
|
||||
}
|
||||
|
||||
Grid3DOverlay::Grid3DOverlay(const Grid3DOverlay* grid3DOverlay) :
|
||||
Planar3DOverlay(grid3DOverlay),
|
||||
_majorGridEvery(grid3DOverlay->_majorGridEvery),
|
||||
_minorGridEvery(grid3DOverlay->_minorGridEvery)
|
||||
{
|
||||
updateGrid();
|
||||
_geometryId = DependencyManager::get<GeometryCache>()->allocateID();
|
||||
}
|
||||
|
||||
Grid3DOverlay::~Grid3DOverlay() {
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
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<GeometryCache>()->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 <code>grid</code> {@link Overlays.OverlayType|OverlayType}.
|
||||
* @typedef {object} Overlays.GridProperties
|
||||
*
|
||||
* @property {string} type=grid - Has the value <code>"grid"</code>. <em>Read-only.</em>
|
||||
* @property {Color} color=255,255,255 - The color of the overlay.
|
||||
* @property {number} alpha=0.7 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>.
|
||||
* @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
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> 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 <code>true</code>, 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: <code>p1</code>, <code>point</code>, and
|
||||
* <code>start</code>.
|
||||
* @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>position</code>.
|
||||
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
|
||||
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
|
||||
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>, and <code>filled</code>.
|
||||
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>. Deprecated.
|
||||
* @property {boolean} ignorePickIntersection=false - If <code>true</code>, picks ignore the overlay. <code>ignoreRayIntersection</code> is a synonym.
|
||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||
* have <code>drawInFront</code> set to <code>true</code>, 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
|
||||
* <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> means "no joint".
|
||||
*
|
||||
* @property {Vec2} dimensions=1,1 - The dimensions of the overlay. Synonyms: <code>scale</code>, <code>size</code>.
|
||||
*
|
||||
* @property {boolean} followCamera=true - If <code>true</code>, the grid is always visible even as the camera moves to another
|
||||
* position.
|
||||
* @property {number} majorGridEvery=5 - Integer number of <code>minorGridEvery</code> intervals at which to draw a thick grid
|
||||
* line. Minimum value = <code>1</code>.
|
||||
* @property {number} minorGridEvery=1 - Real number of meters at which to draw thin grid lines. Minimum value =
|
||||
* <code>0.001</code>.
|
||||
*/
|
||||
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;
|
||||
}
|
|
@ -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
|
|
@ -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 <DependencyManager.h>
|
||||
#include <GeometryCache.h>
|
||||
#include <gpu/Batch.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
|
||||
#include "GeometryUtil.h"
|
||||
|
||||
#include "AbstractViewStateInterface.h"
|
||||
|
||||
QString const Image3DOverlay::TYPE = "image3d";
|
||||
|
||||
Image3DOverlay::Image3DOverlay() {
|
||||
_isLoaded = false;
|
||||
_geometryId = DependencyManager::get<GeometryCache>()->allocateID();
|
||||
}
|
||||
|
||||
Image3DOverlay::Image3DOverlay(const Image3DOverlay* image3DOverlay) :
|
||||
Billboard3DOverlay(image3DOverlay),
|
||||
_url(image3DOverlay->_url),
|
||||
_texture(image3DOverlay->_texture),
|
||||
_emissive(image3DOverlay->_emissive),
|
||||
_fromImage(image3DOverlay->_fromImage)
|
||||
{
|
||||
_geometryId = DependencyManager::get<GeometryCache>()->allocateID();
|
||||
}
|
||||
|
||||
Image3DOverlay::~Image3DOverlay() {
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
if (geometryCache) {
|
||||
geometryCache->releaseID(_geometryId);
|
||||
}
|
||||
}
|
||||
|
||||
void Image3DOverlay::update(float deltatime) {
|
||||
if (!_isLoaded) {
|
||||
_isLoaded = true;
|
||||
_texture = DependencyManager::get<TextureCache>()->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<GeometryCache>()->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 <code>image3d</code> {@link Overlays.OverlayType|OverlayType}.
|
||||
* @typedef {object} Overlays.Image3DProperties
|
||||
*
|
||||
* @property {string} type=image3d - Has the value <code>"image3d"</code>. <em>Read-only.</em>
|
||||
* @property {Color} color=255,255,255 - The color of the overlay.
|
||||
* @property {number} alpha=0.7 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>.
|
||||
* @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
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> 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 <code>true</code>, 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: <code>p1</code>, <code>point</code>, and
|
||||
* <code>start</code>.
|
||||
* @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>position</code>.
|
||||
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
|
||||
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
|
||||
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>, and <code>filled</code>.
|
||||
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>. Deprecated.
|
||||
* @property {boolean} ignorePickIntersection=false - If <code>true</code>, picks ignore the overlay. <code>ignoreRayIntersection</code> is a synonym.
|
||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||
* have <code>drawInFront</code> set to <code>true</code>, 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
|
||||
* <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> means "no joint".
|
||||
*
|
||||
* @property {Vec2} dimensions=1,1 - The dimensions of the overlay. Synonyms: <code>scale</code>, <code>size</code>.
|
||||
*
|
||||
* @property {bool} keepAspectRatio=true - overlays will maintain the aspect ratio when the subImage is applied.
|
||||
*
|
||||
* @property {boolean} isFacingAvatar - If <code>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} 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 <code>true</code>, 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;
|
||||
}
|
|
@ -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 <TextureCache.h>
|
||||
|
||||
#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
|
|
@ -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 <code>image</code> {@link Overlays.OverlayType|OverlayType}.
|
||||
* @typedef {object} Overlays.ImageProperties
|
||||
*
|
||||
* @property {Rect} bounds - The position and size of the image display area, in pixels. <em>Write-only.</em>
|
||||
* @property {number} x - Integer left, x-coordinate value of the image display area = <code>bounds.x</code>.
|
||||
* <em>Write-only.</em>
|
||||
* @property {number} y - Integer top, y-coordinate value of the image display area = <code>bounds.y</code>.
|
||||
* <em>Write-only.</em>
|
||||
* @property {number} width - Integer width of the image display area = <code>bounds.width</code>. <em>Write-only.</em>
|
||||
* @property {number} height - Integer height of the image display area = <code>bounds.height</code>. <em>Write-only.</em>
|
||||
* @property {string} imageURL - The URL of the image file to display. The image is scaled to fit to the <code>bounds</code>.
|
||||
* <em>Write-only.</em>
|
||||
* @property {Vec2} subImage=0,0 - Integer coordinates of the top left pixel to start using image content from.
|
||||
* <em>Write-only.</em>
|
||||
* @property {Color} color=0,0,0 - The color to apply over the top of the image to colorize it. <em>Write-only.</em>
|
||||
* @property {number} alpha=0.0 - The opacity of the color applied over the top of the image, <code>0.0</code> -
|
||||
* <code>1.0</code>. <em>Write-only.</em>
|
||||
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
||||
* <em>Write-only.</em>
|
||||
*/
|
||||
|
||||
ImageOverlay::ImageOverlay()
|
||||
: QmlOverlay(URL) { }
|
||||
|
||||
|
@ -50,5 +27,4 @@ ImageOverlay::ImageOverlay(const ImageOverlay* imageOverlay) :
|
|||
|
||||
ImageOverlay* ImageOverlay::createClone() const {
|
||||
return new ImageOverlay(this);
|
||||
}
|
||||
|
||||
}
|
|
@ -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 <GeometryCache.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
|
||||
#include "AbstractViewStateInterface.h"
|
||||
|
||||
QString const Line3DOverlay::TYPE = "line3d";
|
||||
|
||||
Line3DOverlay::Line3DOverlay() :
|
||||
_geometryCacheID(DependencyManager::get<GeometryCache>()->allocateID())
|
||||
{
|
||||
}
|
||||
|
||||
Line3DOverlay::Line3DOverlay(const Line3DOverlay* line3DOverlay) :
|
||||
Base3DOverlay(line3DOverlay),
|
||||
_geometryCacheID(DependencyManager::get<GeometryCache>()->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<GeometryCache>();
|
||||
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<GeometryCache>();
|
||||
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 <code>line3d</code> {@link Overlays.OverlayType|OverlayType}.
|
||||
* @typedef {object} Overlays.Line3DProperties
|
||||
*
|
||||
* @property {string} type=line3d - Has the value <code>"line3d"</code>. <em>Read-only.</em>
|
||||
* @property {Color} color=255,255,255 - The color of the overlay.
|
||||
* @property {number} alpha=0.7 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>.
|
||||
* @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
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> 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 <code>true</code>, 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: <code>p1</code>, <code>point</code>, and
|
||||
* <code>start</code>.
|
||||
* @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>position</code>.
|
||||
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
|
||||
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
|
||||
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>, and <code>filled</code>.
|
||||
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>. Deprecated.
|
||||
* @property {boolean} ignorePickIntersection=false - If <code>true</code>, picks ignore the overlay. <code>ignoreRayIntersection</code> is a synonym.
|
||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||
* have <code>drawInFront</code> set to <code>true</code>, 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
|
||||
* <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> 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 <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> means "no joint".
|
||||
* @property {Vec3} start - The start point of the line. Synonyms: <code>startPoint</code>, <code>p1</code>, and
|
||||
* <code>position</code>.
|
||||
* @property {Vec3} end - The end point of the line. Synonyms: <code>endPoint</code> and <code>p2</code>.
|
||||
* @property {Vec3} localStart - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>start</code>. Synonym: <code>localPosition</code>.
|
||||
* @property {Vec3} localEnd - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>endParentID</code> set, otherwise the same value as <code>end</code>.
|
||||
* @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 <code>glow > 0</code>, the line is rendered with a glow.
|
||||
* @property {number} lineWidth=0.02 - If <code>glow > 0</code>, 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;
|
||||
}
|
|
@ -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
|
|
@ -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 <glm/gtx/quaternion.hpp>
|
||||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
#include <Rig.h>
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
|
||||
QString const ModelOverlay::TYPE = "model";
|
||||
|
||||
ModelOverlay::ModelOverlay()
|
||||
: _model(std::make_shared<Model>(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<Model>(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<scriptable::ModelProviderFactory>()->modelRemovedFromScene(getID(), NestableType::Overlay, _model);
|
||||
_model->removeFromScene(scene, transaction);
|
||||
_model->addToScene(scene, transaction);
|
||||
|
||||
auto newRenderItemIDs{ _model->fetchRenderItemIDs() };
|
||||
transaction.updateItem<Overlay>(getRenderItemID(), [newRenderItemIDs](Overlay& data) {
|
||||
auto modelOverlay = static_cast<ModelOverlay*>(&data);
|
||||
modelOverlay->setSubRenderItemIDs(newRenderItemIDs);
|
||||
});
|
||||
processMaterials();
|
||||
emit DependencyManager::get<scriptable::ModelProviderFactory>()->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<Overlay>(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<scriptable::ModelProviderFactory>()->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<scriptable::ModelProviderFactory>()->modelRemovedFromScene(getID(), NestableType::Overlay, _model);
|
||||
transaction.updateItem<Overlay>(getRenderItemID(), [](Overlay& data) {
|
||||
auto modelOverlay = static_cast<ModelOverlay*>(&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<QString>()) {
|
||||
_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 <typename vectorType, typename itemType>
|
||||
vectorType ModelOverlay::mapJoints(mapFunction<itemType> 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 <code>model</code> {@link Overlays.OverlayType|OverlayType}.
|
||||
* @typedef {object} Overlays.ModelProperties
|
||||
*
|
||||
* @property {string} type=sphere - Has the value <code>"model"</code>. <em>Read-only.</em>
|
||||
* @property {Color} color=255,255,255 - The color of the overlay.
|
||||
* @property {number} alpha=0.7 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>.
|
||||
* @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
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> 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 <code>true</code>, 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: <code>p1</code>, <code>point</code>, and
|
||||
* <code>start</code>.
|
||||
* @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>position</code>.
|
||||
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
|
||||
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
|
||||
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>, and <code>filled</code>.
|
||||
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>. Deprecated.
|
||||
* @property {boolean} ignorePickIntersection=false - If <code>true</code>, picks ignore the overlay. <code>ignoreRayIntersection</code> is a synonym.
|
||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||
* @property {boolean} isGroupCulled=false - If <code>true</code>, the mesh parts of the model are LOD culled as a group.
|
||||
* If <code>false</code>, 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
|
||||
* <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> 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: <code>size</code>.
|
||||
* @property {Vec3} scale - The scale factor applied to the model's dimensions.
|
||||
* @property {object.<name, url>} textures - Maps the named textures in the model to the JPG or PNG images in the urls.
|
||||
* @property {Array.<string>} jointNames - The names of the joints - if any - in the model. <em>Read-only.</em>
|
||||
* @property {Array.<Quat>} jointRotations - The relative rotations of the model's joints. <em>Not copied if overlay is
|
||||
* cloned.</em>
|
||||
* @property {Array.<Vec3>} jointTranslations - The relative translations of the model's joints. <em>Not copied if overlay is
|
||||
* cloned.</em>
|
||||
* @property {Array.<Quat>} jointOrientations - The absolute orientations of the model's joints, in world coordinates.
|
||||
* <em>Read-only.</em>
|
||||
* @property {Array.<Vec3>} jointPositions - The absolute positions of the model's joints, in world coordinates.
|
||||
* <em>Read-only.</em>
|
||||
* @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<QStringList, QString>([rig](int jointIndex) -> QString {
|
||||
return rig->nameOfJoint(jointIndex);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// relative
|
||||
if (property == "jointRotations") {
|
||||
return mapJoints<QVariantList, QVariant>(
|
||||
[this](int jointIndex) -> QVariant {
|
||||
glm::quat rotation;
|
||||
_model->getJointRotation(jointIndex, rotation);
|
||||
return quatToVariant(rotation);
|
||||
});
|
||||
}
|
||||
|
||||
// relative
|
||||
if (property == "jointTranslations") {
|
||||
return mapJoints<QVariantList, QVariant>(
|
||||
[this](int jointIndex) -> QVariant {
|
||||
glm::vec3 translation;
|
||||
_model->getJointTranslation(jointIndex, translation);
|
||||
return vec3toVariant(translation);
|
||||
});
|
||||
}
|
||||
|
||||
// absolute
|
||||
if (property == "jointOrientations") {
|
||||
return mapJoints<QVariantList, QVariant>(
|
||||
[this](int jointIndex) -> QVariant {
|
||||
glm::quat orientation;
|
||||
_model->getJointRotationInWorldFrame(jointIndex, orientation);
|
||||
return quatToVariant(orientation);
|
||||
});
|
||||
}
|
||||
|
||||
// absolute
|
||||
if (property == "jointPositions") {
|
||||
return mapJoints<QVariantList, QVariant>(
|
||||
[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<JointData> jointsData;
|
||||
|
||||
const QVector<HFMAnimationFrame>& 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<glm::quat>& rotations = frames[_lastKnownCurrentFrame].rotations;
|
||||
const QVector<glm::vec3>& 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<AnimationCache>()->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<JointData> 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<std::mutex> 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<std::mutex> 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();
|
||||
}
|
|
@ -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 <Model.h>
|
||||
#include <AnimationCache.h>
|
||||
|
||||
#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 <typename itemType> using mapFunction = std::function<itemType(int jointIndex)>;
|
||||
template <typename vectorType, typename itemType>
|
||||
vectorType mapJoints(mapFunction<itemType> function) const;
|
||||
|
||||
void animate();
|
||||
void mapAnimationJoints(const QStringList& modelJointNames);
|
||||
bool isAnimatingSomething() const {
|
||||
return !_animationURL.isEmpty() && _animationRunning && _animationFPS != 0.0f;
|
||||
}
|
||||
void copyAnimationJointDataToModel(QVector<JointData> 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<int> _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
|
|
@ -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 <code>"TODO"</code>. <em>Read-only.</em>
|
||||
* @property {Color} color=255,255,255 - The color of the overlay.
|
||||
* @property {number} alpha=0.7 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>.
|
||||
* @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
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> 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 <code>true</code>, 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::Payload>(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<OverlayID> qVectorOverlayIDFromScriptValue(const QScriptValue& array) {
|
||||
if (!array.isArray()) {
|
||||
return QVector<OverlayID>();
|
||||
}
|
||||
QVector<OverlayID> 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<std::mutex> lock(_materialsLock);
|
||||
_materials[parentMaterialName].push(material);
|
||||
}
|
||||
|
||||
void Overlay::removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) {
|
||||
std::lock_guard<std::mutex> 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();
|
||||
|
|
|
@ -13,13 +13,6 @@
|
|||
|
||||
#include <render/Scene.h>
|
||||
|
||||
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<std::string, graphics::MultiMaterial> _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<OverlayID>);
|
||||
QScriptValue OverlayIDtoScriptValue(QScriptEngine* engine, const OverlayID& id);
|
||||
void OverlayIDfromScriptValue(const QScriptValue& object, OverlayID& id);
|
||||
QVector<OverlayID> qVectorOverlayIDFromScriptValue(const QScriptValue& array);
|
||||
|
||||
#endif // hifi_Overlay_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); }
|
||||
|
|
|
@ -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<Base3DOverlay>::getActualScale(const std::shared_ptr<Base3DOverlay>& nestablePointer) const {
|
||||
return nestablePointer->getBounds().getScale();
|
||||
}
|
|
@ -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<Base3DOverlay> {
|
||||
public:
|
||||
OverlayTransformNode(std::weak_ptr<Base3DOverlay> spatiallyNestable, int jointIndex) : BaseNestableTransformNode(spatiallyNestable, jointIndex) {};
|
||||
};
|
||||
|
||||
#endif // hifi_OverlayTransformNode_h
|
File diff suppressed because it is too large
Load diff
|
@ -26,29 +26,16 @@
|
|||
|
||||
#include "Overlay.h"
|
||||
|
||||
#include "PanelAttachable.h"
|
||||
#include <EntityScriptingInterface.h>
|
||||
|
||||
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 - <code>true</code> if the {@link PickRay} intersected with a 3D overlay, otherwise
|
||||
* <code>false</code>.
|
||||
* @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 <code>null</code>; set to <code>null</code> 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 <code>null</code>; set to <code>null</code> 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<OverlayID>& overlaysToInclude,
|
||||
const QVector<OverlayID>& overlaysToDiscard,
|
||||
const QVector<EntityItemID>& include,
|
||||
const QVector<EntityItemID>& discard,
|
||||
bool visibleOnly = false, bool collidableOnly = false);
|
||||
|
||||
ParabolaToOverlayIntersectionResult findParabolaIntersectionVector(const PickParabola& parabola, bool precisionPicking,
|
||||
const QVector<OverlayID>& overlaysToInclude,
|
||||
const QVector<OverlayID>& overlaysToDiscard,
|
||||
const QVector<EntityItemID>& include,
|
||||
const QVector<EntityItemID>& discard,
|
||||
bool visibleOnly = false, bool collidableOnly = false);
|
||||
|
||||
bool mousePressEvent(QMouseEvent* event);
|
||||
std::pair<float, QUuid> 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 <caption>Add an overlay in front of your avatar, clone it, and move the clone to be above the
|
||||
* original.</caption>
|
||||
* 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} <code>true</code> if the overlay was found and edited, otherwise <code>false</code>.
|
||||
* @example <caption>Add an overlay in front of your avatar then change its color.</caption>
|
||||
|
@ -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 <caption>Create an overlay in front of your avatar then delete it.</caption>
|
||||
* 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 <caption>Create an overlay in front of your avatar then get and report its type.</caption>
|
||||
* @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 <caption>Create an object in front of your avatar then get and report its type.</caption>
|
||||
* 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 <code>web3d</code>
|
||||
|
@ -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. <em>Must be for a 3D {@link Overlays.OverlayType|OverlayType}.</em>
|
||||
* @param {Uuid} id - The ID of the overlay. <em>Must be for a 3D {@link Overlays.OverlayType|OverlayType}.</em>
|
||||
* @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
|
||||
* <code>undefined</code>.
|
||||
|
@ -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.<string>} 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
|
||||
* <code>undefined</code>.
|
||||
|
@ -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 <code>drawInFront</code> 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] - <em>Unused</em>; exists to match Entity API.
|
||||
* @param {Array.<Uuid>} [overlayIDsToInclude=[]] - If not empty then the search is restricted to these overlays.
|
||||
* @param {Array.<Uuid>} [overlayIDsToExclude=[]] - Overlays to ignore during the search.
|
||||
* @param {boolean} [visibleOnly=false] - <em>Unused</em>; exists to match Entity API.
|
||||
* @param {boolean} [collidableOnly=false] - <em>Unused</em>; exists to match Entity API.
|
||||
* @param {Array.<Uuid>} [include=[]] - If not empty then the search is restricted to these overlays.
|
||||
* @param {Array.<Uuid>} [discard=[]] - Overlays to ignore during the search.
|
||||
* @param {boolean} [visibleOnly=false] - If <code>true</code> then only entities that are
|
||||
* <code>{@link Entities.EntityProperties|visible}<code> are searched.
|
||||
* @param {boolean} [collideableOnly=false] - If <code>true</code> then only entities that are not
|
||||
* <code>{@link Entities.EntityProperties|collisionless}</code> are searched.
|
||||
* @returns {Overlays.RayToOverlayIntersectionResult} The closest 3D overlay intersected by <code>pickRay</code>, taking
|
||||
* into account <code>overlayIDsToInclude</code> and <code>overlayIDsToExclude</code> if they're not empty.
|
||||
* @example <caption>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 <caption>Create two cube overlays in front of your avatar then search for overlays near your avatar.</caption>
|
||||
* @returns {Uuid[]} An array of entity IDs with bounding boxes that touch a search sphere.
|
||||
* @example <caption>Create two cube entities in front of your avatar then search for entities near your avatar.</caption>
|
||||
* 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 <code>image</code> 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} <code>true</code> if the overlay's assets have been loaded, otherwise <code>false</code>.
|
||||
* @example <caption>Create an image overlay and report whether its image is loaded after 1s.</caption>
|
||||
* 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 <code>text</code> if the overlay is a text overlay, otherwise
|
||||
* <code>{ height: 0, width : 0 }</code>. 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 <caption>Calculate the size of "hello" in a 3D text overlay.</caption>
|
||||
* @returns {Size} The size of the <code>text</code> if the object is a text entity or overlay, otherwise
|
||||
* <code>{ height: 0, width : 0 }</code>. If the object is a 2D overlay, the size is in pixels; if the object is an entity,
|
||||
* the size is in meters.
|
||||
* @example <caption>Calculate the size of "hello" in a 3D text entity.</caption>
|
||||
* 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} <code>true</code> if an overlay with the given ID exists, <code>false</code> otherwise.
|
||||
* @param {Uuid} id - The ID to check.
|
||||
* @returns {boolean} <code>true</code> if an object with the given ID exists, <code>false</code> 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 <caption>Create a 2D rectangle overlay plus a 3D cube overlay and generate mousePressOnOverlay events for the 2D
|
||||
* overlay.</caption>
|
||||
|
@ -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
|
||||
* <code>null</code>.
|
||||
*/
|
||||
OverlayID getKeyboardFocusOverlay();
|
||||
QUuid getKeyboardFocusOverlay() { return DependencyManager::get<EntityScriptingInterface>()->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
|
||||
* <code>null</code> or {@link Uuid|Uuid.NULL} to unset keyboard focus from an overlay.
|
||||
*/
|
||||
void setKeyboardFocusOverlay(const OverlayID& id);
|
||||
void setKeyboardFocusOverlay(const QUuid& id) { DependencyManager::get<EntityScriptingInterface>()->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 <caption>Create an overlay then delete it after 1s.</caption>
|
||||
* 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 <caption>Create a cube overlay in front of your avatar and report mouse clicks on it.</caption>
|
||||
|
@ -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 <caption>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<OverlayID, Overlay::Pointer> _overlaysHUD;
|
||||
QMap<OverlayID, Overlay::Pointer> _overlaysWorld;
|
||||
|
||||
QMap<QUuid, Overlay::Pointer> _overlays;
|
||||
QList<Overlay::Pointer> _overlaysToDelete;
|
||||
|
||||
unsigned int _stackOrder { 1 };
|
||||
|
||||
bool _enabled = true;
|
||||
std::atomic<bool> _shuttingDown{ false };
|
||||
bool _enabled { true };
|
||||
std::atomic<bool> _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<QString, QString> _entityToOverlayTypes;
|
||||
static std::unordered_map<QString, QString> _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
|
||||
|
|
|
@ -8,27 +8,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <limits>
|
||||
#include <typeinfo>
|
||||
|
||||
#include <avatar/AvatarManager.h>
|
||||
#include <avatar/MyAvatar.h>
|
||||
#include <LODManager.h>
|
||||
#include <render/Scene.h>
|
||||
|
||||
#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) {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue