diff --git a/unpublishedScripts/marketplace/spectator-camera/Spectator Camera Marketplace Image.jpg b/unpublishedScripts/marketplace/spectator-camera/Spectator Camera Marketplace Image.jpg new file mode 100644 index 0000000000..19696f88fb Binary files /dev/null and b/unpublishedScripts/marketplace/spectator-camera/Spectator Camera Marketplace Image.jpg differ diff --git a/unpublishedScripts/marketplace/spectator-camera/SpectatorCamera.qml b/unpublishedScripts/marketplace/spectator-camera/SpectatorCamera.qml new file mode 100644 index 0000000000..c1b62368a9 --- /dev/null +++ b/unpublishedScripts/marketplace/spectator-camera/SpectatorCamera.qml @@ -0,0 +1,418 @@ +// +// SpectatorCamera.qml +// qml/hifi +// +// Spectator Camera v2.0 +// +// Created by Zach Fox on 2018-04-18 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import Hifi 1.0 as Hifi +import QtQuick 2.7 +import "qrc:////qml//styles-uit" as HifiStylesUit +import "qrc:////qml//controls-uit" as HifiControlsUit +import "qrc:////qml//controls" as HifiControls +import "qrc:////qml//hifi" as Hifi + +Rectangle { + HifiStylesUit.HifiConstants { id: hifi; } + + id: root; + // Style + color: hifi.colors.baseGray; + + // The letterbox used for popup messages + Hifi.LetterboxMessage { + id: letterboxMessage; + z: 999; // Force the popup on top of everything else + } + function letterbox(headerGlyph, headerText, message) { + letterboxMessage.headerGlyph = headerGlyph; + letterboxMessage.headerText = headerText; + letterboxMessage.text = message; + letterboxMessage.visible = true; + letterboxMessage.popupRadius = 0; + } + + // + // TITLE BAR START + // + Item { + id: titleBarContainer; + // Size + width: root.width; + height: 50; + // Anchors + anchors.left: parent.left; + anchors.top: parent.top; + + // "Spectator" text + HifiStylesUit.RalewaySemiBold { + id: titleBarText; + text: "Spectator Camera"; + // Text size + size: hifi.fontSizes.overlayTitle; + // Anchors + anchors.fill: parent; + anchors.leftMargin: 16; + // Style + color: hifi.colors.lightGrayText; + // Alignment + horizontalAlignment: Text.AlignHLeft; + verticalAlignment: Text.AlignVCenter; + } + + // Separator + HifiControlsUit.Separator { + anchors.left: parent.left; + anchors.right: parent.right; + anchors.bottom: parent.bottom; + } + } + // + // TITLE BAR END + // + + // + // SPECTATOR APP DESCRIPTION START + // + Item { + id: spectatorDescriptionContainer; + // Size + width: root.width; + height: childrenRect.height; + // Anchors + anchors.left: parent.left; + anchors.top: titleBarContainer.bottom; + + // (i) Glyph + HifiStylesUit.HiFiGlyphs { + id: spectatorDescriptionGlyph; + text: hifi.glyphs.info; + // Size + width: 20; + height: parent.height; + size: 60; + // Anchors + anchors.left: parent.left; + anchors.leftMargin: 20; + anchors.top: parent.top; + anchors.topMargin: 0; + // Style + color: hifi.colors.lightGrayText; + horizontalAlignment: Text.AlignHLeft; + verticalAlignment: Text.AlignTop; + } + + // "Spectator" app description text + HifiStylesUit.RalewayLight { + id: spectatorDescriptionText; + text: "Spectator lets you change what your monitor displays while you're using a VR headset. Use Spectator when streaming and recording video."; + // Text size + size: 14; + // Size + width: 350; + height: paintedHeight; + // Anchors + anchors.top: parent.top; + anchors.topMargin: 15; + anchors.left: spectatorDescriptionGlyph.right; + anchors.leftMargin: 40; + // Style + color: hifi.colors.lightGrayText; + wrapMode: Text.Wrap; + // Alignment + horizontalAlignment: Text.AlignHLeft; + verticalAlignment: Text.AlignVCenter; + } + + // "Learn More" text + HifiStylesUit.RalewayRegular { + id: spectatorLearnMoreText; + text: "Learn More About Spectator"; + // Text size + size: 14; + // Size + width: paintedWidth; + height: paintedHeight; + // Anchors + anchors.top: spectatorDescriptionText.bottom; + anchors.topMargin: 10; + anchors.left: spectatorDescriptionText.anchors.left; + anchors.leftMargin: spectatorDescriptionText.anchors.leftMargin; + // Style + color: hifi.colors.blueAccent; + wrapMode: Text.WordWrap; + font.underline: true; + // Alignment + horizontalAlignment: Text.AlignHLeft; + verticalAlignment: Text.AlignVCenter; + + MouseArea { + anchors.fill: parent; + hoverEnabled: enabled; + onClicked: { + letterbox(hifi.glyphs.question, + "Spectator Camera", + "By default, your monitor shows a preview of what you're seeing in VR. " + + "Using the Spectator Camera app, your monitor can display the view " + + "from a virtual hand-held camera - perfect for taking selfies or filming " + + "your friends!
" + + "

Streaming and Recording

" + + "We recommend OBS for streaming and recording the contents of your monitor to services like " + + "Twitch, YouTube Live, and Facebook Live.

" + + "To get started using OBS, click this link now. The page will open in an external browser:
" + + 'OBS Official Overview Guide'); + } + onEntered: parent.color = hifi.colors.blueHighlight; + onExited: parent.color = hifi.colors.blueAccent; + } + } + + // Separator + HifiControlsUit.Separator { + anchors.left: parent.left; + anchors.right: parent.right; + anchors.top: spectatorLearnMoreText.bottom; + anchors.topMargin: spectatorDescriptionText.anchors.topMargin; + } + } + // + // SPECTATOR APP DESCRIPTION END + // + + Item { + visible: !HMD.active; + height: root.height - spectatorDescriptionContainer.height - titleBarContainer.height; + anchors.top: spectatorDescriptionContainer.bottom; + anchors.topMargin: 20; + anchors.left: parent.left; + anchors.leftMargin: 25; + anchors.right: parent.right; + anchors.rightMargin: anchors.leftMargin; + + HifiStylesUit.FiraSansRegular { + text: "Spectator Camera only works in VR mode.\nPlease put on your headset."; + size: 18; + color: hifi.colors.lightGrayText; + anchors.fill: parent; + horizontalAlignment: Text.AlignHCenter; + verticalAlignment: Text.AlignVCenter; + wrapMode: Text.Wrap; + } + } + + // + // SPECTATOR CONTROLS START + // + Item { + id: spectatorControlsContainer; + visible: HMD.active; + // Size + height: root.height - spectatorDescriptionContainer.height - titleBarContainer.height; + // Anchors + anchors.top: spectatorDescriptionContainer.bottom; + anchors.topMargin: 20; + anchors.left: parent.left; + anchors.leftMargin: 25; + anchors.right: parent.right; + anchors.rightMargin: anchors.leftMargin; + + // "Camera On" Button + HifiControlsUit.Button { + property bool camIsOn: false; + + id: cameraToggleButton; + color: camIsOn ? hifi.buttons.red : hifi.buttons.blue; + colorScheme: hifi.colorSchemes.dark; + anchors.left: parent.left; + anchors.top: parent.top; + anchors.right: parent.right; + height: 40; + text: camIsOn ? "TURN OFF SPECTATOR CAMERA" : "TURN ON SPECTATOR CAMERA"; + onClicked: { + camIsOn = !camIsOn; + sendToScript({method: (camIsOn ? 'spectatorCameraOn' : 'spectatorCameraOff')}); + spectatorCameraPreview.ready = camIsOn; + } + } + + // Instructions or Preview + Rectangle { + id: spectatorCameraImageContainer; + anchors.left: parent.left; + anchors.top: cameraToggleButton.bottom; + anchors.topMargin: 20; + anchors.right: parent.right; + height: 250; + color: cameraToggleButton.camIsOn ? "transparent" : "black"; + + AnimatedImage { + source: "static.gif" + visible: !cameraToggleButton.camIsOn; + anchors.fill: parent; + opacity: 0.15; + } + + // Instructions (visible when display texture isn't set) + HifiStylesUit.FiraSansRegular { + id: spectatorCameraInstructions; + text: "Turn on Spectator Camera for a preview\nof what your monitor shows."; + size: 16; + color: hifi.colors.lightGrayText; + visible: !cameraToggleButton.camIsOn; + anchors.fill: parent; + horizontalAlignment: Text.AlignHCenter; + verticalAlignment: Text.AlignVCenter; + } + + // Spectator Camera Preview + Hifi.ResourceImageItem { + id: spectatorCameraPreview; + visible: cameraToggleButton.camIsOn; + url: monitorShowsSwitch.checked ? "resource://spectatorCameraFrame" : "resource://hmdPreviewFrame"; + ready: cameraToggleButton.camIsOn; + mirrorVertically: true; + anchors.fill: parent; + onVisibleChanged: { + ready = cameraToggleButton.camIsOn; + update(); + } + } + } + + + // "Monitor Shows" Switch Label Glyph + HifiStylesUit.HiFiGlyphs { + id: monitorShowsSwitchLabelGlyph; + text: hifi.glyphs.screen; + size: 32; + color: hifi.colors.blueHighlight; + anchors.top: spectatorCameraImageContainer.bottom; + anchors.topMargin: 13; + anchors.left: parent.left; + } + // "Monitor Shows" Switch Label + HifiStylesUit.RalewayLight { + id: monitorShowsSwitchLabel; + text: "MONITOR SHOWS:"; + anchors.top: spectatorCameraImageContainer.bottom; + anchors.topMargin: 20; + anchors.left: monitorShowsSwitchLabelGlyph.right; + anchors.leftMargin: 6; + size: 16; + width: paintedWidth; + height: paintedHeight; + color: hifi.colors.lightGrayText; + verticalAlignment: Text.AlignVCenter; + } + // "Monitor Shows" Switch + HifiControlsUit.Switch { + id: monitorShowsSwitch; + height: 30; + anchors.left: parent.left; + anchors.right: parent.right; + anchors.top: monitorShowsSwitchLabel.bottom; + anchors.topMargin: 10; + labelTextOff: "HMD Preview"; + labelTextOn: "Camera View"; + labelGlyphOnText: hifi.glyphs.alert; + onCheckedChanged: { + sendToScript({method: 'setMonitorShowsCameraView', params: checked}); + } + } + + // "Switch View From Controller" Checkbox + HifiControlsUit.CheckBox { + id: switchViewFromControllerCheckBox; + colorScheme: hifi.colorSchemes.dark; + anchors.left: parent.left; + anchors.top: monitorShowsSwitch.bottom; + anchors.topMargin: 25; + text: ""; + boxSize: 24; + onClicked: { + sendToScript({method: 'changeSwitchViewFromControllerPreference', params: checked}); + } + } + + // "Take Snapshot" Checkbox + HifiControlsUit.CheckBox { + id: takeSnapshotFromControllerCheckBox; + colorScheme: hifi.colorSchemes.dark; + anchors.left: parent.left; + anchors.top: switchViewFromControllerCheckBox.bottom; + anchors.topMargin: 10; + text: ""; + boxSize: 24; + onClicked: { + sendToScript({method: 'changeTakeSnapshotFromControllerPreference', params: checked}); + } + } + } + // + // SPECTATOR CONTROLS END + // + + // + // FUNCTION DEFINITIONS START + // + // + // Function Name: fromScript() + // + // Relevant Variables: + // None + // + // Arguments: + // message: The message sent from the SpectatorCamera JavaScript. + // Messages are in format "{method, params}", like json-rpc. + // + // Description: + // Called when a message is received from spectatorCamera.js. + // + function fromScript(message) { + switch (message.method) { + case 'updateSpectatorCameraCheckbox': + cameraToggleButton.camIsOn = message.params; + break; + case 'updateMonitorShowsSwitch': + monitorShowsSwitch.checked = message.params; + break; + case 'updateControllerMappingCheckbox': + switchViewFromControllerCheckBox.checked = message.switchViewSetting; + switchViewFromControllerCheckBox.enabled = true; + takeSnapshotFromControllerCheckBox.checked = message.takeSnapshotSetting; + takeSnapshotFromControllerCheckBox.enabled = true; + + if (message.controller === "OculusTouch") { + switchViewFromControllerCheckBox.text = "Clicking Touch's Left Thumbstick Switches Monitor View"; + takeSnapshotFromControllerCheckBox.text = "Clicking Touch's Right Thumbstick Takes Snapshot"; + } else if (message.controller === "Vive") { + switchViewFromControllerCheckBox.text = "Clicking Left Thumb Pad Switches Monitor View"; + takeSnapshotFromControllerCheckBox.text = "Clicking Right Thumb Pad Takes Snapshot"; + } else { + switchViewFromControllerCheckBox.text = "Pressing Ctrl+0 Switches Monitor View"; + switchViewFromControllerCheckBox.checked = true; + switchViewFromControllerCheckBox.enabled = false; + takeSnapshotFromControllerCheckBox.visible = false; + } + break; + case 'showPreviewTextureNotInstructions': + console.log('showPreviewTextureNotInstructions recvd', JSON.stringify(message)); + spectatorCameraPreview.url = message.url; + spectatorCameraPreview.visible = message.setting; + break; + default: + console.log('Unrecognized message from spectatorCamera.js:', JSON.stringify(message)); + } + } + signal sendToScript(var message); + + // + // FUNCTION DEFINITIONS END + // +} diff --git a/unpublishedScripts/marketplace/spectator-camera/snap.wav b/unpublishedScripts/marketplace/spectator-camera/snap.wav new file mode 100644 index 0000000000..e5b86c0c71 Binary files /dev/null and b/unpublishedScripts/marketplace/spectator-camera/snap.wav differ diff --git a/unpublishedScripts/marketplace/spectator-camera/spectatorCamera.app.json b/unpublishedScripts/marketplace/spectator-camera/spectatorCamera.app.json new file mode 100644 index 0000000000..e71c657581 --- /dev/null +++ b/unpublishedScripts/marketplace/spectator-camera/spectatorCamera.app.json @@ -0,0 +1,4 @@ +{ + "scriptURL": "http://mpassets-staging.highfidelity.com/26156ea5-cdff-43c2-9581-d6b0fa5e00ef-v1/spectatorCamera.js", + "homeURL": "http://mpassets-staging.highfidelity.com/26156ea5-cdff-43c2-9581-d6b0fa5e00ef-v1/SpectatorCamera.qml" +} \ No newline at end of file diff --git a/unpublishedScripts/marketplace/spectator-camera/spectatorCamera.js b/unpublishedScripts/marketplace/spectator-camera/spectatorCamera.js index 730a5808b8..ce445a5f8e 100644 --- a/unpublishedScripts/marketplace/spectator-camera/spectatorCamera.js +++ b/unpublishedScripts/marketplace/spectator-camera/spectatorCamera.js @@ -150,17 +150,15 @@ // Relevant Variables: // -button: The tablet button. // -buttonName: The name of the button. - // -showSpectatorInDesktop: Set to "true" to show the "SPECTATOR" app in desktop mode. var button = false; var buttonName = "SPECTATOR"; - var showSpectatorInDesktop = false; - function addOrRemoveButton(isShuttingDown, isHMDMode) { + function addOrRemoveButton(isShuttingDown) { if (!tablet) { print("Warning in addOrRemoveButton(): 'tablet' undefined!"); return; } if (!button) { - if ((isHMDMode || showSpectatorInDesktop) && !isShuttingDown) { + if (!isShuttingDown) { button = tablet.addButton({ text: buttonName, icon: "icons/tablet-icons/spectator-i.svg", @@ -169,7 +167,7 @@ button.clicked.connect(onTabletButtonClicked); } } else if (button) { - if ((!isHMDMode && !showSpectatorInDesktop) || isShuttingDown) { + if (isShuttingDown) { button.clicked.disconnect(onTabletButtonClicked); tablet.removeButton(button); button = false; @@ -189,7 +187,7 @@ var tablet = null; function startup() { tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - addOrRemoveButton(false, HMD.active); + addOrRemoveButton(false); tablet.screenChanged.connect(onTabletScreenChanged); Window.domainChanged.connect(onDomainChanged); Window.geometryChanged.connect(resizeViewFinderOverlay); @@ -320,14 +318,14 @@ const SWITCH_VIEW_FROM_CONTROLLER_DEFAULT = false; var switchViewFromController = !!Settings.getValue('spectatorCamera/switchViewFromController', SWITCH_VIEW_FROM_CONTROLLER_DEFAULT); - function setControllerMappingStatus(status) { - if (!controllerMapping) { + function setSwitchViewControllerMappingStatus(status) { + if (!switchViewControllerMapping) { return; } if (status) { - controllerMapping.enable(); + switchViewControllerMapping.enable(); } else { - controllerMapping.disable(); + switchViewControllerMapping.disable(); } } function setSwitchViewFromController(setting) { @@ -335,9 +333,30 @@ return; } switchViewFromController = setting; - setControllerMappingStatus(switchViewFromController); + setSwitchViewControllerMappingStatus(switchViewFromController); Settings.setValue('spectatorCamera/switchViewFromController', setting); } + + const TAKE_SNAPSHOT_FROM_CONTROLLER_DEFAULT = false; + var takeSnapshotFromController = !!Settings.getValue('spectatorCamera/takeSnapshotFromController', TAKE_SNAPSHOT_FROM_CONTROLLER_DEFAULT); + function setTakeSnapshotControllerMappingStatus(status) { + if (!takeSnapshotControllerMapping) { + return; + } + if (status) { + takeSnapshotControllerMapping.enable(); + } else { + takeSnapshotControllerMapping.disable(); + } + } + function setTakeSnapshotFromController(setting) { + if (setting === takeSnapshotFromController) { + return; + } + takeSnapshotFromController = setting; + setTakeSnapshotControllerMappingStatus(takeSnapshotFromController); + Settings.setValue('spectatorCamera/takeSnapshotFromController', setting); + } // Function Name: registerButtonMappings() // @@ -345,11 +364,61 @@ // -Updates controller button mappings for Spectator Camera. // // Relevant Variables: - // -controllerMappingName: The name of the controller mapping. - // -controllerMapping: The controller mapping itself. + // -switchViewControllerMappingName: The name of the controller mapping. + // -switchViewControllerMapping: The controller mapping itself. + // -takeSnapshotControllerMappingName: The name of the controller mapping. + // -takeSnapshotControllerMapping: The controller mapping itself. // -controllerType: "OculusTouch", "Vive", "Other". - var controllerMappingName; - var controllerMapping; + var switchViewControllerMapping; + var switchViewControllerMappingName = 'Hifi-SpectatorCamera-Mapping-SwitchView'; + function registerSwitchViewControllerMapping() { + switchViewControllerMapping = Controller.newMapping(switchViewControllerMappingName); + if (controllerType === "OculusTouch") { + switchViewControllerMapping.from(Controller.Standard.LS).to(function (value) { + if (value === 1.0) { + setMonitorShowsCameraViewAndSendToQml(!monitorShowsCameraView); + } + return; + }); + } else if (controllerType === "Vive") { + switchViewControllerMapping.from(Controller.Standard.LeftPrimaryThumb).to(function (value) { + if (value === 1.0) { + setMonitorShowsCameraViewAndSendToQml(!monitorShowsCameraView); + } + return; + }); + } + } + var takeSnapshotControllerMapping; + var takeSnapshotControllerMappingName = 'Hifi-SpectatorCamera-Mapping-TakeSnapshot'; + function maybeTakeSnapshot() { + if (camera) { + Audio.playSound(SNAPSHOT_SOUND, { + position: { x: MyAvatar.position.x, y: MyAvatar.position.y, z: MyAvatar.position.z }, + localOnly: true, + volume: 1.0 + }); + Window.takeSecondaryCameraSnapshot(); + } + } + function registerTakeSnapshotControllerMapping() { + takeSnapshotControllerMapping = Controller.newMapping(takeSnapshotControllerMappingName); + if (controllerType === "OculusTouch") { + takeSnapshotControllerMapping.from(Controller.Standard.RS).to(function (value) { + if (value === 1.0) { + maybeTakeSnapshot(); + } + return; + }); + } else if (controllerType === "Vive") { + takeSnapshotControllerMapping.from(Controller.Standard.RightPrimaryThumb).to(function (value) { + if (value === 1.0) { + maybeTakeSnapshot(); + } + return; + }); + } + } var controllerType = "Other"; function registerButtonMappings() { var VRDevices = Controller.getDeviceNames().toString(); @@ -359,30 +428,32 @@ } else if (VRDevices.indexOf("OculusTouch") !== -1) { controllerType = "OculusTouch"; } else { - sendToQml({ method: 'updateControllerMappingCheckbox', setting: switchViewFromController, controller: controllerType }); + sendToQml({ + method: 'updateControllerMappingCheckbox', + switchViewSetting: switchViewFromController, + takeSnapshotSetting: takeSnapshotFromController, + controller: controllerType + }); return; // Neither Vive nor Touch detected } } - controllerMappingName = 'Hifi-SpectatorCamera-Mapping'; - controllerMapping = Controller.newMapping(controllerMappingName); - if (controllerType === "OculusTouch") { - controllerMapping.from(Controller.Standard.LS).to(function (value) { - if (value === 1.0) { - setMonitorShowsCameraViewAndSendToQml(!monitorShowsCameraView); - } - return; - }); - } else if (controllerType === "Vive") { - controllerMapping.from(Controller.Standard.LeftPrimaryThumb).to(function (value) { - if (value === 1.0) { - setMonitorShowsCameraViewAndSendToQml(!monitorShowsCameraView); - } - return; - }); + if (!switchViewControllerMapping) { + registerSwitchViewControllerMapping(); } - setControllerMappingStatus(switchViewFromController); - sendToQml({ method: 'updateControllerMappingCheckbox', setting: switchViewFromController, controller: controllerType }); + setSwitchViewControllerMappingStatus(switchViewFromController); + + if (!takeSnapshotControllerMapping) { + registerTakeSnapshotControllerMapping(); + } + setTakeSnapshotControllerMappingStatus(switchViewFromController); + + sendToQml({ + method: 'updateControllerMappingCheckbox', + switchViewSetting: switchViewFromController, + takeSnapshotSetting: takeSnapshotFromController, + controller: controllerType + }); } // Function Name: onTabletButtonClicked() @@ -393,7 +464,7 @@ // Relevant Variables: // -SPECTATOR_CAMERA_QML_SOURCE: The path to the SpectatorCamera QML // -onSpectatorCameraScreen: true/false depending on whether we're looking at the spectator camera app. - var SPECTATOR_CAMERA_QML_SOURCE = "hifi/SpectatorCamera.qml"; + var SPECTATOR_CAMERA_QML_SOURCE = Script.resolvePath("SpectatorCamera.qml"); var onSpectatorCameraScreen = false; function onTabletButtonClicked() { if (!tablet) { @@ -405,15 +476,6 @@ tablet.gotoHomeScreen(); } else { tablet.loadQMLSource(SPECTATOR_CAMERA_QML_SOURCE); - sendToQml({ method: 'updateSpectatorCameraCheckbox', params: !!camera }); - sendToQml({ method: 'updateMonitorShowsSwitch', params: monitorShowsCameraView }); - if (!controllerMapping) { - registerButtonMappings(); - } else { - sendToQml({ method: 'updateControllerMappingCheckbox', setting: switchViewFromController, controller: controllerType }); - } - Menu.setIsOptionChecked("Disable Preview", false); - Menu.setIsOptionChecked("Mono Preview", true); } } @@ -429,6 +491,23 @@ if (button) { button.editProperties({ isActive: onSpectatorCameraScreen || camera }); } + + if (onSpectatorCameraScreen) { + sendToQml({ method: 'updateSpectatorCameraCheckbox', params: !!camera }); + sendToQml({ method: 'updateMonitorShowsSwitch', params: monitorShowsCameraView }); + if (!switchViewControllerMapping) { + registerButtonMappings(); + } else { + sendToQml({ + method: 'updateControllerMappingCheckbox', + switchViewSetting: switchViewFromController, + takeSnapshotSetting: takeSnapshotFromController, + controller: controllerType + }); + } + Menu.setIsOptionChecked("Disable Preview", false); + Menu.setIsOptionChecked("Mono Preview", true); + } } // Function Name: sendToQml() @@ -459,6 +538,9 @@ case 'changeSwitchViewFromControllerPreference': setSwitchViewFromController(message.params); break; + case 'changeTakeSnapshotFromControllerPreference': + setTakeSnapshotFromController(message.params); + break; default: print('Unrecognized message from SpectatorCamera.qml:', JSON.stringify(message)); } @@ -469,12 +551,11 @@ // Description: // -Called from C++ when HMD mode is changed. The argument "isHMDMode" is true if HMD is on; false otherwise. function onHMDChanged(isHMDMode) { - if (!controllerMapping) { + if (!switchViewControllerMapping || !takeSnapshotControllerMapping) { registerButtonMappings(); } setDisplay(monitorShowsCameraView); - addOrRemoveButton(false, isHMDMode); - if (!isHMDMode && !showSpectatorInDesktop) { + if (!isHMDMode) { spectatorCameraOff(); } } @@ -487,7 +568,7 @@ spectatorCameraOff(); Window.domainChanged.disconnect(onDomainChanged); Window.geometryChanged.disconnect(resizeViewFinderOverlay); - addOrRemoveButton(true, HMD.active); + addOrRemoveButton(true); if (tablet) { tablet.screenChanged.disconnect(onTabletScreenChanged); if (onSpectatorCameraScreen) { @@ -496,8 +577,11 @@ } HMD.displayModeChanged.disconnect(onHMDChanged); Controller.keyPressEvent.disconnect(keyPressEvent); - if (controllerMapping) { - controllerMapping.disable(); + if (switchViewControllerMapping) { + switchViewControllerMapping.disable(); + } + if (takeSnapshotControllerMapping) { + takeSnapshotControllerMapping.disable(); } } @@ -511,6 +595,7 @@ // These functions will be called when the script is loaded. var CAMERA_ON_SOUND = SoundCache.getSound(Script.resolvePath("cameraOn.wav")); + var SNAPSHOT_SOUND = SoundCache.getSound(Script.resourcesPath() + "sounds/snapshot/snap.wav"); startup(); Script.scriptEnding.connect(shutdown); diff --git a/unpublishedScripts/marketplace/spectator-camera/static.gif b/unpublishedScripts/marketplace/spectator-camera/static.gif new file mode 100644 index 0000000000..fbe46f48e6 Binary files /dev/null and b/unpublishedScripts/marketplace/spectator-camera/static.gif differ