Merge branch 'master' into feature/pole-vector

This commit is contained in:
Anthony J. Thibault 2017-06-13 15:37:59 -07:00
commit 03a6f7082e
102 changed files with 2213 additions and 1165 deletions

View file

@ -1,14 +1,16 @@
{
"RenderShadowTask": {
"Enabled": {
"enabled": true
}
},
"RenderDeferredTask": {
"AmbientOcclusion": {
"RenderMainView": {
"RenderShadowTask": {
"Enabled": {
"enabled": true
}
},
"RenderDeferredTask": {
"AmbientOcclusion": {
"Enabled": {
"enabled": true
}
}
}
}
}

View file

@ -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);
});

View file

@ -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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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();

View file

@ -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);

View file

@ -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;
}

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -20,7 +20,6 @@ TabletModalWindow {
id: loginDialogRoot
objectName: "LoginDialog"
property var eventBridge;
signal sendToScript(var message);
property bool isHMD: false
property bool gotoPreviousApp: false;

View file

@ -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 {

View file

@ -116,9 +116,7 @@ Preference {
Component {
id: tabletAvatarBrowserBuilder;
TabletAvatarBrowser {
eventBridge: tabletRoot.eventBridge
}
TabletAvatarBrowser { }
}
}

View file

@ -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.");
}
}
}
}

View file

@ -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;

View file

@ -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

View file

@ -20,7 +20,6 @@ Rectangle {
id: root
objectName: "DCConectionTiming"
property var eventBridge;
signal sendToScript(var message);
property bool isHMD: false

View file

@ -19,7 +19,6 @@ Rectangle {
id: root
objectName: "DebugWindow"
property var eventBridge;
property var title: "Debug Window"
property bool isHMD: false

View file

@ -20,7 +20,6 @@ Rectangle {
id: root
objectName: "EntityStatistics"
property var eventBridge;
signal sendToScript(var message);
property bool isHMD: false

View file

@ -20,7 +20,6 @@ Rectangle {
id: root
objectName: "LODTools"
property var eventBridge;
signal sendToScript(var message);
property bool isHMD: false

View file

@ -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 { }

View file

@ -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);
}

View file

@ -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':

View file

@ -18,7 +18,6 @@ import "../../dialogs"
Rectangle {
id: inputRecorder
property var eventBridge;
HifiConstants { id: hifi }
signal sendToScript(var message);
color: hifi.colors.baseGray;

View file

@ -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

View file

@ -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;
}

View file

@ -25,7 +25,6 @@ Item {
property bool keyboardRaised: false
property bool punctuationMode: false
property var eventBridge;
signal sendToScript(var message);
anchors.fill: parent

View file

@ -19,7 +19,6 @@ StackView {
objectName: "stack"
property string title: "Audio Settings"
property var eventBridge;
signal sendToScript(var message);
function pushSource(path) {

View file

@ -19,7 +19,6 @@ StackView {
objectName: "stack"
property string title: "Avatar Settings"
property var eventBridge;
signal sendToScript(var message);
function pushSource(path) {

View file

@ -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) {

View file

@ -19,7 +19,6 @@ StackView {
objectName: "stack"
property string title: "Graphics Settings"
property var eventBridge;
signal sendToScript(var message);
function pushSource(path) {

View file

@ -19,7 +19,6 @@ StackView {
objectName: "stack"
property string title: "LOD Settings"
property var eventBridge;
signal sendToScript(var message);
function pushSource(path) {

View file

@ -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 {

View file

@ -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();

View file

@ -19,7 +19,6 @@ StackView {
objectName: "stack"
property var title: "Networking Settings"
property var eventBridge;
signal sendToScript(var message);
function pushSource(path) {

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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 {

View file

@ -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;

View file

@ -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);

View file

@ -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";

View file

@ -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);
}
}

View file

@ -22,7 +22,7 @@ public:
private:
enum Action {
DELETE_INTERFACE_INI,
RETAIN_AVATAR_INFO,
RETAIN_IMPORTANT_INFO,
DO_NOTHING
};

View file

@ -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);

View file

@ -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();

View file

@ -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);
}

View file

@ -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.
}
}

View file

@ -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 };

View file

@ -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() {

View file

@ -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) {

View file

@ -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

View file

@ -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 };

View file

@ -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);

View file

@ -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()) {

View file

@ -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

View file

@ -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:

View file

@ -236,7 +236,8 @@ enum class AvatarMixerPacketVersion : PacketVersion {
AvatarAsChildFixes,
StickAndBallDefaultAvatar,
IdentityPacketsIncludeUpdateTime,
AvatarIdentitySequenceId
AvatarIdentitySequenceId,
MannequinDefaultAvatar
};
enum class DomainConnectRequestVersion : PacketVersion {

View file

@ -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);

View file

@ -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>();

View file

@ -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];

View file

@ -142,8 +142,8 @@ public:
Dodecahedron,
Icosahedron,
Torus, // not yet implemented
Cone, // not yet implemented
Cylinder, // not yet implemented
Cone,
Cylinder,
NUM_SHAPES,
};

View file

@ -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 };

View file

@ -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) {

View 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);
}
}

View 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

View file

@ -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" };

View file

@ -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;

View file

@ -63,6 +63,4 @@ void EngineStats::run(const RenderContextPointer& renderContext) {
config->frameSetPipelineCount = _gpuStats._PSNumSetPipelines;
config->frameSetInputFormatCount = _gpuStats._ISNumFormatChanges;
config->emitDirty();
}

View file

@ -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()));
}
}
}

View file

@ -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 {

View file

@ -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>

View file

@ -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);
}
}

View file

@ -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)) {

View file

@ -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()

View file

@ -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;
}

View file

@ -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;

View file

@ -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

View file

@ -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>

View file

@ -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);

View 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);

View 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);

View file

@ -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

View file

@ -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;

View file

@ -172,4 +172,4 @@ input[type=radio]:active + label > span > span{
}
.blueButton:disabled {
background-image: linear-gradient(#FFFFFF, #AFAFAF);
}
}

View file

@ -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,

View file

@ -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">

View file

@ -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);
});
}

View file

@ -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 {

File diff suppressed because one or more lines are too long

View 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 = "&#176;";
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;
}
};

File diff suppressed because one or more lines are too long

View file

@ -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>

View file

@ -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