mirror of
https://github.com/overte-org/overte.git
synced 2025-04-25 17:55:36 +02:00
Merge branch 'master' into feature/pole-vector
This commit is contained in:
commit
03a6f7082e
102 changed files with 2213 additions and 1165 deletions
|
@ -1,14 +1,16 @@
|
|||
{
|
||||
"RenderShadowTask": {
|
||||
"Enabled": {
|
||||
"enabled": true
|
||||
}
|
||||
},
|
||||
"RenderDeferredTask": {
|
||||
"AmbientOcclusion": {
|
||||
"RenderMainView": {
|
||||
"RenderShadowTask": {
|
||||
"Enabled": {
|
||||
"enabled": true
|
||||
}
|
||||
},
|
||||
"RenderDeferredTask": {
|
||||
"AmbientOcclusion": {
|
||||
"Enabled": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ var EventBridge;
|
|||
var webChannel = new QWebChannel(qt.webChannelTransport, function (channel) {
|
||||
// replace the TempEventBridge with the real one.
|
||||
var tempEventBridge = EventBridge;
|
||||
EventBridge = channel.objects.eventBridgeWrapper.eventBridge;
|
||||
EventBridge = channel.objects.eventBridge;
|
||||
tempEventBridge._callbacks.forEach(function (callback) {
|
||||
EventBridge.scriptEventReceived.connect(callback);
|
||||
});
|
||||
|
|
Binary file not shown.
|
@ -1,85 +1,89 @@
|
|||
name = Jointy3
|
||||
name = mannequin
|
||||
type = body+head
|
||||
scale = 1
|
||||
filename = Jointy3/Jointy3.fbx
|
||||
texdir = Jointy3/textures
|
||||
filename = mannequin/mannequin.baked.fbx
|
||||
joint = jointEyeLeft = LeftEye
|
||||
joint = jointRightHand = RightHand
|
||||
joint = jointHead = Head
|
||||
joint = jointEyeRight = RightEye
|
||||
joint = jointLean = Spine
|
||||
joint = jointNeck = Neck
|
||||
joint = jointLeftHand = LeftHand
|
||||
joint = jointEyeRight = RightEye
|
||||
joint = jointHead = Head
|
||||
joint = jointRightHand = RightHand
|
||||
joint = jointRoot = Hips
|
||||
joint = jointLean = Spine
|
||||
joint = jointEyeLeft = LeftEye
|
||||
freeJoint = LeftArm
|
||||
freeJoint = LeftForeArm
|
||||
freeJoint = RightArm
|
||||
freeJoint = RightForeArm
|
||||
jointIndex = RightHand = 17
|
||||
jointIndex = LeftHandIndex3 = 56
|
||||
jointIndex = Hips = 0
|
||||
jointIndex = LeftHandRing2 = 47
|
||||
jointIndex = LeftHandThumb3 = 60
|
||||
jointIndex = RightShoulder = 14
|
||||
jointIndex = RightHandRing1 = 30
|
||||
jointIndex = RightHandRing3 = 32
|
||||
jointIndex = LeftHandPinky4 = 45
|
||||
jointIndex = LeftHandRing1 = 46
|
||||
jointIndex = LeftFoot = 8
|
||||
jointIndex = RightHandIndex2 = 23
|
||||
jointIndex = RightToeBase = 4
|
||||
jointIndex = RightHandMiddle4 = 29
|
||||
jointIndex = RightHandPinky4 = 37
|
||||
jointIndex = LeftToe_End = 10
|
||||
jointIndex = RightEye = 66
|
||||
jointIndex = RightHandPinky2 = 35
|
||||
jointIndex = RightHandRing2 = 31
|
||||
jointIndex = LeftHand = 41
|
||||
jointIndex = RightToe_End = 5
|
||||
jointIndex = LeftEye = 65
|
||||
jointIndex = LeftHandThumb2 = 59
|
||||
jointIndex = pCylinder73Shape1 = 67
|
||||
jointIndex = LeftShoulder = 38
|
||||
jointIndex = LeftHandIndex2 = 55
|
||||
jointIndex = RightForeArm = 16
|
||||
jointIndex = LeftHandMiddle2 = 51
|
||||
jointIndex = RightHandRing4 = 33
|
||||
jointIndex = LeftLeg = 7
|
||||
jointIndex = LeftHandThumb4 = 61
|
||||
jointIndex = LeftForeArm = 40
|
||||
jointIndex = HeadTop_End = 64
|
||||
jointIndex = RightHandPinky1 = 34
|
||||
jointIndex = RightHandIndex1 = 22
|
||||
jointIndex = LeftHandIndex1 = 54
|
||||
jointIndex = RightLeg = 2
|
||||
jointIndex = RightHandIndex4 = 25
|
||||
jointIndex = Neck = 62
|
||||
jointIndex = LeftHandMiddle1 = 50
|
||||
jointIndex = RightHandPinky3 = 36
|
||||
jointIndex = LeftHandPinky2 = 43
|
||||
jointIndex = RightHandMiddle3 = 28
|
||||
jointIndex = RightHandThumb4 = 21
|
||||
jointIndex = LeftUpLeg = 6
|
||||
jointIndex = RightFoot = 3
|
||||
jointIndex = LeftHandThumb1 = 58
|
||||
jointIndex = LeftArm = 39
|
||||
jointIndex = RightHandMiddle1 = 26
|
||||
jointIndex = LeftHandRing3 = 48
|
||||
jointIndex = LeftHandMiddle4 = 53
|
||||
jointIndex = RightUpLeg = 1
|
||||
jointIndex = RightHandMiddle2 = 27
|
||||
jointIndex = LeftToeBase = 9
|
||||
jointIndex = RightHandThumb2 = 19
|
||||
jointIndex = Spine2 = 13
|
||||
jointIndex = Spine = 11
|
||||
jointIndex = LeftHandRing4 = 49
|
||||
jointIndex = Head = 63
|
||||
jointIndex = LeftHandPinky3 = 44
|
||||
bs = EyeBlink_L = blink = 1
|
||||
bs = JawOpen = mouth_Open = 1
|
||||
bs = LipsFunnel = Oo = 1
|
||||
bs = BrowsU_L = brow_Up = 1
|
||||
jointIndex = RightHandIndex2 = 27
|
||||
jointIndex = LeftHandIndex2 = 51
|
||||
jointIndex = RightUpLeg = 6
|
||||
jointIndex = RightToe_End = 10
|
||||
jointIndex = RightEye = 65
|
||||
jointIndex = LeftHandPinky1 = 42
|
||||
jointIndex = RightHandThumb1 = 18
|
||||
jointIndex = LeftHandIndex4 = 57
|
||||
jointIndex = LeftHandMiddle3 = 52
|
||||
jointIndex = RightHandIndex3 = 24
|
||||
jointIndex = Spine1 = 12
|
||||
jointIndex = RightHandRing1 = 22
|
||||
jointIndex = face = 67
|
||||
jointIndex = LeftUpLeg = 1
|
||||
jointIndex = LeftHand = 41
|
||||
jointIndex = LeftHandMiddle1 = 58
|
||||
jointIndex = LeftHandIndex1 = 50
|
||||
jointIndex = LeftEye = 64
|
||||
jointIndex = RightHandIndex1 = 26
|
||||
jointIndex = LeftHandPinky4 = 45
|
||||
jointIndex = RightArm = 15
|
||||
jointIndex = RightHandThumb3 = 20
|
||||
jointIndex = LeftShoulder = 38
|
||||
jointIndex = RightHandPinky2 = 19
|
||||
jointIndex = RightHandThumb1 = 30
|
||||
jointIndex = RightForeArm = 16
|
||||
jointIndex = LeftHandMiddle3 = 60
|
||||
jointIndex = Neck = 62
|
||||
jointIndex = LeftHandThumb1 = 54
|
||||
jointIndex = RightHandMiddle2 = 35
|
||||
jointIndex = LeftHandMiddle4 = 61
|
||||
jointIndex = mannequin = 68
|
||||
jointIndex = Spine1 = 12
|
||||
jointIndex = RightFoot = 8
|
||||
jointIndex = RightHand = 17
|
||||
jointIndex = LeftHandIndex3 = 52
|
||||
jointIndex = RightHandIndex3 = 28
|
||||
jointIndex = RightHandMiddle4 = 37
|
||||
jointIndex = LeftLeg = 2
|
||||
jointIndex = RightHandMiddle1 = 34
|
||||
jointIndex = Spine2 = 13
|
||||
jointIndex = LeftHandMiddle2 = 59
|
||||
jointIndex = LeftHandPinky3 = 44
|
||||
jointIndex = LeftHandThumb3 = 56
|
||||
jointIndex = LeftHandRing4 = 49
|
||||
jointIndex = RightHandThumb2 = 31
|
||||
jointIndex = LeftHandRing3 = 48
|
||||
jointIndex = HeadTop_End = 66
|
||||
jointIndex = LeftHandThumb4 = 57
|
||||
jointIndex = RightHandThumb3 = 32
|
||||
jointIndex = RightHandPinky1 = 18
|
||||
jointIndex = RightLeg = 7
|
||||
jointIndex = RightHandMiddle3 = 36
|
||||
jointIndex = RightHandPinky3 = 20
|
||||
jointIndex = LeftToeBase = 4
|
||||
jointIndex = LeftForeArm = 40
|
||||
jointIndex = RightShoulder = 14
|
||||
jointIndex = LeftHandRing2 = 47
|
||||
jointIndex = LeftHandThumb2 = 55
|
||||
jointIndex = Head = 63
|
||||
jointIndex = RightHandRing4 = 25
|
||||
jointIndex = LeftHandRing1 = 46
|
||||
jointIndex = LeftFoot = 3
|
||||
jointIndex = RightHandRing3 = 24
|
||||
jointIndex = RightHandThumb4 = 33
|
||||
jointIndex = LeftArm = 39
|
||||
jointIndex = LeftToe_End = 5
|
||||
jointIndex = RightToeBase = 9
|
||||
jointIndex = RightHandPinky4 = 21
|
||||
jointIndex = Spine = 11
|
||||
jointIndex = LeftHandIndex4 = 53
|
||||
jointIndex = LeftHandPinky2 = 43
|
||||
jointIndex = RightHandIndex4 = 29
|
||||
jointIndex = Hips = 0
|
||||
jointIndex = RightHandRing2 = 23
|
||||
|
|
BIN
interface/resources/meshes/mannequin/Eyes.ktx
Executable file
BIN
interface/resources/meshes/mannequin/Eyes.ktx
Executable file
Binary file not shown.
BIN
interface/resources/meshes/mannequin/lambert1_Base_Color.ktx
Executable file
BIN
interface/resources/meshes/mannequin/lambert1_Base_Color.ktx
Executable file
Binary file not shown.
BIN
interface/resources/meshes/mannequin/lambert1_Normal_OpenGL.ktx
Executable file
BIN
interface/resources/meshes/mannequin/lambert1_Normal_OpenGL.ktx
Executable file
Binary file not shown.
BIN
interface/resources/meshes/mannequin/lambert1_Roughness.ktx
Executable file
BIN
interface/resources/meshes/mannequin/lambert1_Roughness.ktx
Executable file
Binary file not shown.
BIN
interface/resources/meshes/mannequin/mannequin.baked.fbx
Executable file
BIN
interface/resources/meshes/mannequin/mannequin.baked.fbx
Executable file
Binary file not shown.
|
@ -21,8 +21,6 @@ ScrollingWindow {
|
|||
property alias url: webview.url
|
||||
property alias webView: webview
|
||||
|
||||
property alias eventBridge: eventBridgeWrapper.eventBridge
|
||||
|
||||
signal loadingChanged(int status)
|
||||
|
||||
x: 100
|
||||
|
@ -210,17 +208,6 @@ ScrollingWindow {
|
|||
url: "https://highfidelity.com/"
|
||||
profile: FileTypeProfile;
|
||||
|
||||
property alias eventBridgeWrapper: eventBridgeWrapper
|
||||
|
||||
QtObject {
|
||||
id: eventBridgeWrapper
|
||||
WebChannel.id: "eventBridgeWrapper"
|
||||
property var eventBridge;
|
||||
}
|
||||
|
||||
|
||||
webChannel.registeredObjects: [eventBridgeWrapper]
|
||||
|
||||
// Create a global EventBridge object for raiseAndLowerKeyboard.
|
||||
WebEngineScript {
|
||||
id: createGlobalEventBridge
|
||||
|
@ -267,6 +254,8 @@ ScrollingWindow {
|
|||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
webChannel.registerObject("eventBridge", eventBridge);
|
||||
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
|
||||
desktop.initWebviewProfileHandlers(webview.profile);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,15 +26,8 @@ Windows.ScrollingWindow {
|
|||
// Don't destroy on close... otherwise the JS/C++ will have a dangling pointer
|
||||
destroyOnCloseButton: false
|
||||
property alias source: webview.url
|
||||
property alias eventBridge: eventBridgeWrapper.eventBridge;
|
||||
property alias scriptUrl: webview.userScriptUrl
|
||||
|
||||
QtObject {
|
||||
id: eventBridgeWrapper
|
||||
WebChannel.id: "eventBridgeWrapper"
|
||||
property var eventBridge;
|
||||
}
|
||||
|
||||
// This is for JS/QML communication, which is unused in a WebWindow,
|
||||
// but not having this here results in spurious warnings about a
|
||||
// missing signal
|
||||
|
@ -70,7 +63,6 @@ Windows.ScrollingWindow {
|
|||
url: "about:blank"
|
||||
anchors.fill: parent
|
||||
focus: true
|
||||
webChannel.registeredObjects: [eventBridgeWrapper]
|
||||
|
||||
property string userScriptUrl: ""
|
||||
|
||||
|
@ -107,6 +99,8 @@ Windows.ScrollingWindow {
|
|||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
webChannel.registerObject("eventBridge", eventBridge);
|
||||
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
|
||||
eventBridge.webEventReceived.connect(onWebEventReceived);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,15 +30,6 @@ Windows.Window {
|
|||
property bool keyboardRaised: false
|
||||
property bool punctuationMode: false
|
||||
|
||||
// JavaScript event bridge object in case QML content includes Web content.
|
||||
property alias eventBridge: eventBridgeWrapper.eventBridge;
|
||||
|
||||
QtObject {
|
||||
id: eventBridgeWrapper
|
||||
WebChannel.id: "eventBridgeWrapper"
|
||||
property var eventBridge;
|
||||
}
|
||||
|
||||
onSourceChanged: {
|
||||
if (dynamicContent) {
|
||||
dynamicContent.destroy();
|
||||
|
|
|
@ -18,7 +18,6 @@ Item {
|
|||
property variant permissionsBar: {'securityOrigin':'none','feature':'none'}
|
||||
property alias url: webview.url
|
||||
property WebEngineView webView: webview
|
||||
property alias eventBridge: eventBridgeWrapper.eventBridge
|
||||
property bool canGoBack: webview.canGoBack
|
||||
property bool canGoForward: webview.canGoForward
|
||||
|
||||
|
@ -32,12 +31,6 @@ Item {
|
|||
webview.profile = profile;
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: eventBridgeWrapper
|
||||
WebChannel.id: "eventBridgeWrapper"
|
||||
property var eventBridge;
|
||||
}
|
||||
|
||||
WebEngineView {
|
||||
id: webview
|
||||
objectName: "webEngineView"
|
||||
|
@ -78,9 +71,10 @@ Item {
|
|||
|
||||
property string newUrl: ""
|
||||
|
||||
webChannel.registeredObjects: [eventBridgeWrapper]
|
||||
|
||||
Component.onCompleted: {
|
||||
webChannel.registerObject("eventBridge", eventBridge);
|
||||
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
|
||||
|
||||
// Ensure the JS from the web-engine makes it to our logging
|
||||
webview.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
|
||||
console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);
|
||||
|
|
|
@ -79,15 +79,11 @@ ScrollingWindow {
|
|||
id: webView
|
||||
anchors.fill: parent
|
||||
enabled: false
|
||||
property alias eventBridgeWrapper: eventBridgeWrapper
|
||||
|
||||
QtObject {
|
||||
id: eventBridgeWrapper
|
||||
WebChannel.id: "eventBridgeWrapper"
|
||||
property var eventBridge
|
||||
Component.onCompleted: {
|
||||
webChannel.registerObject("eventBridge", eventBridge);
|
||||
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
|
||||
}
|
||||
|
||||
webChannel.registeredObjects: [eventBridgeWrapper]
|
||||
onEnabledChanged: toolWindow.updateVisiblity()
|
||||
}
|
||||
}
|
||||
|
@ -251,12 +247,9 @@ ScrollingWindow {
|
|||
tab.enabled = true;
|
||||
tab.originalUrl = properties.source;
|
||||
|
||||
var eventBridge = properties.eventBridge;
|
||||
|
||||
var result = tab.item;
|
||||
result.enabled = true;
|
||||
tabView.tabCount++;
|
||||
result.eventBridgeWrapper.eventBridge = eventBridge;
|
||||
result.url = properties.source;
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import "../controls-uit" as HiFiControls
|
|||
Item {
|
||||
property alias url: root.url
|
||||
property alias scriptURL: root.userScriptUrl
|
||||
property alias eventBridge: eventBridgeWrapper.eventBridge
|
||||
property alias canGoBack: root.canGoBack;
|
||||
property var goBack: root.goBack;
|
||||
property alias urlTag: root.urlTag
|
||||
|
@ -22,12 +21,6 @@ Item {
|
|||
}
|
||||
*/
|
||||
|
||||
QtObject {
|
||||
id: eventBridgeWrapper
|
||||
WebChannel.id: "eventBridgeWrapper"
|
||||
property var eventBridge;
|
||||
}
|
||||
|
||||
property alias viewProfile: root.profile
|
||||
|
||||
WebEngineView {
|
||||
|
@ -71,10 +64,11 @@ Item {
|
|||
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
|
||||
|
||||
property string newUrl: ""
|
||||
|
||||
webChannel.registeredObjects: [eventBridgeWrapper]
|
||||
|
||||
|
||||
Component.onCompleted: {
|
||||
webChannel.registerObject("eventBridge", eventBridge);
|
||||
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
|
||||
// Ensure the JS from the web-engine makes it to our logging
|
||||
root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
|
||||
console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);
|
||||
|
|
|
@ -17,7 +17,6 @@ Item {
|
|||
property int headerHeight: 70
|
||||
property string url
|
||||
property string scriptURL
|
||||
property alias eventBridge: eventBridgeWrapper.eventBridge
|
||||
property bool keyboardEnabled: false
|
||||
property bool keyboardRaised: false
|
||||
property bool punctuationMode: false
|
||||
|
@ -135,12 +134,6 @@ Item {
|
|||
loadUrl(url);
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: eventBridgeWrapper
|
||||
WebChannel.id: "eventBridgeWrapper"
|
||||
property var eventBridge;
|
||||
}
|
||||
|
||||
WebEngineView {
|
||||
id: webview
|
||||
objectName: "webEngineView"
|
||||
|
@ -182,9 +175,9 @@ Item {
|
|||
|
||||
property string newUrl: ""
|
||||
|
||||
webChannel.registeredObjects: [eventBridgeWrapper]
|
||||
|
||||
Component.onCompleted: {
|
||||
webChannel.registerObject("eventBridge", eventBridge);
|
||||
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
|
||||
// Ensure the JS from the web-engine makes it to our logging
|
||||
webview.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
|
||||
console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);
|
||||
|
|
|
@ -6,7 +6,6 @@ import "../controls-uit" as HiFiControls
|
|||
Item {
|
||||
property alias url: root.url
|
||||
property alias scriptURL: root.userScriptUrl
|
||||
property alias eventBridge: eventBridgeWrapper.eventBridge
|
||||
property alias canGoBack: root.canGoBack;
|
||||
property var goBack: root.goBack;
|
||||
property alias urlTag: root.urlTag
|
||||
|
@ -22,12 +21,6 @@ Item {
|
|||
}
|
||||
*/
|
||||
|
||||
QtObject {
|
||||
id: eventBridgeWrapper
|
||||
WebChannel.id: "eventBridgeWrapper"
|
||||
property var eventBridge;
|
||||
}
|
||||
|
||||
property alias viewProfile: root.profile
|
||||
|
||||
WebEngineView {
|
||||
|
@ -72,9 +65,9 @@ Item {
|
|||
|
||||
property string newUrl: ""
|
||||
|
||||
webChannel.registeredObjects: [eventBridgeWrapper]
|
||||
|
||||
Component.onCompleted: {
|
||||
webChannel.registerObject("eventBridge", eventBridge);
|
||||
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
|
||||
// Ensure the JS from the web-engine makes it to our logging
|
||||
root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
|
||||
console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);
|
||||
|
|
|
@ -20,7 +20,6 @@ TabletModalWindow {
|
|||
id: loginDialogRoot
|
||||
objectName: "LoginDialog"
|
||||
|
||||
property var eventBridge;
|
||||
signal sendToScript(var message);
|
||||
property bool isHMD: false
|
||||
property bool gotoPreviousApp: false;
|
||||
|
|
|
@ -24,8 +24,6 @@ Window {
|
|||
resizable: true
|
||||
modality: Qt.ApplicationModal
|
||||
|
||||
property alias eventBridge: eventBridgeWrapper.eventBridge
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
|
||||
|
@ -45,16 +43,6 @@ Window {
|
|||
bottom: keyboard.top
|
||||
}
|
||||
|
||||
property alias eventBridgeWrapper: eventBridgeWrapper
|
||||
|
||||
QtObject {
|
||||
id: eventBridgeWrapper
|
||||
WebChannel.id: "eventBridgeWrapper"
|
||||
property var eventBridge;
|
||||
}
|
||||
|
||||
webChannel.registeredObjects: [eventBridgeWrapper]
|
||||
|
||||
// Create a global EventBridge object for raiseAndLowerKeyboard.
|
||||
WebEngineScript {
|
||||
id: createGlobalEventBridge
|
||||
|
@ -73,6 +61,10 @@ Window {
|
|||
|
||||
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ]
|
||||
|
||||
Component.onCompleted: {
|
||||
webChannel.registerObject("eventBridge", eventBridge);
|
||||
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
Keyboard {
|
||||
|
|
|
@ -116,9 +116,7 @@ Preference {
|
|||
|
||||
Component {
|
||||
id: tabletAvatarBrowserBuilder;
|
||||
TabletAvatarBrowser {
|
||||
eventBridge: tabletRoot.eventBridge
|
||||
}
|
||||
TabletAvatarBrowser { }
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -31,7 +31,6 @@ Rectangle {
|
|||
HifiConstants { id: hifi; }
|
||||
objectName: "AudioWindow"
|
||||
|
||||
property var eventBridge;
|
||||
property string title: "Audio Options"
|
||||
signal sendToScript(var message);
|
||||
|
||||
|
@ -161,7 +160,12 @@ Rectangle {
|
|||
text.text: devicename
|
||||
onCheckBoxClicked: {
|
||||
if (checked) {
|
||||
AudioDevice.setInputDeviceAsync(devicename)
|
||||
if (devicename.length > 0) {
|
||||
console.log("Audio.qml about to call AudioDevice.setInputDeviceAsync().devicename:" + devicename);
|
||||
AudioDevice.setInputDeviceAsync(devicename);
|
||||
} else {
|
||||
console.log("Audio.qml attempted to set input device to empty device name.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -217,7 +221,13 @@ Rectangle {
|
|||
text.text: devicename
|
||||
onCheckBoxClicked: {
|
||||
if (checked) {
|
||||
AudioDevice.setOutputDeviceAsync(devicename)
|
||||
if (devicename.length > 0) {
|
||||
console.log("Audio.qml about to call AudioDevice.setOutputDeviceAsync().devicename:" + devicename);
|
||||
AudioDevice.setOutputDeviceAsync(devicename);
|
||||
} else {
|
||||
console.log("Audio.qml attempted to set output device to empty device name.");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,6 @@ Rectangle {
|
|||
property var activeTab: "nearbyTab";
|
||||
property bool currentlyEditingDisplayName: false
|
||||
property bool punctuationMode: false;
|
||||
property var eventBridge;
|
||||
|
||||
HifiConstants { id: hifi; }
|
||||
|
||||
|
@ -1043,7 +1042,6 @@ Rectangle {
|
|||
} // Keyboard
|
||||
|
||||
HifiControls.TabletWebView {
|
||||
eventBridge: pal.eventBridge;
|
||||
id: userInfoViewer;
|
||||
anchors {
|
||||
top: parent.top;
|
||||
|
|
|
@ -24,7 +24,6 @@ Rectangle {
|
|||
property string title: "Asset Browser"
|
||||
property bool keyboardRaised: false
|
||||
|
||||
property var eventBridge;
|
||||
signal sendToScript(var message);
|
||||
property bool isHMD: false
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@ Rectangle {
|
|||
id: root
|
||||
objectName: "DCConectionTiming"
|
||||
|
||||
property var eventBridge;
|
||||
signal sendToScript(var message);
|
||||
property bool isHMD: false
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@ Rectangle {
|
|||
id: root
|
||||
|
||||
objectName: "DebugWindow"
|
||||
property var eventBridge;
|
||||
|
||||
property var title: "Debug Window"
|
||||
property bool isHMD: false
|
||||
|
|
|
@ -20,7 +20,6 @@ Rectangle {
|
|||
id: root
|
||||
objectName: "EntityStatistics"
|
||||
|
||||
property var eventBridge;
|
||||
signal sendToScript(var message);
|
||||
property bool isHMD: false
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@ Rectangle {
|
|||
id: root
|
||||
objectName: "LODTools"
|
||||
|
||||
property var eventBridge;
|
||||
signal sendToScript(var message);
|
||||
property bool isHMD: false
|
||||
|
||||
|
|
|
@ -23,7 +23,6 @@ Rectangle {
|
|||
property string title: "Running Scripts"
|
||||
HifiConstants { id: hifi }
|
||||
signal sendToScript(var message);
|
||||
property var eventBridge;
|
||||
property var scripts: ScriptDiscoveryService;
|
||||
property var scriptsModel: scripts.scriptsModelFilter
|
||||
property var runningScriptsModel: ListModel { }
|
||||
|
|
|
@ -7,14 +7,12 @@ StackView {
|
|||
objectName: "stack"
|
||||
initialItem: Qt.resolvedUrl('EditTabView.qml')
|
||||
|
||||
property var eventBridge;
|
||||
signal sendToScript(var message);
|
||||
|
||||
HifiConstants { id: hifi }
|
||||
|
||||
function pushSource(path) {
|
||||
editRoot.push(Qt.resolvedUrl(path));
|
||||
editRoot.currentItem.eventBridge = editRoot.eventBridge;
|
||||
editRoot.currentItem.sendToScript.connect(editRoot.sendToScript);
|
||||
}
|
||||
|
||||
|
|
|
@ -181,7 +181,6 @@ TabView {
|
|||
WebView {
|
||||
id: entityListToolWebView
|
||||
url: "../../../../../scripts/system/html/entityList.html"
|
||||
eventBridge: editRoot.eventBridge
|
||||
anchors.fill: parent
|
||||
enabled: true
|
||||
}
|
||||
|
@ -196,7 +195,6 @@ TabView {
|
|||
WebView {
|
||||
id: entityPropertiesWebView
|
||||
url: "../../../../../scripts/system/html/entityProperties.html"
|
||||
eventBridge: editRoot.eventBridge
|
||||
anchors.fill: parent
|
||||
enabled: true
|
||||
}
|
||||
|
@ -211,7 +209,6 @@ TabView {
|
|||
WebView {
|
||||
id: gridControlsWebView
|
||||
url: "../../../../../scripts/system/html/gridControls.html"
|
||||
eventBridge: editRoot.eventBridge
|
||||
anchors.fill: parent
|
||||
enabled: true
|
||||
}
|
||||
|
@ -226,7 +223,6 @@ TabView {
|
|||
WebView {
|
||||
id: particleExplorerWebView
|
||||
url: "../../../../../scripts/system/particle_explorer/particleExplorer.html"
|
||||
eventBridge: editRoot.eventBridge
|
||||
anchors.fill: parent
|
||||
enabled: true
|
||||
}
|
||||
|
@ -289,7 +285,7 @@ TabView {
|
|||
editTabView.currentIndex = id;
|
||||
} else {
|
||||
console.warn('Attempt to switch to invalid tab:', id);
|
||||
}
|
||||
}
|
||||
} else if (typeof id === 'string'){
|
||||
switch (id.toLowerCase()) {
|
||||
case 'create':
|
||||
|
|
|
@ -18,7 +18,6 @@ import "../../dialogs"
|
|||
|
||||
Rectangle {
|
||||
id: inputRecorder
|
||||
property var eventBridge;
|
||||
HifiConstants { id: hifi }
|
||||
signal sendToScript(var message);
|
||||
color: hifi.colors.baseGray;
|
||||
|
|
|
@ -20,7 +20,6 @@ Rectangle {
|
|||
// height: parent.height
|
||||
HifiConstants { id: hifi }
|
||||
color: hifi.colors.baseGray;
|
||||
property var eventBridge;
|
||||
signal sendToScript(var message);
|
||||
property bool keyboardEnabled: false
|
||||
property bool punctuationMode: false
|
||||
|
|
|
@ -29,7 +29,6 @@ StackView {
|
|||
initialItem: addressBarDialog
|
||||
width: parent !== null ? parent.width : undefined
|
||||
height: parent !== null ? parent.height : undefined
|
||||
property var eventBridge;
|
||||
property int cardWidth: 212;
|
||||
property int cardHeight: 152;
|
||||
property string metaverseBase: addressBarDialog.metaverseServerUrl + "/api/v1/";
|
||||
|
@ -80,7 +79,6 @@ StackView {
|
|||
var card = tabletWebView.createObject();
|
||||
card.url = addressBarDialog.metaverseServerUrl + targetString;
|
||||
card.parentStackItem = root;
|
||||
card.eventBridge = root.eventBridge;
|
||||
root.push(card);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -25,7 +25,6 @@ Item {
|
|||
property bool keyboardRaised: false
|
||||
property bool punctuationMode: false
|
||||
|
||||
property var eventBridge;
|
||||
signal sendToScript(var message);
|
||||
|
||||
anchors.fill: parent
|
||||
|
|
|
@ -19,7 +19,6 @@ StackView {
|
|||
objectName: "stack"
|
||||
property string title: "Audio Settings"
|
||||
|
||||
property var eventBridge;
|
||||
signal sendToScript(var message);
|
||||
|
||||
function pushSource(path) {
|
||||
|
|
|
@ -19,7 +19,6 @@ StackView {
|
|||
objectName: "stack"
|
||||
property string title: "Avatar Settings"
|
||||
|
||||
property var eventBridge;
|
||||
signal sendToScript(var message);
|
||||
|
||||
function pushSource(path) {
|
||||
|
|
|
@ -19,7 +19,6 @@ StackView {
|
|||
objectName: "stack"
|
||||
property string title: "General Settings"
|
||||
property alias gotoPreviousApp: root.gotoPreviousApp;
|
||||
property var eventBridge;
|
||||
signal sendToScript(var message);
|
||||
|
||||
function pushSource(path) {
|
||||
|
|
|
@ -19,7 +19,6 @@ StackView {
|
|||
objectName: "stack"
|
||||
property string title: "Graphics Settings"
|
||||
|
||||
property var eventBridge;
|
||||
signal sendToScript(var message);
|
||||
|
||||
function pushSource(path) {
|
||||
|
|
|
@ -19,7 +19,6 @@ StackView {
|
|||
objectName: "stack"
|
||||
property string title: "LOD Settings"
|
||||
|
||||
property var eventBridge;
|
||||
signal sendToScript(var message);
|
||||
|
||||
function pushSource(path) {
|
||||
|
|
|
@ -21,7 +21,6 @@ FocusScope {
|
|||
property var point: Qt.point(50, 50);
|
||||
TabletMenuStack { id: menuPopperUpper }
|
||||
property string subMenu: ""
|
||||
property var eventBridge;
|
||||
signal sendToScript(var message);
|
||||
|
||||
Rectangle {
|
||||
|
|
|
@ -49,7 +49,6 @@ Item {
|
|||
|
||||
function pushSource(path) {
|
||||
d.push(Qt.resolvedUrl(path));
|
||||
d.currentItem.eventBridge = tabletMenu.eventBridge
|
||||
d.currentItem.sendToScript.connect(tabletMenu.sendToScript);
|
||||
d.currentItem.focus = true;
|
||||
d.currentItem.forceActiveFocus();
|
||||
|
|
|
@ -19,7 +19,6 @@ StackView {
|
|||
objectName: "stack"
|
||||
property var title: "Networking Settings"
|
||||
|
||||
property var eventBridge;
|
||||
signal sendToScript(var message);
|
||||
|
||||
function pushSource(path) {
|
||||
|
|
|
@ -8,7 +8,6 @@ Item {
|
|||
id: tabletRoot
|
||||
objectName: "tabletRoot"
|
||||
property string username: "Unknown user"
|
||||
property var eventBridge;
|
||||
property var rootMenu;
|
||||
property var openModal: null;
|
||||
property var openMessage: null;
|
||||
|
@ -111,7 +110,6 @@ Item {
|
|||
function openBrowserWindow(request, profile) {
|
||||
var component = Qt.createComponent("../../controls/TabletWebView.qml");
|
||||
var newWindow = component.createObject(tabletRoot);
|
||||
newWindow.eventBridge = tabletRoot.eventBridge;
|
||||
newWindow.remove = true;
|
||||
newWindow.profile = profile;
|
||||
request.openIn(newWindow.webView);
|
||||
|
@ -175,7 +173,7 @@ Item {
|
|||
// Hook up callback for clara.io download from the marketplace.
|
||||
Connections {
|
||||
id: eventBridgeConnection
|
||||
target: null
|
||||
target: eventBridge
|
||||
onWebEventReceived: {
|
||||
if (message.slice(0, 17) === "CLARA.IO DOWNLOAD") {
|
||||
ApplicationInterface.addAssetToWorldFromURL(message.slice(18));
|
||||
|
@ -184,10 +182,6 @@ Item {
|
|||
}
|
||||
|
||||
onLoaded: {
|
||||
if (loader.item.hasOwnProperty("eventBridge")) {
|
||||
loader.item.eventBridge = eventBridge;
|
||||
eventBridgeConnection.target = eventBridge
|
||||
}
|
||||
if (loader.item.hasOwnProperty("sendToScript")) {
|
||||
loader.item.sendToScript.connect(tabletRoot.sendToScript);
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ Windows.ScrollingWindow {
|
|||
id: tabletRoot
|
||||
objectName: "tabletRoot"
|
||||
property string username: "Unknown user"
|
||||
property var eventBridge;
|
||||
|
||||
property var rootMenu;
|
||||
property string subMenu: ""
|
||||
|
@ -93,7 +92,7 @@ Windows.ScrollingWindow {
|
|||
// Hook up callback for clara.io download from the marketplace.
|
||||
Connections {
|
||||
id: eventBridgeConnection
|
||||
target: null
|
||||
target: eventBridge
|
||||
onWebEventReceived: {
|
||||
if (message.slice(0, 17) === "CLARA.IO DOWNLOAD") {
|
||||
ApplicationInterface.addAssetToWorldFromURL(message.slice(18));
|
||||
|
@ -102,10 +101,6 @@ Windows.ScrollingWindow {
|
|||
}
|
||||
|
||||
onLoaded: {
|
||||
if (loader.item.hasOwnProperty("eventBridge")) {
|
||||
loader.item.eventBridge = eventBridge;
|
||||
eventBridgeConnection.target = eventBridge
|
||||
}
|
||||
if (loader.item.hasOwnProperty("sendToScript")) {
|
||||
loader.item.sendToScript.connect(tabletRoot.sendToScript);
|
||||
}
|
||||
|
|
|
@ -27,8 +27,6 @@ Item {
|
|||
property bool keyboardRaised: false
|
||||
property bool punctuationMode: false
|
||||
|
||||
property alias eventBridge: eventBridgeWrapper.eventBridge
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
BaseWebView {
|
||||
|
@ -43,14 +41,6 @@ Item {
|
|||
bottom: footer.top
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: eventBridgeWrapper
|
||||
WebChannel.id: "eventBridgeWrapper"
|
||||
property var eventBridge;
|
||||
}
|
||||
|
||||
webChannel.registeredObjects: [eventBridgeWrapper]
|
||||
|
||||
// Create a global EventBridge object for raiseAndLowerKeyboard.
|
||||
WebEngineScript {
|
||||
id: createGlobalEventBridge
|
||||
|
@ -68,6 +58,11 @@ Item {
|
|||
}
|
||||
|
||||
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ]
|
||||
|
||||
Component.onCompleted: {
|
||||
webChannel.registerObject("eventBridge", eventBridge);
|
||||
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
|
|
|
@ -114,6 +114,7 @@
|
|||
#include <render/RenderFetchCullSortTask.h>
|
||||
#include <RenderDeferredTask.h>
|
||||
#include <RenderForwardTask.h>
|
||||
#include <RenderViewTask.h>
|
||||
#include <ResourceCache.h>
|
||||
#include <ResourceRequest.h>
|
||||
#include <SandboxUtils.h>
|
||||
|
@ -1866,15 +1867,9 @@ void Application::initializeGL() {
|
|||
|
||||
// Set up the render engine
|
||||
render::CullFunctor cullFunctor = LODManager::shouldRender;
|
||||
_renderEngine->addJob<RenderShadowTask>("RenderShadowTask", cullFunctor);
|
||||
const auto items = _renderEngine->addJob<RenderFetchCullSortTask>("FetchCullSort", cullFunctor);
|
||||
assert(items.canCast<RenderFetchCullSortTask::Output>());
|
||||
static const QString RENDER_FORWARD = "HIFI_RENDER_FORWARD";
|
||||
if (QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD)) {
|
||||
_renderEngine->addJob<RenderForwardTask>("Forward", items);
|
||||
} else {
|
||||
_renderEngine->addJob<RenderDeferredTask>("RenderDeferredTask", items);
|
||||
}
|
||||
bool isDeferred = !QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD);
|
||||
_renderEngine->addJob<RenderViewTask>("RenderMainView", cullFunctor, isDeferred);
|
||||
_renderEngine->load();
|
||||
_renderEngine->registerScene(_main3DScene);
|
||||
|
||||
|
@ -4126,10 +4121,10 @@ void Application::updateMyAvatarLookAtPosition() {
|
|||
}
|
||||
} else {
|
||||
// I am not looking at anyone else, so just look forward
|
||||
if (isHMD) {
|
||||
glm::mat4 worldHeadMat = myAvatar->getSensorToWorldMatrix() *
|
||||
myAvatar->getHeadControllerPoseInSensorFrame().getMatrix();
|
||||
lookAtSpot = transformPoint(worldHeadMat, glm::vec3(0.0f, 0.0f, -TREE_SCALE));
|
||||
auto headPose = myAvatar->getHeadControllerPoseInSensorFrame();
|
||||
if (headPose.isValid()) {
|
||||
glm::mat4 worldHeadMat = myAvatar->getSensorToWorldMatrix() * headPose.getMatrix();
|
||||
lookAtSpot = transformPoint(worldHeadMat, glm::vec3(0.0f, 0.0f, TREE_SCALE));
|
||||
} else {
|
||||
lookAtSpot = myAvatar->getHead()->getEyePosition() +
|
||||
(myAvatar->getHead()->getFinalOrientationInWorldFrame() * glm::vec3(0.0f, 0.0f, -TREE_SCALE));
|
||||
|
@ -5076,7 +5071,7 @@ namespace render {
|
|||
template <> const ItemKey payloadGetKey(const WorldBoxRenderData::Pointer& stuff) { return ItemKey::Builder::opaqueShape(); }
|
||||
template <> const Item::Bound payloadGetBound(const WorldBoxRenderData::Pointer& stuff) { return Item::Bound(); }
|
||||
template <> void payloadRender(const WorldBoxRenderData::Pointer& stuff, RenderArgs* args) {
|
||||
if (args->_renderMode != RenderArgs::MIRROR_RENDER_MODE && Menu::getInstance()->isOptionChecked(MenuOption::WorldAxes)) {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::WorldAxes)) {
|
||||
PerformanceTimer perfTimer("worldBox");
|
||||
|
||||
auto& batch = *args->_batch;
|
||||
|
|
|
@ -21,15 +21,35 @@
|
|||
|
||||
#include "MainWindow.h"
|
||||
#include "Menu.h"
|
||||
|
||||
#include "AvatarBookmarks.h"
|
||||
#include "InterfaceLogging.h"
|
||||
|
||||
#include <QtQuick/QQuickWindow>
|
||||
|
||||
AvatarBookmarks::AvatarBookmarks() {
|
||||
_bookmarksFilename = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + AVATARBOOKMARKS_FILENAME;
|
||||
_bookmarksFilename = PathUtils::getAppDataPath() + "/" + AVATARBOOKMARKS_FILENAME;
|
||||
readFromFile();
|
||||
}
|
||||
|
||||
void AvatarBookmarks::readFromFile() {
|
||||
// migrate old avatarbookmarks.json, used to be in 'local' folder on windows
|
||||
QString oldConfigPath = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + AVATARBOOKMARKS_FILENAME;
|
||||
QFile oldConfig(oldConfigPath);
|
||||
|
||||
// I imagine that in a year from now, this code for migrating (as well as the two lines above)
|
||||
// may be removed since all bookmarks should have been migrated by then
|
||||
// - Robbie Uvanni (6.8.2017)
|
||||
if (oldConfig.exists()) {
|
||||
if (QDir().rename(oldConfigPath, _bookmarksFilename)) {
|
||||
qCDebug(interfaceapp) << "Successfully migrated" << AVATARBOOKMARKS_FILENAME;
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "Failed to migrate" << AVATARBOOKMARKS_FILENAME;
|
||||
}
|
||||
}
|
||||
|
||||
Bookmarks::readFromFile();
|
||||
}
|
||||
|
||||
void AvatarBookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) {
|
||||
// Add menus/actions
|
||||
auto bookmarkAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::BookmarkAvatar);
|
||||
|
|
|
@ -29,6 +29,7 @@ public slots:
|
|||
|
||||
protected:
|
||||
void addBookmarkToMenu(Menu* menubar, const QString& name, const QString& address) override;
|
||||
void readFromFile();
|
||||
|
||||
private:
|
||||
const QString AVATARBOOKMARKS_FILENAME = "avatarbookmarks.json";
|
||||
|
|
|
@ -73,7 +73,7 @@ CrashHandler::Action CrashHandler::promptUserForAction(bool showCrashMessage) {
|
|||
layout->addWidget(label);
|
||||
|
||||
QRadioButton* option1 = new QRadioButton("Reset all my settings");
|
||||
QRadioButton* option2 = new QRadioButton("Reset my settings but retain avatar info.");
|
||||
QRadioButton* option2 = new QRadioButton("Reset my settings but keep essential info");
|
||||
QRadioButton* option3 = new QRadioButton("Continue with my current settings");
|
||||
option3->setChecked(true);
|
||||
layout->addWidget(option1);
|
||||
|
@ -95,7 +95,7 @@ CrashHandler::Action CrashHandler::promptUserForAction(bool showCrashMessage) {
|
|||
return CrashHandler::DELETE_INTERFACE_INI;
|
||||
}
|
||||
if (option2->isChecked()) {
|
||||
return CrashHandler::RETAIN_AVATAR_INFO;
|
||||
return CrashHandler::RETAIN_IMPORTANT_INFO;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -104,7 +104,7 @@ CrashHandler::Action CrashHandler::promptUserForAction(bool showCrashMessage) {
|
|||
}
|
||||
|
||||
void CrashHandler::handleCrash(CrashHandler::Action action) {
|
||||
if (action != CrashHandler::DELETE_INTERFACE_INI && action != CrashHandler::RETAIN_AVATAR_INFO) {
|
||||
if (action != CrashHandler::DELETE_INTERFACE_INI && action != CrashHandler::RETAIN_IMPORTANT_INFO) {
|
||||
// CrashHandler::DO_NOTHING or unexpected value
|
||||
return;
|
||||
}
|
||||
|
@ -116,12 +116,15 @@ void CrashHandler::handleCrash(CrashHandler::Action action) {
|
|||
const QString DISPLAY_NAME_KEY = "displayName";
|
||||
const QString FULL_AVATAR_URL_KEY = "fullAvatarURL";
|
||||
const QString FULL_AVATAR_MODEL_NAME_KEY = "fullAvatarModelName";
|
||||
const QString TUTORIAL_COMPLETE_FLAG_KEY = "tutorialComplete";
|
||||
|
||||
QString displayName;
|
||||
QUrl fullAvatarURL;
|
||||
QString fullAvatarModelName;
|
||||
QUrl address;
|
||||
bool tutorialComplete = false;
|
||||
|
||||
if (action == CrashHandler::RETAIN_AVATAR_INFO) {
|
||||
if (action == CrashHandler::RETAIN_IMPORTANT_INFO) {
|
||||
// Read avatar info
|
||||
|
||||
// Location and orientation
|
||||
|
@ -135,6 +138,9 @@ void CrashHandler::handleCrash(CrashHandler::Action action) {
|
|||
fullAvatarURL = settings.value(FULL_AVATAR_URL_KEY).toUrl();
|
||||
fullAvatarModelName = settings.value(FULL_AVATAR_MODEL_NAME_KEY).toString();
|
||||
settings.endGroup();
|
||||
|
||||
// Tutorial complete
|
||||
tutorialComplete = settings.value(TUTORIAL_COMPLETE_FLAG_KEY).toBool();
|
||||
}
|
||||
|
||||
// Delete Interface.ini
|
||||
|
@ -143,7 +149,7 @@ void CrashHandler::handleCrash(CrashHandler::Action action) {
|
|||
settingsFile.remove();
|
||||
}
|
||||
|
||||
if (action == CrashHandler::RETAIN_AVATAR_INFO) {
|
||||
if (action == CrashHandler::RETAIN_IMPORTANT_INFO) {
|
||||
// Write avatar info
|
||||
|
||||
// Location and orientation
|
||||
|
@ -157,6 +163,9 @@ void CrashHandler::handleCrash(CrashHandler::Action action) {
|
|||
settings.setValue(FULL_AVATAR_URL_KEY, fullAvatarURL);
|
||||
settings.setValue(FULL_AVATAR_MODEL_NAME_KEY, fullAvatarModelName);
|
||||
settings.endGroup();
|
||||
|
||||
// Tutorial complete
|
||||
settings.setValue(TUTORIAL_COMPLETE_FLAG_KEY, tutorialComplete);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ public:
|
|||
private:
|
||||
enum Action {
|
||||
DELETE_INTERFACE_INI,
|
||||
RETAIN_AVATAR_INFO,
|
||||
RETAIN_IMPORTANT_INFO,
|
||||
DO_NOTHING
|
||||
};
|
||||
|
||||
|
|
|
@ -794,6 +794,77 @@ controller::Pose MyAvatar::getRightHandTipPose() const {
|
|||
return pose;
|
||||
}
|
||||
|
||||
glm::vec3 MyAvatar::worldToJointPoint(const glm::vec3& position, const int jointIndex) const {
|
||||
glm::vec3 jointPos = getPosition();//default value if no or invalid joint specified
|
||||
glm::quat jointRot = getRotation();//default value if no or invalid joint specified
|
||||
if (jointIndex != -1) {
|
||||
if (_skeletonModel->getJointPositionInWorldFrame(jointIndex, jointPos)) {
|
||||
_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot);
|
||||
} else {
|
||||
qWarning() << "Invalid joint index specified: " << jointIndex;
|
||||
}
|
||||
}
|
||||
glm::vec3 modelOffset = position - jointPos;
|
||||
glm::vec3 jointSpacePosition = glm::inverse(jointRot) * modelOffset;
|
||||
|
||||
return jointSpacePosition;
|
||||
}
|
||||
|
||||
glm::vec3 MyAvatar::worldToJointDirection(const glm::vec3& worldDir, const int jointIndex) const {
|
||||
glm::quat jointRot = getRotation();//default value if no or invalid joint specified
|
||||
if ((jointIndex != -1) && (!_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot))) {
|
||||
qWarning() << "Invalid joint index specified: " << jointIndex;
|
||||
}
|
||||
|
||||
glm::vec3 jointSpaceDir = glm::inverse(jointRot) * worldDir;
|
||||
return jointSpaceDir;
|
||||
}
|
||||
|
||||
glm::quat MyAvatar::worldToJointRotation(const glm::quat& worldRot, const int jointIndex) const {
|
||||
glm::quat jointRot = getRotation();//default value if no or invalid joint specified
|
||||
if ((jointIndex != -1) && (!_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot))) {
|
||||
qWarning() << "Invalid joint index specified: " << jointIndex;
|
||||
}
|
||||
glm::quat jointSpaceRot = glm::inverse(jointRot) * worldRot;
|
||||
return jointSpaceRot;
|
||||
}
|
||||
|
||||
glm::vec3 MyAvatar::jointToWorldPoint(const glm::vec3& jointSpacePos, const int jointIndex) const {
|
||||
glm::vec3 jointPos = getPosition();//default value if no or invalid joint specified
|
||||
glm::quat jointRot = getRotation();//default value if no or invalid joint specified
|
||||
|
||||
if (jointIndex != -1) {
|
||||
if (_skeletonModel->getJointPositionInWorldFrame(jointIndex, jointPos)) {
|
||||
_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot);
|
||||
} else {
|
||||
qWarning() << "Invalid joint index specified: " << jointIndex;
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec3 worldOffset = jointRot * jointSpacePos;
|
||||
glm::vec3 worldPos = jointPos + worldOffset;
|
||||
|
||||
return worldPos;
|
||||
}
|
||||
|
||||
glm::vec3 MyAvatar::jointToWorldDirection(const glm::vec3& jointSpaceDir, const int jointIndex) const {
|
||||
glm::quat jointRot = getRotation();//default value if no or invalid joint specified
|
||||
if ((jointIndex != -1) && (!_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot))) {
|
||||
qWarning() << "Invalid joint index specified: " << jointIndex;
|
||||
}
|
||||
glm::vec3 worldDir = jointRot * jointSpaceDir;
|
||||
return worldDir;
|
||||
}
|
||||
|
||||
glm::quat MyAvatar::jointToWorldRotation(const glm::quat& jointSpaceRot, const int jointIndex) const {
|
||||
glm::quat jointRot = getRotation();//default value if no or invalid joint specified
|
||||
if ((jointIndex != -1) && (!_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot))) {
|
||||
qWarning() << "Invalid joint index specified: " << jointIndex;
|
||||
}
|
||||
glm::quat worldRot = jointRot * jointSpaceRot;
|
||||
return worldRot;
|
||||
}
|
||||
|
||||
// virtual
|
||||
void MyAvatar::render(RenderArgs* renderArgs) {
|
||||
// don't render if we've been asked to disable local rendering
|
||||
|
@ -2664,8 +2735,8 @@ bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, co
|
|||
return (offset.y > CYLINDER_TOP) || (offset.y < CYLINDER_BOTTOM);
|
||||
}
|
||||
|
||||
void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix, bool hasDriveInput) {
|
||||
_desiredBodyMatrix = desiredBodyMatrix;
|
||||
void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix,
|
||||
const glm::mat4& currentBodyMatrix, bool hasDriveInput) {
|
||||
|
||||
if (myAvatar.getHMDLeanRecenterEnabled()) {
|
||||
if (!isActive(Rotation) && shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix)) {
|
||||
|
@ -2679,7 +2750,7 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat
|
|||
}
|
||||
}
|
||||
|
||||
glm::mat4 desiredWorldMatrix = myAvatar.getSensorToWorldMatrix() * _desiredBodyMatrix;
|
||||
glm::mat4 desiredWorldMatrix = myAvatar.getSensorToWorldMatrix() * desiredBodyMatrix;
|
||||
glm::mat4 currentWorldMatrix = myAvatar.getSensorToWorldMatrix() * currentBodyMatrix;
|
||||
|
||||
AnimPose followWorldPose(currentWorldMatrix);
|
||||
|
|
|
@ -378,6 +378,15 @@ public:
|
|||
Q_INVOKABLE controller::Pose getLeftHandTipPose() const;
|
||||
Q_INVOKABLE controller::Pose getRightHandTipPose() const;
|
||||
|
||||
// world-space to avatar-space rigconversion functions
|
||||
Q_INVOKABLE glm::vec3 worldToJointPoint(const glm::vec3& position, const int jointIndex = -1) const;
|
||||
Q_INVOKABLE glm::vec3 worldToJointDirection(const glm::vec3& direction, const int jointIndex = -1) const;
|
||||
Q_INVOKABLE glm::quat worldToJointRotation(const glm::quat& rotation, const int jointIndex = -1) const;
|
||||
|
||||
Q_INVOKABLE glm::vec3 jointToWorldPoint(const glm::vec3& position, const int jointIndex = -1) const;
|
||||
Q_INVOKABLE glm::vec3 jointToWorldDirection(const glm::vec3& direction, const int jointIndex = -1) const;
|
||||
Q_INVOKABLE glm::quat jointToWorldRotation(const glm::quat& rotation, const int jointIndex = -1) const;
|
||||
|
||||
AvatarWeakPointer getLookAtTargetAvatar() const { return _lookAtTargetAvatar; }
|
||||
void updateLookAtTargetAvatar();
|
||||
void clearLookAtTargetAvatar();
|
||||
|
@ -702,7 +711,6 @@ private:
|
|||
Vertical,
|
||||
NumFollowTypes
|
||||
};
|
||||
glm::mat4 _desiredBodyMatrix;
|
||||
float _timeRemaining[NumFollowTypes];
|
||||
|
||||
void deactivate();
|
||||
|
|
|
@ -9,7 +9,9 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "AudioClient.h"
|
||||
#include <AudioClient.h>
|
||||
#include <AudioClientLogging.h>
|
||||
|
||||
#include "AudioDeviceScriptingInterface.h"
|
||||
#include "SettingsScriptingInterface.h"
|
||||
|
||||
|
@ -44,17 +46,23 @@ AudioDeviceScriptingInterface::AudioDeviceScriptingInterface(): QAbstractListMod
|
|||
onDeviceChanged();
|
||||
//set up previously saved device
|
||||
SettingsScriptingInterface* settings = SettingsScriptingInterface::getInstance();
|
||||
const QString inDevice = settings->getValue("audio_input_device").toString();
|
||||
const QString inDevice = settings->getValue("audio_input_device", _currentInputDevice).toString();
|
||||
if (inDevice != _currentInputDevice) {
|
||||
qCDebug(audioclient) << __FUNCTION__ << "about to call setInputDeviceAsync() device: [" << inDevice << "] _currentInputDevice:" << _currentInputDevice;
|
||||
setInputDeviceAsync(inDevice);
|
||||
}
|
||||
const QString outDevice = settings->getValue("audio_output_device").toString();
|
||||
|
||||
// If the audio_output_device setting is not available, use the _currentOutputDevice
|
||||
auto outDevice = settings->getValue("audio_output_device", _currentOutputDevice).toString();
|
||||
if (outDevice != _currentOutputDevice) {
|
||||
qCDebug(audioclient) << __FUNCTION__ << "about to call setOutputDeviceAsync() outDevice: [" << outDevice << "] _currentOutputDevice:" << _currentOutputDevice;
|
||||
setOutputDeviceAsync(outDevice);
|
||||
}
|
||||
}
|
||||
|
||||
bool AudioDeviceScriptingInterface::setInputDevice(const QString& deviceName) {
|
||||
qCDebug(audioclient) << __FUNCTION__ << "deviceName:" << deviceName;
|
||||
|
||||
bool result;
|
||||
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "switchInputToAudioDevice",
|
||||
Qt::BlockingQueuedConnection,
|
||||
|
@ -64,6 +72,9 @@ bool AudioDeviceScriptingInterface::setInputDevice(const QString& deviceName) {
|
|||
}
|
||||
|
||||
bool AudioDeviceScriptingInterface::setOutputDevice(const QString& deviceName) {
|
||||
|
||||
qCDebug(audioclient) << __FUNCTION__ << "deviceName:" << deviceName;
|
||||
|
||||
bool result;
|
||||
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "switchOutputToAudioDevice",
|
||||
Qt::BlockingQueuedConnection,
|
||||
|
@ -86,8 +97,10 @@ bool AudioDeviceScriptingInterface::setDeviceFromMenu(const QString& deviceMenuN
|
|||
for (ScriptingAudioDeviceInfo di: _devices) {
|
||||
if (mode == di.mode && deviceMenuName.contains(di.name)) {
|
||||
if (mode == QAudio::AudioOutput) {
|
||||
qCDebug(audioclient) << __FUNCTION__ << "about to call setOutputDeviceAsync() device: [" << di.name << "]";
|
||||
setOutputDeviceAsync(di.name);
|
||||
} else {
|
||||
qCDebug(audioclient) << __FUNCTION__ << "about to call setInputDeviceAsync() device: [" << di.name << "]";
|
||||
setInputDeviceAsync(di.name);
|
||||
}
|
||||
return true;
|
||||
|
@ -98,12 +111,26 @@ bool AudioDeviceScriptingInterface::setDeviceFromMenu(const QString& deviceMenuN
|
|||
}
|
||||
|
||||
void AudioDeviceScriptingInterface::setInputDeviceAsync(const QString& deviceName) {
|
||||
qCDebug(audioclient) << __FUNCTION__ << "deviceName:" << deviceName;
|
||||
|
||||
if (deviceName.isEmpty()) {
|
||||
qCDebug(audioclient) << __FUNCTION__ << "attempt to set empty deviceName:" << deviceName << "... ignoring!";
|
||||
return;
|
||||
}
|
||||
|
||||
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "switchInputToAudioDevice",
|
||||
Qt::QueuedConnection,
|
||||
Q_ARG(const QString&, deviceName));
|
||||
}
|
||||
|
||||
void AudioDeviceScriptingInterface::setOutputDeviceAsync(const QString& deviceName) {
|
||||
qCDebug(audioclient) << __FUNCTION__ << "deviceName:" << deviceName;
|
||||
|
||||
if (deviceName.isEmpty()) {
|
||||
qCDebug(audioclient) << __FUNCTION__ << "attempt to set empty deviceName:" << deviceName << "... ignoring!";
|
||||
return;
|
||||
}
|
||||
|
||||
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "switchOutputToAudioDevice",
|
||||
Qt::QueuedConnection,
|
||||
Q_ARG(const QString&, deviceName));
|
||||
|
@ -241,8 +268,11 @@ void AudioDeviceScriptingInterface::onCurrentInputDeviceChanged(const QString& n
|
|||
void AudioDeviceScriptingInterface::onCurrentOutputDeviceChanged(const QString& name)
|
||||
{
|
||||
currentDeviceUpdate(name, QAudio::AudioOutput);
|
||||
|
||||
// FIXME - this is kinda janky to set the setting on the result of a signal
|
||||
//we got a signal that device changed. Save it now
|
||||
SettingsScriptingInterface* settings = SettingsScriptingInterface::getInstance();
|
||||
qCDebug(audioclient) << __FUNCTION__ << "about to call settings->setValue('audio_output_device', name); name:" << name;
|
||||
settings->setValue("audio_output_device", name);
|
||||
emit currentOutputDeviceChanged(name);
|
||||
}
|
||||
|
|
|
@ -166,7 +166,10 @@ AudioClient::AudioClient() :
|
|||
|
||||
connect(&_receivedAudioStream, &MixedProcessedAudioStream::processSamples,
|
||||
this, &AudioClient::processReceivedSamples, Qt::DirectConnection);
|
||||
connect(this, &AudioClient::changeDevice, this, [=](const QAudioDeviceInfo& outputDeviceInfo) { switchOutputToAudioDevice(outputDeviceInfo); });
|
||||
connect(this, &AudioClient::changeDevice, this, [=](const QAudioDeviceInfo& outputDeviceInfo) {
|
||||
qCDebug(audioclient) << "got AudioClient::changeDevice signal, about to call switchOutputToAudioDevice() outputDeviceInfo: [" << outputDeviceInfo.deviceName() << "]";
|
||||
switchOutputToAudioDevice(outputDeviceInfo);
|
||||
});
|
||||
|
||||
connect(&_receivedAudioStream, &InboundAudioStream::mismatchedAudioCodec, this, &AudioClient::handleMismatchAudioFormat);
|
||||
|
||||
|
@ -379,7 +382,8 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) {
|
|||
CoUninitialize();
|
||||
}
|
||||
|
||||
qCDebug(audioclient) << "[" << deviceName << "] [" << getNamedAudioDeviceForMode(mode, deviceName).deviceName() << "]";
|
||||
qCDebug(audioclient) << "defaultAudioDeviceForMode mode: " << (mode == QAudio::AudioOutput ? "Output" : "Input")
|
||||
<< " [" << deviceName << "] [" << getNamedAudioDeviceForMode(mode, deviceName).deviceName() << "]";
|
||||
|
||||
return getNamedAudioDeviceForMode(mode, deviceName);
|
||||
#endif
|
||||
|
@ -555,8 +559,12 @@ void AudioClient::start() {
|
|||
}
|
||||
|
||||
void AudioClient::stop() {
|
||||
|
||||
// "switch" to invalid devices in order to shut down the state
|
||||
qCDebug(audioclient) << "AudioClient::stop(), about to call switchInputToAudioDevice(null)";
|
||||
switchInputToAudioDevice(QAudioDeviceInfo());
|
||||
|
||||
qCDebug(audioclient) << "AudioClient::stop(), about to call switchOutputToAudioDevice(null)";
|
||||
switchOutputToAudioDevice(QAudioDeviceInfo());
|
||||
}
|
||||
|
||||
|
@ -748,12 +756,12 @@ QVector<QString> AudioClient::getDeviceNames(QAudio::Mode mode) {
|
|||
}
|
||||
|
||||
bool AudioClient::switchInputToAudioDevice(const QString& inputDeviceName) {
|
||||
qCDebug(audioclient) << "[" << inputDeviceName << "] [" << getNamedAudioDeviceForMode(QAudio::AudioInput, inputDeviceName).deviceName() << "]";
|
||||
qCDebug(audioclient) << "switchInputToAudioDevice [" << inputDeviceName << "] [" << getNamedAudioDeviceForMode(QAudio::AudioInput, inputDeviceName).deviceName() << "]";
|
||||
return switchInputToAudioDevice(getNamedAudioDeviceForMode(QAudio::AudioInput, inputDeviceName));
|
||||
}
|
||||
|
||||
bool AudioClient::switchOutputToAudioDevice(const QString& outputDeviceName) {
|
||||
qCDebug(audioclient) << "[" << outputDeviceName << "] [" << getNamedAudioDeviceForMode(QAudio::AudioOutput, outputDeviceName).deviceName() << "]";
|
||||
qCDebug(audioclient) << "switchOutputToAudioDevice [" << outputDeviceName << "] [" << getNamedAudioDeviceForMode(QAudio::AudioOutput, outputDeviceName).deviceName() << "]";
|
||||
return switchOutputToAudioDevice(getNamedAudioDeviceForMode(QAudio::AudioOutput, outputDeviceName));
|
||||
}
|
||||
|
||||
|
@ -1298,6 +1306,7 @@ void AudioClient::setIsStereoInput(bool isStereoInput) {
|
|||
}
|
||||
|
||||
// change in channel count for desired input format, restart the input device
|
||||
qCDebug(audioclient) << __FUNCTION__ << "about to call switchInputToAudioDevice:" << _inputAudioDeviceName;
|
||||
switchInputToAudioDevice(_inputAudioDeviceName);
|
||||
}
|
||||
}
|
||||
|
@ -1334,6 +1343,7 @@ void AudioClient::outputFormatChanged() {
|
|||
}
|
||||
|
||||
bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceInfo) {
|
||||
qCDebug(audioclient) << __FUNCTION__ << "inputDeviceInfo: [" << inputDeviceInfo.deviceName() << "]";
|
||||
bool supportedFormat = false;
|
||||
|
||||
// cleanup any previously initialized device
|
||||
|
@ -1448,6 +1458,8 @@ void AudioClient::outputNotify() {
|
|||
}
|
||||
|
||||
bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo) {
|
||||
qCDebug(audioclient) << "AudioClient::switchOutputToAudioDevice() outputDeviceInfo: [" << outputDeviceInfo.deviceName() << "]";
|
||||
|
||||
bool supportedFormat = false;
|
||||
|
||||
Lock localAudioLock(_localAudioMutex);
|
||||
|
@ -1582,7 +1594,11 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice
|
|||
}
|
||||
|
||||
int AudioClient::setOutputBufferSize(int numFrames, bool persist) {
|
||||
qCDebug(audioclient) << __FUNCTION__ << "numFrames:" << numFrames << "persist:" << persist;
|
||||
|
||||
numFrames = std::min(std::max(numFrames, MIN_BUFFER_FRAMES), MAX_BUFFER_FRAMES);
|
||||
qCDebug(audioclient) << __FUNCTION__ << "clamped numFrames:" << numFrames << "_sessionOutputBufferSizeFrames:" << _sessionOutputBufferSizeFrames;
|
||||
|
||||
if (numFrames != _sessionOutputBufferSizeFrames) {
|
||||
qCInfo(audioclient, "Audio output buffer set to %d frames", numFrames);
|
||||
_sessionOutputBufferSizeFrames = numFrames;
|
||||
|
@ -1594,6 +1610,7 @@ int AudioClient::setOutputBufferSize(int numFrames, bool persist) {
|
|||
// The buffer size can't be adjusted after QAudioOutput::start() has been called, so
|
||||
// recreate the device by switching to the default.
|
||||
QAudioDeviceInfo outputDeviceInfo = defaultAudioDeviceForMode(QAudio::AudioOutput);
|
||||
qCDebug(audioclient) << __FUNCTION__ << "about to send changeDevice signal outputDeviceInfo: [" << outputDeviceInfo.deviceName() << "]";
|
||||
emit changeDevice(outputDeviceInfo); // On correct thread, please, as setOutputBufferSize can be called from main thread.
|
||||
}
|
||||
}
|
||||
|
|
|
@ -221,6 +221,12 @@ void HmdDisplayPlugin::internalPresent() {
|
|||
float shiftLeftBy = getLeftCenterPixel() - (sourceSize.x / 2);
|
||||
float newWidth = sourceSize.x - shiftLeftBy;
|
||||
|
||||
// Experimentally adjusted the region presented in preview to avoid seeing the masked pixels and recenter the center...
|
||||
static float SCALE_WIDTH = 0.9f;
|
||||
static float SCALE_OFFSET = 2.0f;
|
||||
newWidth *= SCALE_WIDTH;
|
||||
shiftLeftBy *= SCALE_OFFSET;
|
||||
|
||||
const unsigned int RATIO_Y = 9;
|
||||
const unsigned int RATIO_X = 16;
|
||||
glm::uvec2 originalClippedSize { newWidth, newWidth * RATIO_Y / RATIO_X };
|
||||
|
|
|
@ -605,7 +605,7 @@ bool RenderableModelEntityItem::findDetailedRayIntersection(const glm::vec3& ori
|
|||
|
||||
QString extraInfo;
|
||||
return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance,
|
||||
face, surfaceNormal, extraInfo, precisionPicking, precisionPicking);
|
||||
face, surfaceNormal, extraInfo, precisionPicking, false);
|
||||
}
|
||||
|
||||
void RenderableModelEntityItem::getCollisionGeometryResource() {
|
||||
|
|
|
@ -460,17 +460,11 @@ FBXLight extractLight(const FBXNode& object) {
|
|||
}
|
||||
|
||||
QByteArray fileOnUrl(const QByteArray& filepath, const QString& url) {
|
||||
QString path = QFileInfo(url).path();
|
||||
QByteArray filename = filepath;
|
||||
QFileInfo checkFile(path + "/" + filepath);
|
||||
// in order to match the behaviour when loading models from remote URLs
|
||||
// we assume that all external textures are right beside the loaded model
|
||||
// ignoring any relative paths or absolute paths inside of models
|
||||
|
||||
// check if the file exists at the RelativeFilename
|
||||
if (!(checkFile.exists() && checkFile.isFile())) {
|
||||
// if not, assume it is in the fbx directory
|
||||
filename = filename.mid(filename.lastIndexOf('/') + 1);
|
||||
}
|
||||
|
||||
return filename;
|
||||
return filepath.mid(filepath.lastIndexOf('/') + 1);
|
||||
}
|
||||
|
||||
FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QString& url) {
|
||||
|
|
|
@ -277,6 +277,23 @@ QString getEventBridgeJavascript() {
|
|||
return javaScriptToInject;
|
||||
}
|
||||
|
||||
class EventBridgeWrapper : public QObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QObject* eventBridge READ getEventBridge CONSTANT);
|
||||
|
||||
public:
|
||||
EventBridgeWrapper(QObject* eventBridge, QObject* parent = nullptr) : QObject(parent), _eventBridge(eventBridge) {
|
||||
}
|
||||
|
||||
QObject* getEventBridge() {
|
||||
return _eventBridge;
|
||||
}
|
||||
|
||||
private:
|
||||
QObject* _eventBridge;
|
||||
};
|
||||
|
||||
|
||||
|
||||
QQmlEngine* acquireEngine(QQuickWindow* window) {
|
||||
Q_ASSERT(QThread::currentThread() == qApp->thread());
|
||||
|
@ -430,7 +447,6 @@ OffscreenQmlSurface::~OffscreenQmlSurface() {
|
|||
|
||||
_canvas->deleteLater();
|
||||
_rootItem->deleteLater();
|
||||
_qmlComponent->deleteLater();
|
||||
_quickWindow->deleteLater();
|
||||
releaseEngine();
|
||||
}
|
||||
|
@ -473,11 +489,12 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) {
|
|||
_qmlContext = new QQmlContext(qmlEngine->rootContext());
|
||||
|
||||
_qmlContext->setContextProperty("offscreenWindow", QVariant::fromValue(getWindow()));
|
||||
_qmlContext->setContextProperty("globalEventBridge", this);
|
||||
_qmlContext->setContextProperty("eventBridge", this);
|
||||
_qmlContext->setContextProperty("webEntity", this);
|
||||
|
||||
_qmlComponent = new QQmlComponent(qmlEngine);
|
||||
|
||||
// FIXME Compatibility mechanism for existing HTML and JS that uses eventBridgeWrapper
|
||||
// Find a way to flag older scripts using this mechanism and wanr that this is deprecated
|
||||
_qmlContext->setContextProperty("eventBridgeWrapper", new EventBridgeWrapper(this, _qmlContext));
|
||||
|
||||
if (!_canvas->makeCurrent()) {
|
||||
qWarning("Failed to make context current for QML Renderer");
|
||||
|
@ -577,71 +594,79 @@ void OffscreenQmlSurface::setBaseUrl(const QUrl& baseUrl) {
|
|||
_qmlContext->setBaseUrl(baseUrl);
|
||||
}
|
||||
|
||||
QObject* OffscreenQmlSurface::load(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> f) {
|
||||
QObject* OffscreenQmlSurface::load(const QUrl& qmlSource, bool createNewContext, std::function<void(QQmlContext*, QObject*)> f) {
|
||||
// Synchronous loading may take a while; restart the deadlock timer
|
||||
QMetaObject::invokeMethod(qApp, "updateHeartbeat", Qt::DirectConnection);
|
||||
|
||||
if ((qmlSource.isRelative() && !qmlSource.isEmpty()) || qmlSource.scheme() == QLatin1String("file")) {
|
||||
_qmlComponent->loadUrl(_qmlContext->resolvedUrl(qmlSource), QQmlComponent::PreferSynchronous);
|
||||
} else {
|
||||
_qmlComponent->loadUrl(qmlSource, QQmlComponent::PreferSynchronous);
|
||||
QQmlContext* targetContext = _qmlContext;
|
||||
if (_rootItem && createNewContext) {
|
||||
targetContext = new QQmlContext(targetContext);
|
||||
}
|
||||
|
||||
QUrl finalQmlSource = qmlSource;
|
||||
if ((qmlSource.isRelative() && !qmlSource.isEmpty()) || qmlSource.scheme() == QLatin1String("file")) {
|
||||
finalQmlSource = _qmlContext->resolvedUrl(qmlSource);
|
||||
}
|
||||
|
||||
if (_qmlComponent->isLoading()) {
|
||||
connect(_qmlComponent, &QQmlComponent::statusChanged, this,
|
||||
[this, f](QQmlComponent::Status){
|
||||
finishQmlLoad(f);
|
||||
});
|
||||
auto qmlComponent = new QQmlComponent(_qmlContext->engine(), finalQmlSource, QQmlComponent::PreferSynchronous);
|
||||
if (qmlComponent->isLoading()) {
|
||||
connect(qmlComponent, &QQmlComponent::statusChanged, this,
|
||||
[this, qmlComponent, targetContext, f](QQmlComponent::Status) {
|
||||
finishQmlLoad(qmlComponent, targetContext, f);
|
||||
});
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return finishQmlLoad(f);
|
||||
return finishQmlLoad(qmlComponent, targetContext, f);
|
||||
}
|
||||
|
||||
QObject* OffscreenQmlSurface::loadInNewContext(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> f) {
|
||||
return load(qmlSource, true, f);
|
||||
}
|
||||
|
||||
QObject* OffscreenQmlSurface::load(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> f) {
|
||||
return load(qmlSource, false, f);
|
||||
}
|
||||
|
||||
void OffscreenQmlSurface::clearCache() {
|
||||
_qmlContext->engine()->clearComponentCache();
|
||||
}
|
||||
|
||||
QObject* OffscreenQmlSurface::finishQmlLoad(std::function<void(QQmlContext*, QObject*)> f) {
|
||||
#if 0
|
||||
if (!_rootItem) {
|
||||
QQmlComponent component(_qmlContext->engine());
|
||||
component.setData(R"QML(
|
||||
import QtQuick 2.0
|
||||
import QtWebChannel 1.0
|
||||
Item { Component.onCompleted: globalEventBridge.WebChannel.id = "globalEventBridge"; }
|
||||
)QML", QUrl());
|
||||
QObject *helper = component.create(_qmlContext);
|
||||
qDebug() << "Created helper";
|
||||
}
|
||||
#endif
|
||||
disconnect(_qmlComponent, &QQmlComponent::statusChanged, this, 0);
|
||||
if (_qmlComponent->isError()) {
|
||||
QList<QQmlError> errorList = _qmlComponent->errors();
|
||||
foreach(const QQmlError& error, errorList) {
|
||||
qWarning() << error.url() << error.line() << error;
|
||||
QObject* OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, std::function<void(QQmlContext*, QObject*)> f) {
|
||||
disconnect(qmlComponent, &QQmlComponent::statusChanged, this, 0);
|
||||
if (qmlComponent->isError()) {
|
||||
for (const auto& error : qmlComponent->errors()) {
|
||||
qCWarning(glLogging) << error.url() << error.line() << error;
|
||||
}
|
||||
qmlComponent->deleteLater();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
QObject* newObject = _qmlComponent->beginCreate(_qmlContext);
|
||||
if (_qmlComponent->isError()) {
|
||||
QList<QQmlError> errorList = _qmlComponent->errors();
|
||||
foreach(const QQmlError& error, errorList)
|
||||
QObject* newObject = qmlComponent->beginCreate(qmlContext);
|
||||
if (qmlComponent->isError()) {
|
||||
for (const auto& error : qmlComponent->errors()) {
|
||||
qCWarning(glLogging) << error.url() << error.line() << error;
|
||||
}
|
||||
if (!_rootItem) {
|
||||
qFatal("Unable to finish loading QML root");
|
||||
}
|
||||
qmlComponent->deleteLater();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
_qmlContext->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership);
|
||||
newObject->setProperty("eventBridge", QVariant::fromValue(this));
|
||||
qmlContext->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership);
|
||||
f(qmlContext, newObject);
|
||||
|
||||
f(_qmlContext, newObject);
|
||||
_qmlComponent->completeCreate();
|
||||
QObject* eventBridge = qmlContext->contextProperty("eventBridge").value<QObject*>();
|
||||
if (qmlContext != _qmlContext && eventBridge && eventBridge != this) {
|
||||
// FIXME Compatibility mechanism for existing HTML and JS that uses eventBridgeWrapper
|
||||
// Find a way to flag older scripts using this mechanism and wanr that this is deprecated
|
||||
qmlContext->setContextProperty("eventBridgeWrapper", new EventBridgeWrapper(eventBridge, qmlContext));
|
||||
}
|
||||
|
||||
qmlComponent->completeCreate();
|
||||
qmlComponent->deleteLater();
|
||||
|
||||
|
||||
// All quick items should be focusable
|
||||
|
|
|
@ -48,6 +48,8 @@ public:
|
|||
void resize(const QSize& size, bool forceResize = false);
|
||||
QSize size() const;
|
||||
|
||||
Q_INVOKABLE QObject* load(const QUrl& qmlSource, bool createNewContext, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {});
|
||||
Q_INVOKABLE QObject* loadInNewContext(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {});
|
||||
Q_INVOKABLE QObject* load(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {});
|
||||
Q_INVOKABLE QObject* load(const QString& qmlSourceFile, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {}) {
|
||||
return load(QUrl(qmlSourceFile), f);
|
||||
|
@ -118,7 +120,7 @@ protected:
|
|||
void setFocusText(bool newFocusText);
|
||||
|
||||
private:
|
||||
QObject* finishQmlLoad(std::function<void(QQmlContext*, QObject*)> f);
|
||||
QObject* finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, std::function<void(QQmlContext*, QObject*)> f);
|
||||
QPointF mapWindowToUi(const QPointF& sourcePosition, QObject* sourceObject);
|
||||
void setupFbo();
|
||||
bool allowNewFrame(uint8_t fps);
|
||||
|
@ -134,7 +136,6 @@ private:
|
|||
QQuickWindow* _quickWindow { nullptr };
|
||||
QMyQuickRenderControl* _renderControl{ nullptr };
|
||||
QQmlContext* _qmlContext { nullptr };
|
||||
QQmlComponent* _qmlComponent { nullptr };
|
||||
QQuickItem* _rootItem { nullptr };
|
||||
OffscreenGLCanvas* _canvas { nullptr };
|
||||
|
||||
|
|
|
@ -63,11 +63,17 @@ void GLBackend::do_clearFramebuffer(const Batch& batch, size_t paramOffset) {
|
|||
int useScissor = batch._params[paramOffset + 0]._int;
|
||||
|
||||
GLuint glmask = 0;
|
||||
bool restoreStencilMask = false;
|
||||
uint8_t cacheStencilMask = 0xFF;
|
||||
if (masks & Framebuffer::BUFFER_STENCIL) {
|
||||
glClearStencil(stencil);
|
||||
glmask |= GL_STENCIL_BUFFER_BIT;
|
||||
// TODO: we will probably need to also check the write mask of stencil like we do
|
||||
// for depth buffer, but as would say a famous Fez owner "We'll cross that bridge when we come to it"
|
||||
|
||||
cacheStencilMask = _pipeline._stateCache.stencilActivation.getWriteMaskFront();
|
||||
if (cacheStencilMask != 0xFF) {
|
||||
restoreStencilMask = true;
|
||||
glStencilMask( 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
bool restoreDepthMask = false;
|
||||
|
@ -121,6 +127,11 @@ void GLBackend::do_clearFramebuffer(const Batch& batch, size_t paramOffset) {
|
|||
glDisable(GL_SCISSOR_TEST);
|
||||
}
|
||||
|
||||
// Restore Stencil write mask
|
||||
if (restoreStencilMask) {
|
||||
glStencilMask(cacheStencilMask);
|
||||
}
|
||||
|
||||
// Restore write mask meaning turn back off
|
||||
if (restoreDepthMask) {
|
||||
glDepthMask(GL_FALSE);
|
||||
|
|
|
@ -20,6 +20,10 @@
|
|||
#include <DependencyManager.h>
|
||||
#include "AddressManager.h"
|
||||
|
||||
UserActivityLogger::UserActivityLogger() {
|
||||
_timer.start();
|
||||
}
|
||||
|
||||
UserActivityLogger& UserActivityLogger::getInstance() {
|
||||
static UserActivityLogger sharedInstance;
|
||||
return sharedInstance;
|
||||
|
@ -42,6 +46,12 @@ void UserActivityLogger::logAction(QString action, QJsonObject details, JSONCall
|
|||
actionPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"action_name\"");
|
||||
actionPart.setBody(QByteArray().append(action));
|
||||
multipart->append(actionPart);
|
||||
|
||||
// Log the local-time that this event was logged
|
||||
QHttpPart elapsedPart;
|
||||
elapsedPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"elapsed_ms\"");
|
||||
elapsedPart.setBody(QString::number(_timer.elapsed()).toLocal8Bit());
|
||||
multipart->append(elapsedPart);
|
||||
|
||||
// If there are action details, add them to the multipart
|
||||
if (!details.isEmpty()) {
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <QString>
|
||||
#include <QJsonObject>
|
||||
#include <QNetworkReply>
|
||||
#include <QElapsedTimer>
|
||||
|
||||
#include <SettingHandle.h>
|
||||
#include "AddressManager.h"
|
||||
|
@ -51,8 +52,10 @@ private slots:
|
|||
void requestError(QNetworkReply& errorReply);
|
||||
|
||||
private:
|
||||
UserActivityLogger() {};
|
||||
UserActivityLogger();
|
||||
Setting::Handle<bool> _disabled { "UserActivityLoggerDisabled", false };
|
||||
|
||||
QElapsedTimer _timer;
|
||||
};
|
||||
|
||||
#endif // hifi_UserActivityLogger_h
|
||||
|
|
|
@ -56,7 +56,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
case PacketType::AvatarData:
|
||||
case PacketType::BulkAvatarData:
|
||||
case PacketType::KillAvatar:
|
||||
return static_cast<PacketVersion>(AvatarMixerPacketVersion::AvatarIdentitySequenceId);
|
||||
return static_cast<PacketVersion>(AvatarMixerPacketVersion::MannequinDefaultAvatar);
|
||||
case PacketType::MessagesData:
|
||||
return static_cast<PacketVersion>(MessageDataVersion::TextOrBinaryData);
|
||||
case PacketType::ICEServerHeartbeat:
|
||||
|
|
|
@ -236,7 +236,8 @@ enum class AvatarMixerPacketVersion : PacketVersion {
|
|||
AvatarAsChildFixes,
|
||||
StickAndBallDefaultAvatar,
|
||||
IdentityPacketsIncludeUpdateTime,
|
||||
AvatarIdentitySequenceId
|
||||
AvatarIdentitySequenceId,
|
||||
MannequinDefaultAvatar
|
||||
};
|
||||
|
||||
enum class DomainConnectRequestVersion : PacketVersion {
|
||||
|
|
|
@ -110,10 +110,6 @@ void Antialiasing::run(const render::RenderContextPointer& renderContext, const
|
|||
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) {
|
||||
return;
|
||||
}
|
||||
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
batch.enableStereo(false);
|
||||
batch.setViewportTransform(args->_viewport);
|
||||
|
|
|
@ -504,10 +504,7 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext,
|
|||
{
|
||||
// Framebuffer copy operations cannot function as multipass stereo operations.
|
||||
batch.enableStereo(false);
|
||||
|
||||
// perform deferred lighting, rendering to free fbo
|
||||
auto framebufferCache = DependencyManager::get<FramebufferCache>();
|
||||
|
||||
|
||||
auto textureCache = DependencyManager::get<TextureCache>();
|
||||
auto deferredLightingEffect = DependencyManager::get<DeferredLightingEffect>();
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ std::vector<vec3> polygon() {
|
|||
std::vector<vec3> result;
|
||||
result.reserve(SIDES);
|
||||
double angleIncrement = 2.0 * M_PI / SIDES;
|
||||
for (size_t i = 0; i < SIDES; ++i) {
|
||||
for (size_t i = 0; i < SIDES; i++) {
|
||||
double angle = (double)i * angleIncrement;
|
||||
result.push_back(vec3{ cos(angle) * 0.5, 0.0, sin(angle) * 0.5 });
|
||||
}
|
||||
|
@ -172,20 +172,20 @@ void setupFlatShape(GeometryCache::ShapeData& shapeData, const geometry::Solid<N
|
|||
vertices.reserve(N * faceCount * 2);
|
||||
solidIndices.reserve(faceIndexCount * faceCount);
|
||||
|
||||
for (size_t f = 0; f < faceCount; ++f) {
|
||||
for (size_t f = 0; f < faceCount; f++) {
|
||||
const Face<N>& face = shape.faces[f];
|
||||
// Compute the face normal
|
||||
vec3 faceNormal = shape.getFaceNormal(f);
|
||||
|
||||
// Create the vertices for the face
|
||||
for (Index i = 0; i < N; ++i) {
|
||||
for (Index i = 0; i < N; i++) {
|
||||
Index originalIndex = face[i];
|
||||
vertices.push_back(shape.vertices[originalIndex]);
|
||||
vertices.push_back(faceNormal);
|
||||
}
|
||||
|
||||
// Create the wire indices for unseen edges
|
||||
for (Index i = 0; i < N; ++i) {
|
||||
for (Index i = 0; i < N; i++) {
|
||||
Index a = i;
|
||||
Index b = (i + 1) % N;
|
||||
auto token = indexToken(face[a], face[b]);
|
||||
|
@ -197,7 +197,7 @@ void setupFlatShape(GeometryCache::ShapeData& shapeData, const geometry::Solid<N
|
|||
}
|
||||
|
||||
// Create the solid face indices
|
||||
for (Index i = 0; i < N - 2; ++i) {
|
||||
for (Index i = 0; i < N - 2; i++) {
|
||||
solidIndices.push_back(0 + baseVertex);
|
||||
solidIndices.push_back(i + 1 + baseVertex);
|
||||
solidIndices.push_back(i + 2 + baseVertex);
|
||||
|
@ -229,10 +229,10 @@ void setupSmoothShape(GeometryCache::ShapeData& shapeData, const geometry::Solid
|
|||
|
||||
solidIndices.reserve(faceIndexCount * faceCount);
|
||||
|
||||
for (size_t f = 0; f < faceCount; ++f) {
|
||||
for (size_t f = 0; f < faceCount; f++) {
|
||||
const Face<N>& face = shape.faces[f];
|
||||
// Create the wire indices for unseen edges
|
||||
for (Index i = 0; i < N; ++i) {
|
||||
for (Index i = 0; i < N; i++) {
|
||||
Index a = face[i];
|
||||
Index b = face[(i + 1) % N];
|
||||
auto token = indexToken(a, b);
|
||||
|
@ -244,7 +244,7 @@ void setupSmoothShape(GeometryCache::ShapeData& shapeData, const geometry::Solid
|
|||
}
|
||||
|
||||
// Create the solid face indices
|
||||
for (Index i = 0; i < N - 2; ++i) {
|
||||
for (Index i = 0; i < N - 2; i++) {
|
||||
solidIndices.push_back(face[i] + baseVertex);
|
||||
solidIndices.push_back(face[i + 1] + baseVertex);
|
||||
solidIndices.push_back(face[i + 2] + baseVertex);
|
||||
|
@ -256,23 +256,30 @@ void setupSmoothShape(GeometryCache::ShapeData& shapeData, const geometry::Solid
|
|||
}
|
||||
|
||||
template <uint32_t N>
|
||||
void extrudePolygon(GeometryCache::ShapeData& shapeData, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer) {
|
||||
void extrudePolygon(GeometryCache::ShapeData& shapeData, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer, bool isConical = false) {
|
||||
using namespace geometry;
|
||||
Index baseVertex = (Index)(vertexBuffer->getSize() / SHAPE_VERTEX_STRIDE);
|
||||
VertexVector vertices;
|
||||
IndexVector solidIndices, wireIndices;
|
||||
|
||||
// Top and bottom faces
|
||||
// Top (if not conical) and bottom faces
|
||||
std::vector<vec3> shape = polygon<N>();
|
||||
for (const vec3& v : shape) {
|
||||
vertices.push_back(vec3(v.x, 0.5f, v.z));
|
||||
vertices.push_back(vec3(0, 1, 0));
|
||||
if (isConical) {
|
||||
for (uint32_t i = 0; i < N; i++) {
|
||||
vertices.push_back(vec3(0.0f, 0.5f, 0.0f));
|
||||
vertices.push_back(vec3(0.0f, 1.0f, 0.0f));
|
||||
}
|
||||
} else {
|
||||
for (const vec3& v : shape) {
|
||||
vertices.push_back(vec3(v.x, 0.5f, v.z));
|
||||
vertices.push_back(vec3(0.0f, 1.0f, 0.0f));
|
||||
}
|
||||
}
|
||||
for (const vec3& v : shape) {
|
||||
vertices.push_back(vec3(v.x, -0.5f, v.z));
|
||||
vertices.push_back(vec3(0, -1, 0));
|
||||
vertices.push_back(vec3(0.0f, -1.0f, 0.0f));
|
||||
}
|
||||
for (uint32_t i = 2; i < N; ++i) {
|
||||
for (uint32_t i = 2; i < N; i++) {
|
||||
solidIndices.push_back(baseVertex + 0);
|
||||
solidIndices.push_back(baseVertex + i);
|
||||
solidIndices.push_back(baseVertex + i - 1);
|
||||
|
@ -280,7 +287,7 @@ void extrudePolygon(GeometryCache::ShapeData& shapeData, gpu::BufferPointer& ver
|
|||
solidIndices.push_back(baseVertex + i + N - 1);
|
||||
solidIndices.push_back(baseVertex + i + N);
|
||||
}
|
||||
for (uint32_t i = 1; i <= N; ++i) {
|
||||
for (uint32_t i = 1; i <= N; i++) {
|
||||
wireIndices.push_back(baseVertex + (i % N));
|
||||
wireIndices.push_back(baseVertex + i - 1);
|
||||
wireIndices.push_back(baseVertex + (i % N) + N);
|
||||
|
@ -290,12 +297,12 @@ void extrudePolygon(GeometryCache::ShapeData& shapeData, gpu::BufferPointer& ver
|
|||
// Now do the sides
|
||||
baseVertex += 2 * N;
|
||||
|
||||
for (uint32_t i = 0; i < N; ++i) {
|
||||
for (uint32_t i = 0; i < N; i++) {
|
||||
vec3 left = shape[i];
|
||||
vec3 right = shape[(i + 1) % N];
|
||||
vec3 normal = glm::normalize(left + right);
|
||||
vec3 topLeft = vec3(left.x, 0.5f, left.z);
|
||||
vec3 topRight = vec3(right.x, 0.5f, right.z);
|
||||
vec3 topLeft = (isConical ? vec3(0.0f, 0.5f, 0.0f) : vec3(left.x, 0.5f, left.z));
|
||||
vec3 topRight = (isConical ? vec3(0.0f, 0.5f, 0.0f) : vec3(right.x, 0.5f, right.z));
|
||||
vec3 bottomLeft = vec3(left.x, -0.5f, left.z);
|
||||
vec3 bottomRight = vec3(right.x, -0.5f, right.z);
|
||||
|
||||
|
@ -325,6 +332,41 @@ void extrudePolygon(GeometryCache::ShapeData& shapeData, gpu::BufferPointer& ver
|
|||
shapeData.setupIndices(indexBuffer, solidIndices, wireIndices);
|
||||
}
|
||||
|
||||
void drawCircle(GeometryCache::ShapeData& shapeData, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer) {
|
||||
// Draw a circle with radius 1/4th the size of the bounding box
|
||||
using namespace geometry;
|
||||
|
||||
Index baseVertex = (Index)(vertexBuffer->getSize() / SHAPE_VERTEX_STRIDE);
|
||||
VertexVector vertices;
|
||||
IndexVector solidIndices, wireIndices;
|
||||
const int NUM_CIRCLE_VERTICES = 64;
|
||||
|
||||
std::vector<vec3> shape = polygon<NUM_CIRCLE_VERTICES>();
|
||||
for (const vec3& v : shape) {
|
||||
vertices.push_back(vec3(v.x, 0.0f, v.z));
|
||||
vertices.push_back(vec3(0.0f, 0.0f, 0.0f));
|
||||
}
|
||||
|
||||
for (uint32_t i = 2; i < NUM_CIRCLE_VERTICES; i++) {
|
||||
solidIndices.push_back(baseVertex + 0);
|
||||
solidIndices.push_back(baseVertex + i);
|
||||
solidIndices.push_back(baseVertex + i - 1);
|
||||
solidIndices.push_back(baseVertex + NUM_CIRCLE_VERTICES);
|
||||
solidIndices.push_back(baseVertex + i + NUM_CIRCLE_VERTICES - 1);
|
||||
solidIndices.push_back(baseVertex + i + NUM_CIRCLE_VERTICES);
|
||||
}
|
||||
|
||||
for (uint32_t i = 1; i <= NUM_CIRCLE_VERTICES; i++) {
|
||||
wireIndices.push_back(baseVertex + (i % NUM_CIRCLE_VERTICES));
|
||||
wireIndices.push_back(baseVertex + i - 1);
|
||||
wireIndices.push_back(baseVertex + (i % NUM_CIRCLE_VERTICES) + NUM_CIRCLE_VERTICES);
|
||||
wireIndices.push_back(baseVertex + (i - 1) + NUM_CIRCLE_VERTICES);
|
||||
}
|
||||
|
||||
shapeData.setupVertices(vertexBuffer, vertices);
|
||||
shapeData.setupIndices(indexBuffer, solidIndices, wireIndices);
|
||||
}
|
||||
|
||||
// FIXME solids need per-face vertices, but smooth shaded
|
||||
// components do not. Find a way to support using draw elements
|
||||
// or draw arrays as appropriate
|
||||
|
@ -357,8 +399,8 @@ void GeometryCache::buildShapes() {
|
|||
Index baseVertex = (Index)(_shapeVertices->getSize() / SHAPE_VERTEX_STRIDE);
|
||||
ShapeData& shapeData = _shapes[Line];
|
||||
shapeData.setupVertices(_shapeVertices, VertexVector {
|
||||
vec3(-0.5, 0, 0), vec3(-0.5f, 0, 0),
|
||||
vec3(0.5f, 0, 0), vec3(0.5f, 0, 0)
|
||||
vec3(-0.5f, 0.0f, 0.0f), vec3(-0.5f, 0.0f, 0.0f),
|
||||
vec3(0.5f, 0.0f, 0.0f), vec3(0.5f, 0.0f, 0.0f)
|
||||
});
|
||||
IndexVector wireIndices;
|
||||
// Only two indices
|
||||
|
@ -367,20 +409,22 @@ void GeometryCache::buildShapes() {
|
|||
shapeData.setupIndices(_shapeIndices, IndexVector(), wireIndices);
|
||||
}
|
||||
|
||||
// Not implememented yet:
|
||||
|
||||
//Triangle,
|
||||
extrudePolygon<3>(_shapes[Triangle], _shapeVertices, _shapeIndices);
|
||||
//Hexagon,
|
||||
extrudePolygon<6>(_shapes[Hexagon], _shapeVertices, _shapeIndices);
|
||||
//Octagon,
|
||||
extrudePolygon<8>(_shapes[Octagon], _shapeVertices, _shapeIndices);
|
||||
|
||||
//Quad,
|
||||
//Circle,
|
||||
//Torus,
|
||||
//Cone,
|
||||
//Cylinder,
|
||||
extrudePolygon<64>(_shapes[Cylinder], _shapeVertices, _shapeIndices);
|
||||
//Cone,
|
||||
extrudePolygon<64>(_shapes[Cone], _shapeVertices, _shapeIndices, true);
|
||||
//Circle
|
||||
drawCircle(_shapes[Circle], _shapeVertices, _shapeIndices);
|
||||
// Not implememented yet:
|
||||
//Quad,
|
||||
//Torus,
|
||||
|
||||
}
|
||||
|
||||
gpu::Stream::FormatPointer& getSolidStreamFormat() {
|
||||
|
@ -597,7 +641,7 @@ void GeometryCache::updateVertices(int id, const QVector<glm::vec2>& points, con
|
|||
auto pointCount = points.size();
|
||||
auto colorCount = colors.size();
|
||||
int compactColor = 0;
|
||||
for (auto i = 0; i < pointCount; ++i) {
|
||||
for (auto i = 0; i < pointCount; i++) {
|
||||
const auto& point = points[i];
|
||||
*(vertex++) = point.x;
|
||||
*(vertex++) = point.y;
|
||||
|
@ -674,7 +718,7 @@ void GeometryCache::updateVertices(int id, const QVector<glm::vec3>& points, con
|
|||
const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f);
|
||||
auto pointCount = points.size();
|
||||
auto colorCount = colors.size();
|
||||
for (auto i = 0; i < pointCount; ++i) {
|
||||
for (auto i = 0; i < pointCount; i++) {
|
||||
const glm::vec3& point = points[i];
|
||||
if (i < colorCount) {
|
||||
const glm::vec4& color = colors[i];
|
||||
|
|
|
@ -142,8 +142,8 @@ public:
|
|||
Dodecahedron,
|
||||
Icosahedron,
|
||||
Torus, // not yet implemented
|
||||
Cone, // not yet implemented
|
||||
Cylinder, // not yet implemented
|
||||
Cone,
|
||||
Cylinder,
|
||||
NUM_SHAPES,
|
||||
};
|
||||
|
||||
|
|
|
@ -152,9 +152,9 @@ public:
|
|||
int numInputLights { 0 };
|
||||
int numClusteredLights { 0 };
|
||||
|
||||
void setNumClusteredLightReferences(int numRefs) { numClusteredLightReferences = numRefs; emit dirty(); }
|
||||
void setNumInputLights(int numLights) { numInputLights = numLights; emit dirty(); }
|
||||
void setNumClusteredLights(int numLights) { numClusteredLights = numLights; emit dirty(); }
|
||||
void setNumClusteredLightReferences(int numRefs) { numClusteredLightReferences = numRefs; }
|
||||
void setNumInputLights(int numLights) { numInputLights = numLights; }
|
||||
void setNumClusteredLights(int numLights) { numClusteredLights = numLights; }
|
||||
|
||||
int numSceneLights { 0 };
|
||||
int numFreeSceneLights { 0 };
|
||||
|
|
|
@ -48,6 +48,7 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext,
|
|||
RenderArgs* args = renderContext->args;
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
args->_batch = &batch;
|
||||
batch.enableStereo(false);
|
||||
|
||||
glm::ivec4 viewport{0, 0, fbo->getWidth(), fbo->getHeight()};
|
||||
batch.setViewportTransform(viewport);
|
||||
|
@ -114,7 +115,7 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende
|
|||
skinProgram, state);
|
||||
}
|
||||
|
||||
const auto cachedMode = task.addJob<RenderShadowSetup>("Setup");
|
||||
const auto cachedMode = task.addJob<RenderShadowSetup>("ShadowSetup");
|
||||
|
||||
// CPU jobs:
|
||||
// Fetch and cull the items from the scene
|
||||
|
@ -129,7 +130,7 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende
|
|||
// GPU jobs: Render to shadow map
|
||||
task.addJob<RenderShadowMap>("RenderShadowMap", sortedShapes, shapePlumber);
|
||||
|
||||
task.addJob<RenderShadowTeardown>("Teardown", cachedMode);
|
||||
task.addJob<RenderShadowTeardown>("ShadowTeardown", cachedMode);
|
||||
}
|
||||
|
||||
void RenderShadowTask::configure(const Config& configuration) {
|
||||
|
|
33
libraries/render-utils/src/RenderViewTask.cpp
Normal file
33
libraries/render-utils/src/RenderViewTask.cpp
Normal file
|
@ -0,0 +1,33 @@
|
|||
//
|
||||
// RenderViewTask.cpp
|
||||
// render-utils/src/
|
||||
//
|
||||
// Created by Sam Gateau on 5/25/2017.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#include "RenderViewTask.h"
|
||||
|
||||
#include "RenderShadowTask.h"
|
||||
#include "RenderDeferredTask.h"
|
||||
#include "RenderForwardTask.h"
|
||||
|
||||
|
||||
|
||||
void RenderViewTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, bool isDeferred) {
|
||||
// auto items = input.get<Input>();
|
||||
|
||||
task.addJob<RenderShadowTask>("RenderShadowTask", cullFunctor);
|
||||
|
||||
const auto items = task.addJob<RenderFetchCullSortTask>("FetchCullSort", cullFunctor);
|
||||
assert(items.canCast<RenderFetchCullSortTask::Output>());
|
||||
|
||||
if (isDeferred) {
|
||||
task.addJob<RenderDeferredTask>("RenderDeferredTask", items);
|
||||
} else {
|
||||
task.addJob<RenderForwardTask>("Forward", items);
|
||||
}
|
||||
}
|
||||
|
31
libraries/render-utils/src/RenderViewTask.h
Normal file
31
libraries/render-utils/src/RenderViewTask.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
//
|
||||
// RenderViewTask.h
|
||||
// render-utils/src/
|
||||
//
|
||||
// Created by Sam Gateau on 5/25/2017.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#pragma once
|
||||
#ifndef hifi_RenderViewTask_h
|
||||
#define hifi_RenderViewTask_h
|
||||
|
||||
#include <render/Engine.h>
|
||||
#include <render/RenderFetchCullSortTask.h>
|
||||
|
||||
|
||||
class RenderViewTask {
|
||||
public:
|
||||
using Input = RenderFetchCullSortTask::Output;
|
||||
using JobModel = render::Task::ModelI<RenderViewTask, Input>;
|
||||
|
||||
RenderViewTask() {}
|
||||
|
||||
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred);
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // hifi_RenderViewTask_h
|
|
@ -14,6 +14,8 @@
|
|||
#include "../RenderUtilsLogging.h"
|
||||
#include "FontFamilies.h"
|
||||
|
||||
static std::mutex fontMutex;
|
||||
|
||||
struct TextureVertex {
|
||||
glm::vec2 pos;
|
||||
glm::vec2 tex;
|
||||
|
@ -56,6 +58,7 @@ Font::Pointer Font::load(QIODevice& fontFile) {
|
|||
}
|
||||
|
||||
Font::Pointer Font::load(const QString& family) {
|
||||
std::lock_guard<std::mutex> lock(fontMutex);
|
||||
if (!LOADED_FONTS.contains(family)) {
|
||||
|
||||
static const QString SDFF_COURIER_PRIME_FILENAME{ ":/CourierPrime.sdff" };
|
||||
|
|
|
@ -31,10 +31,10 @@ public:
|
|||
const glm::vec4* color, EffectType effectType,
|
||||
const glm::vec2& bound, bool layered = false);
|
||||
|
||||
static Pointer load(QIODevice& fontFile);
|
||||
static Pointer load(const QString& family);
|
||||
|
||||
private:
|
||||
static Pointer load(QIODevice& fontFile);
|
||||
QStringList tokenizeForWrapping(const QString& str) const;
|
||||
QStringList splitLines(const QString& str) const;
|
||||
glm::vec2 computeTokenExtent(const QString& str) const;
|
||||
|
|
|
@ -63,6 +63,4 @@ void EngineStats::run(const RenderContextPointer& renderContext) {
|
|||
|
||||
config->frameSetPipelineCount = _gpuStats._PSNumSetPipelines;
|
||||
config->frameSetInputFormatCount = _gpuStats._ISNumFormatChanges;
|
||||
|
||||
config->emitDirty();
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ void TaskConfig::connectChildConfig(QConfigPointer childConfig, const std::strin
|
|||
if (childConfig->metaObject()->indexOfSignal(DIRTY_SIGNAL) != -1) {
|
||||
// Connect dirty->refresh if defined
|
||||
QObject::connect(childConfig.get(), SIGNAL(dirty()), this, SLOT(refresh()));
|
||||
QObject::connect(childConfig.get(), SIGNAL(dirtyEnabled()), this, SLOT(refresh()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,6 +51,7 @@ void TaskConfig::transferChildrenConfigs(QConfigPointer source) {
|
|||
if (child->metaObject()->indexOfSignal(DIRTY_SIGNAL) != -1) {
|
||||
// Connect dirty->refresh if defined
|
||||
QObject::connect(child, SIGNAL(dirty()), this, SLOT(refresh()));
|
||||
QObject::connect(child, SIGNAL(dirtyEnabled()), this, SLOT(refresh()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,7 +89,7 @@ protected:
|
|||
class JobConfig : public QObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(double cpuRunTime READ getCPURunTime NOTIFY newStats()) //ms
|
||||
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
|
||||
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY dirtyEnabled())
|
||||
|
||||
double _msCPURunTime{ 0.0 };
|
||||
public:
|
||||
|
@ -99,7 +99,7 @@ public:
|
|||
JobConfig(bool enabled) : alwaysEnabled{ false }, enabled{ enabled } {}
|
||||
|
||||
bool isEnabled() { return alwaysEnabled || enabled; }
|
||||
void setEnabled(bool enable) { enabled = alwaysEnabled || enable; }
|
||||
void setEnabled(bool enable) { enabled = alwaysEnabled || enable; emit dirtyEnabled(); }
|
||||
|
||||
bool alwaysEnabled{ true };
|
||||
bool enabled{ true };
|
||||
|
@ -121,6 +121,7 @@ public slots:
|
|||
signals:
|
||||
void loaded();
|
||||
void newStats();
|
||||
void dirtyEnabled();
|
||||
};
|
||||
|
||||
class TaskConfig : public JobConfig {
|
||||
|
|
|
@ -170,6 +170,7 @@ protected:
|
|||
std::string _name = "";
|
||||
};
|
||||
|
||||
|
||||
// A task is a specialized job to run a collection of other jobs
|
||||
// It can be created on any type T by aliasing the type JobModel in the class T
|
||||
// using JobModel = Task::Model<T>
|
||||
|
|
|
@ -540,7 +540,7 @@ void TabletProxy::gotoWebScreen(const QString& url, const QString& injectedJavaS
|
|||
|
||||
QObject* TabletProxy::addButton(const QVariant& properties) {
|
||||
auto tabletButtonProxy = QSharedPointer<TabletButtonProxy>(new TabletButtonProxy(properties.toMap()));
|
||||
std::lock_guard<std::mutex> guard(_tabletMutex);
|
||||
std::unique_lock<std::mutex> guard(_tabletMutex);
|
||||
_tabletButtonProxies.push_back(tabletButtonProxy);
|
||||
if (!_toolbarMode && _qmlTabletRoot) {
|
||||
auto tablet = getQmlTablet();
|
||||
|
@ -550,7 +550,6 @@ QObject* TabletProxy::addButton(const QVariant& properties) {
|
|||
qCCritical(scriptengine) << "Could not find tablet in TabletRoot.qml";
|
||||
}
|
||||
} else if (_toolbarMode) {
|
||||
|
||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||
QObject* toolbarProxy = tabletScriptingInterface->getSystemToolbarProxy();
|
||||
|
||||
|
@ -559,6 +558,8 @@ QObject* TabletProxy::addButton(const QVariant& properties) {
|
|||
connectionType = Qt::BlockingQueuedConnection;
|
||||
}
|
||||
|
||||
guard.unlock();
|
||||
|
||||
// copy properties from tablet button proxy to toolbar button proxy.
|
||||
QObject* toolbarButtonProxy = nullptr;
|
||||
bool hasResult = QMetaObject::invokeMethod(toolbarProxy, "addButton", connectionType, Q_RETURN_ARG(QObject*, toolbarButtonProxy), Q_ARG(QVariant, tabletButtonProxy->getProperties()));
|
||||
|
@ -576,31 +577,38 @@ bool TabletProxy::onHomeScreen() {
|
|||
}
|
||||
|
||||
void TabletProxy::removeButton(QObject* tabletButtonProxy) {
|
||||
std::lock_guard<std::mutex> guard(_tabletMutex);
|
||||
std::unique_lock<std::mutex> guard(_tabletMutex);
|
||||
|
||||
auto tablet = getQmlTablet();
|
||||
if (!tablet) {
|
||||
qCCritical(scriptengine) << "Could not find tablet in TabletRoot.qml";
|
||||
}
|
||||
|
||||
auto iter = std::find(_tabletButtonProxies.begin(), _tabletButtonProxies.end(), tabletButtonProxy);
|
||||
if (iter != _tabletButtonProxies.end()) {
|
||||
if (!_toolbarMode && _qmlTabletRoot) {
|
||||
(*iter)->setQmlButton(nullptr);
|
||||
if (tablet) {
|
||||
QMetaObject::invokeMethod(tablet, "removeButtonProxy", Qt::AutoConnection, Q_ARG(QVariant, (*iter)->getProperties()));
|
||||
}
|
||||
} else if (_toolbarMode) {
|
||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||
QObject* toolbarProxy = tabletScriptingInterface->getSystemToolbarProxy();
|
||||
|
||||
// remove button from toolbarProxy
|
||||
QMetaObject::invokeMethod(toolbarProxy, "removeButton", Qt::AutoConnection, Q_ARG(QVariant, (*iter)->getUuid().toString()));
|
||||
(*iter)->setToolbarButtonProxy(nullptr);
|
||||
QSharedPointer<TabletButtonProxy> buttonProxy;
|
||||
{
|
||||
auto iter = std::find(_tabletButtonProxies.begin(), _tabletButtonProxies.end(), tabletButtonProxy);
|
||||
if (iter == _tabletButtonProxies.end()) {
|
||||
qCWarning(scriptengine) << "TabletProxy::removeButton() could not find button " << tabletButtonProxy;
|
||||
return;
|
||||
}
|
||||
buttonProxy = *iter;
|
||||
_tabletButtonProxies.erase(iter);
|
||||
} else {
|
||||
qCWarning(scriptengine) << "TabletProxy::removeButton() could not find button " << tabletButtonProxy;
|
||||
}
|
||||
|
||||
if (!_toolbarMode && _qmlTabletRoot) {
|
||||
buttonProxy->setQmlButton(nullptr);
|
||||
if (tablet) {
|
||||
guard.unlock();
|
||||
QMetaObject::invokeMethod(tablet, "removeButtonProxy", Qt::AutoConnection, Q_ARG(QVariant, buttonProxy->getProperties()));
|
||||
}
|
||||
} else if (_toolbarMode) {
|
||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||
QObject* toolbarProxy = tabletScriptingInterface->getSystemToolbarProxy();
|
||||
|
||||
// remove button from toolbarProxy
|
||||
guard.unlock();
|
||||
QMetaObject::invokeMethod(toolbarProxy, "removeButton", Qt::AutoConnection, Q_ARG(QVariant, buttonProxy->getUuid().toString()));
|
||||
buttonProxy->setToolbarButtonProxy(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -104,9 +104,9 @@ void QmlWindowClass::initQml(QVariantMap properties) {
|
|||
Q_ASSERT(invokeResult);
|
||||
} else {
|
||||
// Build the event bridge and wrapper on the main thread
|
||||
offscreenUi->load(qmlSource(), [&](QQmlContext* context, QObject* object) {
|
||||
offscreenUi->loadInNewContext(qmlSource(), [&](QQmlContext* context, QObject* object) {
|
||||
_qmlWindow = object;
|
||||
_qmlWindow->setProperty("eventBridge", QVariant::fromValue(this));
|
||||
context->setContextProperty("eventBridge", this);
|
||||
context->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership);
|
||||
context->engine()->setObjectOwnership(object, QQmlEngine::CppOwnership);
|
||||
if (properties.contains(TITLE_PROPERTY)) {
|
||||
|
|
|
@ -10,10 +10,10 @@ if (WIN32)
|
|||
# we're using static GLEW, so define GLEW_STATIC
|
||||
add_definitions(-DGLEW_STATIC)
|
||||
set(TARGET_NAME openvr)
|
||||
setup_hifi_plugin(OpenGL Script Qml Widgets)
|
||||
setup_hifi_plugin(OpenGL Script Qml Widgets Multimedia)
|
||||
link_hifi_libraries(shared gl networking controllers ui
|
||||
plugins display-plugins ui-plugins input-plugins script-engine
|
||||
render-utils model gpu gpu-gl render model-networking fbx ktx image procedural)
|
||||
audio-client render-utils model gpu gpu-gl render model-networking fbx ktx image procedural)
|
||||
|
||||
include_hifi_library_headers(octree)
|
||||
|
||||
|
@ -21,4 +21,5 @@ if (WIN32)
|
|||
find_package(OpenVR REQUIRED)
|
||||
target_include_directories(${TARGET_NAME} PRIVATE ${OPENVR_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARIES})
|
||||
target_link_libraries(${TARGET_NAME} Winmm.lib)
|
||||
endif()
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
//
|
||||
#include "OpenVrDisplayPlugin.h"
|
||||
|
||||
// Odd ordering of header is required to avoid 'macro redinition warnings'
|
||||
#include <AudioClient.h>
|
||||
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QLoggingCategory>
|
||||
#include <QtCore/QFileInfo>
|
||||
|
@ -713,3 +716,30 @@ bool OpenVrDisplayPlugin::isKeyboardVisible() {
|
|||
int OpenVrDisplayPlugin::getRequiredThreadCount() const {
|
||||
return Parent::getRequiredThreadCount() + (_threadedSubmit ? 1 : 0);
|
||||
}
|
||||
|
||||
QString OpenVrDisplayPlugin::getPreferredAudioInDevice() const {
|
||||
QString device = getVrSettingString(vr::k_pch_audio_Section, vr::k_pch_audio_OnPlaybackDevice_String);
|
||||
if (!device.isEmpty()) {
|
||||
static const WCHAR INIT = 0;
|
||||
size_t size = device.size() + 1;
|
||||
std::vector<WCHAR> deviceW;
|
||||
deviceW.assign(size, INIT);
|
||||
device.toWCharArray(deviceW.data());
|
||||
device = AudioClient::friendlyNameForAudioDevice(deviceW.data());
|
||||
}
|
||||
return device;
|
||||
}
|
||||
|
||||
QString OpenVrDisplayPlugin::getPreferredAudioOutDevice() const {
|
||||
QString device = getVrSettingString(vr::k_pch_audio_Section, vr::k_pch_audio_OnRecordDevice_String);
|
||||
if (!device.isEmpty()) {
|
||||
static const WCHAR INIT = 0;
|
||||
size_t size = device.size() + 1;
|
||||
std::vector<WCHAR> deviceW;
|
||||
deviceW.assign(size, INIT);
|
||||
device.toWCharArray(deviceW.data());
|
||||
device = AudioClient::friendlyNameForAudioDevice(deviceW.data());
|
||||
}
|
||||
return device;
|
||||
}
|
||||
|
||||
|
|
|
@ -58,6 +58,9 @@ public:
|
|||
// Possibly needs an additional thread for VR submission
|
||||
int getRequiredThreadCount() const override;
|
||||
|
||||
QString getPreferredAudioInDevice() const override;
|
||||
QString getPreferredAudioOutDevice() const override;
|
||||
|
||||
protected:
|
||||
bool internalActivate() override;
|
||||
void internalDeactivate() override;
|
||||
|
|
|
@ -72,6 +72,21 @@ bool openVrSupported() {
|
|||
return (enableDebugOpenVR || !isOculusPresent()) && vr::VR_IsHmdPresent();
|
||||
}
|
||||
|
||||
QString getVrSettingString(const char* section, const char* setting) {
|
||||
QString result;
|
||||
static const uint32_t BUFFER_SIZE = 1024;
|
||||
static char BUFFER[BUFFER_SIZE];
|
||||
vr::IVRSettings * vrSettings = vr::VRSettings();
|
||||
if (vrSettings) {
|
||||
vr::EVRSettingsError error = vr::VRSettingsError_None;
|
||||
vrSettings->GetString(vr::k_pch_audio_Section, vr::k_pch_audio_OnPlaybackDevice_String, BUFFER, BUFFER_SIZE, &error);
|
||||
if (error == vr::VRSettingsError_None) {
|
||||
result = BUFFER;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
vr::IVRSystem* acquireOpenVrSystem() {
|
||||
bool hmdPresent = vr::VR_IsHmdPresent();
|
||||
if (hmdPresent) {
|
||||
|
@ -82,6 +97,7 @@ vr::IVRSystem* acquireOpenVrSystem() {
|
|||
#endif
|
||||
vr::EVRInitError eError = vr::VRInitError_None;
|
||||
activeHmd = vr::VR_Init(&eError, vr::VRApplication_Scene);
|
||||
|
||||
#if DEV_BUILD
|
||||
qCDebug(displayplugins) << "OpenVR display: HMD is " << activeHmd << " error is " << eError;
|
||||
#endif
|
||||
|
|
|
@ -25,6 +25,7 @@ bool openVrQuitRequested();
|
|||
void enableOpenVrKeyboard(PluginContainer* container);
|
||||
void disableOpenVrKeyboard();
|
||||
bool isOpenVrKeyboardShown();
|
||||
QString getVrSettingString(const char* section, const char* setting);
|
||||
|
||||
|
||||
template<typename F>
|
||||
|
|
|
@ -123,15 +123,18 @@ bool ViveControllerManager::isSupported() const {
|
|||
bool ViveControllerManager::activate() {
|
||||
InputPlugin::activate();
|
||||
|
||||
_container->addMenu(MENU_PATH);
|
||||
_container->addMenuItem(PluginType::INPUT_PLUGIN, MENU_PATH, RENDER_CONTROLLERS,
|
||||
[this] (bool clicked) { this->setRenderControllers(clicked); },
|
||||
true, true);
|
||||
|
||||
if (!_system) {
|
||||
_system = acquireOpenVrSystem();
|
||||
}
|
||||
Q_ASSERT(_system);
|
||||
|
||||
if (!_system) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_container->addMenu(MENU_PATH);
|
||||
_container->addMenuItem(PluginType::INPUT_PLUGIN, MENU_PATH, RENDER_CONTROLLERS,
|
||||
[this](bool clicked) { this->setRenderControllers(clicked); },
|
||||
true, true);
|
||||
|
||||
enableOpenVrKeyboard(_container);
|
||||
|
||||
|
|
127
scripts/developer/tests/avatarToWorldTests.js
Normal file
127
scripts/developer/tests/avatarToWorldTests.js
Normal file
|
@ -0,0 +1,127 @@
|
|||
var AVATAR_SELF_ID = "{00000000-0000-0000-0000-000000000001}";
|
||||
|
||||
var debugSphereBaseProperties = {
|
||||
type: "Sphere",
|
||||
dimensions: { x: 0.2, y: 0.2, z: 0.2 },
|
||||
dynamic: false,
|
||||
collisionless: true,
|
||||
gravity: { x: 0, y: 0, z: 0 },
|
||||
lifetime: 10.0,
|
||||
userData: "{ \"grabbableKey\": { \"grabbable\": false, \"kinematic\": false } }"
|
||||
};
|
||||
|
||||
var debugBoxBaseProperties = {
|
||||
type: "Box",
|
||||
dimensions: { x: 0.2, y: 0.2, z: 0.2 },
|
||||
dynamic: false,
|
||||
collisionless: true,
|
||||
gravity: { x: 0, y: 0, z: 0 },
|
||||
lifetime: 10.0,
|
||||
userData: "{ \"grabbableKey\": { \"grabbable\": false, \"kinematic\": false } }"
|
||||
};
|
||||
|
||||
//jointToWorldPoint
|
||||
// create sphere for finger on left hand
|
||||
// each frame, calculate world position of finger, with some offset
|
||||
// update sphere to match this position
|
||||
var jointToWorldPointTest_sphereEntity;
|
||||
function jointToWorldPointTest() {
|
||||
var jointIndex = MyAvatar.getJointIndex("LeftHandPinky4");
|
||||
var jointOffset_WorldSpace = { x: 0.1, y: 0, z: 0 };
|
||||
var worldPos = MyAvatar.jointToWorldPoint(jointOffset_WorldSpace, jointIndex);
|
||||
|
||||
var jointSphereProps = Object.create(debugSphereBaseProperties);
|
||||
jointSphereProps.name = "jointToWorldPointTest_Sphere";
|
||||
jointSphereProps.color = { blue: 240, green: 150, red: 150 };
|
||||
jointSphereProps.position = worldPos;
|
||||
jointToWorldPointTest_sphereEntity = Entities.addEntity(jointSphereProps);
|
||||
}
|
||||
function jointToWorldPointTest_update(deltaTime) {
|
||||
var jointIndex = MyAvatar.getJointIndex("LeftHandPinky4");
|
||||
var jointOffset_WorldSpace = { x: 0.1, y: 0, z: 0 };
|
||||
var worldPos = MyAvatar.jointToWorldPoint(jointOffset_WorldSpace, jointIndex);
|
||||
var newProperties = { position: worldPos };
|
||||
Entities.editEntity(jointToWorldPointTest_sphereEntity, newProperties);
|
||||
}
|
||||
|
||||
//jointToWorldDirection
|
||||
// create line in world space
|
||||
// each frame calculate world space direction of players head z axis
|
||||
// update line to match
|
||||
var jointToWorldDirectionTest_lineEntity;
|
||||
function jointToWorldDirectionTest() {
|
||||
var jointIndex = MyAvatar.getJointIndex("Head");
|
||||
var avatarPos = MyAvatar.getJointPosition(jointIndex);
|
||||
|
||||
var jointDir = { x: 1, y: 0, z: 1 };
|
||||
var worldDir = MyAvatar.jointToWorldDirection(jointDir, jointIndex);
|
||||
print(worldDir.x);
|
||||
print(worldDir.y);
|
||||
print(worldDir.z);
|
||||
jointToWorldDirectionTest_lineEntity = Entities.addEntity({
|
||||
type: "Line",
|
||||
color: {red: 250, green: 0, blue: 0},
|
||||
dimensions: {x: 5, y: 5, z: 5},
|
||||
lifetime: 10.0,
|
||||
linePoints: [{
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
}, worldDir
|
||||
],
|
||||
position : avatarPos,
|
||||
});
|
||||
}
|
||||
function jointToWorldDirection_update(deltaTime) {
|
||||
var jointIndex = MyAvatar.getJointIndex("Head");
|
||||
var avatarPos = MyAvatar.getJointPosition(jointIndex);
|
||||
var jointDir = { x: 1, y: 0, z: 0 };
|
||||
var worldDir = MyAvatar.jointToWorldDirection(jointDir, jointIndex);
|
||||
var newProperties = {
|
||||
linePoints: [{
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
}, worldDir
|
||||
],
|
||||
position : avatarPos
|
||||
};
|
||||
|
||||
Entities.editEntity(jointToWorldDirectionTest_lineEntity, newProperties);
|
||||
}
|
||||
|
||||
//jointToWorldRotation
|
||||
// create box in world space
|
||||
// each frame calculate world space rotation of players head
|
||||
// update box rotation to match
|
||||
var jointToWorldRotationTest_boxEntity;
|
||||
function jointToWorldRotationTest() {
|
||||
var jointIndex = MyAvatar.getJointIndex("Head");
|
||||
var jointPosition_WorldSpace = MyAvatar.getJointPosition(jointIndex);
|
||||
var jointRot = MyAvatar.getJointRotation(jointIndex);
|
||||
var jointRot_WorldSpace = MyAvatar.jointToWorldRotation(jointRot, jointIndex);
|
||||
|
||||
var boxProps = Object.create(debugBoxBaseProperties);
|
||||
boxProps.name = "jointToWorldRotationTest_Box";
|
||||
boxProps.color = { blue: 250, green: 250, red: 250 };
|
||||
boxProps.position = jointPosition_WorldSpace;
|
||||
boxProps.rotation = jointRot_WorldSpace;
|
||||
jointToWorldRotationTest_boxEntity = Entities.addEntity(boxProps);
|
||||
}
|
||||
function jointToWorldRotationTest_update(deltaTime) {
|
||||
var jointIndex = MyAvatar.getJointIndex("Head");
|
||||
var jointPosition_WorldSpace = MyAvatar.getJointPosition(jointIndex);
|
||||
var jointRot = MyAvatar.getJointRotation(jointIndex);
|
||||
var jointRot_WorldSpace = MyAvatar.jointToWorldRotation(jointRot, jointIndex);
|
||||
var newProperties = { position: jointPosition_WorldSpace, rotation: jointRot_WorldSpace };
|
||||
Entities.editEntity(jointToWorldRotationTest_boxEntity, newProperties);
|
||||
}
|
||||
|
||||
jointToWorldPointTest();
|
||||
Script.update.connect(jointToWorldPointTest_update);
|
||||
|
||||
jointToWorldDirectionTest();
|
||||
Script.update.connect(jointToWorldDirection_update);
|
||||
|
||||
jointToWorldRotationTest();
|
||||
Script.update.connect(jointToWorldRotationTest_update);
|
134
scripts/developer/tests/worldToAvatarTests.js
Normal file
134
scripts/developer/tests/worldToAvatarTests.js
Normal file
|
@ -0,0 +1,134 @@
|
|||
var AVATAR_SELF_ID = "{00000000-0000-0000-0000-000000000001}";
|
||||
|
||||
var debugSphereBaseProperties = {
|
||||
type: "Sphere",
|
||||
dimensions: { x: 0.2, y: 0.2, z: 0.2 },
|
||||
dynamic: false,
|
||||
collisionless: true,
|
||||
gravity: { x: 0, y: 0, z: 0 },
|
||||
lifetime: 10.0,
|
||||
userData: "{ \"grabbableKey\": { \"grabbable\": false, \"kinematic\": false } }"
|
||||
};
|
||||
|
||||
var debugBoxBaseProperties = {
|
||||
type: "Box",
|
||||
dimensions: { x: 0.2, y: 0.2, z: 0.2 },
|
||||
dynamic: false,
|
||||
collisionless: true,
|
||||
gravity: { x: 0, y: 0, z: 0 },
|
||||
lifetime: 10.0,
|
||||
userData: "{ \"grabbableKey\": { \"grabbable\": false, \"kinematic\": false } }"
|
||||
};
|
||||
|
||||
//worldToJointPoint
|
||||
// calculate position offset from joint using getJointPosition
|
||||
// pass through worldToJointPoint to get offset in joint space of players joint
|
||||
// create a blue sphere and attach it to players joint using the joint space offset
|
||||
// The two spheres should appear in the same place, but the blue sphere will follow the avatar
|
||||
function worldToJointPointTest() {
|
||||
var jointIndex = MyAvatar.getJointIndex("LeftHandPinky4");
|
||||
var avatarPos = MyAvatar.position;
|
||||
|
||||
var jointPosition_WorldSpace = MyAvatar.getJointPosition(jointIndex);
|
||||
var jointOffset_WorldSpace = { x: 0.1, y: 0, z: 0 };
|
||||
var jointPosition_WorldSpaceOffset = Vec3.sum(jointPosition_WorldSpace, jointOffset_WorldSpace);
|
||||
var jointPosition_JointSpaceOffset = MyAvatar.worldToJointPoint(jointPosition_WorldSpaceOffset, jointIndex);
|
||||
|
||||
var jointSphereProps = Object.create(debugSphereBaseProperties);
|
||||
jointSphereProps.name = "worldToJointPointTest_Joint";
|
||||
jointSphereProps.color = { blue: 240, green: 150, red: 150 };
|
||||
jointSphereProps.localPosition = jointPosition_JointSpaceOffset;
|
||||
jointSphereProps.parentID = AVATAR_SELF_ID;
|
||||
jointSphereProps.parentJointIndex = jointIndex;
|
||||
Entities.addEntity(jointSphereProps);
|
||||
|
||||
var worldSphereProps = Object.create(debugSphereBaseProperties);
|
||||
worldSphereProps.name = "worldToJointPointTest_World";
|
||||
worldSphereProps.color = { blue: 150, green: 250, red: 150 };
|
||||
worldSphereProps.position = jointPosition_WorldSpaceOffset;
|
||||
Entities.addEntity(worldSphereProps);
|
||||
}
|
||||
|
||||
//worldToJointDirection
|
||||
// create line and attach to avatars head
|
||||
// each frame calculate direction of world x axis in joint space of players head
|
||||
// update arrow orientation to match
|
||||
var worldToJointDirectionTest_lineEntity;
|
||||
function worldToJointDirectionTest() {
|
||||
var jointIndex = MyAvatar.getJointIndex("Head");
|
||||
|
||||
var jointPosition_WorldSpace = MyAvatar.getJointPosition(jointIndex);
|
||||
var jointOffset_WorldSpace = { x: 0, y: 0, z: 0 };
|
||||
var jointPosition_WorldSpaceOffset = Vec3.sum(jointPosition_WorldSpace, jointOffset_WorldSpace);
|
||||
var jointPosition_JointSpaceOffset = MyAvatar.worldToJointPoint(jointPosition_WorldSpaceOffset, jointIndex);
|
||||
|
||||
var worldDir = { x: 1, y: 0, z: 0 };
|
||||
var avatarDir = MyAvatar.worldToJointDirection(worldDir, jointIndex);
|
||||
|
||||
worldToJointDirectionTest_lineEntity = Entities.addEntity({
|
||||
type: "Line",
|
||||
color: {red: 200, green: 250, blue: 0},
|
||||
dimensions: {x: 5, y: 5, z: 5},
|
||||
lifetime: 10.0,
|
||||
linePoints: [{
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
}, avatarDir
|
||||
],
|
||||
localPosition : jointOffset_WorldSpace,
|
||||
parentID : AVATAR_SELF_ID,
|
||||
parentJointIndex : jointIndex
|
||||
});
|
||||
}
|
||||
|
||||
function worldToJointDirectionTest_update(deltaTime) {
|
||||
var jointIndex = MyAvatar.getJointIndex("Head");
|
||||
var worldDir = { x: 1, y: 0, z: 0 };
|
||||
var avatarDir = MyAvatar.worldToJointDirection(worldDir, jointIndex);
|
||||
var newProperties = { linePoints: [{
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
}, avatarDir
|
||||
]};
|
||||
|
||||
Entities.editEntity(worldToJointDirectionTest_lineEntity, newProperties);
|
||||
}
|
||||
|
||||
//worldToJointRotation
|
||||
// create box and parent to some player joint
|
||||
// convert world identity rotation to joint space rotation
|
||||
// each frame, update box with new orientation
|
||||
var worldToJointRotationTest_boxEntity;
|
||||
function worldToJointRotationTest() {
|
||||
var jointIndex = MyAvatar.getJointIndex("RightHandPinky4");
|
||||
var avatarPos = MyAvatar.position;
|
||||
|
||||
var jointPosition_WorldSpace = MyAvatar.getJointPosition(jointIndex);
|
||||
var jointOffset_WorldSpace = { x: 0.0, y: 0, z: 0 };
|
||||
var jointPosition_WorldSpaceOffset = Vec3.sum(jointPosition_WorldSpace, jointOffset_WorldSpace);
|
||||
var jointPosition_JointSpaceOffset = MyAvatar.worldToJointPoint(jointPosition_WorldSpaceOffset, jointIndex);
|
||||
|
||||
var jointBoxProps = Object.create(debugBoxBaseProperties);
|
||||
jointBoxProps.name = "worldToJointRotationTest_Box";
|
||||
jointBoxProps.color = { blue: 0, green: 0, red: 250 };
|
||||
jointBoxProps.localPosition = jointPosition_JointSpaceOffset;
|
||||
jointBoxProps.parentID = AVATAR_SELF_ID;
|
||||
jointBoxProps.parentJointIndex = jointIndex;
|
||||
worldToJointRotationTest_boxEntity = Entities.addEntity(jointBoxProps);
|
||||
}
|
||||
function worldToJointRotationTest_update(deltaTime) {
|
||||
var jointIndex = MyAvatar.getJointIndex("RightHandPinky4");
|
||||
var worldRot = Quat.fromPitchYawRollDegrees(0,0,0);
|
||||
var avatarRot = MyAvatar.worldToJointRotation(worldRot, jointIndex);
|
||||
var newProperties = { localRotation: avatarRot };
|
||||
Entities.editEntity(worldToJointRotationTest_boxEntity, newProperties);
|
||||
}
|
||||
|
||||
worldToJointPointTest();
|
||||
worldToJointDirectionTest();
|
||||
worldToJointRotationTest();
|
||||
|
||||
Script.update.connect(worldToJointDirectionTest_update);
|
||||
Script.update.connect(worldToJointRotationTest_update);
|
|
@ -12,7 +12,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
/* global Script, SelectionDisplay, LightOverlayManager, CameraManager, Grid, GridTool, EntityListTool, Vec3, SelectionManager, Overlays, OverlayWebWindow, UserActivityLogger,
|
||||
/* global Script, SelectionDisplay, LightOverlayManager, CameraManager, Grid, GridTool, EntityListTool, Vec3, SelectionManager, Overlays, OverlayWebWindow, UserActivityLogger,
|
||||
Settings, Entities, Tablet, Toolbars, Messages, Menu, Camera, progressDialog, tooltip, MyAvatar, Quat, Controller, Clipboard, HMD, UndoStack, ParticleExplorerTool */
|
||||
|
||||
(function() { // BEGIN LOCAL_SCOPE
|
||||
|
@ -80,27 +80,7 @@ selectionManager.addEventListener(function () {
|
|||
}
|
||||
var type = Entities.getEntityProperties(selectedEntityID, "type").type;
|
||||
if (type === "ParticleEffect") {
|
||||
// Destroy the old particles web view first
|
||||
particleExplorerTool.destroyWebView();
|
||||
particleExplorerTool.createWebView();
|
||||
var properties = Entities.getEntityProperties(selectedEntityID);
|
||||
var particleData = {
|
||||
messageType: "particle_settings",
|
||||
currentProperties: properties
|
||||
};
|
||||
selectedParticleEntityID = selectedEntityID;
|
||||
particleExplorerTool.setActiveParticleEntity(selectedParticleEntityID);
|
||||
|
||||
particleExplorerTool.webView.webEventReceived.connect(function (data) {
|
||||
data = JSON.parse(data);
|
||||
if (data.messageType === "page_loaded") {
|
||||
particleExplorerTool.webView.emitScriptEvent(JSON.stringify(particleData));
|
||||
}
|
||||
});
|
||||
|
||||
// Switch to particle explorer
|
||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
tablet.sendToQml({method: 'selectTab', params: {id: 'particle'}});
|
||||
selectParticleEntity(selectedEntityID);
|
||||
} else {
|
||||
needToDestroyParticleExplorer = true;
|
||||
}
|
||||
|
@ -218,7 +198,7 @@ function hideMarketplace() {
|
|||
// }
|
||||
|
||||
function adjustPositionPerBoundingBox(position, direction, registration, dimensions, orientation) {
|
||||
// Adjust the position such that the bounding box (registration, dimenions, and orientation) lies behind the original
|
||||
// Adjust the position such that the bounding box (registration, dimenions, and orientation) lies behind the original
|
||||
// position in the given direction.
|
||||
var CORNERS = [
|
||||
{ x: 0, y: 0, z: 0 },
|
||||
|
@ -618,7 +598,6 @@ var toolBar = (function () {
|
|||
|
||||
that.toggle = function () {
|
||||
that.setActive(!isActive);
|
||||
activeButton.editProperties({isActive: isActive});
|
||||
if (!isActive) {
|
||||
tablet.gotoHomeScreen();
|
||||
}
|
||||
|
@ -642,6 +621,8 @@ var toolBar = (function () {
|
|||
enabled: active
|
||||
}));
|
||||
isActive = active;
|
||||
activeButton.editProperties({isActive: isActive});
|
||||
|
||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
|
||||
if (!isActive) {
|
||||
|
@ -1372,7 +1353,7 @@ function parentSelectedEntities() {
|
|||
}
|
||||
});
|
||||
|
||||
if(parentCheck) {
|
||||
if (parentCheck) {
|
||||
Window.notify("Entities parented");
|
||||
}else {
|
||||
Window.notify("Entities are already parented to last");
|
||||
|
@ -1574,7 +1555,7 @@ function importSVO(importURL) {
|
|||
var properties = Entities.getEntityProperties(pastedEntityIDs[0], ["position", "dimensions",
|
||||
"registrationPoint"]);
|
||||
var position = Vec3.sum(deltaPosition, properties.position);
|
||||
position = grid.snapToSurface(grid.snapToGrid(position, false, properties.dimensions,
|
||||
position = grid.snapToSurface(grid.snapToGrid(position, false, properties.dimensions,
|
||||
properties.registrationPoint), properties.dimensions, properties.registrationPoint);
|
||||
deltaPosition = Vec3.subtract(position, properties.position);
|
||||
}
|
||||
|
@ -1906,11 +1887,11 @@ var PropertiesTool = function (opts) {
|
|||
}
|
||||
pushCommandForSelections();
|
||||
selectionManager._update();
|
||||
} else if(data.type === 'parent') {
|
||||
} else if (data.type === 'parent') {
|
||||
parentSelectedEntities();
|
||||
} else if(data.type === 'unparent') {
|
||||
} else if (data.type === 'unparent') {
|
||||
unparentSelectedEntities();
|
||||
} else if(data.type === 'saveUserData'){
|
||||
} else if (data.type === 'saveUserData'){
|
||||
//the event bridge and json parsing handle our avatar id string differently.
|
||||
var actualID = data.id.split('"')[1];
|
||||
Entities.editEntity(actualID, data.properties);
|
||||
|
@ -2202,6 +2183,10 @@ var selectedParticleEntityID = null;
|
|||
|
||||
function selectParticleEntity(entityID) {
|
||||
var properties = Entities.getEntityProperties(entityID);
|
||||
|
||||
if (properties.emitOrientation) {
|
||||
properties.emitOrientation = Quat.safeEulerAngles(properties.emitOrientation);
|
||||
}
|
||||
var particleData = {
|
||||
messageType: "particle_settings",
|
||||
currentProperties: properties
|
||||
|
@ -2211,6 +2196,7 @@ function selectParticleEntity(entityID) {
|
|||
|
||||
selectedParticleEntity = entityID;
|
||||
particleExplorerTool.setActiveParticleEntity(entityID);
|
||||
|
||||
particleExplorerTool.webView.emitScriptEvent(JSON.stringify(particleData));
|
||||
|
||||
// Switch to particle explorer
|
||||
|
@ -2228,7 +2214,7 @@ entityListTool.webView.webEventReceived.connect(function (data) {
|
|||
|
||||
if (data.type === 'parent') {
|
||||
parentSelectedEntities();
|
||||
} else if(data.type === 'unparent') {
|
||||
} else if (data.type === 'unparent') {
|
||||
unparentSelectedEntities();
|
||||
} else if (data.type === "selectionUpdate") {
|
||||
var ids = data.entityIds;
|
||||
|
@ -2249,4 +2235,3 @@ entityListTool.webView.webEventReceived.connect(function (data) {
|
|||
});
|
||||
|
||||
}()); // END LOCAL_SCOPE
|
||||
|
||||
|
|
|
@ -610,6 +610,7 @@ hr {
|
|||
.dropdown dl[dropped="true"] {
|
||||
color: #404040;
|
||||
background: linear-gradient(#afafaf, #afafaf);
|
||||
z-index: 998;
|
||||
}
|
||||
|
||||
.dropdown dt {
|
||||
|
@ -657,7 +658,8 @@ hr {
|
|||
font-family: FiraSans-SemiBold;
|
||||
font-size: 15px;
|
||||
color: #404040;
|
||||
background-color: #afafaf
|
||||
background-color: #afafaf;
|
||||
z-index: 999;
|
||||
}
|
||||
.dropdown li:hover {
|
||||
background-color: #00b4ef;
|
||||
|
|
|
@ -172,4 +172,4 @@ input[type=radio]:active + label > span > span{
|
|||
}
|
||||
.blueButton:disabled {
|
||||
background-image: linear-gradient(#FFFFFF, #AFAFAF);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -503,7 +503,7 @@ div.jsoneditor-contextmenu-root {
|
|||
div.jsoneditor-contextmenu {
|
||||
position: absolute;
|
||||
box-sizing: content-box;
|
||||
z-index: 99999;
|
||||
z-index: 998;
|
||||
}
|
||||
|
||||
div.jsoneditor-contextmenu ul,
|
||||
|
|
|
@ -51,6 +51,9 @@
|
|||
<option value="Hexagon">Hexagon</option>
|
||||
<option value="Triangle">Triangle</option>
|
||||
<option value="Octagon">Octagon</option>
|
||||
<option value="Cylinder">Cylinder</option>
|
||||
<option value="Cone">Cone</option>
|
||||
<option value="Circle">Circle</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="property text">
|
||||
|
|
|
@ -13,7 +13,7 @@ var WebChannel;
|
|||
|
||||
openEventBridge = function(callback) {
|
||||
WebChannel = new QWebChannel(qt.webChannelTransport, function (channel) {
|
||||
EventBridge = WebChannel.objects.eventBridgeWrapper.eventBridge;
|
||||
EventBridge = WebChannel.objects.eventBridge;
|
||||
callback(EventBridge);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ var LOCAL_TABLET_MODEL_PATH = Script.resourcesPath() + "meshes/tablet-with-home-
|
|||
// returns object with two fields:
|
||||
// * position - position in front of the user
|
||||
// * rotation - rotation of entity so it faces the user.
|
||||
function calcSpawnInfo(hand, height) {
|
||||
function calcSpawnInfo(hand, tabletHeight) {
|
||||
var finalPosition;
|
||||
|
||||
var headPos = (HMD.active && Camera.mode === "first person") ? HMD.position : Camera.position;
|
||||
|
@ -53,30 +53,35 @@ function calcSpawnInfo(hand, height) {
|
|||
hand = NO_HANDS;
|
||||
}
|
||||
|
||||
var handController = null;
|
||||
if (HMD.active && hand !== NO_HANDS) {
|
||||
var handController = getControllerWorldLocation(hand, true);
|
||||
handController = getControllerWorldLocation(hand, true);
|
||||
}
|
||||
|
||||
var TABLET_UP_OFFSET = 0.1;
|
||||
var TABLET_FORWARD_OFFSET = 0.1;
|
||||
var normal = Vec3.multiplyQbyV(handController.rotation, {x: 0, y: -1, z: 0});
|
||||
var pitch = Math.asin(normal.y);
|
||||
var MAX_PITCH = Math.PI / 4;
|
||||
if (pitch < -MAX_PITCH) {
|
||||
pitch = -MAX_PITCH;
|
||||
} else if (pitch > MAX_PITCH) {
|
||||
pitch = MAX_PITCH;
|
||||
if (handController && handController.valid) {
|
||||
// Orient tablet per hand pitch and yaw.
|
||||
// Angle it back similar to holding it like a book.
|
||||
// Move tablet up so that hand is at bottom.
|
||||
// Move tablet back so that hand is in front.
|
||||
|
||||
var position = handController.position;
|
||||
var rotation = handController.rotation;
|
||||
|
||||
if (hand === Controller.Standard.LeftHand) {
|
||||
rotation = Quat.multiply(rotation, Quat.fromPitchYawRollDegrees(0, 90, 0));
|
||||
} else {
|
||||
rotation = Quat.multiply(rotation, Quat.fromPitchYawRollDegrees(0, -90, 0));
|
||||
}
|
||||
var normal = Vec3.multiplyQbyV(rotation, Vec3.UNIT_NEG_Y);
|
||||
var lookAt = Quat.lookAt(Vec3.ZERO, normal, Vec3.multiplyQbyV(MyAvatar.orientation, Vec3.UNIT_Y));
|
||||
var TABLET_RAKE_ANGLE = 30;
|
||||
rotation = Quat.multiply(Quat.angleAxis(TABLET_RAKE_ANGLE, Vec3.multiplyQbyV(lookAt, Vec3.UNIT_X)), lookAt);
|
||||
|
||||
// rebuild normal from pitch and heading.
|
||||
var heading = Math.atan2(normal.z, normal.x);
|
||||
normal = {x: Math.cos(heading), y: Math.sin(pitch), z: Math.sin(heading)};
|
||||
|
||||
var position = Vec3.sum(handController.position, {x: 0, y: TABLET_UP_OFFSET, z: 0});
|
||||
var rotation = Quat.lookAt({x: 0, y: 0, z: 0}, normal, Y_AXIS);
|
||||
var offset = Vec3.multiplyQbyV(rotation, {x: 0, y: height / 2, z: TABLET_FORWARD_OFFSET});
|
||||
var RELATIVE_SPAWN_OFFSET = { x: 0, y: 0.4, z: 0.05 };
|
||||
position = Vec3.sum(position, Vec3.multiplyQbyV(rotation, Vec3.multiply(tabletHeight, RELATIVE_SPAWN_OFFSET)));
|
||||
|
||||
return {
|
||||
position: Vec3.sum(offset, position),
|
||||
position: position,
|
||||
rotation: rotation
|
||||
};
|
||||
} else {
|
||||
|
|
95
scripts/system/particle_explorer/dat.gui.min.js
vendored
95
scripts/system/particle_explorer/dat.gui.min.js
vendored
File diff suppressed because one or more lines are too long
680
scripts/system/particle_explorer/hifi-entity-ui.js
Normal file
680
scripts/system/particle_explorer/hifi-entity-ui.js
Normal file
|
@ -0,0 +1,680 @@
|
|||
/* global window, document, print, alert, console,setTimeout, clearTimeout, _ $ */
|
||||
/* eslint no-console: 0 */
|
||||
|
||||
/**
|
||||
UI Builder V1.0
|
||||
|
||||
Created by Matti 'Menithal' Lahtinen
|
||||
24/5/2017
|
||||
Copyright 2017 High Fidelity, Inc.
|
||||
|
||||
This can eventually be expanded to all of Edit, for now, starting
|
||||
with Particles Only.
|
||||
|
||||
This is created for the sole purpose of streamliming the bridge, and to simplify
|
||||
the logic between an inputfield in WebView and Entities in High Fidelity.
|
||||
|
||||
We also do not need anything as heavy as jquery or any other platform,
|
||||
as we are mostly only building for QT (while, all the other JS frameworks usually do alot of polyfilling)
|
||||
|
||||
Available Types:
|
||||
|
||||
JSONInputField - Accepts JSON input, once one presses Save, it will be propegated.
|
||||
Button- A Button that listens for a custom event as defined by callback
|
||||
Boolean - Creates a checkbox that the user can either check or uncheck
|
||||
SliderFloat - Creates a slider (with input) that has Float values from min to max.
|
||||
Default is min 0, max 1
|
||||
SliderInteger - Creates a slider (with input) that has a Integer value from min to max.
|
||||
Default is min 1, max 10000
|
||||
SliderRadian - Creates a slider (with input) that has Float values in degrees,
|
||||
that are converted to radians. default is min 0, max Math.PI.
|
||||
Texture - Creates a Image with an url input field that points to texture.
|
||||
If image cannot form, show "cannot find image"
|
||||
VecQuaternion - Creates a 3D Vector field that converts to quaternions.
|
||||
Checkbox exists to show quaternions instead.
|
||||
Color - Create field color button, that when pressed, opens the color picker.
|
||||
Vector - Create a 3D Vector field that has one to one correspondence.
|
||||
|
||||
The script will use this structure to build a UI that is connected The
|
||||
id fields within High Fidelity
|
||||
|
||||
This should make editing, and everything related much more simpler to maintain,
|
||||
and If there is any changes to either the Entities or properties of
|
||||
|
||||
**/
|
||||
|
||||
var RADIANS_PER_DEGREE = Math.PI / 180;
|
||||
|
||||
var roundFloat = function (input, round) {
|
||||
round = round ? round : 1000;
|
||||
var sanitizedInput;
|
||||
if (typeof input === "string") {
|
||||
sanitizedInput = parseFloat(input);
|
||||
} else {
|
||||
sanitizedInput = input;
|
||||
}
|
||||
return Math.round(sanitizedInput * round) / round;
|
||||
};
|
||||
|
||||
function HifiEntityUI(parent) {
|
||||
this.parent = parent;
|
||||
|
||||
var self = this;
|
||||
this.webBridgeSync = _.debounce(function (id, val) {
|
||||
if (self.EventBridge) {
|
||||
var sendPackage = {};
|
||||
sendPackage[id] = val;
|
||||
self.submitChanges(sendPackage);
|
||||
}
|
||||
}, 125);
|
||||
}
|
||||
|
||||
HifiEntityUI.prototype = {
|
||||
setOnSelect: function (callback) {
|
||||
this.onSelect = callback;
|
||||
},
|
||||
submitChanges: function (structure) {
|
||||
var message = {
|
||||
messageType: "settings_update",
|
||||
updatedSettings: structure
|
||||
};
|
||||
this.EventBridge.emitWebEvent(JSON.stringify(message));
|
||||
},
|
||||
setUI: function (structure) {
|
||||
this.structure = structure;
|
||||
},
|
||||
disableFields: function () {
|
||||
var fields = document.getElementsByTagName("input");
|
||||
for (var i = 0; i < fields.length; i++) {
|
||||
if (fields[i].getAttribute("type") !== "button") {
|
||||
fields[i].value = "";
|
||||
}
|
||||
|
||||
fields[i].setAttribute("disabled", true);
|
||||
}
|
||||
var textures = document.getElementsByTagName("img");
|
||||
for (i = 0; i < textures.length; i++) {
|
||||
textures[i].src = "";
|
||||
}
|
||||
|
||||
textures = document.getElementsByClassName("with-texture");
|
||||
for (i = 0; i < textures.length; i++) {
|
||||
textures[i].classList.remove("with-textures");
|
||||
textures[i].classList.add("no-texture");
|
||||
}
|
||||
|
||||
var textareas = document.getElementsByTagName("textarea");
|
||||
for (var x = 0; x < textareas.length; x++) {
|
||||
textareas[x].remove();
|
||||
}
|
||||
},
|
||||
getSettings: function () {
|
||||
var self = this;
|
||||
var json = {};
|
||||
var keys = Object.keys(self.builtRows);
|
||||
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
var key = keys[i];
|
||||
var el = self.builtRows[key];
|
||||
if (el.className.indexOf("checkbox") !== -1) {
|
||||
json[key] = document.getElementById(key)
|
||||
.checked ? true : false;
|
||||
} else if (el.className.indexOf("vector-section") !== -1) {
|
||||
var vector = {};
|
||||
if (el.className.indexOf("rgb") !== -1) {
|
||||
var red = document.getElementById(key + "-red");
|
||||
var blue = document.getElementById(key + "-blue");
|
||||
var green = document.getElementById(key + "-green");
|
||||
vector.red = red.value;
|
||||
vector.blue = blue.value;
|
||||
vector.green = green.value;
|
||||
} else if (el.className.indexOf("pyr") !== -1) {
|
||||
var p = document.getElementById(key + "-Pitch");
|
||||
var y = document.getElementById(key + "-Yaw");
|
||||
var r = document.getElementById(key + "-Roll");
|
||||
vector.x = p.value;
|
||||
vector.y = y.value;
|
||||
vector.z = r.value;
|
||||
} else {
|
||||
var x = document.getElementById(key + "-x");
|
||||
var ey = document.getElementById(key + "-y");
|
||||
var z = document.getElementById(key + "-z");
|
||||
vector.x = x.value;
|
||||
vector.y = ey.value;
|
||||
vector.z = z.value;
|
||||
}
|
||||
json[key] = vector;
|
||||
} else if (el.className.length > 0) {
|
||||
json[key] = document.getElementById(key)
|
||||
.value;
|
||||
}
|
||||
}
|
||||
|
||||
return json;
|
||||
},
|
||||
fillFields: function (currentProperties) {
|
||||
var self = this;
|
||||
var fields = document.getElementsByTagName("input");
|
||||
|
||||
if (!currentProperties.locked) {
|
||||
for (var i = 0; i < fields.length; i++) {
|
||||
fields[i].removeAttribute("disabled");
|
||||
}
|
||||
}
|
||||
if (self.onSelect) {
|
||||
self.onSelect();
|
||||
}
|
||||
var keys = Object.keys(currentProperties);
|
||||
|
||||
|
||||
for (var e in keys) {
|
||||
if (keys.hasOwnProperty(e)) {
|
||||
var value = keys[e];
|
||||
|
||||
var property = currentProperties[value];
|
||||
var field = self.builtRows[value];
|
||||
if (field) {
|
||||
var el = document.getElementById(value);
|
||||
|
||||
if (field.className.indexOf("radian") !== -1) {
|
||||
el.value = property / RADIANS_PER_DEGREE;
|
||||
el.onchange({
|
||||
target: el
|
||||
});
|
||||
} else if (field.className.indexOf("range") !== -1 || field.className.indexOf("texture") !== -1) {
|
||||
el.value = property;
|
||||
el.onchange({
|
||||
target: el
|
||||
});
|
||||
} else if (field.className.indexOf("checkbox") !== -1) {
|
||||
if (property) {
|
||||
el.setAttribute("checked", property);
|
||||
} else {
|
||||
el.removeAttribute("checked");
|
||||
}
|
||||
} else if (field.className.indexOf("vector-section") !== -1) {
|
||||
if (field.className.indexOf("rgb") !== -1) {
|
||||
var red = document.getElementById(value + "-red");
|
||||
var blue = document.getElementById(value + "-blue");
|
||||
var green = document.getElementById(value + "-green");
|
||||
red.value = parseInt(property.red);
|
||||
blue.value = parseInt(property.blue);
|
||||
green.value = parseInt(property.green);
|
||||
|
||||
red.oninput({
|
||||
target: red
|
||||
});
|
||||
} else if (field.className.indexOf("xyz") !== -1) {
|
||||
var x = document.getElementById(value + "-x");
|
||||
var y = document.getElementById(value + "-y");
|
||||
var z = document.getElementById(value + "-z");
|
||||
|
||||
x.value = roundFloat(property.x, 100);
|
||||
y.value = roundFloat(property.y, 100);
|
||||
z.value = roundFloat(property.z, 100);
|
||||
} else if (field.className.indexOf("pyr") !== -1) {
|
||||
var pitch = document.getElementById(value + "-Pitch");
|
||||
var yaw = document.getElementById(value + "-Yaw");
|
||||
var roll = document.getElementById(value + "-Roll");
|
||||
|
||||
pitch.value = roundFloat(property.x, 100);
|
||||
yaw.value = roundFloat(property.y, 100);
|
||||
roll.value = roundFloat(property.z, 100);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
connect: function (EventBridge) {
|
||||
this.EventBridge = EventBridge;
|
||||
|
||||
var self = this;
|
||||
|
||||
EventBridge.emitWebEvent(JSON.stringify({
|
||||
messageType: 'page_loaded'
|
||||
}));
|
||||
|
||||
EventBridge.scriptEventReceived.connect(function (data) {
|
||||
data = JSON.parse(data);
|
||||
|
||||
if (data.messageType === 'particle_settings') {
|
||||
// Update settings
|
||||
var currentProperties = data.currentProperties;
|
||||
self.fillFields(currentProperties);
|
||||
// Do expected property match with structure;
|
||||
} else if (data.messageType === 'particle_close') {
|
||||
self.disableFields();
|
||||
}
|
||||
});
|
||||
},
|
||||
build: function () {
|
||||
var self = this;
|
||||
var sections = Object.keys(this.structure);
|
||||
this.builtRows = {};
|
||||
sections.forEach(function (section, index) {
|
||||
var properties = self.structure[section];
|
||||
self.addSection(self.parent, section, properties, index);
|
||||
});
|
||||
},
|
||||
addSection: function (parent, section, properties, index) {
|
||||
var self = this;
|
||||
|
||||
var sectionDivHeader = document.createElement("div");
|
||||
var title = document.createElement("label");
|
||||
var dropDown = document.createElement("span");
|
||||
|
||||
dropDown.className = "arrow";
|
||||
sectionDivHeader.className = "section-header";
|
||||
title.innerHTML = section;
|
||||
sectionDivHeader.appendChild(title);
|
||||
sectionDivHeader.appendChild(dropDown);
|
||||
var collapsed = index !== 0;
|
||||
|
||||
dropDown.innerHTML = collapsed ? "L" : "M";
|
||||
sectionDivHeader.setAttribute("collapsed", collapsed);
|
||||
parent.appendChild(sectionDivHeader);
|
||||
|
||||
var sectionDivBody = document.createElement("div");
|
||||
sectionDivBody.className = "property-group";
|
||||
|
||||
var animationWrapper = document.createElement("div");
|
||||
animationWrapper.className = "section-wrap";
|
||||
|
||||
for (var property in properties) {
|
||||
if (properties.hasOwnProperty(property)) {
|
||||
var builtRow = self.addElement(animationWrapper, properties[property]);
|
||||
var id = properties[property].id;
|
||||
if (id) {
|
||||
self.builtRows[id] = builtRow;
|
||||
}
|
||||
}
|
||||
}
|
||||
sectionDivBody.appendChild(animationWrapper);
|
||||
parent.appendChild(sectionDivBody);
|
||||
_.defer(function () {
|
||||
var height = (animationWrapper.clientHeight) + "px";
|
||||
if (collapsed) {
|
||||
sectionDivBody.classList.remove("visible");
|
||||
sectionDivBody.style.maxHeight = "0px";
|
||||
} else {
|
||||
sectionDivBody.classList.add("visible");
|
||||
sectionDivBody.style.maxHeight = height;
|
||||
}
|
||||
|
||||
sectionDivHeader.onclick = function () {
|
||||
collapsed = !collapsed;
|
||||
if (collapsed) {
|
||||
sectionDivBody.classList.remove("visible");
|
||||
sectionDivBody.style.maxHeight = "0px";
|
||||
} else {
|
||||
sectionDivBody.classList.add("visible");
|
||||
sectionDivBody.style.maxHeight = (animationWrapper.clientHeight) + "px";
|
||||
}
|
||||
// sectionDivBody.style.display = collapsed ? "none": "block";
|
||||
dropDown.innerHTML = collapsed ? "L" : "M";
|
||||
sectionDivHeader.setAttribute("collapsed", collapsed);
|
||||
};
|
||||
});
|
||||
},
|
||||
addLabel: function (parent, group) {
|
||||
var label = document.createElement("label");
|
||||
label.innerHTML = group.name;
|
||||
parent.appendChild(label);
|
||||
if (group.unit) {
|
||||
var span = document.createElement("span");
|
||||
span.innerHTML = group.unit;
|
||||
span.className = "unit";
|
||||
label.appendChild(span);
|
||||
}
|
||||
return label;
|
||||
},
|
||||
addVector: function (parent, group, labels, domArray) {
|
||||
var self = this;
|
||||
var inputs = labels ? labels : ["x", "y", "z"];
|
||||
domArray = domArray ? domArray : [];
|
||||
parent.id = group.id;
|
||||
for (var index in inputs) {
|
||||
var element = document.createElement("input");
|
||||
|
||||
element.setAttribute("type", "number");
|
||||
element.className = inputs[index];
|
||||
element.id = group.id + "-" + inputs[index];
|
||||
|
||||
if (group.defaultRange) {
|
||||
if (group.defaultRange.min) {
|
||||
element.setAttribute("min", group.defaultRange.min);
|
||||
}
|
||||
if (group.defaultRange.max) {
|
||||
element.setAttribute("max", group.defaultRange.max);
|
||||
}
|
||||
if (group.defaultRange.step) {
|
||||
element.setAttribute("step", group.defaultRange.step);
|
||||
}
|
||||
}
|
||||
if (group.oninput) {
|
||||
element.oninput = group.oninput;
|
||||
} else {
|
||||
element.oninput = function (event) {
|
||||
self.webBridgeSync(group.id, {
|
||||
x: domArray[0].value,
|
||||
y: domArray[1].value,
|
||||
z: domArray[2].value
|
||||
});
|
||||
};
|
||||
}
|
||||
element.onchange = element.oninput;
|
||||
domArray.push(element);
|
||||
}
|
||||
|
||||
this.addLabel(parent, group);
|
||||
var className = "";
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
className += inputs[i].charAt(0)
|
||||
.toLowerCase();
|
||||
}
|
||||
parent.className += " property vector-section " + className;
|
||||
|
||||
// Add Tuple and the rest
|
||||
var tupleContainer = document.createElement("div");
|
||||
tupleContainer.className = "tuple";
|
||||
for (var domIndex in domArray) {
|
||||
var container = domArray[domIndex];
|
||||
var div = document.createElement("div");
|
||||
var label = document.createElement("label");
|
||||
label.innerHTML = inputs[domIndex] + ":";
|
||||
label.setAttribute("for", container.id);
|
||||
div.appendChild(container);
|
||||
div.appendChild(label);
|
||||
tupleContainer.appendChild(div);
|
||||
}
|
||||
parent.appendChild(tupleContainer);
|
||||
},
|
||||
addVectorQuaternion: function (parent, group) {
|
||||
this.addVector(parent, group, ["Pitch", "Yaw", "Roll"]);
|
||||
},
|
||||
addColorPicker: function (parent, group) {
|
||||
var self = this;
|
||||
var $colPickContainer = $('<div>', {
|
||||
id: group.id,
|
||||
class: "color-picker"
|
||||
});
|
||||
var updateColors = function (red, green, blue) {
|
||||
$colPickContainer.css('background-color', "rgb(" +
|
||||
red + "," +
|
||||
green + "," +
|
||||
blue + ")");
|
||||
};
|
||||
|
||||
var inputs = ["red", "green", "blue"];
|
||||
var domArray = [];
|
||||
group.oninput = function (event) {
|
||||
$colPickContainer.colpickSetColor(
|
||||
{
|
||||
r: domArray[0].value,
|
||||
g: domArray[1].value,
|
||||
b: domArray[2].value
|
||||
},
|
||||
true);
|
||||
};
|
||||
group.defaultRange = {
|
||||
min: 0,
|
||||
max: 255,
|
||||
step: 1
|
||||
};
|
||||
|
||||
parent.appendChild($colPickContainer[0]);
|
||||
self.addVector(parent, group, inputs, domArray);
|
||||
|
||||
updateColors(domArray[0].value, domArray[1].value, domArray[2].value);
|
||||
|
||||
// Could probably write a custom one for this to completely write out jquery,
|
||||
// but for now, using the same as earlier.
|
||||
|
||||
/* Color Picker Logic Here */
|
||||
|
||||
|
||||
$colPickContainer.colpick({
|
||||
colorScheme: 'dark',
|
||||
layout: 'hex',
|
||||
color: {
|
||||
r: domArray[0].value,
|
||||
g: domArray[1].value,
|
||||
b: domArray[2].value
|
||||
},
|
||||
onChange: function (hsb, hex, rgb, el) {
|
||||
updateColors(rgb.r, rgb.g, rgb.b);
|
||||
|
||||
domArray[0].value = rgb.r;
|
||||
domArray[1].value = rgb.g;
|
||||
domArray[2].value = rgb.b;
|
||||
self.webBridgeSync(group.id, {
|
||||
red: rgb.r,
|
||||
green: rgb.g,
|
||||
blue: rgb.b
|
||||
});
|
||||
},
|
||||
onSubmit: function (hsb, hex, rgb, el) {
|
||||
$(el)
|
||||
.css('background-color', '#' + hex);
|
||||
$(el)
|
||||
.colpickHide();
|
||||
domArray[0].value = rgb.r;
|
||||
domArray[1].value = rgb.g;
|
||||
domArray[2].value = rgb.b;
|
||||
self.webBridgeSync(group.id, {
|
||||
red: rgb.r,
|
||||
green: rgb.g,
|
||||
blue: rgb.b
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
addTextureField: function (parent, group) {
|
||||
var self = this;
|
||||
this.addLabel(parent, group);
|
||||
parent.className += " property texture";
|
||||
var textureImage = document.createElement("div");
|
||||
var textureUrl = document.createElement("input");
|
||||
textureUrl.setAttribute("type", "text");
|
||||
textureUrl.id = group.id;
|
||||
textureImage.className = "texture-image no-texture";
|
||||
var image = document.createElement("img");
|
||||
var imageLoad = _.debounce(function (url) {
|
||||
if (url.length > 0) {
|
||||
textureImage.classList.remove("no-texture");
|
||||
textureImage.classList.add("with-texture");
|
||||
image.src = url;
|
||||
image.style.display = "block";
|
||||
} else {
|
||||
image.src = "";
|
||||
image.style.display = "none";
|
||||
textureImage.classList.add("no-texture");
|
||||
}
|
||||
self.webBridgeSync(group.id, url);
|
||||
}, 250);
|
||||
|
||||
textureUrl.oninput = function (event) {
|
||||
// Add throttle
|
||||
var url = event.target.value;
|
||||
imageLoad(url);
|
||||
};
|
||||
textureUrl.onchange = textureUrl.oninput;
|
||||
textureImage.appendChild(image);
|
||||
parent.appendChild(textureImage);
|
||||
parent.appendChild(textureUrl);
|
||||
},
|
||||
addSlider: function (parent, group) {
|
||||
var self = this;
|
||||
this.addLabel(parent, group);
|
||||
parent.className += " property range";
|
||||
var container = document.createElement("div");
|
||||
container.className = "slider-wrapper";
|
||||
var slider = document.createElement("input");
|
||||
slider.setAttribute("type", "range");
|
||||
|
||||
var inputField = document.createElement("input");
|
||||
inputField.setAttribute("type", "number");
|
||||
|
||||
container.appendChild(slider);
|
||||
container.appendChild(inputField);
|
||||
parent.appendChild(container);
|
||||
|
||||
if (group.type === "SliderInteger") {
|
||||
inputField.setAttribute("min", group.min !== undefined ? group.min : 0);
|
||||
inputField.setAttribute("step", 1);
|
||||
|
||||
slider.setAttribute("min", group.min !== undefined ? group.min : 0);
|
||||
slider.setAttribute("max", group.max !== undefined ? group.max : 10000);
|
||||
slider.setAttribute("step", 1);
|
||||
|
||||
inputField.oninput = function (event) {
|
||||
|
||||
if (parseInt(event.target.value) > parseInt(slider.getAttribute("max")) && group.max !== 1) {
|
||||
slider.setAttribute("max", event.target.value);
|
||||
}
|
||||
slider.value = event.target.value;
|
||||
|
||||
self.webBridgeSync(group.id, slider.value);
|
||||
};
|
||||
inputField.onchange = inputField.oninput;
|
||||
slider.oninput = function (event) {
|
||||
inputField.value = event.target.value;
|
||||
self.webBridgeSync(group.id, slider.value);
|
||||
};
|
||||
|
||||
inputField.id = group.id;
|
||||
} else if (group.type === "SliderRadian") {
|
||||
slider.setAttribute("min", group.min !== undefined ? group.min : 0);
|
||||
slider.setAttribute("max", group.max !== undefined ? group.max : 180);
|
||||
slider.setAttribute("step", 1);
|
||||
parent.className += " radian";
|
||||
inputField.setAttribute("min", (group.min !== undefined ? group.min : 0));
|
||||
inputField.setAttribute("max", (group.max !== undefined ? group.max : 180));
|
||||
|
||||
inputField.oninput = function (event) {
|
||||
slider.value = event.target.value;
|
||||
self.webBridgeSync(group.id, slider.value * RADIANS_PER_DEGREE);
|
||||
};
|
||||
inputField.onchange = inputField.oninput;
|
||||
|
||||
inputField.id = group.id;
|
||||
slider.oninput = function (event) {
|
||||
if (event.target.value > 0) {
|
||||
inputField.value = Math.floor(event.target.value);
|
||||
} else {
|
||||
inputField.value = Math.ceil(event.target.value);
|
||||
}
|
||||
self.webBridgeSync(group.id, slider.value * RADIANS_PER_DEGREE);
|
||||
};
|
||||
var degrees = document.createElement("label");
|
||||
degrees.innerHTML = "°";
|
||||
degrees.style.fontSize = "1.4rem";
|
||||
degrees.style.display = "inline";
|
||||
degrees.style.verticalAlign = "top";
|
||||
degrees.style.paddingLeft = "0.4rem";
|
||||
container.appendChild(degrees);
|
||||
|
||||
} else {
|
||||
// Must then be Float
|
||||
inputField.setAttribute("min", group.min !== undefined ? group.min : 0);
|
||||
slider.setAttribute("step", 0.01);
|
||||
|
||||
slider.setAttribute("min", group.min !== undefined ? group.min : 0);
|
||||
slider.setAttribute("max", group.max !== undefined ? group.max : 1);
|
||||
slider.setAttribute("step", 0.01);
|
||||
|
||||
inputField.oninput = function (event) {
|
||||
if (parseFloat(event.target.value) > parseFloat(slider.getAttribute("max")) && group.max !== 1) {
|
||||
slider.setAttribute("max", event.target.value);
|
||||
}
|
||||
|
||||
slider.value = event.target.value;
|
||||
self.webBridgeSync(group.id, slider.value);
|
||||
// bind web sock update here.
|
||||
};
|
||||
inputField.onchange = inputField.oninput;
|
||||
slider.oninput = function (event) {
|
||||
inputField.value = event.target.value;
|
||||
self.webBridgeSync(group.id, inputField.value);
|
||||
};
|
||||
|
||||
inputField.id = group.id;
|
||||
}
|
||||
|
||||
// UpdateBinding
|
||||
},
|
||||
addCheckBox: function (parent, group) {
|
||||
var checkBox = document.createElement("input");
|
||||
checkBox.setAttribute("type", "checkbox");
|
||||
var self = this;
|
||||
checkBox.onchange = function (event) {
|
||||
self.webBridgeSync(group.id, event.target.checked);
|
||||
};
|
||||
checkBox.id = group.id;
|
||||
parent.appendChild(checkBox);
|
||||
var label = this.addLabel(parent, group);
|
||||
label.setAttribute("for", checkBox.id);
|
||||
parent.className += " property checkbox";
|
||||
},
|
||||
addElement: function (parent, group) {
|
||||
var self = this;
|
||||
var property = document.createElement("div");
|
||||
property.id = group.id;
|
||||
|
||||
var row = document.createElement("div");
|
||||
switch (group.type) {
|
||||
case "Button":
|
||||
var button = document.createElement("input");
|
||||
button.setAttribute("type", "button");
|
||||
button.id = group.id;
|
||||
if (group.disabled) {
|
||||
button.disabled = group.disabled;
|
||||
}
|
||||
button.className = group.class;
|
||||
button.value = group.name;
|
||||
|
||||
button.onclick = group.callback;
|
||||
parent.appendChild(button);
|
||||
break;
|
||||
case "Row":
|
||||
var hr = document.createElement("hr");
|
||||
hr.className = "splitter";
|
||||
if (group.id) {
|
||||
hr.id = group.id;
|
||||
}
|
||||
parent.appendChild(hr);
|
||||
break;
|
||||
case "Boolean":
|
||||
self.addCheckBox(row, group);
|
||||
parent.appendChild(row);
|
||||
break;
|
||||
case "SliderFloat":
|
||||
case "SliderInteger":
|
||||
case "SliderRadian":
|
||||
self.addSlider(row, group);
|
||||
parent.appendChild(row);
|
||||
break;
|
||||
case "Texture":
|
||||
self.addTextureField(row, group);
|
||||
parent.appendChild(row);
|
||||
break;
|
||||
case "Color":
|
||||
self.addColorPicker(row, group);
|
||||
parent.appendChild(row);
|
||||
break;
|
||||
case "Vector":
|
||||
self.addVector(row, group);
|
||||
parent.appendChild(row);
|
||||
break;
|
||||
case "VectorQuaternion":
|
||||
self.addVectorQuaternion(row, group);
|
||||
parent.appendChild(row);
|
||||
break;
|
||||
default:
|
||||
console.log("not defined");
|
||||
}
|
||||
return row;
|
||||
}
|
||||
};
|
124
scripts/system/particle_explorer/particle-style.css
Normal file
124
scripts/system/particle_explorer/particle-style.css
Normal file
File diff suppressed because one or more lines are too long
|
@ -1,95 +1,42 @@
|
|||
<!--
|
||||
// particleExplorer.hml
|
||||
//
|
||||
//
|
||||
//
|
||||
// Created by James B. Pollack @imgntn on 9/26/2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Loads dat.gui, underscore, and the app
|
||||
// Quickly edit the aesthetics of a particle system.
|
||||
|
||||
// Reworked by Menithal on 20/5/2017
|
||||
// Using a custom built system for High Fidelity
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
-->
|
||||
//
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="../html/css/colpick.css">
|
||||
<script type="text/javascript" src="dat.gui.min.js"></script>
|
||||
<script type="text/javascript" src="underscore-min.js"></script>
|
||||
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
||||
<script type="text/javascript" src="../html/js/eventBridgeLoader.js"></script>
|
||||
<script type="text/javascript" src="../html/js/jquery-2.1.4.min.js"></script>
|
||||
<script type="text/javascript" src="../html/js/colpick.js"></script>
|
||||
<script type="text/javascript" src="particleExplorer.js"></script>
|
||||
<script>
|
||||
function loaded() {
|
||||
// Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked
|
||||
document.addEventListener("contextmenu", function (event) {
|
||||
event.preventDefault();
|
||||
}, false);
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
<link rel="stylesheet" type="text/css" href="../html/css/colpick.css">
|
||||
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
||||
<script type="text/javascript" src="../html/js/eventBridgeLoader.js"></script>
|
||||
<!---->
|
||||
<script type="text/javascript" src="../html/js/jquery-2.1.4.min.js"></script>
|
||||
<script type="text/javascript" src="../html/js/colpick.js"></script>
|
||||
|
||||
body{
|
||||
background-color:black;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
<script type="text/javascript" src="underscore-min.js"></script>
|
||||
<script type="text/javascript" src="hifi-entity-ui.js?v1"></script>
|
||||
|
||||
#my-gui-container{
|
||||
|
||||
}
|
||||
|
||||
.importer{
|
||||
margin-bottom:4px;
|
||||
}
|
||||
.exported-props-section {
|
||||
width: 50%;
|
||||
margin: 0 auto;
|
||||
|
||||
}
|
||||
#exported-props {
|
||||
/* Set the margin-left and margin-right automatically set */
|
||||
color: white;
|
||||
white-space: pre-wrap; /* css-3 */
|
||||
}
|
||||
|
||||
.color-box {
|
||||
display: block;
|
||||
width: 60%;
|
||||
height: 21px;
|
||||
margin-top: 4px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.color-box.highlight {
|
||||
width: 13.5pt;
|
||||
height: 13.5pt;
|
||||
border: 1.5pt solid black;
|
||||
}
|
||||
|
||||
::-webkit-input-placeholder {
|
||||
text-align: center;
|
||||
font-family: Helvetica
|
||||
}
|
||||
|
||||
#importer-input{
|
||||
width:90%;
|
||||
line-height: 2;
|
||||
margin-left:5%;
|
||||
}
|
||||
|
||||
</style>
|
||||
<link rel="stylesheet" type="text/css" href="../html/css/hifi-style.css">
|
||||
<link rel="stylesheet" type="text/css" href="../html/css/edit-style.css">
|
||||
<link rel="stylesheet" type="text/css" href="particle-style.css">
|
||||
</head>
|
||||
<body onload="loaded();">
|
||||
<div class="importer">
|
||||
<input type='text' id="importer-input" placeholder="Import: Paste JSON here." onkeypress="handleInputKeyPress(event)">
|
||||
<div class = "exported-props-section">
|
||||
<div id = "exported-props"></div>
|
||||
</div>
|
||||
<div id="my-gui-container">
|
||||
</div>
|
||||
|
||||
<body>
|
||||
<div id="particle-explorer">
|
||||
<div class="section-header">
|
||||
<label> Particle Explorer </label>
|
||||
</div>
|
||||
<!-- This will be filled by the script! -->
|
||||
</div>
|
||||
<div id="rem"></div>
|
||||
<script type="text/javascript" src="particleExplorer.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -2,550 +2,410 @@
|
|||
// particleExplorer.js
|
||||
//
|
||||
// Created by James B. Pollack @imgntn on 9/26/2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Reworked by Menithal on 20/5/2017
|
||||
//
|
||||
// Web app side of the App - contains GUI.
|
||||
// This is an example of a new, easy way to do two way bindings between dynamically created GUI and in-world entities.
|
||||
// This is an example of a new, easy way to do two way bindings between dynamically created GUI and in-world entities.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
/* global window, alert, EventBridge, dat, listenForSettingsUpdates, createVec3Folder, createQuatFolder, writeVec3ToInterface, writeDataToInterface,
|
||||
$, document, _, openEventBridge */
|
||||
/* global HifiEntityUI, openEventBridge, console, EventBridge, document, window */
|
||||
/* eslint no-console: 0, no-global-assign: 0 */
|
||||
|
||||
var Settings = function () {
|
||||
this.exportSettings = function () {
|
||||
// copyExportSettingsToClipboard();
|
||||
showPreselectedPrompt();
|
||||
};
|
||||
this.importSettings = function () {
|
||||
importSettings();
|
||||
};
|
||||
};
|
||||
(function () {
|
||||
|
||||
// 2-way bindings-aren't quite ready yet. see bottom of file.
|
||||
var AUTO_UPDATE = false;
|
||||
var UPDATE_ALL_FREQUENCY = 100;
|
||||
var root = document.getElementById("particle-explorer");
|
||||
|
||||
var controllers = [];
|
||||
var colpickKeys = [];
|
||||
var folders = [];
|
||||
var gui = null;
|
||||
var settings = new Settings();
|
||||
var updateInterval;
|
||||
|
||||
var active = false;
|
||||
|
||||
var currentInputField;
|
||||
var storedController;
|
||||
// CHANGE TO WHITELIST
|
||||
var keysToAllow = [
|
||||
'isEmitting',
|
||||
'maxParticles',
|
||||
'lifespan',
|
||||
'emitRate',
|
||||
'emitSpeed',
|
||||
'speedSpread',
|
||||
'emitOrientation',
|
||||
'emitDimensions',
|
||||
'polarStart',
|
||||
'polarFinish',
|
||||
'azimuthStart',
|
||||
'azimuthFinish',
|
||||
'emitAcceleration',
|
||||
'accelerationSpread',
|
||||
'particleRadius',
|
||||
'radiusSpread',
|
||||
'radiusStart',
|
||||
'radiusFinish',
|
||||
'color',
|
||||
'colorSpread',
|
||||
'colorStart',
|
||||
'colorFinish',
|
||||
'alpha',
|
||||
'alphaSpread',
|
||||
'alphaStart',
|
||||
'alphaFinish',
|
||||
'emitterShouldTrail',
|
||||
'textures'
|
||||
];
|
||||
|
||||
var individualKeys = [];
|
||||
var vec3Keys = [];
|
||||
var quatKeys = [];
|
||||
var colorKeys = [];
|
||||
|
||||
window.onload = function () {
|
||||
openEventBridge(function () {
|
||||
var stringifiedData = JSON.stringify({
|
||||
messageType: 'page_loaded'
|
||||
window.onload = function () {
|
||||
var ui = new HifiEntityUI(root);
|
||||
var textarea = document.createElement("textarea");
|
||||
var properties = "";
|
||||
var menuStructure = {
|
||||
General: [
|
||||
{
|
||||
type: "Row",
|
||||
id: "export-import-field"
|
||||
},
|
||||
{
|
||||
id: "show-properties-button",
|
||||
name: "Show Properties",
|
||||
type: "Button",
|
||||
class: "blue",
|
||||
disabled: true,
|
||||
callback: function (event) {
|
||||
var insertZone = document.getElementById("export-import-field");
|
||||
var json = ui.getSettings();
|
||||
properties = JSON.stringify(json);
|
||||
textarea.value = properties;
|
||||
if (!insertZone.contains(textarea)) {
|
||||
insertZone.appendChild(textarea);
|
||||
insertZone.parentNode.parentNode.style.maxHeight =
|
||||
insertZone.parentNode.clientHeight + "px";
|
||||
document.getElementById("export-properties-button").removeAttribute("disabled");
|
||||
textarea.onchange = function (e) {
|
||||
if (e.target.value !== properties) {
|
||||
document.getElementById("import-properties-button").removeAttribute("disabled");
|
||||
}
|
||||
};
|
||||
textarea.oninput = textarea.onchange;
|
||||
} else {
|
||||
textarea.onchange = function () {};
|
||||
textarea.oninput = textarea.onchange;
|
||||
textarea.value = "";
|
||||
textarea.remove();
|
||||
insertZone.parentNode.parentNode.style.maxHeight =
|
||||
insertZone.parentNode.clientHeight + "px";
|
||||
document.getElementById("export-properties-button").setAttribute("disabled", true);
|
||||
document.getElementById("import-properties-button").setAttribute("disabled", true);
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "import-properties-button",
|
||||
name: "Import",
|
||||
type: "Button",
|
||||
class: "blue",
|
||||
disabled: true,
|
||||
callback: function (event) {
|
||||
ui.fillFields(JSON.parse(textarea.value));
|
||||
ui.submitChanges(JSON.parse(textarea.value));
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "export-properties-button",
|
||||
name: "Export",
|
||||
type: "Button",
|
||||
class: "red",
|
||||
disabled: true,
|
||||
callback: function (event) {
|
||||
textarea.select();
|
||||
try {
|
||||
var success = document.execCommand('copy');
|
||||
if (!success) {
|
||||
throw "Not success :(";
|
||||
}
|
||||
} catch (e) {
|
||||
print("couldnt copy field");
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "Row"
|
||||
},
|
||||
{
|
||||
id: "isEmitting",
|
||||
name: "Is Emitting",
|
||||
type: "Boolean"
|
||||
},
|
||||
{
|
||||
type: "Row"
|
||||
},
|
||||
{
|
||||
id: "lifespan",
|
||||
name: "Lifespan",
|
||||
type: "SliderFloat",
|
||||
min: 0.01,
|
||||
max: 10
|
||||
},
|
||||
{
|
||||
type: "Row"
|
||||
},
|
||||
{
|
||||
id: "maxParticles",
|
||||
name: "Max Particles",
|
||||
type: "SliderInteger",
|
||||
min: 1,
|
||||
max: 10000
|
||||
},
|
||||
{
|
||||
type: "Row"
|
||||
},
|
||||
{
|
||||
id: "textures",
|
||||
name: "Textures",
|
||||
type: "Texture"
|
||||
},
|
||||
{
|
||||
type: "Row"
|
||||
}
|
||||
],
|
||||
Emit: [
|
||||
{
|
||||
id: "emitRate",
|
||||
name: "Emit Rate",
|
||||
type: "SliderInteger",
|
||||
max: 1000,
|
||||
min: 1
|
||||
},
|
||||
{
|
||||
type: "Row"
|
||||
},
|
||||
{
|
||||
id: "emitSpeed",
|
||||
name: "Emit Speed",
|
||||
type: "SliderFloat",
|
||||
max: 5
|
||||
},
|
||||
{
|
||||
type: "Row"
|
||||
},
|
||||
{
|
||||
id: "emitDimensions",
|
||||
name: "Emit Dimension",
|
||||
type: "Vector",
|
||||
defaultRange: {
|
||||
min: 0,
|
||||
step: 0.01
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "Row"
|
||||
},
|
||||
{
|
||||
id: "emitOrientation",
|
||||
unit: "deg",
|
||||
name: "Emit Orientation",
|
||||
type: "VectorQuaternion",
|
||||
defaultRange: {
|
||||
min: 0,
|
||||
step: 0.01
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "Row"
|
||||
},
|
||||
{
|
||||
id: "emitShouldTrail",
|
||||
name: "Emit Should Trail",
|
||||
type: "Boolean"
|
||||
},
|
||||
{
|
||||
type: "Row"
|
||||
}
|
||||
],
|
||||
Radius: [
|
||||
{
|
||||
id: "particleRadius",
|
||||
name: "Particle Radius",
|
||||
type: "SliderFloat",
|
||||
max: 4.0
|
||||
},
|
||||
{
|
||||
type: "Row"
|
||||
},
|
||||
{
|
||||
id: "radiusSpread",
|
||||
name: "Radius Spread",
|
||||
type: "SliderFloat",
|
||||
max: 4.0
|
||||
},
|
||||
{
|
||||
type: "Row"
|
||||
},
|
||||
{
|
||||
id: "radiusStart",
|
||||
name: "Radius Start",
|
||||
type: "SliderFloat",
|
||||
max: 4.0
|
||||
},
|
||||
{
|
||||
type: "Row"
|
||||
},
|
||||
{
|
||||
id: "radiusFinish",
|
||||
name: "Radius Finish",
|
||||
type: "SliderFloat",
|
||||
max: 4.0
|
||||
},
|
||||
{
|
||||
type: "Row"
|
||||
}
|
||||
],
|
||||
Color: [
|
||||
{
|
||||
id: "color",
|
||||
name: "Color",
|
||||
type: "Color",
|
||||
defaultColor: {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "Row"
|
||||
},
|
||||
{
|
||||
id: "colorSpread",
|
||||
name: "Color Spread",
|
||||
type: "Color",
|
||||
defaultColor: {
|
||||
red: 0,
|
||||
green: 0,
|
||||
blue: 0
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "Row"
|
||||
},
|
||||
{
|
||||
id: "colorStart",
|
||||
name: "Color Start",
|
||||
type: "Color",
|
||||
defaultColor: {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "Row"
|
||||
},
|
||||
{
|
||||
id: "colorFinish",
|
||||
name: "Color Finish",
|
||||
type: "Color",
|
||||
defaultColor: {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "Row"
|
||||
}
|
||||
],
|
||||
Acceleration: [
|
||||
{
|
||||
id: "emitAcceleration",
|
||||
name: "Emit Acceleration",
|
||||
type: "Vector",
|
||||
defaultRange: {
|
||||
step: 0.01
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "Row"
|
||||
},
|
||||
{
|
||||
id: "accelerationSpread",
|
||||
name: "Acceleration Spread",
|
||||
type: "Vector",
|
||||
defaultRange: {
|
||||
step: 0.01
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "Row"
|
||||
}
|
||||
],
|
||||
Alpha: [
|
||||
{
|
||||
id: "alpha",
|
||||
name: "Alpha",
|
||||
type: "SliderFloat"
|
||||
},
|
||||
{
|
||||
type: "Row"
|
||||
},
|
||||
{
|
||||
id: "alphaSpread",
|
||||
name: "Alpha Spread",
|
||||
type: "SliderFloat"
|
||||
},
|
||||
{
|
||||
type: "Row"
|
||||
},
|
||||
{
|
||||
id: "alphaStart",
|
||||
name: "Alpha Start",
|
||||
type: "SliderFloat"
|
||||
},
|
||||
{
|
||||
type: "Row"
|
||||
},
|
||||
{
|
||||
id: "alphaFinish",
|
||||
name: "Alpha Finish",
|
||||
type: "SliderFloat"
|
||||
},
|
||||
{
|
||||
type: "Row"
|
||||
}
|
||||
],
|
||||
Polar: [
|
||||
{
|
||||
id: "polarStart",
|
||||
name: "Polar Start",
|
||||
unit: "deg",
|
||||
type: "SliderRadian"
|
||||
},
|
||||
{
|
||||
type: "Row"
|
||||
},
|
||||
{
|
||||
id: "polarFinish",
|
||||
name: "Polar Finish",
|
||||
unit: "deg",
|
||||
type: "SliderRadian"
|
||||
},
|
||||
{
|
||||
type: "Row"
|
||||
}
|
||||
],
|
||||
Azimuth: [
|
||||
{
|
||||
id: "azimuthStart",
|
||||
name: "Azimuth Start",
|
||||
unit: "deg",
|
||||
type: "SliderRadian",
|
||||
min: -180,
|
||||
max: 0
|
||||
},
|
||||
{
|
||||
type: "Row"
|
||||
},
|
||||
{
|
||||
id: "azimuthFinish",
|
||||
name: "Azimuth Finish",
|
||||
unit: "deg",
|
||||
type: "SliderRadian"
|
||||
},
|
||||
{
|
||||
type: "Row"
|
||||
}
|
||||
]
|
||||
};
|
||||
ui.setUI(menuStructure);
|
||||
ui.setOnSelect(function () {
|
||||
document.getElementById("show-properties-button").removeAttribute("disabled");
|
||||
document.getElementById("export-properties-button").setAttribute("disabled", true);
|
||||
document.getElementById("import-properties-button").setAttribute("disabled", true);
|
||||
});
|
||||
ui.build();
|
||||
var overrideLoad = false;
|
||||
if (openEventBridge === undefined) {
|
||||
overrideLoad = true,
|
||||
openEventBridge = function (callback) {
|
||||
callback({
|
||||
emitWebEvent: function () {},
|
||||
submitChanges: function () {},
|
||||
scriptEventReceived: {
|
||||
connect: function () {
|
||||
|
||||
EventBridge.emitWebEvent(
|
||||
stringifiedData
|
||||
);
|
||||
|
||||
listenForSettingsUpdates();
|
||||
window.onresize = setGUIWidthToWindowWidth;
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
function loadGUI() {
|
||||
// whether or not to autoplace
|
||||
gui = new dat.GUI({
|
||||
autoPlace: false
|
||||
});
|
||||
|
||||
// if not autoplacing, put gui in a custom container
|
||||
if (gui.autoPlace === false) {
|
||||
var customContainer = document.getElementById('my-gui-container');
|
||||
customContainer.appendChild(gui.domElement);
|
||||
}
|
||||
|
||||
// presets for the GUI itself. a little confusing and import/export is mostly what we want to do at the moment.
|
||||
// gui.remember(settings);
|
||||
|
||||
colpickKeys = [];
|
||||
var keys = _.keys(settings);
|
||||
|
||||
_.each(keys, function(key) {
|
||||
var shouldAllow = _.contains(keysToAllow, key);
|
||||
|
||||
if (shouldAllow) {
|
||||
var subKeys = _.keys(settings[key]);
|
||||
var hasX = _.contains(subKeys, 'x');
|
||||
var hasY = _.contains(subKeys, 'y');
|
||||
var hasZ = _.contains(subKeys, 'z');
|
||||
var hasW = _.contains(subKeys, 'w');
|
||||
var hasRed = _.contains(subKeys, 'red');
|
||||
var hasGreen = _.contains(subKeys, 'green');
|
||||
var hasBlue = _.contains(subKeys, 'blue');
|
||||
|
||||
if ((hasX && hasY && hasZ) && hasW === false) {
|
||||
vec3Keys.push(key);
|
||||
} else if (hasX && hasY && hasZ && hasW) {
|
||||
quatKeys.push(key);
|
||||
} else if (hasRed || hasGreen || hasBlue) {
|
||||
colorKeys.push(key);
|
||||
|
||||
} else {
|
||||
individualKeys.push(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// alphabetize our keys
|
||||
individualKeys.sort();
|
||||
vec3Keys.sort();
|
||||
quatKeys.sort();
|
||||
colorKeys.sort();
|
||||
|
||||
// add to gui in the order they should appear
|
||||
gui.add(settings, 'importSettings');
|
||||
gui.add(settings, 'exportSettings');
|
||||
addIndividualKeys();
|
||||
addFolders();
|
||||
|
||||
// set the gui width to match the web window width
|
||||
gui.width = window.innerWidth;
|
||||
|
||||
// 2-way binding stuff
|
||||
// if (AUTO_UPDATE) {
|
||||
// setInterval(manuallyUpdateDisplay, UPDATE_ALL_FREQUENCY);
|
||||
// registerDOMElementsForListenerBlocking();
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
function addIndividualKeys() {
|
||||
_.each(individualKeys, function(key) {
|
||||
// temporary patch for not crashing when this goes below 0
|
||||
var controller;
|
||||
|
||||
if (key.indexOf('emitRate') > -1) {
|
||||
controller = gui.add(settings, key).min(0);
|
||||
} else {
|
||||
controller = gui.add(settings, key);
|
||||
}
|
||||
|
||||
// 2-way - need to fix not being able to input exact values if constantly listening
|
||||
// controller.listen();
|
||||
|
||||
// keep track of our controller
|
||||
controllers.push(controller);
|
||||
|
||||
// hook into change events for this gui controller
|
||||
controller.onChange(function(value) {
|
||||
// Fires on every change, drag, keypress, etc.
|
||||
writeDataToInterface(this.property, value);
|
||||
openEventBridge(function (EventBridge) {
|
||||
ui.connect(EventBridge);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function addFolders() {
|
||||
_.each(colorKeys, function(key) {
|
||||
createColorPicker(key);
|
||||
});
|
||||
_.each(vec3Keys, function(key) {
|
||||
createVec3Folder(key);
|
||||
});
|
||||
_.each(quatKeys, function(key) {
|
||||
createQuatFolder(key);
|
||||
});
|
||||
}
|
||||
|
||||
function createColorPicker(key) {
|
||||
var colorObject = settings[key];
|
||||
|
||||
// Embed colpick's color picker into dat.GUI
|
||||
var name = document.createElement('span');
|
||||
name.className = 'property-name';
|
||||
name.innerHTML = key;
|
||||
|
||||
var container = document.createElement('div');
|
||||
container.appendChild(name);
|
||||
|
||||
var $colPickContainer = $('<div>', {
|
||||
id: key.toString(),
|
||||
class: "color-box"
|
||||
});
|
||||
$colPickContainer.css('background-color', "rgb(" + colorObject.red + "," + colorObject.green + "," + colorObject.blue + ")");
|
||||
container.appendChild($colPickContainer[0]);
|
||||
|
||||
var $li = $('<li>', { class: 'cr object color' });
|
||||
$li.append(container);
|
||||
gui.__ul.appendChild($li[0]);
|
||||
gui.onResize();
|
||||
|
||||
$colPickContainer.colpick({
|
||||
colorScheme: 'dark',
|
||||
layout: 'hex',
|
||||
color: { r: colorObject.red, g: colorObject.green, b: colorObject.blue },
|
||||
onSubmit: function (hsb, hex, rgb, el) {
|
||||
$(el).css('background-color', '#' + hex);
|
||||
$(el).colpickHide();
|
||||
|
||||
var obj = {};
|
||||
obj[key] = { red: rgb.r, green: rgb.g, blue: rgb.b };
|
||||
writeVec3ToInterface(obj);
|
||||
if (overrideLoad) {
|
||||
openEventBridge();
|
||||
}
|
||||
});
|
||||
|
||||
colpickKeys.push(key);
|
||||
}
|
||||
|
||||
function createVec3Folder(category) {
|
||||
var folder = gui.addFolder(category);
|
||||
|
||||
folder.add(settings[category], 'x').step(0.1).onChange(function(value) {
|
||||
// Fires when a controller loses focus.
|
||||
var obj = {};
|
||||
obj[category] = {};
|
||||
obj[category][this.property] = value;
|
||||
obj[category].y = settings[category].y;
|
||||
obj[category].z = settings[category].z;
|
||||
writeVec3ToInterface(obj);
|
||||
});
|
||||
|
||||
folder.add(settings[category], 'y').step(0.1).onChange(function(value) {
|
||||
// Fires when a controller loses focus.
|
||||
var obj = {};
|
||||
obj[category] = {};
|
||||
obj[category].x = settings[category].x;
|
||||
obj[category][this.property] = value;
|
||||
obj[category].z = settings[category].z;
|
||||
writeVec3ToInterface(obj);
|
||||
});
|
||||
|
||||
folder.add(settings[category], 'z').step(0.1).onChange(function(value) {
|
||||
// Fires when a controller loses focus.
|
||||
var obj = {};
|
||||
obj[category] = {};
|
||||
obj[category].y = settings[category].y;
|
||||
obj[category].x = settings[category].x;
|
||||
obj[category][this.property] = value;
|
||||
writeVec3ToInterface(obj);
|
||||
});
|
||||
|
||||
folders.push(folder);
|
||||
folder.open();
|
||||
}
|
||||
|
||||
function createQuatFolder(category) {
|
||||
var folder = gui.addFolder(category);
|
||||
|
||||
folder.add(settings[category], 'x').step(0.1).onChange(function(value) {
|
||||
// Fires when a controller loses focus.
|
||||
var obj = {};
|
||||
obj[category] = {};
|
||||
obj[category][this.property] = value;
|
||||
obj[category].y = settings[category].y;
|
||||
obj[category].z = settings[category].z;
|
||||
obj[category].w = settings[category].w;
|
||||
writeVec3ToInterface(obj);
|
||||
});
|
||||
|
||||
folder.add(settings[category], 'y').step(0.1).onChange(function(value) {
|
||||
// Fires when a controller loses focus.
|
||||
var obj = {};
|
||||
obj[category] = {};
|
||||
obj[category].x = settings[category].x;
|
||||
obj[category][this.property] = value;
|
||||
obj[category].z = settings[category].z;
|
||||
obj[category].w = settings[category].w;
|
||||
writeVec3ToInterface(obj);
|
||||
});
|
||||
|
||||
folder.add(settings[category], 'z').step(0.1).onChange(function(value) {
|
||||
// Fires when a controller loses focus.
|
||||
var obj = {};
|
||||
obj[category] = {};
|
||||
obj[category].x = settings[category].x;
|
||||
obj[category].y = settings[category].y;
|
||||
obj[category][this.property] = value;
|
||||
obj[category].w = settings[category].w;
|
||||
writeVec3ToInterface(obj);
|
||||
});
|
||||
|
||||
folder.add(settings[category], 'w').step(0.1).onChange(function(value) {
|
||||
// Fires when a controller loses focus.
|
||||
var obj = {};
|
||||
obj[category] = {};
|
||||
obj[category].x = settings[category].x;
|
||||
obj[category].y = settings[category].y;
|
||||
obj[category].z = settings[category].z;
|
||||
obj[category][this.property] = value;
|
||||
writeVec3ToInterface(obj);
|
||||
});
|
||||
|
||||
folders.push(folder);
|
||||
folder.open();
|
||||
}
|
||||
|
||||
function convertColorObjectToArray(colorObject) {
|
||||
var colorArray = [];
|
||||
|
||||
_.each(colorObject, function(singleColor) {
|
||||
colorArray.push(singleColor);
|
||||
});
|
||||
|
||||
return colorArray;
|
||||
}
|
||||
|
||||
function convertColorArrayToObject(colorArray) {
|
||||
var colorObject = {
|
||||
red: colorArray[0],
|
||||
green: colorArray[1],
|
||||
blue: colorArray[2]
|
||||
};
|
||||
|
||||
return colorObject;
|
||||
}
|
||||
|
||||
function writeDataToInterface(property, value) {
|
||||
var data = {};
|
||||
data[property] = value;
|
||||
|
||||
var sendData = {
|
||||
messageType: "settings_update",
|
||||
updatedSettings: data
|
||||
};
|
||||
|
||||
var stringifiedData = JSON.stringify(sendData);
|
||||
|
||||
EventBridge.emitWebEvent(stringifiedData);
|
||||
}
|
||||
|
||||
function writeVec3ToInterface(obj) {
|
||||
var sendData = {
|
||||
messageType: "settings_update",
|
||||
updatedSettings: obj
|
||||
};
|
||||
|
||||
var stringifiedData = JSON.stringify(sendData);
|
||||
|
||||
EventBridge.emitWebEvent(stringifiedData);
|
||||
}
|
||||
|
||||
function listenForSettingsUpdates() {
|
||||
EventBridge.scriptEventReceived.connect(function(data) {
|
||||
data = JSON.parse(data);
|
||||
if (data.messageType === 'particle_settings') {
|
||||
_.each(data.currentProperties, function(value, key) {
|
||||
settings[key] = {};
|
||||
settings[key] = value;
|
||||
});
|
||||
|
||||
if (gui) {
|
||||
manuallyUpdateDisplay();
|
||||
} else {
|
||||
loadGUI();
|
||||
}
|
||||
if (!active) {
|
||||
// gui.toggleHide();
|
||||
gui.closed = false;
|
||||
}
|
||||
active = true;
|
||||
|
||||
} else if (data.messageType === "particle_close") {
|
||||
// none of this seems to work.
|
||||
// if (active) {
|
||||
// gui.toggleHide();
|
||||
// }
|
||||
active = false;
|
||||
gui.closed = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function manuallyUpdateDisplay() {
|
||||
// Iterate over all controllers
|
||||
// this is expensive, write a method for indiviudal controllers and use it when the value is different than a cached value, perhaps.
|
||||
var i;
|
||||
for (i in gui.__controllers) {
|
||||
gui.__controllers[i].updateDisplay();
|
||||
}
|
||||
|
||||
// Update color pickers
|
||||
for (i in colpickKeys) {
|
||||
var color = settings[colpickKeys[i]];
|
||||
var $object = $('#' + colpickKeys[i]);
|
||||
$object.css('background-color', "rgb(" + color.red + "," + color.green + "," + color.blue + ")");
|
||||
$object.colpickSetColor({ r: color.red, g: color.green, b: color.blue }, true);
|
||||
}
|
||||
}
|
||||
|
||||
function setGUIWidthToWindowWidth() {
|
||||
if (gui !== null) {
|
||||
gui.width = window.innerWidth;
|
||||
}
|
||||
}
|
||||
|
||||
function handleInputKeyPress(e) {
|
||||
if (e.keyCode === 13) {
|
||||
importSettings();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function importSettings() {
|
||||
var importInput = document.getElementById('importer-input');
|
||||
|
||||
try {
|
||||
var importedSettings = JSON.parse(importInput.value);
|
||||
|
||||
var keys = _.keys(importedSettings);
|
||||
|
||||
_.each(keys, function(key) {
|
||||
var shouldAllow = _.contains(keysToAllow, key);
|
||||
|
||||
if (!shouldAllow) {
|
||||
return;
|
||||
}
|
||||
|
||||
settings[key] = importedSettings[key];
|
||||
});
|
||||
|
||||
writeVec3ToInterface(settings);
|
||||
|
||||
manuallyUpdateDisplay();
|
||||
} catch (e) {
|
||||
alert('Not properly formatted JSON');
|
||||
}
|
||||
}
|
||||
|
||||
function prepareSettingsForExport() {
|
||||
var keys = _.keys(settings);
|
||||
|
||||
var exportSettings = {};
|
||||
|
||||
_.each(keys, function(key) {
|
||||
var shouldAllow = _.contains(keysToAllow, key);
|
||||
|
||||
if (!shouldAllow) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (key.indexOf('color') > -1) {
|
||||
var colorObject = convertColorArrayToObject(settings[key]);
|
||||
settings[key] = colorObject;
|
||||
}
|
||||
|
||||
exportSettings[key] = settings[key];
|
||||
});
|
||||
|
||||
return JSON.stringify(exportSettings, null, 4);
|
||||
}
|
||||
|
||||
function showPreselectedPrompt() {
|
||||
var elem = document.getElementById("exported-props");
|
||||
var exportSettings = prepareSettingsForExport();
|
||||
elem.innerHTML = "";
|
||||
var buttonnode = document.createElement('input');
|
||||
buttonnode.setAttribute('type', 'button');
|
||||
buttonnode.setAttribute('value', 'close');
|
||||
elem.appendChild(document.createTextNode("COPY THE BELOW FIELD TO CLIPBOARD:"));
|
||||
elem.appendChild(document.createElement("br"));
|
||||
var textAreaNode = document.createElement("textarea");
|
||||
textAreaNode.value = exportSettings;
|
||||
elem.appendChild(textAreaNode);
|
||||
elem.appendChild(document.createElement("br"));
|
||||
elem.appendChild(buttonnode);
|
||||
|
||||
buttonnode.onclick = function() {
|
||||
console.log("click");
|
||||
elem.innerHTML = "";
|
||||
};
|
||||
|
||||
// window.alert("Ctrl-C to copy, then Enter.", prepareSettingsForExport());
|
||||
}
|
||||
|
||||
function removeContainerDomElement() {
|
||||
var elem = document.getElementById("my-gui-container");
|
||||
elem.parentNode.removeChild(elem);
|
||||
}
|
||||
|
||||
function removeListenerFromGUI(key) {
|
||||
_.each(gui.__listening, function(controller, index) {
|
||||
if (controller.property === key) {
|
||||
storedController = controller;
|
||||
gui.__listening.splice(index, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// the section below is to try to work at achieving two way bindings;
|
||||
|
||||
function addListenersBackToGUI() {
|
||||
gui.__listening.push(storedController);
|
||||
storedController = null;
|
||||
}
|
||||
|
||||
function registerDOMElementsForListenerBlocking() {
|
||||
_.each(gui.__controllers, function(controller) {
|
||||
var input = controller.domElement.childNodes[0];
|
||||
input.addEventListener('focus', function() {
|
||||
console.log('INPUT ELEMENT GOT FOCUS!' + controller.property);
|
||||
removeListenerFromGUI(controller.property);
|
||||
});
|
||||
});
|
||||
|
||||
_.each(gui.__controllers, function(controller) {
|
||||
var input = controller.domElement.childNodes[0];
|
||||
input.addEventListener('blur', function() {
|
||||
console.log('INPUT ELEMENT GOT BLUR!' + controller.property);
|
||||
addListenersBackToGUI();
|
||||
});
|
||||
});
|
||||
|
||||
// also listen to inputs inside of folders
|
||||
_.each(gui.__folders, function(folder) {
|
||||
_.each(folder.__controllers, function(controller) {
|
||||
var input = controller.__input;
|
||||
input.addEventListener('focus', function() {
|
||||
console.log('FOLDER ELEMENT GOT FOCUS!' + controller.property);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue