From a2b84c0fbbfa11353310a0924ed2c92cad3f6ab5 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 30 Apr 2018 12:00:39 -0700 Subject: [PATCH] It's working!! --- interface/src/Application.cpp | 6 +- interface/src/Application.h | 2 +- .../scripting/WindowScriptingInterface.cpp | 4 +- .../src/scripting/WindowScriptingInterface.h | 12 +- interface/src/ui/Snapshot.cpp | 150 ++++++++++++++---- interface/src/ui/Snapshot.h | 16 +- scripts/system/notifications.js | 1 + .../spectator-camera/SpectatorCamera.qml | 19 ++- .../spectator-camera/spectatorCamera.js | 13 +- 9 files changed, 174 insertions(+), 49 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6487254e26..fe7b6c2555 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -7278,9 +7278,9 @@ void Application::takeSecondaryCameraSnapshot(const QString& filename) { }); } -void Application::takeSecondaryCamera360Snapshot(const QString& filename) { - postLambdaEvent([filename, this] { - Snapshot::save360Snapshot(filename); +void Application::takeSecondaryCamera360Snapshot(const glm::vec3& cameraPosition, const QString& filename) { + postLambdaEvent([filename, cameraPosition, this] { + Snapshot::save360Snapshot(cameraPosition, filename); }); } diff --git a/interface/src/Application.h b/interface/src/Application.h index 3c0babdc5d..2dddf3e6be 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -273,7 +273,7 @@ public: void takeSnapshot(bool notify, bool includeAnimated = false, float aspectRatio = 0.0f, const QString& filename = QString()); void takeSecondaryCameraSnapshot(const QString& filename = QString()); - void takeSecondaryCamera360Snapshot(const QString& filename = QString()); + void takeSecondaryCamera360Snapshot(const glm::vec3& cameraPosition, const QString& filename = QString()); void shareSnapshot(const QString& filename, const QUrl& href = QUrl("")); diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 2d94e3e02e..f5416bf4c6 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -431,8 +431,8 @@ void WindowScriptingInterface::takeSecondaryCameraSnapshot(const QString& filena qApp->takeSecondaryCameraSnapshot(filename); } -void WindowScriptingInterface::takeSecondaryCamera360Snapshot(const QString& filename) { - qApp->takeSecondaryCamera360Snapshot(filename); +void WindowScriptingInterface::takeSecondaryCamera360Snapshot(const glm::vec3& cameraPosition, const QString& filename) { + qApp->takeSecondaryCamera360Snapshot(cameraPosition, filename); } void WindowScriptingInterface::shareSnapshot(const QString& path, const QUrl& href) { diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index d3105acd72..23df0a47f5 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -376,7 +376,7 @@ public slots: * * var filename = QString(); */ - void takeSecondaryCamera360Snapshot(const QString& filename = QString()); + void takeSecondaryCamera360Snapshot(const glm::vec3& cameraPosition, const QString& filename = QString()); /**jsdoc * Emit a {@link Window.connectionAdded|connectionAdded} or a {@link Window.connectionError|connectionError} signal that @@ -586,6 +586,16 @@ signals: */ void stillSnapshotTaken(const QString& pathStillSnapshot, bool notify); + /**jsdoc + * Triggered when a still equirectangular snapshot has been taken by calling {@link Window.take360Snapshot|take360Snapshot} + * @function Window.equirectangularSnapshotTaken + * @param {string} pathStillSnapshot - The path and name of the snapshot image file. + * @param {boolean} notify - The value of the notify parameter that {@link Window.take360Snapshot|take360Snapshot} + * was called with. + * @returns {Signal} + */ + void equirectangularSnapshotTaken(const QString& pathEquirectangularSnapshot, bool notify); + /**jsdoc * Triggered when a snapshot submitted via {@link Window.shareSnapshot|shareSnapshot} is ready for sharing. The snapshot * may then be shared via the {@link Account.metaverseServerURL} Web API. diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp index 5feca7ee70..852869b98e 100644 --- a/interface/src/ui/Snapshot.cpp +++ b/interface/src/ui/Snapshot.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -91,57 +92,66 @@ QString Snapshot::saveSnapshot(QImage image, const QString& filename) { return snapshotPath; } -void Snapshot::save360Snapshot(const QString& filename) { + +qint16 Snapshot::snapshotIndex = 0; +QVariant Snapshot::oldAttachedEntityId = 0; +QVariant Snapshot::oldOrientation = 0; +QVariant Snapshot::oldvFoV = 0; +QVariant Snapshot::oldNearClipPlaneDistance = 0; +QVariant Snapshot::oldFarClipPlaneDistance = 0; + +QImage Snapshot::downImage; +QImage Snapshot::frontImage; +QImage Snapshot::leftImage; +QImage Snapshot::backImage; +QImage Snapshot::rightImage; +QImage Snapshot::upImage; + +void Snapshot::save360Snapshot(const glm::vec3& cameraPosition, const QString& filename) { SecondaryCameraJobConfig* secondaryCameraRenderConfig = static_cast(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCamera")); // Save initial values of secondary camera render config - auto oldAttachedEntityId = secondaryCameraRenderConfig->property("attachedEntityId"); - auto oldOrientation = secondaryCameraRenderConfig->property("orientation"); - auto oldvFoV = secondaryCameraRenderConfig->property("vFoV"); - auto oldNearClipPlaneDistance = secondaryCameraRenderConfig->property("nearClipPlaneDistance"); - auto oldFarClipPlaneDistance = secondaryCameraRenderConfig->property("farClipPlaneDistance"); + oldAttachedEntityId = secondaryCameraRenderConfig->property("attachedEntityId"); + oldOrientation = secondaryCameraRenderConfig->property("orientation"); + oldvFoV = secondaryCameraRenderConfig->property("vFoV"); + oldNearClipPlaneDistance = secondaryCameraRenderConfig->property("nearClipPlaneDistance"); + oldFarClipPlaneDistance = secondaryCameraRenderConfig->property("farClipPlaneDistance"); // Initialize some secondary camera render config options for 360 snapshot capture secondaryCameraRenderConfig->resetSizeSpectatorCamera(2048, 2048); secondaryCameraRenderConfig->setProperty("attachedEntityId", ""); + secondaryCameraRenderConfig->setPosition(cameraPosition); secondaryCameraRenderConfig->setProperty("vFoV", 90.0f); - secondaryCameraRenderConfig->setProperty("nearClipPlaneDistance", 0.5f); - secondaryCameraRenderConfig->setProperty("farClipPlaneDistance", 1000.0f); + secondaryCameraRenderConfig->setProperty("nearClipPlaneDistance", 0.3f); + secondaryCameraRenderConfig->setProperty("farClipPlaneDistance", 5000.0f); secondaryCameraRenderConfig->setOrientation(glm::quat(glm::radians(glm::vec3(-90.0f, 0.0f, 0.0f)))); - qint16 snapshotIndex = 0; + snapshotIndex = 0; QTimer* snapshotTimer = new QTimer(); snapshotTimer->setSingleShot(false); - snapshotTimer->setInterval(200); + snapshotTimer->setInterval(250); connect(snapshotTimer, &QTimer::timeout, [&] { SecondaryCameraJobConfig* config = static_cast(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCamera")); - qDebug() << "ZRF HERE" << snapshotIndex; if (snapshotIndex == 0) { - QImage downImage = qApp->getActiveDisplayPlugin()->getSecondaryCameraScreenshot(); - Snapshot::saveSnapshot(downImage, "down"); + downImage = qApp->getActiveDisplayPlugin()->getSecondaryCameraScreenshot(); config->setOrientation(glm::quat(glm::radians(glm::vec3(0.0f, 0.0f, 0.0f)))); } else if (snapshotIndex == 1) { - QImage frontImage = qApp->getActiveDisplayPlugin()->getSecondaryCameraScreenshot(); - Snapshot::saveSnapshot(frontImage, "front"); + frontImage = qApp->getActiveDisplayPlugin()->getSecondaryCameraScreenshot(); config->setOrientation(glm::quat(glm::radians(glm::vec3(0.0f, 90.0f, 0.0f)))); } else if (snapshotIndex == 2) { - QImage leftImage = qApp->getActiveDisplayPlugin()->getSecondaryCameraScreenshot(); - Snapshot::saveSnapshot(leftImage, "left"); + leftImage = qApp->getActiveDisplayPlugin()->getSecondaryCameraScreenshot(); config->setOrientation(glm::quat(glm::radians(glm::vec3(0.0f, 180.0f, 0.0f)))); } else if (snapshotIndex == 3) { - QImage backImage = qApp->getActiveDisplayPlugin()->getSecondaryCameraScreenshot(); - Snapshot::saveSnapshot(backImage, "back"); + backImage = qApp->getActiveDisplayPlugin()->getSecondaryCameraScreenshot(); config->setOrientation(glm::quat(glm::radians(glm::vec3(0.0f, 270.0f, 0.0f)))); } else if (snapshotIndex == 4) { - QImage rightImage = qApp->getActiveDisplayPlugin()->getSecondaryCameraScreenshot(); - Snapshot::saveSnapshot(rightImage, "right"); + rightImage = qApp->getActiveDisplayPlugin()->getSecondaryCameraScreenshot(); config->setOrientation(glm::quat(glm::radians(glm::vec3(90.0f, 0.0f, 0.0f)))); } else if (snapshotIndex == 5) { - QImage upImage = qApp->getActiveDisplayPlugin()->getSecondaryCameraScreenshot(); - Snapshot::saveSnapshot(upImage, "up"); - } else if (snapshotIndex == 6) { + upImage = qApp->getActiveDisplayPlugin()->getSecondaryCameraScreenshot(); + } else { // Reset secondary camera render config config->resetSizeSpectatorCamera(qApp->getWindow()->geometry().width(), qApp->getWindow()->geometry().height()); config->setProperty("attachedEntityId", oldAttachedEntityId); @@ -149,25 +159,97 @@ void Snapshot::save360Snapshot(const QString& filename) { config->setProperty("nearClipPlaneDistance", oldNearClipPlaneDistance); config->setProperty("farClipPlaneDistance", oldFarClipPlaneDistance); - QFile* snapshotFile = savedFileForSnapshot(qApp->getActiveDisplayPlugin()->getSecondaryCameraScreenshot(), false, filename); - - // we don't need the snapshot file, so close it, grab its filename and delete it - snapshotFile->close(); - - QString snapshotPath = QFileInfo(*snapshotFile).absoluteFilePath(); - - delete snapshotFile; + // Process six QImages + QtConcurrent::run(convertToEquirectangular); snapshotTimer->stop(); snapshotTimer->deleteLater(); - - emit DependencyManager::get()->stillSnapshotTaken(snapshotPath, true); } snapshotIndex++; }); snapshotTimer->start(); } +void Snapshot::convertToEquirectangular() { + float outputImageWidth = 8192.0f; + float outputImageHeight = 4096.0f; + QImage outputImage(outputImageWidth, outputImageHeight, QImage::Format_RGB32); + outputImage.fill(0); + QRgb sourceColorValue; + float phi, theta; + int cubeFaceWidth = 2048.0f; + int cubeFaceHeight = 2048.0f; + + for (int j = 0; j < outputImageHeight; j++) { + theta = (1.0f - ((float)j / outputImageHeight)) * PI; + + for (int i = 0; i < outputImageWidth; i++) { + phi = ((float)i / outputImageWidth) * 2.0f * PI; + + float x, y, z; + x = glm::sin(phi) * glm::sin(theta) * -1.0f; + y = glm::cos(theta); + z = glm::cos(phi) * glm::sin(theta) * -1.0f; + + float xa, ya, za; + float a; + + a = std::max(std::max(std::abs(x), std::abs(y)), std::abs(z)); + + xa = x / a; + ya = y / a; + za = z / a; + + // Pixel in the source images + int xPixel, yPixel; + QImage sourceImage; + + if (xa == 1) { + // Right image + xPixel = (int)((((za + 1.0f) / 2.0f) - 1.0f) * cubeFaceWidth); + yPixel = (int)((((ya + 1.0f) / 2.0f)) * cubeFaceHeight); + sourceImage = rightImage; + } else if (xa == -1) { + // Left image + xPixel = (int)((((za + 1.0f) / 2.0f)) * cubeFaceWidth); + yPixel = (int)((((ya + 1.0f) / 2.0f)) * cubeFaceHeight); + sourceImage = leftImage; + } else if (ya == 1) { + // Down image + xPixel = (int)((((xa + 1.0f) / 2.0f)) * cubeFaceWidth); + yPixel = (int)((((za + 1.0f) / 2.0f) - 1.0f) * cubeFaceHeight); + sourceImage = downImage; + } else if (ya == -1) { + // Up image + xPixel = (int)((((xa + 1.0f) / 2.0f)) * cubeFaceWidth); + yPixel = (int)((((za + 1.0f) / 2.0f)) * cubeFaceHeight); + sourceImage = upImage; + } else if (za == 1) { + // Front image + xPixel = (int)((((xa + 1.0f) / 2.0f)) * cubeFaceWidth); + yPixel = (int)((((ya + 1.0f) / 2.0f)) * cubeFaceHeight); + sourceImage = frontImage; + } else if (za == -1) { + // Back image + xPixel = (int)((((xa + 1.0f) / 2.0f) - 1.0f) * cubeFaceWidth); + yPixel = (int)((((ya + 1.0f) / 2.0f)) * cubeFaceHeight); + sourceImage = backImage; + } else { + qDebug() << "Unknown face encountered when processing 360 Snapshot"; + xPixel = 0; + yPixel = 0; + } + + xPixel = std::min(std::abs(xPixel), 2047); + yPixel = std::min(std::abs(yPixel), 2047); + + sourceColorValue = sourceImage.pixel(xPixel, yPixel); + outputImage.setPixel(i, j, sourceColorValue); + } + } + + emit DependencyManager::get()->equirectangularSnapshotTaken(saveSnapshot(outputImage, QString()), true); +} QTemporaryFile* Snapshot::saveTempSnapshot(QImage image) { // return whatever we get back from saved file for snapshot diff --git a/interface/src/ui/Snapshot.h b/interface/src/ui/Snapshot.h index 834daea0dc..0e8f3f83bd 100644 --- a/interface/src/ui/Snapshot.h +++ b/interface/src/ui/Snapshot.h @@ -38,7 +38,7 @@ class Snapshot : public QObject, public Dependency { SINGLETON_DEPENDENCY public: static QString saveSnapshot(QImage image, const QString& filename); - static void save360Snapshot(const QString& filename); + static void save360Snapshot(const glm::vec3& cameraPosition, const QString& filename); static QTemporaryFile* saveTempSnapshot(QImage image); static SnapshotMetaData* parseSnapshotData(QString snapshotPath); @@ -53,6 +53,20 @@ public slots: Q_INVOKABLE void setSnapshotsLocation(const QString& location); private: static QFile* savedFileForSnapshot(QImage & image, bool isTemporary, const QString& userSelectedFilename = QString()); + + static qint16 snapshotIndex; + static QVariant oldAttachedEntityId; + static QVariant oldOrientation; + static QVariant oldvFoV; + static QVariant oldNearClipPlaneDistance; + static QVariant oldFarClipPlaneDistance; + static QImage downImage; + static QImage frontImage; + static QImage leftImage; + static QImage backImage; + static QImage rightImage; + static QImage upImage; + static void convertToEquirectangular(); }; #endif // hifi_Snapshot_h diff --git a/scripts/system/notifications.js b/scripts/system/notifications.js index ba37f6ee4e..3b3ac4dfe6 100644 --- a/scripts/system/notifications.js +++ b/scripts/system/notifications.js @@ -672,6 +672,7 @@ Menu.menuItemEvent.connect(menuItemEvent); Window.domainConnectionRefused.connect(onDomainConnectionRefused); Window.stillSnapshotTaken.connect(onSnapshotTaken); + Window.equirectangularSnapshotTaken.connect(onSnapshotTaken); Window.processingGifStarted.connect(processingGif); Window.connectionAdded.connect(connectionAdded); Window.connectionError.connect(connectionError); diff --git a/unpublishedScripts/marketplace/spectator-camera/SpectatorCamera.qml b/unpublishedScripts/marketplace/spectator-camera/SpectatorCamera.qml index c4dc2c29f6..c85f990a6d 100644 --- a/unpublishedScripts/marketplace/spectator-camera/SpectatorCamera.qml +++ b/unpublishedScripts/marketplace/spectator-camera/SpectatorCamera.qml @@ -239,7 +239,7 @@ Rectangle { // Instructions (visible when display texture isn't set) HifiStylesUit.FiraSansRegular { id: spectatorCameraInstructions; - text: "Turn on Spectator Camera for a preview\nof what your monitor shows."; + text: "Turn on Spectator Camera for a preview\nof " + (HMD.active ? "what your monitor shows." : "the camera's view."); size: 16; color: hifi.colors.lightGrayText; visible: !cameraToggleButton.camIsOn; @@ -252,7 +252,7 @@ Rectangle { Hifi.ResourceImageItem { id: spectatorCameraPreview; visible: cameraToggleButton.camIsOn; - url: monitorShowsSwitch.checked ? "resource://spectatorCameraFrame" : "resource://hmdPreviewFrame"; + url: monitorShowsSwitch.checked || !HMD.active ? "resource://spectatorCameraFrame" : "resource://hmdPreviewFrame"; ready: cameraToggleButton.camIsOn; mirrorVertically: true; anchors.fill: parent; @@ -267,6 +267,7 @@ Rectangle { // "Monitor Shows" Switch Label Glyph HifiStylesUit.HiFiGlyphs { id: monitorShowsSwitchLabelGlyph; + visible: HMD.active; text: hifi.glyphs.screen; size: 32; color: hifi.colors.blueHighlight; @@ -277,6 +278,7 @@ Rectangle { // "Monitor Shows" Switch Label HifiStylesUit.RalewayLight { id: monitorShowsSwitchLabel; + visible: HMD.active; text: "MONITOR SHOWS:"; anchors.top: spectatorCameraImageContainer.bottom; anchors.topMargin: 20; @@ -291,6 +293,7 @@ Rectangle { // "Monitor Shows" Switch HifiControlsUit.Switch { id: monitorShowsSwitch; + visible: HMD.active; height: 30; anchors.left: parent.left; anchors.right: parent.right; @@ -307,6 +310,7 @@ Rectangle { // "Switch View From Controller" Checkbox HifiControlsUit.CheckBox { id: switchViewFromControllerCheckBox; + visible: HMD.active; colorScheme: hifi.colorSchemes.dark; anchors.left: parent.left; anchors.top: monitorShowsSwitch.bottom; @@ -335,15 +339,18 @@ Rectangle { HifiControlsUit.Button { id: take360SnapshotButton; - text: "Take 360 Snapshot" + text: "Take 360 Snapshot"; + enabled: cameraToggleButton.camIsOn; colorScheme: hifi.colorSchemes.dark; color: hifi.buttons.blue; - anchors.top: takeSnapshotFromControllerCheckBox.visible ? takeSnapshotFromControllerCheckBox.bottom : switchViewFromControllerCheckBox.bottom; + anchors.top: takeSnapshotFromControllerCheckBox.visible ? takeSnapshotFromControllerCheckBox.bottom : spectatorCameraImageContainer.bottom; anchors.topMargin: 8; anchors.left: parent.left; anchors.right: parent.right; height: 40; onClicked: { + take360SnapshotButton.enabled = false; + take360SnapshotButton.text = "360 SNAPSHOT PROCESSING..."; sendToScript({method: 'takeSecondaryCamera360Snapshot'}); } } @@ -400,6 +407,10 @@ Rectangle { spectatorCameraPreview.url = message.url; spectatorCameraPreview.visible = message.setting; break; + case 'enable360SnapshotButton': + take360SnapshotButton.text = "Take 360 Snapshot"; + take360SnapshotButton.enabled = cameraToggleButton.camIsOn; + break; default: console.log('Unrecognized message from spectatorCamera.js:', JSON.stringify(message)); } diff --git a/unpublishedScripts/marketplace/spectator-camera/spectatorCamera.js b/unpublishedScripts/marketplace/spectator-camera/spectatorCamera.js index c95e112d49..66219b65f7 100644 --- a/unpublishedScripts/marketplace/spectator-camera/spectatorCamera.js +++ b/unpublishedScripts/marketplace/spectator-camera/spectatorCamera.js @@ -68,8 +68,8 @@ spectatorCameraConfig.resetSizeSpectatorCamera(Window.innerWidth, Window.innerHeight); cameraRotation = Quat.multiply(MyAvatar.orientation, Quat.fromPitchYawRollDegrees(15, -155, 0)), cameraPosition = inFrontOf(0.85, Vec3.sum(MyAvatar.position, { x: 0, y: 0.28, z: 0 })); camera = Entities.addEntity({ - "angularDamping": 0.8, - "damping": 0.8, + "angularDamping": 0.95, + "damping": 0.95, "collidesWith": "static,dynamic,kinematic,", "collisionMask": 7, "dynamic": true, @@ -193,6 +193,7 @@ tablet.screenChanged.connect(onTabletScreenChanged); Window.domainChanged.connect(onDomainChanged); Window.geometryChanged.connect(resizeViewFinderOverlay); + Window.equirectangularSnapshotTaken.connect(onEquirectangularSnapshotTaken); Controller.keyPressEvent.connect(keyPressEvent); HMD.displayModeChanged.connect(onHMDChanged); viewFinderOverlay = false; @@ -403,6 +404,11 @@ Window.takeSecondaryCameraSnapshot(); } } + function onEquirectangularSnapshotTaken() { + sendToQml({ + method: 'enable360SnapshotButton' + }); + } function maybeTake360Snapshot() { if (camera) { Audio.playSound(SNAPSHOT_SOUND, { @@ -410,7 +416,7 @@ localOnly: true, volume: 1.0 }); - Window.takeSecondaryCamera360Snapshot(); + Window.takeSecondaryCamera360Snapshot(Entities.getEntityProperties(camera, ["positon"]).position); } } function registerTakeSnapshotControllerMapping() { @@ -583,6 +589,7 @@ spectatorCameraOff(); Window.domainChanged.disconnect(onDomainChanged); Window.geometryChanged.disconnect(resizeViewFinderOverlay); + Window.equirectangularSnapshotTaken.disconnect(onEquirectangularSnapshotTaken); addOrRemoveButton(true); if (tablet) { tablet.screenChanged.disconnect(onTabletScreenChanged);