Merge branch 'master' of github.com:highfidelity/hifi into controller-ui

This commit is contained in:
Dante Ruiz 2017-06-15 16:24:59 +01:00
commit d65c0dfbac
99 changed files with 1795 additions and 1124 deletions

View file

@ -47,7 +47,7 @@ void OctreeInboundPacketProcessor::resetStats() {
_singleSenderStats.clear();
}
unsigned long OctreeInboundPacketProcessor::getMaxWait() const {
uint32_t OctreeInboundPacketProcessor::getMaxWait() const {
// calculate time until next sendNackPackets()
quint64 nextNackTime = _lastNackTime + TOO_LONG_SINCE_LAST_NACK;
quint64 now = usecTimestampNow();

View file

@ -80,7 +80,7 @@ protected:
virtual void processPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) override;
virtual unsigned long getMaxWait() const override;
virtual uint32_t getMaxWait() const override;
virtual void preProcess() override;
virtual void midProcess() override;

View file

@ -223,6 +223,14 @@ $(document).ready(function(){
// set focus to the first input in the new row
$target.closest('table').find('tr.inputs input:first').focus();
}
var tableRows = sibling.parent();
var tableBody = tableRows.parent();
// if theres no more siblings, we should jump to a new row
if (sibling.next().length == 0 && tableRows.nextAll().length == 1) {
tableBody.find("." + Settings.ADD_ROW_BUTTON_CLASS).click();
}
}
} else if ($target.is('input')) {

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

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; }
@ -129,7 +128,7 @@ Rectangle {
pal.sendToScript({method: 'refreshNearby', params: params});
}
Item {
Rectangle {
id: palTabContainer;
// Anchors
anchors {
@ -138,6 +137,7 @@ Rectangle {
left: parent.left;
right: parent.right;
}
color: "white";
Rectangle {
id: tabSelectorContainer;
// Anchors
@ -1043,7 +1043,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

@ -115,6 +115,7 @@
#include <render/RenderFetchCullSortTask.h>
#include <RenderDeferredTask.h>
#include <RenderForwardTask.h>
#include <RenderViewTask.h>
#include <ResourceCache.h>
#include <ResourceRequest.h>
#include <SandboxUtils.h>
@ -1722,6 +1723,10 @@ void Application::cleanupBeforeQuit() {
// Cleanup all overlays after the scripts, as scripts might add more
_overlays.cleanupAllOverlays();
// The cleanup process enqueues the transactions but does not process them. Calling this here will force the actual
// removal of the items.
// See https://highfidelity.fogbugz.com/f/cases/5328
_main3DScene->processTransactionQueue();
// first stop all timers directly or by invokeMethod
// depending on what thread they run in
@ -1868,15 +1873,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);
@ -5078,7 +5077,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

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

@ -1961,6 +1961,32 @@ void MyAvatar::updateOrientation(float deltaTime) {
totalBodyYaw += (speedFactor * deltaAngle * (180.0f / PI));
}
// Use head/HMD roll to turn while walking or flying.
if (qApp->isHMDMode() && _hmdRollControlEnabled) {
// Turn with head roll.
const float MIN_CONTROL_SPEED = 0.01f;
float speed = glm::length(getVelocity());
if (speed >= MIN_CONTROL_SPEED) {
// Feather turn when stopping moving.
float speedFactor;
if (getDriveKey(TRANSLATE_Z) != 0.0f || _lastDrivenSpeed == 0.0f) {
_lastDrivenSpeed = speed;
speedFactor = 1.0f;
} else {
speedFactor = glm::min(speed / _lastDrivenSpeed, 1.0f);
}
float direction = glm::dot(getVelocity(), getRotation() * Vectors::UNIT_NEG_Z) > 0.0f ? 1.0f : -1.0f;
float rollAngle = glm::degrees(asinf(glm::dot(IDENTITY_UP, _hmdSensorOrientation * IDENTITY_RIGHT)));
float rollSign = rollAngle < 0.0f ? -1.0f : 1.0f;
rollAngle = fabsf(rollAngle);
rollAngle = rollAngle > _hmdRollControlDeadZone ? rollSign * (rollAngle - _hmdRollControlDeadZone) : 0.0f;
totalBodyYaw += speedFactor * direction * rollAngle * deltaTime * _hmdRollControlRate;
}
}
// update body orientation by movement inputs
glm::quat initialOrientation = getOrientationOutbound();
setOrientation(getOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, totalBodyYaw, 0.0f))));

View file

@ -132,6 +132,10 @@ class MyAvatar : public Avatar {
Q_PROPERTY(bool characterControllerEnabled READ getCharacterControllerEnabled WRITE setCharacterControllerEnabled)
Q_PROPERTY(bool useAdvancedMovementControls READ useAdvancedMovementControls WRITE setUseAdvancedMovementControls)
Q_PROPERTY(bool hmdRollControlEnabled READ getHMDRollControlEnabled WRITE setHMDRollControlEnabled)
Q_PROPERTY(float hmdRollControlDeadZone READ getHMDRollControlDeadZone WRITE setHMDRollControlDeadZone)
Q_PROPERTY(float hmdRollControlRate READ getHMDRollControlRate WRITE setHMDRollControlRate)
public:
enum DriveKeys {
TRANSLATE_X = 0,
@ -337,6 +341,13 @@ public:
void setUseAdvancedMovementControls(bool useAdvancedMovementControls)
{ _useAdvancedMovementControls.set(useAdvancedMovementControls); }
void setHMDRollControlEnabled(bool value) { _hmdRollControlEnabled = value; }
bool getHMDRollControlEnabled() const { return _hmdRollControlEnabled; }
void setHMDRollControlDeadZone(float value) { _hmdRollControlDeadZone = value; }
float getHMDRollControlDeadZone() const { return _hmdRollControlDeadZone; }
void setHMDRollControlRate(float value) { _hmdRollControlRate = value; }
float getHMDRollControlRate() const { return _hmdRollControlRate; }
// get/set avatar data
void saveData();
void loadData();
@ -687,6 +698,13 @@ private:
bool _useSnapTurn { true };
bool _clearOverlayWhenMoving { true };
const float ROLL_CONTROL_DEAD_ZONE_DEFAULT = 8.0f; // deg
const float ROLL_CONTROL_RATE_DEFAULT = 2.5f; // deg/sec/deg
bool _hmdRollControlEnabled { true };
float _hmdRollControlDeadZone { ROLL_CONTROL_DEAD_ZONE_DEFAULT };
float _hmdRollControlRate { ROLL_CONTROL_RATE_DEFAULT };
float _lastDrivenSpeed { 0.0f };
// working copies -- see AvatarData for thread-safe _sensorToWorldMatrixCache, used for outward facing access
glm::mat4 _sensorToWorldMatrix { glm::mat4() };

View file

@ -184,6 +184,8 @@ AudioClient::AudioClient() :
checkDevices();
});
});
const unsigned long DEVICE_CHECK_INTERVAL_MSECS = 2 * 1000;
_checkDevicesTimer->start(DEVICE_CHECK_INTERVAL_MSECS);
configureReverb();

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

@ -584,7 +584,7 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t
return matchingNode;
} else {
// we didn't have this node, so add them
Node* newNode = new Node(uuid, nodeType, publicSocket, localSocket, permissions, connectionSecret, this);
Node* newNode = new Node(uuid, nodeType, publicSocket, localSocket, permissions, connectionSecret);
if (nodeType == NodeType::AudioMixer) {
LimitedNodeList::flagTimeForConnectionStep(LimitedNodeList::AddedAudioMixer);
@ -617,24 +617,28 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t
}
// insert the new node and release our read lock
_nodeHash.insert(UUIDNodePair(newNode->getUUID(), newNodePointer));
_nodeHash.emplace(newNode->getUUID(), newNodePointer);
readLocker.unlock();
qCDebug(networking) << "Added" << *newNode;
auto weakPtr = newNodePointer.toWeakRef(); // We don't want the lambdas to hold a strong ref
emit nodeAdded(newNodePointer);
if (newNodePointer->getActiveSocket()) {
emit nodeActivated(newNodePointer);
} else {
connect(newNodePointer.data(), &NetworkPeer::socketActivated, this, [=] {
emit nodeActivated(newNodePointer);
disconnect(newNodePointer.data(), &NetworkPeer::socketActivated, this, 0);
connect(newNodePointer.data(), &NetworkPeer::socketActivated, this, [this, weakPtr] {
auto sharedPtr = weakPtr.lock();
if (sharedPtr) {
emit nodeActivated(sharedPtr);
disconnect(sharedPtr.data(), &NetworkPeer::socketActivated, this, 0);
}
});
}
// Signal when a socket changes, so we can start the hole punch over.
auto weakPtr = newNodePointer.toWeakRef(); // We don't want the lambda to hold a strong ref
connect(newNodePointer.data(), &NetworkPeer::socketUpdated, this, [=] {
connect(newNodePointer.data(), &NetworkPeer::socketUpdated, this, [this, weakPtr] {
emit nodeSocketUpdated(weakPtr);
});

View file

@ -40,7 +40,7 @@ public:
Node(const QUuid& uuid, NodeType_t type,
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket,
const NodePermissions& permissions, const QUuid& connectionSecret = QUuid(),
QObject* parent = 0);
QObject* parent = nullptr);
bool operator==(const Node& otherNode) const { return _uuid == otherNode._uuid; }
bool operator!=(const Node& otherNode) const { return !(*this == otherNode); }

View file

@ -20,6 +20,8 @@
class ReceivedPacketProcessor : public GenericThread {
Q_OBJECT
public:
static const uint64_t MAX_WAIT_TIME { 100 }; // Max wait time in ms
ReceivedPacketProcessor();
/// Add packet from network receive thread to the processing queue.
@ -63,8 +65,8 @@ protected:
/// Implements generic processing behavior for this thread.
virtual bool process() override;
/// Determines the timeout of the wait when there are no packets to process. Default value means no timeout
virtual unsigned long getMaxWait() const { return ULONG_MAX; }
/// Determines the timeout of the wait when there are no packets to process. Default value is 100ms to allow for regular event processing.
virtual uint32_t getMaxWait() const { return MAX_WAIT_TIME; }
/// Override to do work before the packets processing loop. Default does nothing.
virtual void preProcess() { }

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

@ -35,14 +35,13 @@ void JurisdictionListener::nodeKilled(SharedNodePointer node) {
}
bool JurisdictionListener::queueJurisdictionRequest() {
auto packet = NLPacket::create(PacketType::JurisdictionRequest, 0);
auto nodeList = DependencyManager::get<NodeList>();
int nodeCount = 0;
nodeList->eachNode([&](const SharedNodePointer& node) {
if (node->getType() == getNodeType() && node->getActiveSocket()) {
auto packet = NLPacket::create(PacketType::JurisdictionRequest, 0);
_packetSender.queuePacketForSending(node, std::move(packet));
nodeCount++;
}

View file

@ -41,8 +41,6 @@ bool JurisdictionSender::process() {
// call our ReceivedPacketProcessor base class process so we'll get any pending packets
if (continueProcessing && (continueProcessing = ReceivedPacketProcessor::process())) {
auto packet = (_jurisdictionMap) ? _jurisdictionMap->packIntoPacket()
: JurisdictionMap::packEmptyJurisdictionIntoMessage(getNodeType());
int nodeCount = 0;
lockRequestingNodes();
@ -53,6 +51,8 @@ bool JurisdictionSender::process() {
SharedNodePointer node = DependencyManager::get<NodeList>()->nodeWithUUID(nodeUUID);
if (node && node->getActiveSocket()) {
auto packet = (_jurisdictionMap) ? _jurisdictionMap->packIntoPacket()
: JurisdictionMap::packEmptyJurisdictionIntoMessage(getNodeType());
_packetSender.queuePacketForSending(node, std::move(packet));
nodeCount++;
}

View file

@ -19,7 +19,6 @@
#include "AntialiasingEffect.h"
#include "StencilMaskPass.h"
#include "TextureCache.h"
#include "FramebufferCache.h"
#include "DependencyManager.h"
#include "ViewFrustum.h"
#include "GeometryCache.h"
@ -40,9 +39,9 @@ Antialiasing::~Antialiasing() {
}
}
const gpu::PipelinePointer& Antialiasing::getAntialiasingPipeline() {
int width = DependencyManager::get<FramebufferCache>()->getFrameBufferSize().width();
int height = DependencyManager::get<FramebufferCache>()->getFrameBufferSize().height();
const gpu::PipelinePointer& Antialiasing::getAntialiasingPipeline(RenderArgs* args) {
int width = args->_viewport.z;
int height = args->_viewport.w;
if (_antialiasingBuffer && _antialiasingBuffer->getSize() != uvec2(width, height)) {
_antialiasingBuffer.reset();
@ -51,7 +50,7 @@ const gpu::PipelinePointer& Antialiasing::getAntialiasingPipeline() {
if (!_antialiasingBuffer) {
// Link the antialiasing FBO to texture
_antialiasingBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("antialiasing"));
auto format = gpu::Element::COLOR_SRGBA_32; // DependencyManager::get<FramebufferCache>()->getLightingTexture()->getTexelFormat();
auto format = gpu::Element::COLOR_SRGBA_32;
auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT);
_antialiasingTexture = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, defaultSampler);
_antialiasingBuffer->setRenderBuffer(0, _antialiasingTexture);
@ -110,19 +109,13 @@ 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);
// FIXME: NEED to simplify that code to avoid all the GeometryCahce call, this is purely pixel manipulation
auto framebufferCache = DependencyManager::get<FramebufferCache>();
QSize framebufferSize = framebufferCache->getFrameBufferSize();
float fbWidth = framebufferSize.width();
float fbHeight = framebufferSize.height();
float fbWidth = renderContext->args->_viewport.z;
float fbHeight = renderContext->args->_viewport.w;
// float sMin = args->_viewport.x / fbWidth;
// float sWidth = args->_viewport.z / fbWidth;
// float tMin = args->_viewport.y / fbHeight;
@ -137,10 +130,10 @@ void Antialiasing::run(const render::RenderContextPointer& renderContext, const
batch.setModelTransform(Transform());
// FXAA step
getAntialiasingPipeline();
auto pipeline = getAntialiasingPipeline(renderContext->args);
batch.setResourceTexture(0, sourceBuffer->getRenderBuffer(0));
batch.setFramebuffer(_antialiasingBuffer);
batch.setPipeline(getAntialiasingPipeline());
batch.setPipeline(pipeline);
// initialize the view-space unpacking uniforms using frustum data
float left, right, bottom, top, nearVal, farVal;

View file

@ -33,7 +33,7 @@ public:
void configure(const Config& config) {}
void run(const render::RenderContextPointer& renderContext, const gpu::FramebufferPointer& sourceBuffer);
const gpu::PipelinePointer& getAntialiasingPipeline();
const gpu::PipelinePointer& getAntialiasingPipeline(RenderArgs* args);
const gpu::PipelinePointer& getBlendPipeline();
private:

View file

@ -19,7 +19,6 @@
#include <ViewFrustum.h>
#include "GeometryCache.h"
#include "FramebufferCache.h"
#include "TextureCache.h"
#include "DeferredLightingEffect.h"
@ -410,7 +409,6 @@ void DebugDeferredBuffer::run(const RenderContextPointer& renderContext, const I
batch.setViewportTransform(args->_viewport);
const auto geometryBuffer = DependencyManager::get<GeometryCache>();
const auto framebufferCache = DependencyManager::get<FramebufferCache>();
const auto textureCache = DependencyManager::get<TextureCache>();
glm::mat4 projMat;

View file

@ -418,10 +418,7 @@ model::MeshPointer DeferredLightingEffect::getSpotLightMesh() {
}
void PreparePrimaryFramebuffer::run(const RenderContextPointer& renderContext, gpu::FramebufferPointer& primaryFramebuffer) {
auto framebufferCache = DependencyManager::get<FramebufferCache>();
auto framebufferSize = framebufferCache->getFrameBufferSize();
glm::uvec2 frameSize(framebufferSize.width(), framebufferSize.height());
glm::uvec2 frameSize(renderContext->args->_viewport.z, renderContext->args->_viewport.w);
// Resizing framebuffers instead of re-building them seems to cause issues with threaded
// rendering
@ -504,10 +501,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

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

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

@ -10,6 +10,7 @@
//
#include <QDebug>
#include <QtCore/QCoreApplication>
#include "GenericThread.h"
@ -73,6 +74,7 @@ void GenericThread::threadRoutine() {
}
while (!_stopThread) {
QCoreApplication::processEvents();
// override this function to do whatever your class actually does, return false to exit thread early
if (!process()) {

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

@ -141,15 +141,18 @@ QString ViveControllerManager::configurationLayout() {
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,17 @@
//
// hmdRollControlDisable.js
//
// Created by David Rowe on 4 Jun 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
//
var hmdRollControlEnabled = false;
//print("HMD roll control: " + hmdRollControlEnabled);
MyAvatar.hmdRollControlEnabled = hmdRollControlEnabled;
Script.stop();

View file

@ -0,0 +1,21 @@
//
// hmdRollControlEnable.js
//
// Created by David Rowe on 4 Jun 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
//
var hmdRollControlEnabled = true;
var hmdRollControlDeadZone = 8.0; // deg
var hmdRollControlRate = 2.5; // deg/sec/deg
//print("HMD roll control: " + hmdRollControlEnabled + ", " + hmdRollControlDeadZone + ", " + hmdRollControlRate);
MyAvatar.hmdRollControlEnabled = hmdRollControlEnabled;
MyAvatar.hmdRollControlDeadZone = hmdRollControlDeadZone;
MyAvatar.hmdRollControlRate = hmdRollControlRate;
Script.stop();

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 },
@ -1373,7 +1353,7 @@ function parentSelectedEntities() {
}
});
if(parentCheck) {
if (parentCheck) {
Window.notify("Entities parented");
}else {
Window.notify("Entities are already parented to last");
@ -1575,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);
}
@ -1907,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);
@ -2203,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
@ -2212,6 +2196,7 @@ function selectParticleEntity(entityID) {
selectedParticleEntity = entityID;
particleExplorerTool.setActiveParticleEntity(entityID);
particleExplorerTool.webView.emitScriptEvent(JSON.stringify(particleData));
// Switch to particle explorer
@ -2229,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;
@ -2250,4 +2235,3 @@ entityListTool.webView.webEventReceived.connect(function (data) {
});
}()); // END LOCAL_SCOPE

View file

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

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

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

View file

@ -4,23 +4,22 @@
// Created by Eric Levin on 2/15/16
// Copyright 2016 High Fidelity, Inc.
// Adds particleExplorer tool to the edit panel when a user selects a particle entity from the edit tool window
// 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*/
/* global window, alert, ParticleExplorerTool, EventBridge, dat, listenForSettingsUpdates,createVec3Folder,createQuatFolder,writeVec3ToInterface,writeDataToInterface*/
var PARTICLE_EXPLORER_HTML_URL = Script.resolvePath('particleExplorer.html');
ParticleExplorerTool = function() {
var that = {};
that.createWebView = function() {
that.webView = Tablet.getTablet("com.highfidelity.interface.tablet.system");
that.webView.setVisible = function(value) {};
that.webView.webEventReceived.connect(that.webEventReceived);
that.webView.webEventReceived.connect(that.webEventReceived);
}
that.destroyWebView = function() {
@ -38,6 +37,9 @@ ParticleExplorerTool = function() {
that.webEventReceived = function(data) {
var data = JSON.parse(data);
if (data.messageType === "settings_update") {
if (data.updatedSettings.emitOrientation) {
data.updatedSettings.emitOrientation = Quat.fromVec3Degrees(data.updatedSettings.emitOrientation);
}
Entities.editEntity(that.activeParticleEntity, data.updatedSettings);
}
}

View file

@ -101,6 +101,10 @@ function getApplicationDataDirectory() {
return path.join(getRootHifiDataDirectory(), '/Server Console');
}
// Update lock filepath
const UPDATER_LOCK_FILENAME = ".updating";
const UPDATER_LOCK_FULL_PATH = getRootHifiDataDirectory() + "/" + UPDATER_LOCK_FILENAME;
// Configure log
global.log = require('electron-log');
const logFile = getApplicationDataDirectory() + '/log.txt';
@ -630,11 +634,22 @@ function checkNewContent() {
userConfig.save(configPath);
}
});
} else if (fs.existsSync(UPDATER_LOCK_FULL_PATH)) {
backupResourceDirectoriesAndRestart();
}
}
});
}
function removeIncompleteUpdate(acResourceDirectory, dsResourceDirectory) {
if (fs.existsSync(UPDATER_LOCK_FULL_PATH)) {
log.debug('Removing incomplete content update files before copying new update');
fs.emptyDirSync(dsResourceDirectory);
fs.emptyDirSync(acResourceDirectory);
} else {
fs.ensureFileSync(UPDATER_LOCK_FULL_PATH);
}
}
function maybeInstallDefaultContentSet(onComplete) {
// Check for existing data
@ -673,7 +688,11 @@ function maybeInstallDefaultContentSet(onComplete) {
}
log.debug("Found contentPath:" + argv.contentPath);
if (argv.contentPath) {
// check if we're updating a data folder whose update is incomplete
removeIncompleteUpdate(acResourceDirectory, dsResourceDirectory);
fs.copy(argv.contentPath, getRootHifiDataDirectory(), function (err) {
if (err) {
log.debug('Could not copy home content: ' + err);
@ -682,12 +701,12 @@ function maybeInstallDefaultContentSet(onComplete) {
log.debug('Copied home content over to: ' + getRootHifiDataDirectory());
userConfig.set('homeContentLastModified', new Date());
userConfig.save(configPath);
fs.removeSync(UPDATER_LOCK_FULL_PATH);
onComplete();
});
return;
}
// Show popup
var window = new BrowserWindow({
icon: appIcon,
@ -718,6 +737,9 @@ function maybeInstallDefaultContentSet(onComplete) {
var aborted = false;
// check if we're updating a data folder whose update is incomplete
removeIncompleteUpdate(acResourceDirectory, dsResourceDirectory);
// Start downloading content set
var req = progress(request.get({
url: HOME_CONTENT_URL
@ -763,6 +785,7 @@ function maybeInstallDefaultContentSet(onComplete) {
log.debug("Finished unarchiving home content set");
userConfig.set('homeContentLastModified', new Date());
userConfig.save(configPath);
fs.removeSync(UPDATER_LOCK_FULL_PATH);
sendStateUpdate('complete');
});