diff --git a/interface/resources/qml/hifi/SpectatorCamera.qml b/interface/resources/qml/hifi/SpectatorCamera.qml index 6081419f57..12861ee386 100644 --- a/interface/resources/qml/hifi/SpectatorCamera.qml +++ b/interface/resources/qml/hifi/SpectatorCamera.qml @@ -227,7 +227,7 @@ Rectangle { height: 250; color: spectatorCameraPreview.visible ? "transparent" : "black"; - + AnimatedImage { source: "../../images/static.gif" @@ -235,7 +235,7 @@ Rectangle { anchors.fill: parent; opacity: 0.15; } - + // Instructions (visible when display texture isn't set) FiraSansRegular { id: spectatorCameraInstructions; @@ -247,7 +247,7 @@ Rectangle { horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter; } - + // Spectator Camera Preview Hifi.ResourceImageItem { id: spectatorCameraPreview; @@ -360,6 +360,8 @@ Rectangle { } break; case 'showPreviewTextureNotInstructions': + console.log('showPreviewTextureNotInstructions recvd', JSON.stringify(message)); + spectatorCameraPreview.url = message.url; spectatorCameraPreview.visible = message.setting; break; default: diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 63df7fdda9..e5de6fce8a 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -497,16 +497,26 @@ 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) { + renderFromTexture(batch, texture, viewport, scissor, gpu::FramebufferPointer()); +} + +void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer texture, glm::ivec4 viewport, const glm::ivec4 scissor, gpu::FramebufferPointer copyFbo /*=gpu::FramebufferPointer()*/) { + auto fbo = gpu::FramebufferPointer(); batch.enableStereo(false); batch.resetViewTransform(); - batch.setFramebuffer(gpu::FramebufferPointer()); + batch.setFramebuffer(fbo); 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); + if (copyFbo) { + gpu::Vec4i rect {0, 0, scissor.z, scissor.w}; + batch.blit(fbo, rect, copyFbo, rect); + } } void OpenGLDisplayPlugin::updateFrameData() { diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index 237864ded6..2f93fa630d 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -113,6 +113,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, gpu::FramebufferPointer fbo); void renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer texture, glm::ivec4 viewport, const glm::ivec4 scissor); virtual void updateFrameData(); diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index ea91890f33..656d3013ab 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -134,7 +134,7 @@ void HmdDisplayPlugin::customizeContext() { state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); - + gpu::Shader::BindingSet bindings; bindings.insert({ "lineData", LINE_DATA_SLOT });; gpu::Shader::makeProgram(*program, bindings); @@ -242,7 +242,6 @@ void HmdDisplayPlugin::internalPresent() { glm::ivec4 viewport = getViewportForSourceSize(sourceSize); glm::ivec4 scissor = viewport; - render([&](gpu::Batch& batch) { if (_monoPreview) { @@ -287,9 +286,11 @@ void HmdDisplayPlugin::internalPresent() { viewport.z *= 2; } - renderFromTexture(batch, _compositeFramebuffer->getRenderBuffer(0), viewport, scissor); + auto fbo = DependencyManager::get()->getHmdPreviewFramebuffer(); + renderFromTexture(batch, _compositeFramebuffer->getRenderBuffer(0), viewport, scissor, fbo); }); swapBuffers(); + } else if (_clearPreviewFlag) { QImage image; if (_vsyncEnabled) { @@ -312,7 +313,7 @@ void HmdDisplayPlugin::internalPresent() { _previewTexture->assignStoredMip(0, image.byteCount(), image.constBits()); _previewTexture->setAutoGenerateMips(true); } - + auto viewport = getViewportForSourceSize(uvec2(_previewTexture->getDimensions())); render([&](gpu::Batch& batch) { @@ -323,7 +324,7 @@ void HmdDisplayPlugin::internalPresent() { } postPreview(); - // If preview is disabled, we need to check to see if the window size has changed + // If preview is disabled, we need to check to see if the window size has changed // and re-render the no-preview message if (_disablePreview) { auto window = _container->getPrimaryWidget(); @@ -510,7 +511,7 @@ void HmdDisplayPlugin::OverlayRenderer::build() { indices = std::make_shared(); //UV mapping source: http://www.mvps.org/directx/articles/spheremap.htm - + static const float fov = CompositorHelper::VIRTUAL_UI_TARGET_FOV.y; static const float aspectRatio = CompositorHelper::VIRTUAL_UI_ASPECT_RATIO; static const uint16_t stacks = 128; @@ -672,7 +673,7 @@ bool HmdDisplayPlugin::setHandLaser(uint32_t hands, HandLaserMode mode, const ve _handLasers[1] = info; } }); - // FIXME defer to a child class plugin to determine if hand lasers are actually + // FIXME defer to a child class plugin to determine if hand lasers are actually // available based on the presence or absence of hand controllers return true; } @@ -687,7 +688,7 @@ bool HmdDisplayPlugin::setExtraLaser(HandLaserMode mode, const vec4& color, cons _extraLaserStart = sensorSpaceStart; }); - // FIXME defer to a child class plugin to determine if hand lasers are actually + // FIXME defer to a child class plugin to determine if hand lasers are actually // available based on the presence or absence of hand controllers return true; } @@ -702,7 +703,7 @@ void HmdDisplayPlugin::compositeExtra() { if (_presentHandPoses[0] == IDENTITY_MATRIX && _presentHandPoses[1] == IDENTITY_MATRIX && !_presentExtraLaser.valid()) { return; } - + render([&](gpu::Batch& batch) { batch.setFramebuffer(_compositeFramebuffer); batch.setModelTransform(Transform()); diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 4d2fdd2bc4..c511db28ac 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -54,6 +54,7 @@ const std::string TextureCache::KTX_EXT { "ktx" }; static const QString RESOURCE_SCHEME = "resource"; static const QUrl SPECTATOR_CAMERA_FRAME_URL("resource://spectatorCameraFrame"); +static const QUrl HMD_PREVIEW_FRAME_URL("resource://hmdPreviewFrame"); static const float SKYBOX_LOAD_PRIORITY { 10.0f }; // Make sure skybox loads first static const float HIGH_MIPS_LOAD_PRIORITY { 9.0f }; // Make sure high mips loads after skybox but before models @@ -969,7 +970,6 @@ void ImageReader::read() { Q_ARG(int, texture->getHeight())); } - NetworkTexturePointer TextureCache::getResourceTexture(QUrl resourceTextureUrl) { gpu::TexturePointer texture; if (resourceTextureUrl == SPECTATOR_CAMERA_FRAME_URL) { @@ -984,10 +984,30 @@ NetworkTexturePointer TextureCache::getResourceTexture(QUrl resourceTextureUrl) } } } + // FIXME: Generalize this, DRY up this code + if (resourceTextureUrl == HMD_PREVIEW_FRAME_URL) { + if (!_hmdPreviewNetworkTexture) { + _hmdPreviewNetworkTexture.reset(new NetworkTexture(resourceTextureUrl)); + } + if (_hmdPreviewFramebuffer) { + texture = _hmdPreviewFramebuffer->getRenderBuffer(0); + if (texture) { + _hmdPreviewNetworkTexture->setImage(texture, texture->getWidth(), texture->getHeight()); + return _hmdPreviewNetworkTexture; + } + } + } return NetworkTexturePointer(); } +const gpu::FramebufferPointer& TextureCache::getHmdPreviewFramebuffer() { + if (!_hmdPreviewFramebuffer) { + _hmdPreviewFramebuffer.reset(gpu::Framebuffer::create("hmdPreview",gpu::Element::COLOR_SRGBA_32, 2040, 1024)); + } + return _hmdPreviewFramebuffer; +} + const gpu::FramebufferPointer& TextureCache::getSpectatorCameraFramebuffer() { if (!_spectatorCameraFramebuffer) { resetSpectatorCameraFramebuffer(2048, 1024); diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 43edc3593d..8b7607d04d 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -74,7 +74,7 @@ protected: virtual bool isCacheable() const override { return _loaded; } virtual void downloadFinished(const QByteArray& data) override; - + Q_INVOKABLE void loadContent(const QByteArray& content); Q_INVOKABLE void setImage(gpu::TexturePointer texture, int originalWidth, int originalHeight); @@ -170,6 +170,7 @@ public: NetworkTexturePointer getResourceTexture(QUrl resourceTextureUrl); const gpu::FramebufferPointer& getSpectatorCameraFramebuffer(); void resetSpectatorCameraFramebuffer(int width, int height); + const gpu::FramebufferPointer& getHmdPreviewFramebuffer(); protected: // Overload ResourceCache::prefetch to allow specifying texture type for loads @@ -202,6 +203,9 @@ private: NetworkTexturePointer _spectatorCameraNetworkTexture; gpu::FramebufferPointer _spectatorCameraFramebuffer; + + NetworkTexturePointer _hmdPreviewNetworkTexture; + gpu::FramebufferPointer _hmdPreviewFramebuffer; }; #endif // hifi_TextureCache_h diff --git a/scripts/system/spectatorCamera.js b/scripts/system/spectatorCamera.js index 99f064e933..a9e4d3004d 100644 --- a/scripts/system/spectatorCamera.js +++ b/scripts/system/spectatorCamera.js @@ -19,7 +19,7 @@ onTabletButtonClicked, wireEventBridge, startup, shutdown, registerButtonMappings; // Function Name: inFrontOf() - // + // // Description: // -Returns the position in front of the given "position" argument, where the forward vector is based off // the "orientation" argument and the amount in front is based off the "distance" argument. @@ -29,7 +29,7 @@ } // Function Name: spectatorCameraOn() - // + // // Description: // -Call this function to set up the spectator camera and // spawn the camera entity. @@ -103,7 +103,7 @@ } // Function Name: spectatorCameraOff() - // + // // Description: // -Call this function to shut down the spectator camera and // destroy the camera entity. @@ -218,9 +218,17 @@ // 3. Camera is on; "Monitor Shows" is "HMD Preview": "url" is "" // 4. Camera is on; "Monitor Shows" is "Camera View": "url" is "resource://spectatorCameraFrame" function setDisplay(showCameraView) { - var url = (camera && showCameraView) ? "resource://spectatorCameraFrame" : ""; - sendToQml({ method: 'showPreviewTextureNotInstructions', setting: !!url }); - Window.setDisplayTexture(url); + + var url = (camera) ? (showCameraView ? "resource://spectatorCameraFrame" : "resource://hmdPreviewFrame") : ""; + sendToQml({ method: 'showPreviewTextureNotInstructions', setting: !!url, url: url}); + + // FIXME: temporary hack to avoid setting the display texture to hmdPreviewFrame + // until it is the correct mono. + if (url === "resource://hmdPreviewFrame") { + Window.setDisplayTexture(""); + } else { + Window.setDisplayTexture(url); + } } const MONITOR_SHOWS_CAMERA_VIEW_DEFAULT = false; var monitorShowsCameraView = !!Settings.getValue('spectatorCamera/monitorShowsCameraView', MONITOR_SHOWS_CAMERA_VIEW_DEFAULT); @@ -421,7 +429,7 @@ } // Function Name: onHMDChanged() - // + // // Description: // -Called from C++ when HMD mode is changed. The argument "isHMDMode" should be true if HMD is on; false otherwise. function onHMDChanged(isHMDMode) { @@ -433,7 +441,7 @@ } // Function Name: shutdown() - // + // // Description: // -shutdown() will be called when the script ends (i.e. is stopped). function shutdown() {