diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a6f7e63247..59b55fe22f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6755,6 +6755,12 @@ void Application::update(float deltaTime) { if (!getActiveDisplayPlugin()->isActive()) { getMain3DScene()->processTransactionQueue(); } + + // decide if the sensorToWorldMatrix is changing in a way that warrents squeezing the edges of the view down + if (getActiveDisplayPlugin()->isHmd()) { + PerformanceTimer perfTimer("squeezeVision"); + _visionSqueeze.updateVisionSqueeze(myAvatar->getSensorToWorldMatrix(), deltaTime); + } } void Application::updateRenderArgs(float deltaTime) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 2d6821bbd9..1e7180e203 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -78,6 +78,7 @@ #include #include "Sound.h" +#include "VisionSqueeze.h" class GLCanvas; class FaceTracker; @@ -364,6 +365,9 @@ public: void forceLoginWithTokens(const QString& tokens); void setConfigFileURL(const QString& fileUrl); + // used by preferences and HMDScriptingInterface... + VisionSqueeze& getVisionSqueeze() { return _visionSqueeze; } + signals: void svoImportRequested(const QString& url); @@ -731,6 +735,7 @@ private: bool _loginDialogPoppedUp{ false }; bool _desktopRootItemCreated{ false }; + bool _developerMenuVisible{ false }; QString _previousAvatarSkeletonModel; float _previousAvatarTargetScale; @@ -837,5 +842,7 @@ private: bool _resumeAfterLoginDialogActionTaken_SafeToRun { false }; bool _startUpFinished { false }; bool _overrideEntry { false }; + + VisionSqueeze _visionSqueeze; }; #endif // hifi_Application_h diff --git a/interface/src/VisionSqueeze.cpp b/interface/src/VisionSqueeze.cpp new file mode 100644 index 0000000000..cb895cc74f --- /dev/null +++ b/interface/src/VisionSqueeze.cpp @@ -0,0 +1,213 @@ +// +// VisionSqueeze.cpp +// interface/src +// +// Created by Seth Alves on 2019-3-13. +// 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 +// + +#include "VisionSqueeze.h" + +#include +#include + +#include + +#include "Application.h" + +VisionSqueeze::VisionSqueeze() : + _visionSqueezeEnabled(_visionSqueezeEnabledSetting.get()), + _visionSqueezeRatioX(_visionSqueezeRatioXSetting.get()), + _visionSqueezeRatioY(_visionSqueezeRatioYSetting.get()), + _visionSqueezeUnSqueezeDelay(_visionSqueezeUnSqueezeDelaySetting.get()), + _visionSqueezeUnSqueezeSpeed(_visionSqueezeUnSqueezeSpeedSetting.get()), + _visionSqueezeTransition(_visionSqueezeTransitionSetting.get()), + _visionSqueezePerEye(_visionSqueezePerEyeSetting.get()), + _visionSqueezeGroundPlaneY(_visionSqueezeGroundPlaneYSetting.get()), + _visionSqueezeSpotlightSize(_visionSqueezeSpotlightSizeSetting.get()), + _visionSqueezeTurningXFactor(_visionSqueezeTurningXFactorSetting.get()), + _visionSqueezeTurningYFactor(_visionSqueezeTurningYFactorSetting.get()) { +} + +void VisionSqueeze::setVisionSqueezeEnabled(bool value) { + if (value != _visionSqueezeEnabled) { + _visionSqueezeEnabled = value; + _visionSqueezeEnabledSetting.set(_visionSqueezeEnabled); + } +} + +void VisionSqueeze::setVisionSqueezeRatioX(float value) { + if (value != _visionSqueezeRatioX) { + _visionSqueezeRatioX = value; + _visionSqueezeRatioXSetting.set(_visionSqueezeRatioX); + } +} + +void VisionSqueeze::setVisionSqueezeRatioY(float value) { + if (value != _visionSqueezeRatioY) { + _visionSqueezeRatioY = value; + _visionSqueezeRatioYSetting.set(_visionSqueezeRatioY); + } +} + +void VisionSqueeze::setVisionSqueezeUnSqueezeDelay(float value) { + if (value != _visionSqueezeUnSqueezeDelay) { + _visionSqueezeUnSqueezeDelay = value; + _visionSqueezeUnSqueezeDelaySetting.set(_visionSqueezeUnSqueezeDelay); + } +} + +void VisionSqueeze::setVisionSqueezeUnSqueezeSpeed(float value) { + if (value != _visionSqueezeUnSqueezeSpeed) { + _visionSqueezeUnSqueezeSpeed = value; + _visionSqueezeUnSqueezeSpeedSetting.set(_visionSqueezeUnSqueezeSpeed); + } +} + +void VisionSqueeze::setVisionSqueezeTransition(float value) { + if (value != _visionSqueezeTransition) { + _visionSqueezeTransition = value; + _visionSqueezeTransitionSetting.set(_visionSqueezeTransition); + } +} + +void VisionSqueeze::setVisionSqueezePerEye(int value) { + if (value != _visionSqueezePerEye) { + _visionSqueezePerEye = value; + _visionSqueezePerEyeSetting.set(_visionSqueezePerEye); + } +} + +void VisionSqueeze::setVisionSqueezeGroundPlaneY(float value) { + if (value != _visionSqueezeGroundPlaneY) { + _visionSqueezeGroundPlaneY = value; + _visionSqueezeGroundPlaneYSetting.set(_visionSqueezeGroundPlaneY); + } +} + +void VisionSqueeze::setVisionSqueezeSpotlightSize(float value) { + if (value != _visionSqueezeSpotlightSize) { + _visionSqueezeSpotlightSize = value; + _visionSqueezeSpotlightSizeSetting.set(_visionSqueezeSpotlightSize); + } +} + +void VisionSqueeze::setVisionSqueezeTurningXFactor(float value) { + if (value != _visionSqueezeTurningXFactor) { + _visionSqueezeTurningXFactor = value; + _visionSqueezeTurningXFactorSetting.set(_visionSqueezeTurningXFactor); + } +} + +void VisionSqueeze::setVisionSqueezeTurningYFactor(float value) { + if (value != _visionSqueezeTurningYFactor) { + _visionSqueezeTurningYFactor = value; + _visionSqueezeTurningYFactorSetting.set(_visionSqueezeTurningYFactor); + } +} + +void VisionSqueeze::updateVisionSqueeze(const glm::mat4& sensorToWorldMatrix, float deltaTime) { + + const float SENSOR_TO_WORLD_TRANS_EPSILON = 0.0001f; + const float SENSOR_TO_WORLD_TRANS_Y_EPSILON = 0.01f; + const float SENSOR_TO_WORLD_TRANS_ITS_A_TELEPORT_SQUARED = 2.0f; + const float SENSOR_TO_WORLD_ROT_EPSILON = 0.000005f; + const float SENSOR_TO_WORLD_ROT_ITS_A_SNAP_TURN = 0.99f; + const float VISION_SQUEEZE_TP_LOCKOUT = 0.1f; // seconds + + glm::vec3 scale; + glm::quat rotation; + glm::vec3 translation; + glm::vec3 skew; + glm::vec4 perspective; + glm::decompose(sensorToWorldMatrix, scale, rotation, translation, skew, perspective); + + if (!_visionSqueezeEnabled) { + _squeezeVision = false; + _squeezeVisionTurning = false; + } else if (_visionSqueezeLockout > 0.0f) { + _visionSqueezeLockout -= deltaTime; + } else { + _squeezeVision = false; + _squeezeVisionTurning = false; + glm::vec3 absTransDelta = glm::abs(translation - _prevTranslation); + float rotDot = fabsf(glm::dot(rotation, _prevRotation)); + + // if the avatar has just teleported or snap-turned, briefly disable triggering of vision-squeeze + if (glm::length2(translation - _prevTranslation) > SENSOR_TO_WORLD_TRANS_ITS_A_TELEPORT_SQUARED || + rotDot < SENSOR_TO_WORLD_ROT_ITS_A_SNAP_TURN) { + _visionSqueezeLockout = VISION_SQUEEZE_TP_LOCKOUT; + _squeezeVision = true; + _squeezeVisionTurning = true; + } else if (rotDot < 1.0f - SENSOR_TO_WORLD_ROT_EPSILON) { + _squeezeVision = true; + _squeezeVisionTurning = true; + } else if (absTransDelta.x > SENSOR_TO_WORLD_TRANS_EPSILON || + absTransDelta.y > SENSOR_TO_WORLD_TRANS_Y_EPSILON || + absTransDelta.z > SENSOR_TO_WORLD_TRANS_EPSILON) { + _squeezeVision = true; + _squeezeVisionTurning = false; + } + } + + _prevTranslation = translation; + _prevRotation = rotation; + + static quint64 lastSqueezeTime = 0; + quint64 now = usecTimestampNow(); + static float visionSqueezeX = 0.0f; // 0.0 -- unobstructed, 1.0 -- fully blocked + static float visionSqueezeY = 0.0f; // 0.0 -- unobstructed, 1.0 -- fully blocked + + if (_squeezeVision) { + float ratioX = getVisionSqueezeRatioX(); + float ratioY = getVisionSqueezeRatioY(); + + if (ratioX >= 0.0f) { + if (_squeezeVisionTurning) { + ratioX += (1.0f - ratioX) * getVisionSqueezeTurningXFactor(); + } + float newVisionSqueezeX = ratioX; + if (newVisionSqueezeX >= visionSqueezeX) { + lastSqueezeTime = now; + visionSqueezeX = newVisionSqueezeX; + } + } else { + visionSqueezeX = -1.0f; + } + + if (ratioY >= 0.0f) { + float newVisionSqueezeY = ratioY; + if (newVisionSqueezeY >= visionSqueezeY) { + lastSqueezeTime = now; + visionSqueezeY = newVisionSqueezeY; + } + } else { + visionSqueezeY = -1.0f; + } + } + + float unsqueezeAmount = deltaTime * getVisionSqueezeUnSqueezeSpeed(); + if (now - lastSqueezeTime > getVisionSqueezeUnSqueezeDelay() * USECS_PER_SECOND) { + visionSqueezeX -= unsqueezeAmount; + if (visionSqueezeX < 0.0f) { + visionSqueezeX = -1.0f; + } + visionSqueezeY -= unsqueezeAmount; + if (visionSqueezeY < 0.0f) { + visionSqueezeY = -1.0f; + } + } + + std::shared_ptr hmdDisplayPlugin = + std::dynamic_pointer_cast(qApp->getActiveDisplayPlugin()); + if (hmdDisplayPlugin) { + hmdDisplayPlugin->updateVisionSqueezeParameters(visionSqueezeX, visionSqueezeY, + getVisionSqueezeTransition(), + getVisionSqueezePerEye(), + getVisionSqueezeGroundPlaneY(), + getVisionSqueezeSpotlightSize()); + } +} diff --git a/interface/src/VisionSqueeze.h b/interface/src/VisionSqueeze.h new file mode 100644 index 0000000000..d32f608ed0 --- /dev/null +++ b/interface/src/VisionSqueeze.h @@ -0,0 +1,108 @@ +// +// VisionSqueeze.h +// interface/src +// +// Created by Seth Alves on 2019-3-13. +// 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_VisionSqueeze_h +#define hifi_VisionSqueeze_h + +#include +#include + +#include + +static const float DEFAULT_VISION_SQUEEZE_TURNING_X_FACTOR = 0.51f; +static const float DEFAULT_VISION_SQUEEZE_TURNING_Y_FACTOR = 0.36f; +static const float DEFAULT_VISION_SQUEEZE_UNSQUEEZE_DELAY = 0.2f; // seconds +static const float DEFAULT_VISION_SQUEEZE_UNSQUEEZE_SPEED = 3.0f; +static const float DEFAULT_VISION_SQUEEZE_TRANSITION = 0.25f; +static const int DEFAULT_VISION_SQUEEZE_PER_EYE = 1; +static const float DEFAULT_VISION_SQUEEZE_GROUND_PLANE_Y = 0.0f; +static const float DEFAULT_VISION_SQUEEZE_SPOTLIGHT_SIZE = 6.0f; + + +class VisionSqueeze { + +public: + + VisionSqueeze(); + + bool getVisionSqueezeEnabled() const { return _visionSqueezeEnabled; } + void setVisionSqueezeEnabled(bool value); + float getVisionSqueezeRatioX() const { return _visionSqueezeRatioX; } + float getVisionSqueezeRatioY() const { return _visionSqueezeRatioY; } + void setVisionSqueezeRatioX(float value); + void setVisionSqueezeRatioY(float value); + float getVisionSqueezeUnSqueezeDelay() const { return _visionSqueezeUnSqueezeDelay; } + void setVisionSqueezeUnSqueezeDelay(float value); + float getVisionSqueezeUnSqueezeSpeed() const { return _visionSqueezeUnSqueezeSpeed; } + void setVisionSqueezeUnSqueezeSpeed(float value); + float getVisionSqueezeTransition() const { return _visionSqueezeTransition; } + void setVisionSqueezeTransition(float value); + int getVisionSqueezePerEye() const { return _visionSqueezePerEye; } + void setVisionSqueezePerEye(int value); + float getVisionSqueezeGroundPlaneY() const { return _visionSqueezeGroundPlaneY; } + void setVisionSqueezeGroundPlaneY(float value); + float getVisionSqueezeSpotlightSize() const { return _visionSqueezeSpotlightSize; } + void setVisionSqueezeSpotlightSize(float value); + float getVisionSqueezeTurningXFactor() const { return _visionSqueezeTurningXFactor; } + void setVisionSqueezeTurningXFactor(float value); + float getVisionSqueezeTurningYFactor() const { return _visionSqueezeTurningYFactor; } + void setVisionSqueezeTurningYFactor(float value); + + void updateVisionSqueeze(const glm::mat4& sensorToWorldMatrix, float deltaTime); + + // state variable accessors used by Application.cpp... + bool getSqueezeVision() const { return _squeezeVision; } + void setSqueezeVision(bool value) { _squeezeVision = value; } + bool getSqueezeVisionTurning() const { return _squeezeVisionTurning; } + void setSqueezeVisionTurning(bool value) { _squeezeVisionTurning = value; } + +private: + Setting::Handle _visionSqueezeEnabledSetting {"visionSqueezeEnabled", false}; + Setting::Handle _visionSqueezeRatioXSetting {"visionSqueezeRatioX", 0.0f}; + Setting::Handle _visionSqueezeRatioYSetting {"visionSqueezeRatioY", 0.0f}; + Setting::Handle _visionSqueezeUnSqueezeDelaySetting {"visionSqueezeUnSqueezeDelay", + DEFAULT_VISION_SQUEEZE_UNSQUEEZE_DELAY}; + Setting::Handle _visionSqueezeUnSqueezeSpeedSetting {"visionSqueezeUnSqueezeSpeed", + DEFAULT_VISION_SQUEEZE_UNSQUEEZE_SPEED}; + Setting::Handle _visionSqueezeTransitionSetting {"visionSqueezeTransition", DEFAULT_VISION_SQUEEZE_TRANSITION}; + Setting::Handle _visionSqueezePerEyeSetting {"visionSqueezePerEye", DEFAULT_VISION_SQUEEZE_PER_EYE}; + Setting::Handle _visionSqueezeGroundPlaneYSetting {"visionSqueezeGroundPlaneY", + DEFAULT_VISION_SQUEEZE_GROUND_PLANE_Y}; + Setting::Handle _visionSqueezeSpotlightSizeSetting {"visionSqueezeSpotlightSize", + DEFAULT_VISION_SQUEEZE_SPOTLIGHT_SIZE}; + Setting::Handle _visionSqueezeTurningXFactorSetting {"visionSqueezeTurningXFactor", + DEFAULT_VISION_SQUEEZE_TURNING_X_FACTOR}; + Setting::Handle _visionSqueezeTurningYFactorSetting {"visionSqueezeTurningYFactor", + DEFAULT_VISION_SQUEEZE_TURNING_Y_FACTOR}; + + + // these are readable and writable from the scripting interface (on a different thread), so make them atomic + std::atomic _visionSqueezeEnabled { false }; + std::atomic _visionSqueezeRatioX { 0.0f }; + std::atomic _visionSqueezeRatioY { 0.0f }; + std::atomic _visionSqueezeUnSqueezeDelay { DEFAULT_VISION_SQUEEZE_UNSQUEEZE_DELAY }; // seconds + std::atomic _visionSqueezeUnSqueezeSpeed { DEFAULT_VISION_SQUEEZE_UNSQUEEZE_SPEED }; + std::atomic _visionSqueezeTransition { DEFAULT_VISION_SQUEEZE_TRANSITION }; + std::atomic _visionSqueezePerEye { DEFAULT_VISION_SQUEEZE_PER_EYE }; + std::atomic _visionSqueezeGroundPlaneY { DEFAULT_VISION_SQUEEZE_GROUND_PLANE_Y }; + std::atomic _visionSqueezeSpotlightSize { DEFAULT_VISION_SQUEEZE_SPOTLIGHT_SIZE }; + std::atomic _visionSqueezeTurningXFactor { DEFAULT_VISION_SQUEEZE_TURNING_X_FACTOR }; + std::atomic _visionSqueezeTurningYFactor { DEFAULT_VISION_SQUEEZE_TURNING_Y_FACTOR }; + + bool _squeezeVision { false }; + bool _squeezeVisionTurning { false }; + + float _visionSqueezeLockout { 0.0 }; + glm::vec3 _prevTranslation; + glm::quat _prevRotation; +}; + +#endif // hifi_VisionSqueeze_h diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index cce2af466d..acb199bb97 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -6144,4 +6144,3 @@ void MyAvatar::sendPacket(const QUuid& entityID) const { }); } } - diff --git a/interface/src/scripting/HMDScriptingInterface.cpp b/interface/src/scripting/HMDScriptingInterface.cpp index baca6250d2..8f7ae7c4dc 100644 --- a/interface/src/scripting/HMDScriptingInterface.cpp +++ b/interface/src/scripting/HMDScriptingInterface.cpp @@ -236,3 +236,83 @@ QVariant HMDScriptingInterface::getPlayAreaRect() { QVector HMDScriptingInterface::getSensorPositions() { return qApp->getActiveDisplayPlugin()->getSensorPositions(); } + +float HMDScriptingInterface::getVisionSqueezeRatioX() const { + return qApp->getVisionSqueeze().getVisionSqueezeRatioX(); +} + +float HMDScriptingInterface::getVisionSqueezeRatioY() const { + return qApp->getVisionSqueeze().getVisionSqueezeRatioY(); +} + +void HMDScriptingInterface::setVisionSqueezeRatioX(float value) { + qApp->getVisionSqueeze().setVisionSqueezeRatioX(value); +} + +void HMDScriptingInterface::setVisionSqueezeRatioY(float value) { + qApp->getVisionSqueeze().setVisionSqueezeRatioY(value); +} + +float HMDScriptingInterface::getVisionSqueezeUnSqueezeDelay() const { + return qApp->getVisionSqueeze().getVisionSqueezeUnSqueezeDelay(); +} + +void HMDScriptingInterface::setVisionSqueezeUnSqueezeDelay(float value) { + qApp->getVisionSqueeze().setVisionSqueezeUnSqueezeDelay(value); +} + +float HMDScriptingInterface::getVisionSqueezeUnSqueezeSpeed() const { + return qApp->getVisionSqueeze().getVisionSqueezeUnSqueezeSpeed(); +} + +void HMDScriptingInterface::setVisionSqueezeUnSqueezeSpeed(float value) { + qApp->getVisionSqueeze().setVisionSqueezeUnSqueezeSpeed(value); +} + +float HMDScriptingInterface::getVisionSqueezeTransition() const { + return qApp->getVisionSqueeze().getVisionSqueezeTransition(); +} + +void HMDScriptingInterface::setVisionSqueezeTransition(float value) { + qApp->getVisionSqueeze().setVisionSqueezeTransition(value); +} + +int HMDScriptingInterface::getVisionSqueezePerEye() const { + return qApp->getVisionSqueeze().getVisionSqueezePerEye(); +} + +void HMDScriptingInterface::setVisionSqueezePerEye(int value) { + qApp->getVisionSqueeze().setVisionSqueezePerEye(value); +} + +float HMDScriptingInterface::getVisionSqueezeGroundPlaneY() const { + return qApp->getVisionSqueeze().getVisionSqueezeGroundPlaneY(); +} + +void HMDScriptingInterface::setVisionSqueezeGroundPlaneY(float value) { + qApp->getVisionSqueeze().setVisionSqueezeGroundPlaneY(value); +} + +float HMDScriptingInterface::getVisionSqueezeSpotlightSize() const { + return qApp->getVisionSqueeze().getVisionSqueezeSpotlightSize(); +} + +void HMDScriptingInterface::setVisionSqueezeSpotlightSize(float value) { + qApp->getVisionSqueeze().setVisionSqueezeSpotlightSize(value); +} + +float HMDScriptingInterface::getVisionSqueezeTurningXFactor() const { + return qApp->getVisionSqueeze().getVisionSqueezeTurningXFactor(); +} + +void HMDScriptingInterface::setVisionSqueezeTurningXFactor(float value) { + qApp->getVisionSqueeze().setVisionSqueezeTurningXFactor(value); +} + +float HMDScriptingInterface::getVisionSqueezeTurningYFactor() const { + return qApp->getVisionSqueeze().getVisionSqueezeTurningYFactor(); +} + +void HMDScriptingInterface::setVisionSqueezeTurningYFactor(float value) { + qApp->getVisionSqueeze().setVisionSqueezeTurningYFactor(value); +} diff --git a/interface/src/scripting/HMDScriptingInterface.h b/interface/src/scripting/HMDScriptingInterface.h index 335816bf7c..ad3b4404a8 100644 --- a/interface/src/scripting/HMDScriptingInterface.h +++ b/interface/src/scripting/HMDScriptingInterface.h @@ -84,6 +84,17 @@ class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Depen Q_PROPERTY(QVariant playArea READ getPlayAreaRect); Q_PROPERTY(QVector sensorPositions READ getSensorPositions); + Q_PROPERTY(float visionSqueezeRatioX READ getVisionSqueezeRatioX WRITE setVisionSqueezeRatioX); + Q_PROPERTY(float visionSqueezeRatioY READ getVisionSqueezeRatioY WRITE setVisionSqueezeRatioY); + Q_PROPERTY(float visionSqueezeUnSqueezeDelay READ getVisionSqueezeUnSqueezeDelay WRITE setVisionSqueezeUnSqueezeDelay); + Q_PROPERTY(float visionSqueezeUnSqueezeSpeed READ getVisionSqueezeUnSqueezeSpeed WRITE setVisionSqueezeUnSqueezeSpeed); + Q_PROPERTY(float visionSqueezeTransition READ getVisionSqueezeTransition WRITE setVisionSqueezeTransition); + Q_PROPERTY(int visionSqueezePerEye READ getVisionSqueezePerEye WRITE setVisionSqueezePerEye); + Q_PROPERTY(float visionSqueezeGroundPlaneY READ getVisionSqueezeGroundPlaneY WRITE setVisionSqueezeGroundPlaneY); + Q_PROPERTY(float visionSqueezeSpotlightSize READ getVisionSqueezeSpotlightSize WRITE setVisionSqueezeSpotlightSize); + Q_PROPERTY(float visionSqueezeTurningXFactor READ getVisionSqueezeTurningXFactor WRITE setVisionSqueezeTurningXFactor); + Q_PROPERTY(float visionSqueezeTurningYFactor READ getVisionSqueezeTurningYFactor WRITE setVisionSqueezeTurningYFactor); + public: /**jsdoc @@ -339,6 +350,27 @@ public: */ Q_INVOKABLE void openTablet(bool contextualMode = false); + float getVisionSqueezeRatioX() const; + float getVisionSqueezeRatioY() const; + void setVisionSqueezeRatioX(float value); + void setVisionSqueezeRatioY(float value); + float getVisionSqueezeUnSqueezeDelay() const; + void setVisionSqueezeUnSqueezeDelay(float value); + float getVisionSqueezeUnSqueezeSpeed() const; + void setVisionSqueezeUnSqueezeSpeed(float value); + float getVisionSqueezeTransition() const; + void setVisionSqueezeTransition(float value); + int getVisionSqueezePerEye() const; + void setVisionSqueezePerEye(int value); + float getVisionSqueezeGroundPlaneY() const; + void setVisionSqueezeGroundPlaneY(float value); + float getVisionSqueezeSpotlightSize() const; + void setVisionSqueezeSpotlightSize(float value); + float getVisionSqueezeTurningXFactor() const; + void setVisionSqueezeTurningXFactor(float value); + float getVisionSqueezeTurningYFactor() const; + void setVisionSqueezeTurningYFactor(float value); + signals: /**jsdoc * Triggered when a request to show or hide models of the HMD hand controllers is made using diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index ce70e91128..ec15dd8111 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include "scripting/RenderScriptingInterface.h" #include "Application.h" #include "DialogsManager.h" @@ -377,6 +378,33 @@ void setupPreferences() { preference->setDecimals(2); preferences->addPreference(preference); } + { + auto getter = []()->bool { + return qApp->getVisionSqueeze().getVisionSqueezeEnabled(); + }; + auto setter = [](bool value) { + qApp->getVisionSqueeze().setVisionSqueezeEnabled(value); + }; + auto preference = new CheckPreference(VR_MOVEMENT, "Enable HMD Comfort Mode", getter, setter); + preferences->addPreference(preference); + } + { + const float sliderPositions = 5.0f; + auto getter = [sliderPositions]()->float { + return roundf(sliderPositions * qApp->getVisionSqueeze().getVisionSqueezeRatioX()); + }; + auto setter = [sliderPositions](float value) { + float ratio = value / sliderPositions; + qApp->getVisionSqueeze().setVisionSqueezeRatioX(ratio); + qApp->getVisionSqueeze().setVisionSqueezeRatioY(ratio); + }; + auto preference = new SpinnerSliderPreference(VR_MOVEMENT, "Comfort Mode", getter, setter); + preference->setMin(0.0f); + preference->setMax(sliderPositions); + preference->setStep(1.0f); + preference->setDecimals(0); + preferences->addPreference(preference); + } { auto getter = [myAvatar]()->bool { return myAvatar->getShowPlayArea(); }; auto setter = [myAvatar](bool value) { myAvatar->setShowPlayArea(value); }; diff --git a/libraries/display-plugins/src/display-plugins/DrawTextureWithVisionSqueeze.slf b/libraries/display-plugins/src/display-plugins/DrawTextureWithVisionSqueeze.slf new file mode 100644 index 0000000000..1092608421 --- /dev/null +++ b/libraries/display-plugins/src/display-plugins/DrawTextureWithVisionSqueeze.slf @@ -0,0 +1,104 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// DrawTextureWithVisionSqueeze.frag +// +// Draw texture 0 fetched at texcoord.xy +// +// Created by Seth Alves on 2019-2-15 +// 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 +// + +<@include display-plugins/VisionSqueeze.slh@> + +struct DrawTextureWithVisionSqueezeParams { + float _visionSqueezeX; + float _visionSqueezeY; + float _spareA; + float _spareB; + mat4 _leftProjection; + mat4 _rightProjection; + mat4 _hmdSensorMatrix; + float _visionSqueezeTransition; + int _visionSqueezePerEye; + float _visionSqueezeGroundPlaneY; + float _visionSqueezeSpotlightSize; +}; + + +LAYOUT(binding=0) uniform sampler2D colorMap; + +// binding=1 must match drawTextureWithSqueezeParamsSlot in OpenGLDisplayPlugin.h +LAYOUT(binding=1) uniform drawTextureWithSqueezeMappingParamsBuffer { + DrawTextureWithVisionSqueezeParams params; +}; + +layout(location=0) in vec2 varTexCoord0; +layout(location=0) out vec4 outFragColor; + + +float getVisionSqueezeX() { + return params._visionSqueezeX; +} +float getVisionSqueezeY() { + return params._visionSqueezeY; +} +float getVisionSqueezeTransitionRatio() { + return params._visionSqueezeTransition; +} +int getVisionSqueezePerEye() { + return params._visionSqueezePerEye; +} +float getVisionSqueezeGroundPlaneY() { + return params._visionSqueezeGroundPlaneY; +} +float getVisionSqueezeSpotlightSize() { + return params._visionSqueezeSpotlightSize; +} +mat4 getProjectionMatrix(float eye) { + if (eye == 0.0) { + return params._leftProjection; + } else { + return params._rightProjection; + } +} +mat4 getHMDSensorMatrix() { + return params._hmdSensorMatrix; +} + + +void main(void) { + + float side = float(varTexCoord0.x > 0.5); + mat4 leftProjectionMatrix = getProjectionMatrix(0.0); + vec4 fovTan = extractFovTan(leftProjectionMatrix); + vec2 focalPointNDC = extractFocalPoint(fovTan); + focalPointNDC.x -= 2.0 * focalPointNDC.x * (1.0 - side); + vec2 focalPointUV = 0.5 * (focalPointNDC + vec2(1.0)); + + // block edges of vision to avoid sickness + vec2 visionSqueezeRatios = vec2(getVisionSqueezeX(), getVisionSqueezeY()); + bool perEye = getVisionSqueezePerEye() > 0; + float frac = squeezeVision(perEye, varTexCoord0, visionSqueezeRatios, getVisionSqueezeTransitionRatio(), focalPointUV); + + if (frac == 0.0) { + // world only + outFragColor = texture(colorMap, varTexCoord0); + } else { + // grid on the floor only or mixed + mat4 hmdSensorMatrix = getHMDSensorMatrix(); + mat4 projectionMatrix = getProjectionMatrix(side); + mat4 projectionInverse = inverse(projectionMatrix); + float groundPlaneY = getVisionSqueezeGroundPlaneY(); + float spotLightSize = getVisionSqueezeSpotlightSize(); + vec4 gridColor = vec4(gridFloor(varTexCoord0, hmdSensorMatrix, projectionInverse, groundPlaneY, spotLightSize), 1.0); + + vec4 preSqueezeColor = texture(colorMap, varTexCoord0); + // mix between grid and world + outFragColor = mix(preSqueezeColor, gridColor, frac); + } +} diff --git a/libraries/display-plugins/src/display-plugins/DrawTextureWithVisionSqueeze.slp b/libraries/display-plugins/src/display-plugins/DrawTextureWithVisionSqueeze.slp new file mode 100644 index 0000000000..c2c4bfbebd --- /dev/null +++ b/libraries/display-plugins/src/display-plugins/DrawTextureWithVisionSqueeze.slp @@ -0,0 +1 @@ +VERTEX gpu::vertex::DrawUnitQuadTexcoord diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index ebbf888e83..1d0fc5a128 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -392,6 +392,9 @@ void OpenGLDisplayPlugin::customizeContext() { _drawTexturePipeline = gpu::Pipeline::create(gpu::Shader::createProgram(DrawTexture), scissorState); + _drawTextureSqueezePipeline = + gpu::Pipeline::create(gpu::Shader::createProgram(shader::display_plugins::program::DrawTextureWithVisionSqueeze), scissorState); + _linearToSRGBPipeline = gpu::Pipeline::create(gpu::Shader::createProgram(DrawTextureLinearToSRGB), scissorState); _SRGBToLinearPipeline = gpu::Pipeline::create(gpu::Shader::createProgram(DrawTextureSRGBToLinear), scissorState); @@ -407,6 +410,7 @@ void OpenGLDisplayPlugin::customizeContext() { void OpenGLDisplayPlugin::uncustomizeContext() { _drawTexturePipeline.reset(); + _drawTextureSqueezePipeline.reset(); _linearToSRGBPipeline.reset(); _SRGBToLinearPipeline.reset(); _cursorPipeline.reset(); @@ -629,6 +633,10 @@ void OpenGLDisplayPlugin::compositePointer() { }); } +void OpenGLDisplayPlugin::setupCompositeScenePipeline(gpu::Batch& batch) { + batch.setPipeline(_drawTexturePipeline); +} + void OpenGLDisplayPlugin::compositeScene() { render([&](gpu::Batch& batch) { batch.enableStereo(false); @@ -637,8 +645,8 @@ void OpenGLDisplayPlugin::compositeScene() { batch.setStateScissorRect(ivec4(uvec2(), _compositeFramebuffer->getSize())); batch.resetViewTransform(); batch.setProjectionTransform(mat4()); - batch.setPipeline(_drawTexturePipeline); batch.setResourceTexture(0, _currentFrame->framebuffer->getRenderBuffer(0)); + setupCompositeScenePipeline(batch); batch.draw(gpu::TRIANGLE_STRIP, 4); }); } @@ -958,4 +966,3 @@ void OpenGLDisplayPlugin::copyTextureToQuickFramebuffer(NetworkTexturePointer ne gpu::PipelinePointer OpenGLDisplayPlugin::getRenderTexturePipeline() { return _drawTexturePipeline; } - diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index 6fb36bff90..5eebd92fba 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -100,6 +100,7 @@ protected: virtual QThread::Priority getPresentPriority() { return QThread::HighPriority; } virtual void compositeLayers(); + virtual void setupCompositeScenePipeline(gpu::Batch& batch); virtual void compositeScene(); virtual void compositePointer(); virtual void compositeExtra(){}; @@ -155,6 +156,7 @@ protected: gpu::PipelinePointer _mirrorHUDPipeline; gpu::ShaderPointer _mirrorHUDPS; gpu::PipelinePointer _drawTexturePipeline; + gpu::PipelinePointer _drawTextureSqueezePipeline; gpu::PipelinePointer _linearToSRGBPipeline; gpu::PipelinePointer _SRGBToLinearPipeline; gpu::PipelinePointer _cursorPipeline; diff --git a/libraries/display-plugins/src/display-plugins/VisionSqueeze.slh b/libraries/display-plugins/src/display-plugins/VisionSqueeze.slh new file mode 100644 index 0000000000..28e5f12337 --- /dev/null +++ b/libraries/display-plugins/src/display-plugins/VisionSqueeze.slh @@ -0,0 +1,106 @@ +// Generated on <$_SCRIBE_DATE$> +// +// Created by Seth Alves on 2019-2-13. +// 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 +// + +<@if not VISION_SQUEEZE_SLH@> +<@def VISION_SQUEEZE_SLH@> + + +float ellipse(vec2 coord, vec2 centerUV, vec2 semiAxis) { + return pow(coord.x - centerUV.x, 2.0) / semiAxis.x + pow(coord.y - centerUV.y, 2.0) / semiAxis.y; +} + +vec4 extractFovTan(mat4 m) { + mat4 mt = transpose(m); + vec4 v; + vec4 result; + + // x -- Left + v = mt * vec4(1.0, 0.0, 0.0, 1.0); + result.x = -(v.z / v.x); + // y -- Right + v = mt * vec4(-1.0, 0.0, 0.0, 1.0); + result.y = (v.z / v.x); + // z -- Down + v = mt * vec4(0.0, 1.0, 0.0, 1.0); + result.z = -(v.z / v.y); + // w -- Up + v = mt * vec4(0.0, -1.0, 0.0, 1.0); + result.w = v.z / v.y; + return result; +} + +// takes left-side projection matrix, returns NDC for right eye. to get left, invert sign on x coord of result. +vec2 extractFocalPoint(vec4 fovTan) { + float fovwidth = fovTan.x + fovTan.y; + float fovheight = fovTan.z + fovTan.w; + vec2 focalPoint = vec2(fovTan.y / fovwidth, (fovTan.z / fovheight) - 0.5f); + return focalPoint; +} + +float squeezeVision(bool perEye, vec2 varTexCoord0, vec2 visionSqueezeRatios, float transitionRatio, vec2 focalPointUV) { + if (visionSqueezeRatios.x == 0.0 && visionSqueezeRatios.y == 0.0) { + return 0.0; + } + + vec2 centerUV; + vec2 semiAxis; + if (perEye) { + // tubes in front of each eye + centerUV = focalPointUV; + semiAxis = vec2(0.25) - visionSqueezeRatios * 0.25; + } else { + // one tube in front of face + centerUV = vec2(0.5, focalPointUV.y); + semiAxis = vec2(0.5) - visionSqueezeRatios * 0.5; + } + float ellipseValue = ellipse(varTexCoord0, centerUV, semiAxis); + float frac = clamp((ellipseValue - 1.0) / clamp(transitionRatio, 0.01, 0.7), 0.0, 1.0); + + return frac; +} + +vec3 gridFloor(vec2 varTexCoord0, mat4 hmdSensorMatrix, mat4 projectionInverse, float groundPlaneY, float spotLightSize) { + vec4 ndc = vec4(varTexCoord0.x * 4.0 - 1.0 - 2.0 * float(varTexCoord0.x > 0.5), varTexCoord0.y * 2.0 - 1.0, -1.0, 1.0); + + vec4 fragmentEyeCoords = hmdSensorMatrix * projectionInverse * ndc; + vec4 near4 = hmdSensorMatrix * vec4(0.0, 0.0, 0.0, 1.0); + + vec3 near = (near4 / near4.w).xyz; + vec3 far = fragmentEyeCoords.xyz / fragmentEyeCoords.w; + + // intersect a line from near to far with the plane y = groundPlaneY + float t = -(near.y - groundPlaneY) / (far.y - near.y); + vec2 R = (near + t * (far - near)).xz; + + float lineThickness = 1.5 / length(R); + vec4 gridColor = vec4(0.35); + vec4 baseColor = vec4(0.1); + vec4 skyColor = vec4(0.0, 0.0, 0.0, 0.0); + vec2 wrapped = fract(R) - 0.5f; + vec2 range = abs(wrapped); + vec2 speeds = fwidth(R); + vec2 pixelRange = range/speeds; + float lineWeight = clamp(min(pixelRange.x, pixelRange.y) - lineThickness, 0.0, 1.0); + + float horizonFuzz = 0.02; + if (t < 0.0) { + return mix(gridColor, skyColor, clamp(0.0, 1.0, -t)).xyz; + } else if (t < horizonFuzz) { + lineWeight = lineWeight * max(0.0, t); + } + + vec4 c = mix(gridColor, baseColor, lineWeight); + + // fade out grid to avoid shimmer + float fadeVal = 0.7; + return mix(c, baseColor * fadeVal + gridColor * (1.0 - fadeVal), + 0.1 * clamp((length(R) - spotLightSize), 0.0, 1.0)).xyz; +} + +<@endif@> diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index 3cec5e8265..9cbf189b38 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -1,4 +1,4 @@ -// +// // Created by Bradley Austin Davis on 2016/02/15 // Copyright 2016 High Fidelity, Inc. // @@ -113,6 +113,11 @@ void HmdDisplayPlugin::internalDeactivate() { } void HmdDisplayPlugin::customizeContext() { + + VisionSqueezeParameters parameters; + _visionSqueezeParametersBuffer = + gpu::BufferView(std::make_shared(sizeof(VisionSqueezeParameters), (const gpu::Byte*) ¶meters)); + Parent::customizeContext(); _hudRenderer.build(); } @@ -478,3 +483,57 @@ HmdDisplayPlugin::~HmdDisplayPlugin() { float HmdDisplayPlugin::stutterRate() const { return _stutterRate.rate(); } + +float adjustVisionSqueezeRatioForDevice(float visionSqueezeRatio, float visionSqueezeDeviceLow, float visionSqueezeDeviceHigh) { + const float SETTINGS_STEP = 0.2f; // adjusting the slider in preferences changes the ratio by this much + + if (visionSqueezeRatio == 0.0f) { + return 0.0f; + } + + float deviceRange = visionSqueezeDeviceHigh - visionSqueezeDeviceLow; + + if (visionSqueezeRatio <= SETTINGS_STEP) { + // lowest "enabled" setting -- without this special case the user doesn't see anything on the lowest setting + float scaleFactor = (visionSqueezeRatio == SETTINGS_STEP) ? 0.24f : 0.18f; // these magic values were picked through experimentation + return deviceRange * scaleFactor + visionSqueezeDeviceLow; + } else { + const float SQUEEZE_ADJUSTMENT = 0.75f; // magic number picked through experimentation + return deviceRange * (SQUEEZE_ADJUSTMENT * visionSqueezeRatio) + visionSqueezeDeviceLow; + } +} + +void HmdDisplayPlugin::updateVisionSqueezeParameters(float visionSqueezeX, float visionSqueezeY, + float visionSqueezeTransition, + int visionSqueezePerEye, float visionSqueezeGroundPlaneY, + float visionSqueezeSpotlightSize) { + + visionSqueezeX = adjustVisionSqueezeRatioForDevice(visionSqueezeX, _visionSqueezeDeviceLowX, _visionSqueezeDeviceHighX); + visionSqueezeY = adjustVisionSqueezeRatioForDevice(visionSqueezeY, _visionSqueezeDeviceLowY, _visionSqueezeDeviceHighY); + + auto& params = _visionSqueezeParametersBuffer.get(); + if (params._visionSqueezeX != visionSqueezeX) { + _visionSqueezeParametersBuffer.edit()._visionSqueezeX = visionSqueezeX; + } + if (params._visionSqueezeY != visionSqueezeY) { + _visionSqueezeParametersBuffer.edit()._visionSqueezeY = visionSqueezeY; + } + if (params._visionSqueezeTransition != visionSqueezeTransition) { + _visionSqueezeParametersBuffer.edit()._visionSqueezeTransition = visionSqueezeTransition; + } + if (params._visionSqueezePerEye != visionSqueezePerEye) { + _visionSqueezeParametersBuffer.edit()._visionSqueezePerEye = visionSqueezePerEye; + } + if (params._visionSqueezeGroundPlaneY != visionSqueezeGroundPlaneY) { + _visionSqueezeParametersBuffer.edit()._visionSqueezeGroundPlaneY = visionSqueezeGroundPlaneY; + } + if (params._visionSqueezeSpotlightSize != visionSqueezeSpotlightSize) { + _visionSqueezeParametersBuffer.edit()._visionSqueezeSpotlightSize = visionSqueezeSpotlightSize; + } +} + +void HmdDisplayPlugin::setupCompositeScenePipeline(gpu::Batch& batch) { + batch.setPipeline(_drawTextureSqueezePipeline); + _visionSqueezeParametersBuffer.edit()._hmdSensorMatrix = _currentPresentFrameInfo.presentPose; + batch.setUniformBuffer(drawTextureWithVisionSqueezeParamsSlot, _visionSqueezeParametersBuffer); +} diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h index a381f04689..5317ec54da 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h @@ -50,6 +50,9 @@ public: std::function getHUDOperator() override; virtual StencilMaskMode getStencilMaskMode() const override { return StencilMaskMode::PAINT; } + void updateVisionSqueezeParameters(float visionSqueezeX, float visionSqueezeY, float visionSqueezeTransition, + int visionSqueezePerEye, float visionSqueezeGroundPlaneY, + float visionSqueezeSpotlightSize); signals: void hmdMountedChanged(); @@ -91,6 +94,33 @@ protected: RateCounter<> _stutterRate; bool _disablePreview { true }; + + class VisionSqueezeParameters { + public: + float _visionSqueezeX { 0.0f }; + float _visionSqueezeY { 0.0f }; + float _spareA { 0.0f }; + float _spareB { 0.0f }; + glm::mat4 _leftProjection; + glm::mat4 _rightProjection; + glm::mat4 _hmdSensorMatrix; + float _visionSqueezeTransition { 0.15f }; + int _visionSqueezePerEye { 0 }; + float _visionSqueezeGroundPlaneY { 0.0f }; + float _visionSqueezeSpotlightSize { 0.0f }; + + VisionSqueezeParameters() {} + }; + typedef gpu::BufferView UniformBufferView; + gpu::BufferView _visionSqueezeParametersBuffer; + + virtual void setupCompositeScenePipeline(gpu::Batch& batch) override; + + float _visionSqueezeDeviceLowX { 0.0f }; + float _visionSqueezeDeviceHighX { 1.0f }; + float _visionSqueezeDeviceLowY { 0.0f }; + float _visionSqueezeDeviceHighY { 1.0f }; + private: ivec4 getViewportForSourceSize(const uvec2& size) const; float getLeftCenterPixel() const; @@ -112,7 +142,7 @@ private: struct Uniforms { float alpha { 1.0f }; } uniforms; - + struct Vertex { vec3 pos; vec2 uv; @@ -126,3 +156,5 @@ private: std::function render(); } _hudRenderer; }; + +const int drawTextureWithVisionSqueezeParamsSlot = 1; // must match binding in DrawTextureWithVisionSqueeze.slf diff --git a/libraries/gpu/src/gpu/Context.cpp b/libraries/gpu/src/gpu/Context.cpp index 927e872fbc..97b67586c6 100644 --- a/libraries/gpu/src/gpu/Context.cpp +++ b/libraries/gpu/src/gpu/Context.cpp @@ -220,7 +220,7 @@ double Context::getFrameTimerBatchAverage() const { const Backend::TransformCamera& Backend::TransformCamera::recomputeDerived(const Transform& xformView) const { _projectionInverse = glm::inverse(_projection); - // Get the viewEyeToWorld matrix form the transformView as passed to the gpu::Batch + // Get the viewEyeToWorld matrix from the transformView as passed to the gpu::Batch // this is the "_viewInverse" fed to the shader // Genetrate the "_view" matrix as well from the xform xformView.getMatrix(_viewInverse); diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index 2315f6e4ba..5c95bf05f8 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -214,6 +214,9 @@ public: virtual StencilMaskMode getStencilMaskMode() const { return StencilMaskMode::NONE; } using StencilMaskMeshOperator = std::function; virtual StencilMaskMeshOperator getStencilMaskMeshOperator() { return nullptr; } + virtual void updateParameters(float visionSqueezeX, float visionSqueezeY, float visionSqueezeTransition, + int visionSqueezePerEye, float visionSqueezeGroundPlaneY, + float visionSqueezeSpotlightSize) {} signals: void recommendedFramebufferSizeChanged(const QSize& size); diff --git a/libraries/render/src/render/Args.h b/libraries/render/src/render/Args.h index cb1ca668a7..a75192bad7 100644 --- a/libraries/render/src/render/Args.h +++ b/libraries/render/src/render/Args.h @@ -142,6 +142,13 @@ namespace render { bool _takingSnapshot { false }; StencilMaskMode _stencilMaskMode { StencilMaskMode::NONE }; std::function _stencilMaskOperator; + + float _visionSqueezeX { 0.0f }; + float _visionSqueezeY { 0.0f }; + float _visionSqueezeTransition { 0.15f }; + int _visionSqueezePerEye { 0 }; + float _visionSqueezeGroundPlaneY { 0.0f }; + float _visionSqueezeSpotlightSize { 0.02f }; }; } diff --git a/plugins/oculus/src/OculusDisplayPlugin.cpp b/plugins/oculus/src/OculusDisplayPlugin.cpp index bd0c77ae57..b2fc6881f4 100644 --- a/plugins/oculus/src/OculusDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusDisplayPlugin.cpp @@ -53,6 +53,13 @@ bool OculusDisplayPlugin::internalActivate() { void OculusDisplayPlugin::init() { Plugin::init(); + // Different HMDs end up showing the squeezed-vision egg as different sizes. These values + // attempt to make them appear the same. + _visionSqueezeDeviceLowX = 0.8f; + _visionSqueezeDeviceHighX = 0.98f; + _visionSqueezeDeviceLowY = 0.8f; + _visionSqueezeDeviceHighY = 0.9f; + emit deviceConnected(getName()); } @@ -151,8 +158,11 @@ void OculusDisplayPlugin::hmdPresent() { GLuint curTexId; ovr_GetTextureSwapChainBufferGL(_session, _textureSwapChain, curIndex, &curTexId); + _visionSqueezeParametersBuffer.edit()._leftProjection = _eyeProjections[0]; + _visionSqueezeParametersBuffer.edit()._rightProjection = _eyeProjections[1]; + // Manually bind the texture to the FBO - // FIXME we should have a way of wrapping raw GL ids in GPU objects without + // FIXME we should have a way of wrapping raw GL ids in GPU objects without // taking ownership of the object auto fbo = getGLBackend()->getFramebufferID(_outputFramebuffer); glNamedFramebufferTexture(fbo, GL_COLOR_ATTACHMENT0, curTexId, 0); diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index cd318dd9b4..78b462369b 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -396,6 +396,13 @@ void OpenVrDisplayPlugin::init() { _lastGoodHMDPose.m[2][2] = 1.0f; _lastGoodHMDPose.m[2][3] = 0.0f; + // Different HMDs end up showing the squeezed-vision egg as different sizes. These values + // attempt to make them appear the same. + _visionSqueezeDeviceLowX = 0.8f; + _visionSqueezeDeviceHighX = 0.98f; + _visionSqueezeDeviceLowY = 0.8f; + _visionSqueezeDeviceHighY = 0.9f; + emit deviceConnected(getName()); } @@ -651,6 +658,11 @@ void OpenVrDisplayPlugin::hmdPresent() { if (_threadedSubmit) { _submitThread->waitForPresent(); } else { + + _visionSqueezeParametersBuffer.edit()._leftProjection = _eyeProjections[0]; + _visionSqueezeParametersBuffer.edit()._rightProjection = _eyeProjections[1]; + _visionSqueezeParametersBuffer.edit()._hmdSensorMatrix = _currentPresentFrameInfo.presentPose; + GLuint glTexId = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0)); vr::Texture_t vrTexture{ (void*)(uintptr_t)glTexId, vr::TextureType_OpenGL, vr::ColorSpace_Auto }; vr::VRCompositor()->Submit(vr::Eye_Left, &vrTexture, &OPENVR_TEXTURE_BOUNDS_LEFT); @@ -828,4 +840,15 @@ DisplayPlugin::StencilMaskMeshOperator OpenVrDisplayPlugin::getStencilMaskMeshOp } } return nullptr; -} \ No newline at end of file +} + +void OpenVrDisplayPlugin::updateParameters(float visionSqueezeX, float visionSqueezeY, float visionSqueezeTransition, + int visionSqueezePerEye, float visionSqueezeGroundPlaneY, + float visionSqueezeSpotlightSize) { + _visionSqueezeX = visionSqueezeX; + _visionSqueezeY = visionSqueezeY; + _visionSqueezeTransition = visionSqueezeTransition; + _visionSqueezePerEye = visionSqueezePerEye; + _visionSqueezeGroundPlaneY = visionSqueezeGroundPlaneY; + _visionSqueezeSpotlightSize = visionSqueezeSpotlightSize; +} diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.h b/plugins/openvr/src/OpenVrDisplayPlugin.h index 4b042a700d..25427c6dcd 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.h +++ b/plugins/openvr/src/OpenVrDisplayPlugin.h @@ -72,6 +72,10 @@ public: virtual StencilMaskMode getStencilMaskMode() const override { return StencilMaskMode::MESH; } virtual StencilMaskMeshOperator getStencilMaskMeshOperator() override; + virtual void updateParameters(float visionSqueezeX, float visionSqueezeY, float visionSqueezeTransition, + int visionSqueezePerEye, float visionSqueezeGroundPlaneY, + float visionSqueezeSpotlightSize) override; + protected: bool internalActivate() override; void internalDeactivate() override; @@ -102,4 +106,11 @@ private: std::array _stencilMeshes; bool _stencilMeshesInitialized { false }; + + float _visionSqueezeX; + float _visionSqueezeY; + float _visionSqueezeTransition; + int _visionSqueezePerEye; + float _visionSqueezeGroundPlaneY; + float _visionSqueezeSpotlightSize; };