mirror of
https://github.com/overte-org/overte.git
synced 2025-08-06 11:17:32 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into dk/encryptFile
This commit is contained in:
commit
0bd3f1728d
105 changed files with 2912 additions and 1387 deletions
|
@ -3,7 +3,7 @@ This is a stand-alone guide for creating your first High Fidelity build for Wind
|
||||||
## Building High Fidelity
|
## Building High Fidelity
|
||||||
Note: We are now using Visual Studio 2017 and Qt 5.9.1. If you are upgrading from Visual Studio 2013 and Qt 5.6.2, do a clean uninstall of those versions before going through this guide.
|
Note: We are now using Visual Studio 2017 and Qt 5.9.1. If you are upgrading from Visual Studio 2013 and Qt 5.6.2, do a clean uninstall of those versions before going through this guide.
|
||||||
|
|
||||||
Note: The prerequisites will require about 10 GB of space on your drive.
|
Note: The prerequisites will require about 10 GB of space on your drive. You will also need a system with at least 8GB of main memory.
|
||||||
|
|
||||||
### Step 1. Visual Studio 2017
|
### Step 1. Visual Studio 2017
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ Item {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height : parent.height
|
height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height : parent.height
|
||||||
|
|
||||||
profile: HFTabletWebEngineProfile;
|
profile: HFWebEngineProfile;
|
||||||
|
|
||||||
property string userScriptUrl: ""
|
property string userScriptUrl: ""
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ Item {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height : parent.height
|
height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height : parent.height
|
||||||
|
|
||||||
profile: HFTabletWebEngineProfile;
|
profile: HFWebEngineProfile;
|
||||||
|
|
||||||
property string userScriptUrl: ""
|
property string userScriptUrl: ""
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ Item {
|
||||||
property alias webView: webview
|
property alias webView: webview
|
||||||
property alias profile: webview.profile
|
property alias profile: webview.profile
|
||||||
property bool remove: false
|
property bool remove: false
|
||||||
|
property bool closeButtonVisible: true
|
||||||
|
|
||||||
// Manage own browse history because WebEngineView history is wiped when a new URL is loaded via
|
// Manage own browse history because WebEngineView history is wiped when a new URL is loaded via
|
||||||
// onNewViewRequested, e.g., as happens when a social media share button is clicked.
|
// onNewViewRequested, e.g., as happens when a social media share button is clicked.
|
||||||
|
@ -64,6 +65,7 @@ Item {
|
||||||
disabledColor: hifi.colors.lightGrayText
|
disabledColor: hifi.colors.lightGrayText
|
||||||
enabled: true
|
enabled: true
|
||||||
text: "CLOSE"
|
text: "CLOSE"
|
||||||
|
visible: closeButtonVisible
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
@ -142,7 +144,7 @@ Item {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height - web.headerHeight : parent.height - web.headerHeight
|
height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height - web.headerHeight : parent.height - web.headerHeight
|
||||||
anchors.top: buttons.bottom
|
anchors.top: buttons.bottom
|
||||||
profile: HFTabletWebEngineProfile;
|
profile: HFWebEngineProfile;
|
||||||
|
|
||||||
property string userScriptUrl: ""
|
property string userScriptUrl: ""
|
||||||
|
|
||||||
|
@ -182,8 +184,6 @@ Item {
|
||||||
webview.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
|
webview.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
|
||||||
console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);
|
console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);
|
||||||
});
|
});
|
||||||
|
|
||||||
webview.profile.httpUserAgent = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onFeaturePermissionRequested: {
|
onFeaturePermissionRequested: {
|
||||||
|
|
|
@ -73,7 +73,6 @@ Item {
|
||||||
console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);
|
console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);
|
||||||
});
|
});
|
||||||
|
|
||||||
root.profile.httpUserAgent = "Mozilla/5.0 Chrome (HighFidelityInterface)";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onFeaturePermissionRequested: {
|
onFeaturePermissionRequested: {
|
||||||
|
|
|
@ -46,6 +46,8 @@ Item {
|
||||||
property int stackShadowNarrowing: 5;
|
property int stackShadowNarrowing: 5;
|
||||||
property string defaultThumbnail: Qt.resolvedUrl("../../images/default-domain.gif");
|
property string defaultThumbnail: Qt.resolvedUrl("../../images/default-domain.gif");
|
||||||
property int shadowHeight: 10;
|
property int shadowHeight: 10;
|
||||||
|
property bool hovered: false
|
||||||
|
|
||||||
HifiConstants { id: hifi }
|
HifiConstants { id: hifi }
|
||||||
|
|
||||||
function pastTime(timestamp) { // Answer a descriptive string
|
function pastTime(timestamp) { // Answer a descriptive string
|
||||||
|
@ -231,6 +233,13 @@ Item {
|
||||||
// to that which is being hovered over.
|
// to that which is being hovered over.
|
||||||
property var hoverThunk: function () { };
|
property var hoverThunk: function () { };
|
||||||
property var unhoverThunk: function () { };
|
property var unhoverThunk: function () { };
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent;
|
||||||
|
visible: root.hovered
|
||||||
|
color: "transparent";
|
||||||
|
border.width: 4; border.color: hifiStyleConstants.colors.primaryHighlight;
|
||||||
|
z: 1;
|
||||||
|
}
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent;
|
anchors.fill: parent;
|
||||||
acceptedButtons: Qt.LeftButton;
|
acceptedButtons: Qt.LeftButton;
|
||||||
|
|
|
@ -206,9 +206,9 @@ Column {
|
||||||
id: scroll;
|
id: scroll;
|
||||||
model: suggestions;
|
model: suggestions;
|
||||||
orientation: ListView.Horizontal;
|
orientation: ListView.Horizontal;
|
||||||
|
highlightFollowsCurrentItem: false
|
||||||
highlightMoveDuration: -1;
|
highlightMoveDuration: -1;
|
||||||
highlightMoveVelocity: -1;
|
highlightMoveVelocity: -1;
|
||||||
highlight: Rectangle { color: "transparent"; border.width: 4; border.color: hifiStyleConstants.colors.primaryHighlight; z: 1; }
|
|
||||||
currentIndex: -1;
|
currentIndex: -1;
|
||||||
|
|
||||||
spacing: 12;
|
spacing: 12;
|
||||||
|
@ -237,9 +237,8 @@ Column {
|
||||||
textSizeSmall: root.textSizeSmall;
|
textSizeSmall: root.textSizeSmall;
|
||||||
stackShadowNarrowing: root.stackShadowNarrowing;
|
stackShadowNarrowing: root.stackShadowNarrowing;
|
||||||
shadowHeight: root.stackedCardShadowHeight;
|
shadowHeight: root.stackedCardShadowHeight;
|
||||||
|
hoverThunk: function () { hovered = true }
|
||||||
hoverThunk: function () { scrollToIndex(index); }
|
unhoverThunk: function () { hovered = false }
|
||||||
unhoverThunk: function () { scrollToIndex(-1); }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
|
|
|
@ -147,7 +147,7 @@ Rectangle {
|
||||||
width: parent.width;
|
width: parent.width;
|
||||||
height: keyboardEnabled && keyboardRaised ? webViewHeight - keyboard.height : webViewHeight
|
height: keyboardEnabled && keyboardRaised ? webViewHeight - keyboard.height : webViewHeight
|
||||||
|
|
||||||
profile: HFTabletWebEngineProfile;
|
profile: HFWebEngineProfile;
|
||||||
|
|
||||||
property string userScriptUrl: ""
|
property string userScriptUrl: ""
|
||||||
|
|
||||||
|
|
|
@ -35,8 +35,8 @@ Rectangle {
|
||||||
Hifi.QmlCommerce {
|
Hifi.QmlCommerce {
|
||||||
id: commerce;
|
id: commerce;
|
||||||
onBuyResult: {
|
onBuyResult: {
|
||||||
if (failureMessage.length) {
|
if (result.status !== 'success') {
|
||||||
buyButton.text = "Buy Failed";
|
buyButton.text = result.message;
|
||||||
buyButton.enabled = false;
|
buyButton.enabled = false;
|
||||||
} else {
|
} else {
|
||||||
if (urlHandler.canHandleUrl(itemHref)) {
|
if (urlHandler.canHandleUrl(itemHref)) {
|
||||||
|
@ -46,20 +46,21 @@ Rectangle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onBalanceResult: {
|
onBalanceResult: {
|
||||||
if (failureMessage.length) {
|
if (result.status !== 'success') {
|
||||||
console.log("Failed to get balance", failureMessage);
|
console.log("Failed to get balance", result.message);
|
||||||
} else {
|
} else {
|
||||||
balanceReceived = true;
|
balanceReceived = true;
|
||||||
hfcBalanceText.text = balance;
|
hfcBalanceText.text = result.data.balance;
|
||||||
balanceAfterPurchase = balance - parseInt(itemPriceText.text, 10);
|
balanceAfterPurchase = result.data.balance - parseInt(itemPriceText.text, 10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onInventoryResult: {
|
onInventoryResult: {
|
||||||
if (failureMessage.length) {
|
if (result.status !== 'success') {
|
||||||
console.log("Failed to get inventory", failureMessage);
|
console.log("Failed to get inventory", result.message);
|
||||||
} else {
|
} else {
|
||||||
inventoryReceived = true;
|
inventoryReceived = true;
|
||||||
if (inventoryContains(inventory.assets, itemId)) {
|
console.log('inventory fixme', JSON.stringify(result));
|
||||||
|
if (inventoryContains(result.data.assets, itemId)) {
|
||||||
alreadyOwned = true;
|
alreadyOwned = true;
|
||||||
} else {
|
} else {
|
||||||
alreadyOwned = false;
|
alreadyOwned = false;
|
||||||
|
|
|
@ -30,17 +30,17 @@ Rectangle {
|
||||||
Hifi.QmlCommerce {
|
Hifi.QmlCommerce {
|
||||||
id: commerce;
|
id: commerce;
|
||||||
onBalanceResult: {
|
onBalanceResult: {
|
||||||
if (failureMessage.length) {
|
if (result.status !== 'success') {
|
||||||
console.log("Failed to get balance", failureMessage);
|
console.log("Failed to get balance", result.message);
|
||||||
} else {
|
} else {
|
||||||
hfcBalanceText.text = balance;
|
hfcBalanceText.text = result.data.balance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onInventoryResult: {
|
onInventoryResult: {
|
||||||
if (failureMessage.length) {
|
if (result.status !== 'success') {
|
||||||
console.log("Failed to get inventory", failureMessage);
|
console.log("Failed to get inventory", result.message);
|
||||||
} else {
|
} else {
|
||||||
inventoryContentsList.model = inventory.assets;
|
inventoryContentsList.model = result.data.assets;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onSecurityImageResult: {
|
onSecurityImageResult: {
|
||||||
|
|
|
@ -93,6 +93,11 @@ Item {
|
||||||
loader.source = "";
|
loader.source = "";
|
||||||
loader.source = "TabletWebView.qml";
|
loader.source = "TabletWebView.qml";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function loadTabletWebBase() {
|
||||||
|
loader.source = "";
|
||||||
|
loader.source = "../../controls/TabletWebView.qml";
|
||||||
|
}
|
||||||
|
|
||||||
function returnToPreviousApp() {
|
function returnToPreviousApp() {
|
||||||
tabletApps.remove(currentApp);
|
tabletApps.remove(currentApp);
|
||||||
|
@ -121,6 +126,9 @@ Item {
|
||||||
loader.item.url = url;
|
loader.item.url = url;
|
||||||
loader.item.scriptURL = injectedJavaScriptUrl;
|
loader.item.scriptURL = injectedJavaScriptUrl;
|
||||||
tabletApps.append({"appUrl": "TabletWebView.qml", "isWebUrl": true, "scriptUrl": injectedJavaScriptUrl, "appWebUrl": url});
|
tabletApps.append({"appUrl": "TabletWebView.qml", "isWebUrl": true, "scriptUrl": injectedJavaScriptUrl, "appWebUrl": url});
|
||||||
|
if (loader.item.hasOwnProperty("closeButtonVisible")) {
|
||||||
|
loader.item.closeButtonVisible = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// used to send a message from qml to interface script.
|
// used to send a message from qml to interface script.
|
||||||
|
|
|
@ -42,9 +42,17 @@ Windows.ScrollingWindow {
|
||||||
loader.source = "WindowWebView.qml";
|
loader.source = "WindowWebView.qml";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function loadTabletWebBase() {
|
||||||
|
loader.source = "";
|
||||||
|
loader.source = "../../controls/TabletWebView.qml";
|
||||||
|
}
|
||||||
|
|
||||||
function loadWebUrl(url, injectedJavaScriptUrl) {
|
function loadWebUrl(url, injectedJavaScriptUrl) {
|
||||||
loader.item.url = url;
|
loader.item.url = url;
|
||||||
loader.item.scriptURL = injectedJavaScriptUrl;
|
loader.item.scriptURL = injectedJavaScriptUrl;
|
||||||
|
if (loader.item.hasOwnProperty("closeButtonVisible")) {
|
||||||
|
loader.item.closeButtonVisible = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// used to send a message from qml to interface script.
|
// used to send a message from qml to interface script.
|
||||||
|
|
30
interface/resources/shaders/hmd_ui.frag
Normal file
30
interface/resources/shaders/hmd_ui.frag
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2016/07/11
|
||||||
|
// Copyright 2013-2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
uniform sampler2D sampler;
|
||||||
|
|
||||||
|
struct OverlayData {
|
||||||
|
mat4 mvp;
|
||||||
|
float alpha;
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(std140) uniform overlayBuffer {
|
||||||
|
OverlayData overlay;
|
||||||
|
};
|
||||||
|
|
||||||
|
in vec2 vTexCoord;
|
||||||
|
|
||||||
|
out vec4 FragColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
FragColor = texture(sampler, vTexCoord);
|
||||||
|
FragColor.a *= overlay.alpha;
|
||||||
|
if (FragColor.a <= 0.0) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,12 +8,7 @@
|
||||||
|
|
||||||
struct OverlayData {
|
struct OverlayData {
|
||||||
mat4 mvp;
|
mat4 mvp;
|
||||||
vec4 glowPoints;
|
float alpha;
|
||||||
vec4 glowColors[2];
|
|
||||||
vec4 resolutionRadiusAlpha;
|
|
||||||
|
|
||||||
vec4 extraGlowColor;
|
|
||||||
vec2 extraGlowPoint;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
layout(std140) uniform overlayBuffer {
|
layout(std140) uniform overlayBuffer {
|
||||||
|
@ -25,11 +20,9 @@ mat4 mvp = overlay.mvp;
|
||||||
layout(location = 0) in vec3 Position;
|
layout(location = 0) in vec3 Position;
|
||||||
layout(location = 3) in vec2 TexCoord;
|
layout(location = 3) in vec2 TexCoord;
|
||||||
|
|
||||||
out vec3 vPosition;
|
|
||||||
out vec2 vTexCoord;
|
out vec2 vTexCoord;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
gl_Position = mvp * vec4(Position, 1);
|
gl_Position = mvp * vec4(Position, 1);
|
||||||
vTexCoord = TexCoord;
|
vTexCoord = TexCoord;
|
||||||
vPosition = Position;
|
|
||||||
}
|
}
|
|
@ -1,86 +0,0 @@
|
||||||
//
|
|
||||||
// Created by Bradley Austin Davis on 2016/07/11
|
|
||||||
// Copyright 2013-2016 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
uniform sampler2D sampler;
|
|
||||||
|
|
||||||
struct OverlayData {
|
|
||||||
mat4 mvp;
|
|
||||||
vec4 glowPoints;
|
|
||||||
vec4 glowColors[2];
|
|
||||||
vec4 resolutionRadiusAlpha;
|
|
||||||
|
|
||||||
vec4 extraGlowColor;
|
|
||||||
vec2 extraGlowPoint;
|
|
||||||
};
|
|
||||||
|
|
||||||
layout(std140) uniform overlayBuffer {
|
|
||||||
OverlayData overlay;
|
|
||||||
};
|
|
||||||
|
|
||||||
vec2 resolution = overlay.resolutionRadiusAlpha.xy;
|
|
||||||
float radius = overlay.resolutionRadiusAlpha.z;
|
|
||||||
float alpha = overlay.resolutionRadiusAlpha.w;
|
|
||||||
vec4 glowPoints = overlay.glowPoints;
|
|
||||||
vec4 glowColors[2] = overlay.glowColors;
|
|
||||||
|
|
||||||
vec2 extraGlowPoint = overlay.extraGlowPoint;
|
|
||||||
vec4 extraGlowColor = overlay.extraGlowColor;
|
|
||||||
|
|
||||||
in vec3 vPosition;
|
|
||||||
in vec2 vTexCoord;
|
|
||||||
|
|
||||||
out vec4 FragColor;
|
|
||||||
|
|
||||||
float easeInOutCubic(float f) {
|
|
||||||
const float d = 1.0;
|
|
||||||
const float b = 0.0;
|
|
||||||
const float c = 1.0;
|
|
||||||
float t = f;
|
|
||||||
if ((t /= d / 2.0) < 1.0) return c / 2.0 * t * t * t + b;
|
|
||||||
return c / 2.0 * ((t -= 2.0) * t * t + 2.0) + b;
|
|
||||||
}
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
FragColor = texture(sampler, vTexCoord);
|
|
||||||
|
|
||||||
vec2 aspect = resolution;
|
|
||||||
aspect /= resolution.x;
|
|
||||||
|
|
||||||
float glowIntensity = 0.0;
|
|
||||||
float dist1 = distance(vTexCoord * aspect, glowPoints.xy * aspect);
|
|
||||||
float dist2 = distance(vTexCoord * aspect, glowPoints.zw * aspect);
|
|
||||||
float dist3 = distance(vTexCoord * aspect, extraGlowPoint * aspect);
|
|
||||||
float distX = min(dist1, dist2);
|
|
||||||
float dist = min(distX, dist3);
|
|
||||||
vec3 glowColor = glowColors[0].rgb;
|
|
||||||
if (dist2 < dist1) {
|
|
||||||
glowColor = glowColors[1].rgb;
|
|
||||||
}
|
|
||||||
if (dist3 < dist2) {
|
|
||||||
glowColor = extraGlowColor.rgb;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dist <= radius) {
|
|
||||||
glowIntensity = 1.0 - (dist / radius);
|
|
||||||
glowColor.rgb = pow(glowColor, vec3(1.0 - glowIntensity));
|
|
||||||
glowIntensity = easeInOutCubic(glowIntensity);
|
|
||||||
glowIntensity = pow(glowIntensity, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (alpha <= 0.0) {
|
|
||||||
if (glowIntensity <= 0.0) {
|
|
||||||
discard;
|
|
||||||
}
|
|
||||||
|
|
||||||
FragColor = vec4(glowColor, glowIntensity);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
FragColor.rgb = mix(FragColor.rgb, glowColor.rgb, glowIntensity);
|
|
||||||
FragColor.a *= alpha;
|
|
||||||
}
|
|
|
@ -193,6 +193,10 @@
|
||||||
#include <src/scripting/LimitlessVoiceRecognitionScriptingInterface.h>
|
#include <src/scripting/LimitlessVoiceRecognitionScriptingInterface.h>
|
||||||
#include <EntityScriptClient.h>
|
#include <EntityScriptClient.h>
|
||||||
#include <ModelScriptingInterface.h>
|
#include <ModelScriptingInterface.h>
|
||||||
|
|
||||||
|
#include <raypick/RayPickScriptingInterface.h>
|
||||||
|
#include <raypick/LaserPointerScriptingInterface.h>
|
||||||
|
|
||||||
#include "commerce/Ledger.h"
|
#include "commerce/Ledger.h"
|
||||||
#include "commerce/Wallet.h"
|
#include "commerce/Wallet.h"
|
||||||
#include "commerce/QmlCommerce.h"
|
#include "commerce/QmlCommerce.h"
|
||||||
|
@ -608,6 +612,9 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
|
||||||
DependencyManager::set<Ledger>();
|
DependencyManager::set<Ledger>();
|
||||||
DependencyManager::set<Wallet>();
|
DependencyManager::set<Wallet>();
|
||||||
|
|
||||||
|
DependencyManager::set<LaserPointerScriptingInterface>();
|
||||||
|
DependencyManager::set<RayPickScriptingInterface>();
|
||||||
|
|
||||||
return previousSessionCrashed;
|
return previousSessionCrashed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2468,6 +2475,7 @@ void Application::paintGL() {
|
||||||
finalFramebuffer = framebufferCache->getFramebuffer();
|
finalFramebuffer = framebufferCache->getFramebuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mat4 eyeProjections[2];
|
||||||
{
|
{
|
||||||
PROFILE_RANGE(render, "/mainRender");
|
PROFILE_RANGE(render, "/mainRender");
|
||||||
PerformanceTimer perfTimer("mainRender");
|
PerformanceTimer perfTimer("mainRender");
|
||||||
|
@ -2489,7 +2497,6 @@ void Application::paintGL() {
|
||||||
_myCamera.setProjection(displayPlugin->getCullingProjection(_myCamera.getProjection()));
|
_myCamera.setProjection(displayPlugin->getCullingProjection(_myCamera.getProjection()));
|
||||||
renderArgs._context->enableStereo(true);
|
renderArgs._context->enableStereo(true);
|
||||||
mat4 eyeOffsets[2];
|
mat4 eyeOffsets[2];
|
||||||
mat4 eyeProjections[2];
|
|
||||||
auto baseProjection = renderArgs.getViewFrustum().getProjection();
|
auto baseProjection = renderArgs.getViewFrustum().getProjection();
|
||||||
auto hmdInterface = DependencyManager::get<HMDScriptingInterface>();
|
auto hmdInterface = DependencyManager::get<HMDScriptingInterface>();
|
||||||
float IPDScale = hmdInterface->getIPDScale();
|
float IPDScale = hmdInterface->getIPDScale();
|
||||||
|
@ -2520,6 +2527,19 @@ void Application::paintGL() {
|
||||||
displaySide(&renderArgs, _myCamera);
|
displaySide(&renderArgs, _myCamera);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gpu::Batch postCompositeBatch;
|
||||||
|
{
|
||||||
|
PROFILE_RANGE(render, "/postComposite");
|
||||||
|
PerformanceTimer perfTimer("postComposite");
|
||||||
|
renderArgs._batch = &postCompositeBatch;
|
||||||
|
renderArgs._batch->setViewportTransform(ivec4(0, 0, finalFramebufferSize.width(), finalFramebufferSize.height()));
|
||||||
|
renderArgs._batch->setViewTransform(renderArgs.getViewFrustum().getView());
|
||||||
|
for_each_eye([&](Eye eye) {
|
||||||
|
renderArgs._batch->setProjectionTransform(eyeProjections[eye]);
|
||||||
|
_overlays.render3DHUDOverlays(&renderArgs);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
auto frame = _gpuContext->endFrame();
|
auto frame = _gpuContext->endFrame();
|
||||||
frame->frameIndex = _frameCount;
|
frame->frameIndex = _frameCount;
|
||||||
frame->framebuffer = finalFramebuffer;
|
frame->framebuffer = finalFramebuffer;
|
||||||
|
@ -2527,6 +2547,7 @@ void Application::paintGL() {
|
||||||
DependencyManager::get<FramebufferCache>()->releaseFramebuffer(framebuffer);
|
DependencyManager::get<FramebufferCache>()->releaseFramebuffer(framebuffer);
|
||||||
};
|
};
|
||||||
frame->overlay = _applicationOverlay.getOverlayTexture();
|
frame->overlay = _applicationOverlay.getOverlayTexture();
|
||||||
|
frame->postCompositeBatch = postCompositeBatch;
|
||||||
// deliver final scene rendering commands to the display plugin
|
// deliver final scene rendering commands to the display plugin
|
||||||
{
|
{
|
||||||
PROFILE_RANGE(render, "/pluginOutput");
|
PROFILE_RANGE(render, "/pluginOutput");
|
||||||
|
@ -2830,6 +2851,18 @@ bool Application::importSVOFromURL(const QString& urlString) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Application::importFromZIP(const QString& filePath) {
|
||||||
|
qDebug() << "A zip file has been dropped in: " << filePath;
|
||||||
|
QUrl empty;
|
||||||
|
// handle Blocks download from Marketplace
|
||||||
|
if (filePath.contains("vr.google.com/downloads")) {
|
||||||
|
addAssetToWorldFromURL(filePath);
|
||||||
|
} else {
|
||||||
|
qApp->getFileDownloadInterface()->runUnzip(filePath, empty, true, true, false);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void Application::onPresent(quint32 frameCount) {
|
void Application::onPresent(quint32 frameCount) {
|
||||||
if (shouldPaint()) {
|
if (shouldPaint()) {
|
||||||
postEvent(this, new QEvent(static_cast<QEvent::Type>(Idle)), Qt::HighEventPriority);
|
postEvent(this, new QEvent(static_cast<QEvent::Type>(Idle)), Qt::HighEventPriority);
|
||||||
|
@ -2837,13 +2870,6 @@ void Application::onPresent(quint32 frameCount) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Application::importFromZIP(const QString& filePath) {
|
|
||||||
qDebug() << "A zip file has been dropped in: " << filePath;
|
|
||||||
QUrl empty;
|
|
||||||
qApp->getFileDownloadInterface()->runUnzip(filePath, empty, true, true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _renderRequested { false };
|
bool _renderRequested { false };
|
||||||
|
|
||||||
bool Application::event(QEvent* event) {
|
bool Application::event(QEvent* event) {
|
||||||
|
@ -5078,6 +5104,16 @@ void Application::update(float deltaTime) {
|
||||||
_overlays.update(deltaTime);
|
_overlays.update(deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
PROFILE_RANGE(app, "RayPickManager");
|
||||||
|
_rayPickManager.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
PROFILE_RANGE(app, "LaserPointerManager");
|
||||||
|
_laserPointerManager.update();
|
||||||
|
}
|
||||||
|
|
||||||
// Update _viewFrustum with latest camera and view frustum data...
|
// Update _viewFrustum with latest camera and view frustum data...
|
||||||
// NOTE: we get this from the view frustum, to make it simpler, since the
|
// NOTE: we get this from the view frustum, to make it simpler, since the
|
||||||
// loadViewFrumstum() method will get the correct details from the camera
|
// loadViewFrumstum() method will get the correct details from the camera
|
||||||
|
@ -5939,6 +5975,9 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
||||||
scriptEngine->registerGlobalObject("AvatarBookmarks", DependencyManager::get<AvatarBookmarks>().data());
|
scriptEngine->registerGlobalObject("AvatarBookmarks", DependencyManager::get<AvatarBookmarks>().data());
|
||||||
scriptEngine->registerGlobalObject("LocationBookmarks", DependencyManager::get<LocationBookmarks>().data());
|
scriptEngine->registerGlobalObject("LocationBookmarks", DependencyManager::get<LocationBookmarks>().data());
|
||||||
|
|
||||||
|
scriptEngine->registerGlobalObject("RayPick", DependencyManager::get<RayPickScriptingInterface>().data());
|
||||||
|
scriptEngine->registerGlobalObject("LaserPointers", DependencyManager::get<LaserPointerScriptingInterface>().data());
|
||||||
|
|
||||||
// Caches
|
// Caches
|
||||||
scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCache>().data());
|
scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCache>().data());
|
||||||
scriptEngine->registerGlobalObject("TextureCache", DependencyManager::get<TextureCache>().data());
|
scriptEngine->registerGlobalObject("TextureCache", DependencyManager::get<TextureCache>().data());
|
||||||
|
@ -6320,8 +6359,15 @@ void Application::showAssetServerWidget(QString filePath) {
|
||||||
|
|
||||||
void Application::addAssetToWorldFromURL(QString url) {
|
void Application::addAssetToWorldFromURL(QString url) {
|
||||||
qInfo(interfaceapp) << "Download model and add to world from" << url;
|
qInfo(interfaceapp) << "Download model and add to world from" << url;
|
||||||
|
|
||||||
QString filename = url.section("filename=", 1, 1); // Filename is in "?filename=" parameter at end of URL.
|
QString filename;
|
||||||
|
if (url.contains("filename")) {
|
||||||
|
filename = url.section("filename=", 1, 1); // Filename is in "?filename=" parameter at end of URL.
|
||||||
|
}
|
||||||
|
if (url.contains("vr.google.com/downloads")) {
|
||||||
|
filename = url.section('/', -1);
|
||||||
|
filename.remove(".zip?noDownload=false");
|
||||||
|
}
|
||||||
|
|
||||||
if (!DependencyManager::get<NodeList>()->getThisNodeCanWriteAssets()) {
|
if (!DependencyManager::get<NodeList>()->getThisNodeCanWriteAssets()) {
|
||||||
QString errorInfo = "You do not have permissions to write to the Asset Server.";
|
QString errorInfo = "You do not have permissions to write to the Asset Server.";
|
||||||
|
@ -6342,7 +6388,17 @@ void Application::addAssetToWorldFromURLRequestFinished() {
|
||||||
auto url = request->getUrl().toString();
|
auto url = request->getUrl().toString();
|
||||||
auto result = request->getResult();
|
auto result = request->getResult();
|
||||||
|
|
||||||
QString filename = url.section("filename=", 1, 1); // Filename from trailing "?filename=" URL parameter.
|
QString filename;
|
||||||
|
bool isBlocks = false;
|
||||||
|
|
||||||
|
if (url.contains("filename")) {
|
||||||
|
filename = url.section("filename=", 1, 1); // Filename is in "?filename=" parameter at end of URL.
|
||||||
|
}
|
||||||
|
if (url.contains("vr.google.com/downloads")) {
|
||||||
|
filename = url.section('/', -1);
|
||||||
|
filename.remove(".zip?noDownload=false");
|
||||||
|
isBlocks = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (result == ResourceRequest::Success) {
|
if (result == ResourceRequest::Success) {
|
||||||
qInfo(interfaceapp) << "Downloaded model from" << url;
|
qInfo(interfaceapp) << "Downloaded model from" << url;
|
||||||
|
@ -6357,7 +6413,7 @@ void Application::addAssetToWorldFromURLRequestFinished() {
|
||||||
if (tempFile.open(QIODevice::WriteOnly)) {
|
if (tempFile.open(QIODevice::WriteOnly)) {
|
||||||
tempFile.write(request->getData());
|
tempFile.write(request->getData());
|
||||||
addAssetToWorldInfoClear(filename); // Remove message from list; next one added will have a different key.
|
addAssetToWorldInfoClear(filename); // Remove message from list; next one added will have a different key.
|
||||||
qApp->getFileDownloadInterface()->runUnzip(downloadPath, url, true, false);
|
qApp->getFileDownloadInterface()->runUnzip(downloadPath, url, true, false, isBlocks);
|
||||||
} else {
|
} else {
|
||||||
QString errorInfo = "Couldn't open temporary file for download";
|
QString errorInfo = "Couldn't open temporary file for download";
|
||||||
qWarning(interfaceapp) << errorInfo;
|
qWarning(interfaceapp) << errorInfo;
|
||||||
|
@ -6387,7 +6443,7 @@ void Application::addAssetToWorldUnzipFailure(QString filePath) {
|
||||||
addAssetToWorldError(filename, "Couldn't unzip file " + filename + ".");
|
addAssetToWorldError(filename, "Couldn't unzip file " + filename + ".");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::addAssetToWorld(QString filePath, QString zipFile, bool isZip) {
|
void Application::addAssetToWorld(QString filePath, QString zipFile, bool isZip, bool isBlocks) {
|
||||||
// Automatically upload and add asset to world as an alternative manual process initiated by showAssetServerWidget().
|
// Automatically upload and add asset to world as an alternative manual process initiated by showAssetServerWidget().
|
||||||
QString mapping;
|
QString mapping;
|
||||||
QString path = filePath;
|
QString path = filePath;
|
||||||
|
@ -6396,6 +6452,11 @@ void Application::addAssetToWorld(QString filePath, QString zipFile, bool isZip)
|
||||||
QString assetFolder = zipFile.section("/", -1);
|
QString assetFolder = zipFile.section("/", -1);
|
||||||
assetFolder.remove(".zip");
|
assetFolder.remove(".zip");
|
||||||
mapping = "/" + assetFolder + "/" + filename;
|
mapping = "/" + assetFolder + "/" + filename;
|
||||||
|
} else if (isBlocks) {
|
||||||
|
qCDebug(interfaceapp) << "Path to asset folder: " << zipFile;
|
||||||
|
QString assetFolder = zipFile.section('/', -1);
|
||||||
|
assetFolder.remove(".zip?noDownload=false");
|
||||||
|
mapping = "/" + assetFolder + "/" + filename;
|
||||||
} else {
|
} else {
|
||||||
mapping = "/" + filename;
|
mapping = "/" + filename;
|
||||||
}
|
}
|
||||||
|
@ -6775,12 +6836,12 @@ void Application::onAssetToWorldMessageBoxClosed() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Application::handleUnzip(QString zipFile, QStringList unzipFile, bool autoAdd, bool isZip) {
|
void Application::handleUnzip(QString zipFile, QStringList unzipFile, bool autoAdd, bool isZip, bool isBlocks) {
|
||||||
if (autoAdd) {
|
if (autoAdd) {
|
||||||
if (!unzipFile.isEmpty()) {
|
if (!unzipFile.isEmpty()) {
|
||||||
for (int i = 0; i < unzipFile.length(); i++) {
|
for (int i = 0; i < unzipFile.length(); i++) {
|
||||||
qCDebug(interfaceapp) << "Preparing file for asset server: " << unzipFile.at(i);
|
qCDebug(interfaceapp) << "Preparing file for asset server: " << unzipFile.at(i);
|
||||||
addAssetToWorld(unzipFile.at(i), zipFile, isZip);
|
addAssetToWorld(unzipFile.at(i), zipFile, isZip, isBlocks);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
addAssetToWorldUnzipFailure(zipFile);
|
addAssetToWorldUnzipFailure(zipFile);
|
||||||
|
|
|
@ -71,6 +71,9 @@
|
||||||
#include "ui/overlays/Overlays.h"
|
#include "ui/overlays/Overlays.h"
|
||||||
#include "UndoStackScriptingInterface.h"
|
#include "UndoStackScriptingInterface.h"
|
||||||
|
|
||||||
|
#include "raypick/RayPickManager.h"
|
||||||
|
#include "raypick/LaserPointerManager.h"
|
||||||
|
|
||||||
#include <procedural/ProceduralSkybox.h>
|
#include <procedural/ProceduralSkybox.h>
|
||||||
#include <model/Skybox.h>
|
#include <model/Skybox.h>
|
||||||
#include <ModelScriptingInterface.h>
|
#include <ModelScriptingInterface.h>
|
||||||
|
@ -302,6 +305,9 @@ public:
|
||||||
QUrl getAvatarOverrideUrl() { return _avatarOverrideUrl; }
|
QUrl getAvatarOverrideUrl() { return _avatarOverrideUrl; }
|
||||||
bool getSaveAvatarOverrideUrl() { return _saveAvatarOverrideUrl; }
|
bool getSaveAvatarOverrideUrl() { return _saveAvatarOverrideUrl; }
|
||||||
|
|
||||||
|
LaserPointerManager& getLaserPointerManager() { return _laserPointerManager; }
|
||||||
|
RayPickManager& getRayPickManager() { return _rayPickManager; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void svoImportRequested(const QString& url);
|
void svoImportRequested(const QString& url);
|
||||||
|
|
||||||
|
@ -333,14 +339,14 @@ public slots:
|
||||||
// FIXME: Move addAssetToWorld* methods to own class?
|
// FIXME: Move addAssetToWorld* methods to own class?
|
||||||
void addAssetToWorldFromURL(QString url);
|
void addAssetToWorldFromURL(QString url);
|
||||||
void addAssetToWorldFromURLRequestFinished();
|
void addAssetToWorldFromURLRequestFinished();
|
||||||
void addAssetToWorld(QString filePath, QString zipFile, bool isZip);
|
void addAssetToWorld(QString filePath, QString zipFile, bool isZip, bool isBlocks);
|
||||||
void addAssetToWorldUnzipFailure(QString filePath);
|
void addAssetToWorldUnzipFailure(QString filePath);
|
||||||
void addAssetToWorldWithNewMapping(QString filePath, QString mapping, int copy);
|
void addAssetToWorldWithNewMapping(QString filePath, QString mapping, int copy);
|
||||||
void addAssetToWorldUpload(QString filePath, QString mapping);
|
void addAssetToWorldUpload(QString filePath, QString mapping);
|
||||||
void addAssetToWorldSetMapping(QString filePath, QString mapping, QString hash);
|
void addAssetToWorldSetMapping(QString filePath, QString mapping, QString hash);
|
||||||
void addAssetToWorldAddEntity(QString filePath, QString mapping);
|
void addAssetToWorldAddEntity(QString filePath, QString mapping);
|
||||||
|
|
||||||
void handleUnzip(QString sourceFile, QStringList destinationFile, bool autoAdd, bool isZip);
|
void handleUnzip(QString sourceFile, QStringList destinationFile, bool autoAdd, bool isZip, bool isBlocks);
|
||||||
|
|
||||||
FileScriptingInterface* getFileDownloadInterface() { return _fileDownload; }
|
FileScriptingInterface* getFileDownloadInterface() { return _fileDownload; }
|
||||||
|
|
||||||
|
@ -701,5 +707,8 @@ private:
|
||||||
QUrl _avatarOverrideUrl;
|
QUrl _avatarOverrideUrl;
|
||||||
bool _saveAvatarOverrideUrl { false };
|
bool _saveAvatarOverrideUrl { false };
|
||||||
|
|
||||||
|
LaserPointerManager _laserPointerManager;
|
||||||
|
RayPickManager _rayPickManager;
|
||||||
|
|
||||||
};
|
};
|
||||||
#endif // hifi_Application_h
|
#endif // hifi_Application_h
|
||||||
|
|
|
@ -473,19 +473,25 @@ AvatarSharedPointer AvatarManager::getAvatarBySessionID(const QUuid& sessionID)
|
||||||
RayToAvatarIntersectionResult AvatarManager::findRayIntersection(const PickRay& ray,
|
RayToAvatarIntersectionResult AvatarManager::findRayIntersection(const PickRay& ray,
|
||||||
const QScriptValue& avatarIdsToInclude,
|
const QScriptValue& avatarIdsToInclude,
|
||||||
const QScriptValue& avatarIdsToDiscard) {
|
const QScriptValue& avatarIdsToDiscard) {
|
||||||
RayToAvatarIntersectionResult result;
|
|
||||||
if (QThread::currentThread() != thread()) {
|
|
||||||
BLOCKING_INVOKE_METHOD(const_cast<AvatarManager*>(this), "findRayIntersection",
|
|
||||||
Q_RETURN_ARG(RayToAvatarIntersectionResult, result),
|
|
||||||
Q_ARG(const PickRay&, ray),
|
|
||||||
Q_ARG(const QScriptValue&, avatarIdsToInclude),
|
|
||||||
Q_ARG(const QScriptValue&, avatarIdsToDiscard));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVector<EntityItemID> avatarsToInclude = qVectorEntityItemIDFromScriptValue(avatarIdsToInclude);
|
QVector<EntityItemID> avatarsToInclude = qVectorEntityItemIDFromScriptValue(avatarIdsToInclude);
|
||||||
QVector<EntityItemID> avatarsToDiscard = qVectorEntityItemIDFromScriptValue(avatarIdsToDiscard);
|
QVector<EntityItemID> avatarsToDiscard = qVectorEntityItemIDFromScriptValue(avatarIdsToDiscard);
|
||||||
|
|
||||||
|
return findRayIntersectionVector(ray, avatarsToInclude, avatarsToDiscard);
|
||||||
|
}
|
||||||
|
|
||||||
|
RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const PickRay& ray,
|
||||||
|
const QVector<EntityItemID>& avatarsToInclude,
|
||||||
|
const QVector<EntityItemID>& avatarsToDiscard) {
|
||||||
|
RayToAvatarIntersectionResult result;
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
BLOCKING_INVOKE_METHOD(const_cast<AvatarManager*>(this), "findRayIntersectionVector",
|
||||||
|
Q_RETURN_ARG(RayToAvatarIntersectionResult, result),
|
||||||
|
Q_ARG(const PickRay&, ray),
|
||||||
|
Q_ARG(const QVector<EntityItemID>&, avatarsToInclude),
|
||||||
|
Q_ARG(const QVector<EntityItemID>&, avatarsToDiscard));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
glm::vec3 normDirection = glm::normalize(ray.direction);
|
glm::vec3 normDirection = glm::normalize(ray.direction);
|
||||||
|
|
||||||
for (auto avatarData : _avatarHash) {
|
for (auto avatarData : _avatarHash) {
|
||||||
|
|
|
@ -73,6 +73,9 @@ public:
|
||||||
Q_INVOKABLE RayToAvatarIntersectionResult findRayIntersection(const PickRay& ray,
|
Q_INVOKABLE RayToAvatarIntersectionResult findRayIntersection(const PickRay& ray,
|
||||||
const QScriptValue& avatarIdsToInclude = QScriptValue(),
|
const QScriptValue& avatarIdsToInclude = QScriptValue(),
|
||||||
const QScriptValue& avatarIdsToDiscard = QScriptValue());
|
const QScriptValue& avatarIdsToDiscard = QScriptValue());
|
||||||
|
RayToAvatarIntersectionResult findRayIntersectionVector(const PickRay& ray,
|
||||||
|
const QVector<EntityItemID>& avatarsToInclude,
|
||||||
|
const QVector<EntityItemID>& avatarsToDiscard);
|
||||||
|
|
||||||
// TODO: remove this HACK once we settle on optimal default sort coefficients
|
// TODO: remove this HACK once we settle on optimal default sort coefficients
|
||||||
Q_INVOKABLE float getAvatarSortCoefficient(const QString& name);
|
Q_INVOKABLE float getAvatarSortCoefficient(const QString& name);
|
||||||
|
|
|
@ -10,72 +10,100 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
|
#include <QJsonArray>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include "AccountManager.h"
|
#include "AccountManager.h"
|
||||||
#include "Wallet.h"
|
#include "Wallet.h"
|
||||||
#include "Ledger.h"
|
#include "Ledger.h"
|
||||||
#include "CommerceLogging.h"
|
#include "CommerceLogging.h"
|
||||||
|
|
||||||
|
// inventory answers {status: 'success', data: {assets: [{id: "guid", title: "name", preview: "url"}....]}}
|
||||||
|
// balance answers {status: 'success', data: {balance: integer}}
|
||||||
|
// buy and receive_at answer {status: 'success'}
|
||||||
|
|
||||||
|
QJsonObject Ledger::apiResponse(const QString& label, QNetworkReply& reply) {
|
||||||
|
QByteArray response = reply.readAll();
|
||||||
|
QJsonObject data = QJsonDocument::fromJson(response).object();
|
||||||
|
qInfo(commerce) << label << "response" << QJsonDocument(data).toJson(QJsonDocument::Compact);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
// Non-200 responses are not json:
|
||||||
|
QJsonObject Ledger::failResponse(const QString& label, QNetworkReply& reply) {
|
||||||
|
QString response = reply.readAll();
|
||||||
|
qWarning(commerce) << "FAILED" << label << response;
|
||||||
|
QJsonObject result
|
||||||
|
{
|
||||||
|
{ "status", "fail" },
|
||||||
|
{ "message", response }
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#define ApiHandler(NAME) void Ledger::NAME##Success(QNetworkReply& reply) { emit NAME##Result(apiResponse(#NAME, reply)); }
|
||||||
|
#define FailHandler(NAME) void Ledger::NAME##Failure(QNetworkReply& reply) { emit NAME##Result(failResponse(#NAME, reply)); }
|
||||||
|
#define Handler(NAME) ApiHandler(NAME) FailHandler(NAME)
|
||||||
|
Handler(buy)
|
||||||
|
Handler(receiveAt)
|
||||||
|
Handler(balance)
|
||||||
|
Handler(inventory)
|
||||||
|
|
||||||
|
void Ledger::send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, QJsonObject request) {
|
||||||
|
auto accountManager = DependencyManager::get<AccountManager>();
|
||||||
|
const QString URL = "/api/v1/commerce/";
|
||||||
|
JSONCallbackParameters callbackParams(this, success, this, fail);
|
||||||
|
qCInfo(commerce) << "Sending" << endpoint << QJsonDocument(request).toJson(QJsonDocument::Compact);
|
||||||
|
accountManager->sendRequest(URL + endpoint,
|
||||||
|
AccountManagerAuth::Required,
|
||||||
|
method,
|
||||||
|
callbackParams,
|
||||||
|
QJsonDocument(request).toJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Ledger::signedSend(const QString& propertyName, const QByteArray& text, const QString& key, const QString& endpoint, const QString& success, const QString& fail) {
|
||||||
|
auto wallet = DependencyManager::get<Wallet>();
|
||||||
|
QString signature = key.isEmpty() ? "" : wallet->signWithKey(text, key);
|
||||||
|
QJsonObject request;
|
||||||
|
request[propertyName] = QString(text);
|
||||||
|
request["signature"] = signature;
|
||||||
|
send(endpoint, success, fail, QNetworkAccessManager::PutOperation, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Ledger::keysQuery(const QString& endpoint, const QString& success, const QString& fail) {
|
||||||
|
auto wallet = DependencyManager::get<Wallet>();
|
||||||
|
QJsonObject request;
|
||||||
|
request["public_keys"] = QJsonArray::fromStringList(wallet->listPublicKeys());
|
||||||
|
send(endpoint, success, fail, QNetworkAccessManager::PostOperation, request);
|
||||||
|
}
|
||||||
|
|
||||||
void Ledger::buy(const QString& hfc_key, int cost, const QString& asset_id, const QString& inventory_key, const QString& buyerUsername) {
|
void Ledger::buy(const QString& hfc_key, int cost, const QString& asset_id, const QString& inventory_key, const QString& buyerUsername) {
|
||||||
QJsonObject transaction;
|
QJsonObject transaction;
|
||||||
transaction["hfc_key"] = hfc_key;
|
transaction["hfc_key"] = hfc_key;
|
||||||
transaction["hfc"] = cost;
|
transaction["cost"] = cost;
|
||||||
transaction["asset_id"] = asset_id;
|
transaction["asset_id"] = asset_id;
|
||||||
transaction["inventory_key"] = inventory_key;
|
transaction["inventory_key"] = inventory_key;
|
||||||
transaction["inventory_buyer_username"] = buyerUsername;
|
transaction["inventory_buyer_username"] = buyerUsername;
|
||||||
QJsonDocument transactionDoc{ transaction };
|
QJsonDocument transactionDoc{ transaction };
|
||||||
auto transactionString = transactionDoc.toJson(QJsonDocument::Compact);
|
auto transactionString = transactionDoc.toJson(QJsonDocument::Compact);
|
||||||
|
signedSend("transaction", transactionString, hfc_key, "buy", "buySuccess", "buyFailure");
|
||||||
auto wallet = DependencyManager::get<Wallet>();
|
|
||||||
QString signature = wallet->signWithKey(transactionString, hfc_key);
|
|
||||||
QJsonObject request;
|
|
||||||
request["transaction"] = QString(transactionString);
|
|
||||||
request["signature"] = signature;
|
|
||||||
|
|
||||||
qCInfo(commerce) << "Transaction:" << QJsonDocument(request).toJson(QJsonDocument::Compact);
|
|
||||||
// FIXME: talk to server instead
|
|
||||||
if (_inventory.contains(asset_id)) {
|
|
||||||
// This is here more for testing than as a definition of semantics.
|
|
||||||
// When we have popcerts, you will certainly be able to buy a new instance of an item that you already own a different instance of.
|
|
||||||
// I'm not sure what the server should do for now in this project's MVP.
|
|
||||||
return emit buyResult("Already owned.");
|
|
||||||
}
|
|
||||||
if (initializedBalance() < cost) {
|
|
||||||
return emit buyResult("Insufficient funds.");
|
|
||||||
}
|
|
||||||
_balance -= cost;
|
|
||||||
QJsonObject inventoryAdditionObject;
|
|
||||||
inventoryAdditionObject["id"] = asset_id;
|
|
||||||
inventoryAdditionObject["title"] = "Test Title";
|
|
||||||
inventoryAdditionObject["preview"] = "https://www.aspca.org/sites/default/files/cat-care_cat-nutrition-tips_overweight_body4_left.jpg";
|
|
||||||
_inventory.push_back(inventoryAdditionObject);
|
|
||||||
emit buyResult("");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Ledger::receiveAt(const QString& hfc_key) {
|
bool Ledger::receiveAt(const QString& hfc_key, const QString& old_key) {
|
||||||
auto accountManager = DependencyManager::get<AccountManager>();
|
auto accountManager = DependencyManager::get<AccountManager>();
|
||||||
if (!accountManager->isLoggedIn()) {
|
if (!accountManager->isLoggedIn()) {
|
||||||
qCWarning(commerce) << "Cannot set receiveAt when not logged in.";
|
qCWarning(commerce) << "Cannot set receiveAt when not logged in.";
|
||||||
emit receiveAtResult("Not logged in");
|
QJsonObject result{ { "status", "fail" }, { "message", "Not logged in" } };
|
||||||
|
emit receiveAtResult(result);
|
||||||
return false; // We know right away that we will fail, so tell the caller.
|
return false; // We know right away that we will fail, so tell the caller.
|
||||||
}
|
}
|
||||||
auto username = accountManager->getAccountInfo().getUsername();
|
|
||||||
qCInfo(commerce) << "Setting default receiving key for" << username;
|
signedSend("public_key", hfc_key.toUtf8(), old_key, "receive_at", "receiveAtSuccess", "receiveAtFailure");
|
||||||
emit receiveAtResult(""); // FIXME: talk to server instead.
|
|
||||||
return true; // Note that there may still be an asynchronous signal of failure that callers might be interested in.
|
return true; // Note that there may still be an asynchronous signal of failure that callers might be interested in.
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ledger::balance(const QStringList& keys) {
|
void Ledger::balance(const QStringList& keys) {
|
||||||
// FIXME: talk to server instead
|
keysQuery("balance", "balanceSuccess", "balanceFailure");
|
||||||
qCInfo(commerce) << "Balance:" << initializedBalance();
|
|
||||||
emit balanceResult(_balance, "");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ledger::inventory(const QStringList& keys) {
|
void Ledger::inventory(const QStringList& keys) {
|
||||||
// FIXME: talk to server instead
|
keysQuery("inventory", "inventorySuccess", "inventoryFailure");
|
||||||
QJsonObject inventoryObject;
|
|
||||||
inventoryObject.insert("success", true);
|
|
||||||
inventoryObject.insert("assets", _inventory);
|
|
||||||
qCInfo(commerce) << "Inventory:" << inventoryObject;
|
|
||||||
emit inventoryResult(inventoryObject, "");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,9 +14,10 @@
|
||||||
#ifndef hifi_Ledger_h
|
#ifndef hifi_Ledger_h
|
||||||
#define hifi_Ledger_h
|
#define hifi_Ledger_h
|
||||||
|
|
||||||
|
#include <QJsonObject>
|
||||||
#include <DependencyManager.h>
|
#include <DependencyManager.h>
|
||||||
#include <qjsonobject.h>
|
#include <QtNetwork/QNetworkReply>
|
||||||
#include <qjsonarray.h>
|
|
||||||
|
|
||||||
class Ledger : public QObject, public Dependency {
|
class Ledger : public QObject, public Dependency {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -24,21 +25,32 @@ class Ledger : public QObject, public Dependency {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void buy(const QString& hfc_key, int cost, const QString& asset_id, const QString& inventory_key, const QString& buyerUsername = "");
|
void buy(const QString& hfc_key, int cost, const QString& asset_id, const QString& inventory_key, const QString& buyerUsername = "");
|
||||||
bool receiveAt(const QString& hfc_key);
|
bool receiveAt(const QString& hfc_key, const QString& old_key);
|
||||||
void balance(const QStringList& keys);
|
void balance(const QStringList& keys);
|
||||||
void inventory(const QStringList& keys);
|
void inventory(const QStringList& keys);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void buyResult(const QString& failureReason);
|
void buyResult(QJsonObject result);
|
||||||
void receiveAtResult(const QString& failureReason);
|
void receiveAtResult(QJsonObject result);
|
||||||
void balanceResult(int balance, const QString& failureReason);
|
void balanceResult(QJsonObject result);
|
||||||
void inventoryResult(QJsonObject inventory, const QString& failureReason);
|
void inventoryResult(QJsonObject result);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void buySuccess(QNetworkReply& reply);
|
||||||
|
void buyFailure(QNetworkReply& reply);
|
||||||
|
void receiveAtSuccess(QNetworkReply& reply);
|
||||||
|
void receiveAtFailure(QNetworkReply& reply);
|
||||||
|
void balanceSuccess(QNetworkReply& reply);
|
||||||
|
void balanceFailure(QNetworkReply& reply);
|
||||||
|
void inventorySuccess(QNetworkReply& reply);
|
||||||
|
void inventoryFailure(QNetworkReply& reply);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// These in-memory caches is temporary, until we start sending things to the server.
|
QJsonObject apiResponse(const QString& label, QNetworkReply& reply);
|
||||||
int _balance{ -1 };
|
QJsonObject failResponse(const QString& label, QNetworkReply& reply);
|
||||||
QJsonArray _inventory{};
|
void send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, QJsonObject request);
|
||||||
int initializedBalance() { if (_balance < 0) _balance = 100; return _balance; }
|
void keysQuery(const QString& endpoint, const QString& success, const QString& fail);
|
||||||
|
void signedSend(const QString& propertyName, const QByteArray& text, const QString& key, const QString& endpoint, const QString& success, const QString& fail);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_Ledger_h
|
#endif // hifi_Ledger_h
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// Commerce.cpp
|
// QmlCommerce.cpp
|
||||||
// interface/src/commerce
|
// interface/src/commerce
|
||||||
//
|
//
|
||||||
// Created by Howard Stearns on 8/4/17.
|
// Created by Howard Stearns on 8/4/17.
|
||||||
|
@ -31,14 +31,12 @@ void QmlCommerce::buy(const QString& assetId, int cost, const QString& buyerUser
|
||||||
auto wallet = DependencyManager::get<Wallet>();
|
auto wallet = DependencyManager::get<Wallet>();
|
||||||
QStringList keys = wallet->listPublicKeys();
|
QStringList keys = wallet->listPublicKeys();
|
||||||
if (keys.count() == 0) {
|
if (keys.count() == 0) {
|
||||||
return emit buyResult("Uninitialized Wallet.");
|
QJsonObject result{ { "status", "fail" }, { "message", "Uninitialized Wallet." } };
|
||||||
|
return emit buyResult(result);
|
||||||
}
|
}
|
||||||
QString key = keys[0];
|
QString key = keys[0];
|
||||||
// For now, we receive at the same key that pays for it.
|
// For now, we receive at the same key that pays for it.
|
||||||
ledger->buy(key, cost, assetId, key, buyerUsername);
|
ledger->buy(key, cost, assetId, key, buyerUsername);
|
||||||
// FIXME: until we start talking to server, report post-transaction balance and inventory so we can see log for testing.
|
|
||||||
balance();
|
|
||||||
inventory();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QmlCommerce::balance() {
|
void QmlCommerce::balance() {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// Commerce.h
|
// QmlCommerce.h
|
||||||
// interface/src/commerce
|
// interface/src/commerce
|
||||||
//
|
//
|
||||||
// Guard for safe use of Commerce (Wallet, Ledger) by authorized QML.
|
// Guard for safe use of Commerce (Wallet, Ledger) by authorized QML.
|
||||||
|
@ -15,6 +15,7 @@
|
||||||
#ifndef hifi_QmlCommerce_h
|
#ifndef hifi_QmlCommerce_h
|
||||||
#define hifi_QmlCommerce_h
|
#define hifi_QmlCommerce_h
|
||||||
|
|
||||||
|
#include <QJsonObject>
|
||||||
#include <OffscreenQmlDialog.h>
|
#include <OffscreenQmlDialog.h>
|
||||||
|
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
|
@ -27,11 +28,11 @@ public:
|
||||||
QmlCommerce(QQuickItem* parent = nullptr);
|
QmlCommerce(QQuickItem* parent = nullptr);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void buyResult(const QString& failureMessage);
|
void buyResult(QJsonObject result);
|
||||||
// Balance and Inventory are NOT properties, because QML can't change them (without risk of failure), and
|
// Balance and Inventory are NOT properties, because QML can't change them (without risk of failure), and
|
||||||
// because we can't scalably know of out-of-band changes (e.g., another machine interacting with the block chain).
|
// because we can't scalably know of out-of-band changes (e.g., another machine interacting with the block chain).
|
||||||
void balanceResult(int balance, const QString& failureMessage);
|
void balanceResult(QJsonObject result);
|
||||||
void inventoryResult(QJsonObject inventory, const QString& failureMessage);
|
void inventoryResult(QJsonObject result);
|
||||||
void securityImageResult(QPixmap* image);
|
void securityImageResult(QPixmap* image);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -323,7 +323,6 @@ bool Wallet::createIfNeeded() {
|
||||||
RSA_free(key);
|
RSA_free(key);
|
||||||
// K -- add the public key since we have a legit private key associated with it
|
// K -- add the public key since we have a legit private key associated with it
|
||||||
_publicKeys.push_back(QUrl::toPercentEncoding(publicKey.toBase64()));
|
_publicKeys.push_back(QUrl::toPercentEncoding(publicKey.toBase64()));
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -334,16 +333,17 @@ bool Wallet::createIfNeeded() {
|
||||||
bool Wallet::generateKeyPair() {
|
bool Wallet::generateKeyPair() {
|
||||||
qCInfo(commerce) << "Generating keypair.";
|
qCInfo(commerce) << "Generating keypair.";
|
||||||
auto keyPair = generateRSAKeypair();
|
auto keyPair = generateRSAKeypair();
|
||||||
|
QString oldKey = _publicKeys.count() == 0 ? "" : _publicKeys.last();
|
||||||
_publicKeys.push_back(QUrl::toPercentEncoding(keyPair.first->toBase64()));
|
QString key = keyPair.first->toBase64();
|
||||||
qCDebug(commerce) << "public key:" << _publicKeys.last();
|
_publicKeys.push_back(key);
|
||||||
|
qCDebug(commerce) << "public key:" << key;
|
||||||
|
|
||||||
// It's arguable whether we want to change the receiveAt every time, but:
|
// It's arguable whether we want to change the receiveAt every time, but:
|
||||||
// 1. It's certainly needed the first time, when createIfNeeded answers true.
|
// 1. It's certainly needed the first time, when createIfNeeded answers true.
|
||||||
// 2. It is maximally private, and we can step back from that later if desired.
|
// 2. It is maximally private, and we can step back from that later if desired.
|
||||||
// 3. It maximally exercises all the machinery, so we are most likely to surface issues now.
|
// 3. It maximally exercises all the machinery, so we are most likely to surface issues now.
|
||||||
auto ledger = DependencyManager::get<Ledger>();
|
auto ledger = DependencyManager::get<Ledger>();
|
||||||
return ledger->receiveAt(_publicKeys.last());
|
return ledger->receiveAt(key, oldKey);
|
||||||
}
|
}
|
||||||
QStringList Wallet::listPublicKeys() {
|
QStringList Wallet::listPublicKeys() {
|
||||||
qCInfo(commerce) << "Enumerating public keys.";
|
qCInfo(commerce) << "Enumerating public keys.";
|
||||||
|
@ -378,7 +378,7 @@ QString Wallet::signWithKey(const QByteArray& text, const QString& key) {
|
||||||
RSA_free(rsaPrivateKey);
|
RSA_free(rsaPrivateKey);
|
||||||
|
|
||||||
if (encryptReturn != -1) {
|
if (encryptReturn != -1) {
|
||||||
return QUrl::toPercentEncoding(signature.toBase64());
|
return signature.toBase64();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return QString();
|
return QString();
|
||||||
|
|
|
@ -216,7 +216,12 @@ int main(int argc, const char* argv[]) {
|
||||||
SandboxUtils::runLocalSandbox(serverContentPath, true, noUpdater);
|
SandboxUtils::runLocalSandbox(serverContentPath, true, noUpdater);
|
||||||
}
|
}
|
||||||
|
|
||||||
Application app(argc, const_cast<char**>(argv), startupTime, runningMarkerExisted);
|
// Extend argv to enable WebGL rendering
|
||||||
|
std::vector<const char*> argvExtended(&argv[0], &argv[argc]);
|
||||||
|
argvExtended.push_back("--ignore-gpu-blacklist");
|
||||||
|
int argcExtended = (int)argvExtended.size();
|
||||||
|
|
||||||
|
Application app(argcExtended, const_cast<char**>(argvExtended.data()), startupTime, runningMarkerExisted);
|
||||||
|
|
||||||
// If we failed the OpenGLVersion check, log it.
|
// If we failed the OpenGLVersion check, log it.
|
||||||
if (override) {
|
if (override) {
|
||||||
|
|
48
interface/src/raypick/JointRayPick.cpp
Normal file
48
interface/src/raypick/JointRayPick.cpp
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
//
|
||||||
|
// JointRayPick.cpp
|
||||||
|
// interface/src/raypick
|
||||||
|
//
|
||||||
|
// Created by Sam Gondelman 7/11/2017
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
#include "JointRayPick.h"
|
||||||
|
|
||||||
|
#include "DependencyManager.h"
|
||||||
|
#include "avatar/AvatarManager.h"
|
||||||
|
|
||||||
|
JointRayPick::JointRayPick(const std::string& jointName, const glm::vec3& posOffset, const glm::vec3& dirOffset, const RayPickFilter& filter, const float maxDistance, const bool enabled) :
|
||||||
|
RayPick(filter, maxDistance, enabled),
|
||||||
|
_jointName(jointName),
|
||||||
|
_posOffset(posOffset),
|
||||||
|
_dirOffset(dirOffset)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const PickRay JointRayPick::getPickRay(bool& valid) const {
|
||||||
|
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||||
|
int jointIndex = myAvatar->getJointIndex(QString::fromStdString(_jointName));
|
||||||
|
bool useAvatarHead = _jointName == "Avatar";
|
||||||
|
const int INVALID_JOINT = -1;
|
||||||
|
if (jointIndex != INVALID_JOINT || useAvatarHead) {
|
||||||
|
glm::vec3 jointPos = useAvatarHead ? myAvatar->getHeadPosition() : myAvatar->getAbsoluteJointTranslationInObjectFrame(jointIndex);
|
||||||
|
glm::quat jointRot = useAvatarHead ? myAvatar->getHeadOrientation() : myAvatar->getAbsoluteJointRotationInObjectFrame(jointIndex);
|
||||||
|
glm::vec3 avatarPos = myAvatar->getPosition();
|
||||||
|
glm::quat avatarRot = myAvatar->getOrientation();
|
||||||
|
|
||||||
|
glm::vec3 pos = useAvatarHead ? jointPos : avatarPos + (avatarRot * jointPos);
|
||||||
|
glm::quat rot = useAvatarHead ? jointRot * glm::angleAxis(-PI / 2.0f, Vectors::RIGHT) : avatarRot * jointRot;
|
||||||
|
|
||||||
|
// Apply offset
|
||||||
|
pos = pos + (rot * _posOffset);
|
||||||
|
glm::vec3 dir = rot * glm::normalize(_dirOffset);
|
||||||
|
|
||||||
|
valid = true;
|
||||||
|
return PickRay(pos, dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
valid = false;
|
||||||
|
return PickRay();
|
||||||
|
}
|
30
interface/src/raypick/JointRayPick.h
Normal file
30
interface/src/raypick/JointRayPick.h
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
//
|
||||||
|
// JointRayPick.h
|
||||||
|
// interface/src/raypick
|
||||||
|
//
|
||||||
|
// Created by Sam Gondelman 7/11/2017
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
#ifndef hifi_JointRayPick_h
|
||||||
|
#define hifi_JointRayPick_h
|
||||||
|
|
||||||
|
#include "RayPick.h"
|
||||||
|
|
||||||
|
class JointRayPick : public RayPick {
|
||||||
|
|
||||||
|
public:
|
||||||
|
JointRayPick(const std::string& jointName, const glm::vec3& posOffset, const glm::vec3& dirOffset, const RayPickFilter& filter, const float maxDistance = 0.0f, const bool enabled = false);
|
||||||
|
|
||||||
|
const PickRay getPickRay(bool& valid) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string _jointName;
|
||||||
|
glm::vec3 _posOffset;
|
||||||
|
glm::vec3 _dirOffset;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_JointRayPick_h
|
221
interface/src/raypick/LaserPointer.cpp
Normal file
221
interface/src/raypick/LaserPointer.cpp
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
//
|
||||||
|
// LaserPointer.cpp
|
||||||
|
// interface/src/raypick
|
||||||
|
//
|
||||||
|
// Created by Sam Gondelman 7/11/2017
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
#include "LaserPointer.h"
|
||||||
|
|
||||||
|
#include "Application.h"
|
||||||
|
#include "ui/overlays/Overlay.h"
|
||||||
|
#include "avatar/AvatarManager.h"
|
||||||
|
|
||||||
|
LaserPointer::LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates,
|
||||||
|
const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool enabled) :
|
||||||
|
_renderingEnabled(enabled),
|
||||||
|
_renderStates(renderStates),
|
||||||
|
_defaultRenderStates(defaultRenderStates),
|
||||||
|
_faceAvatar(faceAvatar),
|
||||||
|
_centerEndY(centerEndY),
|
||||||
|
_lockEnd(lockEnd)
|
||||||
|
{
|
||||||
|
_rayPickUID = DependencyManager::get<RayPickScriptingInterface>()->createRayPick(rayProps);
|
||||||
|
|
||||||
|
for (auto& state : _renderStates) {
|
||||||
|
if (!enabled || state.first != _currentRenderState) {
|
||||||
|
disableRenderState(state.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto& state : _defaultRenderStates) {
|
||||||
|
if (!enabled || state.first != _currentRenderState) {
|
||||||
|
disableRenderState(state.second.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LaserPointer::~LaserPointer() {
|
||||||
|
DependencyManager::get<RayPickScriptingInterface>()->removeRayPick(_rayPickUID);
|
||||||
|
|
||||||
|
for (auto& renderState : _renderStates) {
|
||||||
|
renderState.second.deleteOverlays();
|
||||||
|
}
|
||||||
|
for (auto& renderState : _defaultRenderStates) {
|
||||||
|
renderState.second.second.deleteOverlays();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LaserPointer::enable() {
|
||||||
|
DependencyManager::get<RayPickScriptingInterface>()->enableRayPick(_rayPickUID);
|
||||||
|
_renderingEnabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LaserPointer::disable() {
|
||||||
|
DependencyManager::get<RayPickScriptingInterface>()->disableRayPick(_rayPickUID);
|
||||||
|
_renderingEnabled = false;
|
||||||
|
if (!_currentRenderState.empty()) {
|
||||||
|
if (_renderStates.find(_currentRenderState) != _renderStates.end()) {
|
||||||
|
disableRenderState(_renderStates[_currentRenderState]);
|
||||||
|
}
|
||||||
|
if (_defaultRenderStates.find(_currentRenderState) != _defaultRenderStates.end()) {
|
||||||
|
disableRenderState(_defaultRenderStates[_currentRenderState].second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LaserPointer::setRenderState(const std::string& state) {
|
||||||
|
if (!_currentRenderState.empty() && state != _currentRenderState) {
|
||||||
|
if (_renderStates.find(_currentRenderState) != _renderStates.end()) {
|
||||||
|
disableRenderState(_renderStates[_currentRenderState]);
|
||||||
|
}
|
||||||
|
if (_defaultRenderStates.find(_currentRenderState) != _defaultRenderStates.end()) {
|
||||||
|
disableRenderState(_defaultRenderStates[_currentRenderState].second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_currentRenderState = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LaserPointer::editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) {
|
||||||
|
updateRenderStateOverlay(_renderStates[state].getStartID(), startProps);
|
||||||
|
updateRenderStateOverlay(_renderStates[state].getPathID(), pathProps);
|
||||||
|
updateRenderStateOverlay(_renderStates[state].getEndID(), endProps);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LaserPointer::updateRenderStateOverlay(const OverlayID& id, const QVariant& props) {
|
||||||
|
if (!id.isNull() && props.isValid()) {
|
||||||
|
qApp->getOverlays().editOverlay(id, props);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LaserPointer::updateRenderState(const RenderState& renderState, const IntersectionType type, const float distance, const QUuid& objectID, const bool defaultState) {
|
||||||
|
PickRay pickRay = qApp->getRayPickManager().getPickRay(_rayPickUID);
|
||||||
|
if (!renderState.getStartID().isNull()) {
|
||||||
|
QVariantMap startProps;
|
||||||
|
startProps.insert("position", vec3toVariant(pickRay.origin));
|
||||||
|
startProps.insert("visible", true);
|
||||||
|
startProps.insert("ignoreRayIntersection", renderState.doesStartIgnoreRays());
|
||||||
|
qApp->getOverlays().editOverlay(renderState.getStartID(), startProps);
|
||||||
|
}
|
||||||
|
glm::vec3 endVec;
|
||||||
|
if (((defaultState || !_lockEnd) && _objectLockEnd.first.isNull()) || type == IntersectionType::HUD) {
|
||||||
|
endVec = pickRay.origin + pickRay.direction * distance;
|
||||||
|
} else {
|
||||||
|
if (!_objectLockEnd.first.isNull()) {
|
||||||
|
glm::vec3 pos;
|
||||||
|
glm::quat rot;
|
||||||
|
glm::vec3 dim;
|
||||||
|
glm::vec3 registrationPoint;
|
||||||
|
if (_objectLockEnd.second) {
|
||||||
|
pos = vec3FromVariant(qApp->getOverlays().getProperty(_objectLockEnd.first, "position").value);
|
||||||
|
rot = quatFromVariant(qApp->getOverlays().getProperty(_objectLockEnd.first, "rotation").value);
|
||||||
|
dim = vec3FromVariant(qApp->getOverlays().getProperty(_objectLockEnd.first, "dimensions").value);
|
||||||
|
registrationPoint = glm::vec3(0.5f);
|
||||||
|
} else {
|
||||||
|
EntityItemProperties props = DependencyManager::get<EntityScriptingInterface>()->getEntityProperties(_objectLockEnd.first);
|
||||||
|
pos = props.getPosition();
|
||||||
|
rot = props.getRotation();
|
||||||
|
dim = props.getDimensions();
|
||||||
|
registrationPoint = props.getRegistrationPoint();
|
||||||
|
}
|
||||||
|
const glm::vec3 DEFAULT_REGISTRATION_POINT = glm::vec3(0.5f);
|
||||||
|
endVec = pos + rot * (dim * (DEFAULT_REGISTRATION_POINT - registrationPoint));
|
||||||
|
} else {
|
||||||
|
if (type == IntersectionType::ENTITY) {
|
||||||
|
endVec = DependencyManager::get<EntityScriptingInterface>()->getEntityTransform(objectID)[3];
|
||||||
|
} else if (type == IntersectionType::OVERLAY) {
|
||||||
|
endVec = vec3FromVariant(qApp->getOverlays().getProperty(objectID, "position").value);
|
||||||
|
} else if (type == IntersectionType::AVATAR) {
|
||||||
|
endVec = DependencyManager::get<AvatarHashMap>()->getAvatar(objectID)->getPosition();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QVariant end = vec3toVariant(endVec);
|
||||||
|
if (!renderState.getPathID().isNull()) {
|
||||||
|
QVariantMap pathProps;
|
||||||
|
pathProps.insert("start", vec3toVariant(pickRay.origin));
|
||||||
|
pathProps.insert("end", end);
|
||||||
|
pathProps.insert("visible", true);
|
||||||
|
pathProps.insert("ignoreRayIntersection", renderState.doesPathIgnoreRays());
|
||||||
|
qApp->getOverlays().editOverlay(renderState.getPathID(), pathProps);
|
||||||
|
}
|
||||||
|
if (!renderState.getEndID().isNull()) {
|
||||||
|
QVariantMap endProps;
|
||||||
|
if (_centerEndY) {
|
||||||
|
endProps.insert("position", end);
|
||||||
|
} else {
|
||||||
|
glm::vec3 dim = vec3FromVariant(qApp->getOverlays().getProperty(renderState.getEndID(), "dimensions").value);
|
||||||
|
endProps.insert("position", vec3toVariant(endVec + glm::vec3(0, 0.5f * dim.y, 0)));
|
||||||
|
}
|
||||||
|
if (_faceAvatar) {
|
||||||
|
glm::quat rotation = glm::inverse(glm::quat_cast(glm::lookAt(endVec, DependencyManager::get<AvatarManager>()->getMyAvatar()->getPosition(), Vectors::UP)));
|
||||||
|
endProps.insert("rotation", quatToVariant(glm::quat(glm::radians(glm::vec3(0, glm::degrees(safeEulerAngles(rotation)).y, 0)))));
|
||||||
|
}
|
||||||
|
endProps.insert("visible", true);
|
||||||
|
endProps.insert("ignoreRayIntersection", renderState.doesEndIgnoreRays());
|
||||||
|
qApp->getOverlays().editOverlay(renderState.getEndID(), endProps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LaserPointer::disableRenderState(const RenderState& renderState) {
|
||||||
|
if (!renderState.getStartID().isNull()) {
|
||||||
|
QVariantMap startProps;
|
||||||
|
startProps.insert("visible", false);
|
||||||
|
startProps.insert("ignoreRayIntersection", true);
|
||||||
|
qApp->getOverlays().editOverlay(renderState.getStartID(), startProps);
|
||||||
|
}
|
||||||
|
if (!renderState.getPathID().isNull()) {
|
||||||
|
QVariantMap pathProps;
|
||||||
|
pathProps.insert("visible", false);
|
||||||
|
pathProps.insert("ignoreRayIntersection", true);
|
||||||
|
qApp->getOverlays().editOverlay(renderState.getPathID(), pathProps);
|
||||||
|
}
|
||||||
|
if (!renderState.getEndID().isNull()) {
|
||||||
|
QVariantMap endProps;
|
||||||
|
endProps.insert("visible", false);
|
||||||
|
endProps.insert("ignoreRayIntersection", true);
|
||||||
|
qApp->getOverlays().editOverlay(renderState.getEndID(), endProps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LaserPointer::update() {
|
||||||
|
RayPickResult prevRayPickResult = DependencyManager::get<RayPickScriptingInterface>()->getPrevRayPickResult(_rayPickUID);
|
||||||
|
if (_renderingEnabled && !_currentRenderState.empty() && _renderStates.find(_currentRenderState) != _renderStates.end() && prevRayPickResult.type != IntersectionType::NONE) {
|
||||||
|
updateRenderState(_renderStates[_currentRenderState], prevRayPickResult.type, prevRayPickResult.distance, prevRayPickResult.objectID, false);
|
||||||
|
disableRenderState(_defaultRenderStates[_currentRenderState].second);
|
||||||
|
} else if (_renderingEnabled && !_currentRenderState.empty() && _defaultRenderStates.find(_currentRenderState) != _defaultRenderStates.end()) {
|
||||||
|
disableRenderState(_renderStates[_currentRenderState]);
|
||||||
|
updateRenderState(_defaultRenderStates[_currentRenderState].second, IntersectionType::NONE, _defaultRenderStates[_currentRenderState].first, QUuid(), true);
|
||||||
|
} else if (!_currentRenderState.empty()) {
|
||||||
|
disableRenderState(_renderStates[_currentRenderState]);
|
||||||
|
disableRenderState(_defaultRenderStates[_currentRenderState].second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderState::RenderState(const OverlayID& startID, const OverlayID& pathID, const OverlayID& endID) :
|
||||||
|
_startID(startID), _pathID(pathID), _endID(endID)
|
||||||
|
{
|
||||||
|
if (!_startID.isNull()) {
|
||||||
|
_startIgnoreRays = qApp->getOverlays().getProperty(_startID, "ignoreRayIntersection").value.toBool();
|
||||||
|
}
|
||||||
|
if (!_pathID.isNull()) {
|
||||||
|
_pathIgnoreRays = qApp->getOverlays().getProperty(_pathID, "ignoreRayIntersection").value.toBool();
|
||||||
|
}
|
||||||
|
if (!_endID.isNull()) {
|
||||||
|
_endIgnoreRays = qApp->getOverlays().getProperty(_endID, "ignoreRayIntersection").value.toBool();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderState::deleteOverlays() {
|
||||||
|
if (!_startID.isNull()) {
|
||||||
|
qApp->getOverlays().deleteOverlay(_startID);
|
||||||
|
}
|
||||||
|
if (!_pathID.isNull()) {
|
||||||
|
qApp->getOverlays().deleteOverlay(_pathID);
|
||||||
|
}
|
||||||
|
if (!_endID.isNull()) {
|
||||||
|
qApp->getOverlays().deleteOverlay(_endID);
|
||||||
|
}
|
||||||
|
}
|
96
interface/src/raypick/LaserPointer.h
Normal file
96
interface/src/raypick/LaserPointer.h
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
//
|
||||||
|
// LaserPointer.h
|
||||||
|
// interface/src/raypick
|
||||||
|
//
|
||||||
|
// Created by Sam Gondelman 7/11/2017
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
#ifndef hifi_LaserPointer_h
|
||||||
|
#define hifi_LaserPointer_h
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include "glm/glm.hpp"
|
||||||
|
|
||||||
|
#include <DependencyManager.h>
|
||||||
|
#include "raypick/RayPickScriptingInterface.h"
|
||||||
|
|
||||||
|
class RayPickResult;
|
||||||
|
|
||||||
|
class RenderState {
|
||||||
|
|
||||||
|
public:
|
||||||
|
RenderState() {}
|
||||||
|
RenderState(const OverlayID& startID, const OverlayID& pathID, const OverlayID& endID);
|
||||||
|
|
||||||
|
const OverlayID& getStartID() const { return _startID; }
|
||||||
|
const OverlayID& getPathID() const { return _pathID; }
|
||||||
|
const OverlayID& getEndID() const { return _endID; }
|
||||||
|
const bool& doesStartIgnoreRays() const { return _startIgnoreRays; }
|
||||||
|
const bool& doesPathIgnoreRays() const { return _pathIgnoreRays; }
|
||||||
|
const bool& doesEndIgnoreRays() const { return _endIgnoreRays; }
|
||||||
|
|
||||||
|
void deleteOverlays();
|
||||||
|
|
||||||
|
private:
|
||||||
|
OverlayID _startID;
|
||||||
|
OverlayID _pathID;
|
||||||
|
OverlayID _endID;
|
||||||
|
bool _startIgnoreRays;
|
||||||
|
bool _pathIgnoreRays;
|
||||||
|
bool _endIgnoreRays;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class LaserPointer {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
typedef std::unordered_map<std::string, RenderState> RenderStateMap;
|
||||||
|
typedef std::unordered_map<std::string, std::pair<float, RenderState>> DefaultRenderStateMap;
|
||||||
|
|
||||||
|
LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates,
|
||||||
|
const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool enabled);
|
||||||
|
~LaserPointer();
|
||||||
|
|
||||||
|
QUuid getRayUID() { return _rayPickUID; }
|
||||||
|
void enable();
|
||||||
|
void disable();
|
||||||
|
const RayPickResult getPrevRayPickResult() { return DependencyManager::get<RayPickScriptingInterface>()->getPrevRayPickResult(_rayPickUID); }
|
||||||
|
|
||||||
|
void setRenderState(const std::string& state);
|
||||||
|
// You cannot use editRenderState to change the overlay type of any part of the laser pointer. You can only edit the properties of the existing overlays.
|
||||||
|
void editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps);
|
||||||
|
|
||||||
|
void setIgnoreEntities(const QScriptValue& ignoreEntities) { DependencyManager::get<RayPickScriptingInterface>()->setIgnoreEntities(_rayPickUID, ignoreEntities); }
|
||||||
|
void setIncludeEntities(const QScriptValue& includeEntities) { DependencyManager::get<RayPickScriptingInterface>()->setIncludeEntities(_rayPickUID, includeEntities); }
|
||||||
|
void setIgnoreOverlays(const QScriptValue& ignoreOverlays) { DependencyManager::get<RayPickScriptingInterface>()->setIgnoreOverlays(_rayPickUID, ignoreOverlays); }
|
||||||
|
void setIncludeOverlays(const QScriptValue& includeOverlays) { DependencyManager::get<RayPickScriptingInterface>()->setIncludeOverlays(_rayPickUID, includeOverlays); }
|
||||||
|
void setIgnoreAvatars(const QScriptValue& ignoreAvatars) { DependencyManager::get<RayPickScriptingInterface>()->setIgnoreAvatars(_rayPickUID, ignoreAvatars); }
|
||||||
|
void setIncludeAvatars(const QScriptValue& includeAvatars) { DependencyManager::get<RayPickScriptingInterface>()->setIncludeAvatars(_rayPickUID, includeAvatars); }
|
||||||
|
|
||||||
|
void setLockEndUUID(QUuid objectID, const bool isOverlay) { _objectLockEnd = std::pair<QUuid, bool>(objectID, isOverlay); }
|
||||||
|
|
||||||
|
void update();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool _renderingEnabled;
|
||||||
|
std::string _currentRenderState { "" };
|
||||||
|
RenderStateMap _renderStates;
|
||||||
|
DefaultRenderStateMap _defaultRenderStates;
|
||||||
|
bool _faceAvatar;
|
||||||
|
bool _centerEndY;
|
||||||
|
bool _lockEnd;
|
||||||
|
std::pair<QUuid, bool> _objectLockEnd { std::pair<QUuid, bool>(QUuid(), false)};
|
||||||
|
|
||||||
|
QUuid _rayPickUID;
|
||||||
|
|
||||||
|
void updateRenderStateOverlay(const OverlayID& id, const QVariant& props);
|
||||||
|
void updateRenderState(const RenderState& renderState, const IntersectionType type, const float distance, const QUuid& objectID, const bool defaultState);
|
||||||
|
void disableRenderState(const RenderState& renderState);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_LaserPointer_h
|
154
interface/src/raypick/LaserPointerManager.cpp
Normal file
154
interface/src/raypick/LaserPointerManager.cpp
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
//
|
||||||
|
// LaserPointerManager.cpp
|
||||||
|
// interface/src/raypick
|
||||||
|
//
|
||||||
|
// Created by Sam Gondelman 7/11/2017
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
#include "LaserPointerManager.h"
|
||||||
|
|
||||||
|
QUuid LaserPointerManager::createLaserPointer(const QVariant& rayProps, const LaserPointer::RenderStateMap& renderStates, const LaserPointer::DefaultRenderStateMap& defaultRenderStates,
|
||||||
|
const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool enabled) {
|
||||||
|
std::shared_ptr<LaserPointer> laserPointer = std::make_shared<LaserPointer>(rayProps, renderStates, defaultRenderStates, faceAvatar, centerEndY, lockEnd, enabled);
|
||||||
|
if (!laserPointer->getRayUID().isNull()) {
|
||||||
|
QWriteLocker lock(&_addLock);
|
||||||
|
QUuid id = QUuid::createUuid();
|
||||||
|
_laserPointersToAdd.push(std::pair<QUuid, std::shared_ptr<LaserPointer>>(id, laserPointer));
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
return QUuid();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LaserPointerManager::removeLaserPointer(const QUuid uid) {
|
||||||
|
QWriteLocker lock(&_removeLock);
|
||||||
|
_laserPointersToRemove.push(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LaserPointerManager::enableLaserPointer(const QUuid uid) {
|
||||||
|
QReadLocker lock(&_containsLock);
|
||||||
|
if (_laserPointers.contains(uid)) {
|
||||||
|
QWriteLocker laserLock(_laserPointerLocks[uid].get());
|
||||||
|
_laserPointers[uid]->enable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LaserPointerManager::disableLaserPointer(const QUuid uid) {
|
||||||
|
QReadLocker lock(&_containsLock);
|
||||||
|
if (_laserPointers.contains(uid)) {
|
||||||
|
QWriteLocker laserLock(_laserPointerLocks[uid].get());
|
||||||
|
_laserPointers[uid]->disable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LaserPointerManager::setRenderState(QUuid uid, const std::string& renderState) {
|
||||||
|
QReadLocker lock(&_containsLock);
|
||||||
|
if (_laserPointers.contains(uid)) {
|
||||||
|
QWriteLocker laserLock(_laserPointerLocks[uid].get());
|
||||||
|
_laserPointers[uid]->setRenderState(renderState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LaserPointerManager::editRenderState(QUuid uid, const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) {
|
||||||
|
QReadLocker lock(&_containsLock);
|
||||||
|
if (_laserPointers.contains(uid)) {
|
||||||
|
QWriteLocker laserLock(_laserPointerLocks[uid].get());
|
||||||
|
_laserPointers[uid]->editRenderState(state, startProps, pathProps, endProps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const RayPickResult LaserPointerManager::getPrevRayPickResult(const QUuid uid) {
|
||||||
|
QReadLocker lock(&_containsLock);
|
||||||
|
if (_laserPointers.contains(uid)) {
|
||||||
|
QReadLocker laserLock(_laserPointerLocks[uid].get());
|
||||||
|
return _laserPointers[uid]->getPrevRayPickResult();
|
||||||
|
}
|
||||||
|
return RayPickResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LaserPointerManager::update() {
|
||||||
|
for (QUuid& uid : _laserPointers.keys()) {
|
||||||
|
// This only needs to be a read lock because update won't change any of the properties that can be modified from scripts
|
||||||
|
QReadLocker laserLock(_laserPointerLocks[uid].get());
|
||||||
|
_laserPointers[uid]->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
QWriteLocker containsLock(&_containsLock);
|
||||||
|
{
|
||||||
|
QWriteLocker lock(&_addLock);
|
||||||
|
while (!_laserPointersToAdd.empty()) {
|
||||||
|
std::pair<QUuid, std::shared_ptr<LaserPointer>> laserPointerToAdd = _laserPointersToAdd.front();
|
||||||
|
_laserPointersToAdd.pop();
|
||||||
|
_laserPointers[laserPointerToAdd.first] = laserPointerToAdd.second;
|
||||||
|
_laserPointerLocks[laserPointerToAdd.first] = std::make_shared<QReadWriteLock>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
QWriteLocker lock(&_removeLock);
|
||||||
|
while (!_laserPointersToRemove.empty()) {
|
||||||
|
QUuid uid = _laserPointersToRemove.front();
|
||||||
|
_laserPointersToRemove.pop();
|
||||||
|
_laserPointers.remove(uid);
|
||||||
|
_laserPointerLocks.remove(uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LaserPointerManager::setIgnoreEntities(QUuid uid, const QScriptValue& ignoreEntities) {
|
||||||
|
QReadLocker lock(&_containsLock);
|
||||||
|
if (_laserPointers.contains(uid)) {
|
||||||
|
QWriteLocker laserLock(_laserPointerLocks[uid].get());
|
||||||
|
_laserPointers[uid]->setIgnoreEntities(ignoreEntities);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LaserPointerManager::setIncludeEntities(QUuid uid, const QScriptValue& includeEntities) {
|
||||||
|
QReadLocker lock(&_containsLock);
|
||||||
|
if (_laserPointers.contains(uid)) {
|
||||||
|
QWriteLocker laserLock(_laserPointerLocks[uid].get());
|
||||||
|
_laserPointers[uid]->setIncludeEntities(includeEntities);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LaserPointerManager::setIgnoreOverlays(QUuid uid, const QScriptValue& ignoreOverlays) {
|
||||||
|
QReadLocker lock(&_containsLock);
|
||||||
|
if (_laserPointers.contains(uid)) {
|
||||||
|
QWriteLocker laserLock(_laserPointerLocks[uid].get());
|
||||||
|
_laserPointers[uid]->setIgnoreOverlays(ignoreOverlays);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LaserPointerManager::setIncludeOverlays(QUuid uid, const QScriptValue& includeOverlays) {
|
||||||
|
QReadLocker lock(&_containsLock);
|
||||||
|
if (_laserPointers.contains(uid)) {
|
||||||
|
QWriteLocker laserLock(_laserPointerLocks[uid].get());
|
||||||
|
_laserPointers[uid]->setIncludeOverlays(includeOverlays);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LaserPointerManager::setIgnoreAvatars(QUuid uid, const QScriptValue& ignoreAvatars) {
|
||||||
|
QReadLocker lock(&_containsLock);
|
||||||
|
if (_laserPointers.contains(uid)) {
|
||||||
|
QWriteLocker laserLock(_laserPointerLocks[uid].get());
|
||||||
|
_laserPointers[uid]->setIgnoreAvatars(ignoreAvatars);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LaserPointerManager::setIncludeAvatars(QUuid uid, const QScriptValue& includeAvatars) {
|
||||||
|
QReadLocker lock(&_containsLock);
|
||||||
|
if (_laserPointers.contains(uid)) {
|
||||||
|
QWriteLocker laserLock(_laserPointerLocks[uid].get());
|
||||||
|
_laserPointers[uid]->setIncludeAvatars(includeAvatars);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LaserPointerManager::setLockEndUUID(QUuid uid, QUuid objectID, const bool isOverlay) {
|
||||||
|
QReadLocker lock(&_containsLock);
|
||||||
|
if (_laserPointers.contains(uid)) {
|
||||||
|
QWriteLocker laserLock(_laserPointerLocks[uid].get());
|
||||||
|
_laserPointers[uid]->setLockEndUUID(objectID, isOverlay);
|
||||||
|
}
|
||||||
|
}
|
56
interface/src/raypick/LaserPointerManager.h
Normal file
56
interface/src/raypick/LaserPointerManager.h
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
//
|
||||||
|
// LaserPointerManager.h
|
||||||
|
// interface/src/raypick
|
||||||
|
//
|
||||||
|
// Created by Sam Gondelman 7/11/2017
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
#ifndef hifi_LaserPointerManager_h
|
||||||
|
#define hifi_LaserPointerManager_h
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <QReadWriteLock>
|
||||||
|
|
||||||
|
#include "LaserPointer.h"
|
||||||
|
|
||||||
|
class RayPickResult;
|
||||||
|
|
||||||
|
class LaserPointerManager {
|
||||||
|
|
||||||
|
public:
|
||||||
|
QUuid createLaserPointer(const QVariant& rayProps, const LaserPointer::RenderStateMap& renderStates, const LaserPointer::DefaultRenderStateMap& defaultRenderStates,
|
||||||
|
const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool enabled);
|
||||||
|
void removeLaserPointer(const QUuid uid);
|
||||||
|
void enableLaserPointer(const QUuid uid);
|
||||||
|
void disableLaserPointer(const QUuid uid);
|
||||||
|
void setRenderState(QUuid uid, const std::string& renderState);
|
||||||
|
void editRenderState(QUuid uid, const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps);
|
||||||
|
const RayPickResult getPrevRayPickResult(const QUuid uid);
|
||||||
|
|
||||||
|
void setIgnoreEntities(QUuid uid, const QScriptValue& ignoreEntities);
|
||||||
|
void setIncludeEntities(QUuid uid, const QScriptValue& includeEntities);
|
||||||
|
void setIgnoreOverlays(QUuid uid, const QScriptValue& ignoreOverlays);
|
||||||
|
void setIncludeOverlays(QUuid uid, const QScriptValue& includeOverlays);
|
||||||
|
void setIgnoreAvatars(QUuid uid, const QScriptValue& ignoreAvatars);
|
||||||
|
void setIncludeAvatars(QUuid uid, const QScriptValue& includeAvatars);
|
||||||
|
|
||||||
|
void setLockEndUUID(QUuid uid, QUuid objectID, const bool isOverlay);
|
||||||
|
|
||||||
|
void update();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QHash<QUuid, std::shared_ptr<LaserPointer>> _laserPointers;
|
||||||
|
QHash<QUuid, std::shared_ptr<QReadWriteLock>> _laserPointerLocks;
|
||||||
|
QReadWriteLock _addLock;
|
||||||
|
std::queue<std::pair<QUuid, std::shared_ptr<LaserPointer>>> _laserPointersToAdd;
|
||||||
|
QReadWriteLock _removeLock;
|
||||||
|
std::queue<QUuid> _laserPointersToRemove;
|
||||||
|
QReadWriteLock _containsLock;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_LaserPointerManager_h
|
120
interface/src/raypick/LaserPointerScriptingInterface.cpp
Normal file
120
interface/src/raypick/LaserPointerScriptingInterface.cpp
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
//
|
||||||
|
// LaserPointerScriptingInterface.cpp
|
||||||
|
// interface/src/raypick
|
||||||
|
//
|
||||||
|
// Created by Sam Gondelman 7/11/2017
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "LaserPointerScriptingInterface.h"
|
||||||
|
|
||||||
|
#include <QVariant>
|
||||||
|
#include "GLMHelpers.h"
|
||||||
|
|
||||||
|
QUuid LaserPointerScriptingInterface::createLaserPointer(const QVariant& properties) {
|
||||||
|
QVariantMap propertyMap = properties.toMap();
|
||||||
|
|
||||||
|
bool faceAvatar = false;
|
||||||
|
if (propertyMap["faceAvatar"].isValid()) {
|
||||||
|
faceAvatar = propertyMap["faceAvatar"].toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool centerEndY = true;
|
||||||
|
if (propertyMap["centerEndY"].isValid()) {
|
||||||
|
centerEndY = propertyMap["centerEndY"].toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lockEnd = false;
|
||||||
|
if (propertyMap["lockEnd"].isValid()) {
|
||||||
|
lockEnd = propertyMap["lockEnd"].toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool enabled = false;
|
||||||
|
if (propertyMap["enabled"].isValid()) {
|
||||||
|
enabled = propertyMap["enabled"].toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
LaserPointer::RenderStateMap renderStates;
|
||||||
|
if (propertyMap["renderStates"].isValid()) {
|
||||||
|
QList<QVariant> renderStateVariants = propertyMap["renderStates"].toList();
|
||||||
|
for (QVariant& renderStateVariant : renderStateVariants) {
|
||||||
|
if (renderStateVariant.isValid()) {
|
||||||
|
QVariantMap renderStateMap = renderStateVariant.toMap();
|
||||||
|
if (renderStateMap["name"].isValid()) {
|
||||||
|
std::string name = renderStateMap["name"].toString().toStdString();
|
||||||
|
renderStates[name] = buildRenderState(renderStateMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LaserPointer::DefaultRenderStateMap defaultRenderStates;
|
||||||
|
if (propertyMap["defaultRenderStates"].isValid()) {
|
||||||
|
QList<QVariant> renderStateVariants = propertyMap["defaultRenderStates"].toList();
|
||||||
|
for (QVariant& renderStateVariant : renderStateVariants) {
|
||||||
|
if (renderStateVariant.isValid()) {
|
||||||
|
QVariantMap renderStateMap = renderStateVariant.toMap();
|
||||||
|
if (renderStateMap["name"].isValid() && renderStateMap["distance"].isValid()) {
|
||||||
|
std::string name = renderStateMap["name"].toString().toStdString();
|
||||||
|
float distance = renderStateMap["distance"].toFloat();
|
||||||
|
defaultRenderStates[name] = std::pair<float, RenderState>(distance, buildRenderState(renderStateMap));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return qApp->getLaserPointerManager().createLaserPointer(properties, renderStates, defaultRenderStates, faceAvatar, centerEndY, lockEnd, enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LaserPointerScriptingInterface::editRenderState(QUuid uid, const QString& renderState, const QVariant& properties) {
|
||||||
|
QVariantMap propMap = properties.toMap();
|
||||||
|
|
||||||
|
QVariant startProps;
|
||||||
|
if (propMap["start"].isValid()) {
|
||||||
|
startProps = propMap["start"];
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant pathProps;
|
||||||
|
if (propMap["path"].isValid()) {
|
||||||
|
pathProps = propMap["path"];
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant endProps;
|
||||||
|
if (propMap["end"].isValid()) {
|
||||||
|
endProps = propMap["end"];
|
||||||
|
}
|
||||||
|
|
||||||
|
qApp->getLaserPointerManager().editRenderState(uid, renderState.toStdString(), startProps, pathProps, endProps);
|
||||||
|
}
|
||||||
|
|
||||||
|
const RenderState LaserPointerScriptingInterface::buildRenderState(const QVariantMap& propMap) {
|
||||||
|
QUuid startID;
|
||||||
|
if (propMap["start"].isValid()) {
|
||||||
|
QVariantMap startMap = propMap["start"].toMap();
|
||||||
|
if (startMap["type"].isValid()) {
|
||||||
|
startID = qApp->getOverlays().addOverlay(startMap["type"].toString(), startMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QUuid pathID;
|
||||||
|
if (propMap["path"].isValid()) {
|
||||||
|
QVariantMap pathMap = propMap["path"].toMap();
|
||||||
|
// right now paths must be line3ds
|
||||||
|
if (pathMap["type"].isValid() && pathMap["type"].toString() == "line3d") {
|
||||||
|
pathID = qApp->getOverlays().addOverlay(pathMap["type"].toString(), pathMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QUuid endID;
|
||||||
|
if (propMap["end"].isValid()) {
|
||||||
|
QVariantMap endMap = propMap["end"].toMap();
|
||||||
|
if (endMap["type"].isValid()) {
|
||||||
|
endID = qApp->getOverlays().addOverlay(endMap["type"].toString(), endMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return RenderState(startID, pathID, endID);
|
||||||
|
}
|
47
interface/src/raypick/LaserPointerScriptingInterface.h
Normal file
47
interface/src/raypick/LaserPointerScriptingInterface.h
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
//
|
||||||
|
// LaserPointerScriptingInterface.h
|
||||||
|
// interface/src/raypick
|
||||||
|
//
|
||||||
|
// Created by Sam Gondelman 7/11/2017
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
#ifndef hifi_LaserPointerScriptingInterface_h
|
||||||
|
#define hifi_LaserPointerScriptingInterface_h
|
||||||
|
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
|
||||||
|
#include "RegisteredMetaTypes.h"
|
||||||
|
#include "DependencyManager.h"
|
||||||
|
#include "Application.h"
|
||||||
|
|
||||||
|
class LaserPointerScriptingInterface : public QObject, public Dependency {
|
||||||
|
Q_OBJECT
|
||||||
|
SINGLETON_DEPENDENCY
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
Q_INVOKABLE QUuid createLaserPointer(const QVariant& properties);
|
||||||
|
Q_INVOKABLE void enableLaserPointer(QUuid uid) { qApp->getLaserPointerManager().enableLaserPointer(uid); }
|
||||||
|
Q_INVOKABLE void disableLaserPointer(QUuid uid) { qApp->getLaserPointerManager().disableLaserPointer(uid); }
|
||||||
|
Q_INVOKABLE void removeLaserPointer(QUuid uid) { qApp->getLaserPointerManager().removeLaserPointer(uid); }
|
||||||
|
Q_INVOKABLE void editRenderState(QUuid uid, const QString& renderState, const QVariant& properties);
|
||||||
|
Q_INVOKABLE void setRenderState(QUuid uid, const QString& renderState) { qApp->getLaserPointerManager().setRenderState(uid, renderState.toStdString()); }
|
||||||
|
Q_INVOKABLE RayPickResult getPrevRayPickResult(QUuid uid) { return qApp->getLaserPointerManager().getPrevRayPickResult(uid); }
|
||||||
|
|
||||||
|
Q_INVOKABLE void setIgnoreEntities(QUuid uid, const QScriptValue& ignoreEntities) { qApp->getLaserPointerManager().setIgnoreEntities(uid, ignoreEntities); }
|
||||||
|
Q_INVOKABLE void setIncludeEntities(QUuid uid, const QScriptValue& includeEntities) { qApp->getLaserPointerManager().setIncludeEntities(uid, includeEntities); }
|
||||||
|
Q_INVOKABLE void setIgnoreOverlays(QUuid uid, const QScriptValue& ignoreOverlays) { qApp->getLaserPointerManager().setIgnoreOverlays(uid, ignoreOverlays); }
|
||||||
|
Q_INVOKABLE void setIncludeOverlays(QUuid uid, const QScriptValue& includeOverlays) { qApp->getLaserPointerManager().setIncludeOverlays(uid, includeOverlays); }
|
||||||
|
Q_INVOKABLE void setIgnoreAvatars(QUuid uid, const QScriptValue& ignoreAvatars) { qApp->getLaserPointerManager().setIgnoreAvatars(uid, ignoreAvatars); }
|
||||||
|
Q_INVOKABLE void setIncludeAvatars(QUuid uid, const QScriptValue& includeAvatars) { qApp->getLaserPointerManager().setIncludeAvatars(uid, includeAvatars); }
|
||||||
|
|
||||||
|
Q_INVOKABLE void setLockEndUUID(QUuid uid, QUuid objectID, const bool isOverlay) { qApp->getLaserPointerManager().setLockEndUUID(uid, objectID, isOverlay); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const RenderState buildRenderState(const QVariantMap& propMap);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_LaserPointerScriptingInterface_h
|
32
interface/src/raypick/MouseRayPick.cpp
Normal file
32
interface/src/raypick/MouseRayPick.cpp
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
//
|
||||||
|
// MouseRayPick.cpp
|
||||||
|
// interface/src/raypick
|
||||||
|
//
|
||||||
|
// Created by Sam Gondelman 7/19/2017
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
#include "MouseRayPick.h"
|
||||||
|
|
||||||
|
#include "DependencyManager.h"
|
||||||
|
#include "Application.h"
|
||||||
|
#include "display-plugins/CompositorHelper.h"
|
||||||
|
|
||||||
|
MouseRayPick::MouseRayPick(const RayPickFilter& filter, const float maxDistance, const bool enabled) :
|
||||||
|
RayPick(filter, maxDistance, enabled)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const PickRay MouseRayPick::getPickRay(bool& valid) const {
|
||||||
|
QVariant position = qApp->getApplicationCompositor().getReticleInterface()->getPosition();
|
||||||
|
if (position.isValid()) {
|
||||||
|
QVariantMap posMap = position.toMap();
|
||||||
|
valid = true;
|
||||||
|
return qApp->getCamera().computePickRay(posMap["x"].toFloat(), posMap["y"].toFloat());
|
||||||
|
}
|
||||||
|
|
||||||
|
valid = false;
|
||||||
|
return PickRay();
|
||||||
|
}
|
24
interface/src/raypick/MouseRayPick.h
Normal file
24
interface/src/raypick/MouseRayPick.h
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
//
|
||||||
|
// MouseRayPick.h
|
||||||
|
// interface/src/raypick
|
||||||
|
//
|
||||||
|
// Created by Sam Gondelman 7/19/2017
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
#ifndef hifi_MouseRayPick_h
|
||||||
|
#define hifi_MouseRayPick_h
|
||||||
|
|
||||||
|
#include "RayPick.h"
|
||||||
|
|
||||||
|
class MouseRayPick : public RayPick {
|
||||||
|
|
||||||
|
public:
|
||||||
|
MouseRayPick(const RayPickFilter& filter, const float maxDistance = 0.0f, const bool enabled = false);
|
||||||
|
|
||||||
|
const PickRay getPickRay(bool& valid) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_MouseRayPick_h
|
18
interface/src/raypick/RayPick.cpp
Normal file
18
interface/src/raypick/RayPick.cpp
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
//
|
||||||
|
// RayPick.cpp
|
||||||
|
// interface/src/raypick
|
||||||
|
//
|
||||||
|
// Created by Sam Gondelman 7/11/2017
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
#include "RayPick.h"
|
||||||
|
|
||||||
|
RayPick::RayPick(const RayPickFilter& filter, const float maxDistance, const bool enabled) :
|
||||||
|
_filter(filter),
|
||||||
|
_maxDistance(maxDistance),
|
||||||
|
_enabled(enabled)
|
||||||
|
{
|
||||||
|
}
|
134
interface/src/raypick/RayPick.h
Normal file
134
interface/src/raypick/RayPick.h
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
//
|
||||||
|
// RayPick.h
|
||||||
|
// interface/src/raypick
|
||||||
|
//
|
||||||
|
// Created by Sam Gondelman 7/11/2017
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
#ifndef hifi_RayPick_h
|
||||||
|
#define hifi_RayPick_h
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "RegisteredMetaTypes.h"
|
||||||
|
|
||||||
|
#include "EntityItemID.h"
|
||||||
|
#include "ui/overlays/Overlay.h"
|
||||||
|
|
||||||
|
class RayPickFilter {
|
||||||
|
public:
|
||||||
|
enum FlagBit {
|
||||||
|
PICK_NOTHING = 0,
|
||||||
|
PICK_ENTITIES,
|
||||||
|
PICK_OVERLAYS,
|
||||||
|
PICK_AVATARS,
|
||||||
|
PICK_HUD,
|
||||||
|
|
||||||
|
PICK_COURSE, // if not set, does precise intersection, otherwise, doesn't
|
||||||
|
|
||||||
|
PICK_INCLUDE_INVISIBLE, // if not set, will not intersect invisible elements, otherwise, intersects both visible and invisible elements
|
||||||
|
PICK_INCLUDE_NONCOLLIDABLE, // if not set, will not intersect noncollidable elements, otherwise, intersects both collidable and noncollidable elements
|
||||||
|
|
||||||
|
// NOT YET IMPLEMENTED
|
||||||
|
PICK_ALL_INTERSECTIONS, // if not set, returns closest intersection, otherwise, returns list of all intersections
|
||||||
|
|
||||||
|
NUM_FLAGS, // Not a valid flag
|
||||||
|
};
|
||||||
|
typedef std::bitset<NUM_FLAGS> Flags;
|
||||||
|
|
||||||
|
// The key is the Flags
|
||||||
|
Flags _flags;
|
||||||
|
|
||||||
|
RayPickFilter() : _flags(PICK_NOTHING) {}
|
||||||
|
RayPickFilter(const Flags& flags) : _flags(flags) {}
|
||||||
|
|
||||||
|
bool operator== (const RayPickFilter& rhs) const { return _flags == rhs._flags; }
|
||||||
|
bool operator!= (const RayPickFilter& rhs) const { return _flags != rhs._flags; }
|
||||||
|
|
||||||
|
bool doesPickNothing() const { return _flags[PICK_NOTHING]; }
|
||||||
|
bool doesPickEntities() const { return _flags[PICK_ENTITIES]; }
|
||||||
|
bool doesPickOverlays() const { return _flags[PICK_OVERLAYS]; }
|
||||||
|
bool doesPickAvatars() const { return _flags[PICK_AVATARS]; }
|
||||||
|
bool doesPickHUD() const { return _flags[PICK_HUD]; }
|
||||||
|
|
||||||
|
bool doesPickCourse() const { return _flags[PICK_COURSE]; }
|
||||||
|
bool doesPickInvisible() const { return _flags[PICK_INCLUDE_INVISIBLE]; }
|
||||||
|
bool doesPickNonCollidable() const { return _flags[PICK_INCLUDE_NONCOLLIDABLE]; }
|
||||||
|
|
||||||
|
bool doesWantAllIntersections() const { return _flags[PICK_ALL_INTERSECTIONS]; }
|
||||||
|
|
||||||
|
// Helpers for RayPickManager
|
||||||
|
Flags getEntityFlags() const {
|
||||||
|
Flags toReturn(PICK_ENTITIES);
|
||||||
|
if (doesPickInvisible()) {
|
||||||
|
toReturn |= Flags(PICK_INCLUDE_INVISIBLE);
|
||||||
|
}
|
||||||
|
if (doesPickNonCollidable()) {
|
||||||
|
toReturn |= Flags(PICK_INCLUDE_NONCOLLIDABLE);
|
||||||
|
}
|
||||||
|
return toReturn;
|
||||||
|
}
|
||||||
|
Flags getOverlayFlags() const {
|
||||||
|
Flags toReturn(PICK_OVERLAYS);
|
||||||
|
if (doesPickInvisible()) {
|
||||||
|
toReturn |= Flags(PICK_INCLUDE_INVISIBLE);
|
||||||
|
}
|
||||||
|
if (doesPickNonCollidable()) {
|
||||||
|
toReturn |= Flags(PICK_INCLUDE_NONCOLLIDABLE);
|
||||||
|
}
|
||||||
|
return toReturn;
|
||||||
|
}
|
||||||
|
Flags getAvatarFlags() const { return Flags(PICK_AVATARS); }
|
||||||
|
Flags getHUDFlags() const { return Flags(PICK_HUD); }
|
||||||
|
|
||||||
|
static unsigned int getBitMask(FlagBit bit) { return 1 << bit; }
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class RayPick {
|
||||||
|
|
||||||
|
public:
|
||||||
|
RayPick(const RayPickFilter& filter, const float maxDistance, const bool enabled);
|
||||||
|
|
||||||
|
virtual const PickRay getPickRay(bool& valid) const = 0;
|
||||||
|
|
||||||
|
void enable() { _enabled = true; }
|
||||||
|
void disable() { _enabled = false; }
|
||||||
|
|
||||||
|
const RayPickFilter& getFilter() { return _filter; }
|
||||||
|
const float& getMaxDistance() { return _maxDistance; }
|
||||||
|
const bool& isEnabled() { return _enabled; }
|
||||||
|
const RayPickResult& getPrevRayPickResult() { return _prevResult; }
|
||||||
|
|
||||||
|
void setRayPickResult(const RayPickResult& rayPickResult) { _prevResult = rayPickResult; }
|
||||||
|
|
||||||
|
const QVector<EntityItemID>& getIgnoreEntites() { return _ignoreEntities; }
|
||||||
|
const QVector<EntityItemID>& getIncludeEntites() { return _includeEntities; }
|
||||||
|
const QVector<OverlayID>& getIgnoreOverlays() { return _ignoreOverlays; }
|
||||||
|
const QVector<OverlayID>& getIncludeOverlays() { return _includeOverlays; }
|
||||||
|
const QVector<EntityItemID>& getIgnoreAvatars() { return _ignoreAvatars; }
|
||||||
|
const QVector<EntityItemID>& getIncludeAvatars() { return _includeAvatars; }
|
||||||
|
void setIgnoreEntities(const QScriptValue& ignoreEntities) { _ignoreEntities = qVectorEntityItemIDFromScriptValue(ignoreEntities); }
|
||||||
|
void setIncludeEntities(const QScriptValue& includeEntities) { _includeEntities = qVectorEntityItemIDFromScriptValue(includeEntities); }
|
||||||
|
void setIgnoreOverlays(const QScriptValue& ignoreOverlays) { _ignoreOverlays = qVectorOverlayIDFromScriptValue(ignoreOverlays); }
|
||||||
|
void setIncludeOverlays(const QScriptValue& includeOverlays) { _includeOverlays = qVectorOverlayIDFromScriptValue(includeOverlays); }
|
||||||
|
void setIgnoreAvatars(const QScriptValue& ignoreAvatars) { _ignoreAvatars = qVectorEntityItemIDFromScriptValue(ignoreAvatars); }
|
||||||
|
void setIncludeAvatars(const QScriptValue& includeAvatars) { _includeAvatars = qVectorEntityItemIDFromScriptValue(includeAvatars); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
RayPickFilter _filter;
|
||||||
|
float _maxDistance;
|
||||||
|
bool _enabled;
|
||||||
|
RayPickResult _prevResult;
|
||||||
|
|
||||||
|
QVector<EntityItemID> _ignoreEntities;
|
||||||
|
QVector<EntityItemID> _includeEntities;
|
||||||
|
QVector<OverlayID> _ignoreOverlays;
|
||||||
|
QVector<OverlayID> _includeOverlays;
|
||||||
|
QVector<EntityItemID> _ignoreAvatars;
|
||||||
|
QVector<EntityItemID> _includeAvatars;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_RayPick_h
|
253
interface/src/raypick/RayPickManager.cpp
Normal file
253
interface/src/raypick/RayPickManager.cpp
Normal file
|
@ -0,0 +1,253 @@
|
||||||
|
//
|
||||||
|
// RayPickManager.cpp
|
||||||
|
// interface/src/raypick
|
||||||
|
//
|
||||||
|
// Created by Sam Gondelman 7/11/2017
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
#include "RayPickManager.h"
|
||||||
|
|
||||||
|
#include "Application.h"
|
||||||
|
#include "EntityScriptingInterface.h"
|
||||||
|
#include "ui/overlays/Overlays.h"
|
||||||
|
#include "avatar/AvatarManager.h"
|
||||||
|
#include "scripting/HMDScriptingInterface.h"
|
||||||
|
#include "DependencyManager.h"
|
||||||
|
|
||||||
|
#include "JointRayPick.h"
|
||||||
|
#include "StaticRayPick.h"
|
||||||
|
#include "MouseRayPick.h"
|
||||||
|
|
||||||
|
bool RayPickManager::checkAndCompareCachedResults(QPair<glm::vec3, glm::vec3>& ray, RayPickCache& cache, RayPickResult& res, const RayPickFilter::Flags& mask) {
|
||||||
|
if (cache.contains(ray) && cache[ray].find(mask) != cache[ray].end()) {
|
||||||
|
if (cache[ray][mask].distance < res.distance) {
|
||||||
|
res = cache[ray][mask];
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RayPickManager::cacheResult(const bool intersects, const RayPickResult& resTemp, const RayPickFilter::Flags& mask, RayPickResult& res, QPair<glm::vec3, glm::vec3>& ray, RayPickCache& cache) {
|
||||||
|
if (intersects) {
|
||||||
|
cache[ray][mask] = resTemp;
|
||||||
|
if (resTemp.distance < res.distance) {
|
||||||
|
res = resTemp;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cache[ray][mask] = RayPickResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RayPickManager::update() {
|
||||||
|
RayPickCache results;
|
||||||
|
for (auto& uid : _rayPicks.keys()) {
|
||||||
|
std::shared_ptr<RayPick> rayPick = _rayPicks[uid];
|
||||||
|
if (!rayPick->isEnabled() || rayPick->getFilter().doesPickNothing() || rayPick->getMaxDistance() < 0.0f) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool valid;
|
||||||
|
PickRay ray = rayPick->getPickRay(valid);
|
||||||
|
|
||||||
|
if (!valid) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPair<glm::vec3, glm::vec3> rayKey = QPair<glm::vec3, glm::vec3>(ray.origin, ray.direction);
|
||||||
|
RayPickResult res;
|
||||||
|
|
||||||
|
if (rayPick->getFilter().doesPickEntities()) {
|
||||||
|
RayToEntityIntersectionResult entityRes;
|
||||||
|
bool fromCache = true;
|
||||||
|
bool invisible = rayPick->getFilter().doesPickInvisible();
|
||||||
|
bool noncollidable = rayPick->getFilter().doesPickNonCollidable();
|
||||||
|
RayPickFilter::Flags entityMask = rayPick->getFilter().getEntityFlags();
|
||||||
|
if (!checkAndCompareCachedResults(rayKey, results, res, entityMask)) {
|
||||||
|
entityRes = DependencyManager::get<EntityScriptingInterface>()->findRayIntersectionVector(ray, true, rayPick->getIncludeEntites(), rayPick->getIgnoreEntites(), !invisible, !noncollidable);
|
||||||
|
fromCache = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fromCache) {
|
||||||
|
cacheResult(entityRes.intersects, RayPickResult(IntersectionType::ENTITY, entityRes.entityID, entityRes.distance, entityRes.intersection, entityRes.surfaceNormal),
|
||||||
|
entityMask, res, rayKey, results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rayPick->getFilter().doesPickOverlays()) {
|
||||||
|
RayToOverlayIntersectionResult overlayRes;
|
||||||
|
bool fromCache = true;
|
||||||
|
bool invisible = rayPick->getFilter().doesPickInvisible();
|
||||||
|
bool noncollidable = rayPick->getFilter().doesPickNonCollidable();
|
||||||
|
RayPickFilter::Flags overlayMask = rayPick->getFilter().getOverlayFlags();
|
||||||
|
if (!checkAndCompareCachedResults(rayKey, results, res, overlayMask)) {
|
||||||
|
overlayRes = qApp->getOverlays().findRayIntersectionVector(ray, true, rayPick->getIncludeOverlays(), rayPick->getIgnoreOverlays(), !invisible, !noncollidable);
|
||||||
|
fromCache = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fromCache) {
|
||||||
|
cacheResult(overlayRes.intersects, RayPickResult(IntersectionType::OVERLAY, overlayRes.overlayID, overlayRes.distance, overlayRes.intersection, overlayRes.surfaceNormal),
|
||||||
|
overlayMask, res, rayKey, results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rayPick->getFilter().doesPickAvatars()) {
|
||||||
|
RayPickFilter::Flags avatarMask = rayPick->getFilter().getAvatarFlags();
|
||||||
|
if (!checkAndCompareCachedResults(rayKey, results, res, avatarMask)) {
|
||||||
|
RayToAvatarIntersectionResult avatarRes = DependencyManager::get<AvatarManager>()->findRayIntersectionVector(ray, rayPick->getIncludeAvatars(), rayPick->getIgnoreAvatars());
|
||||||
|
cacheResult(avatarRes.intersects, RayPickResult(IntersectionType::AVATAR, avatarRes.avatarID, avatarRes.distance, avatarRes.intersection), avatarMask, res, rayKey, results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can't intersect with HUD in desktop mode
|
||||||
|
if (rayPick->getFilter().doesPickHUD() && DependencyManager::get<HMDScriptingInterface>()->isHMDMode()) {
|
||||||
|
RayPickFilter::Flags hudMask = rayPick->getFilter().getHUDFlags();
|
||||||
|
if (!checkAndCompareCachedResults(rayKey, results, res, hudMask)) {
|
||||||
|
glm::vec3 hudRes = DependencyManager::get<HMDScriptingInterface>()->calculateRayUICollisionPoint(ray.origin, ray.direction);
|
||||||
|
cacheResult(true, RayPickResult(IntersectionType::HUD, 0, glm::distance(ray.origin, hudRes), hudRes), hudMask, res, rayKey, results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QWriteLocker lock(_rayPickLocks[uid].get());
|
||||||
|
if (rayPick->getMaxDistance() == 0.0f || (rayPick->getMaxDistance() > 0.0f && res.distance < rayPick->getMaxDistance())) {
|
||||||
|
rayPick->setRayPickResult(res);
|
||||||
|
} else {
|
||||||
|
rayPick->setRayPickResult(RayPickResult());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QWriteLocker containsLock(&_containsLock);
|
||||||
|
{
|
||||||
|
QWriteLocker lock(&_addLock);
|
||||||
|
while (!_rayPicksToAdd.empty()) {
|
||||||
|
std::pair<QUuid, std::shared_ptr<RayPick>> rayPickToAdd = _rayPicksToAdd.front();
|
||||||
|
_rayPicksToAdd.pop();
|
||||||
|
_rayPicks[rayPickToAdd.first] = rayPickToAdd.second;
|
||||||
|
_rayPickLocks[rayPickToAdd.first] = std::make_shared<QReadWriteLock>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
QWriteLocker lock(&_removeLock);
|
||||||
|
while (!_rayPicksToRemove.empty()) {
|
||||||
|
QUuid uid = _rayPicksToRemove.front();
|
||||||
|
_rayPicksToRemove.pop();
|
||||||
|
_rayPicks.remove(uid);
|
||||||
|
_rayPickLocks.remove(uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QUuid RayPickManager::createRayPick(const std::string& jointName, const glm::vec3& posOffset, const glm::vec3& dirOffset, const RayPickFilter& filter, const float maxDistance, const bool enabled) {
|
||||||
|
QWriteLocker lock(&_addLock);
|
||||||
|
QUuid id = QUuid::createUuid();
|
||||||
|
_rayPicksToAdd.push(std::pair<QUuid, std::shared_ptr<RayPick>>(id, std::make_shared<JointRayPick>(jointName, posOffset, dirOffset, filter, maxDistance, enabled)));
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
QUuid RayPickManager::createRayPick(const RayPickFilter& filter, const float maxDistance, const bool enabled) {
|
||||||
|
QWriteLocker lock(&_addLock);
|
||||||
|
QUuid id = QUuid::createUuid();
|
||||||
|
_rayPicksToAdd.push(std::pair<QUuid, std::shared_ptr<RayPick>>(id, std::make_shared<MouseRayPick>(filter, maxDistance, enabled)));
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
QUuid RayPickManager::createRayPick(const glm::vec3& position, const glm::vec3& direction, const RayPickFilter& filter, const float maxDistance, const bool enabled) {
|
||||||
|
QWriteLocker lock(&_addLock);
|
||||||
|
QUuid id = QUuid::createUuid();
|
||||||
|
_rayPicksToAdd.push(std::pair<QUuid, std::shared_ptr<RayPick>>(id, std::make_shared<StaticRayPick>(position, direction, filter, maxDistance, enabled)));
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RayPickManager::removeRayPick(const QUuid uid) {
|
||||||
|
QWriteLocker lock(&_removeLock);
|
||||||
|
_rayPicksToRemove.push(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RayPickManager::enableRayPick(const QUuid uid) {
|
||||||
|
QReadLocker containsLock(&_containsLock);
|
||||||
|
if (_rayPicks.contains(uid)) {
|
||||||
|
QWriteLocker rayPickLock(_rayPickLocks[uid].get());
|
||||||
|
_rayPicks[uid]->enable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RayPickManager::disableRayPick(const QUuid uid) {
|
||||||
|
QReadLocker containsLock(&_containsLock);
|
||||||
|
if (_rayPicks.contains(uid)) {
|
||||||
|
QWriteLocker rayPickLock(_rayPickLocks[uid].get());
|
||||||
|
_rayPicks[uid]->disable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const PickRay RayPickManager::getPickRay(const QUuid uid) {
|
||||||
|
QReadLocker containsLock(&_containsLock);
|
||||||
|
if (_rayPicks.contains(uid)) {
|
||||||
|
bool valid;
|
||||||
|
PickRay pickRay = _rayPicks[uid]->getPickRay(valid);
|
||||||
|
if (valid) {
|
||||||
|
return pickRay;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return PickRay();
|
||||||
|
}
|
||||||
|
|
||||||
|
const RayPickResult RayPickManager::getPrevRayPickResult(const QUuid uid) {
|
||||||
|
QReadLocker containsLock(&_containsLock);
|
||||||
|
if (_rayPicks.contains(uid)) {
|
||||||
|
QReadLocker lock(_rayPickLocks[uid].get());
|
||||||
|
return _rayPicks[uid]->getPrevRayPickResult();
|
||||||
|
}
|
||||||
|
return RayPickResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RayPickManager::setIgnoreEntities(QUuid uid, const QScriptValue& ignoreEntities) {
|
||||||
|
QReadLocker containsLock(&_containsLock);
|
||||||
|
if (_rayPicks.contains(uid)) {
|
||||||
|
QWriteLocker lock(_rayPickLocks[uid].get());
|
||||||
|
_rayPicks[uid]->setIgnoreEntities(ignoreEntities);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RayPickManager::setIncludeEntities(QUuid uid, const QScriptValue& includeEntities) {
|
||||||
|
QReadLocker containsLock(&_containsLock);
|
||||||
|
if (_rayPicks.contains(uid)) {
|
||||||
|
QWriteLocker lock(_rayPickLocks[uid].get());
|
||||||
|
_rayPicks[uid]->setIncludeEntities(includeEntities);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RayPickManager::setIgnoreOverlays(QUuid uid, const QScriptValue& ignoreOverlays) {
|
||||||
|
QReadLocker containsLock(&_containsLock);
|
||||||
|
if (_rayPicks.contains(uid)) {
|
||||||
|
QWriteLocker lock(_rayPickLocks[uid].get());
|
||||||
|
_rayPicks[uid]->setIgnoreOverlays(ignoreOverlays);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RayPickManager::setIncludeOverlays(QUuid uid, const QScriptValue& includeOverlays) {
|
||||||
|
QReadLocker containsLock(&_containsLock);
|
||||||
|
if (_rayPicks.contains(uid)) {
|
||||||
|
QWriteLocker lock(_rayPickLocks[uid].get());
|
||||||
|
_rayPicks[uid]->setIncludeOverlays(includeOverlays);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RayPickManager::setIgnoreAvatars(QUuid uid, const QScriptValue& ignoreAvatars) {
|
||||||
|
QReadLocker containsLock(&_containsLock);
|
||||||
|
if (_rayPicks.contains(uid)) {
|
||||||
|
QWriteLocker lock(_rayPickLocks[uid].get());
|
||||||
|
_rayPicks[uid]->setIgnoreAvatars(ignoreAvatars);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RayPickManager::setIncludeAvatars(QUuid uid, const QScriptValue& includeAvatars) {
|
||||||
|
QReadLocker containsLock(&_containsLock);
|
||||||
|
if (_rayPicks.contains(uid)) {
|
||||||
|
QWriteLocker lock(_rayPickLocks[uid].get());
|
||||||
|
_rayPicks[uid]->setIncludeAvatars(includeAvatars);
|
||||||
|
}
|
||||||
|
}
|
64
interface/src/raypick/RayPickManager.h
Normal file
64
interface/src/raypick/RayPickManager.h
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
//
|
||||||
|
// RayPickManager.h
|
||||||
|
// interface/src/raypick
|
||||||
|
//
|
||||||
|
// Created by Sam Gondelman 7/11/2017
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
#ifndef hifi_RayPickManager_h
|
||||||
|
#define hifi_RayPickManager_h
|
||||||
|
|
||||||
|
#include "RayPick.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <QReadWriteLock>
|
||||||
|
|
||||||
|
#include "RegisteredMetaTypes.h"
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
class RayPickResult;
|
||||||
|
|
||||||
|
class RayPickManager {
|
||||||
|
|
||||||
|
public:
|
||||||
|
void update();
|
||||||
|
const PickRay getPickRay(const QUuid uid);
|
||||||
|
|
||||||
|
QUuid createRayPick(const std::string& jointName, const glm::vec3& posOffset, const glm::vec3& dirOffset, const RayPickFilter& filter, const float maxDistance, const bool enabled);
|
||||||
|
QUuid createRayPick(const RayPickFilter& filter, const float maxDistance, const bool enabled);
|
||||||
|
QUuid createRayPick(const glm::vec3& position, const glm::vec3& direction, const RayPickFilter& filter, const float maxDistance, const bool enabled);
|
||||||
|
void removeRayPick(const QUuid uid);
|
||||||
|
void enableRayPick(const QUuid uid);
|
||||||
|
void disableRayPick(const QUuid uid);
|
||||||
|
const RayPickResult getPrevRayPickResult(const QUuid uid);
|
||||||
|
|
||||||
|
void setIgnoreEntities(QUuid uid, const QScriptValue& ignoreEntities);
|
||||||
|
void setIncludeEntities(QUuid uid, const QScriptValue& includeEntities);
|
||||||
|
void setIgnoreOverlays(QUuid uid, const QScriptValue& ignoreOverlays);
|
||||||
|
void setIncludeOverlays(QUuid uid, const QScriptValue& includeOverlays);
|
||||||
|
void setIgnoreAvatars(QUuid uid, const QScriptValue& ignoreAvatars);
|
||||||
|
void setIncludeAvatars(QUuid uid, const QScriptValue& includeAvatars);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QHash<QUuid, std::shared_ptr<RayPick>> _rayPicks;
|
||||||
|
QHash<QUuid, std::shared_ptr<QReadWriteLock>> _rayPickLocks;
|
||||||
|
QReadWriteLock _addLock;
|
||||||
|
std::queue<std::pair<QUuid, std::shared_ptr<RayPick>>> _rayPicksToAdd;
|
||||||
|
QReadWriteLock _removeLock;
|
||||||
|
std::queue<QUuid> _rayPicksToRemove;
|
||||||
|
QReadWriteLock _containsLock;
|
||||||
|
|
||||||
|
typedef QHash<QPair<glm::vec3, glm::vec3>, std::unordered_map<RayPickFilter::Flags, RayPickResult>> RayPickCache;
|
||||||
|
|
||||||
|
// Returns true if this ray exists in the cache, and if it does, update res if the cached result is closer
|
||||||
|
bool checkAndCompareCachedResults(QPair<glm::vec3, glm::vec3>& ray, RayPickCache& cache, RayPickResult& res, const RayPickFilter::Flags& mask);
|
||||||
|
void cacheResult(const bool intersects, const RayPickResult& resTemp, const RayPickFilter::Flags& mask, RayPickResult& res, QPair<glm::vec3, glm::vec3>& ray, RayPickCache& cache);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_RayPickManager_h
|
107
interface/src/raypick/RayPickScriptingInterface.cpp
Normal file
107
interface/src/raypick/RayPickScriptingInterface.cpp
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
//
|
||||||
|
// RayPickScriptingInterface.cpp
|
||||||
|
// interface/src/raypick
|
||||||
|
//
|
||||||
|
// Created by Sam Gondelman 8/15/2017
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "RayPickScriptingInterface.h"
|
||||||
|
|
||||||
|
#include <QVariant>
|
||||||
|
#include "GLMHelpers.h"
|
||||||
|
#include "Application.h"
|
||||||
|
|
||||||
|
QUuid RayPickScriptingInterface::createRayPick(const QVariant& properties) {
|
||||||
|
QVariantMap propMap = properties.toMap();
|
||||||
|
|
||||||
|
bool enabled = false;
|
||||||
|
if (propMap["enabled"].isValid()) {
|
||||||
|
enabled = propMap["enabled"].toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
RayPickFilter filter = RayPickFilter();
|
||||||
|
if (propMap["filter"].isValid()) {
|
||||||
|
filter = RayPickFilter(propMap["filter"].toUInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
float maxDistance = 0.0f;
|
||||||
|
if (propMap["maxDistance"].isValid()) {
|
||||||
|
maxDistance = propMap["maxDistance"].toFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (propMap["joint"].isValid()) {
|
||||||
|
std::string jointName = propMap["joint"].toString().toStdString();
|
||||||
|
|
||||||
|
if (jointName != "Mouse") {
|
||||||
|
// x = upward, y = forward, z = lateral
|
||||||
|
glm::vec3 posOffset = Vectors::ZERO;
|
||||||
|
if (propMap["posOffset"].isValid()) {
|
||||||
|
posOffset = vec3FromVariant(propMap["posOffset"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec3 dirOffset = Vectors::UP;
|
||||||
|
if (propMap["dirOffset"].isValid()) {
|
||||||
|
dirOffset = vec3FromVariant(propMap["dirOffset"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return qApp->getRayPickManager().createRayPick(jointName, posOffset, dirOffset, filter, maxDistance, enabled);
|
||||||
|
} else {
|
||||||
|
return qApp->getRayPickManager().createRayPick(filter, maxDistance, enabled);
|
||||||
|
}
|
||||||
|
} else if (propMap["position"].isValid()) {
|
||||||
|
glm::vec3 position = vec3FromVariant(propMap["position"]);
|
||||||
|
|
||||||
|
glm::vec3 direction = -Vectors::UP;
|
||||||
|
if (propMap["direction"].isValid()) {
|
||||||
|
direction = vec3FromVariant(propMap["direction"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return qApp->getRayPickManager().createRayPick(position, direction, filter, maxDistance, enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
return QUuid();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RayPickScriptingInterface::enableRayPick(QUuid uid) {
|
||||||
|
qApp->getRayPickManager().enableRayPick(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RayPickScriptingInterface::disableRayPick(QUuid uid) {
|
||||||
|
qApp->getRayPickManager().disableRayPick(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RayPickScriptingInterface::removeRayPick(QUuid uid) {
|
||||||
|
qApp->getRayPickManager().removeRayPick(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
RayPickResult RayPickScriptingInterface::getPrevRayPickResult(QUuid uid) {
|
||||||
|
return qApp->getRayPickManager().getPrevRayPickResult(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RayPickScriptingInterface::setIgnoreEntities(QUuid uid, const QScriptValue& ignoreEntities) {
|
||||||
|
qApp->getRayPickManager().setIgnoreEntities(uid, ignoreEntities);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RayPickScriptingInterface::setIncludeEntities(QUuid uid, const QScriptValue& includeEntities) {
|
||||||
|
qApp->getRayPickManager().setIncludeEntities(uid, includeEntities);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RayPickScriptingInterface::setIgnoreOverlays(QUuid uid, const QScriptValue& ignoreOverlays) {
|
||||||
|
qApp->getRayPickManager().setIgnoreOverlays(uid, ignoreOverlays);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RayPickScriptingInterface::setIncludeOverlays(QUuid uid, const QScriptValue& includeOverlays) {
|
||||||
|
qApp->getRayPickManager().setIncludeOverlays(uid, includeOverlays);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RayPickScriptingInterface::setIgnoreAvatars(QUuid uid, const QScriptValue& ignoreAvatars) {
|
||||||
|
qApp->getRayPickManager().setIgnoreAvatars(uid, ignoreAvatars);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RayPickScriptingInterface::setIncludeAvatars(QUuid uid, const QScriptValue& includeAvatars) {
|
||||||
|
qApp->getRayPickManager().setIncludeAvatars(uid, includeAvatars);
|
||||||
|
}
|
70
interface/src/raypick/RayPickScriptingInterface.h
Normal file
70
interface/src/raypick/RayPickScriptingInterface.h
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
//
|
||||||
|
// RayPickScriptingInterface.h
|
||||||
|
// interface/src/raypick
|
||||||
|
//
|
||||||
|
// Created by Sam Gondelman 8/15/2017
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
#ifndef hifi_RayPickScriptingInterface_h
|
||||||
|
#define hifi_RayPickScriptingInterface_h
|
||||||
|
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
|
||||||
|
#include "RegisteredMetaTypes.h"
|
||||||
|
#include "DependencyManager.h"
|
||||||
|
|
||||||
|
#include "RayPick.h"
|
||||||
|
|
||||||
|
class RayPickScriptingInterface : public QObject, public Dependency {
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(unsigned int PICK_NOTHING READ PICK_NOTHING CONSTANT)
|
||||||
|
Q_PROPERTY(unsigned int PICK_ENTITIES READ PICK_ENTITIES CONSTANT)
|
||||||
|
Q_PROPERTY(unsigned int PICK_OVERLAYS READ PICK_OVERLAYS CONSTANT)
|
||||||
|
Q_PROPERTY(unsigned int PICK_AVATARS READ PICK_AVATARS CONSTANT)
|
||||||
|
Q_PROPERTY(unsigned int PICK_HUD READ PICK_HUD CONSTANT)
|
||||||
|
Q_PROPERTY(unsigned int PICK_COURSE READ PICK_COURSE CONSTANT)
|
||||||
|
Q_PROPERTY(unsigned int PICK_INCLUDE_INVISIBLE READ PICK_INCLUDE_INVISIBLE CONSTANT)
|
||||||
|
Q_PROPERTY(unsigned int PICK_INCLUDE_NONCOLLIDABLE READ PICK_INCLUDE_NONCOLLIDABLE CONSTANT)
|
||||||
|
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_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)
|
||||||
|
SINGLETON_DEPENDENCY
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
Q_INVOKABLE QUuid createRayPick(const QVariant& properties);
|
||||||
|
Q_INVOKABLE void enableRayPick(QUuid uid);
|
||||||
|
Q_INVOKABLE void disableRayPick(QUuid uid);
|
||||||
|
Q_INVOKABLE void removeRayPick(QUuid uid);
|
||||||
|
Q_INVOKABLE RayPickResult getPrevRayPickResult(QUuid uid);
|
||||||
|
|
||||||
|
Q_INVOKABLE void setIgnoreEntities(QUuid uid, const QScriptValue& ignoreEntities);
|
||||||
|
Q_INVOKABLE void setIncludeEntities(QUuid uid, const QScriptValue& includeEntities);
|
||||||
|
Q_INVOKABLE void setIgnoreOverlays(QUuid uid, const QScriptValue& ignoreOverlays);
|
||||||
|
Q_INVOKABLE void setIncludeOverlays(QUuid uid, const QScriptValue& includeOverlays);
|
||||||
|
Q_INVOKABLE void setIgnoreAvatars(QUuid uid, const QScriptValue& ignoreAvatars);
|
||||||
|
Q_INVOKABLE void setIncludeAvatars(QUuid uid, const QScriptValue& includeAvatars);
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned int PICK_NOTHING() { return RayPickFilter::getBitMask(RayPickFilter::FlagBit::PICK_NOTHING); }
|
||||||
|
unsigned int PICK_ENTITIES() { return RayPickFilter::getBitMask(RayPickFilter::FlagBit::PICK_ENTITIES); }
|
||||||
|
unsigned int PICK_OVERLAYS() { return RayPickFilter::getBitMask(RayPickFilter::FlagBit::PICK_OVERLAYS); }
|
||||||
|
unsigned int PICK_AVATARS() { return RayPickFilter::getBitMask(RayPickFilter::FlagBit::PICK_AVATARS); }
|
||||||
|
unsigned int PICK_HUD() { return RayPickFilter::getBitMask(RayPickFilter::FlagBit::PICK_HUD); }
|
||||||
|
unsigned int PICK_COURSE() { return RayPickFilter::getBitMask(RayPickFilter::FlagBit::PICK_COURSE); }
|
||||||
|
unsigned int PICK_INCLUDE_INVISIBLE() { return RayPickFilter::getBitMask(RayPickFilter::FlagBit::PICK_INCLUDE_INVISIBLE); }
|
||||||
|
unsigned int PICK_INCLUDE_NONCOLLIDABLE() { return RayPickFilter::getBitMask(RayPickFilter::FlagBit::PICK_INCLUDE_NONCOLLIDABLE); }
|
||||||
|
unsigned int PICK_ALL_INTERSECTIONS() { return RayPickFilter::getBitMask(RayPickFilter::FlagBit::PICK_ALL_INTERSECTIONS); }
|
||||||
|
unsigned int INTERSECTED_NONE() { return IntersectionType::NONE; }
|
||||||
|
unsigned int INTERSECTED_ENTITY() { return IntersectionType::ENTITY; }
|
||||||
|
unsigned int INTERSECTED_OVERLAY() { return IntersectionType::OVERLAY; }
|
||||||
|
unsigned int INTERSECTED_AVATAR() { return IntersectionType::AVATAR; }
|
||||||
|
unsigned int INTERSECTED_HUD() { return IntersectionType::HUD; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_RayPickScriptingInterface_h
|
22
interface/src/raypick/StaticRayPick.cpp
Normal file
22
interface/src/raypick/StaticRayPick.cpp
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
//
|
||||||
|
// StaticRayPick.cpp
|
||||||
|
// interface/src/raypick
|
||||||
|
//
|
||||||
|
// Created by Sam Gondelman 7/11/2017
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
#include "StaticRayPick.h"
|
||||||
|
|
||||||
|
StaticRayPick::StaticRayPick(const glm::vec3& position, const glm::vec3& direction, const RayPickFilter& filter, const float maxDistance, const bool enabled) :
|
||||||
|
RayPick(filter, maxDistance, enabled),
|
||||||
|
_pickRay(position, direction)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const PickRay StaticRayPick::getPickRay(bool& valid) const {
|
||||||
|
valid = true;
|
||||||
|
return _pickRay;
|
||||||
|
}
|
28
interface/src/raypick/StaticRayPick.h
Normal file
28
interface/src/raypick/StaticRayPick.h
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
//
|
||||||
|
// StaticRayPick.h
|
||||||
|
// interface/src/raypick
|
||||||
|
//
|
||||||
|
// Created by Sam Gondelman 7/11/2017
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
#ifndef hifi_StaticRayPick_h
|
||||||
|
#define hifi_StaticRayPick_h
|
||||||
|
|
||||||
|
#include "RayPick.h"
|
||||||
|
|
||||||
|
class StaticRayPick : public RayPick {
|
||||||
|
|
||||||
|
public:
|
||||||
|
StaticRayPick(const glm::vec3& position, const glm::vec3& direction, const RayPickFilter& filter, const float maxDistance = 0.0f, const bool enabled = false);
|
||||||
|
|
||||||
|
const PickRay getPickRay(bool& valid) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
PickRay _pickRay;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_StaticRayPick_h
|
|
@ -82,6 +82,22 @@ bool HMDScriptingInterface::shouldShowHandControllers() const {
|
||||||
return _showHandControllersCount > 0;
|
return _showHandControllersCount > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HMDScriptingInterface::activateHMDHandMouse() {
|
||||||
|
QWriteLocker lock(&_hmdHandMouseLock);
|
||||||
|
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||||
|
offscreenUi->getDesktop()->setProperty("hmdHandMouseActive", true);
|
||||||
|
_hmdHandMouseCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HMDScriptingInterface::deactivateHMDHandMouse() {
|
||||||
|
QWriteLocker lock(&_hmdHandMouseLock);
|
||||||
|
_hmdHandMouseCount = std::max(_hmdHandMouseCount - 1, 0);
|
||||||
|
if (_hmdHandMouseCount == 0) {
|
||||||
|
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||||
|
offscreenUi->getDesktop()->setProperty("hmdHandMouseActive", false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void HMDScriptingInterface::closeTablet() {
|
void HMDScriptingInterface::closeTablet() {
|
||||||
_showTablet = false;
|
_showTablet = false;
|
||||||
}
|
}
|
||||||
|
@ -153,50 +169,6 @@ QString HMDScriptingInterface::preferredAudioOutput() const {
|
||||||
return qApp->getActiveDisplayPlugin()->getPreferredAudioOutDevice();
|
return qApp->getActiveDisplayPlugin()->getPreferredAudioOutDevice();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HMDScriptingInterface::setHandLasers(int hands, bool enabled, const glm::vec4& color, const glm::vec3& direction) {
|
|
||||||
if (QThread::currentThread() != thread()) {
|
|
||||||
bool result;
|
|
||||||
BLOCKING_INVOKE_METHOD(this, "setHandLasers", Q_RETURN_ARG(bool, result),
|
|
||||||
Q_ARG(int, hands), Q_ARG(bool, enabled), Q_ARG(glm::vec4, color), Q_ARG(glm::vec3, direction));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
|
||||||
offscreenUi->getDesktop()->setProperty("hmdHandMouseActive", enabled);
|
|
||||||
return qApp->getActiveDisplayPlugin()->setHandLaser(hands,
|
|
||||||
enabled ? DisplayPlugin::HandLaserMode::Overlay : DisplayPlugin::HandLaserMode::None,
|
|
||||||
color, direction);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HMDScriptingInterface::setExtraLaser(const glm::vec3& worldStart, bool enabled, const glm::vec4& color, const glm::vec3& direction) {
|
|
||||||
if (QThread::currentThread() != thread()) {
|
|
||||||
bool result;
|
|
||||||
BLOCKING_INVOKE_METHOD(this, "setExtraLaser", Q_RETURN_ARG(bool, result),
|
|
||||||
Q_ARG(glm::vec3, worldStart), Q_ARG(bool, enabled), Q_ARG(glm::vec4, color), Q_ARG(glm::vec3, direction));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
|
||||||
offscreenUi->getDesktop()->setProperty("hmdHandMouseActive", enabled);
|
|
||||||
|
|
||||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
|
||||||
auto sensorToWorld = myAvatar->getSensorToWorldMatrix();
|
|
||||||
auto worldToSensor = glm::inverse(sensorToWorld);
|
|
||||||
auto sensorStart = ::transformPoint(worldToSensor, worldStart);
|
|
||||||
auto sensorDirection = ::transformVectorFast(worldToSensor, direction);
|
|
||||||
|
|
||||||
return qApp->getActiveDisplayPlugin()->setExtraLaser(enabled ? DisplayPlugin::HandLaserMode::Overlay : DisplayPlugin::HandLaserMode::None,
|
|
||||||
color, sensorStart, sensorDirection);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HMDScriptingInterface::disableExtraLaser() {
|
|
||||||
setExtraLaser(vec3(0), false, vec4(0), vec3(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
void HMDScriptingInterface::disableHandLasers(int hands) {
|
|
||||||
setHandLasers(hands, false, vec4(0), vec3(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HMDScriptingInterface::suppressKeyboard() {
|
bool HMDScriptingInterface::suppressKeyboard() {
|
||||||
return qApp->getActiveDisplayPlugin()->suppressKeyboard();
|
return qApp->getActiveDisplayPlugin()->suppressKeyboard();
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ class QScriptEngine;
|
||||||
#include <DependencyManager.h>
|
#include <DependencyManager.h>
|
||||||
#include <display-plugins/AbstractHMDScriptingInterface.h>
|
#include <display-plugins/AbstractHMDScriptingInterface.h>
|
||||||
|
|
||||||
|
#include <QReadWriteLock>
|
||||||
|
|
||||||
class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Dependency {
|
class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Dependency {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -51,12 +52,8 @@ public:
|
||||||
Q_INVOKABLE void requestHideHandControllers();
|
Q_INVOKABLE void requestHideHandControllers();
|
||||||
Q_INVOKABLE bool shouldShowHandControllers() const;
|
Q_INVOKABLE bool shouldShowHandControllers() const;
|
||||||
|
|
||||||
Q_INVOKABLE bool setHandLasers(int hands, bool enabled, const glm::vec4& color, const glm::vec3& direction);
|
Q_INVOKABLE void activateHMDHandMouse();
|
||||||
Q_INVOKABLE void disableHandLasers(int hands);
|
Q_INVOKABLE void deactivateHMDHandMouse();
|
||||||
|
|
||||||
Q_INVOKABLE bool setExtraLaser(const glm::vec3& worldStart, bool enabled, const glm::vec4& color, const glm::vec3& direction);
|
|
||||||
Q_INVOKABLE void disableExtraLaser();
|
|
||||||
|
|
||||||
|
|
||||||
/// Suppress the activation of any on-screen keyboard so that a script operation will
|
/// Suppress the activation of any on-screen keyboard so that a script operation will
|
||||||
/// not be interrupted by a keyboard popup
|
/// not be interrupted by a keyboard popup
|
||||||
|
@ -119,6 +116,9 @@ private:
|
||||||
bool getHUDLookAtPosition3D(glm::vec3& result) const;
|
bool getHUDLookAtPosition3D(glm::vec3& result) const;
|
||||||
glm::mat4 getWorldHMDMatrix() const;
|
glm::mat4 getWorldHMDMatrix() const;
|
||||||
std::atomic<int> _showHandControllersCount { 0 };
|
std::atomic<int> _showHandControllersCount { 0 };
|
||||||
|
|
||||||
|
QReadWriteLock _hmdHandMouseLock;
|
||||||
|
int _hmdHandMouseCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_HMDScriptingInterface_h
|
#endif // hifi_HMDScriptingInterface_h
|
||||||
|
|
|
@ -264,6 +264,10 @@ void WindowScriptingInterface::showAssetServer(const QString& upload) {
|
||||||
QMetaObject::invokeMethod(qApp, "showAssetServerWidget", Qt::QueuedConnection, Q_ARG(QString, upload));
|
QMetaObject::invokeMethod(qApp, "showAssetServerWidget", Qt::QueuedConnection, Q_ARG(QString, upload));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString WindowScriptingInterface::checkVersion() {
|
||||||
|
return QCoreApplication::applicationVersion();
|
||||||
|
}
|
||||||
|
|
||||||
int WindowScriptingInterface::getInnerWidth() {
|
int WindowScriptingInterface::getInnerWidth() {
|
||||||
return qApp->getWindow()->geometry().width();
|
return qApp->getWindow()->geometry().width();
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,6 +58,7 @@ public slots:
|
||||||
QScriptValue save(const QString& title = "", const QString& directory = "", const QString& nameFilter = "");
|
QScriptValue save(const QString& title = "", const QString& directory = "", const QString& nameFilter = "");
|
||||||
QScriptValue browseAssets(const QString& title = "", const QString& directory = "", const QString& nameFilter = "");
|
QScriptValue browseAssets(const QString& title = "", const QString& directory = "", const QString& nameFilter = "");
|
||||||
void showAssetServer(const QString& upload = "");
|
void showAssetServer(const QString& upload = "");
|
||||||
|
QString checkVersion();
|
||||||
void copyToClipboard(const QString& text);
|
void copyToClipboard(const QString& text);
|
||||||
void takeSnapshot(bool notify = true, bool includeAnimated = false, float aspectRatio = 0.0f);
|
void takeSnapshot(bool notify = true, bool includeAnimated = false, float aspectRatio = 0.0f);
|
||||||
void takeSecondaryCameraSnapshot();
|
void takeSecondaryCameraSnapshot();
|
||||||
|
|
|
@ -267,7 +267,7 @@ const render::ShapeKey Circle3DOverlay::getShapeKey() {
|
||||||
if (getAlpha() != 1.0f) {
|
if (getAlpha() != 1.0f) {
|
||||||
builder.withTranslucent();
|
builder.withTranslucent();
|
||||||
}
|
}
|
||||||
if (!getIsSolid()) {
|
if (!getIsSolid() || shouldDrawHUDLayer()) {
|
||||||
builder.withUnlit().withDepthBias();
|
builder.withUnlit().withDepthBias();
|
||||||
}
|
}
|
||||||
return builder.build();
|
return builder.build();
|
||||||
|
|
|
@ -120,7 +120,7 @@ const render::ShapeKey Cube3DOverlay::getShapeKey() {
|
||||||
if (getAlpha() != 1.0f) {
|
if (getAlpha() != 1.0f) {
|
||||||
builder.withTranslucent();
|
builder.withTranslucent();
|
||||||
}
|
}
|
||||||
if (!getIsSolid()) {
|
if (!getIsSolid() || shouldDrawHUDLayer()) {
|
||||||
builder.withUnlit().withDepthBias();
|
builder.withUnlit().withDepthBias();
|
||||||
}
|
}
|
||||||
return builder.build();
|
return builder.build();
|
||||||
|
|
|
@ -123,7 +123,7 @@ void Image3DOverlay::render(RenderArgs* args) {
|
||||||
|
|
||||||
const render::ShapeKey Image3DOverlay::getShapeKey() {
|
const render::ShapeKey Image3DOverlay::getShapeKey() {
|
||||||
auto builder = render::ShapeKey::Builder().withoutCullFace().withDepthBias();
|
auto builder = render::ShapeKey::Builder().withoutCullFace().withDepthBias();
|
||||||
if (_emissive) {
|
if (_emissive || shouldDrawHUDLayer()) {
|
||||||
builder.withUnlit();
|
builder.withUnlit();
|
||||||
}
|
}
|
||||||
if (getAlpha() != 1.0f) {
|
if (getAlpha() != 1.0f) {
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
#include <NumericalConstants.h>
|
#include <NumericalConstants.h>
|
||||||
#include <RegisteredMetaTypes.h>
|
#include <RegisteredMetaTypes.h>
|
||||||
|
|
||||||
|
#include "Application.h"
|
||||||
|
|
||||||
static const xColor DEFAULT_OVERLAY_COLOR = { 255, 255, 255 };
|
static const xColor DEFAULT_OVERLAY_COLOR = { 255, 255, 255 };
|
||||||
static const float DEFAULT_ALPHA = 0.7f;
|
static const float DEFAULT_ALPHA = 0.7f;
|
||||||
|
|
||||||
|
@ -30,6 +32,7 @@ Overlay::Overlay() :
|
||||||
_colorPulse(0.0f),
|
_colorPulse(0.0f),
|
||||||
_color(DEFAULT_OVERLAY_COLOR),
|
_color(DEFAULT_OVERLAY_COLOR),
|
||||||
_visible(true),
|
_visible(true),
|
||||||
|
_drawHUDLayer(false),
|
||||||
_anchor(NO_ANCHOR)
|
_anchor(NO_ANCHOR)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -48,6 +51,7 @@ Overlay::Overlay(const Overlay* overlay) :
|
||||||
_colorPulse(overlay->_colorPulse),
|
_colorPulse(overlay->_colorPulse),
|
||||||
_color(overlay->_color),
|
_color(overlay->_color),
|
||||||
_visible(overlay->_visible),
|
_visible(overlay->_visible),
|
||||||
|
_drawHUDLayer(overlay->_drawHUDLayer),
|
||||||
_anchor(overlay->_anchor)
|
_anchor(overlay->_anchor)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -86,6 +90,11 @@ void Overlay::setProperties(const QVariantMap& properties) {
|
||||||
setColorPulse(properties["colorPulse"].toFloat());
|
setColorPulse(properties["colorPulse"].toFloat());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (properties["drawHUDLayer"].isValid()) {
|
||||||
|
bool drawHUDLayer = properties["drawHUDLayer"].toBool();
|
||||||
|
setDrawHUDLayer(drawHUDLayer);
|
||||||
|
}
|
||||||
|
|
||||||
if (properties["visible"].isValid()) {
|
if (properties["visible"].isValid()) {
|
||||||
bool visible = properties["visible"].toBool();
|
bool visible = properties["visible"].toBool();
|
||||||
setVisible(visible);
|
setVisible(visible);
|
||||||
|
@ -161,6 +170,12 @@ float Overlay::getAlpha() {
|
||||||
return (_alphaPulse >= 0.0f) ? _alpha * pulseLevel : _alpha * (1.0f - pulseLevel);
|
return (_alphaPulse >= 0.0f) ? _alpha * pulseLevel : _alpha * (1.0f - pulseLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Overlay::setDrawHUDLayer(bool drawHUDLayer) {
|
||||||
|
if (drawHUDLayer != _drawHUDLayer) {
|
||||||
|
qApp->getOverlays().setOverlayDrawHUDLayer(getOverlayID(), drawHUDLayer);
|
||||||
|
_drawHUDLayer = drawHUDLayer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// pulse travels from min to max, then max to min in one period.
|
// pulse travels from min to max, then max to min in one period.
|
||||||
float Overlay::updatePulse() {
|
float Overlay::updatePulse() {
|
||||||
|
|
|
@ -58,6 +58,7 @@ public:
|
||||||
virtual bool is3D() const = 0;
|
virtual bool is3D() const = 0;
|
||||||
bool isLoaded() { return _isLoaded; }
|
bool isLoaded() { return _isLoaded; }
|
||||||
bool getVisible() const { return _visible; }
|
bool getVisible() const { return _visible; }
|
||||||
|
bool shouldDrawHUDLayer() const { return _drawHUDLayer; }
|
||||||
xColor getColor();
|
xColor getColor();
|
||||||
float getAlpha();
|
float getAlpha();
|
||||||
Anchor getAnchor() const { return _anchor; }
|
Anchor getAnchor() const { return _anchor; }
|
||||||
|
@ -72,6 +73,7 @@ public:
|
||||||
|
|
||||||
// setters
|
// setters
|
||||||
void setVisible(bool visible) { _visible = visible; }
|
void setVisible(bool visible) { _visible = visible; }
|
||||||
|
void setDrawHUDLayer(bool drawHUDLayer);
|
||||||
void setColor(const xColor& color) { _color = color; }
|
void setColor(const xColor& color) { _color = color; }
|
||||||
void setAlpha(float alpha) { _alpha = alpha; }
|
void setAlpha(float alpha) { _alpha = alpha; }
|
||||||
void setAnchor(Anchor anchor) { _anchor = anchor; }
|
void setAnchor(Anchor anchor) { _anchor = anchor; }
|
||||||
|
@ -114,6 +116,7 @@ protected:
|
||||||
|
|
||||||
xColor _color;
|
xColor _color;
|
||||||
bool _visible; // should the overlay be drawn at all
|
bool _visible; // should the overlay be drawn at all
|
||||||
|
bool _drawHUDLayer; // should the overlay be drawn on the HUD layer
|
||||||
Anchor _anchor;
|
Anchor _anchor;
|
||||||
|
|
||||||
unsigned int _stackOrder { 0 };
|
unsigned int _stackOrder { 0 };
|
||||||
|
|
|
@ -37,20 +37,29 @@
|
||||||
#include "Web3DOverlay.h"
|
#include "Web3DOverlay.h"
|
||||||
#include <QtQuick/QQuickWindow>
|
#include <QtQuick/QQuickWindow>
|
||||||
|
|
||||||
|
#include "render/ShapePipeline.h"
|
||||||
|
|
||||||
Q_LOGGING_CATEGORY(trace_render_overlays, "trace.render.overlays")
|
Q_LOGGING_CATEGORY(trace_render_overlays, "trace.render.overlays")
|
||||||
|
|
||||||
|
extern void initOverlay3DPipelines(render::ShapePlumber& plumber, bool depthTest = false);
|
||||||
|
|
||||||
void Overlays::cleanupAllOverlays() {
|
void Overlays::cleanupAllOverlays() {
|
||||||
QMap<OverlayID, Overlay::Pointer> overlaysHUD;
|
QMap<OverlayID, Overlay::Pointer> overlaysHUD;
|
||||||
|
QMap<OverlayID, Overlay::Pointer> overlays3DHUD;
|
||||||
QMap<OverlayID, Overlay::Pointer> overlaysWorld;
|
QMap<OverlayID, Overlay::Pointer> overlaysWorld;
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&_mutex);
|
QMutexLocker locker(&_mutex);
|
||||||
overlaysHUD.swap(_overlaysHUD);
|
overlaysHUD.swap(_overlaysHUD);
|
||||||
|
overlays3DHUD.swap(_overlays3DHUD);
|
||||||
overlaysWorld.swap(_overlaysWorld);
|
overlaysWorld.swap(_overlaysWorld);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach(Overlay::Pointer overlay, overlaysHUD) {
|
foreach(Overlay::Pointer overlay, overlaysHUD) {
|
||||||
_overlaysToDelete.push_back(overlay);
|
_overlaysToDelete.push_back(overlay);
|
||||||
}
|
}
|
||||||
|
foreach(Overlay::Pointer overlay, overlays3DHUD) {
|
||||||
|
_overlaysToDelete.push_back(overlay);
|
||||||
|
}
|
||||||
foreach(Overlay::Pointer overlay, overlaysWorld) {
|
foreach(Overlay::Pointer overlay, overlaysWorld) {
|
||||||
_overlaysToDelete.push_back(overlay);
|
_overlaysToDelete.push_back(overlay);
|
||||||
}
|
}
|
||||||
|
@ -64,6 +73,8 @@ void Overlays::init() {
|
||||||
#if OVERLAY_PANELS
|
#if OVERLAY_PANELS
|
||||||
_scriptEngine = new QScriptEngine();
|
_scriptEngine = new QScriptEngine();
|
||||||
#endif
|
#endif
|
||||||
|
_shapePlumber = std::make_shared<render::ShapePlumber>();
|
||||||
|
initOverlay3DPipelines(*_shapePlumber, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Overlays::update(float deltatime) {
|
void Overlays::update(float deltatime) {
|
||||||
|
@ -72,6 +83,9 @@ void Overlays::update(float deltatime) {
|
||||||
foreach(const auto& thisOverlay, _overlaysHUD) {
|
foreach(const auto& thisOverlay, _overlaysHUD) {
|
||||||
thisOverlay->update(deltatime);
|
thisOverlay->update(deltatime);
|
||||||
}
|
}
|
||||||
|
foreach(const auto& thisOverlay, _overlays3DHUD) {
|
||||||
|
thisOverlay->update(deltatime);
|
||||||
|
}
|
||||||
foreach(const auto& thisOverlay, _overlaysWorld) {
|
foreach(const auto& thisOverlay, _overlaysWorld) {
|
||||||
thisOverlay->update(deltatime);
|
thisOverlay->update(deltatime);
|
||||||
}
|
}
|
||||||
|
@ -128,6 +142,23 @@ void Overlays::renderHUD(RenderArgs* renderArgs) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Overlays::render3DHUDOverlays(RenderArgs* renderArgs) {
|
||||||
|
PROFILE_RANGE(render_overlays, __FUNCTION__);
|
||||||
|
gpu::Batch& batch = *renderArgs->_batch;
|
||||||
|
|
||||||
|
auto textureCache = DependencyManager::get<TextureCache>();
|
||||||
|
|
||||||
|
QMutexLocker lock(&_mutex);
|
||||||
|
foreach(Overlay::Pointer thisOverlay, _overlays3DHUD) {
|
||||||
|
// Reset necessary batch pipeline settings between overlays
|
||||||
|
batch.setResourceTexture(0, textureCache->getWhiteTexture()); // FIXME - do we really need to do this??
|
||||||
|
batch.setModelTransform(Transform());
|
||||||
|
|
||||||
|
renderArgs->_shapePipeline = _shapePlumber->pickPipeline(renderArgs, thisOverlay->getShapeKey());
|
||||||
|
thisOverlay->render(renderArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Overlays::disable() {
|
void Overlays::disable() {
|
||||||
_enabled = false;
|
_enabled = false;
|
||||||
}
|
}
|
||||||
|
@ -142,8 +173,9 @@ Overlay::Pointer Overlays::getOverlay(OverlayID id) const {
|
||||||
QMutexLocker locker(&_mutex);
|
QMutexLocker locker(&_mutex);
|
||||||
if (_overlaysHUD.contains(id)) {
|
if (_overlaysHUD.contains(id)) {
|
||||||
return _overlaysHUD[id];
|
return _overlaysHUD[id];
|
||||||
}
|
} else if (_overlays3DHUD.contains(id)) {
|
||||||
if (_overlaysWorld.contains(id)) {
|
return _overlays3DHUD[id];
|
||||||
|
} else if (_overlaysWorld.contains(id)) {
|
||||||
return _overlaysWorld[id];
|
return _overlaysWorld[id];
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -200,7 +232,7 @@ OverlayID Overlays::addOverlay(const Overlay::Pointer& overlay) {
|
||||||
OverlayID thisID = OverlayID(QUuid::createUuid());
|
OverlayID thisID = OverlayID(QUuid::createUuid());
|
||||||
overlay->setOverlayID(thisID);
|
overlay->setOverlayID(thisID);
|
||||||
overlay->setStackOrder(_stackOrder++);
|
overlay->setStackOrder(_stackOrder++);
|
||||||
if (overlay->is3D()) {
|
if (overlay->is3D() && !overlay->shouldDrawHUDLayer()) {
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&_mutex);
|
QMutexLocker locker(&_mutex);
|
||||||
_overlaysWorld[thisID] = overlay;
|
_overlaysWorld[thisID] = overlay;
|
||||||
|
@ -210,6 +242,9 @@ OverlayID Overlays::addOverlay(const Overlay::Pointer& overlay) {
|
||||||
render::Transaction transaction;
|
render::Transaction transaction;
|
||||||
overlay->addToScene(overlay, scene, transaction);
|
overlay->addToScene(overlay, scene, transaction);
|
||||||
scene->enqueueTransaction(transaction);
|
scene->enqueueTransaction(transaction);
|
||||||
|
} else if (overlay->is3D() && overlay->shouldDrawHUDLayer()) {
|
||||||
|
QMutexLocker locker(&_mutex);
|
||||||
|
_overlays3DHUD[thisID] = overlay;
|
||||||
} else {
|
} else {
|
||||||
QMutexLocker locker(&_mutex);
|
QMutexLocker locker(&_mutex);
|
||||||
_overlaysHUD[thisID] = overlay;
|
_overlaysHUD[thisID] = overlay;
|
||||||
|
@ -218,6 +253,28 @@ OverlayID Overlays::addOverlay(const Overlay::Pointer& overlay) {
|
||||||
return thisID;
|
return thisID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Overlays::setOverlayDrawHUDLayer(const OverlayID& id, const bool drawHUDLayer) {
|
||||||
|
QMutexLocker locker(&_mutex);
|
||||||
|
if (drawHUDLayer && _overlaysWorld.contains(id)) {
|
||||||
|
std::shared_ptr<Overlay> overlay = _overlaysWorld.take(id);
|
||||||
|
render::ScenePointer scene = qApp->getMain3DScene();
|
||||||
|
render::Transaction transaction;
|
||||||
|
auto itemID = overlay->getRenderItemID();
|
||||||
|
if (render::Item::isValidID(itemID)) {
|
||||||
|
overlay->removeFromScene(overlay, scene, transaction);
|
||||||
|
scene->enqueueTransaction(transaction);
|
||||||
|
}
|
||||||
|
_overlays3DHUD[id] = overlay;
|
||||||
|
} else if (!drawHUDLayer && _overlays3DHUD.contains(id)) {
|
||||||
|
std::shared_ptr<Overlay> overlay = _overlays3DHUD.take(id);
|
||||||
|
render::ScenePointer scene = qApp->getMain3DScene();
|
||||||
|
render::Transaction transaction;
|
||||||
|
overlay->addToScene(overlay, scene, transaction);
|
||||||
|
scene->enqueueTransaction(transaction);
|
||||||
|
_overlaysWorld[id] = overlay;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
OverlayID Overlays::cloneOverlay(OverlayID id) {
|
OverlayID Overlays::cloneOverlay(OverlayID id) {
|
||||||
if (QThread::currentThread() != thread()) {
|
if (QThread::currentThread() != thread()) {
|
||||||
OverlayID result;
|
OverlayID result;
|
||||||
|
@ -294,6 +351,8 @@ void Overlays::deleteOverlay(OverlayID id) {
|
||||||
QMutexLocker locker(&_mutex);
|
QMutexLocker locker(&_mutex);
|
||||||
if (_overlaysHUD.contains(id)) {
|
if (_overlaysHUD.contains(id)) {
|
||||||
overlayToDelete = _overlaysHUD.take(id);
|
overlayToDelete = _overlaysHUD.take(id);
|
||||||
|
} else if (_overlays3DHUD.contains(id)) {
|
||||||
|
overlayToDelete = _overlays3DHUD.take(id);
|
||||||
} else if (_overlaysWorld.contains(id)) {
|
} else if (_overlaysWorld.contains(id)) {
|
||||||
overlayToDelete = _overlaysWorld.take(id);
|
overlayToDelete = _overlaysWorld.take(id);
|
||||||
} else {
|
} else {
|
||||||
|
@ -475,15 +534,15 @@ RayToOverlayIntersectionResult Overlays::findRayIntersection(const PickRay& ray,
|
||||||
const QVector<OverlayID> overlaysToInclude = qVectorOverlayIDFromScriptValue(overlayIDsToInclude);
|
const QVector<OverlayID> overlaysToInclude = qVectorOverlayIDFromScriptValue(overlayIDsToInclude);
|
||||||
const QVector<OverlayID> overlaysToDiscard = qVectorOverlayIDFromScriptValue(overlayIDsToDiscard);
|
const QVector<OverlayID> overlaysToDiscard = qVectorOverlayIDFromScriptValue(overlayIDsToDiscard);
|
||||||
|
|
||||||
return findRayIntersectionInternal(ray, precisionPicking,
|
return findRayIntersectionVector(ray, precisionPicking,
|
||||||
overlaysToInclude, overlaysToDiscard, visibleOnly, collidableOnly);
|
overlaysToInclude, overlaysToDiscard, visibleOnly, collidableOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
RayToOverlayIntersectionResult Overlays::findRayIntersectionInternal(const PickRay& ray, bool precisionPicking,
|
RayToOverlayIntersectionResult Overlays::findRayIntersectionVector(const PickRay& ray, bool precisionPicking,
|
||||||
const QVector<OverlayID>& overlaysToInclude,
|
const QVector<OverlayID>& overlaysToInclude,
|
||||||
const QVector<OverlayID>& overlaysToDiscard,
|
const QVector<OverlayID>& overlaysToDiscard,
|
||||||
bool visibleOnly, bool collidableOnly) {
|
bool visibleOnly, bool collidableOnly) {
|
||||||
float bestDistance = std::numeric_limits<float>::max();
|
float bestDistance = std::numeric_limits<float>::max();
|
||||||
bool bestIsFront = false;
|
bool bestIsFront = false;
|
||||||
|
|
||||||
|
@ -702,7 +761,7 @@ bool Overlays::isAddedOverlay(OverlayID id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QMutexLocker locker(&_mutex);
|
QMutexLocker locker(&_mutex);
|
||||||
return _overlaysHUD.contains(id) || _overlaysWorld.contains(id);
|
return _overlaysHUD.contains(id) || _overlays3DHUD.contains(id) || _overlaysWorld.contains(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Overlays::sendMousePressOnOverlay(const OverlayID& overlayID, const PointerEvent& event) {
|
void Overlays::sendMousePressOnOverlay(const OverlayID& overlayID, const PointerEvent& event) {
|
||||||
|
@ -849,21 +908,21 @@ RayToOverlayIntersectionResult Overlays::findRayIntersectionForMouseEvent(PickRa
|
||||||
|
|
||||||
// first priority is tablet screen
|
// first priority is tablet screen
|
||||||
overlaysToInclude << qApp->getTabletScreenID();
|
overlaysToInclude << qApp->getTabletScreenID();
|
||||||
rayPickResult = findRayIntersectionInternal(ray, true, overlaysToInclude, overlaysToDiscard);
|
rayPickResult = findRayIntersectionVector(ray, true, overlaysToInclude, overlaysToDiscard);
|
||||||
if (rayPickResult.intersects) {
|
if (rayPickResult.intersects) {
|
||||||
return rayPickResult;
|
return rayPickResult;
|
||||||
}
|
}
|
||||||
// then tablet home button
|
// then tablet home button
|
||||||
overlaysToInclude.clear();
|
overlaysToInclude.clear();
|
||||||
overlaysToInclude << qApp->getTabletHomeButtonID();
|
overlaysToInclude << qApp->getTabletHomeButtonID();
|
||||||
rayPickResult = findRayIntersectionInternal(ray, true, overlaysToInclude, overlaysToDiscard);
|
rayPickResult = findRayIntersectionVector(ray, true, overlaysToInclude, overlaysToDiscard);
|
||||||
if (rayPickResult.intersects) {
|
if (rayPickResult.intersects) {
|
||||||
return rayPickResult;
|
return rayPickResult;
|
||||||
}
|
}
|
||||||
// then tablet frame
|
// then tablet frame
|
||||||
overlaysToInclude.clear();
|
overlaysToInclude.clear();
|
||||||
overlaysToInclude << OverlayID(qApp->getTabletFrameID());
|
overlaysToInclude << OverlayID(qApp->getTabletFrameID());
|
||||||
rayPickResult = findRayIntersectionInternal(ray, true, overlaysToInclude, overlaysToDiscard);
|
rayPickResult = findRayIntersectionVector(ray, true, overlaysToInclude, overlaysToDiscard);
|
||||||
if (rayPickResult.intersects) {
|
if (rayPickResult.intersects) {
|
||||||
return rayPickResult;
|
return rayPickResult;
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,6 +90,7 @@ public:
|
||||||
void init();
|
void init();
|
||||||
void update(float deltatime);
|
void update(float deltatime);
|
||||||
void renderHUD(RenderArgs* renderArgs);
|
void renderHUD(RenderArgs* renderArgs);
|
||||||
|
void render3DHUDOverlays(RenderArgs* renderArgs);
|
||||||
void disable();
|
void disable();
|
||||||
void enable();
|
void enable();
|
||||||
|
|
||||||
|
@ -102,6 +103,8 @@ public:
|
||||||
OverlayID addOverlay(Overlay* overlay) { return addOverlay(Overlay::Pointer(overlay)); }
|
OverlayID addOverlay(Overlay* overlay) { return addOverlay(Overlay::Pointer(overlay)); }
|
||||||
OverlayID addOverlay(const Overlay::Pointer& overlay);
|
OverlayID addOverlay(const Overlay::Pointer& overlay);
|
||||||
|
|
||||||
|
void setOverlayDrawHUDLayer(const OverlayID& id, const bool drawHUDLayer);
|
||||||
|
|
||||||
bool mousePressEvent(QMouseEvent* event);
|
bool mousePressEvent(QMouseEvent* event);
|
||||||
bool mouseDoublePressEvent(QMouseEvent* event);
|
bool mouseDoublePressEvent(QMouseEvent* event);
|
||||||
bool mouseReleaseEvent(QMouseEvent* event);
|
bool mouseReleaseEvent(QMouseEvent* event);
|
||||||
|
@ -213,6 +216,12 @@ public slots:
|
||||||
bool visibleOnly = false,
|
bool visibleOnly = false,
|
||||||
bool collidableOnly = false);
|
bool collidableOnly = false);
|
||||||
|
|
||||||
|
// Same as above but with QVectors
|
||||||
|
RayToOverlayIntersectionResult findRayIntersectionVector(const PickRay& ray, bool precisionPicking,
|
||||||
|
const QVector<OverlayID>& overlaysToInclude,
|
||||||
|
const QVector<OverlayID>& overlaysToDiscard,
|
||||||
|
bool visibleOnly = false, bool collidableOnly = false);
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* Return a list of 3d overlays with bounding boxes that touch the given sphere
|
* Return a list of 3d overlays with bounding boxes that touch the given sphere
|
||||||
*
|
*
|
||||||
|
@ -325,7 +334,11 @@ private:
|
||||||
|
|
||||||
mutable QMutex _mutex { QMutex::Recursive };
|
mutable QMutex _mutex { QMutex::Recursive };
|
||||||
QMap<OverlayID, Overlay::Pointer> _overlaysHUD;
|
QMap<OverlayID, Overlay::Pointer> _overlaysHUD;
|
||||||
|
QMap<OverlayID, Overlay::Pointer> _overlays3DHUD;
|
||||||
QMap<OverlayID, Overlay::Pointer> _overlaysWorld;
|
QMap<OverlayID, Overlay::Pointer> _overlaysWorld;
|
||||||
|
|
||||||
|
render::ShapePlumberPointer _shapePlumber;
|
||||||
|
|
||||||
#if OVERLAY_PANELS
|
#if OVERLAY_PANELS
|
||||||
QMap<OverlayID, OverlayPanel::Pointer> _panels;
|
QMap<OverlayID, OverlayPanel::Pointer> _panels;
|
||||||
#endif
|
#endif
|
||||||
|
@ -343,10 +356,6 @@ private:
|
||||||
OverlayID _currentClickingOnOverlayID { UNKNOWN_OVERLAY_ID };
|
OverlayID _currentClickingOnOverlayID { UNKNOWN_OVERLAY_ID };
|
||||||
OverlayID _currentHoverOverOverlayID { UNKNOWN_OVERLAY_ID };
|
OverlayID _currentHoverOverOverlayID { UNKNOWN_OVERLAY_ID };
|
||||||
|
|
||||||
Q_INVOKABLE RayToOverlayIntersectionResult findRayIntersectionInternal(const PickRay& ray, bool precisionPicking,
|
|
||||||
const QVector<OverlayID>& overlaysToInclude,
|
|
||||||
const QVector<OverlayID>& overlaysToDiscard,
|
|
||||||
bool visibleOnly = false, bool collidableOnly = false);
|
|
||||||
RayToOverlayIntersectionResult findRayIntersectionForMouseEvent(PickRay ray);
|
RayToOverlayIntersectionResult findRayIntersectionForMouseEvent(PickRay ray);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -37,9 +37,6 @@ namespace render {
|
||||||
if (std::static_pointer_cast<Base3DOverlay>(overlay)->getDrawInFront()) {
|
if (std::static_pointer_cast<Base3DOverlay>(overlay)->getDrawInFront()) {
|
||||||
builder.withLayered();
|
builder.withLayered();
|
||||||
}
|
}
|
||||||
if (!std::static_pointer_cast<Base3DOverlay>(overlay)->isAA()) {
|
|
||||||
builder.withLayered();
|
|
||||||
}
|
|
||||||
if (overlay->getAlphaPulse() != 0.0f || overlay->getAlpha() != 1.0f) {
|
if (overlay->getAlphaPulse() != 0.0f || overlay->getAlpha() != 1.0f) {
|
||||||
builder.withTransparent();
|
builder.withTransparent();
|
||||||
}
|
}
|
||||||
|
@ -53,21 +50,17 @@ namespace render {
|
||||||
}
|
}
|
||||||
template <> int payloadGetLayer(const Overlay::Pointer& overlay) {
|
template <> int payloadGetLayer(const Overlay::Pointer& overlay) {
|
||||||
// Magic number while we are defining the layering mechanism:
|
// Magic number while we are defining the layering mechanism:
|
||||||
const int LAYER_NO_AA = 3;
|
|
||||||
const int LAYER_2D = 2;
|
const int LAYER_2D = 2;
|
||||||
const int LAYER_3D_FRONT = 1;
|
const int LAYER_3D_FRONT = 1;
|
||||||
const int LAYER_3D = 0;
|
const int LAYER_3D = 0;
|
||||||
|
|
||||||
if (overlay->is3D()) {
|
if (overlay->is3D()) {
|
||||||
auto overlay3D = std::dynamic_pointer_cast<Base3DOverlay>(overlay);
|
auto overlay3D = std::dynamic_pointer_cast<Base3DOverlay>(overlay);
|
||||||
if (overlay3D->isAA())
|
if (overlay3D->getDrawInFront()) {
|
||||||
if (overlay3D->getDrawInFront()) {
|
return LAYER_3D_FRONT;
|
||||||
return LAYER_3D_FRONT;
|
} else {
|
||||||
} else {
|
return LAYER_3D;
|
||||||
return LAYER_3D;
|
}
|
||||||
}
|
|
||||||
else
|
|
||||||
return LAYER_NO_AA;
|
|
||||||
} else {
|
} else {
|
||||||
return LAYER_2D;
|
return LAYER_2D;
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,7 @@ const render::ShapeKey Shape3DOverlay::getShapeKey() {
|
||||||
if (getAlpha() != 1.0f) {
|
if (getAlpha() != 1.0f) {
|
||||||
builder.withTranslucent();
|
builder.withTranslucent();
|
||||||
}
|
}
|
||||||
if (!getIsSolid()) {
|
if (!getIsSolid() || shouldDrawHUDLayer()) {
|
||||||
builder.withUnlit().withDepthBias();
|
builder.withUnlit().withDepthBias();
|
||||||
}
|
}
|
||||||
return builder.build();
|
return builder.build();
|
||||||
|
|
|
@ -62,7 +62,7 @@ const render::ShapeKey Sphere3DOverlay::getShapeKey() {
|
||||||
if (getAlpha() != 1.0f) {
|
if (getAlpha() != 1.0f) {
|
||||||
builder.withTranslucent();
|
builder.withTranslucent();
|
||||||
}
|
}
|
||||||
if (!getIsSolid()) {
|
if (!getIsSolid() || shouldDrawHUDLayer()) {
|
||||||
builder.withUnlit().withDepthBias();
|
builder.withUnlit().withDepthBias();
|
||||||
}
|
}
|
||||||
return builder.build();
|
return builder.build();
|
||||||
|
|
|
@ -349,10 +349,9 @@ bool CompositorHelper::calculateRayUICollisionPoint(const glm::vec3& position, c
|
||||||
auto relativePosition = vec3(relativePosition4) / relativePosition4.w;
|
auto relativePosition = vec3(relativePosition4) / relativePosition4.w;
|
||||||
auto relativeDirection = glm::inverse(glm::quat_cast(UITransform)) * direction;
|
auto relativeDirection = glm::inverse(glm::quat_cast(UITransform)) * direction;
|
||||||
|
|
||||||
float uiRadius = _hmdUIRadius; // * myAvatar->getUniformScale(); // FIXME - how do we want to handle avatar scale
|
const float UI_RADIUS = 1.0f; // * myAvatar->getUniformScale(); // FIXME - how do we want to handle avatar scale
|
||||||
|
|
||||||
float instersectionDistance;
|
float instersectionDistance;
|
||||||
if (raySphereIntersect(relativeDirection, relativePosition, uiRadius, &instersectionDistance)){
|
if (raySphereIntersect(relativeDirection, relativePosition, UI_RADIUS, &instersectionDistance)){
|
||||||
result = position + glm::normalize(direction) * instersectionDistance;
|
result = position + glm::normalize(direction) * instersectionDistance;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,8 +111,6 @@ public:
|
||||||
void setDisplayPlugin(const DisplayPluginPointer& displayPlugin) { _currentDisplayPlugin = displayPlugin; }
|
void setDisplayPlugin(const DisplayPluginPointer& displayPlugin) { _currentDisplayPlugin = displayPlugin; }
|
||||||
void setFrameInfo(uint32_t frame, const glm::mat4& camera) { _currentCamera = camera; }
|
void setFrameInfo(uint32_t frame, const glm::mat4& camera) { _currentCamera = camera; }
|
||||||
|
|
||||||
float getHmdUiRadius() const { return _hmdUIRadius; }
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void allowMouseCaptureChanged();
|
void allowMouseCaptureChanged();
|
||||||
void alphaChanged();
|
void alphaChanged();
|
||||||
|
@ -142,7 +140,6 @@ private:
|
||||||
float _textureAspectRatio { VIRTUAL_UI_ASPECT_RATIO };
|
float _textureAspectRatio { VIRTUAL_UI_ASPECT_RATIO };
|
||||||
|
|
||||||
float _alpha { 1.0f };
|
float _alpha { 1.0f };
|
||||||
float _hmdUIRadius { 1.0f };
|
|
||||||
|
|
||||||
int _previousBorderWidth { -1 };
|
int _previousBorderWidth { -1 };
|
||||||
int _previousBorderHeight { -1 };
|
int _previousBorderHeight { -1 };
|
||||||
|
|
|
@ -626,6 +626,12 @@ void OpenGLDisplayPlugin::compositeLayers() {
|
||||||
compositeScene();
|
compositeScene();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear the depth framebuffer after drawing the scene so that the HUD elements can depth test against each other
|
||||||
|
render([&](gpu::Batch& batch) {
|
||||||
|
batch.enableStereo(false);
|
||||||
|
batch.setFramebuffer(_compositeFramebuffer);
|
||||||
|
batch.clearDepthFramebuffer((float) UINT32_MAX);
|
||||||
|
});
|
||||||
|
|
||||||
#ifdef HIFI_ENABLE_NSIGHT_DEBUG
|
#ifdef HIFI_ENABLE_NSIGHT_DEBUG
|
||||||
if (false) // do not compositeoverlay if running nsight debug
|
if (false) // do not compositeoverlay if running nsight debug
|
||||||
|
@ -635,16 +641,33 @@ void OpenGLDisplayPlugin::compositeLayers() {
|
||||||
compositeOverlay();
|
compositeOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto compositorHelper = DependencyManager::get<CompositorHelper>();
|
// Only render HUD layer 3D overlays in HMD mode
|
||||||
if (compositorHelper->getReticleVisible()) {
|
if (isHmd()) {
|
||||||
PROFILE_RANGE_EX(render_detail, "compositePointer", 0xff0077ff, (uint64_t)presentCount())
|
PROFILE_RANGE_EX(render_detail, "compositeHUDOverlays", 0xff0077ff, (uint64_t)presentCount())
|
||||||
compositePointer();
|
render([&](gpu::Batch& batch) {
|
||||||
|
batch.enableStereo(false);
|
||||||
|
batch.setFramebuffer(_compositeFramebuffer);
|
||||||
|
});
|
||||||
|
_gpuContext->executeBatch(_currentFrame->postCompositeBatch);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
PROFILE_RANGE_EX(render_detail, "compositeExtra", 0xff0077ff, (uint64_t)presentCount())
|
PROFILE_RANGE_EX(render_detail, "compositeExtra", 0xff0077ff, (uint64_t)presentCount())
|
||||||
compositeExtra();
|
compositeExtra();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear the depth buffer again and draw the pointer last so it's on top of everything
|
||||||
|
render([&](gpu::Batch& batch) {
|
||||||
|
batch.enableStereo(false);
|
||||||
|
batch.setFramebuffer(_compositeFramebuffer);
|
||||||
|
batch.clearDepthFramebuffer((float) UINT32_MAX);
|
||||||
|
});
|
||||||
|
|
||||||
|
auto compositorHelper = DependencyManager::get<CompositorHelper>();
|
||||||
|
if (compositorHelper->getReticleVisible()) {
|
||||||
|
PROFILE_RANGE_EX(render_detail, "compositePointer", 0xff0077ff, (uint64_t)presentCount())
|
||||||
|
compositePointer();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLDisplayPlugin::internalPresent() {
|
void OpenGLDisplayPlugin::internalPresent() {
|
||||||
|
@ -864,7 +887,8 @@ OpenGLDisplayPlugin::~OpenGLDisplayPlugin() {
|
||||||
void OpenGLDisplayPlugin::updateCompositeFramebuffer() {
|
void OpenGLDisplayPlugin::updateCompositeFramebuffer() {
|
||||||
auto renderSize = getRecommendedRenderSize();
|
auto renderSize = getRecommendedRenderSize();
|
||||||
if (!_compositeFramebuffer || _compositeFramebuffer->getSize() != renderSize) {
|
if (!_compositeFramebuffer || _compositeFramebuffer->getSize() != renderSize) {
|
||||||
_compositeFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("OpenGLDisplayPlugin::composite", gpu::Element::COLOR_RGBA_32, renderSize.x, renderSize.y));
|
auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL);
|
||||||
|
_compositeFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("OpenGLDisplayPlugin::composite", gpu::Element::COLOR_RGBA_32, depthFormat, renderSize.x, renderSize.y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,16 +35,7 @@ bool DebugHmdDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
|
||||||
//_currentRenderFrameInfo.presentPose = _currentRenderFrameInfo.renderPose;
|
//_currentRenderFrameInfo.presentPose = _currentRenderFrameInfo.renderPose;
|
||||||
|
|
||||||
withNonPresentThreadLock([&] {
|
withNonPresentThreadLock([&] {
|
||||||
_uiModelTransform = DependencyManager::get<CompositorHelper>()->getModelTransform();
|
|
||||||
_frameInfos[frameIndex] = _currentRenderFrameInfo;
|
_frameInfos[frameIndex] = _currentRenderFrameInfo;
|
||||||
|
|
||||||
_handPoses[0] = glm::translate(mat4(), vec3(0.3f * cosf(secTimestampNow() * 3.0f), -0.3f * sinf(secTimestampNow() * 5.0f), 0.0f));
|
|
||||||
_handLasers[0].color = vec4(1, 0, 0, 1);
|
|
||||||
_handLasers[0].mode = HandLaserMode::Overlay;
|
|
||||||
|
|
||||||
_handPoses[1] = glm::translate(mat4(), vec3(0.3f * sinf(secTimestampNow() * 3.0f), -0.3f * cosf(secTimestampNow() * 5.0f), 0.0f));
|
|
||||||
_handLasers[1].color = vec4(0, 1, 1, 1);
|
|
||||||
_handLasers[1].mode = HandLaserMode::Overlay;
|
|
||||||
});
|
});
|
||||||
return Parent::beginFrameRender(frameIndex);
|
return Parent::beginFrameRender(frameIndex);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,9 +32,6 @@
|
||||||
|
|
||||||
#include "../Logging.h"
|
#include "../Logging.h"
|
||||||
#include "../CompositorHelper.h"
|
#include "../CompositorHelper.h"
|
||||||
#include <../render-utils/shaders/render-utils/glowLine_vert.h>
|
|
||||||
#include <../render-utils/shaders/render-utils/glowLine_frag.h>
|
|
||||||
|
|
||||||
|
|
||||||
static const QString MONO_PREVIEW = "Mono Preview";
|
static const QString MONO_PREVIEW = "Mono Preview";
|
||||||
static const QString DISABLE_PREVIEW = "Disable Preview";
|
static const QString DISABLE_PREVIEW = "Disable Preview";
|
||||||
|
@ -45,17 +42,10 @@ static const bool DEFAULT_MONO_VIEW = true;
|
||||||
static const bool DEFAULT_DISABLE_PREVIEW = false;
|
static const bool DEFAULT_DISABLE_PREVIEW = false;
|
||||||
#endif
|
#endif
|
||||||
static const glm::mat4 IDENTITY_MATRIX;
|
static const glm::mat4 IDENTITY_MATRIX;
|
||||||
static const size_t NUMBER_OF_HANDS = 2;
|
|
||||||
|
|
||||||
//#define LIVE_SHADER_RELOAD 1
|
//#define LIVE_SHADER_RELOAD 1
|
||||||
extern glm::vec3 getPoint(float yaw, float pitch);
|
extern glm::vec3 getPoint(float yaw, float pitch);
|
||||||
|
|
||||||
struct HandLaserData {
|
|
||||||
vec4 p1;
|
|
||||||
vec4 p2;
|
|
||||||
vec4 color;
|
|
||||||
};
|
|
||||||
|
|
||||||
static QString readFile(const QString& filename) {
|
static QString readFile(const QString& filename) {
|
||||||
QFile file(filename);
|
QFile file(filename);
|
||||||
file.open(QFile::Text | QFile::ReadOnly);
|
file.open(QFile::Text | QFile::ReadOnly);
|
||||||
|
@ -118,31 +108,9 @@ void HmdDisplayPlugin::internalDeactivate() {
|
||||||
Parent::internalDeactivate();
|
Parent::internalDeactivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
static const int32_t LINE_DATA_SLOT = 1;
|
|
||||||
|
|
||||||
void HmdDisplayPlugin::customizeContext() {
|
void HmdDisplayPlugin::customizeContext() {
|
||||||
Parent::customizeContext();
|
Parent::customizeContext();
|
||||||
_overlayRenderer.build();
|
_overlayRenderer.build();
|
||||||
|
|
||||||
{
|
|
||||||
auto state = std::make_shared<gpu::State>();
|
|
||||||
auto VS = gpu::Shader::createVertex(std::string(glowLine_vert));
|
|
||||||
auto PS = gpu::Shader::createPixel(std::string(glowLine_frag));
|
|
||||||
auto program = gpu::Shader::createProgram(VS, PS);
|
|
||||||
state->setCullMode(gpu::State::CULL_NONE);
|
|
||||||
state->setDepthTest(true, false, gpu::LESS_EQUAL);
|
|
||||||
state->setBlendFunction(true,
|
|
||||||
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
|
||||||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
|
||||||
|
|
||||||
gpu::Shader::BindingSet bindings;
|
|
||||||
bindings.insert({ "lineData", LINE_DATA_SLOT });;
|
|
||||||
gpu::Shader::makeProgram(*program, bindings);
|
|
||||||
_glowLinePipeline = gpu::Pipeline::create(program, state);
|
|
||||||
_handLaserUniforms = std::array<gpu::BufferPointer, 2>{ { std::make_shared<gpu::Buffer>(), std::make_shared<gpu::Buffer>() } };
|
|
||||||
_extraLaserUniforms = std::make_shared<gpu::Buffer>();
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HmdDisplayPlugin::uncustomizeContext() {
|
void HmdDisplayPlugin::uncustomizeContext() {
|
||||||
|
@ -157,10 +125,6 @@ void HmdDisplayPlugin::uncustomizeContext() {
|
||||||
});
|
});
|
||||||
_overlayRenderer = OverlayRenderer();
|
_overlayRenderer = OverlayRenderer();
|
||||||
_previewTexture.reset();
|
_previewTexture.reset();
|
||||||
_handLaserUniforms[0].reset();
|
|
||||||
_handLaserUniforms[1].reset();
|
|
||||||
_extraLaserUniforms.reset();
|
|
||||||
_glowLinePipeline.reset();
|
|
||||||
Parent::uncustomizeContext();
|
Parent::uncustomizeContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -383,132 +347,13 @@ void HmdDisplayPlugin::updateFrameData() {
|
||||||
getGLBackend()->setCameraCorrection(correction);
|
getGLBackend()->setCameraCorrection(correction);
|
||||||
}
|
}
|
||||||
|
|
||||||
withPresentThreadLock([&] {
|
|
||||||
_presentHandLasers = _handLasers;
|
|
||||||
_presentHandPoses = _handPoses;
|
|
||||||
_presentUiModelTransform = _uiModelTransform;
|
|
||||||
|
|
||||||
_presentExtraLaser = _extraLaser;
|
|
||||||
_presentExtraLaserStart = _extraLaserStart;
|
|
||||||
});
|
|
||||||
|
|
||||||
auto compositorHelper = DependencyManager::get<CompositorHelper>();
|
auto compositorHelper = DependencyManager::get<CompositorHelper>();
|
||||||
glm::mat4 modelMat = compositorHelper->getModelTransform().getMatrix();
|
glm::mat4 modelMat = compositorHelper->getModelTransform().getMatrix();
|
||||||
static const float OUT_OF_BOUNDS = -1;
|
|
||||||
std::array<vec2, NUMBER_OF_HANDS> handGlowPoints { { vec2(OUT_OF_BOUNDS), vec2(OUT_OF_BOUNDS) } };
|
|
||||||
vec2 extraGlowPoint(OUT_OF_BOUNDS);
|
|
||||||
|
|
||||||
float uiRadius = compositorHelper->getHmdUiRadius();
|
|
||||||
|
|
||||||
// compute the glow point interesections
|
|
||||||
for (size_t i = 0; i < NUMBER_OF_HANDS; ++i) {
|
|
||||||
if (_presentHandPoses[i] == IDENTITY_MATRIX) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const auto& handLaser = _presentHandLasers[i];
|
|
||||||
if (!handLaser.valid()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const vec3& laserDirection = handLaser.direction;
|
|
||||||
mat4 model = _presentHandPoses[i];
|
|
||||||
vec3 castStart = vec3(model[3]);
|
|
||||||
vec3 castDirection = glm::quat_cast(model) * laserDirection;
|
|
||||||
|
|
||||||
// this offset needs to match GRAB_POINT_SPHERE_OFFSET in scripts/system/libraries/controllers.js:19
|
|
||||||
static const vec3 GRAB_POINT_SPHERE_OFFSET(0.04f, 0.13f, 0.039f); // x = upward, y = forward, z = lateral
|
|
||||||
|
|
||||||
// swizzle grab point so that (x = upward, y = lateral, z = forward)
|
|
||||||
vec3 grabPointOffset = glm::vec3(GRAB_POINT_SPHERE_OFFSET.x, GRAB_POINT_SPHERE_OFFSET.z, -GRAB_POINT_SPHERE_OFFSET.y);
|
|
||||||
if (i == 0) {
|
|
||||||
grabPointOffset.x *= -1.0f; // this changes between left and right hands
|
|
||||||
}
|
|
||||||
castStart += glm::quat_cast(model) * grabPointOffset;
|
|
||||||
|
|
||||||
// Find the intersection of the laser with he UI and use it to scale the model matrix
|
|
||||||
float distance;
|
|
||||||
if (!glm::intersectRaySphere(castStart, castDirection,
|
|
||||||
_presentUiModelTransform.getTranslation(), uiRadius * uiRadius, distance)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
_presentHandLaserPoints[i].first = castStart;
|
|
||||||
_presentHandLaserPoints[i].second = _presentHandLaserPoints[i].first + (castDirection * distance);
|
|
||||||
|
|
||||||
vec3 intersectionPosition = castStart + (castDirection * distance) - _presentUiModelTransform.getTranslation();
|
|
||||||
intersectionPosition = glm::inverse(_presentUiModelTransform.getRotation()) * intersectionPosition;
|
|
||||||
|
|
||||||
// Take the interesection normal and convert it to a texture coordinate
|
|
||||||
vec2 yawPitch;
|
|
||||||
{
|
|
||||||
vec2 xdir = glm::normalize(vec2(intersectionPosition.x, -intersectionPosition.z));
|
|
||||||
yawPitch.x = glm::atan(xdir.x, xdir.y);
|
|
||||||
yawPitch.y = (acosf(intersectionPosition.y) * -1.0f) + (float)M_PI_2;
|
|
||||||
}
|
|
||||||
vec2 halfFov = CompositorHelper::VIRTUAL_UI_TARGET_FOV / 2.0f;
|
|
||||||
|
|
||||||
// Are we out of range
|
|
||||||
if (glm::any(glm::greaterThan(glm::abs(yawPitch), halfFov))) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
yawPitch /= CompositorHelper::VIRTUAL_UI_TARGET_FOV;
|
|
||||||
yawPitch += 0.5f;
|
|
||||||
handGlowPoints[i] = yawPitch;
|
|
||||||
}
|
|
||||||
|
|
||||||
// compute the glow point interesections
|
|
||||||
if (_presentExtraLaser.valid()) {
|
|
||||||
const vec3& laserDirection = _presentExtraLaser.direction;
|
|
||||||
vec3 castStart = _presentExtraLaserStart;
|
|
||||||
vec3 castDirection = laserDirection;
|
|
||||||
|
|
||||||
// Find the intersection of the laser with he UI and use it to scale the model matrix
|
|
||||||
float distance;
|
|
||||||
if (glm::intersectRaySphere(castStart, castDirection,
|
|
||||||
_presentUiModelTransform.getTranslation(), uiRadius * uiRadius, distance)) {
|
|
||||||
|
|
||||||
|
|
||||||
_presentExtraLaserPoints.first = castStart;
|
|
||||||
_presentExtraLaserPoints.second = _presentExtraLaserPoints.first + (castDirection * distance);
|
|
||||||
|
|
||||||
vec3 intersectionPosition = castStart + (castDirection * distance) - _presentUiModelTransform.getTranslation();
|
|
||||||
intersectionPosition = glm::inverse(_presentUiModelTransform.getRotation()) * intersectionPosition;
|
|
||||||
|
|
||||||
// Take the interesection normal and convert it to a texture coordinate
|
|
||||||
vec2 yawPitch;
|
|
||||||
{
|
|
||||||
vec2 xdir = glm::normalize(vec2(intersectionPosition.x, -intersectionPosition.z));
|
|
||||||
yawPitch.x = glm::atan(xdir.x, xdir.y);
|
|
||||||
yawPitch.y = (acosf(intersectionPosition.y) * -1.0f) + (float)M_PI_2;
|
|
||||||
}
|
|
||||||
vec2 halfFov = CompositorHelper::VIRTUAL_UI_TARGET_FOV / 2.0f;
|
|
||||||
|
|
||||||
// Are we out of range
|
|
||||||
if (!glm::any(glm::greaterThan(glm::abs(yawPitch), halfFov))) {
|
|
||||||
yawPitch /= CompositorHelper::VIRTUAL_UI_TARGET_FOV;
|
|
||||||
yawPitch += 0.5f;
|
|
||||||
extraGlowPoint = yawPitch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
for_each_eye([&](Eye eye) {
|
for_each_eye([&](Eye eye) {
|
||||||
auto modelView = glm::inverse(_currentPresentFrameInfo.presentPose * getEyeToHeadTransform(eye)) * modelMat;
|
auto modelView = glm::inverse(_currentPresentFrameInfo.presentPose * getEyeToHeadTransform(eye)) * modelMat;
|
||||||
_overlayRenderer.mvps[eye] = _eyeProjections[eye] * modelView;
|
_overlayRenderer.mvps[eye] = _eyeProjections[eye] * modelView;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Setup the uniforms
|
|
||||||
{
|
|
||||||
auto& uniforms = _overlayRenderer.uniforms;
|
|
||||||
uniforms.alpha = _compositeOverlayAlpha;
|
|
||||||
uniforms.glowPoints = vec4(handGlowPoints[0], handGlowPoints[1]);
|
|
||||||
uniforms.glowColors[0] = _presentHandLasers[0].color;
|
|
||||||
uniforms.glowColors[1] = _presentHandLasers[1].color;
|
|
||||||
uniforms.extraGlowPoint = extraGlowPoint;
|
|
||||||
uniforms.extraGlowColor = _presentExtraLaser.color;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HmdDisplayPlugin::OverlayRenderer::build() {
|
void HmdDisplayPlugin::OverlayRenderer::build() {
|
||||||
|
@ -573,8 +418,8 @@ void HmdDisplayPlugin::OverlayRenderer::build() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HmdDisplayPlugin::OverlayRenderer::updatePipeline() {
|
void HmdDisplayPlugin::OverlayRenderer::updatePipeline() {
|
||||||
static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_ui_glow.vert";
|
static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_ui.vert";
|
||||||
static const QString fsFile = PathUtils::resourcesPath() + "/shaders/hmd_ui_glow.frag";
|
static const QString fsFile = PathUtils::resourcesPath() + "/shaders/hmd_ui.frag";
|
||||||
|
|
||||||
#if LIVE_SHADER_RELOAD
|
#if LIVE_SHADER_RELOAD
|
||||||
static qint64 vsBuiltAge = 0;
|
static qint64 vsBuiltAge = 0;
|
||||||
|
@ -598,7 +443,7 @@ void HmdDisplayPlugin::OverlayRenderer::updatePipeline() {
|
||||||
this->uniformsLocation = program->getUniformBuffers().findLocation("overlayBuffer");
|
this->uniformsLocation = program->getUniformBuffers().findLocation("overlayBuffer");
|
||||||
|
|
||||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||||
state->setDepthTest(gpu::State::DepthTest(false));
|
state->setDepthTest(gpu::State::DepthTest(true, true, gpu::LESS_EQUAL));
|
||||||
state->setBlendFunction(true,
|
state->setBlendFunction(true,
|
||||||
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
||||||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||||
|
@ -665,84 +510,6 @@ void HmdDisplayPlugin::compositeOverlay() {
|
||||||
_overlayRenderer.render(*this);
|
_overlayRenderer.render(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HmdDisplayPlugin::setHandLaser(uint32_t hands, HandLaserMode mode, const vec4& color, const vec3& direction) {
|
|
||||||
HandLaserInfo info;
|
|
||||||
info.mode = mode;
|
|
||||||
info.color = color;
|
|
||||||
info.direction = direction;
|
|
||||||
withNonPresentThreadLock([&] {
|
|
||||||
if (hands & Hand::LeftHand) {
|
|
||||||
_handLasers[0] = info;
|
|
||||||
}
|
|
||||||
if (hands & Hand::RightHand) {
|
|
||||||
_handLasers[1] = info;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// FIXME defer to a child class plugin to determine if hand lasers are actually
|
|
||||||
// available based on the presence or absence of hand controllers
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HmdDisplayPlugin::setExtraLaser(HandLaserMode mode, const vec4& color, const glm::vec3& sensorSpaceStart, const vec3& sensorSpaceDirection) {
|
|
||||||
HandLaserInfo info;
|
|
||||||
info.mode = mode;
|
|
||||||
info.color = color;
|
|
||||||
info.direction = sensorSpaceDirection;
|
|
||||||
withNonPresentThreadLock([&] {
|
|
||||||
_extraLaser = info;
|
|
||||||
_extraLaserStart = sensorSpaceStart;
|
|
||||||
});
|
|
||||||
|
|
||||||
// FIXME defer to a child class plugin to determine if hand lasers are actually
|
|
||||||
// available based on the presence or absence of hand controllers
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void HmdDisplayPlugin::compositeExtra() {
|
|
||||||
// If neither hand laser is activated, exit
|
|
||||||
if (!_presentHandLasers[0].valid() && !_presentHandLasers[1].valid() && !_presentExtraLaser.valid()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_presentHandPoses[0] == IDENTITY_MATRIX && _presentHandPoses[1] == IDENTITY_MATRIX && !_presentExtraLaser.valid()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
render([&](gpu::Batch& batch) {
|
|
||||||
batch.setFramebuffer(_compositeFramebuffer);
|
|
||||||
batch.setModelTransform(Transform());
|
|
||||||
batch.setViewportTransform(ivec4(uvec2(0), _renderTargetSize));
|
|
||||||
batch.setViewTransform(_currentPresentFrameInfo.presentPose, false);
|
|
||||||
// Compile the shaders
|
|
||||||
batch.setPipeline(_glowLinePipeline);
|
|
||||||
|
|
||||||
|
|
||||||
bilateral::for_each_side([&](bilateral::Side side){
|
|
||||||
auto index = bilateral::index(side);
|
|
||||||
if (_presentHandPoses[index] == IDENTITY_MATRIX) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const auto& laser = _presentHandLasers[index];
|
|
||||||
if (laser.valid()) {
|
|
||||||
const auto& points = _presentHandLaserPoints[index];
|
|
||||||
_handLaserUniforms[index]->resize(sizeof(HandLaserData));
|
|
||||||
_handLaserUniforms[index]->setSubData(0, HandLaserData { vec4(points.first, 1.0f), vec4(points.second, 1.0f), _handLasers[index].color });
|
|
||||||
batch.setUniformBuffer(LINE_DATA_SLOT, _handLaserUniforms[index]);
|
|
||||||
batch.draw(gpu::TRIANGLE_STRIP, 4, 0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (_presentExtraLaser.valid()) {
|
|
||||||
const auto& points = _presentExtraLaserPoints;
|
|
||||||
_extraLaserUniforms->resize(sizeof(HandLaserData));
|
|
||||||
_extraLaserUniforms->setSubData(0, HandLaserData { vec4(points.first, 1.0f), vec4(points.second, 1.0f), _presentExtraLaser.color });
|
|
||||||
batch.setUniformBuffer(LINE_DATA_SLOT, _extraLaserUniforms);
|
|
||||||
batch.draw(gpu::TRIANGLE_STRIP, 4, 0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
HmdDisplayPlugin::~HmdDisplayPlugin() {
|
HmdDisplayPlugin::~HmdDisplayPlugin() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,9 +37,6 @@ public:
|
||||||
|
|
||||||
virtual glm::mat4 getHeadPose() const override;
|
virtual glm::mat4 getHeadPose() const override;
|
||||||
|
|
||||||
bool setHandLaser(uint32_t hands, HandLaserMode mode, const vec4& color, const vec3& direction) override;
|
|
||||||
bool setExtraLaser(HandLaserMode mode, const vec4& color, const glm::vec3& sensorSpaceStart, const vec3& sensorSpaceDirection) override;
|
|
||||||
|
|
||||||
bool wantVsync() const override {
|
bool wantVsync() const override {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -63,33 +60,6 @@ protected:
|
||||||
void customizeContext() override;
|
void customizeContext() override;
|
||||||
void uncustomizeContext() override;
|
void uncustomizeContext() override;
|
||||||
void updateFrameData() override;
|
void updateFrameData() override;
|
||||||
void compositeExtra() override;
|
|
||||||
|
|
||||||
struct HandLaserInfo {
|
|
||||||
HandLaserMode mode { HandLaserMode::None };
|
|
||||||
vec4 color { 1.0f };
|
|
||||||
vec3 direction { 0, 0, -1 };
|
|
||||||
|
|
||||||
// Is this hand laser info suitable for drawing?
|
|
||||||
bool valid() const {
|
|
||||||
return (mode != HandLaserMode::None && color.a > 0.0f && direction != vec3());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Transform _uiModelTransform;
|
|
||||||
std::array<HandLaserInfo, 2> _handLasers;
|
|
||||||
std::array<mat4, 2> _handPoses;
|
|
||||||
|
|
||||||
Transform _presentUiModelTransform;
|
|
||||||
std::array<HandLaserInfo, 2> _presentHandLasers;
|
|
||||||
std::array<mat4, 2> _presentHandPoses;
|
|
||||||
std::array<std::pair<vec3, vec3>, 2> _presentHandLaserPoints;
|
|
||||||
|
|
||||||
HandLaserInfo _extraLaser;
|
|
||||||
HandLaserInfo _presentExtraLaser;
|
|
||||||
vec3 _extraLaserStart;
|
|
||||||
vec3 _presentExtraLaserStart;
|
|
||||||
std::pair<vec3, vec3> _presentExtraLaserPoints;
|
|
||||||
|
|
||||||
std::array<mat4, 2> _eyeOffsets;
|
std::array<mat4, 2> _eyeOffsets;
|
||||||
std::array<mat4, 2> _eyeProjections;
|
std::array<mat4, 2> _eyeProjections;
|
||||||
|
@ -120,9 +90,6 @@ private:
|
||||||
bool _disablePreviewItemAdded { false };
|
bool _disablePreviewItemAdded { false };
|
||||||
bool _monoPreview { true };
|
bool _monoPreview { true };
|
||||||
bool _clearPreviewFlag { false };
|
bool _clearPreviewFlag { false };
|
||||||
std::array<gpu::BufferPointer, 2> _handLaserUniforms;
|
|
||||||
gpu::BufferPointer _extraLaserUniforms;
|
|
||||||
gpu::PipelinePointer _glowLinePipeline;
|
|
||||||
gpu::TexturePointer _previewTexture;
|
gpu::TexturePointer _previewTexture;
|
||||||
glm::vec2 _lastWindowSize;
|
glm::vec2 _lastWindowSize;
|
||||||
|
|
||||||
|
@ -140,14 +107,7 @@ private:
|
||||||
|
|
||||||
struct Uniforms {
|
struct Uniforms {
|
||||||
mat4 mvp;
|
mat4 mvp;
|
||||||
vec4 glowPoints { -1 };
|
|
||||||
vec4 glowColors[2];
|
|
||||||
vec2 resolution { CompositorHelper::VIRTUAL_SCREEN_SIZE };
|
|
||||||
float radius { 0.005f };
|
|
||||||
float alpha { 1.0f };
|
float alpha { 1.0f };
|
||||||
|
|
||||||
vec4 extraGlowColor;
|
|
||||||
vec2 extraGlowPoint { -1 };
|
|
||||||
} uniforms;
|
} uniforms;
|
||||||
|
|
||||||
struct Vertex {
|
struct Vertex {
|
||||||
|
|
|
@ -262,7 +262,7 @@ public:
|
||||||
glm::vec3 getRegistrationPoint() const; /// registration point as ratio of entity
|
glm::vec3 getRegistrationPoint() const; /// registration point as ratio of entity
|
||||||
|
|
||||||
/// registration point as ratio of entity
|
/// registration point as ratio of entity
|
||||||
void setRegistrationPoint(const glm::vec3& value);
|
virtual void setRegistrationPoint(const glm::vec3& value);
|
||||||
|
|
||||||
bool hasAngularVelocity() const { return getAngularVelocity() != ENTITY_ITEM_ZERO_VEC3; }
|
bool hasAngularVelocity() const { return getAngularVelocity() != ENTITY_ITEM_ZERO_VEC3; }
|
||||||
bool hasLocalAngularVelocity() const { return getLocalAngularVelocity() != ENTITY_ITEM_ZERO_VEC3; }
|
bool hasLocalAngularVelocity() const { return getLocalAngularVelocity() != ENTITY_ITEM_ZERO_VEC3; }
|
||||||
|
|
|
@ -679,11 +679,17 @@ QVector<QUuid> EntityScriptingInterface::findEntitiesByType(const QString entity
|
||||||
|
|
||||||
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking,
|
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking,
|
||||||
const QScriptValue& entityIdsToInclude, const QScriptValue& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) {
|
const QScriptValue& entityIdsToInclude, const QScriptValue& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) {
|
||||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
|
||||||
|
|
||||||
QVector<EntityItemID> entitiesToInclude = qVectorEntityItemIDFromScriptValue(entityIdsToInclude);
|
QVector<EntityItemID> entitiesToInclude = qVectorEntityItemIDFromScriptValue(entityIdsToInclude);
|
||||||
QVector<EntityItemID> entitiesToDiscard = qVectorEntityItemIDFromScriptValue(entityIdsToDiscard);
|
QVector<EntityItemID> entitiesToDiscard = qVectorEntityItemIDFromScriptValue(entityIdsToDiscard);
|
||||||
return findRayIntersectionWorker(ray, Octree::Lock, precisionPicking, entitiesToInclude, entitiesToDiscard, visibleOnly, collidableOnly);
|
|
||||||
|
return findRayIntersectionVector(ray, precisionPicking, entitiesToInclude, entitiesToDiscard, visibleOnly, collidableOnly);
|
||||||
|
}
|
||||||
|
|
||||||
|
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionVector(const PickRay& ray, bool precisionPicking,
|
||||||
|
const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) {
|
||||||
|
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||||
|
|
||||||
|
return findRayIntersectionWorker(ray, Octree::Lock, precisionPicking, entityIdsToInclude, entityIdsToDiscard, visibleOnly, collidableOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME - we should remove this API and encourage all users to use findRayIntersection() instead. We've changed
|
// FIXME - we should remove this API and encourage all users to use findRayIntersection() instead. We've changed
|
||||||
|
|
|
@ -229,6 +229,11 @@ public slots:
|
||||||
const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = QScriptValue(),
|
const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = QScriptValue(),
|
||||||
bool visibleOnly = false, bool collidableOnly = false);
|
bool visibleOnly = false, bool collidableOnly = false);
|
||||||
|
|
||||||
|
/// Same as above but with QVectors
|
||||||
|
RayToEntityIntersectionResult findRayIntersectionVector(const PickRay& ray, bool precisionPicking,
|
||||||
|
const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIdsToDiscard,
|
||||||
|
bool visibleOnly, bool collidableOnly);
|
||||||
|
|
||||||
/// If the scripting context has visible entities, this will determine a ray intersection, and will block in
|
/// If the scripting context has visible entities, this will determine a ray intersection, and will block in
|
||||||
/// order to return an accurate result
|
/// order to return an accurate result
|
||||||
Q_INVOKABLE RayToEntityIntersectionResult findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking = false, const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = QScriptValue());
|
Q_INVOKABLE RayToEntityIntersectionResult findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking = false, const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = QScriptValue());
|
||||||
|
|
|
@ -92,13 +92,12 @@ bool PolyLineEntityItem::appendPoint(const glm::vec3& point) {
|
||||||
qCDebug(entities) << "MAX POINTS REACHED!";
|
qCDebug(entities) << "MAX POINTS REACHED!";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
glm::vec3 halfBox = getDimensions() * 0.5f;
|
|
||||||
if ((point.x < -halfBox.x || point.x > halfBox.x) || (point.y < -halfBox.y || point.y > halfBox.y) || (point.z < -halfBox.z || point.z > halfBox.z)) {
|
|
||||||
qCDebug(entities) << "Point is outside entity's bounding box";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
_points << point;
|
_points << point;
|
||||||
_pointsChanged = true;
|
_pointsChanged = true;
|
||||||
|
|
||||||
|
calculateScaleAndRegistrationPoint();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,23 +140,69 @@ bool PolyLineEntityItem::setLinePoints(const QVector<glm::vec3>& points) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < points.size(); i++) {
|
|
||||||
glm::vec3 point = points.at(i);
|
|
||||||
glm::vec3 halfBox = getDimensions() * 0.5f;
|
|
||||||
if ((point.x < -halfBox.x || point.x > halfBox.x) ||
|
|
||||||
(point.y < -halfBox.y || point.y > halfBox.y) ||
|
|
||||||
(point.z < -halfBox.z || point.z > halfBox.z)) {
|
|
||||||
qCDebug(entities) << "Point is outside entity's bounding box";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_points = points;
|
_points = points;
|
||||||
|
|
||||||
|
calculateScaleAndRegistrationPoint();
|
||||||
|
|
||||||
result = true;
|
result = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PolyLineEntityItem::calculateScaleAndRegistrationPoint() {
|
||||||
|
glm::vec3 high(0.0f, 0.0f, 0.0f);
|
||||||
|
glm::vec3 low(0.0f, 0.0f, 0.0f);
|
||||||
|
for (int i = 0; i < _points.size(); i++) {
|
||||||
|
glm::vec3 point = _points.at(i);
|
||||||
|
|
||||||
|
if (point.x > high.x) {
|
||||||
|
high.x = point.x;
|
||||||
|
} else if (point.x < low.x) {
|
||||||
|
low.x = point.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (point.y > high.y) {
|
||||||
|
high.y = point.y;
|
||||||
|
} else if (point.y < low.y) {
|
||||||
|
low.y = point.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (point.z > high.z) {
|
||||||
|
high.z = point.z;
|
||||||
|
} else if (point.z < low.z) {
|
||||||
|
low.z = point.z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const float EPSILON = 0.0001f;
|
||||||
|
if (_points.size() > 1) {
|
||||||
|
// if all the points in the Polyline are at the same place in space, use default dimension settings
|
||||||
|
if ((low - high).length() < EPSILON) {
|
||||||
|
SpatiallyNestable::setScale(glm::vec3(1.0f, 1.0f, 1.0f));
|
||||||
|
EntityItem::setRegistrationPoint(glm::vec3(0.5f));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec3 result;
|
||||||
|
const float halfLineWidth = 0.075f; // sadly _strokeWidths() don't seem to correspond to reality, so just use a flat assumption of the stroke width
|
||||||
|
result.x = fabsf(high.x) + fabsf(low.x) + halfLineWidth;
|
||||||
|
result.y = fabsf(high.y) + fabsf(low.y) + halfLineWidth;
|
||||||
|
result.z = fabsf(high.z) + fabsf(low.z) + halfLineWidth;
|
||||||
|
SpatiallyNestable::setScale(result);
|
||||||
|
|
||||||
|
// Center the poly line in the bounding box
|
||||||
|
glm::vec3 point = _points.at(0);
|
||||||
|
glm::vec3 startPointInScaleSpace = point - low;
|
||||||
|
startPointInScaleSpace += glm::vec3(halfLineWidth * 0.5f);
|
||||||
|
glm::vec3 newRegistrationPoint = startPointInScaleSpace / result;
|
||||||
|
EntityItem::setRegistrationPoint(newRegistrationPoint);
|
||||||
|
} else {
|
||||||
|
// if Polyline has only one or fewer points, use default dimension settings
|
||||||
|
SpatiallyNestable::setScale(glm::vec3(1.0f, 1.0f, 1.0f));
|
||||||
|
EntityItem::setRegistrationPoint(glm::vec3(0.5f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int PolyLineEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
|
int PolyLineEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
|
||||||
ReadBitstreamToTreeParams& args,
|
ReadBitstreamToTreeParams& args,
|
||||||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
|
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
|
||||||
|
|
|
@ -81,10 +81,17 @@ class PolyLineEntityItem : public EntityItem {
|
||||||
BoxFace& face, glm::vec3& surfaceNormal,
|
BoxFace& face, glm::vec3& surfaceNormal,
|
||||||
void** intersectedObject, bool precisionPicking) const override { return false; }
|
void** intersectedObject, bool precisionPicking) const override { return false; }
|
||||||
|
|
||||||
|
// disable these external interfaces as PolyLineEntities caculate their own dimensions based on the points they contain
|
||||||
|
virtual void setRegistrationPoint(const glm::vec3& value) override {};
|
||||||
|
virtual void setScale(const glm::vec3& scale) override {};
|
||||||
|
virtual void setScale(float value) override {};
|
||||||
|
|
||||||
virtual void debugDump() const override;
|
virtual void debugDump() const override;
|
||||||
static const float DEFAULT_LINE_WIDTH;
|
static const float DEFAULT_LINE_WIDTH;
|
||||||
static const int MAX_POINTS_PER_LINE;
|
static const int MAX_POINTS_PER_LINE;
|
||||||
|
private:
|
||||||
|
void calculateScaleAndRegistrationPoint();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
rgbColor _color;
|
rgbColor _color;
|
||||||
float _lineWidth;
|
float _lineWidth;
|
||||||
|
|
|
@ -32,6 +32,8 @@ namespace gpu {
|
||||||
Mat4 pose;
|
Mat4 pose;
|
||||||
/// The collection of batches which make up the frame
|
/// The collection of batches which make up the frame
|
||||||
Batches batches;
|
Batches batches;
|
||||||
|
/// Single batch containing overlays to be drawn in the composite framebuffer
|
||||||
|
Batch postCompositeBatch;
|
||||||
/// The main thread updates to buffers that are applicable for this frame.
|
/// The main thread updates to buffers that are applicable for this frame.
|
||||||
BufferUpdates bufferUpdates;
|
BufferUpdates bufferUpdates;
|
||||||
/// The destination framebuffer in which the results will be placed
|
/// The destination framebuffer in which the results will be placed
|
||||||
|
|
|
@ -53,8 +53,12 @@ QVariant NodePermissions::toVariant(QHash<QUuid, GroupRank> groupRanks) {
|
||||||
values["permissions_id"] = _id;
|
values["permissions_id"] = _id;
|
||||||
if (_groupIDSet) {
|
if (_groupIDSet) {
|
||||||
values["group_id"] = _groupID;
|
values["group_id"] = _groupID;
|
||||||
if (groupRanks.contains(_rankID)) {
|
|
||||||
|
if (!_rankID.isNull()) {
|
||||||
values["rank_id"] = _rankID;
|
values["rank_id"] = _rankID;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (groupRanks.contains(_rankID)) {
|
||||||
values["rank_name"] = groupRanks[_rankID].name;
|
values["rank_name"] = groupRanks[_rankID].name;
|
||||||
values["rank_order"] = groupRanks[_rankID].order;
|
values["rank_order"] = groupRanks[_rankID].order;
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,24 +109,6 @@ public:
|
||||||
RightHand = 0x02,
|
RightHand = 0x02,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class HandLaserMode {
|
|
||||||
None, // Render no hand lasers
|
|
||||||
Overlay, // Render hand lasers only if they intersect with the UI layer, and stop at the UI layer
|
|
||||||
};
|
|
||||||
|
|
||||||
virtual bool setHandLaser(
|
|
||||||
uint32_t hands, // Bits from the Hand enum
|
|
||||||
HandLaserMode mode, // Mode in which to render
|
|
||||||
const vec4& color = vec4(1), // The color of the rendered laser
|
|
||||||
const vec3& direction = vec3(0, 0, -1) // The direction in which to render the hand lasers
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool setExtraLaser(HandLaserMode mode, const vec4& color, const glm::vec3& sensorSpaceStart, const vec3& sensorSpaceDirection) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool suppressKeyboard() { return false; }
|
virtual bool suppressKeyboard() { return false; }
|
||||||
virtual void unsuppressKeyboard() {};
|
virtual void unsuppressKeyboard() {};
|
||||||
virtual bool isKeyboardVisible() { return false; }
|
virtual bool isKeyboardVisible() { return false; }
|
||||||
|
|
|
@ -70,7 +70,7 @@ const gpu::PipelinePointer& Antialiasing::getAntialiasingPipeline(RenderArgs* ar
|
||||||
|
|
||||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||||
|
|
||||||
PrepareStencil::testMask(*state);
|
PrepareStencil::testMaskNoAA(*state);
|
||||||
|
|
||||||
state->setDepthTest(false, false, gpu::LESS_EQUAL);
|
state->setDepthTest(false, false, gpu::LESS_EQUAL);
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ const gpu::PipelinePointer& Antialiasing::getBlendPipeline() {
|
||||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||||
|
|
||||||
state->setDepthTest(false, false, gpu::LESS_EQUAL);
|
state->setDepthTest(false, false, gpu::LESS_EQUAL);
|
||||||
PrepareStencil::testMask(*state);
|
PrepareStencil::testMaskNoAA(*state);
|
||||||
|
|
||||||
// Good to go add the brand new pipeline
|
// Good to go add the brand new pipeline
|
||||||
_blendPipeline = gpu::Pipeline::create(program, state);
|
_blendPipeline = gpu::Pipeline::create(program, state);
|
||||||
|
@ -159,7 +159,6 @@ void Antialiasing::run(const render::RenderContextPointer& renderContext, const
|
||||||
|
|
||||||
|
|
||||||
// Blend step
|
// Blend step
|
||||||
getBlendPipeline();
|
|
||||||
batch.setResourceTexture(0, _antialiasingTexture);
|
batch.setResourceTexture(0, _antialiasingTexture);
|
||||||
batch.setFramebuffer(sourceBuffer);
|
batch.setFramebuffer(sourceBuffer);
|
||||||
batch.setPipeline(getBlendPipeline());
|
batch.setPipeline(getBlendPipeline());
|
||||||
|
|
|
@ -594,7 +594,7 @@ void FadeJob::run(const render::RenderContextPointer& renderContext, FadeJob::Ou
|
||||||
if (update(*jobConfig, scene, transaction, state, deltaTime)) {
|
if (update(*jobConfig, scene, transaction, state, deltaTime)) {
|
||||||
hasTransaction = true;
|
hasTransaction = true;
|
||||||
}
|
}
|
||||||
if (isFirstItem) {
|
if (isFirstItem && jobConfig->manualFade && (state.threshold != jobConfig->threshold)) {
|
||||||
jobConfig->setProperty("threshold", state.threshold);
|
jobConfig->setProperty("threshold", state.threshold);
|
||||||
isFirstItem = false;
|
isFirstItem = false;
|
||||||
}
|
}
|
||||||
|
@ -645,9 +645,6 @@ bool FadeJob::update(const Config& config, const render::ScenePointer& scene, re
|
||||||
{
|
{
|
||||||
transition.threshold = computeElementEnterRatio(transition.time, eventDuration, timing);
|
transition.threshold = computeElementEnterRatio(transition.time, eventDuration, timing);
|
||||||
transition.baseOffset = transition.noiseOffset;
|
transition.baseOffset = transition.noiseOffset;
|
||||||
transition.baseInvSize.x = 1.f / dimensions.x;
|
|
||||||
transition.baseInvSize.y = 1.f / dimensions.y;
|
|
||||||
transition.baseInvSize.z = 1.f / dimensions.z;
|
|
||||||
transition.isFinished += (transition.threshold >= 1.f) & 1;
|
transition.isFinished += (transition.threshold >= 1.f) & 1;
|
||||||
if (transition.eventType == render::Transition::ELEMENT_ENTER_DOMAIN) {
|
if (transition.eventType == render::Transition::ELEMENT_ENTER_DOMAIN) {
|
||||||
transition.threshold = 1.f - transition.threshold;
|
transition.threshold = 1.f - transition.threshold;
|
||||||
|
|
|
@ -50,7 +50,7 @@ class FadeConfig : public render::Job::Config {
|
||||||
Q_PROPERTY(float baseSizeY READ getBaseSizeY WRITE setBaseSizeY NOTIFY dirty)
|
Q_PROPERTY(float baseSizeY READ getBaseSizeY WRITE setBaseSizeY NOTIFY dirty)
|
||||||
Q_PROPERTY(float baseSizeZ READ getBaseSizeZ WRITE setBaseSizeZ NOTIFY dirty)
|
Q_PROPERTY(float baseSizeZ READ getBaseSizeZ WRITE setBaseSizeZ NOTIFY dirty)
|
||||||
Q_PROPERTY(float baseLevel READ getBaseLevel WRITE setBaseLevel NOTIFY dirty)
|
Q_PROPERTY(float baseLevel READ getBaseLevel WRITE setBaseLevel NOTIFY dirty)
|
||||||
Q_PROPERTY(bool _isInverted READ isInverted WRITE setInverted NOTIFY dirty)
|
Q_PROPERTY(bool isInverted READ isInverted WRITE setInverted NOTIFY dirty)
|
||||||
Q_PROPERTY(float noiseSizeX READ getNoiseSizeX WRITE setNoiseSizeX NOTIFY dirty)
|
Q_PROPERTY(float noiseSizeX READ getNoiseSizeX WRITE setNoiseSizeX NOTIFY dirty)
|
||||||
Q_PROPERTY(float noiseSizeY READ getNoiseSizeY WRITE setNoiseSizeY NOTIFY dirty)
|
Q_PROPERTY(float noiseSizeY READ getNoiseSizeY WRITE setNoiseSizeY NOTIFY dirty)
|
||||||
Q_PROPERTY(float noiseSizeZ READ getNoiseSizeZ WRITE setNoiseSizeZ NOTIFY dirty)
|
Q_PROPERTY(float noiseSizeZ READ getNoiseSizeZ WRITE setNoiseSizeZ NOTIFY dirty)
|
||||||
|
|
|
@ -42,9 +42,7 @@
|
||||||
#include "simple_textured_fade_frag.h"
|
#include "simple_textured_fade_frag.h"
|
||||||
#include "simple_textured_unlit_fade_frag.h"
|
#include "simple_textured_unlit_fade_frag.h"
|
||||||
#include "simple_opaque_web_browser_frag.h"
|
#include "simple_opaque_web_browser_frag.h"
|
||||||
#include "simple_opaque_web_browser_overlay_frag.h"
|
|
||||||
#include "simple_transparent_web_browser_frag.h"
|
#include "simple_transparent_web_browser_frag.h"
|
||||||
#include "simple_transparent_web_browser_overlay_frag.h"
|
|
||||||
#include "glowLine_vert.h"
|
#include "glowLine_vert.h"
|
||||||
#include "glowLine_frag.h"
|
#include "glowLine_frag.h"
|
||||||
|
|
||||||
|
@ -1828,7 +1826,7 @@ void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) {
|
||||||
|
|
||||||
|
|
||||||
auto stateNoBlend = std::make_shared<gpu::State>();
|
auto stateNoBlend = std::make_shared<gpu::State>();
|
||||||
PrepareStencil::testMaskDrawShape(*state);
|
PrepareStencil::testMaskDrawShape(*stateNoBlend);
|
||||||
|
|
||||||
auto noBlendPS = gpu::StandardShaderLib::getDrawTextureOpaquePS();
|
auto noBlendPS = gpu::StandardShaderLib::getDrawTextureOpaquePS();
|
||||||
auto programNoBlend = gpu::Shader::createProgram(vs, noBlendPS);
|
auto programNoBlend = gpu::Shader::createProgram(vs, noBlendPS);
|
||||||
|
@ -1925,7 +1923,7 @@ inline bool operator==(const SimpleProgramKey& a, const SimpleProgramKey& b) {
|
||||||
return a.getRaw() == b.getRaw();
|
return a.getRaw() == b.getRaw();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void buildWebShader(const std::string& vertShaderText, const std::string& fragShaderText, bool blendEnable,
|
static void buildWebShader(const std::string& vertShaderText, const std::string& fragShaderText, bool blendEnable, bool isAA,
|
||||||
gpu::ShaderPointer& shaderPointerOut, gpu::PipelinePointer& pipelinePointerOut) {
|
gpu::ShaderPointer& shaderPointerOut, gpu::PipelinePointer& pipelinePointerOut) {
|
||||||
auto VS = gpu::Shader::createVertex(vertShaderText);
|
auto VS = gpu::Shader::createVertex(vertShaderText);
|
||||||
auto PS = gpu::Shader::createPixel(fragShaderText);
|
auto PS = gpu::Shader::createPixel(fragShaderText);
|
||||||
|
@ -1940,10 +1938,11 @@ static void buildWebShader(const std::string& vertShaderText, const std::string&
|
||||||
state->setBlendFunction(blendEnable,
|
state->setBlendFunction(blendEnable,
|
||||||
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
||||||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||||
if (blendEnable) {
|
|
||||||
PrepareStencil::testMask(*state);
|
if (isAA) {
|
||||||
|
blendEnable ? PrepareStencil::testMask(*state) : PrepareStencil::testMaskDrawShape(*state);
|
||||||
} else {
|
} else {
|
||||||
PrepareStencil::testMaskDrawShape(*state);
|
PrepareStencil::testMaskDrawShapeNoAA(*state);
|
||||||
}
|
}
|
||||||
|
|
||||||
pipelinePointerOut = gpu::Pipeline::create(shaderPointerOut, state);
|
pipelinePointerOut = gpu::Pipeline::create(shaderPointerOut, state);
|
||||||
|
@ -1957,11 +1956,11 @@ gpu::PipelinePointer GeometryCache::getOpaqueWebBrowserProgram(bool isAA) {
|
||||||
static std::once_flag once;
|
static std::once_flag once;
|
||||||
std::call_once(once, [&]() {
|
std::call_once(once, [&]() {
|
||||||
const bool BLEND_ENABLE = false;
|
const bool BLEND_ENABLE = false;
|
||||||
buildWebShader(simple_vert, simple_opaque_web_browser_frag, BLEND_ENABLE, _simpleOpaqueWebBrowserShader, _simpleOpaqueWebBrowserPipeline);
|
buildWebShader(simple_vert, simple_opaque_web_browser_frag, BLEND_ENABLE, true, _simpleOpaqueWebBrowserShader, _simpleOpaqueWebBrowserPipeline);
|
||||||
buildWebShader(simple_vert, simple_opaque_web_browser_overlay_frag, BLEND_ENABLE, _simpleOpaqueWebBrowserOverlayShader, _simpleOpaqueWebBrowserOverlayPipeline);
|
buildWebShader(simple_vert, simple_opaque_web_browser_frag, BLEND_ENABLE, false, _simpleOpaqueWebBrowserShader, _simpleOpaqueWebBrowserPipelineNoAA);
|
||||||
});
|
});
|
||||||
|
|
||||||
return isAA ? _simpleOpaqueWebBrowserPipeline : _simpleOpaqueWebBrowserOverlayPipeline;
|
return isAA ? _simpleOpaqueWebBrowserPipeline : _simpleOpaqueWebBrowserPipelineNoAA;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GeometryCache::bindTransparentWebBrowserProgram(gpu::Batch& batch, bool isAA) {
|
void GeometryCache::bindTransparentWebBrowserProgram(gpu::Batch& batch, bool isAA) {
|
||||||
|
@ -1971,13 +1970,12 @@ void GeometryCache::bindTransparentWebBrowserProgram(gpu::Batch& batch, bool isA
|
||||||
gpu::PipelinePointer GeometryCache::getTransparentWebBrowserProgram(bool isAA) {
|
gpu::PipelinePointer GeometryCache::getTransparentWebBrowserProgram(bool isAA) {
|
||||||
static std::once_flag once;
|
static std::once_flag once;
|
||||||
std::call_once(once, [&]() {
|
std::call_once(once, [&]() {
|
||||||
|
|
||||||
const bool BLEND_ENABLE = true;
|
const bool BLEND_ENABLE = true;
|
||||||
buildWebShader(simple_vert, simple_transparent_web_browser_frag, BLEND_ENABLE, _simpleTransparentWebBrowserShader, _simpleTransparentWebBrowserPipeline);
|
buildWebShader(simple_vert, simple_transparent_web_browser_frag, BLEND_ENABLE, true, _simpleTransparentWebBrowserShader, _simpleTransparentWebBrowserPipeline);
|
||||||
buildWebShader(simple_vert, simple_transparent_web_browser_overlay_frag, BLEND_ENABLE, _simpleTransparentWebBrowserOverlayShader, _simpleTransparentWebBrowserOverlayPipeline);
|
buildWebShader(simple_vert, simple_transparent_web_browser_frag, BLEND_ENABLE, false, _simpleTransparentWebBrowserShader, _simpleTransparentWebBrowserPipelineNoAA);
|
||||||
});
|
});
|
||||||
|
|
||||||
return isAA ? _simpleTransparentWebBrowserPipeline : _simpleTransparentWebBrowserOverlayPipeline;
|
return isAA ? _simpleTransparentWebBrowserPipeline : _simpleTransparentWebBrowserPipelineNoAA;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GeometryCache::bindSimpleProgram(gpu::Batch& batch, bool textured, bool transparent, bool culled, bool unlit, bool depthBiased) {
|
void GeometryCache::bindSimpleProgram(gpu::Batch& batch, bool textured, bool transparent, bool culled, bool unlit, bool depthBiased) {
|
||||||
|
|
|
@ -460,13 +460,10 @@ private:
|
||||||
|
|
||||||
gpu::ShaderPointer _simpleOpaqueWebBrowserShader;
|
gpu::ShaderPointer _simpleOpaqueWebBrowserShader;
|
||||||
gpu::PipelinePointer _simpleOpaqueWebBrowserPipeline;
|
gpu::PipelinePointer _simpleOpaqueWebBrowserPipeline;
|
||||||
|
gpu::PipelinePointer _simpleOpaqueWebBrowserPipelineNoAA;
|
||||||
gpu::ShaderPointer _simpleTransparentWebBrowserShader;
|
gpu::ShaderPointer _simpleTransparentWebBrowserShader;
|
||||||
gpu::PipelinePointer _simpleTransparentWebBrowserPipeline;
|
gpu::PipelinePointer _simpleTransparentWebBrowserPipeline;
|
||||||
|
gpu::PipelinePointer _simpleTransparentWebBrowserPipelineNoAA;
|
||||||
gpu::ShaderPointer _simpleOpaqueWebBrowserOverlayShader;
|
|
||||||
gpu::PipelinePointer _simpleOpaqueWebBrowserOverlayPipeline;
|
|
||||||
gpu::ShaderPointer _simpleTransparentWebBrowserOverlayShader;
|
|
||||||
gpu::PipelinePointer _simpleTransparentWebBrowserOverlayPipeline;
|
|
||||||
|
|
||||||
static render::ShapePipelinePointer getShapePipeline(bool textured = false, bool transparent = false, bool culled = true,
|
static render::ShapePipelinePointer getShapePipeline(bool textured = false, bool transparent = false, bool culled = true,
|
||||||
bool unlit = false, bool depthBias = false);
|
bool unlit = false, bool depthBias = false);
|
||||||
|
|
|
@ -45,7 +45,7 @@
|
||||||
|
|
||||||
|
|
||||||
using namespace render;
|
using namespace render;
|
||||||
extern void initOverlay3DPipelines(render::ShapePlumber& plumber);
|
extern void initOverlay3DPipelines(render::ShapePlumber& plumber, bool depthTest = false);
|
||||||
extern void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter);
|
extern void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter);
|
||||||
|
|
||||||
RenderDeferredTask::RenderDeferredTask() {
|
RenderDeferredTask::RenderDeferredTask() {
|
||||||
|
@ -76,10 +76,6 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
|
||||||
|
|
||||||
fadeEffect->build(task, opaques);
|
fadeEffect->build(task, opaques);
|
||||||
|
|
||||||
// Filter the non antialiaased overlays
|
|
||||||
const int LAYER_NO_AA = 3;
|
|
||||||
const auto nonAAOverlays = task.addJob<FilterLayeredItems>("Filter2DWebOverlays", overlayOpaques, LAYER_NO_AA);
|
|
||||||
|
|
||||||
// Prepare deferred, generate the shared Deferred Frame Transform
|
// Prepare deferred, generate the shared Deferred Frame Transform
|
||||||
const auto deferredFrameTransform = task.addJob<GenerateDeferredFrameTransform>("DeferredFrameTransform");
|
const auto deferredFrameTransform = task.addJob<GenerateDeferredFrameTransform>("DeferredFrameTransform");
|
||||||
const auto lightingModel = task.addJob<MakeLightingModel>("LightingModel");
|
const auto lightingModel = task.addJob<MakeLightingModel>("LightingModel");
|
||||||
|
@ -166,7 +162,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
|
||||||
const auto toneMappingInputs = render::Varying(ToneMappingDeferred::Inputs(lightingFramebuffer, primaryFramebuffer));
|
const auto toneMappingInputs = render::Varying(ToneMappingDeferred::Inputs(lightingFramebuffer, primaryFramebuffer));
|
||||||
task.addJob<ToneMappingDeferred>("ToneMapping", toneMappingInputs);
|
task.addJob<ToneMappingDeferred>("ToneMapping", toneMappingInputs);
|
||||||
|
|
||||||
{ // DEbug the bounds of the rendered items, still look at the zbuffer
|
{ // Debug the bounds of the rendered items, still look at the zbuffer
|
||||||
task.addJob<DrawBounds>("DrawMetaBounds", metas);
|
task.addJob<DrawBounds>("DrawMetaBounds", metas);
|
||||||
task.addJob<DrawBounds>("DrawOpaqueBounds", opaques);
|
task.addJob<DrawBounds>("DrawOpaqueBounds", opaques);
|
||||||
task.addJob<DrawBounds>("DrawTransparentBounds", transparents);
|
task.addJob<DrawBounds>("DrawTransparentBounds", transparents);
|
||||||
|
@ -181,11 +177,11 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
|
||||||
task.addJob<DrawOverlay3D>("DrawOverlay3DOpaque", overlayOpaquesInputs, true);
|
task.addJob<DrawOverlay3D>("DrawOverlay3DOpaque", overlayOpaquesInputs, true);
|
||||||
task.addJob<DrawOverlay3D>("DrawOverlay3DTransparent", overlayTransparentsInputs, false);
|
task.addJob<DrawOverlay3D>("DrawOverlay3DTransparent", overlayTransparentsInputs, false);
|
||||||
|
|
||||||
{ // DEbug the bounds of the rendered OVERLAY items, still look at the zbuffer
|
{ // Debug the bounds of the rendered Overlay items, still look at the zbuffer
|
||||||
task.addJob<DrawBounds>("DrawOverlayOpaqueBounds", overlayOpaques);
|
task.addJob<DrawBounds>("DrawOverlayOpaqueBounds", overlayOpaques);
|
||||||
task.addJob<DrawBounds>("DrawOverlayTransparentBounds", overlayTransparents);
|
task.addJob<DrawBounds>("DrawOverlayTransparentBounds", overlayTransparents);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debugging stages
|
// Debugging stages
|
||||||
{
|
{
|
||||||
// Debugging Deferred buffer job
|
// Debugging Deferred buffer job
|
||||||
|
@ -216,14 +212,9 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
|
||||||
task.addJob<DebugZoneLighting>("DrawZoneStack", deferredFrameTransform);
|
task.addJob<DebugZoneLighting>("DrawZoneStack", deferredFrameTransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// AA job to be revisited
|
// AA job to be revisited
|
||||||
task.addJob<Antialiasing>("Antialiasing", primaryFramebuffer);
|
task.addJob<Antialiasing>("Antialiasing", primaryFramebuffer);
|
||||||
|
|
||||||
// Draw 2DWeb non AA
|
|
||||||
const auto nonAAOverlaysInputs = DrawOverlay3D::Inputs(nonAAOverlays, lightingModel).asVarying();
|
|
||||||
task.addJob<DrawOverlay3D>("Draw2DWebSurfaces", nonAAOverlaysInputs, false);
|
|
||||||
|
|
||||||
task.addJob<EndGPURangeTimer>("ToneAndPostRangeTimer", toneAndPostRangeTimer);
|
task.addJob<EndGPURangeTimer>("ToneAndPostRangeTimer", toneAndPostRangeTimer);
|
||||||
|
|
||||||
// Blit!
|
// Blit!
|
||||||
|
|
|
@ -152,7 +152,7 @@ public:
|
||||||
protected:
|
protected:
|
||||||
render::ShapePlumberPointer _shapePlumber;
|
render::ShapePlumberPointer _shapePlumber;
|
||||||
int _maxDrawn; // initialized by Config
|
int _maxDrawn; // initialized by Config
|
||||||
bool _opaquePass{ true };
|
bool _opaquePass { true };
|
||||||
};
|
};
|
||||||
|
|
||||||
class Blit {
|
class Blit {
|
||||||
|
|
|
@ -99,7 +99,7 @@
|
||||||
using namespace render;
|
using namespace render;
|
||||||
using namespace std::placeholders;
|
using namespace std::placeholders;
|
||||||
|
|
||||||
void initOverlay3DPipelines(ShapePlumber& plumber);
|
void initOverlay3DPipelines(ShapePlumber& plumber, bool depthTest = false);
|
||||||
void initDeferredPipelines(ShapePlumber& plumber, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter);
|
void initDeferredPipelines(ShapePlumber& plumber, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter);
|
||||||
void initForwardPipelines(ShapePlumber& plumber);
|
void initForwardPipelines(ShapePlumber& plumber);
|
||||||
|
|
||||||
|
@ -110,7 +110,7 @@ void addPlumberPipeline(ShapePlumber& plumber,
|
||||||
void batchSetter(const ShapePipeline& pipeline, gpu::Batch& batch, RenderArgs* args);
|
void batchSetter(const ShapePipeline& pipeline, gpu::Batch& batch, RenderArgs* args);
|
||||||
void lightBatchSetter(const ShapePipeline& pipeline, gpu::Batch& batch, RenderArgs* args);
|
void lightBatchSetter(const ShapePipeline& pipeline, gpu::Batch& batch, RenderArgs* args);
|
||||||
|
|
||||||
void initOverlay3DPipelines(ShapePlumber& plumber) {
|
void initOverlay3DPipelines(ShapePlumber& plumber, bool depthTest) {
|
||||||
auto vertex = gpu::Shader::createVertex(std::string(overlay3D_vert));
|
auto vertex = gpu::Shader::createVertex(std::string(overlay3D_vert));
|
||||||
auto vertexModel = gpu::Shader::createVertex(std::string(model_vert));
|
auto vertexModel = gpu::Shader::createVertex(std::string(model_vert));
|
||||||
auto pixel = gpu::Shader::createPixel(std::string(overlay3D_frag));
|
auto pixel = gpu::Shader::createPixel(std::string(overlay3D_frag));
|
||||||
|
@ -137,7 +137,11 @@ void initOverlay3DPipelines(ShapePlumber& plumber) {
|
||||||
bool isOpaque = (i & 4);
|
bool isOpaque = (i & 4);
|
||||||
|
|
||||||
auto state = std::make_shared<gpu::State>();
|
auto state = std::make_shared<gpu::State>();
|
||||||
state->setDepthTest(false);
|
if (depthTest) {
|
||||||
|
state->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||||
|
} else {
|
||||||
|
state->setDepthTest(false);
|
||||||
|
}
|
||||||
state->setCullMode(isCulled ? gpu::State::CULL_BACK : gpu::State::CULL_NONE);
|
state->setCullMode(isCulled ? gpu::State::CULL_BACK : gpu::State::CULL_NONE);
|
||||||
if (isBiased) {
|
if (isBiased) {
|
||||||
state->setDepthBias(1.0f);
|
state->setDepthBias(1.0f);
|
||||||
|
|
|
@ -112,6 +112,10 @@ void PrepareStencil::testMask(gpu::State& state) {
|
||||||
state.setStencilTest(true, 0x00, gpu::State::StencilTest(PrepareStencil::STENCIL_MASK, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
|
state.setStencilTest(true, 0x00, gpu::State::StencilTest(PrepareStencil::STENCIL_MASK, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PrepareStencil::testMaskNoAA(gpu::State& state) {
|
||||||
|
state.setStencilTest(true, 0x00, gpu::State::StencilTest(PrepareStencil::STENCIL_MASK | PrepareStencil::STENCIL_NO_AA, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
|
||||||
|
}
|
||||||
|
|
||||||
void PrepareStencil::testBackground(gpu::State& state) {
|
void PrepareStencil::testBackground(gpu::State& state) {
|
||||||
state.setStencilTest(true, 0x00, gpu::State::StencilTest(PrepareStencil::STENCIL_BACKGROUND, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
|
state.setStencilTest(true, 0x00, gpu::State::StencilTest(PrepareStencil::STENCIL_BACKGROUND, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
|
||||||
}
|
}
|
||||||
|
@ -120,6 +124,10 @@ void PrepareStencil::testMaskDrawShape(gpu::State& state) {
|
||||||
state.setStencilTest(true, 0xFF, gpu::State::StencilTest(PrepareStencil::STENCIL_MASK, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_ZERO));
|
state.setStencilTest(true, 0xFF, gpu::State::StencilTest(PrepareStencil::STENCIL_MASK, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_ZERO));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PrepareStencil::testMaskDrawShapeNoAA(gpu::State& state) {
|
||||||
|
state.setStencilTest(true, 0xFF, gpu::State::StencilTest(PrepareStencil::STENCIL_MASK | PrepareStencil::STENCIL_NO_AA, 0xFF, gpu::ALWAYS, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_REPLACE));
|
||||||
|
}
|
||||||
|
|
||||||
void PrepareStencil::testShape(gpu::State& state) {
|
void PrepareStencil::testShape(gpu::State& state) {
|
||||||
state.setStencilTest(true, 0x00, gpu::State::StencilTest(PrepareStencil::STENCIL_SHAPE, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
|
state.setStencilTest(true, 0x00, gpu::State::StencilTest(PrepareStencil::STENCIL_SHAPE, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
|
||||||
}
|
}
|
|
@ -41,15 +41,18 @@ public:
|
||||||
|
|
||||||
void run(const render::RenderContextPointer& renderContext, const gpu::FramebufferPointer& dstFramebuffer);
|
void run(const render::RenderContextPointer& renderContext, const gpu::FramebufferPointer& dstFramebuffer);
|
||||||
|
|
||||||
static const gpu::int8 STENCIL_MASK = 2;
|
|
||||||
static const gpu::int8 STENCIL_BACKGROUND = 1;
|
|
||||||
static const gpu::int8 STENCIL_SHAPE = 0;
|
static const gpu::int8 STENCIL_SHAPE = 0;
|
||||||
|
static const gpu::int8 STENCIL_BACKGROUND = 1 << 0;
|
||||||
|
static const gpu::int8 STENCIL_MASK = 1 << 1;
|
||||||
|
static const gpu::int8 STENCIL_NO_AA = 1 << 2;
|
||||||
|
|
||||||
|
|
||||||
static void drawMask(gpu::State& state);
|
static void drawMask(gpu::State& state);
|
||||||
static void testMask(gpu::State& state);
|
static void testMask(gpu::State& state);
|
||||||
|
static void testMaskNoAA(gpu::State& state);
|
||||||
static void testBackground(gpu::State& state);
|
static void testBackground(gpu::State& state);
|
||||||
static void testMaskDrawShape(gpu::State& state);
|
static void testMaskDrawShape(gpu::State& state);
|
||||||
|
static void testMaskDrawShapeNoAA(gpu::State& state);
|
||||||
static void testShape(gpu::State& state);
|
static void testShape(gpu::State& state);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
<@include gpu/Config.slh@>
|
|
||||||
<$VERSION_HEADER$>
|
|
||||||
// Generated on <$_SCRIBE_DATE$>
|
|
||||||
//
|
|
||||||
// simple_opaque_web_browser_overlay.slf
|
|
||||||
// fragment shader
|
|
||||||
//
|
|
||||||
// Created by Anthony Thibault on 1/30/17.
|
|
||||||
// Copyright 2017 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
// Same as simple_opaque_web_browser.slf except frame buffer is sRGB, so colorToLinearRGBA is not necessary.
|
|
||||||
|
|
||||||
<@include gpu/Color.slh@>
|
|
||||||
<@include DeferredBufferWrite.slh@>
|
|
||||||
|
|
||||||
// the albedo texture
|
|
||||||
uniform sampler2D originalTexture;
|
|
||||||
|
|
||||||
// the interpolated normal
|
|
||||||
in vec3 _normal;
|
|
||||||
in vec4 _color;
|
|
||||||
in vec2 _texCoord0;
|
|
||||||
|
|
||||||
void main(void) {
|
|
||||||
vec4 texel = texture(originalTexture, _texCoord0.st);
|
|
||||||
_fragColor0 = vec4(_color.rgb * texel.rgb, 1.0);
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
<@include gpu/Config.slh@>
|
|
||||||
<$VERSION_HEADER$>
|
|
||||||
// Generated on <$_SCRIBE_DATE$>
|
|
||||||
//
|
|
||||||
// simple_transparent_web_browser_overlay.slf
|
|
||||||
// fragment shader
|
|
||||||
//
|
|
||||||
// Created by Anthony Thibault on 1/30/17.
|
|
||||||
// Copyright 2017 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
// Same as simple_transparent_web_browser.slf except frame buffer is sRGB, So colorToLinearRGBA is not necessary.
|
|
||||||
//
|
|
||||||
|
|
||||||
<@include gpu/Color.slh@>
|
|
||||||
<@include DeferredBufferWrite.slh@>
|
|
||||||
|
|
||||||
// the albedo texture
|
|
||||||
uniform sampler2D originalTexture;
|
|
||||||
|
|
||||||
// the interpolated normal
|
|
||||||
in vec3 _normal;
|
|
||||||
in vec4 _color;
|
|
||||||
in vec2 _texCoord0;
|
|
||||||
|
|
||||||
void main(void) {
|
|
||||||
vec4 texel = texture(originalTexture, _texCoord0.st);
|
|
||||||
_fragColor0 = vec4(_color.rgb * texel.rgb, _color.a);
|
|
||||||
}
|
|
|
@ -32,10 +32,11 @@ FileScriptingInterface::FileScriptingInterface(QObject* parent) : QObject(parent
|
||||||
// nothing for now
|
// nothing for now
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileScriptingInterface::runUnzip(QString path, QUrl url, bool autoAdd, bool isZip) {
|
void FileScriptingInterface::runUnzip(QString path, QUrl url, bool autoAdd, bool isZip, bool isBlocks) {
|
||||||
qCDebug(scriptengine) << "Url that was downloaded: " + url.toString();
|
qCDebug(scriptengine) << "Url that was downloaded: " + url.toString();
|
||||||
qCDebug(scriptengine) << "Path where download is saved: " + path;
|
qCDebug(scriptengine) << "Path where download is saved: " + path;
|
||||||
QString fileName = "/" + path.section("/", -1);
|
QString fileName = "/" + path.section("/", -1);
|
||||||
|
qCDebug(scriptengine) << "Filename: " << fileName;
|
||||||
QString tempDir = path;
|
QString tempDir = path;
|
||||||
if (!isZip) {
|
if (!isZip) {
|
||||||
tempDir.remove(fileName);
|
tempDir.remove(fileName);
|
||||||
|
@ -52,14 +53,17 @@ void FileScriptingInterface::runUnzip(QString path, QUrl url, bool autoAdd, bool
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList fileList = unzipFile(path, tempDir);
|
QStringList fileList = unzipFile(path, tempDir);
|
||||||
QString filename = QUrl::fromLocalFile(fileList.first()).toString();
|
|
||||||
|
|
||||||
if (filename != "") {
|
if (!fileList.isEmpty()) {
|
||||||
qCDebug(scriptengine) << "File to upload: " + filename;
|
qCDebug(scriptengine) << "File to upload: " + fileList.first();
|
||||||
} else {
|
} else {
|
||||||
qCDebug(scriptengine) << "Unzip failed";
|
qCDebug(scriptengine) << "Unzip failed";
|
||||||
}
|
}
|
||||||
emit unzipResult(path, fileList, autoAdd, isZip);
|
|
||||||
|
if (path.contains("vr.google.com/downloads")) {
|
||||||
|
isZip = true;
|
||||||
|
}
|
||||||
|
emit unzipResult(path, fileList, autoAdd, isZip, isBlocks);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,11 +24,11 @@ public:
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
QString convertUrlToPath(QUrl url);
|
QString convertUrlToPath(QUrl url);
|
||||||
void runUnzip(QString path, QUrl url, bool autoAdd, bool isZip);
|
void runUnzip(QString path, QUrl url, bool autoAdd, bool isZip, bool isBlocks);
|
||||||
QString getTempDir();
|
QString getTempDir();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void unzipResult(QString zipFile, QStringList unzipFile, bool autoAdd, bool isZip);
|
void unzipResult(QString zipFile, QStringList unzipFile, bool autoAdd, bool isZip, bool isBlocks);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool isTempDir(QString tempDir);
|
bool isTempDir(QString tempDir);
|
||||||
|
|
|
@ -34,6 +34,7 @@ int vec2MetaTypeId = qRegisterMetaType<glm::vec2>();
|
||||||
int quatMetaTypeId = qRegisterMetaType<glm::quat>();
|
int quatMetaTypeId = qRegisterMetaType<glm::quat>();
|
||||||
int xColorMetaTypeId = qRegisterMetaType<xColor>();
|
int xColorMetaTypeId = qRegisterMetaType<xColor>();
|
||||||
int pickRayMetaTypeId = qRegisterMetaType<PickRay>();
|
int pickRayMetaTypeId = qRegisterMetaType<PickRay>();
|
||||||
|
int rayPickResultMetaTypeId = qRegisterMetaType<RayPickResult>();
|
||||||
int collisionMetaTypeId = qRegisterMetaType<Collision>();
|
int collisionMetaTypeId = qRegisterMetaType<Collision>();
|
||||||
int qMapURLStringMetaTypeId = qRegisterMetaType<QMap<QUrl,QString>>();
|
int qMapURLStringMetaTypeId = qRegisterMetaType<QMap<QUrl,QString>>();
|
||||||
int socketErrorMetaTypeId = qRegisterMetaType<QAbstractSocket::SocketError>();
|
int socketErrorMetaTypeId = qRegisterMetaType<QAbstractSocket::SocketError>();
|
||||||
|
@ -56,6 +57,7 @@ void registerMetaTypes(QScriptEngine* engine) {
|
||||||
qScriptRegisterMetaType(engine, qColorToScriptValue, qColorFromScriptValue);
|
qScriptRegisterMetaType(engine, qColorToScriptValue, qColorFromScriptValue);
|
||||||
qScriptRegisterMetaType(engine, qURLToScriptValue, qURLFromScriptValue);
|
qScriptRegisterMetaType(engine, qURLToScriptValue, qURLFromScriptValue);
|
||||||
qScriptRegisterMetaType(engine, pickRayToScriptValue, pickRayFromScriptValue);
|
qScriptRegisterMetaType(engine, pickRayToScriptValue, pickRayFromScriptValue);
|
||||||
|
qScriptRegisterMetaType(engine, rayPickResultToScriptValue, rayPickResultFromScriptValue);
|
||||||
qScriptRegisterMetaType(engine, collisionToScriptValue, collisionFromScriptValue);
|
qScriptRegisterMetaType(engine, collisionToScriptValue, collisionFromScriptValue);
|
||||||
qScriptRegisterMetaType(engine, quuidToScriptValue, quuidFromScriptValue);
|
qScriptRegisterMetaType(engine, quuidToScriptValue, quuidFromScriptValue);
|
||||||
qScriptRegisterMetaType(engine, qSizeFToScriptValue, qSizeFFromScriptValue);
|
qScriptRegisterMetaType(engine, qSizeFToScriptValue, qSizeFFromScriptValue);
|
||||||
|
@ -751,6 +753,23 @@ void pickRayFromScriptValue(const QScriptValue& object, PickRay& pickRay) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QScriptValue rayPickResultToScriptValue(QScriptEngine* engine, const RayPickResult& rayPickResult) {
|
||||||
|
QScriptValue obj = engine->newObject();
|
||||||
|
obj.setProperty("type", rayPickResult.type);
|
||||||
|
QScriptValue objectID = quuidToScriptValue(engine, rayPickResult.objectID);
|
||||||
|
obj.setProperty("objectID", objectID);
|
||||||
|
obj.setProperty("distance", rayPickResult.distance);
|
||||||
|
QScriptValue intersection = vec3toScriptValue(engine, rayPickResult.intersection);
|
||||||
|
obj.setProperty("intersection", intersection);
|
||||||
|
QScriptValue surfaceNormal = vec3toScriptValue(engine, rayPickResult.surfaceNormal);
|
||||||
|
obj.setProperty("surfaceNormal", surfaceNormal);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rayPickResultFromScriptValue(const QScriptValue& object, RayPickResult& rayPickResult) {
|
||||||
|
// TODO: cannot currently accept RayPickResults from JS
|
||||||
|
}
|
||||||
|
|
||||||
QScriptValue collisionToScriptValue(QScriptEngine* engine, const Collision& collision) {
|
QScriptValue collisionToScriptValue(QScriptEngine* engine, const Collision& collision) {
|
||||||
QScriptValue obj = engine->newObject();
|
QScriptValue obj = engine->newObject();
|
||||||
obj.setProperty("type", collision.type);
|
obj.setProperty("type", collision.type);
|
||||||
|
|
|
@ -136,6 +136,29 @@ Q_DECLARE_METATYPE(PickRay)
|
||||||
QScriptValue pickRayToScriptValue(QScriptEngine* engine, const PickRay& pickRay);
|
QScriptValue pickRayToScriptValue(QScriptEngine* engine, const PickRay& pickRay);
|
||||||
void pickRayFromScriptValue(const QScriptValue& object, PickRay& pickRay);
|
void pickRayFromScriptValue(const QScriptValue& object, PickRay& pickRay);
|
||||||
|
|
||||||
|
enum IntersectionType {
|
||||||
|
NONE,
|
||||||
|
ENTITY,
|
||||||
|
OVERLAY,
|
||||||
|
AVATAR,
|
||||||
|
HUD
|
||||||
|
};
|
||||||
|
|
||||||
|
class RayPickResult {
|
||||||
|
public:
|
||||||
|
RayPickResult() {}
|
||||||
|
RayPickResult(const IntersectionType type, const QUuid& objectID, const float distance, const glm::vec3& intersection, const glm::vec3& surfaceNormal = glm::vec3(NAN)) :
|
||||||
|
type(type), objectID(objectID), distance(distance), intersection(intersection), surfaceNormal(surfaceNormal) {}
|
||||||
|
IntersectionType type { NONE };
|
||||||
|
QUuid objectID { 0 };
|
||||||
|
float distance { FLT_MAX };
|
||||||
|
glm::vec3 intersection { NAN };
|
||||||
|
glm::vec3 surfaceNormal { NAN };
|
||||||
|
};
|
||||||
|
Q_DECLARE_METATYPE(RayPickResult)
|
||||||
|
QScriptValue rayPickResultToScriptValue(QScriptEngine* engine, const RayPickResult& rayPickResult);
|
||||||
|
void rayPickResultFromScriptValue(const QScriptValue& object, RayPickResult& rayPickResult);
|
||||||
|
|
||||||
enum ContactEventType {
|
enum ContactEventType {
|
||||||
CONTACT_EVENT_TYPE_START,
|
CONTACT_EVENT_TYPE_START,
|
||||||
CONTACT_EVENT_TYPE_CONTINUE,
|
CONTACT_EVENT_TYPE_CONTINUE,
|
||||||
|
|
|
@ -43,7 +43,6 @@
|
||||||
|
|
||||||
#include "types/FileTypeProfile.h"
|
#include "types/FileTypeProfile.h"
|
||||||
#include "types/HFWebEngineProfile.h"
|
#include "types/HFWebEngineProfile.h"
|
||||||
#include "types/HFTabletWebEngineProfile.h"
|
|
||||||
#include "types/SoundEffect.h"
|
#include "types/SoundEffect.h"
|
||||||
|
|
||||||
#include "Logging.h"
|
#include "Logging.h"
|
||||||
|
@ -328,7 +327,6 @@ void initializeQmlEngine(QQmlEngine* engine, QQuickWindow* window) {
|
||||||
}
|
}
|
||||||
rootContext->setContextProperty("FileTypeProfile", new FileTypeProfile(rootContext));
|
rootContext->setContextProperty("FileTypeProfile", new FileTypeProfile(rootContext));
|
||||||
rootContext->setContextProperty("HFWebEngineProfile", new HFWebEngineProfile(rootContext));
|
rootContext->setContextProperty("HFWebEngineProfile", new HFWebEngineProfile(rootContext));
|
||||||
rootContext->setContextProperty("HFTabletWebEngineProfile", new HFTabletWebEngineProfile(rootContext));
|
|
||||||
rootContext->setContextProperty("Paths", DependencyManager::get<PathUtils>().data());
|
rootContext->setContextProperty("Paths", DependencyManager::get<PathUtils>().data());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -604,9 +604,9 @@ void TabletProxy::loadWebScreenOnTop(const QVariant& url, const QString& injectJ
|
||||||
_state = State::Web;
|
_state = State::Web;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TabletProxy::gotoWebScreen(const QString& url, const QString& injectedJavaScriptUrl) {
|
void TabletProxy::gotoWebScreen(const QString& url, const QString& injectedJavaScriptUrl, bool loadOtherBase) {
|
||||||
if (QThread::currentThread() != thread()) {
|
if (QThread::currentThread() != thread()) {
|
||||||
QMetaObject::invokeMethod(this, "gotoWebScreen", Q_ARG(QString, url), Q_ARG(QString, injectedJavaScriptUrl));
|
QMetaObject::invokeMethod(this, "gotoWebScreen", Q_ARG(QString, url), Q_ARG(QString, injectedJavaScriptUrl), Q_ARG(bool, loadOtherBase));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -619,7 +619,11 @@ void TabletProxy::gotoWebScreen(const QString& url, const QString& injectedJavaS
|
||||||
|
|
||||||
if (root) {
|
if (root) {
|
||||||
removeButtonsFromHomeScreen();
|
removeButtonsFromHomeScreen();
|
||||||
QMetaObject::invokeMethod(root, "loadWebBase");
|
if (loadOtherBase) {
|
||||||
|
QMetaObject::invokeMethod(root, "loadTabletWebBase");
|
||||||
|
} else {
|
||||||
|
QMetaObject::invokeMethod(root, "loadWebBase");
|
||||||
|
}
|
||||||
QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true)));
|
QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true)));
|
||||||
QMetaObject::invokeMethod(root, "loadWebUrl", Q_ARG(const QVariant&, QVariant(url)), Q_ARG(const QVariant&, QVariant(injectedJavaScriptUrl)));
|
QMetaObject::invokeMethod(root, "loadWebUrl", Q_ARG(const QVariant&, QVariant(url)), Q_ARG(const QVariant&, QVariant(injectedJavaScriptUrl)));
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,7 +122,7 @@ public:
|
||||||
* @param [injectedJavaScriptUrl] {string} optional url to an additional JS script to inject into the web page.
|
* @param [injectedJavaScriptUrl] {string} optional url to an additional JS script to inject into the web page.
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE void gotoWebScreen(const QString& url);
|
Q_INVOKABLE void gotoWebScreen(const QString& url);
|
||||||
Q_INVOKABLE void gotoWebScreen(const QString& url, const QString& injectedJavaScriptUrl);
|
Q_INVOKABLE void gotoWebScreen(const QString& url, const QString& injectedJavaScriptUrl, bool loadOtherBase = false);
|
||||||
|
|
||||||
Q_INVOKABLE void loadQMLSource(const QVariant& path);
|
Q_INVOKABLE void loadQMLSource(const QVariant& path);
|
||||||
// FIXME: This currently relies on a script initializing the tablet (hence the bool denoting success);
|
// FIXME: This currently relies on a script initializing the tablet (hence the bool denoting success);
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
//
|
|
||||||
// HFTabletWebEngineProfile.h
|
|
||||||
// interface/src/networking
|
|
||||||
//
|
|
||||||
// Created by Dante Ruiz on 2017-03-31.
|
|
||||||
// Copyright 2017 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "HFTabletWebEngineProfile.h"
|
|
||||||
#include "HFTabletWebEngineRequestInterceptor.h"
|
|
||||||
|
|
||||||
static const QString QML_WEB_ENGINE_NAME = "qmlTabletWebEngine";
|
|
||||||
|
|
||||||
HFTabletWebEngineProfile::HFTabletWebEngineProfile(QObject* parent) : QQuickWebEngineProfile(parent) {
|
|
||||||
|
|
||||||
static const QString WEB_ENGINE_USER_AGENT = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36";
|
|
||||||
|
|
||||||
setHttpUserAgent(WEB_ENGINE_USER_AGENT);
|
|
||||||
setStorageName(QML_WEB_ENGINE_NAME);
|
|
||||||
|
|
||||||
auto requestInterceptor = new HFTabletWebEngineRequestInterceptor(this);
|
|
||||||
setRequestInterceptor(requestInterceptor);
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
//
|
|
||||||
// HFTabletWebEngineRequestInterceptor.cpp
|
|
||||||
// interface/src/networking
|
|
||||||
//
|
|
||||||
// Created by Dante Ruiz on 2017-3-31.
|
|
||||||
// Copyright 2017 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "HFTabletWebEngineRequestInterceptor.h"
|
|
||||||
#include <QtCore/QDebug>
|
|
||||||
#include "AccountManager.h"
|
|
||||||
|
|
||||||
bool isTabletAuthableHighFidelityURL(const QUrl& url) {
|
|
||||||
static const QStringList HF_HOSTS = {
|
|
||||||
"highfidelity.com", "highfidelity.io",
|
|
||||||
"metaverse.highfidelity.com", "metaverse.highfidelity.io"
|
|
||||||
};
|
|
||||||
|
|
||||||
return url.scheme() == "https" && HF_HOSTS.contains(url.host());
|
|
||||||
}
|
|
||||||
|
|
||||||
void HFTabletWebEngineRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) {
|
|
||||||
// check if this is a request to a highfidelity URL
|
|
||||||
if (isTabletAuthableHighFidelityURL(info.requestUrl())) {
|
|
||||||
// if we have an access token, add it to the right HTTP header for authorization
|
|
||||||
auto accountManager = DependencyManager::get<AccountManager>();
|
|
||||||
|
|
||||||
if (accountManager->hasValidAccessToken()) {
|
|
||||||
static const QString OAUTH_AUTHORIZATION_HEADER = "Authorization";
|
|
||||||
|
|
||||||
QString bearerTokenString = "Bearer " + accountManager->getAccountInfo().getAccessToken().token;
|
|
||||||
info.setHttpHeader(OAUTH_AUTHORIZATION_HEADER.toLocal8Bit(), bearerTokenString.toLocal8Bit());
|
|
||||||
}
|
|
||||||
|
|
||||||
static const QString USER_AGENT = "User-Agent";
|
|
||||||
QString tokenString = "Chrome/48.0 (HighFidelityInterface)";
|
|
||||||
info.setHttpHeader(USER_AGENT.toLocal8Bit(), tokenString.toLocal8Bit());
|
|
||||||
} else {
|
|
||||||
static const QString USER_AGENT = "User-Agent";
|
|
||||||
QString tokenString = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36";
|
|
||||||
info.setHttpHeader(USER_AGENT.toLocal8Bit(), tokenString.toLocal8Bit());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -18,8 +18,6 @@ static const QString QML_WEB_ENGINE_STORAGE_NAME = "qmlWebEngine";
|
||||||
HFWebEngineProfile::HFWebEngineProfile(QObject* parent) :
|
HFWebEngineProfile::HFWebEngineProfile(QObject* parent) :
|
||||||
QQuickWebEngineProfile(parent)
|
QQuickWebEngineProfile(parent)
|
||||||
{
|
{
|
||||||
static const QString WEB_ENGINE_USER_AGENT = "Chrome/48.0 (HighFidelityInterface)";
|
|
||||||
setHttpUserAgent(WEB_ENGINE_USER_AGENT);
|
|
||||||
setStorageName(QML_WEB_ENGINE_STORAGE_NAME);
|
setStorageName(QML_WEB_ENGINE_STORAGE_NAME);
|
||||||
|
|
||||||
// we use the HFWebEngineRequestInterceptor to make sure that web requests are authenticated for the interface user
|
// we use the HFWebEngineRequestInterceptor to make sure that web requests are authenticated for the interface user
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "NetworkingConstants.h"
|
#include "NetworkingConstants.h"
|
||||||
|
|
||||||
#include <QtCore/QDebug>
|
#include <QtCore/QDebug>
|
||||||
|
#include <SettingHandle.h>
|
||||||
|
|
||||||
#include "AccountManager.h"
|
#include "AccountManager.h"
|
||||||
|
|
||||||
|
@ -42,7 +43,8 @@ namespace {
|
||||||
|
|
||||||
void RequestFilters::interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info) {
|
void RequestFilters::interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info) {
|
||||||
// check if this is a request to a highfidelity URL
|
// check if this is a request to a highfidelity URL
|
||||||
if (isAuthableHighFidelityURL(info.requestUrl())) {
|
bool isAuthable = isAuthableHighFidelityURL(info.requestUrl());
|
||||||
|
if (isAuthable) {
|
||||||
// if we have an access token, add it to the right HTTP header for authorization
|
// if we have an access token, add it to the right HTTP header for authorization
|
||||||
auto accountManager = DependencyManager::get<AccountManager>();
|
auto accountManager = DependencyManager::get<AccountManager>();
|
||||||
|
|
||||||
|
@ -53,6 +55,17 @@ void RequestFilters::interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info)
|
||||||
info.setHttpHeader(OAUTH_AUTHORIZATION_HEADER.toLocal8Bit(), bearerTokenString.toLocal8Bit());
|
info.setHttpHeader(OAUTH_AUTHORIZATION_HEADER.toLocal8Bit(), bearerTokenString.toLocal8Bit());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
static const QString USER_AGENT = "User-Agent";
|
||||||
|
const QString tokenStringMobile{ "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36" };
|
||||||
|
const QString tokenStringMetaverse{ "Chrome/48.0 (HighFidelityInterface)" };
|
||||||
|
|
||||||
|
// During the period in which we have HFC commerce in the system, but not applied everywhere:
|
||||||
|
const QString tokenStringCommerce{ "Chrome/48.0 (HighFidelityInterface WithHFC)" };
|
||||||
|
static Setting::Handle<bool> _settingSwitch{ "inspectionMode", false };
|
||||||
|
bool isMoney = _settingSwitch.get();
|
||||||
|
|
||||||
|
const QString tokenString = !isAuthable ? tokenStringMobile : (isMoney ? tokenStringCommerce : tokenStringMetaverse);
|
||||||
|
info.setHttpHeader(USER_AGENT.toLocal8Bit(), tokenString.toLocal8Bit());
|
||||||
}
|
}
|
||||||
|
|
||||||
void RequestFilters::interceptFileType(QWebEngineUrlRequestInfo& info) {
|
void RequestFilters::interceptFileType(QWebEngineUrlRequestInfo& info) {
|
||||||
|
|
|
@ -51,8 +51,6 @@ bool OculusBaseDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
|
||||||
});
|
});
|
||||||
|
|
||||||
withNonPresentThreadLock([&] {
|
withNonPresentThreadLock([&] {
|
||||||
_uiModelTransform = DependencyManager::get<CompositorHelper>()->getModelTransform();
|
|
||||||
_handPoses = handPoses;
|
|
||||||
_frameInfos[frameIndex] = _currentRenderFrameInfo;
|
_frameInfos[frameIndex] = _currentRenderFrameInfo;
|
||||||
});
|
});
|
||||||
return Parent::beginFrameRender(frameIndex);
|
return Parent::beginFrameRender(frameIndex);
|
||||||
|
|
|
@ -594,9 +594,6 @@ bool OpenVrDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
|
||||||
}
|
}
|
||||||
|
|
||||||
withNonPresentThreadLock([&] {
|
withNonPresentThreadLock([&] {
|
||||||
_uiModelTransform = DependencyManager::get<CompositorHelper>()->getModelTransform();
|
|
||||||
// Make controller poses available to the presentation thread
|
|
||||||
_handPoses = handPoses;
|
|
||||||
_frameInfos[frameIndex] = _currentRenderFrameInfo;
|
_frameInfos[frameIndex] = _currentRenderFrameInfo;
|
||||||
});
|
});
|
||||||
return Parent::beginFrameRender(frameIndex);
|
return Parent::beginFrameRender(frameIndex);
|
||||||
|
|
|
@ -197,53 +197,24 @@ Mouse.prototype.restoreRotateCursor = function() {
|
||||||
|
|
||||||
var mouse = new Mouse();
|
var mouse = new Mouse();
|
||||||
|
|
||||||
|
var beacon = {
|
||||||
// Beacon class stores info for drawing a line at object's target position
|
type: "cube",
|
||||||
function Beacon() {
|
dimensions: {
|
||||||
this.height = 0.10;
|
x: 0.01,
|
||||||
this.overlayID = Overlays.addOverlay("line3d", {
|
y: 0,
|
||||||
color: {
|
z: 0.01
|
||||||
red: 200,
|
},
|
||||||
green: 200,
|
color: {
|
||||||
blue: 200
|
red: 200,
|
||||||
},
|
green: 200,
|
||||||
alpha: 1,
|
blue: 200
|
||||||
visible: false,
|
},
|
||||||
lineWidth: 2
|
alpha: 1,
|
||||||
});
|
solid: true,
|
||||||
}
|
ignoreRayIntersection: true,
|
||||||
|
visible: true
|
||||||
Beacon.prototype.enable = function() {
|
|
||||||
Overlays.editOverlay(this.overlayID, {
|
|
||||||
visible: true
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Beacon.prototype.disable = function() {
|
|
||||||
Overlays.editOverlay(this.overlayID, {
|
|
||||||
visible: false
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Beacon.prototype.updatePosition = function(position) {
|
|
||||||
Overlays.editOverlay(this.overlayID, {
|
|
||||||
visible: true,
|
|
||||||
start: {
|
|
||||||
x: position.x,
|
|
||||||
y: position.y + this.height,
|
|
||||||
z: position.z
|
|
||||||
},
|
|
||||||
end: {
|
|
||||||
x: position.x,
|
|
||||||
y: position.y - this.height,
|
|
||||||
z: position.z
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var beacon = new Beacon();
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: play sounds again when we aren't leaking AudioInjector threads
|
// TODO: play sounds again when we aren't leaking AudioInjector threads
|
||||||
// var grabSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/CloseClamp.wav");
|
// var grabSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/CloseClamp.wav");
|
||||||
// var releaseSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/ReleaseClamp.wav");
|
// var releaseSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/ReleaseClamp.wav");
|
||||||
|
@ -285,6 +256,21 @@ function Grabber() {
|
||||||
|
|
||||||
this.liftKey = false; // SHIFT
|
this.liftKey = false; // SHIFT
|
||||||
this.rotateKey = false; // CONTROL
|
this.rotateKey = false; // CONTROL
|
||||||
|
|
||||||
|
this.mouseRayOverlays = RayPick.createRayPick({
|
||||||
|
joint: "Mouse",
|
||||||
|
filter: RayPick.PICK_OVERLAYS,
|
||||||
|
enabled: true
|
||||||
|
});
|
||||||
|
RayPick.setIncludeOverlays(this.mouseRayOverlays, [HMD.tabletID, HMD.tabletScreenID, HMD.homeButtonID]);
|
||||||
|
var renderStates = [{name: "grabbed", end: beacon}];
|
||||||
|
this.mouseRayEntities = LaserPointers.createLaserPointer({
|
||||||
|
joint: "Mouse",
|
||||||
|
filter: RayPick.PICK_ENTITIES,
|
||||||
|
faceAvatar: true,
|
||||||
|
enabled: true,
|
||||||
|
renderStates: renderStates
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Grabber.prototype.computeNewGrabPlane = function() {
|
Grabber.prototype.computeNewGrabPlane = function() {
|
||||||
|
@ -333,40 +319,42 @@ Grabber.prototype.pressEvent = function(event) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
var overlayResult = RayPick.getPrevRayPickResult(this.mouseRayOverlays);
|
||||||
|
if (overlayResult.type != RayPick.INTERSECTED_NONE) {
|
||||||
var overlayResult = Overlays.findRayIntersection(pickRay, true, [HMD.tabletID, HMD.tabletScreenID, HMD.homeButtonID]);
|
|
||||||
if (overlayResult.intersects) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var pickResults = Entities.findRayIntersection(pickRay, true); // accurate picking
|
var pickResults = LaserPointers.getPrevRayPickResult(this.mouseRayEntities);
|
||||||
if (!pickResults.intersects) {
|
if (pickResults.type == RayPick.INTERSECTED_NONE) {
|
||||||
// didn't click on anything
|
LaserPointers.setRenderState(this.mouseRayEntities, "");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var isDynamic = Entities.getEntityProperties(pickResults.entityID, "dynamic").dynamic;
|
var isDynamic = Entities.getEntityProperties(pickResults.objectID, "dynamic").dynamic;
|
||||||
if (!isDynamic) {
|
if (!isDynamic) {
|
||||||
// only grab dynamic objects
|
// only grab dynamic objects
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, pickResults.entityID, DEFAULT_GRABBABLE_DATA);
|
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, pickResults.objectID, DEFAULT_GRABBABLE_DATA);
|
||||||
if (grabbableData.grabbable === false) {
|
if (grabbableData.grabbable === false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LaserPointers.setRenderState(this.mouseRayEntities, "grabbed");
|
||||||
|
LaserPointers.setLockEndUUID(this.mouseRayEntities, pickResults.objectID, false);
|
||||||
|
|
||||||
mouse.startDrag(event);
|
mouse.startDrag(event);
|
||||||
|
|
||||||
var clickedEntity = pickResults.entityID;
|
var clickedEntity = pickResults.objectID;
|
||||||
var entityProperties = Entities.getEntityProperties(clickedEntity);
|
var entityProperties = Entities.getEntityProperties(clickedEntity);
|
||||||
this.startPosition = entityProperties.position;
|
this.startPosition = entityProperties.position;
|
||||||
this.lastRotation = entityProperties.rotation;
|
this.lastRotation = entityProperties.rotation;
|
||||||
var cameraPosition = Camera.getPosition();
|
var cameraPosition = Camera.getPosition();
|
||||||
|
|
||||||
var objectBoundingDiameter = Vec3.length(entityProperties.dimensions);
|
var objectBoundingDiameter = Vec3.length(entityProperties.dimensions);
|
||||||
beacon.height = objectBoundingDiameter;
|
beacon.dimensions.y = objectBoundingDiameter;
|
||||||
|
LaserPointers.editRenderState(this.mouseRayEntities, "grabbed", {end: beacon});
|
||||||
this.maxDistance = objectBoundingDiameter / MAX_SOLID_ANGLE;
|
this.maxDistance = objectBoundingDiameter / MAX_SOLID_ANGLE;
|
||||||
if (Vec3.distance(this.startPosition, cameraPosition) > this.maxDistance) {
|
if (Vec3.distance(this.startPosition, cameraPosition) > this.maxDistance) {
|
||||||
// don't allow grabs of things far away
|
// don't allow grabs of things far away
|
||||||
|
@ -385,6 +373,7 @@ Grabber.prototype.pressEvent = function(event) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// compute the grab point
|
// compute the grab point
|
||||||
|
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||||
var nearestPoint = Vec3.subtract(this.startPosition, cameraPosition);
|
var nearestPoint = Vec3.subtract(this.startPosition, cameraPosition);
|
||||||
var distanceToGrab = Vec3.dot(nearestPoint, pickRay.direction);
|
var distanceToGrab = Vec3.dot(nearestPoint, pickRay.direction);
|
||||||
nearestPoint = Vec3.multiply(distanceToGrab, pickRay.direction);
|
nearestPoint = Vec3.multiply(distanceToGrab, pickRay.direction);
|
||||||
|
@ -395,8 +384,6 @@ Grabber.prototype.pressEvent = function(event) {
|
||||||
|
|
||||||
this.computeNewGrabPlane();
|
this.computeNewGrabPlane();
|
||||||
|
|
||||||
beacon.updatePosition(this.startPosition);
|
|
||||||
|
|
||||||
if (!entityIsGrabbedByOther(this.entityID)) {
|
if (!entityIsGrabbedByOther(this.entityID)) {
|
||||||
this.moveEvent(event);
|
this.moveEvent(event);
|
||||||
}
|
}
|
||||||
|
@ -431,7 +418,7 @@ Grabber.prototype.releaseEvent = function(event) {
|
||||||
}
|
}
|
||||||
this.actionID = null;
|
this.actionID = null;
|
||||||
|
|
||||||
beacon.disable();
|
LaserPointers.setRenderState(this.mouseRayEntities, "");
|
||||||
|
|
||||||
var args = "mouse";
|
var args = "mouse";
|
||||||
Entities.callEntityMethod(this.entityID, "releaseGrab", args);
|
Entities.callEntityMethod(this.entityID, "releaseGrab", args);
|
||||||
|
@ -552,7 +539,6 @@ Grabber.prototype.moveEventProcess = function() {
|
||||||
ttl: ACTION_TTL
|
ttl: ACTION_TTL
|
||||||
};
|
};
|
||||||
|
|
||||||
beacon.updatePosition(this.targetPosition);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.actionID) {
|
if (!this.actionID) {
|
||||||
|
@ -586,6 +572,11 @@ Grabber.prototype.keyPressEvent = function(event) {
|
||||||
this.computeNewGrabPlane();
|
this.computeNewGrabPlane();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Grabber.prototype.cleanup = function() {
|
||||||
|
LaserPointers.removeLaserPointer(this.mouseRayEntities);
|
||||||
|
RayPick.removeRayPick(this.mouseRayOverlays);
|
||||||
|
};
|
||||||
|
|
||||||
var grabber = new Grabber();
|
var grabber = new Grabber();
|
||||||
|
|
||||||
function pressEvent(event) {
|
function pressEvent(event) {
|
||||||
|
@ -608,10 +599,15 @@ function keyReleaseEvent(event) {
|
||||||
grabber.keyReleaseEvent(event);
|
grabber.keyReleaseEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cleanup() {
|
||||||
|
grabber.cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
Controller.mousePressEvent.connect(pressEvent);
|
Controller.mousePressEvent.connect(pressEvent);
|
||||||
Controller.mouseMoveEvent.connect(moveEvent);
|
Controller.mouseMoveEvent.connect(moveEvent);
|
||||||
Controller.mouseReleaseEvent.connect(releaseEvent);
|
Controller.mouseReleaseEvent.connect(releaseEvent);
|
||||||
Controller.keyPressEvent.connect(keyPressEvent);
|
Controller.keyPressEvent.connect(keyPressEvent);
|
||||||
Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
||||||
|
Script.scriptEnding.connect(cleanup);
|
||||||
|
|
||||||
}()); // END LOCAL_SCOPE
|
}()); // END LOCAL_SCOPE
|
||||||
|
|
|
@ -64,8 +64,6 @@ var HAPTIC_STYLUS_DURATION = 20.0;
|
||||||
var HAPTIC_LASER_UI_STRENGTH = 1.0;
|
var HAPTIC_LASER_UI_STRENGTH = 1.0;
|
||||||
var HAPTIC_LASER_UI_DURATION = 20.0;
|
var HAPTIC_LASER_UI_DURATION = 20.0;
|
||||||
|
|
||||||
var HAND_HEAD_MIX_RATIO = 0.0; // 0 = only use hands for search/move. 1 = only use head for search/move.
|
|
||||||
|
|
||||||
var PICK_WITH_HAND_RAY = true;
|
var PICK_WITH_HAND_RAY = true;
|
||||||
|
|
||||||
var EQUIP_SPHERE_SCALE_FACTOR = 0.65;
|
var EQUIP_SPHERE_SCALE_FACTOR = 0.65;
|
||||||
|
@ -157,7 +155,6 @@ var INCHES_TO_METERS = 1.0 / 39.3701;
|
||||||
// these control how long an abandoned pointer line or action will hang around
|
// these control how long an abandoned pointer line or action will hang around
|
||||||
var ACTION_TTL = 15; // seconds
|
var ACTION_TTL = 15; // seconds
|
||||||
var ACTION_TTL_REFRESH = 5;
|
var ACTION_TTL_REFRESH = 5;
|
||||||
var PICKS_PER_SECOND_PER_HAND = 60;
|
|
||||||
var MSECS_PER_SEC = 1000.0;
|
var MSECS_PER_SEC = 1000.0;
|
||||||
var GRABBABLE_PROPERTIES = [
|
var GRABBABLE_PROPERTIES = [
|
||||||
"position",
|
"position",
|
||||||
|
@ -355,7 +352,7 @@ function projectOntoXYPlane(worldPos, position, rotation, dimensions, registrati
|
||||||
|
|
||||||
function projectOntoEntityXYPlane(entityID, worldPos) {
|
function projectOntoEntityXYPlane(entityID, worldPos) {
|
||||||
var props = entityPropertiesCache.getProps(entityID);
|
var props = entityPropertiesCache.getProps(entityID);
|
||||||
if (props) {
|
if (props && props.position && props.rotation && props.dimensions && props.registrationPoint) {
|
||||||
return projectOntoXYPlane(worldPos, props.position, props.rotation, props.dimensions, props.registrationPoint);
|
return projectOntoXYPlane(worldPos, props.position, props.rotation, props.dimensions, props.registrationPoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -371,16 +368,20 @@ function projectOntoOverlayXYPlane(overlayID, worldPos) {
|
||||||
var resolution = Overlays.getProperty(overlayID, "resolution");
|
var resolution = Overlays.getProperty(overlayID, "resolution");
|
||||||
resolution.z = 1; // Circumvent divide-by-zero.
|
resolution.z = 1; // Circumvent divide-by-zero.
|
||||||
var scale = Overlays.getProperty(overlayID, "dimensions");
|
var scale = Overlays.getProperty(overlayID, "dimensions");
|
||||||
scale.z = 0.01; // overlay dimensions are 2D, not 3D.
|
if (scale) {
|
||||||
dimensions = Vec3.multiplyVbyV(Vec3.multiply(resolution, INCHES_TO_METERS / dpi), scale);
|
scale.z = 0.01; // overlay dimensions are 2D, not 3D.
|
||||||
|
dimensions = Vec3.multiplyVbyV(Vec3.multiply(resolution, INCHES_TO_METERS / dpi), scale);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
dimensions = Overlays.getProperty(overlayID, "dimensions");
|
dimensions = Overlays.getProperty(overlayID, "dimensions");
|
||||||
if (dimensions.z) {
|
if (dimensions && dimensions.z) {
|
||||||
dimensions.z = 0.01; // overlay dimensions are 2D, not 3D.
|
dimensions.z = 0.01; // overlay dimensions are 2D, not 3D.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return projectOntoXYPlane(worldPos, position, rotation, dimensions, DEFAULT_REGISTRATION_POINT);
|
if (position && rotation && dimensions) {
|
||||||
|
return projectOntoXYPlane(worldPos, position, rotation, dimensions, DEFAULT_REGISTRATION_POINT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handLaserIntersectItem(position, rotation, start) {
|
function handLaserIntersectItem(position, rotation, start) {
|
||||||
|
@ -458,15 +459,6 @@ function entityHasActions(entityID) {
|
||||||
return Entities.getActionIDs(entityID).length > 0;
|
return Entities.getActionIDs(entityID).length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function findRayIntersection(pickRay, precise, include, exclude) {
|
|
||||||
var entities = Entities.findRayIntersection(pickRay, precise, include, exclude, true);
|
|
||||||
var overlays = Overlays.findRayIntersection(pickRay, precise, [], [HMD.tabletID]);
|
|
||||||
if (!overlays.intersects || (entities.intersects && (entities.distance <= overlays.distance))) {
|
|
||||||
return entities;
|
|
||||||
}
|
|
||||||
return overlays;
|
|
||||||
}
|
|
||||||
|
|
||||||
function entityIsGrabbedByOther(entityID) {
|
function entityIsGrabbedByOther(entityID) {
|
||||||
// by convention, a distance grab sets the tag of its action to be grab-*owner-session-id*.
|
// by convention, a distance grab sets the tag of its action to be grab-*owner-session-id*.
|
||||||
var actionIDs = Entities.getActionIDs(entityID);
|
var actionIDs = Entities.getActionIDs(entityID);
|
||||||
|
@ -1062,6 +1054,74 @@ function getControllerJointIndex(hand) {
|
||||||
// global EquipHotspotBuddy instance
|
// global EquipHotspotBuddy instance
|
||||||
var equipHotspotBuddy = new EquipHotspotBuddy();
|
var equipHotspotBuddy = new EquipHotspotBuddy();
|
||||||
|
|
||||||
|
var halfPath = {
|
||||||
|
type: "line3d",
|
||||||
|
color: COLORS_GRAB_SEARCHING_HALF_SQUEEZE,
|
||||||
|
visible: true,
|
||||||
|
alpha: 1,
|
||||||
|
solid: true,
|
||||||
|
glow: 1.0,
|
||||||
|
lineWidth: 5,
|
||||||
|
ignoreRayIntersection: true, // always ignore this
|
||||||
|
drawInFront: true, // Even when burried inside of something, show it.
|
||||||
|
parentID: AVATAR_SELF_ID
|
||||||
|
}
|
||||||
|
var halfEnd = {
|
||||||
|
type: "sphere",
|
||||||
|
solid: true,
|
||||||
|
color: COLORS_GRAB_SEARCHING_HALF_SQUEEZE,
|
||||||
|
alpha: 0.9,
|
||||||
|
ignoreRayIntersection: true,
|
||||||
|
drawInFront: true, // Even when burried inside of something, show it.
|
||||||
|
visible: true
|
||||||
|
}
|
||||||
|
var fullPath = {
|
||||||
|
type: "line3d",
|
||||||
|
color: COLORS_GRAB_SEARCHING_FULL_SQUEEZE,
|
||||||
|
visible: true,
|
||||||
|
alpha: 1,
|
||||||
|
solid: true,
|
||||||
|
glow: 1.0,
|
||||||
|
lineWidth: 5,
|
||||||
|
ignoreRayIntersection: true, // always ignore this
|
||||||
|
drawInFront: true, // Even when burried inside of something, show it.
|
||||||
|
parentID: AVATAR_SELF_ID
|
||||||
|
}
|
||||||
|
var fullEnd = {
|
||||||
|
type: "sphere",
|
||||||
|
solid: true,
|
||||||
|
color: COLORS_GRAB_SEARCHING_FULL_SQUEEZE,
|
||||||
|
alpha: 0.9,
|
||||||
|
ignoreRayIntersection: true,
|
||||||
|
drawInFront: true, // Even when burried inside of something, show it.
|
||||||
|
visible: true
|
||||||
|
}
|
||||||
|
var holdPath = {
|
||||||
|
type: "line3d",
|
||||||
|
color: COLORS_GRAB_DISTANCE_HOLD,
|
||||||
|
visible: true,
|
||||||
|
alpha: 1,
|
||||||
|
solid: true,
|
||||||
|
glow: 1.0,
|
||||||
|
lineWidth: 5,
|
||||||
|
ignoreRayIntersection: true, // always ignore this
|
||||||
|
drawInFront: true, // Even when burried inside of something, show it.
|
||||||
|
parentID: AVATAR_SELF_ID
|
||||||
|
}
|
||||||
|
|
||||||
|
var renderStates = [{name: "half", path: halfPath, end: halfEnd},
|
||||||
|
{name: "full", path: fullPath, end: fullEnd},
|
||||||
|
{name: "hold", path: holdPath}];
|
||||||
|
var headRenderStates = [{name: "half", end: halfEnd},
|
||||||
|
{name: "full", end: fullEnd},
|
||||||
|
{name: "hold", path: holdPath}];
|
||||||
|
|
||||||
|
// how far from camera to search intersection?
|
||||||
|
var DEFAULT_SEARCH_SPHERE_DISTANCE = 1000;
|
||||||
|
var defaultRenderStates = [{name: "half", distance: DEFAULT_SEARCH_SPHERE_DISTANCE, path: halfPath},
|
||||||
|
{name: "full", distance: DEFAULT_SEARCH_SPHERE_DISTANCE, path: fullPath},
|
||||||
|
{name: "hold", distance: DEFAULT_SEARCH_SPHERE_DISTANCE, path: holdPath}];
|
||||||
|
|
||||||
function MyController(hand) {
|
function MyController(hand) {
|
||||||
this.hand = hand;
|
this.hand = hand;
|
||||||
this.autoUnequipCounter = 0;
|
this.autoUnequipCounter = 0;
|
||||||
|
@ -1110,7 +1170,6 @@ function MyController(hand) {
|
||||||
this.grabbedThingID = null; // on this entity.
|
this.grabbedThingID = null; // on this entity.
|
||||||
this.grabbedOverlay = null;
|
this.grabbedOverlay = null;
|
||||||
this.state = STATE_OFF;
|
this.state = STATE_OFF;
|
||||||
this.pointer = null; // entity-id of line object
|
|
||||||
|
|
||||||
this.triggerValue = 0; // rolling average of trigger value
|
this.triggerValue = 0; // rolling average of trigger value
|
||||||
this.triggerClicked = false;
|
this.triggerClicked = false;
|
||||||
|
@ -1119,18 +1178,33 @@ function MyController(hand) {
|
||||||
this.rawThumbValue = 0;
|
this.rawThumbValue = 0;
|
||||||
|
|
||||||
// for visualizations
|
// for visualizations
|
||||||
this.overlayLine = null;
|
this.halfEnd = halfEnd;
|
||||||
this.searchSphere = null;
|
this.fullEnd = fullEnd;
|
||||||
this.otherGrabbingLine = null;
|
this.laserPointer = LaserPointers.createLaserPointer({
|
||||||
|
joint: (hand == RIGHT_HAND) ? "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND",
|
||||||
|
filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS,
|
||||||
|
maxDistance: PICK_MAX_DISTANCE,
|
||||||
|
posOffset: getGrabPointSphereOffset(this.handToController()),
|
||||||
|
renderStates: renderStates,
|
||||||
|
faceAvatar: true,
|
||||||
|
defaultRenderStates: defaultRenderStates
|
||||||
|
});
|
||||||
|
this.headLaserPointer = LaserPointers.createLaserPointer({
|
||||||
|
joint: "Avatar",
|
||||||
|
filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS,
|
||||||
|
maxDistance: PICK_MAX_DISTANCE,
|
||||||
|
renderStates: headRenderStates,
|
||||||
|
faceAvatar: true,
|
||||||
|
defaultRenderStates: defaultRenderStates
|
||||||
|
});
|
||||||
|
LaserPointers.setIgnoreOverlays(this.laserPointer, [HMD.tabletID]);
|
||||||
|
LaserPointers.setIgnoreOverlays(this.headLaserPointer, [HMD.tabletID]);
|
||||||
|
|
||||||
this.otherGrabbingUUID = null;
|
this.otherGrabbingUUID = null;
|
||||||
|
|
||||||
this.waitForTriggerRelease = false;
|
this.waitForTriggerRelease = false;
|
||||||
|
|
||||||
// how far from camera to search intersection?
|
|
||||||
var DEFAULT_SEARCH_SPHERE_DISTANCE = 1000;
|
|
||||||
this.intersectionDistance = 0.0;
|
this.intersectionDistance = 0.0;
|
||||||
this.searchSphereDistance = DEFAULT_SEARCH_SPHERE_DISTANCE;
|
|
||||||
|
|
||||||
this.ignoreIK = false;
|
this.ignoreIK = false;
|
||||||
this.offsetPosition = Vec3.ZERO;
|
this.offsetPosition = Vec3.ZERO;
|
||||||
|
@ -1311,41 +1385,6 @@ function MyController(hand) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.searchSphereOn = function(location, size, color) {
|
|
||||||
|
|
||||||
var rotation = Quat.lookAt(location, Camera.getPosition(), Vec3.UP);
|
|
||||||
var brightColor = colorPow(color, 0.06);
|
|
||||||
if (this.searchSphere === null) {
|
|
||||||
var sphereProperties = {
|
|
||||||
name: "searchSphere",
|
|
||||||
position: location,
|
|
||||||
rotation: rotation,
|
|
||||||
outerRadius: size * 1.2,
|
|
||||||
innerColor: brightColor,
|
|
||||||
outerColor: color,
|
|
||||||
innerAlpha: 0.9,
|
|
||||||
outerAlpha: 0.0,
|
|
||||||
solid: true,
|
|
||||||
ignoreRayIntersection: true,
|
|
||||||
drawInFront: true, // Even when burried inside of something, show it.
|
|
||||||
visible: true
|
|
||||||
};
|
|
||||||
this.searchSphere = Overlays.addOverlay("circle3d", sphereProperties);
|
|
||||||
} else {
|
|
||||||
Overlays.editOverlay(this.searchSphere, {
|
|
||||||
position: location,
|
|
||||||
rotation: rotation,
|
|
||||||
innerColor: brightColor,
|
|
||||||
outerColor: color,
|
|
||||||
innerAlpha: 1.0,
|
|
||||||
outerAlpha: 0.0,
|
|
||||||
outerRadius: size * 1.2,
|
|
||||||
visible: true,
|
|
||||||
ignoreRayIntersection: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.showStylus = function() {
|
this.showStylus = function() {
|
||||||
if (this.stylus) {
|
if (this.stylus) {
|
||||||
return;
|
return;
|
||||||
|
@ -1381,99 +1420,40 @@ function MyController(hand) {
|
||||||
this.stylus = null;
|
this.stylus = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.overlayLineOn = function(closePoint, farPoint, color, farParentID) {
|
this.updateLaserPointer = function() {
|
||||||
if (this.overlayLine === null) {
|
|
||||||
var lineProperties = {
|
|
||||||
name: "line",
|
|
||||||
glow: 1.0,
|
|
||||||
lineWidth: 5,
|
|
||||||
start: closePoint,
|
|
||||||
end: farPoint,
|
|
||||||
color: color,
|
|
||||||
ignoreRayIntersection: true, // always ignore this
|
|
||||||
drawInFront: true, // Even when burried inside of something, show it.
|
|
||||||
visible: true,
|
|
||||||
alpha: 1,
|
|
||||||
parentID: AVATAR_SELF_ID,
|
|
||||||
parentJointIndex: MyAvatar.getJointIndex(this.hand === RIGHT_HAND ?
|
|
||||||
"_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" :
|
|
||||||
"_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"),
|
|
||||||
endParentID: farParentID
|
|
||||||
};
|
|
||||||
this.overlayLine = Overlays.addOverlay("line3d", lineProperties);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if (farParentID && farParentID != NULL_UUID) {
|
|
||||||
Overlays.editOverlay(this.overlayLine, {
|
|
||||||
color: color,
|
|
||||||
endParentID: farParentID
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
Overlays.editOverlay(this.overlayLine, {
|
|
||||||
length: Vec3.distance(farPoint, closePoint),
|
|
||||||
color: color,
|
|
||||||
endParentID: farParentID
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.searchIndicatorOn = function(distantPickRay) {
|
|
||||||
var handPosition = distantPickRay.origin;
|
|
||||||
var SEARCH_SPHERE_SIZE = 0.011;
|
var SEARCH_SPHERE_SIZE = 0.011;
|
||||||
var SEARCH_SPHERE_FOLLOW_RATE = 0.50;
|
var MIN_SPHERE_SIZE = 0.0005;
|
||||||
|
var radius = Math.max(1.2 * SEARCH_SPHERE_SIZE * this.intersectionDistance, MIN_SPHERE_SIZE);
|
||||||
if (this.intersectionDistance > 0) {
|
var dim = {x: radius, y: radius, z: radius};
|
||||||
// If we hit something with our pick ray, move the search sphere toward that distance
|
var mode = "hold";
|
||||||
this.searchSphereDistance = this.searchSphereDistance * SEARCH_SPHERE_FOLLOW_RATE +
|
if (this.state !== STATE_DISTANCE_HOLDING && this.state !== STATE_DISTANCE_ROTATING) {
|
||||||
this.intersectionDistance * (1.0 - SEARCH_SPHERE_FOLLOW_RATE);
|
mode = (this.triggerSmoothedGrab() || this.secondarySqueezed()) ? "full" : "half";
|
||||||
}
|
}
|
||||||
|
|
||||||
var searchSphereLocation = Vec3.sum(distantPickRay.origin,
|
var laserPointerID = PICK_WITH_HAND_RAY ? this.laserPointer : this.headLaserPointer;
|
||||||
Vec3.multiply(distantPickRay.direction, this.searchSphereDistance));
|
if (mode === "full") {
|
||||||
this.searchSphereOn(searchSphereLocation, SEARCH_SPHERE_SIZE * this.searchSphereDistance,
|
var fullEndToEdit = PICK_WITH_HAND_RAY ? this.fullEnd : fullEnd;
|
||||||
(this.triggerSmoothedGrab() || this.secondarySqueezed()) ?
|
fullEndToEdit.dimensions = dim;
|
||||||
COLORS_GRAB_SEARCHING_FULL_SQUEEZE :
|
LaserPointers.editRenderState(laserPointerID, mode, {path: fullPath, end: fullEndToEdit});
|
||||||
COLORS_GRAB_SEARCHING_HALF_SQUEEZE);
|
} else if (mode === "half") {
|
||||||
if (PICK_WITH_HAND_RAY) {
|
var halfEndToEdit = PICK_WITH_HAND_RAY ? this.halfEnd : halfEnd;
|
||||||
this.overlayLineOn(handPosition, searchSphereLocation,
|
halfEndToEdit.dimensions = dim;
|
||||||
(this.triggerSmoothedGrab() || this.secondarySqueezed()) ?
|
LaserPointers.editRenderState(laserPointerID, mode, {path: halfPath, end: halfEndToEdit});
|
||||||
COLORS_GRAB_SEARCHING_FULL_SQUEEZE :
|
|
||||||
COLORS_GRAB_SEARCHING_HALF_SQUEEZE);
|
|
||||||
}
|
}
|
||||||
};
|
LaserPointers.enableLaserPointer(laserPointerID);
|
||||||
|
LaserPointers.setRenderState(laserPointerID, mode);
|
||||||
// Turns off indicators used for searching. Overlay line and sphere.
|
if (this.state === STATE_DISTANCE_HOLDING || this.state === STATE_DISTANCE_ROTATING) {
|
||||||
this.searchIndicatorOff = function() {
|
LaserPointers.setLockEndUUID(laserPointerID, this.grabbedThingID, this.grabbedIsOverlay);
|
||||||
this.searchSphereOff();
|
|
||||||
if (PICK_WITH_HAND_RAY) {
|
|
||||||
this.overlayLineOff();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.otherGrabbingLineOn = function(avatarPosition, entityPosition, color) {
|
|
||||||
if (this.otherGrabbingLine === null) {
|
|
||||||
var lineProperties = {
|
|
||||||
lineWidth: 5,
|
|
||||||
start: avatarPosition,
|
|
||||||
end: entityPosition,
|
|
||||||
color: color,
|
|
||||||
glow: 1.0,
|
|
||||||
ignoreRayIntersection: true,
|
|
||||||
drawInFront: true,
|
|
||||||
visible: true,
|
|
||||||
alpha: 1
|
|
||||||
};
|
|
||||||
this.otherGrabbingLine = Overlays.addOverlay("line3d", lineProperties);
|
|
||||||
} else {
|
} else {
|
||||||
Overlays.editOverlay(this.otherGrabbingLine, {
|
LaserPointers.setLockEndUUID(laserPointerID, null, false);
|
||||||
start: avatarPosition,
|
|
||||||
end: entityPosition,
|
|
||||||
color: color
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.laserPointerOff = function() {
|
||||||
|
var laserPointerID = PICK_WITH_HAND_RAY ? this.laserPointer : this.headLaserPointer;
|
||||||
|
LaserPointers.disableLaserPointer(laserPointerID);
|
||||||
|
};
|
||||||
|
|
||||||
this.evalLightWorldTransform = function(modelPos, modelRot) {
|
this.evalLightWorldTransform = function(modelPos, modelRot) {
|
||||||
|
|
||||||
var MODEL_LIGHT_POSITION = {
|
var MODEL_LIGHT_POSITION = {
|
||||||
|
@ -1494,42 +1474,9 @@ function MyController(hand) {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
this.lineOff = function() {
|
|
||||||
if (this.pointer !== null) {
|
|
||||||
Entities.deleteEntity(this.pointer);
|
|
||||||
}
|
|
||||||
this.pointer = null;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.overlayLineOff = function() {
|
|
||||||
if (this.overlayLine !== null) {
|
|
||||||
Overlays.deleteOverlay(this.overlayLine);
|
|
||||||
}
|
|
||||||
this.overlayLine = null;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.searchSphereOff = function() {
|
|
||||||
if (this.searchSphere !== null) {
|
|
||||||
Overlays.deleteOverlay(this.searchSphere);
|
|
||||||
this.searchSphere = null;
|
|
||||||
this.searchSphereDistance = DEFAULT_SEARCH_SPHERE_DISTANCE;
|
|
||||||
this.intersectionDistance = 0.0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.otherGrabbingLineOff = function() {
|
|
||||||
if (this.otherGrabbingLine !== null) {
|
|
||||||
Overlays.deleteOverlay(this.otherGrabbingLine);
|
|
||||||
}
|
|
||||||
this.otherGrabbingLine = null;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.turnOffVisualizations = function() {
|
this.turnOffVisualizations = function() {
|
||||||
this.overlayLineOff();
|
|
||||||
this.grabPointSphereOff();
|
this.grabPointSphereOff();
|
||||||
this.lineOff();
|
this.laserPointerOff();
|
||||||
this.searchSphereOff();
|
|
||||||
this.otherGrabbingLineOff();
|
|
||||||
restore2DMode();
|
restore2DMode();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1824,12 +1771,12 @@ function MyController(hand) {
|
||||||
var rayPickInfo = this.calcRayPickInfo(this.hand);
|
var rayPickInfo = this.calcRayPickInfo(this.hand);
|
||||||
if (rayPickInfo.isValid) {
|
if (rayPickInfo.isValid) {
|
||||||
this.intersectionDistance = (rayPickInfo.entityID || rayPickInfo.overlayID) ? rayPickInfo.distance : 0;
|
this.intersectionDistance = (rayPickInfo.entityID || rayPickInfo.overlayID) ? rayPickInfo.distance : 0;
|
||||||
this.searchIndicatorOn(rayPickInfo.searchRay);
|
this.updateLaserPointer();
|
||||||
} else {
|
} else {
|
||||||
this.searchIndicatorOff();
|
this.laserPointerOff();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.searchIndicatorOff();
|
this.laserPointerOff();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1870,29 +1817,26 @@ function MyController(hand) {
|
||||||
|
|
||||||
// Performs ray pick test from the hand controller into the world
|
// Performs ray pick test from the hand controller into the world
|
||||||
// @param {number} which hand to use, RIGHT_HAND or LEFT_HAND
|
// @param {number} which hand to use, RIGHT_HAND or LEFT_HAND
|
||||||
// @param {object} if set, use this as as the pick ray, expects origin, direction, and length fields.
|
|
||||||
// @returns {object} returns object with two keys entityID and distance
|
// @returns {object} returns object with two keys entityID and distance
|
||||||
//
|
//
|
||||||
this.calcRayPickInfo = function(hand, pickRayOverride) {
|
this.calcRayPickInfo = function(hand) {
|
||||||
|
var controllerLocation = getControllerWorldLocation(this.handToController(), true);
|
||||||
|
var worldHandPosition = controllerLocation.position;
|
||||||
|
var worldHandRotation = controllerLocation.orientation;
|
||||||
|
|
||||||
var pickRay;
|
var pickRay;
|
||||||
var valid = true
|
var valid = true
|
||||||
if (pickRayOverride) {
|
|
||||||
pickRay = pickRayOverride;
|
|
||||||
} else {
|
|
||||||
var controllerLocation = getControllerWorldLocation(this.handToController(), true);
|
|
||||||
var worldHandPosition = controllerLocation.position;
|
|
||||||
var worldHandRotation = controllerLocation.orientation;
|
|
||||||
valid = !(worldHandPosition === undefined);
|
|
||||||
|
|
||||||
pickRay = {
|
var controllerLocation = getControllerWorldLocation(this.handToController(), true);
|
||||||
origin: PICK_WITH_HAND_RAY ? worldHandPosition : Camera.position,
|
var worldHandPosition = controllerLocation.position;
|
||||||
direction: PICK_WITH_HAND_RAY ? Quat.getUp(worldHandRotation) : Vec3.mix(Quat.getUp(worldHandRotation),
|
var worldHandRotation = controllerLocation.orientation;
|
||||||
Quat.getFront(Camera.orientation),
|
valid = !(worldHandPosition === undefined);
|
||||||
HAND_HEAD_MIX_RATIO),
|
|
||||||
length: PICK_MAX_DISTANCE
|
pickRay = {
|
||||||
};
|
origin: PICK_WITH_HAND_RAY ? worldHandPosition : MyAvatar.getHeadPosition(),
|
||||||
}
|
direction: PICK_WITH_HAND_RAY ? Quat.getUp(worldHandRotation) : Quat.getFront(Camera.orientation),
|
||||||
|
length: PICK_MAX_DISTANCE
|
||||||
|
};
|
||||||
|
|
||||||
var result = {
|
var result = {
|
||||||
entityID: null,
|
entityID: null,
|
||||||
|
@ -1902,28 +1846,17 @@ function MyController(hand) {
|
||||||
isValid: valid
|
isValid: valid
|
||||||
};
|
};
|
||||||
|
|
||||||
var now = Date.now();
|
var laserPointerID = PICK_WITH_HAND_RAY ? this.laserPointer : this.headLaserPointer;
|
||||||
if (now - this.lastPickTime < MSECS_PER_SEC / PICKS_PER_SECOND_PER_HAND) {
|
var intersection = LaserPointers.getPrevRayPickResult(laserPointerID);
|
||||||
return result;
|
|
||||||
}
|
|
||||||
this.lastPickTime = now;
|
|
||||||
|
|
||||||
var intersection;
|
if (intersection.type != RayPick.INTERSECTED_NONE) {
|
||||||
if (USE_BLACKLIST === true && blacklist.length !== 0) {
|
|
||||||
intersection = findRayIntersection(pickRay, true, [], blacklist, true);
|
|
||||||
} else {
|
|
||||||
intersection = findRayIntersection(pickRay, true, [], [], true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (intersection.intersects) {
|
|
||||||
return {
|
return {
|
||||||
entityID: intersection.entityID,
|
entityID: intersection.type == RayPick.INTERSECTED_ENTITY ? intersection.objectID : null,
|
||||||
overlayID: intersection.overlayID,
|
overlayID: intersection.type == RayPick.INTERSECTED_OVERLAY ? intersection.objectID : null,
|
||||||
searchRay: pickRay,
|
searchRay: pickRay,
|
||||||
distance: Vec3.distance(pickRay.origin, intersection.intersection),
|
distance: intersection.distance,
|
||||||
intersection: intersection.intersection,
|
intersection: intersection.intersection,
|
||||||
normal: intersection.surfaceNormal,
|
normal: intersection.surfaceNormal
|
||||||
properties: intersection.properties
|
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return result;
|
return result;
|
||||||
|
@ -2195,7 +2128,6 @@ function MyController(hand) {
|
||||||
var rayPickInfo = this.calcRayPickInfo(this.hand);
|
var rayPickInfo = this.calcRayPickInfo(this.hand);
|
||||||
if (rayPickInfo.entityID || rayPickInfo.overlayID) {
|
if (rayPickInfo.entityID || rayPickInfo.overlayID) {
|
||||||
this.intersectionDistance = rayPickInfo.distance;
|
this.intersectionDistance = rayPickInfo.distance;
|
||||||
this.searchSphereDistance = this.intersectionDistance;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2387,7 +2319,7 @@ function MyController(hand) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isInEditMode()) {
|
if (isInEditMode()) {
|
||||||
this.searchIndicatorOn(rayPickInfo.searchRay);
|
this.updateLaserPointer();
|
||||||
if (this.triggerSmoothedGrab()) {
|
if (this.triggerSmoothedGrab()) {
|
||||||
if (!this.editTriggered){
|
if (!this.editTriggered){
|
||||||
if (rayPickInfo.entityID) {
|
if (rayPickInfo.entityID) {
|
||||||
|
@ -2420,7 +2352,7 @@ function MyController(hand) {
|
||||||
} else {
|
} else {
|
||||||
// potentialFarTriggerEntity = entity;
|
// potentialFarTriggerEntity = entity;
|
||||||
}
|
}
|
||||||
this.otherGrabbingLineOff();
|
this.laserPointerOff();
|
||||||
} else if (this.entityIsDistanceGrabbable(rayPickInfo.entityID, handPosition)) {
|
} else if (this.entityIsDistanceGrabbable(rayPickInfo.entityID, handPosition)) {
|
||||||
if (this.triggerSmoothedGrab() && !isEditing() && farGrabEnabled && farSearching) {
|
if (this.triggerSmoothedGrab() && !isEditing() && farGrabEnabled && farSearching) {
|
||||||
this.grabbedThingID = entity;
|
this.grabbedThingID = entity;
|
||||||
|
@ -2435,26 +2367,18 @@ function MyController(hand) {
|
||||||
} else {
|
} else {
|
||||||
// potentialFarGrabEntity = entity;
|
// potentialFarGrabEntity = entity;
|
||||||
}
|
}
|
||||||
this.otherGrabbingLineOff();
|
this.laserPointerOff();
|
||||||
} else if (this.otherGrabbingUUID !== null) {
|
} else if (this.otherGrabbingUUID !== null) {
|
||||||
if (this.triggerSmoothedGrab() && !isEditing() && farGrabEnabled && farSearching) {
|
if (this.triggerSmoothedGrab() && !isEditing() && farGrabEnabled && farSearching) {
|
||||||
var avatar = AvatarList.getAvatar(this.otherGrabbingUUID);
|
this.updateLaserPointer();
|
||||||
var IN_FRONT_OF_AVATAR = { x: 0, y: 0.2, z: 0.4 }; // Up from hips and in front of avatar.
|
|
||||||
var startPosition = Vec3.sum(avatar.position, Vec3.multiplyQbyV(avatar.rotation, IN_FRONT_OF_AVATAR));
|
|
||||||
var rayHitProps = entityPropertiesCache.getProps(rayPickInfo.entityID);
|
|
||||||
var finishPisition = Vec3.sum(rayHitProps.position, // Entity's centroid.
|
|
||||||
Vec3.multiplyQbyV(rayHitProps.rotation,
|
|
||||||
Vec3.multiplyVbyV(rayHitProps.dimensions,
|
|
||||||
Vec3.subtract(DEFAULT_REGISTRATION_POINT, rayHitProps.registrationPoint))));
|
|
||||||
this.otherGrabbingLineOn(startPosition, finishPisition, COLORS_GRAB_DISTANCE_HOLD);
|
|
||||||
} else {
|
} else {
|
||||||
this.otherGrabbingLineOff();
|
this.laserPointerOff();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.otherGrabbingLineOff();
|
this.laserPointerOff();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.otherGrabbingLineOff();
|
this.laserPointerOff();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updateEquipHaptics(potentialEquipHotspot, handPosition);
|
this.updateEquipHaptics(potentialEquipHotspot, handPosition);
|
||||||
|
@ -2466,7 +2390,7 @@ function MyController(hand) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (farGrabEnabled && farSearching) {
|
if (farGrabEnabled && farSearching) {
|
||||||
this.searchIndicatorOn(rayPickInfo.searchRay);
|
this.updateLaserPointer();
|
||||||
}
|
}
|
||||||
Reticle.setVisible(false);
|
Reticle.setVisible(false);
|
||||||
};
|
};
|
||||||
|
@ -2799,7 +2723,7 @@ function MyController(hand) {
|
||||||
y: 0.0,
|
y: 0.0,
|
||||||
z: objDistance
|
z: objDistance
|
||||||
});
|
});
|
||||||
var change = Vec3.multiply(Vec3.subtract(before, after), HAND_HEAD_MIX_RATIO);
|
var change = Vec3.subtract(before, after) * (PICK_WITH_HAND_RAY ? 0.0 : 1.0);
|
||||||
this.currentCameraOrientation = Camera.orientation;
|
this.currentCameraOrientation = Camera.orientation;
|
||||||
this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, change);
|
this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, change);
|
||||||
}
|
}
|
||||||
|
@ -2808,11 +2732,7 @@ function MyController(hand) {
|
||||||
this.maybeScale(grabbedProperties);
|
this.maybeScale(grabbedProperties);
|
||||||
|
|
||||||
// visualizations
|
// visualizations
|
||||||
var rayPickInfo = this.calcRayPickInfo(this.hand);
|
this.updateLaserPointer();
|
||||||
this.overlayLineOn(rayPickInfo.searchRay.origin,
|
|
||||||
Vec3.subtract(grabbedProperties.position, this.offsetPosition),
|
|
||||||
COLORS_GRAB_DISTANCE_HOLD,
|
|
||||||
this.grabbedThingID);
|
|
||||||
|
|
||||||
var distanceToObject = Vec3.length(Vec3.subtract(MyAvatar.position, this.currentObjectPosition));
|
var distanceToObject = Vec3.length(Vec3.subtract(MyAvatar.position, this.currentObjectPosition));
|
||||||
|
|
||||||
|
@ -2919,9 +2839,7 @@ function MyController(hand) {
|
||||||
this.getOtherHandController().offsetPosition = Vec3.multiplyQbyV(controllerRotationDelta,
|
this.getOtherHandController().offsetPosition = Vec3.multiplyQbyV(controllerRotationDelta,
|
||||||
this.getOtherHandController().offsetPosition);
|
this.getOtherHandController().offsetPosition);
|
||||||
|
|
||||||
var rayPickInfo = this.calcRayPickInfo(this.hand);
|
this.updateLaserPointer();
|
||||||
this.overlayLineOn(rayPickInfo.searchRay.origin, Vec3.subtract(grabbedProperties.position, this.offsetPosition),
|
|
||||||
COLORS_GRAB_DISTANCE_HOLD, this.grabbedThingID);
|
|
||||||
|
|
||||||
this.previousWorldControllerRotation = worldControllerRotation;
|
this.previousWorldControllerRotation = worldControllerRotation;
|
||||||
};
|
};
|
||||||
|
@ -3005,10 +2923,7 @@ function MyController(hand) {
|
||||||
|
|
||||||
this.nearGrabbingEnter = function() {
|
this.nearGrabbingEnter = function() {
|
||||||
this.grabPointSphereOff();
|
this.grabPointSphereOff();
|
||||||
this.lineOff();
|
this.laserPointerOff();
|
||||||
this.overlayLineOff();
|
|
||||||
this.searchSphereOff();
|
|
||||||
this.otherGrabbingLineOff();
|
|
||||||
|
|
||||||
this.dropGestureReset();
|
this.dropGestureReset();
|
||||||
this.clearEquipHaptics();
|
this.clearEquipHaptics();
|
||||||
|
@ -3512,28 +3427,18 @@ function MyController(hand) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var pickRay = {
|
var laserPointerID = PICK_WITH_HAND_RAY ? this.laserPointer : this.headLaserPointer;
|
||||||
origin: getControllerWorldLocation(this.handToController(), false).position,
|
var intersection = LaserPointers.getPrevRayPickResult(laserPointerID);
|
||||||
direction: Quat.getUp(getControllerWorldLocation(this.handToController(), false).orientation)
|
if (intersection.type != RayPick.INTERSECTED_NONE) {
|
||||||
};
|
if (intersection.objectID != this.grabbedThingID) {
|
||||||
|
this.callEntityMethodOnGrabbed("stopFarTrigger");
|
||||||
var now = Date.now();
|
this.grabbedThingID = null;
|
||||||
if (now - this.lastPickTime > MSECS_PER_SEC / PICKS_PER_SECOND_PER_HAND) {
|
this.setState(STATE_OFF, "laser moved off of entity");
|
||||||
var intersection = findRayIntersection(pickRay, true, [], [], true);
|
return;
|
||||||
if (intersection.accurate || intersection.overlayID) {
|
}
|
||||||
this.lastPickTime = now;
|
this.intersectionDistance = intersection.distance;
|
||||||
if (intersection.entityID != this.grabbedThingID) {
|
if (farGrabEnabled) {
|
||||||
this.callEntityMethodOnGrabbed("stopFarTrigger");
|
this.updateLaserPointer();
|
||||||
this.grabbedThingID = null;
|
|
||||||
this.setState(STATE_OFF, "laser moved off of entity");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (intersection.intersects) {
|
|
||||||
this.intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection);
|
|
||||||
}
|
|
||||||
if (farGrabEnabled) {
|
|
||||||
this.searchIndicatorOn(pickRay);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3541,8 +3446,6 @@ function MyController(hand) {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.offEnter = function() {
|
this.offEnter = function() {
|
||||||
// Reuse the existing search distance if lasers were active since
|
|
||||||
// they will be shown in OFF state while in edit mode.
|
|
||||||
var existingSearchDistance = this.searchSphereDistance;
|
var existingSearchDistance = this.searchSphereDistance;
|
||||||
this.release();
|
this.release();
|
||||||
|
|
||||||
|
@ -3664,7 +3567,7 @@ function MyController(hand) {
|
||||||
|
|
||||||
this.intersectionDistance = intersectInfo.distance;
|
this.intersectionDistance = intersectInfo.distance;
|
||||||
if (this.state == STATE_ENTITY_LASER_TOUCHING) {
|
if (this.state == STATE_ENTITY_LASER_TOUCHING) {
|
||||||
this.searchIndicatorOn(intersectInfo.searchRay);
|
this.updateLaserPointer();
|
||||||
}
|
}
|
||||||
Reticle.setVisible(false);
|
Reticle.setVisible(false);
|
||||||
} else {
|
} else {
|
||||||
|
@ -3792,7 +3695,7 @@ function MyController(hand) {
|
||||||
|
|
||||||
this.intersectionDistance = intersectInfo.distance;
|
this.intersectionDistance = intersectInfo.distance;
|
||||||
if (this.state == STATE_OVERLAY_LASER_TOUCHING) {
|
if (this.state == STATE_OVERLAY_LASER_TOUCHING) {
|
||||||
this.searchIndicatorOn(intersectInfo.searchRay);
|
this.updateLaserPointer();
|
||||||
}
|
}
|
||||||
Reticle.setVisible(false);
|
Reticle.setVisible(false);
|
||||||
} else {
|
} else {
|
||||||
|
@ -3935,7 +3838,8 @@ function MyController(hand) {
|
||||||
this.release();
|
this.release();
|
||||||
this.grabPointSphereOff();
|
this.grabPointSphereOff();
|
||||||
this.hideStylus();
|
this.hideStylus();
|
||||||
this.overlayLineOff();
|
LaserPointers.removeLaserPointer(this.laserPointer);
|
||||||
|
LaserPointers.removeLaserPointer(this.headLaserPointer);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.thisHandIsParent = function(props) {
|
this.thisHandIsParent = function(props) {
|
||||||
|
@ -3986,8 +3890,7 @@ function MyController(hand) {
|
||||||
children = children.concat(Entities.getChildrenIDsOfJoint(AVATAR_SELF_ID, controllerCRJointIndex));
|
children = children.concat(Entities.getChildrenIDsOfJoint(AVATAR_SELF_ID, controllerCRJointIndex));
|
||||||
|
|
||||||
children.forEach(function(childID) {
|
children.forEach(function(childID) {
|
||||||
if (childID !== _this.stylus &&
|
if (childID !== _this.stylus) {
|
||||||
childID !== _this.overlayLine) {
|
|
||||||
// we appear to be holding something and this script isn't in a state that would be holding something.
|
// we appear to be holding something and this script isn't in a state that would be holding something.
|
||||||
// unhook it. if we previously took note of this entity's parent, put it back where it was. This
|
// unhook it. if we previously took note of this entity's parent, put it back where it was. This
|
||||||
// works around some problems that happen when more than one hand or avatar is passing something around.
|
// works around some problems that happen when more than one hand or avatar is passing something around.
|
||||||
|
@ -4101,6 +4004,15 @@ Messages.subscribe('Hifi-Hand-RayPick-Blacklist');
|
||||||
Messages.subscribe('Hifi-Object-Manipulation');
|
Messages.subscribe('Hifi-Object-Manipulation');
|
||||||
Messages.subscribe('Hifi-Hand-Drop');
|
Messages.subscribe('Hifi-Hand-Drop');
|
||||||
|
|
||||||
|
var setBlacklist = function() {
|
||||||
|
if (USE_BLACKLIST) {
|
||||||
|
LaserPointers.setIgnoreEntities(leftController.laserPointer, blacklist);
|
||||||
|
LaserPointers.setIgnoreEntities(leftController.headLaserPointer, blacklist);
|
||||||
|
LaserPointers.setIgnoreEntities(rightController.laserPointer, blacklist);
|
||||||
|
LaserPointers.setIgnoreEntities(rightController.headLaserPointer, blacklist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var handleHandMessages = function(channel, message, sender) {
|
var handleHandMessages = function(channel, message, sender) {
|
||||||
var data;
|
var data;
|
||||||
if (sender === MyAvatar.sessionUUID) {
|
if (sender === MyAvatar.sessionUUID) {
|
||||||
|
@ -4175,10 +4087,12 @@ var handleHandMessages = function(channel, message, sender) {
|
||||||
|
|
||||||
if (action === 'add' && index === -1) {
|
if (action === 'add' && index === -1) {
|
||||||
blacklist.push(id);
|
blacklist.push(id);
|
||||||
|
setBlacklist();
|
||||||
}
|
}
|
||||||
if (action === 'remove') {
|
if (action === 'remove') {
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
blacklist.splice(index, 1);
|
blacklist.splice(index, 1);
|
||||||
|
setBlacklist();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -150,15 +150,92 @@ var setReticlePosition = function (point2d) {
|
||||||
Reticle.setPosition(point2d);
|
Reticle.setPosition(point2d);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Generalizations of utilities that work with system and overlay elements.
|
// VISUAL AID -----------
|
||||||
function findRayIntersection(pickRay) {
|
// Same properties as handControllerGrab search sphere
|
||||||
// Check 3D overlays and entities. Argument is an object with origin and direction.
|
var LASER_ALPHA = 0.5;
|
||||||
var result = Overlays.findRayIntersection(pickRay);
|
var LASER_SEARCH_COLOR = {red: 10, green: 10, blue: 255};
|
||||||
if (!result.intersects) {
|
var LASER_TRIGGER_COLOR = {red: 250, green: 10, blue: 10};
|
||||||
result = Entities.findRayIntersection(pickRay, true);
|
var END_DIAMETER = 0.05;
|
||||||
}
|
var systemLaserOn = false;
|
||||||
return result;
|
|
||||||
|
var triggerPath = {
|
||||||
|
type: "line3d",
|
||||||
|
color: LASER_TRIGGER_COLOR,
|
||||||
|
ignoreRayIntersection: true,
|
||||||
|
visible: true,
|
||||||
|
alpha: LASER_ALPHA,
|
||||||
|
solid: true,
|
||||||
|
glow: 1.0,
|
||||||
|
drawHUDLayer: true
|
||||||
}
|
}
|
||||||
|
var triggerEnd = {
|
||||||
|
type: "sphere",
|
||||||
|
dimensions: {x: END_DIAMETER, y: END_DIAMETER, z: END_DIAMETER},
|
||||||
|
color: LASER_TRIGGER_COLOR,
|
||||||
|
ignoreRayIntersection: true,
|
||||||
|
visible: true,
|
||||||
|
alpha: LASER_ALPHA,
|
||||||
|
solid: true,
|
||||||
|
drawHUDLayer: true
|
||||||
|
}
|
||||||
|
|
||||||
|
var searchPath = {
|
||||||
|
type: "line3d",
|
||||||
|
color: LASER_SEARCH_COLOR,
|
||||||
|
ignoreRayIntersection: true,
|
||||||
|
visible: true,
|
||||||
|
alpha: LASER_ALPHA,
|
||||||
|
solid: true,
|
||||||
|
glow: 1.0,
|
||||||
|
drawHUDLayer: true
|
||||||
|
}
|
||||||
|
var searchEnd = {
|
||||||
|
type: "sphere",
|
||||||
|
dimensions: {x: END_DIAMETER, y: END_DIAMETER, z: END_DIAMETER},
|
||||||
|
color: LASER_SEARCH_COLOR,
|
||||||
|
ignoreRayIntersection: true,
|
||||||
|
visible: true,
|
||||||
|
alpha: LASER_ALPHA,
|
||||||
|
solid: true,
|
||||||
|
drawHUDLayer: true
|
||||||
|
}
|
||||||
|
|
||||||
|
var hudRayStates = [{name: "trigger", path: triggerPath, end: triggerEnd},
|
||||||
|
{name: "search", path: searchPath, end: searchEnd}];
|
||||||
|
// this offset needs to match the one in libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp:378
|
||||||
|
var GRAB_POINT_SPHERE_OFFSET_RIGHT = { x: 0.04, y: 0.13, z: 0.039 };
|
||||||
|
var GRAB_POINT_SPHERE_OFFSET_LEFT = { x: -0.04, y: 0.13, z: 0.039 };
|
||||||
|
var hudRayRight = LaserPointers.createLaserPointer({
|
||||||
|
joint: "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND",
|
||||||
|
filter: RayPick.PICK_HUD,
|
||||||
|
posOffset: GRAB_POINT_SPHERE_OFFSET_RIGHT,
|
||||||
|
renderStates: hudRayStates,
|
||||||
|
enabled: true
|
||||||
|
});
|
||||||
|
var hudRayLeft = LaserPointers.createLaserPointer({
|
||||||
|
joint: "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND",
|
||||||
|
filter: RayPick.PICK_HUD,
|
||||||
|
posOffset: GRAB_POINT_SPHERE_OFFSET_LEFT,
|
||||||
|
renderStates: hudRayStates,
|
||||||
|
enabled: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// NOTE: keep this offset in sync with scripts/system/librarires/controllers.js:57
|
||||||
|
var VERTICAL_HEAD_LASER_OFFSET = 0.1;
|
||||||
|
var hudRayHead = LaserPointers.createLaserPointer({
|
||||||
|
joint: "Avatar",
|
||||||
|
filter: RayPick.PICK_HUD,
|
||||||
|
posOffset: {x: 0, y: VERTICAL_HEAD_LASER_OFFSET, z: 0},
|
||||||
|
renderStates: hudRayStates,
|
||||||
|
enabled: true
|
||||||
|
});
|
||||||
|
|
||||||
|
var mouseRayPick = RayPick.createRayPick({
|
||||||
|
joint: "Mouse",
|
||||||
|
filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS,
|
||||||
|
enabled: true
|
||||||
|
});
|
||||||
|
|
||||||
function isPointingAtOverlay(optionalHudPosition2d) {
|
function isPointingAtOverlay(optionalHudPosition2d) {
|
||||||
return Reticle.pointingAtSystemOverlay || Overlays.getOverlayAtPoint(optionalHudPosition2d || Reticle.position);
|
return Reticle.pointingAtSystemOverlay || Overlays.getOverlayAtPoint(optionalHudPosition2d || Reticle.position);
|
||||||
}
|
}
|
||||||
|
@ -166,10 +243,21 @@ function isPointingAtOverlay(optionalHudPosition2d) {
|
||||||
// Generalized HUD utilities, with or without HMD:
|
// Generalized HUD utilities, with or without HMD:
|
||||||
// This "var" is for documentation. Do not change the value!
|
// This "var" is for documentation. Do not change the value!
|
||||||
var PLANAR_PERPENDICULAR_HUD_DISTANCE = 1;
|
var PLANAR_PERPENDICULAR_HUD_DISTANCE = 1;
|
||||||
function calculateRayUICollisionPoint(position, direction) {
|
function calculateRayUICollisionPoint(position, direction, isHands) {
|
||||||
// Answer the 3D intersection of the HUD by the given ray, or falsey if no intersection.
|
// Answer the 3D intersection of the HUD by the given ray, or falsey if no intersection.
|
||||||
if (HMD.active) {
|
if (HMD.active) {
|
||||||
return HMD.calculateRayUICollisionPoint(position, direction);
|
var laserPointer;
|
||||||
|
if (isHands) {
|
||||||
|
laserPointer = activeHand == Controller.Standard.RightHand ? hudRayRight : hudRayLeft;
|
||||||
|
} else {
|
||||||
|
laserPointer = hudRayHead;
|
||||||
|
}
|
||||||
|
var result = LaserPointers.getPrevRayPickResult(laserPointer);
|
||||||
|
if (result.type != RayPick.INTERSECTED_NONE) {
|
||||||
|
return result.intersection;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// interect HUD plane, 1m in front of camera, using formula:
|
// interect HUD plane, 1m in front of camera, using formula:
|
||||||
// scale = hudNormal dot (hudPoint - position) / hudNormal dot direction
|
// scale = hudNormal dot (hudPoint - position) / hudNormal dot direction
|
||||||
|
@ -215,7 +303,7 @@ function activeHudPoint2dGamePad() {
|
||||||
var headPosition = MyAvatar.getHeadPosition();
|
var headPosition = MyAvatar.getHeadPosition();
|
||||||
var headDirection = Quat.getUp(Quat.multiply(MyAvatar.headOrientation, Quat.angleAxis(-90, { x: 1, y: 0, z: 0 })));
|
var headDirection = Quat.getUp(Quat.multiply(MyAvatar.headOrientation, Quat.angleAxis(-90, { x: 1, y: 0, z: 0 })));
|
||||||
|
|
||||||
var hudPoint3d = calculateRayUICollisionPoint(headPosition, headDirection);
|
var hudPoint3d = calculateRayUICollisionPoint(headPosition, headDirection, false);
|
||||||
|
|
||||||
if (!hudPoint3d) {
|
if (!hudPoint3d) {
|
||||||
if (Menu.isOptionChecked("Overlays")) { // With our hud resetting strategy, hudPoint3d should be valid here
|
if (Menu.isOptionChecked("Overlays")) { // With our hud resetting strategy, hudPoint3d should be valid here
|
||||||
|
@ -241,7 +329,7 @@ function activeHudPoint2d(activeHand) { // if controller is valid, update reticl
|
||||||
var controllerPosition = controllerPose.position;
|
var controllerPosition = controllerPose.position;
|
||||||
var controllerDirection = Quat.getUp(controllerPose.rotation);
|
var controllerDirection = Quat.getUp(controllerPose.rotation);
|
||||||
|
|
||||||
var hudPoint3d = calculateRayUICollisionPoint(controllerPosition, controllerDirection);
|
var hudPoint3d = calculateRayUICollisionPoint(controllerPosition, controllerDirection, true);
|
||||||
if (!hudPoint3d) {
|
if (!hudPoint3d) {
|
||||||
if (Menu.isOptionChecked("Overlays")) { // With our hud resetting strategy, hudPoint3d should be valid here
|
if (Menu.isOptionChecked("Overlays")) { // With our hud resetting strategy, hudPoint3d should be valid here
|
||||||
print('Controller is parallel to HUD'); // so let us know that our assumptions are wrong.
|
print('Controller is parallel to HUD'); // so let us know that our assumptions are wrong.
|
||||||
|
@ -355,7 +443,7 @@ function onMouseMove() {
|
||||||
if (isPointingAtOverlay()) {
|
if (isPointingAtOverlay()) {
|
||||||
Reticle.depth = hudReticleDistance();
|
Reticle.depth = hudReticleDistance();
|
||||||
} else {
|
} else {
|
||||||
var result = findRayIntersection(Camera.computePickRay(Reticle.position.x, Reticle.position.y));
|
var result = RayPick.getPrevRayPickResult(mouseRayPick);
|
||||||
Reticle.depth = result.intersects ? result.distance : APPARENT_MAXIMUM_DEPTH;
|
Reticle.depth = result.intersects ? result.distance : APPARENT_MAXIMUM_DEPTH;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -473,14 +561,6 @@ clickMapping.from(rightTrigger.partial).to(makeToggleAction(Controller.Standard.
|
||||||
clickMapping.from(leftTrigger.partial).to(makeToggleAction(Controller.Standard.LeftHand));
|
clickMapping.from(leftTrigger.partial).to(makeToggleAction(Controller.Standard.LeftHand));
|
||||||
clickMapping.enable();
|
clickMapping.enable();
|
||||||
|
|
||||||
// VISUAL AID -----------
|
|
||||||
// Same properties as handControllerGrab search sphere
|
|
||||||
var LASER_ALPHA = 0.5;
|
|
||||||
var LASER_SEARCH_COLOR_XYZW = {x: 10 / 255, y: 10 / 255, z: 255 / 255, w: LASER_ALPHA};
|
|
||||||
var LASER_TRIGGER_COLOR_XYZW = {x: 250 / 255, y: 10 / 255, z: 10 / 255, w: LASER_ALPHA};
|
|
||||||
var SYSTEM_LASER_DIRECTION = {x: 0, y: 0, z: -1};
|
|
||||||
var systemLaserOn = false;
|
|
||||||
|
|
||||||
var HIFI_POINTER_DISABLE_MESSAGE_CHANNEL = "Hifi-Pointer-Disable";
|
var HIFI_POINTER_DISABLE_MESSAGE_CHANNEL = "Hifi-Pointer-Disable";
|
||||||
var isPointerEnabled = true;
|
var isPointerEnabled = true;
|
||||||
|
|
||||||
|
@ -488,23 +568,34 @@ function clearSystemLaser() {
|
||||||
if (!systemLaserOn) {
|
if (!systemLaserOn) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
HMD.disableHandLasers(BOTH_HUD_LASERS);
|
HMD.deactivateHMDHandMouse();
|
||||||
HMD.disableExtraLaser();
|
LaserPointers.setRenderState(hudRayRight, "");
|
||||||
|
LaserPointers.setRenderState(hudRayLeft, "");
|
||||||
|
LaserPointers.setRenderState(hudRayHead, "");
|
||||||
systemLaserOn = false;
|
systemLaserOn = false;
|
||||||
weMovedReticle = true;
|
weMovedReticle = true;
|
||||||
}
|
}
|
||||||
function setColoredLaser() { // answer trigger state if lasers supported, else falsey.
|
function setColoredLaser() { // answer trigger state if lasers supported, else falsey.
|
||||||
var color = (activeTrigger.state === 'full') ? LASER_TRIGGER_COLOR_XYZW : LASER_SEARCH_COLOR_XYZW;
|
var mode = (activeTrigger.state === 'full') ? 'trigger' : 'search';
|
||||||
|
|
||||||
if (!HMD.isHandControllerAvailable()) {
|
if (!systemLaserOn) {
|
||||||
// NOTE: keep this offset in sync with scripts/system/librarires/controllers.js:57
|
HMD.activateHMDHandMouse();
|
||||||
var VERTICAL_HEAD_LASER_OFFSET = 0.1;
|
|
||||||
var position = Vec3.sum(HMD.position, Vec3.multiplyQbyV(HMD.orientation, {x: 0, y: VERTICAL_HEAD_LASER_OFFSET, z: 0}));
|
|
||||||
var orientation = Quat.multiply(HMD.orientation, Quat.angleAxis(-90, { x: 1, y: 0, z: 0 }));
|
|
||||||
return HMD.setExtraLaser(position, true, color, Quat.getUp(orientation));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return HMD.setHandLasers(activeHudLaser, true, color, SYSTEM_LASER_DIRECTION) && activeTrigger.state;
|
var pose = Controller.getPoseValue(activeHand);
|
||||||
|
if (!pose.valid) {
|
||||||
|
LaserPointers.setRenderState(hudRayRight, "");
|
||||||
|
LaserPointers.setRenderState(hudRayLeft, "");
|
||||||
|
LaserPointers.setRenderState(hudRayHead, mode);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var right = activeHand == Controller.Standard.RightHand;
|
||||||
|
LaserPointers.setRenderState(hudRayRight, right ? mode : "");
|
||||||
|
LaserPointers.setRenderState(hudRayLeft, right ? "" : mode);
|
||||||
|
LaserPointers.setRenderState(hudRayHead, "");
|
||||||
|
|
||||||
|
return activeTrigger.state;
|
||||||
}
|
}
|
||||||
|
|
||||||
// MAIN OPERATIONS -----------
|
// MAIN OPERATIONS -----------
|
||||||
|
@ -551,11 +642,13 @@ function update() {
|
||||||
if (HMD.active) {
|
if (HMD.active) {
|
||||||
Reticle.depth = hudReticleDistance();
|
Reticle.depth = hudReticleDistance();
|
||||||
|
|
||||||
if (!HMD.isHandControllerAvailable()) {
|
var pose = Controller.getPoseValue(activeHand);
|
||||||
var color = (activeTrigger.state === 'full') ? LASER_TRIGGER_COLOR_XYZW : LASER_SEARCH_COLOR_XYZW;
|
if (!pose.valid) {
|
||||||
var position = MyAvatar.getHeadPosition();
|
var mode = (activeTrigger.state === 'full') ? 'trigger' : 'search';
|
||||||
var direction = Quat.getUp(Quat.multiply(MyAvatar.headOrientation, Quat.angleAxis(-90, { x: 1, y: 0, z: 0 })));
|
if (!systemLaserOn) {
|
||||||
HMD.setExtraLaser(position, true, color, direction);
|
HMD.activateHMDHandMouse();
|
||||||
|
}
|
||||||
|
LaserPointers.setRenderState(hudRayHead, mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -604,6 +697,10 @@ Script.scriptEnding.connect(function () {
|
||||||
Script.clearInterval(settingsChecker);
|
Script.clearInterval(settingsChecker);
|
||||||
Script.update.disconnect(update);
|
Script.update.disconnect(update);
|
||||||
OffscreenFlags.navigationFocusDisabled = false;
|
OffscreenFlags.navigationFocusDisabled = false;
|
||||||
|
LaserPointers.removeLaserPointer(hudRayRight);
|
||||||
|
LaserPointers.removeLaserPointer(hudRayLeft);
|
||||||
|
LaserPointers.removeLaserPointer(hudRayHead);
|
||||||
|
HMD.deactivateHMDHandMouse();
|
||||||
});
|
});
|
||||||
|
|
||||||
}()); // END LOCAL_SCOPE
|
}()); // END LOCAL_SCOPE
|
||||||
|
|
|
@ -37,12 +37,6 @@ var COLORS_TELEPORT_CAN_TELEPORT = {
|
||||||
blue: 255
|
blue: 255
|
||||||
};
|
};
|
||||||
|
|
||||||
var COLORS_TELEPORT_CANNOT_TELEPORT = {
|
|
||||||
red: 0,
|
|
||||||
green: 121,
|
|
||||||
blue: 141
|
|
||||||
};
|
|
||||||
|
|
||||||
var COLORS_TELEPORT_CANCEL = {
|
var COLORS_TELEPORT_CANCEL = {
|
||||||
red: 255,
|
red: 255,
|
||||||
green: 184,
|
green: 184,
|
||||||
|
@ -61,6 +55,59 @@ var handInfo = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var cancelPath = {
|
||||||
|
type: "line3d",
|
||||||
|
color: COLORS_TELEPORT_CANCEL,
|
||||||
|
ignoreRayIntersection: true,
|
||||||
|
alpha: 1,
|
||||||
|
solid: true,
|
||||||
|
drawInFront: true,
|
||||||
|
glow: 1.0
|
||||||
|
};
|
||||||
|
var teleportPath = {
|
||||||
|
type: "line3d",
|
||||||
|
color: COLORS_TELEPORT_CAN_TELEPORT,
|
||||||
|
ignoreRayIntersection: true,
|
||||||
|
alpha: 1,
|
||||||
|
solid: true,
|
||||||
|
drawInFront: true,
|
||||||
|
glow: 1.0
|
||||||
|
};
|
||||||
|
var seatPath = {
|
||||||
|
type: "line3d",
|
||||||
|
color: COLORS_TELEPORT_SEAT,
|
||||||
|
ignoreRayIntersection: true,
|
||||||
|
alpha: 1,
|
||||||
|
solid: true,
|
||||||
|
drawInFront: true,
|
||||||
|
glow: 1.0
|
||||||
|
};
|
||||||
|
var cancelEnd = {
|
||||||
|
type: "model",
|
||||||
|
url: TOO_CLOSE_MODEL_URL,
|
||||||
|
dimensions: TARGET_MODEL_DIMENSIONS,
|
||||||
|
ignoreRayIntersection: true
|
||||||
|
};
|
||||||
|
var teleportEnd = {
|
||||||
|
type: "model",
|
||||||
|
url: TARGET_MODEL_URL,
|
||||||
|
dimensions: TARGET_MODEL_DIMENSIONS,
|
||||||
|
ignoreRayIntersection: true
|
||||||
|
};
|
||||||
|
var seatEnd = {
|
||||||
|
type: "model",
|
||||||
|
url: SEAT_MODEL_URL,
|
||||||
|
dimensions: TARGET_MODEL_DIMENSIONS,
|
||||||
|
ignoreRayIntersection: true
|
||||||
|
}
|
||||||
|
|
||||||
|
var teleportRenderStates = [{name: "cancel", path: cancelPath, end: cancelEnd},
|
||||||
|
{name: "teleport", path: teleportPath, end: teleportEnd},
|
||||||
|
{name: "seat", path: seatPath, end: seatEnd}];
|
||||||
|
|
||||||
|
var DEFAULT_DISTANCE = 50;
|
||||||
|
var teleportDefaultRenderStates = [{name: "cancel", distance: DEFAULT_DISTANCE, path: cancelPath}];
|
||||||
|
|
||||||
function ThumbPad(hand) {
|
function ThumbPad(hand) {
|
||||||
this.hand = hand;
|
this.hand = hand;
|
||||||
var _thisPad = this;
|
var _thisPad = this;
|
||||||
|
@ -108,33 +155,59 @@ function Teleporter() {
|
||||||
this.state = TELEPORTER_STATES.IDLE;
|
this.state = TELEPORTER_STATES.IDLE;
|
||||||
this.currentTarget = TARGET.INVALID;
|
this.currentTarget = TARGET.INVALID;
|
||||||
|
|
||||||
this.overlayLines = {
|
this.teleportRayLeftVisible = LaserPointers.createLaserPointer({
|
||||||
left: null,
|
joint: "LeftHand",
|
||||||
right: null,
|
filter: RayPick.PICK_ENTITIES,
|
||||||
};
|
faceAvatar: true,
|
||||||
|
centerEndY: false,
|
||||||
|
renderStates: teleportRenderStates,
|
||||||
|
defaultRenderStates: teleportDefaultRenderStates
|
||||||
|
});
|
||||||
|
this.teleportRayLeftInvisible = LaserPointers.createLaserPointer({
|
||||||
|
joint: "LeftHand",
|
||||||
|
filter: RayPick.PICK_ENTITIES | RayPick.PICK_INCLUDE_INVISIBLE,
|
||||||
|
faceAvatar: true,
|
||||||
|
centerEndY: false,
|
||||||
|
renderStates: teleportRenderStates
|
||||||
|
});
|
||||||
|
this.teleportRayRightVisible = LaserPointers.createLaserPointer({
|
||||||
|
joint: "RightHand",
|
||||||
|
filter: RayPick.PICK_ENTITIES,
|
||||||
|
faceAvatar: true,
|
||||||
|
centerEndY: false,
|
||||||
|
renderStates: teleportRenderStates,
|
||||||
|
defaultRenderStates: teleportDefaultRenderStates
|
||||||
|
});
|
||||||
|
this.teleportRayRightInvisible = LaserPointers.createLaserPointer({
|
||||||
|
joint: "RightHand",
|
||||||
|
filter: RayPick.PICK_ENTITIES | RayPick.PICK_INCLUDE_INVISIBLE,
|
||||||
|
faceAvatar: true,
|
||||||
|
centerEndY: false,
|
||||||
|
renderStates: teleportRenderStates
|
||||||
|
});
|
||||||
|
|
||||||
|
this.teleportRayHeadVisible = LaserPointers.createLaserPointer({
|
||||||
|
joint: "Avatar",
|
||||||
|
filter: RayPick.PICK_ENTITIES,
|
||||||
|
faceAvatar: true,
|
||||||
|
centerEndY: false,
|
||||||
|
renderStates: teleportRenderStates,
|
||||||
|
defaultRenderStates: teleportDefaultRenderStates
|
||||||
|
});
|
||||||
|
this.teleportRayHeadInvisible = LaserPointers.createLaserPointer({
|
||||||
|
joint: "Avatar",
|
||||||
|
filter: RayPick.PICK_ENTITIES | RayPick.PICK_INCLUDE_INVISIBLE,
|
||||||
|
faceAvatar: true,
|
||||||
|
centerEndY: false,
|
||||||
|
renderStates: teleportRenderStates
|
||||||
|
});
|
||||||
|
|
||||||
this.updateConnected = null;
|
this.updateConnected = null;
|
||||||
this.activeHand = null;
|
this.activeHand = null;
|
||||||
|
|
||||||
this.teleporterMappingInternalName = 'Hifi-Teleporter-Internal-Dev-' + Math.random();
|
this.teleporterMappingInternalName = 'Hifi-Teleporter-Internal-Dev-' + Math.random();
|
||||||
this.teleportMappingInternal = Controller.newMapping(this.teleporterMappingInternalName);
|
this.teleportMappingInternal = Controller.newMapping(this.teleporterMappingInternalName);
|
||||||
|
|
||||||
// Setup overlays
|
|
||||||
this.cancelOverlay = Overlays.addOverlay("model", {
|
|
||||||
url: TOO_CLOSE_MODEL_URL,
|
|
||||||
dimensions: TARGET_MODEL_DIMENSIONS,
|
|
||||||
visible: false
|
|
||||||
});
|
|
||||||
this.targetOverlay = Overlays.addOverlay("model", {
|
|
||||||
url: TARGET_MODEL_URL,
|
|
||||||
dimensions: TARGET_MODEL_DIMENSIONS,
|
|
||||||
visible: false
|
|
||||||
});
|
|
||||||
this.seatOverlay = Overlays.addOverlay("model", {
|
|
||||||
url: SEAT_MODEL_URL,
|
|
||||||
dimensions: TARGET_MODEL_DIMENSIONS,
|
|
||||||
visible: false
|
|
||||||
});
|
|
||||||
|
|
||||||
this.enableMappings = function() {
|
this.enableMappings = function() {
|
||||||
Controller.enableMapping(this.teleporterMappingInternalName);
|
Controller.enableMapping(this.teleporterMappingInternalName);
|
||||||
};
|
};
|
||||||
|
@ -146,16 +219,13 @@ function Teleporter() {
|
||||||
this.cleanup = function() {
|
this.cleanup = function() {
|
||||||
this.disableMappings();
|
this.disableMappings();
|
||||||
|
|
||||||
Overlays.deleteOverlay(this.targetOverlay);
|
LaserPointers.removeLaserPointer(this.teleportRayLeftVisible);
|
||||||
this.targetOverlay = null;
|
LaserPointers.removeLaserPointer(this.teleportRayLeftInvisible);
|
||||||
|
LaserPointers.removeLaserPointer(this.teleportRayRightVisible);
|
||||||
|
LaserPointers.removeLaserPointer(this.teleportRayRightInvisible);
|
||||||
|
LaserPointers.removeLaserPointer(this.teleportRayHeadVisible);
|
||||||
|
LaserPointers.removeLaserPointer(this.teleportRayHeadInvisible);
|
||||||
|
|
||||||
Overlays.deleteOverlay(this.cancelOverlay);
|
|
||||||
this.cancelOverlay = null;
|
|
||||||
|
|
||||||
Overlays.deleteOverlay(this.seatOverlay);
|
|
||||||
this.seatOverlay = null;
|
|
||||||
|
|
||||||
this.deleteOverlayBeams();
|
|
||||||
if (this.updateConnected === true) {
|
if (this.updateConnected === true) {
|
||||||
Script.update.disconnect(this, this.update);
|
Script.update.disconnect(this, this.update);
|
||||||
}
|
}
|
||||||
|
@ -194,43 +264,47 @@ function Teleporter() {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.disableMappings();
|
this.disableMappings();
|
||||||
this.deleteOverlayBeams();
|
LaserPointers.disableLaserPointer(this.teleportRayLeftVisible);
|
||||||
this.hideTargetOverlay();
|
LaserPointers.disableLaserPointer(this.teleportRayLeftInvisible);
|
||||||
this.hideCancelOverlay();
|
LaserPointers.disableLaserPointer(this.teleportRayRightVisible);
|
||||||
|
LaserPointers.disableLaserPointer(this.teleportRayRightInvisible);
|
||||||
|
LaserPointers.disableLaserPointer(this.teleportRayHeadVisible);
|
||||||
|
LaserPointers.disableLaserPointer(this.teleportRayHeadInvisible);
|
||||||
|
|
||||||
this.updateConnected = null;
|
this.updateConnected = null;
|
||||||
this.state = TELEPORTER_STATES.IDLE;
|
this.state = TELEPORTER_STATES.IDLE;
|
||||||
inTeleportMode = false;
|
inTeleportMode = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.deleteOverlayBeams = function() {
|
|
||||||
for (var key in this.overlayLines) {
|
|
||||||
if (this.overlayLines[key] !== null) {
|
|
||||||
Overlays.deleteOverlay(this.overlayLines[key]);
|
|
||||||
this.overlayLines[key] = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.update = function() {
|
this.update = function() {
|
||||||
if (_this.state === TELEPORTER_STATES.IDLE) {
|
if (_this.state === TELEPORTER_STATES.IDLE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get current hand pose information so that we can get the direction of the teleport beam
|
// Get current hand pose information to see if the pose is valid
|
||||||
var pose = Controller.getPoseValue(handInfo[_this.activeHand].controllerInput);
|
var pose = Controller.getPoseValue(handInfo[_this.activeHand].controllerInput);
|
||||||
var handPosition = pose.valid ? Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, pose.translation), MyAvatar.position) : MyAvatar.getHeadPosition();
|
var mode = pose.valid ? _this.activeHand : 'head';
|
||||||
var handRotation = pose.valid ? Quat.multiply(MyAvatar.orientation, pose.rotation) :
|
if (!pose.valid) {
|
||||||
Quat.multiply(MyAvatar.headOrientation, Quat.angleAxis(-90, {
|
if (mode === 'right') {
|
||||||
x: 1,
|
LaserPointers.disableLaserPointer(_this.teleportRayRightVisible);
|
||||||
y: 0,
|
LaserPointers.disableLaserPointer(_this.teleportRayRightInvisible);
|
||||||
z: 0
|
} else {
|
||||||
}));
|
LaserPointers.disableLaserPointer(_this.teleportRayLeftVisible);
|
||||||
|
LaserPointers.disableLaserPointer(_this.teleportRayLeftInvisible);
|
||||||
var pickRay = {
|
}
|
||||||
origin: handPosition,
|
LaserPointers.enableLaserPointer(_this.teleportRayHeadVisible);
|
||||||
direction: Quat.getUp(handRotation),
|
LaserPointers.enableLaserPointer(_this.teleportRayHeadInvisible);
|
||||||
};
|
} else {
|
||||||
|
if (mode === 'right') {
|
||||||
|
LaserPointers.enableLaserPointer(_this.teleportRayRightVisible);
|
||||||
|
LaserPointers.enableLaserPointer(_this.teleportRayRightInvisible);
|
||||||
|
} else {
|
||||||
|
LaserPointers.enableLaserPointer(_this.teleportRayLeftVisible);
|
||||||
|
LaserPointers.enableLaserPointer(_this.teleportRayLeftInvisible);
|
||||||
|
}
|
||||||
|
LaserPointers.disableLaserPointer(_this.teleportRayHeadVisible);
|
||||||
|
LaserPointers.disableLaserPointer(_this.teleportRayHeadInvisible);
|
||||||
|
}
|
||||||
|
|
||||||
// We do up to 2 ray picks to find a teleport location.
|
// We do up to 2 ray picks to find a teleport location.
|
||||||
// There are 2 types of teleport locations we are interested in:
|
// There are 2 types of teleport locations we are interested in:
|
||||||
|
@ -241,48 +315,40 @@ function Teleporter() {
|
||||||
// We might hit an invisible entity that is not a seat, so we need to do a second pass.
|
// We might hit an invisible entity that is not a seat, so we need to do a second pass.
|
||||||
// * In the second pass we pick against visible entities only.
|
// * In the second pass we pick against visible entities only.
|
||||||
//
|
//
|
||||||
var intersection = Entities.findRayIntersection(pickRay, true, [], [this.targetEntity].concat(ignoredEntities), false, true);
|
var result;
|
||||||
|
if (mode === 'right') {
|
||||||
|
result = LaserPointers.getPrevRayPickResult(_this.teleportRayRightInvisible);
|
||||||
|
} else if (mode === 'left') {
|
||||||
|
result = LaserPointers.getPrevRayPickResult(_this.teleportRayLeftInvisible);
|
||||||
|
} else {
|
||||||
|
result = LaserPointers.getPrevRayPickResult(_this.teleportRayHeadInvisible);
|
||||||
|
}
|
||||||
|
|
||||||
var teleportLocationType = getTeleportTargetType(intersection);
|
var teleportLocationType = getTeleportTargetType(result);
|
||||||
if (teleportLocationType === TARGET.INVISIBLE) {
|
if (teleportLocationType === TARGET.INVISIBLE) {
|
||||||
intersection = Entities.findRayIntersection(pickRay, true, [], [this.targetEntity].concat(ignoredEntities), true, true);
|
if (mode === 'right') {
|
||||||
teleportLocationType = getTeleportTargetType(intersection);
|
result = LaserPointers.getPrevRayPickResult(_this.teleportRayRightVisible);
|
||||||
|
} else if (mode === 'left') {
|
||||||
|
result = LaserPointers.getPrevRayPickResult(_this.teleportRayLeftVisible);
|
||||||
|
} else {
|
||||||
|
result = LaserPointers.getPrevRayPickResult(_this.teleportRayHeadVisible);
|
||||||
|
}
|
||||||
|
teleportLocationType = getTeleportTargetType(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (teleportLocationType === TARGET.NONE) {
|
if (teleportLocationType === TARGET.NONE) {
|
||||||
this.hideTargetOverlay();
|
// Use the cancel default state
|
||||||
this.hideCancelOverlay();
|
this.setTeleportState(mode, "cancel", "");
|
||||||
this.hideSeatOverlay();
|
|
||||||
|
|
||||||
var farPosition = Vec3.sum(pickRay.origin, Vec3.multiply(pickRay.direction, 50));
|
|
||||||
this.updateLineOverlay(_this.activeHand, pickRay.origin, farPosition, COLORS_TELEPORT_CANNOT_TELEPORT);
|
|
||||||
} else if (teleportLocationType === TARGET.INVALID || teleportLocationType === TARGET.INVISIBLE) {
|
} else if (teleportLocationType === TARGET.INVALID || teleportLocationType === TARGET.INVISIBLE) {
|
||||||
this.hideTargetOverlay();
|
this.setTeleportState(mode, "", "cancel");
|
||||||
this.hideSeatOverlay();
|
|
||||||
|
|
||||||
this.updateLineOverlay(_this.activeHand, pickRay.origin, intersection.intersection, COLORS_TELEPORT_CANCEL);
|
|
||||||
this.updateDestinationOverlay(this.cancelOverlay, intersection);
|
|
||||||
} else if (teleportLocationType === TARGET.SURFACE) {
|
} else if (teleportLocationType === TARGET.SURFACE) {
|
||||||
if (this.state === TELEPORTER_STATES.COOL_IN) {
|
if (this.state === TELEPORTER_STATES.COOL_IN) {
|
||||||
this.hideTargetOverlay();
|
this.setTeleportState(mode, "cancel", "");
|
||||||
this.hideSeatOverlay();
|
|
||||||
|
|
||||||
this.updateLineOverlay(_this.activeHand, pickRay.origin, intersection.intersection, COLORS_TELEPORT_CANCEL);
|
|
||||||
this.updateDestinationOverlay(this.cancelOverlay, intersection);
|
|
||||||
} else {
|
} else {
|
||||||
this.hideCancelOverlay();
|
this.setTeleportState(mode, "teleport", "");
|
||||||
this.hideSeatOverlay();
|
|
||||||
|
|
||||||
this.updateLineOverlay(_this.activeHand, pickRay.origin, intersection.intersection,
|
|
||||||
COLORS_TELEPORT_CAN_TELEPORT);
|
|
||||||
this.updateDestinationOverlay(this.targetOverlay, intersection);
|
|
||||||
}
|
}
|
||||||
} else if (teleportLocationType === TARGET.SEAT) {
|
} else if (teleportLocationType === TARGET.SEAT) {
|
||||||
this.hideCancelOverlay();
|
this.setTeleportState(mode, "", "seat");
|
||||||
this.hideTargetOverlay();
|
|
||||||
|
|
||||||
this.updateLineOverlay(_this.activeHand, pickRay.origin, intersection.intersection, COLORS_TELEPORT_SEAT);
|
|
||||||
this.updateDestinationOverlay(this.seatOverlay, intersection);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -290,79 +356,33 @@ function Teleporter() {
|
||||||
// remember the state before we exit teleport mode and set it back to IDLE
|
// remember the state before we exit teleport mode and set it back to IDLE
|
||||||
var previousState = this.state;
|
var previousState = this.state;
|
||||||
this.exitTeleportMode();
|
this.exitTeleportMode();
|
||||||
this.hideCancelOverlay();
|
|
||||||
this.hideTargetOverlay();
|
|
||||||
this.hideSeatOverlay();
|
|
||||||
|
|
||||||
if (teleportLocationType === TARGET.NONE || teleportLocationType === TARGET.INVALID || previousState === TELEPORTER_STATES.COOL_IN) {
|
if (teleportLocationType === TARGET.NONE || teleportLocationType === TARGET.INVALID || previousState === TELEPORTER_STATES.COOL_IN) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
} else if (teleportLocationType === TARGET.SEAT) {
|
} else if (teleportLocationType === TARGET.SEAT) {
|
||||||
Entities.callEntityMethod(intersection.entityID, 'sit');
|
Entities.callEntityMethod(result.objectID, 'sit');
|
||||||
} else if (teleportLocationType === TARGET.SURFACE) {
|
} else if (teleportLocationType === TARGET.SURFACE) {
|
||||||
var offset = getAvatarFootOffset();
|
var offset = getAvatarFootOffset();
|
||||||
intersection.intersection.y += offset;
|
result.intersection.y += offset;
|
||||||
MyAvatar.goToLocation(intersection.intersection, false, {x: 0, y: 0, z: 0, w: 1}, false);
|
MyAvatar.goToLocation(result.intersection, false, {x: 0, y: 0, z: 0, w: 1}, false);
|
||||||
HMD.centerUI();
|
HMD.centerUI();
|
||||||
MyAvatar.centerBody();
|
MyAvatar.centerBody();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.updateLineOverlay = function(hand, closePoint, farPoint, color) {
|
this.setTeleportState = function(mode, visibleState, invisibleState) {
|
||||||
if (this.overlayLines[hand] === null) {
|
if (mode === 'right') {
|
||||||
var lineProperties = {
|
LaserPointers.setRenderState(_this.teleportRayRightVisible, visibleState);
|
||||||
start: closePoint,
|
LaserPointers.setRenderState(_this.teleportRayRightInvisible, invisibleState);
|
||||||
end: farPoint,
|
} else if (mode === 'left') {
|
||||||
color: color,
|
LaserPointers.setRenderState(_this.teleportRayLeftVisible, visibleState);
|
||||||
ignoreRayIntersection: true,
|
LaserPointers.setRenderState(_this.teleportRayLeftInvisible, invisibleState);
|
||||||
visible: true,
|
|
||||||
alpha: 1,
|
|
||||||
solid: true,
|
|
||||||
drawInFront: true,
|
|
||||||
glow: 1.0
|
|
||||||
};
|
|
||||||
|
|
||||||
this.overlayLines[hand] = Overlays.addOverlay("line3d", lineProperties);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Overlays.editOverlay(this.overlayLines[hand], {
|
LaserPointers.setRenderState(_this.teleportRayHeadVisible, visibleState);
|
||||||
start: closePoint,
|
LaserPointers.setRenderState(_this.teleportRayHeadInvisible, invisibleState);
|
||||||
end: farPoint,
|
|
||||||
color: color
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.hideCancelOverlay = function() {
|
|
||||||
Overlays.editOverlay(this.cancelOverlay, { visible: false });
|
|
||||||
};
|
|
||||||
|
|
||||||
this.hideTargetOverlay = function() {
|
|
||||||
Overlays.editOverlay(this.targetOverlay, { visible: false });
|
|
||||||
};
|
|
||||||
|
|
||||||
this.hideSeatOverlay = function() {
|
|
||||||
Overlays.editOverlay(this.seatOverlay, { visible: false });
|
|
||||||
};
|
|
||||||
|
|
||||||
this.updateDestinationOverlay = function(overlayID, intersection) {
|
|
||||||
var rotation = Quat.lookAt(intersection.intersection, MyAvatar.position, Vec3.UP);
|
|
||||||
var euler = Quat.safeEulerAngles(rotation);
|
|
||||||
var position = {
|
|
||||||
x: intersection.intersection.x,
|
|
||||||
y: intersection.intersection.y + TARGET_MODEL_DIMENSIONS.y / 2,
|
|
||||||
z: intersection.intersection.z
|
|
||||||
};
|
|
||||||
|
|
||||||
var towardUs = Quat.fromPitchYawRollDegrees(0, euler.y, 0);
|
|
||||||
|
|
||||||
Overlays.editOverlay(overlayID, {
|
|
||||||
visible: true,
|
|
||||||
position: position,
|
|
||||||
rotation: towardUs
|
|
||||||
});
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// related to repositioning the avatar after you teleport
|
// related to repositioning the avatar after you teleport
|
||||||
|
@ -424,12 +444,12 @@ function parseJSON(json) {
|
||||||
// than MAX_ANGLE_FROM_UP_TO_TELEPORT degrees from <0, 1, 0> (straight up), then
|
// than MAX_ANGLE_FROM_UP_TO_TELEPORT degrees from <0, 1, 0> (straight up), then
|
||||||
// you can't teleport there.
|
// you can't teleport there.
|
||||||
var MAX_ANGLE_FROM_UP_TO_TELEPORT = 70;
|
var MAX_ANGLE_FROM_UP_TO_TELEPORT = 70;
|
||||||
function getTeleportTargetType(intersection) {
|
function getTeleportTargetType(result) {
|
||||||
if (!intersection.intersects) {
|
if (result.type == RayPick.INTERSECTED_NONE) {
|
||||||
return TARGET.NONE;
|
return TARGET.NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
var props = Entities.getEntityProperties(intersection.entityID, ['userData', 'visible']);
|
var props = Entities.getEntityProperties(result.objectID, ['userData', 'visible']);
|
||||||
var data = parseJSON(props.userData);
|
var data = parseJSON(props.userData);
|
||||||
if (data !== undefined && data.seat !== undefined) {
|
if (data !== undefined && data.seat !== undefined) {
|
||||||
var avatarUuid = Uuid.fromString(data.seat.user);
|
var avatarUuid = Uuid.fromString(data.seat.user);
|
||||||
|
@ -444,13 +464,13 @@ function getTeleportTargetType(intersection) {
|
||||||
return TARGET.INVISIBLE;
|
return TARGET.INVISIBLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
var surfaceNormal = intersection.surfaceNormal;
|
var surfaceNormal = result.surfaceNormal;
|
||||||
var adj = Math.sqrt(surfaceNormal.x * surfaceNormal.x + surfaceNormal.z * surfaceNormal.z);
|
var adj = Math.sqrt(surfaceNormal.x * surfaceNormal.x + surfaceNormal.z * surfaceNormal.z);
|
||||||
var angleUp = Math.atan2(surfaceNormal.y, adj) * (180 / Math.PI);
|
var angleUp = Math.atan2(surfaceNormal.y, adj) * (180 / Math.PI);
|
||||||
|
|
||||||
if (angleUp < (90 - MAX_ANGLE_FROM_UP_TO_TELEPORT) ||
|
if (angleUp < (90 - MAX_ANGLE_FROM_UP_TO_TELEPORT) ||
|
||||||
angleUp > (90 + MAX_ANGLE_FROM_UP_TO_TELEPORT) ||
|
angleUp > (90 + MAX_ANGLE_FROM_UP_TO_TELEPORT) ||
|
||||||
Vec3.distance(MyAvatar.position, intersection.intersection) <= TELEPORT_CANCEL_RANGE) {
|
Vec3.distance(MyAvatar.position, result.intersection) <= TELEPORT_CANCEL_RANGE) {
|
||||||
return TARGET.INVALID;
|
return TARGET.INVALID;
|
||||||
} else {
|
} else {
|
||||||
return TARGET.SURFACE;
|
return TARGET.SURFACE;
|
||||||
|
@ -509,6 +529,15 @@ function cleanup() {
|
||||||
}
|
}
|
||||||
Script.scriptEnding.connect(cleanup);
|
Script.scriptEnding.connect(cleanup);
|
||||||
|
|
||||||
|
var setIgnoreEntities = function() {
|
||||||
|
LaserPointers.setIgnoreEntities(teleporter.teleportRayRightVisible, ignoredEntities);
|
||||||
|
LaserPointers.setIgnoreEntities(teleporter.teleportRayRightInvisible, ignoredEntities);
|
||||||
|
LaserPointers.setIgnoreEntities(teleporter.teleportRayLeftVisible, ignoredEntities);
|
||||||
|
LaserPointers.setIgnoreEntities(teleporter.teleportRayLeftInvisible, ignoredEntities);
|
||||||
|
LaserPointers.setIgnoreEntities(teleporter.teleportRayHeadVisible, ignoredEntities);
|
||||||
|
LaserPointers.setIgnoreEntities(teleporter.teleportRayHeadInvisible, ignoredEntities);
|
||||||
|
}
|
||||||
|
|
||||||
var isDisabled = false;
|
var isDisabled = false;
|
||||||
var handleTeleportMessages = function(channel, message, sender) {
|
var handleTeleportMessages = function(channel, message, sender) {
|
||||||
if (sender === MyAvatar.sessionUUID) {
|
if (sender === MyAvatar.sessionUUID) {
|
||||||
|
@ -527,10 +556,12 @@ var handleTeleportMessages = function(channel, message, sender) {
|
||||||
}
|
}
|
||||||
} else if (channel === 'Hifi-Teleport-Ignore-Add' && !Uuid.isNull(message) && ignoredEntities.indexOf(message) === -1) {
|
} else if (channel === 'Hifi-Teleport-Ignore-Add' && !Uuid.isNull(message) && ignoredEntities.indexOf(message) === -1) {
|
||||||
ignoredEntities.push(message);
|
ignoredEntities.push(message);
|
||||||
|
setIgnoreEntities();
|
||||||
} else if (channel === 'Hifi-Teleport-Ignore-Remove' && !Uuid.isNull(message)) {
|
} else if (channel === 'Hifi-Teleport-Ignore-Remove' && !Uuid.isNull(message)) {
|
||||||
var removeIndex = ignoredEntities.indexOf(message);
|
var removeIndex = ignoredEntities.indexOf(message);
|
||||||
if (removeIndex > -1) {
|
if (removeIndex > -1) {
|
||||||
ignoredEntities.splice(removeIndex, 1);
|
ignoredEntities.splice(removeIndex, 1);
|
||||||
|
setIgnoreEntities();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue