From c730e51d1e7d37271a6c7b948f6d62806de6a3a0 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Thu, 11 Apr 2019 13:22:51 -0700 Subject: [PATCH] wip redo stencil work --- cmake/externals/LibOVR/CMakeLists.txt | 4 +- interface/src/Application.cpp | 24 +++++-- interface/src/Application.h | 6 ++ interface/src/SecondaryCamera.cpp | 11 +-- interface/src/graphics/GraphicsEngine.cpp | 4 ++ interface/src/ui/SnapshotAnimated.cpp | 67 ++++++++++--------- .../display-plugins/OpenGLDisplayPlugin.cpp | 9 +++ .../display-plugins/hmd/HmdDisplayPlugin.h | 2 + libraries/gpu/src/gpu/Frame.h | 3 + libraries/plugins/src/plugins/DisplayPlugin.h | 5 ++ .../render-utils/src/StencilMaskPass.cpp | 42 +++++++----- libraries/render-utils/src/StencilMaskPass.h | 13 ++-- libraries/render/src/render/Args.h | 5 ++ libraries/shared/src/RegisteredMetaTypes.cpp | 11 +++ libraries/shared/src/RegisteredMetaTypes.h | 4 ++ libraries/shared/src/StencilMaskMode.h | 18 +++++ plugins/oculus/CMakeLists.txt | 2 +- .../oculus/src/OculusBaseDisplayPlugin.cpp | 63 +++++++++++++++++ plugins/oculus/src/OculusBaseDisplayPlugin.h | 8 +++ plugins/openvr/src/OpenVrDisplayPlugin.cpp | 45 +++++++++++++ plugins/openvr/src/OpenVrDisplayPlugin.h | 8 +++ 21 files changed, 288 insertions(+), 66 deletions(-) create mode 100644 libraries/shared/src/StencilMaskMode.h diff --git a/cmake/externals/LibOVR/CMakeLists.txt b/cmake/externals/LibOVR/CMakeLists.txt index ae4cf6320e..481753f7e0 100644 --- a/cmake/externals/LibOVR/CMakeLists.txt +++ b/cmake/externals/LibOVR/CMakeLists.txt @@ -17,8 +17,8 @@ if (WIN32) ExternalProject_Add( ${EXTERNAL_NAME} - URL https://public.highfidelity.com/dependencies/ovr_sdk_win_1.26.0_public.zip - URL_MD5 06804ff9727b910dcd04a37c800053b5 + URL https://hifi-public.s3.amazonaws.com/dependencies/ovr_sdk_win_1.35.0.zip + URL_MD5 1e3e8b2101387af07ff9c841d0ea285e CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= PATCH_COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/LibOVRCMakeLists.txt" /CMakeLists.txt LOG_DOWNLOAD 1 diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e809120a74..bc0feefb9a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6725,6 +6725,11 @@ void Application::updateRenderArgs(float deltaTime) { } } + appRenderArgs._renderArgs._stencilMaskMode = getActiveDisplayPlugin()->getStencilMaskMode(); + if (appRenderArgs._renderArgs._stencilMaskMode == StencilMaskMode::MESH) { + appRenderArgs._renderArgs._stencilMaskOperator = getActiveDisplayPlugin()->getStencilMaskMeshOperator(); + } + { QMutexLocker viewLocker(&_viewMutex); _myCamera.loadViewFrustum(_displayViewFrustum); @@ -8412,11 +8417,20 @@ void Application::loadAvatarBrowser() const { DependencyManager::get()->openTablet(); } +void Application::addSnapshotOperator(const SnapshotOperator& snapshotOperator) { + std::lock_guard lock(_snapshotMutex); + _snapshotOperators.push(snapshotOperator); +} + +bool Application::takeSnapshotOperators(std::queue& snapshotOperators) { + std::lock_guard lock(_snapshotMutex); + _snapshotOperators.swap(snapshotOperators); + return !snapshotOperators.empty(); +} + void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio, const QString& filename) { - postLambdaEvent([notify, includeAnimated, aspectRatio, filename, this] { - // Get a screenshot and save it - QString path = DependencyManager::get()->saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio), filename, - TestScriptingInterface::getInstance()->getTestResultsLocation()); + addSnapshotOperator({ [notify, includeAnimated, aspectRatio, filename](const QImage& snapshot) { + QString path = DependencyManager::get()->saveSnapshot(snapshot, filename, TestScriptingInterface::getInstance()->getTestResultsLocation()); // If we're not doing an animated snapshot as well... if (!includeAnimated) { @@ -8428,7 +8442,7 @@ void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRa // Get an animated GIF snapshot and save it SnapshotAnimated::saveSnapshotAnimated(path, aspectRatio, qApp, DependencyManager::get()); } - }); + }, aspectRatio }); } void Application::takeSecondaryCameraSnapshot(const bool& notify, const QString& filename) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 99e57f1866..b2e59e97d4 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -344,6 +344,10 @@ public: void toggleAwayMode(); #endif + using SnapshotOperator = std::pair, float>; + void addSnapshotOperator(const SnapshotOperator& snapshotOperator); + bool takeSnapshotOperators(std::queue& snapshotOperators); + signals: void svoImportRequested(const QString& url); @@ -788,6 +792,8 @@ private: AudioInjectorPointer _snapshotSoundInjector; SharedSoundPointer _snapshotSound; SharedSoundPointer _sampleSound; + std::mutex _snapshotMutex; + std::queue _snapshotOperators; DisplayPluginPointer _autoSwitchDisplayModeSupportedHMDPlugin; QString _autoSwitchDisplayModeSupportedHMDPluginName; diff --git a/interface/src/SecondaryCamera.cpp b/interface/src/SecondaryCamera.cpp index 12c9636746..da2874a3f4 100644 --- a/interface/src/SecondaryCamera.cpp +++ b/interface/src/SecondaryCamera.cpp @@ -152,10 +152,12 @@ public: _cachedArgsPointer->_viewport = args->_viewport; _cachedArgsPointer->_displayMode = args->_displayMode; _cachedArgsPointer->_renderMode = args->_renderMode; + _cachedArgsPointer->_stencilMaskMode = args->_stencilMaskMode; args->_blitFramebuffer = destFramebuffer; args->_viewport = glm::ivec4(0, 0, destFramebuffer->getWidth(), destFramebuffer->getHeight()); args->_displayMode = RenderArgs::MONO; args->_renderMode = RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE; + args->_stencilMaskMode = StencilMaskMode::NONE; gpu::doInBatch("SecondaryCameraJob::run", args->_context, [&](gpu::Batch& batch) { batch.disableContextStereo(); @@ -255,10 +257,11 @@ public: void run(const render::RenderContextPointer& renderContext, const RenderArgsPointer& cachedArgs) { auto args = renderContext->args; if (cachedArgs) { - args->_blitFramebuffer = cachedArgs->_blitFramebuffer; - args->_viewport = cachedArgs->_viewport; - args->_displayMode = cachedArgs->_displayMode; - args->_renderMode = cachedArgs->_renderMode; + args->_blitFramebuffer = cachedArgs->_blitFramebuffer; + args->_viewport = cachedArgs->_viewport; + args->_displayMode = cachedArgs->_displayMode; + args->_renderMode = cachedArgs->_renderMode; + args->_stencilMaskMode = cachedArgs->_stencilMaskMode; } args->popViewFrustum(); diff --git a/interface/src/graphics/GraphicsEngine.cpp b/interface/src/graphics/GraphicsEngine.cpp index c2137d3d97..1842c20e4e 100644 --- a/interface/src/graphics/GraphicsEngine.cpp +++ b/interface/src/graphics/GraphicsEngine.cpp @@ -244,6 +244,7 @@ void GraphicsEngine::render_performFrame() { finalFramebuffer = framebufferCache->getFramebuffer(); } + std::queue snapshotOperators; if (!_programsCompiled.load()) { gpu::doInBatch("splashFrame", _gpuContext, [&](gpu::Batch& batch) { batch.setFramebuffer(finalFramebuffer); @@ -271,11 +272,13 @@ void GraphicsEngine::render_performFrame() { PROFILE_RANGE(render, "/runRenderFrame"); renderArgs._hudOperator = displayPlugin->getHUDOperator(); renderArgs._hudTexture = qApp->getApplicationOverlay().getOverlayTexture(); + renderArgs._takingSnapshot = qApp->takeSnapshotOperators(snapshotOperators); renderArgs._blitFramebuffer = finalFramebuffer; render_runRenderFrame(&renderArgs); } } + qDebug() << "boop" << renderArgs._takingSnapshot << snapshotOperators.size(); auto frame = getGPUContext()->endFrame(); frame->frameIndex = _renderFrameCount; frame->framebuffer = finalFramebuffer; @@ -285,6 +288,7 @@ void GraphicsEngine::render_performFrame() { frameBufferCache->releaseFramebuffer(framebuffer); } }; + frame->snapshotOperators = snapshotOperators; // deliver final scene rendering commands to the display plugin { PROFILE_RANGE(render, "/pluginOutput"); diff --git a/interface/src/ui/SnapshotAnimated.cpp b/interface/src/ui/SnapshotAnimated.cpp index 9d58d89385..1c5a25b68f 100644 --- a/interface/src/ui/SnapshotAnimated.cpp +++ b/interface/src/ui/SnapshotAnimated.cpp @@ -62,44 +62,45 @@ void SnapshotAnimated::saveSnapshotAnimated(QString pathStill, float aspectRatio void SnapshotAnimated::captureFrames() { if (SnapshotAnimated::snapshotAnimatedTimerRunning) { - // Get a screenshot from the display, then scale the screenshot down, - // then convert it to the image format the GIF library needs, - // then save all that to the QImage named "frame" - QImage frame(SnapshotAnimated::app->getActiveDisplayPlugin()->getScreenshot(SnapshotAnimated::aspectRatio)); - frame = frame.scaledToWidth(SNAPSNOT_ANIMATED_WIDTH); - SnapshotAnimated::snapshotAnimatedFrameVector.append(frame); + SnapshotAnimated::app->addSnapshotOperator({ [](const QImage& snapshot) { + // Get a screenshot from the display, then scale the screenshot down, + // then convert it to the image format the GIF library needs, + // then save all that to the QImage named "frame" + QImage frame = snapshot.scaledToWidth(SNAPSNOT_ANIMATED_WIDTH); + SnapshotAnimated::snapshotAnimatedFrameVector.append(frame); - // If that was the first frame... - if (SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp == 0) { - // Record the current frame timestamp - SnapshotAnimated::snapshotAnimatedTimestamp = QDateTime::currentMSecsSinceEpoch(); - // Record the first frame timestamp - SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp = SnapshotAnimated::snapshotAnimatedTimestamp; - SnapshotAnimated::snapshotAnimatedFrameDelayVector.append(SNAPSNOT_ANIMATED_FRAME_DELAY_MSEC / 10); - // If this is an intermediate or the final frame... - } else { - // Push the current frame delay onto the vector - SnapshotAnimated::snapshotAnimatedFrameDelayVector.append(round(((float)(QDateTime::currentMSecsSinceEpoch() - SnapshotAnimated::snapshotAnimatedTimestamp)) / 10)); - // Record the current frame timestamp - SnapshotAnimated::snapshotAnimatedTimestamp = QDateTime::currentMSecsSinceEpoch(); + // If that was the first frame... + if (SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp == 0) { + // Record the current frame timestamp + SnapshotAnimated::snapshotAnimatedTimestamp = QDateTime::currentMSecsSinceEpoch(); + // Record the first frame timestamp + SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp = SnapshotAnimated::snapshotAnimatedTimestamp; + SnapshotAnimated::snapshotAnimatedFrameDelayVector.append(SNAPSNOT_ANIMATED_FRAME_DELAY_MSEC / 10); + // If this is an intermediate or the final frame... + } else { + // Push the current frame delay onto the vector + SnapshotAnimated::snapshotAnimatedFrameDelayVector.append(round(((float)(QDateTime::currentMSecsSinceEpoch() - SnapshotAnimated::snapshotAnimatedTimestamp)) / 10)); + // Record the current frame timestamp + SnapshotAnimated::snapshotAnimatedTimestamp = QDateTime::currentMSecsSinceEpoch(); - // If that was the last frame... - if ((SnapshotAnimated::snapshotAnimatedTimestamp - SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp) >= (SnapshotAnimated::snapshotAnimatedDuration.get() * MSECS_PER_SECOND)) { - SnapshotAnimated::snapshotAnimatedTimerRunning = false; + // If that was the last frame... + if ((SnapshotAnimated::snapshotAnimatedTimestamp - SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp) >= (SnapshotAnimated::snapshotAnimatedDuration.get() * MSECS_PER_SECOND)) { + SnapshotAnimated::snapshotAnimatedTimerRunning = false; - // Notify the user that we're processing the snapshot - // This also pops up the "Share" dialog. The unprocessed GIF will be visualized as a loading icon until processingGifCompleted() is called. - emit SnapshotAnimated::snapshotAnimatedDM->processingGifStarted(SnapshotAnimated::snapshotStillPath); + // Notify the user that we're processing the snapshot + // This also pops up the "Share" dialog. The unprocessed GIF will be visualized as a loading icon until processingGifCompleted() is called. + emit SnapshotAnimated::snapshotAnimatedDM->processingGifStarted(SnapshotAnimated::snapshotStillPath); - // Kick off the thread that'll pack the frames into the GIF - QtConcurrent::run(processFrames); - // Stop the snapshot QTimer. This action by itself DOES NOT GUARANTEE - // that the slot will not be called again in the future. - // See: http://lists.qt-project.org/pipermail/qt-interest-old/2009-October/013926.html - SnapshotAnimated::snapshotAnimatedTimer->stop(); - delete SnapshotAnimated::snapshotAnimatedTimer; + // Kick off the thread that'll pack the frames into the GIF + QtConcurrent::run(processFrames); + // Stop the snapshot QTimer. This action by itself DOES NOT GUARANTEE + // that the slot will not be called again in the future. + // See: http://lists.qt-project.org/pipermail/qt-interest-old/2009-October/013926.html + SnapshotAnimated::snapshotAnimatedTimer->stop(); + delete SnapshotAnimated::snapshotAnimatedTimer; + } } - } + }, SnapshotAnimated::aspectRatio }); } } diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index c536e6b6e2..1700ca9d54 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -723,6 +723,15 @@ void OpenGLDisplayPlugin::present() { compositeLayers(); } + { // If we have any snapshots this frame, handle them + PROFILE_RANGE_EX(render, "snapshotOperators", 0xffff00ff, frameId) + while (!_currentFrame->snapshotOperators.empty()) { + auto& snapshotOperator = _currentFrame->snapshotOperators.front(); + snapshotOperator.first(getScreenshot(snapshotOperator.second)); + _currentFrame->snapshotOperators.pop(); + } + } + // Take the composite framebuffer and send it to the output device { PROFILE_RANGE_EX(render, "internalPresent", 0xff00ffff, frameId) diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h index d8c0ce8e1d..e952b1e8db 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h @@ -48,6 +48,8 @@ public: void pluginUpdate() override {}; + virtual StencilMaskMode getStencilMaskMode() const override { return StencilMaskMode::PAINT; } + signals: void hmdMountedChanged(); void hmdVisibleChanged(bool visible); diff --git a/libraries/gpu/src/gpu/Frame.h b/libraries/gpu/src/gpu/Frame.h index 3787ebfacd..94c0919fdb 100644 --- a/libraries/gpu/src/gpu/Frame.h +++ b/libraries/gpu/src/gpu/Frame.h @@ -9,6 +9,7 @@ #define hifi_gpu_Frame_h #include +#include #include "Forward.h" #include "Batch.h" @@ -41,6 +42,8 @@ namespace gpu { /// How to process the framebuffer when the frame dies. MUST BE THREAD SAFE FramebufferRecycler framebufferRecycler; + std::queue, float>> snapshotOperators; + protected: friend class Deserializer; diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index aa52e57c3f..9db8fc995c 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -27,6 +27,7 @@ #include #include #include "Plugin.h" +#include "StencilMaskMode.h" class QOpenGLFramebufferObject; @@ -221,6 +222,10 @@ public: // for updating plugin-related commands. Mimics the input plugin. virtual void pluginUpdate() = 0; + virtual StencilMaskMode getStencilMaskMode() const { return StencilMaskMode::NONE; } + using StencilMaskMeshOperator = std::function; + virtual StencilMaskMeshOperator getStencilMaskMeshOperator() { return nullptr; } + signals: void recommendedFramebufferSizeChanged(const QSize& size); void resetSensorsRequested(); diff --git a/libraries/render-utils/src/StencilMaskPass.cpp b/libraries/render-utils/src/StencilMaskPass.cpp index 7217a3e5eb..7c89e6b601 100644 --- a/libraries/render-utils/src/StencilMaskPass.cpp +++ b/libraries/render-utils/src/StencilMaskPass.cpp @@ -19,7 +19,6 @@ using namespace render; void PrepareStencil::configure(const Config& config) { _maskMode = config.maskMode; - _forceDraw = config.forceDraw; } graphics::MeshPointer PrepareStencil::getMesh() { @@ -43,6 +42,7 @@ gpu::PipelinePointer PrepareStencil::getMeshStencilPipeline() { auto state = std::make_shared(); drawMask(*state); state->setColorWriteMask(gpu::State::WRITE_NONE); + state->setCullMode(gpu::State::CullMode::CULL_NONE); _meshStencilPipeline = gpu::Pipeline::create(program, state); } @@ -64,8 +64,28 @@ gpu::PipelinePointer PrepareStencil::getPaintStencilPipeline() { void PrepareStencil::run(const RenderContextPointer& renderContext, const gpu::FramebufferPointer& srcFramebuffer) { RenderArgs* args = renderContext->args; - // Only draw the stencil mask if in HMD mode or not forced. - if (!_forceDraw && (args->_displayMode != RenderArgs::STEREO_HMD)) { + if (args->_takingSnapshot) { + return; + } + + StencilMaskMode maskMode = _maskMode; + std::function maskOperator = [this](gpu::Batch& batch) { + auto mesh = getMesh(); + batch.setIndexBuffer(mesh->getIndexBuffer()); + batch.setInputFormat((mesh->getVertexFormat())); + batch.setInputStream(0, mesh->getVertexStream()); + + // Draw + auto part = mesh->getPartBuffer().get(0); + batch.drawIndexed(gpu::TRIANGLES, part._numIndices, part._startIndex); + }; + + if (maskMode == StencilMaskMode::NONE) { + maskMode = args->_stencilMaskMode; + maskOperator = args->_stencilMaskOperator; + } + + if (maskMode == StencilMaskMode::NONE || (maskMode == StencilMaskMode::MESH && !maskOperator)) { return; } @@ -74,20 +94,12 @@ void PrepareStencil::run(const RenderContextPointer& renderContext, const gpu::F batch.setViewportTransform(args->_viewport); - if (_maskMode < 0) { - batch.setPipeline(getMeshStencilPipeline()); - - auto mesh = getMesh(); - batch.setIndexBuffer(mesh->getIndexBuffer()); - batch.setInputFormat((mesh->getVertexFormat())); - batch.setInputStream(0, mesh->getVertexStream()); - - // Draw - auto part = mesh->getPartBuffer().get(0); - batch.drawIndexed(gpu::TRIANGLES, part._numIndices, part._startIndex); - } else { + if (maskMode == StencilMaskMode::PAINT) { batch.setPipeline(getPaintStencilPipeline()); batch.draw(gpu::TRIANGLE_STRIP, 4); + } else if (maskMode == StencilMaskMode::MESH) { + batch.setPipeline(getMeshStencilPipeline()); + maskOperator(batch); } }); } diff --git a/libraries/render-utils/src/StencilMaskPass.h b/libraries/render-utils/src/StencilMaskPass.h index a8e4d1e1f2..bca2ef17a5 100644 --- a/libraries/render-utils/src/StencilMaskPass.h +++ b/libraries/render-utils/src/StencilMaskPass.h @@ -15,17 +15,19 @@ #include #include #include +#include class PrepareStencilConfig : public render::Job::Config { Q_OBJECT - Q_PROPERTY(int maskMode MEMBER maskMode NOTIFY dirty) - Q_PROPERTY(bool forceDraw MEMBER forceDraw NOTIFY dirty) + Q_PROPERTY(StencilMaskMode maskMode MEMBER maskMode NOTIFY dirty) public: PrepareStencilConfig(bool enabled = true) : JobConfig(enabled) {} - int maskMode { 0 }; - bool forceDraw { false }; + // -1 -> don't force drawing (fallback to render args mode) + // 0 -> force draw without mesh + // 1 -> force draw with mesh + StencilMaskMode maskMode { StencilMaskMode::NONE }; signals: void dirty(); @@ -66,8 +68,7 @@ private: graphics::MeshPointer _mesh; graphics::MeshPointer getMesh(); - int _maskMode { 0 }; - bool _forceDraw { false }; + StencilMaskMode _maskMode { StencilMaskMode::NONE }; }; diff --git a/libraries/render/src/render/Args.h b/libraries/render/src/render/Args.h index b5c98e3428..c4b823256e 100644 --- a/libraries/render/src/render/Args.h +++ b/libraries/render/src/render/Args.h @@ -16,6 +16,7 @@ #include #include +#include #include #include "Forward.h" @@ -133,6 +134,10 @@ namespace render { std::function _hudOperator; gpu::TexturePointer _hudTexture; + + bool _takingSnapshot { false }; + StencilMaskMode _stencilMaskMode { StencilMaskMode::NONE }; + std::function _stencilMaskOperator; }; } diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index 597e537d8d..9a35796db8 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -40,6 +40,7 @@ int qMapURLStringMetaTypeId = qRegisterMetaType>(); int socketErrorMetaTypeId = qRegisterMetaType(); int voidLambdaType = qRegisterMetaType>(); int variantLambdaType = qRegisterMetaType>(); +int stencilModeMetaTypeId = qRegisterMetaType(); void registerMetaTypes(QScriptEngine* engine) { qScriptRegisterMetaType(engine, vec2ToScriptValue, vec2FromScriptValue); @@ -64,6 +65,8 @@ void registerMetaTypes(QScriptEngine* engine) { qScriptRegisterMetaType(engine, collisionToScriptValue, collisionFromScriptValue); qScriptRegisterMetaType(engine, quuidToScriptValue, quuidFromScriptValue); qScriptRegisterMetaType(engine, aaCubeToScriptValue, aaCubeFromScriptValue); + + qScriptRegisterMetaType(engine, stencilMaskModeToScriptValue, stencilMaskModeFromScriptValue); } QScriptValue vec2ToScriptValue(QScriptEngine* engine, const glm::vec2& vec2) { @@ -1283,4 +1286,12 @@ QVariantMap parseTexturesToMap(QString newTextures, const QVariantMap& defaultTe } return toReturn; +} + +QScriptValue stencilMaskModeToScriptValue(QScriptEngine* engine, const StencilMaskMode& stencilMode) { + return engine->newVariant((int)stencilMode); +} + +void stencilMaskModeFromScriptValue(const QScriptValue& object, StencilMaskMode& stencilMode) { + stencilMode = StencilMaskMode(object.toVariant().toInt()); } \ No newline at end of file diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index ea2c5b8354..e663a9d90f 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -25,6 +25,7 @@ #include "shared/Bilateral.h" #include "Transform.h" #include "PhysicsCollisionGroups.h" +#include "StencilMaskMode.h" class QColor; class QUrl; @@ -729,5 +730,8 @@ void qVectorMeshFaceFromScriptValue(const QScriptValue& array, QVector QVariantMap parseTexturesToMap(QString textures, const QVariantMap& defaultTextures); +Q_DECLARE_METATYPE(StencilMaskMode) +QScriptValue stencilMaskModeToScriptValue(QScriptEngine* engine, const StencilMaskMode& stencilMode); +void stencilMaskModeFromScriptValue(const QScriptValue& object, StencilMaskMode& stencilMode); #endif // hifi_RegisteredMetaTypes_h diff --git a/libraries/shared/src/StencilMaskMode.h b/libraries/shared/src/StencilMaskMode.h new file mode 100644 index 0000000000..98372890ec --- /dev/null +++ b/libraries/shared/src/StencilMaskMode.h @@ -0,0 +1,18 @@ +// +// Created by Sam Gondelman on 3/26/19. +// Copyright 2019 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 +// + +#ifndef hifi_StencilMaskMode_h +#define hifi_StencilMaskMode_h + +enum class StencilMaskMode { + NONE = -1, // for legacy reasons, this is -1 + PAINT = 0, + MESH = 1 +}; + +#endif // hifi_StencilMaskMode_h \ No newline at end of file diff --git a/plugins/oculus/CMakeLists.txt b/plugins/oculus/CMakeLists.txt index abce753b4d..6ddc75e1e5 100644 --- a/plugins/oculus/CMakeLists.txt +++ b/plugins/oculus/CMakeLists.txt @@ -18,7 +18,7 @@ if (WIN32 AND (NOT USE_GLES)) link_hifi_libraries( shared task gl shaders gpu ${PLATFORM_GL_BACKEND} controllers ui qml plugins ui-plugins display-plugins input-plugins - audio-client networking render-utils + audio-client networking render-utils graphics ${PLATFORM_GL_BACKEND} ) include_hifi_library_headers(octree) diff --git a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp index a67e3127e5..db26537a19 100644 --- a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp @@ -227,3 +227,66 @@ QVector OculusBaseDisplayPlugin::getSensorPositions() { return result; } + +DisplayPlugin::StencilMaskMeshOperator OculusBaseDisplayPlugin::getStencilMaskMeshOperator() { + if (_session) { + if (!_stencilMeshesInitialized) { + _stencilMeshesInitialized = true; + ovr::for_each_eye([&](ovrEyeType eye) { + ovrFovStencilDesc stencilDesc = { + ovrFovStencil_HiddenArea, 0, eye, + _eyeRenderDescs[eye].Fov, _eyeRenderDescs[eye].HmdToEyePose.Orientation + }; + // First we get the size of the buffer we need + ovrFovStencilMeshBuffer buffer = { 0, 0, nullptr, 0, 0, nullptr }; + ovrResult result = ovr_GetFovStencil(_session, &stencilDesc, &buffer); + if (!OVR_SUCCESS(result)) { + _stencilMeshesInitialized = false; + return; + } + + std::vector ovrVertices(buffer.UsedVertexCount); + std::vector ovrIndices(buffer.UsedIndexCount); + + // Now we populate the actual buffer + buffer = { (int)ovrVertices.size(), 0, ovrVertices.data(), (int)ovrIndices.size(), 0, ovrIndices.data() }; + result = ovr_GetFovStencil(_session, &stencilDesc, &buffer); + + if (!OVR_SUCCESS(result)) { + _stencilMeshesInitialized = false; + return; + } + + std::vector vertices; + vertices.reserve(ovrVertices.size()); + for (auto& ovrVertex : ovrVertices) { + // We need the vertices in clip space + vertices.emplace_back(ovrVertex.x - (1.0f - (float)eye), 2.0f * ovrVertex.y - 1.0f, 0.0f); + } + + std::vector indices; + indices.reserve(ovrIndices.size()); + for (auto& ovrIndex : ovrIndices) { + indices.push_back(ovrIndex); + } + + _stencilMeshes[eye] = graphics::Mesh::createIndexedTriangles_P3F((uint32_t)vertices.size(), (uint32_t)indices.size(), vertices.data(), indices.data()); + }); + } + + if (_stencilMeshesInitialized) { + return [&](gpu::Batch& batch) { + for (auto& mesh : _stencilMeshes) { + batch.setIndexBuffer(mesh->getIndexBuffer()); + batch.setInputFormat((mesh->getVertexFormat())); + batch.setInputStream(0, mesh->getVertexStream()); + + // Draw + auto part = mesh->getPartBuffer().get(0); + batch.drawIndexed(gpu::TRIANGLES, part._numIndices, part._startIndex); + } + }; + } + } + return nullptr; +} \ No newline at end of file diff --git a/plugins/oculus/src/OculusBaseDisplayPlugin.h b/plugins/oculus/src/OculusBaseDisplayPlugin.h index 1abb7cdad7..d442c365ae 100644 --- a/plugins/oculus/src/OculusBaseDisplayPlugin.h +++ b/plugins/oculus/src/OculusBaseDisplayPlugin.h @@ -16,6 +16,8 @@ #define OVRPL_DISABLED #include +#include + class OculusBaseDisplayPlugin : public HmdDisplayPlugin { using Parent = HmdDisplayPlugin; public: @@ -34,6 +36,9 @@ public: QRectF getPlayAreaRect() override; QVector getSensorPositions() override; + virtual StencilMaskMode getStencilMaskMode() const override { return StencilMaskMode::MESH; } + virtual StencilMaskMeshOperator getStencilMaskMeshOperator() override; + protected: void customizeContext() override; void uncustomizeContext() override; @@ -52,4 +57,7 @@ protected: // ovrLayerEyeFovDepth _depthLayer; bool _hmdMounted { false }; bool _visible { true }; + + std::array _stencilMeshes; + bool _stencilMeshesInitialized { false }; }; diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index 11d941dcd0..cd318dd9b4 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -784,3 +784,48 @@ QRectF OpenVrDisplayPlugin::getPlayAreaRect() { return QRectF(center.x, center.y, dimensions.x, dimensions.y); } + +DisplayPlugin::StencilMaskMeshOperator OpenVrDisplayPlugin::getStencilMaskMeshOperator() { + if (_system) { + if (!_stencilMeshesInitialized) { + _stencilMeshesInitialized = true; + for (auto eye : VR_EYES) { + vr::HiddenAreaMesh_t stencilMesh = _system->GetHiddenAreaMesh(eye); + if (stencilMesh.pVertexData && stencilMesh.unTriangleCount > 0) { + std::vector vertices; + std::vector indices; + + const int NUM_INDICES_PER_TRIANGLE = 3; + int numIndices = stencilMesh.unTriangleCount * NUM_INDICES_PER_TRIANGLE; + vertices.reserve(numIndices); + indices.reserve(numIndices); + for (int i = 0; i < numIndices; i++) { + vr::HmdVector2_t vertex2D = stencilMesh.pVertexData[i]; + // We need the vertices in clip space + vertices.emplace_back(vertex2D.v[0] - (1.0f - (float)eye), 2.0f * vertex2D.v[1] - 1.0f, 0.0f); + indices.push_back(i); + } + + _stencilMeshes[eye] = graphics::Mesh::createIndexedTriangles_P3F((uint32_t)vertices.size(), (uint32_t)indices.size(), vertices.data(), indices.data()); + } else { + _stencilMeshesInitialized = false; + } + } + } + + if (_stencilMeshesInitialized) { + return [&](gpu::Batch& batch) { + for (auto& mesh : _stencilMeshes) { + batch.setIndexBuffer(mesh->getIndexBuffer()); + batch.setInputFormat((mesh->getVertexFormat())); + batch.setInputStream(0, mesh->getVertexStream()); + + // Draw + auto part = mesh->getPartBuffer().get(0); + batch.drawIndexed(gpu::TRIANGLES, part._numIndices, part._startIndex); + } + }; + } + } + return nullptr; +} \ No newline at end of file diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.h b/plugins/openvr/src/OpenVrDisplayPlugin.h index 265f328920..4b042a700d 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.h +++ b/plugins/openvr/src/OpenVrDisplayPlugin.h @@ -13,6 +13,8 @@ #include +#include + const float TARGET_RATE_OpenVr = 90.0f; // FIXME: get from sdk tracked device property? This number is vive-only. namespace gl { @@ -67,6 +69,9 @@ public: QRectF getPlayAreaRect() override; + virtual StencilMaskMode getStencilMaskMode() const override { return StencilMaskMode::MESH; } + virtual StencilMaskMeshOperator getStencilMaskMeshOperator() override; + protected: bool internalActivate() override; void internalDeactivate() override; @@ -94,4 +99,7 @@ private: bool _asyncReprojectionActive { false }; bool _hmdMounted { false }; + + std::array _stencilMeshes; + bool _stencilMeshesInitialized { false }; };