diff --git a/interface/resources/qml/hifi/SpectatorCamera.qml b/interface/resources/qml/hifi/SpectatorCamera.qml index fa19a4b371..544fc72927 100644 --- a/interface/resources/qml/hifi/SpectatorCamera.qml +++ b/interface/resources/qml/hifi/SpectatorCamera.qml @@ -242,7 +242,7 @@ Rectangle { labelTextOn: "Camera View"; labelGlyphOnText: hifi.glyphs.alert; onCheckedChanged: { - sendToScript({method: (checked ? 'showCameraViewOnMonitor' : 'showHmdPreviewOnMonitor')}); + sendToScript({method: 'setMonitorShowsCameraView', params: checked}); } } diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 1e14c24da3..38f467f22b 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -284,6 +284,11 @@ void WindowScriptingInterface::copyToClipboard(const QString& text) { QApplication::clipboard()->setText(text); } + +bool WindowScriptingInterface::setDisplayTexture(const QString& name) { + return qApp->getActiveDisplayPlugin()->setDisplayTexture(name); // Plugins that don't know how, answer false. +} + void WindowScriptingInterface::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio) { qApp->takeSnapshot(notify, includeAnimated, aspectRatio); } diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 2b1e48d918..4fb4829636 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -62,6 +62,7 @@ public slots: void displayAnnouncement(const QString& message); void shareSnapshot(const QString& path, const QUrl& href = QUrl("")); bool isPhysicsEnabled(); + bool setDisplayTexture(const QString& name); int openMessageBox(QString title, QString text, int buttons, int defaultButton); void updateMessageBox(int id, QString title, QString text, int buttons, int defaultButton); diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index bfd158ffb5..ba8842c2ec 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -496,6 +496,17 @@ void OpenGLDisplayPlugin::submitFrame(const gpu::FramePointer& newFrame) { _newFrameQueue.push(newFrame); }); } +void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer texture, glm::ivec4 viewport, const glm::ivec4 scissor) { + batch.enableStereo(false); + batch.resetViewTransform(); + batch.setFramebuffer(gpu::FramebufferPointer()); + batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0)); + batch.setStateScissorRect(scissor); + batch.setViewportTransform(viewport); + batch.setResourceTexture(0, texture); + batch.setPipeline(_presentPipeline); + batch.draw(gpu::TRIANGLE_STRIP, 4); +} void OpenGLDisplayPlugin::updateFrameData() { PROFILE_RANGE(render, __FUNCTION__) @@ -605,14 +616,11 @@ void OpenGLDisplayPlugin::compositeLayers() { void OpenGLDisplayPlugin::internalPresent() { render([&](gpu::Batch& batch) { - batch.enableStereo(false); - batch.resetViewTransform(); - batch.setFramebuffer(gpu::FramebufferPointer()); - batch.setViewportTransform(ivec4(uvec2(0), getSurfacePixels())); - batch.setResourceTexture(0, _compositeFramebuffer->getRenderBuffer(0)); - batch.setPipeline(_presentPipeline); - batch.draw(gpu::TRIANGLE_STRIP, 4); - }); + // Note: _displayTexture must currently be the same size as the display. + uvec2 dims = _displayTexture ? uvec2(_displayTexture->getDimensions()) : getSurfacePixels(); + auto viewport = ivec4(uvec2(0), dims); + renderFromTexture(batch, _displayTexture ? _displayTexture : _compositeFramebuffer->getRenderBuffer(0), viewport, viewport); + }); swapBuffers(); _presentRate.increment(); } @@ -694,6 +702,21 @@ void OpenGLDisplayPlugin::withMainThreadContext(std::function f) const { _container->makeRenderingContextCurrent(); } +bool OpenGLDisplayPlugin::setDisplayTexture(const QString& name) { + // Note: it is the caller's responsibility to keep the network texture in cache. + if (name.isEmpty()) { + _displayTexture.reset(); + return true; + } + auto textureCache = DependencyManager::get(); + auto displayNetworkTexture = textureCache->getTexture(name); + if (!displayNetworkTexture) { + return false; + } + _displayTexture = displayNetworkTexture->getGPUTexture(); + return !!_displayTexture; +} + QImage OpenGLDisplayPlugin::getScreenshot(float aspectRatio) const { auto size = _compositeFramebuffer->getSize(); if (isHmd()) { diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index 10a7558398..cf874fb721 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -57,6 +57,7 @@ public: return getSurfaceSize(); } + virtual bool setDisplayTexture(const QString& name) override; QImage getScreenshot(float aspectRatio = 0.0f) const override; float presentRate() const override; @@ -109,6 +110,7 @@ protected: // Plugin specific functionality to send the composed scene to the output window or device virtual void internalPresent(); + void renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer texture, glm::ivec4 viewport, const glm::ivec4 scissor); virtual void updateFrameData(); void withMainThreadContext(std::function f) const; @@ -134,6 +136,7 @@ protected: gpu::PipelinePointer _simplePipeline; gpu::PipelinePointer _presentPipeline; gpu::PipelinePointer _cursorPipeline; + gpu::TexturePointer _displayTexture{}; float _compositeOverlayAlpha { 1.0f }; struct CursorData { diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index cab96c258b..b3797853f0 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include "../Logging.h" @@ -211,7 +212,15 @@ void HmdDisplayPlugin::internalPresent() { // Composite together the scene, overlay and mouse cursor hmdPresent(); - if (!_disablePreview) { + if (_displayTexture) { + // Note: _displayTexture must currently be the same size as the display. + uvec2 dims = uvec2(_displayTexture->getDimensions()); + auto viewport = ivec4(uvec2(0), dims); + render([&](gpu::Batch& batch) { + renderFromTexture(batch, _displayTexture, viewport, viewport); + }); + swapBuffers(); + } else if (!_disablePreview) { // screen preview mirroring auto sourceSize = _renderTargetSize; if (_monoPreview) { @@ -272,16 +281,7 @@ void HmdDisplayPlugin::internalPresent() { viewport.z *= 2; } - - batch.enableStereo(false); - batch.resetViewTransform(); - batch.setFramebuffer(gpu::FramebufferPointer()); - batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0)); - batch.setStateScissorRect(scissor); // was viewport - batch.setViewportTransform(viewport); - batch.setResourceTexture(0, _compositeFramebuffer->getRenderBuffer(0)); - batch.setPipeline(_presentPipeline); - batch.draw(gpu::TRIANGLE_STRIP, 4); + renderFromTexture(batch, _compositeFramebuffer->getRenderBuffer(0), viewport, scissor); }); swapBuffers(); } else if (_clearPreviewFlag) { @@ -310,15 +310,7 @@ void HmdDisplayPlugin::internalPresent() { auto viewport = getViewportForSourceSize(uvec2(_previewTexture->getDimensions())); render([&](gpu::Batch& batch) { - batch.enableStereo(false); - batch.resetViewTransform(); - batch.setFramebuffer(gpu::FramebufferPointer()); - batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0)); - batch.setStateScissorRect(viewport); - batch.setViewportTransform(viewport); - batch.setResourceTexture(0, _previewTexture); - batch.setPipeline(_presentPipeline); - batch.draw(gpu::TRIANGLE_STRIP, 4); + renderFromTexture(batch, _previewTexture, viewport, viewport); }); _clearPreviewFlag = false; swapBuffers(); diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index 297bdb2cca..7bfdbddbc5 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -184,6 +184,9 @@ public: // will query the underlying hmd api to compute the most recent head pose virtual bool beginFrameRender(uint32_t frameIndex) { return true; } + // Set the texture to display on the monitor and return true, if allowed. Empty string resets. + virtual bool setDisplayTexture(const QString& name) { return false; } + virtual float devicePixelRatio() { return 1.0f; } // Rate at which we render frames virtual float renderRate() const { return -1.0f; } diff --git a/scripts/system/spectatorCamera.js b/scripts/system/spectatorCamera.js index 6f676ef632..44419d73ff 100644 --- a/scripts/system/spectatorCamera.js +++ b/scripts/system/spectatorCamera.js @@ -117,6 +117,7 @@ orientation: flip(cameraRotation), scale: -0.35, }); + setDisplay(monitorShowsCameraView); } // @@ -147,6 +148,12 @@ } camera = false; viewFinderOverlay = false; + setDisplay(monitorShowsCameraView); + } + + function onHMDChanged(isHMDMode) { + // Will also eventually enable disable app, camera, etc. + setDisplay(monitorShowsCameraView); } // @@ -174,6 +181,8 @@ button.clicked.connect(onTabletButtonClicked); tablet.screenChanged.connect(onTabletScreenChanged); Window.domainChanged.connect(spectatorCameraOff); + Controller.keyPressEvent.connect(keyPressEvent); + HMD.displayModeChanged.connect(onHMDChanged); viewFinderOverlay = false; camera = false; } @@ -205,6 +214,31 @@ } } + function setDisplay(showCameraView) { + // It would be fancy if (showCameraView && !isUpdateRenderWired) would show instructions, but that's out of scope for now. + var url = (showCameraView && isUpdateRenderWired) ? "http://selfieFrame" : ""; + Window.setDisplayTexture(url); + } + const MONITOR_SHOWS_CAMERA_VIEW_DEFAULT = false; + var monitorShowsCameraView = !!Settings.getValue('spectatorCamera/monitorShowsCameraView', MONITOR_SHOWS_CAMERA_VIEW_DEFAULT); + function setMonitorShowsCameraView(showCameraView) { + if (showCameraView === monitorShowsCameraView) { + return; + } + monitorShowsCameraView = showCameraView; + setDisplay(showCameraView); + Settings.setValue('spectatorCamera/monitorShowsCameraView', showCameraView); + } + function setMonitorShowsCameraViewAndSendToQml(showCameraView) { + setMonitorShowsCameraView(showCameraView); + sendToQml({ method: 'updateMonitorShowsSwitch', params: showCameraView }); + } + function keyPressEvent(event) { + if ((event.text === "0") && !event.isAutoRepeat && !event.isShifted && !event.isMeta && event.isControl && !event.isAlt) { + setMonitorShowsCameraViewAndSendToQml(!monitorShowsCameraView); + } + } + // // Function Name: onTabletButtonClicked() // @@ -230,7 +264,7 @@ tablet.loadQMLSource("../SpectatorCamera.qml"); onSpectatorCameraScreen = true; sendToQml({ method: 'updateSpectatorCameraCheckbox', params: !!camera }); - sendToQml({ method: 'updateMonitorShowsSwitch', params: !!Settings.getValue('spectatorCamera/monitorShowsCameraView', false) }); + setMonitorShowsCameraViewAndSendToQml(monitorShowsCameraView); } } @@ -293,13 +327,8 @@ case 'spectatorCameraOff': spectatorCameraOff(); break; - case 'showHmdPreviewOnMonitor': - print('FIXME: showHmdPreviewOnMonitor'); - Settings.setValue('spectatorCamera/monitorShowsCameraView', false); - break; - case 'showCameraViewOnMonitor': - print('FIXME: showCameraViewOnMonitor'); - Settings.setValue('spectatorCamera/monitorShowsCameraView', true); + case 'setMonitorShowsCameraView': + setMonitorShowsCameraView(message.params); break; case 'changeSwitchViewFromControllerPreference': print('FIXME: Preference is now: ' + message.params); @@ -327,6 +356,8 @@ tablet.removeButton(button); button.clicked.disconnect(onTabletButtonClicked); tablet.screenChanged.disconnect(onTabletScreenChanged); + HMD.displayModeChanged.disconnect(onHMDChanged); + Controller.keyPressEvent.disconnect(keyPressEvent); } //