mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-10 04:06:46 +02:00
Huge progress today. Buggy behavior with multiple snapshots remains
This commit is contained in:
parent
8f9ffd2bc5
commit
a81289a0d7
10 changed files with 735 additions and 678 deletions
|
@ -152,6 +152,7 @@
|
|||
#include "ui/LoginDialog.h"
|
||||
#include "ui/overlays/Cube3DOverlay.h"
|
||||
#include "ui/Snapshot.h"
|
||||
#include "ui/SnapshotAnimated.h"
|
||||
#include "ui/StandAloneJSConsole.h"
|
||||
#include "ui/Stats.h"
|
||||
#include "ui/UpdateDialog.h"
|
||||
|
@ -5428,19 +5429,9 @@ void Application::toggleLogDialog() {
|
|||
}
|
||||
}
|
||||
|
||||
// If the snapshot width or the framerate are too high for the
|
||||
// application to handle, the framerate of the output GIF will drop.
|
||||
#define SNAPSNOT_ANIMATED_WIDTH (720)
|
||||
// This value should divide evenly into 100. Snapshot framerate is NOT guaranteed.
|
||||
#define SNAPSNOT_ANIMATED_FRAMERATE_FPS (25)
|
||||
#define SNAPSNOT_ANIMATED_DURATION_SECS (3)
|
||||
|
||||
#define SNAPSNOT_ANIMATED_FRAME_DELAY_MSEC (1000/SNAPSNOT_ANIMATED_FRAMERATE_FPS)
|
||||
#define SNAPSNOT_ANIMATED_NUM_FRAMES (SNAPSNOT_ANIMATED_DURATION_SECS * SNAPSNOT_ANIMATED_FRAMERATE_FPS)
|
||||
|
||||
|
||||
void Application::takeSnapshot(bool notify, float aspectRatio) {
|
||||
postLambdaEvent([notify, aspectRatio, this] {
|
||||
void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio) {
|
||||
postLambdaEvent([notify, includeAnimated, aspectRatio, this] {
|
||||
QMediaPlayer* player = new QMediaPlayer();
|
||||
QFileInfo inf = QFileInfo(PathUtils::resourcesPath() + "sounds/snap.wav");
|
||||
player->setMedia(QUrl::fromLocalFile(inf.absoluteFilePath()));
|
||||
|
@ -5449,87 +5440,7 @@ void Application::takeSnapshot(bool notify, float aspectRatio) {
|
|||
// Get a screenshot and save it
|
||||
QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio));
|
||||
|
||||
// If we're in the middle of capturing a GIF...
|
||||
if (_animatedSnapshotFirstFrameTimestamp != 0)
|
||||
{
|
||||
// Notify the window scripting interface that we've taken a Snapshot
|
||||
emit DependencyManager::get<WindowScriptingInterface>()->snapshotTaken(path, notify);
|
||||
// Protect against clobbering it and return immediately.
|
||||
// (Perhaps with a "snapshot failed" message?
|
||||
}
|
||||
else
|
||||
{
|
||||
// Reset the current animated snapshot frame
|
||||
qApp->_animatedSnapshotFirstFrameTimestamp = 0;
|
||||
// Reset the current animated snapshot frame timestamp
|
||||
qApp->_animatedSnapshotTimestamp = 0;
|
||||
|
||||
// Change the extension of the future GIF saved snapshot file to "gif"
|
||||
qApp->_animatedSnapshotPath = path.replace("jpg", "gif");
|
||||
|
||||
// Ensure the snapshot timer is Precise (attempted millisecond precision)
|
||||
qApp->animatedSnapshotTimer.setTimerType(Qt::PreciseTimer);
|
||||
|
||||
// Connect the animatedSnapshotTimer QTimer to the lambda slot function
|
||||
connect(&(qApp->animatedSnapshotTimer), &QTimer::timeout, [=] {
|
||||
// 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(qApp->getActiveDisplayPlugin()->getScreenshot(aspectRatio));
|
||||
frame = frame.scaledToWidth(SNAPSNOT_ANIMATED_WIDTH);
|
||||
frame = frame.convertToFormat(QImage::Format_RGBA8888);
|
||||
|
||||
// If this is the first frame...
|
||||
if (qApp->_animatedSnapshotTimestamp == 0)
|
||||
{
|
||||
// Write out the header and beginning of the GIF file
|
||||
GifBegin(&(qApp->_animatedSnapshotGifWriter), qPrintable(qApp->_animatedSnapshotPath), frame.width(), frame.height(), SNAPSNOT_ANIMATED_FRAME_DELAY_MSEC / 10);
|
||||
// Write the first to the gif
|
||||
GifWriteFrame(&(qApp->_animatedSnapshotGifWriter),
|
||||
(uint8_t*)frame.bits(),
|
||||
frame.width(),
|
||||
frame.height(),
|
||||
SNAPSNOT_ANIMATED_FRAME_DELAY_MSEC / 10);
|
||||
// Record the current frame timestamp
|
||||
qApp->_animatedSnapshotTimestamp = QDateTime::currentMSecsSinceEpoch();
|
||||
qApp->_animatedSnapshotFirstFrameTimestamp = qApp->_animatedSnapshotTimestamp;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If that was the last frame...
|
||||
if ((qApp->_animatedSnapshotTimestamp - qApp->_animatedSnapshotFirstFrameTimestamp) >= (SNAPSNOT_ANIMATED_DURATION_SECS * 1000))
|
||||
{
|
||||
// Reset the current frame timestamp
|
||||
qApp->_animatedSnapshotTimestamp = 0;
|
||||
qApp->_animatedSnapshotFirstFrameTimestamp = 0;
|
||||
// Write out the end of the GIF
|
||||
GifEnd(&(qApp->_animatedSnapshotGifWriter));
|
||||
// Notify the Window Scripting Interface that the snapshot was taken
|
||||
emit DependencyManager::get<WindowScriptingInterface>()->snapshotTaken(qApp->_animatedSnapshotPath, false);
|
||||
// Stop the snapshot QTimer
|
||||
qApp->animatedSnapshotTimer.stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Variable used to determine how long the current frame took to pack
|
||||
qint64 temp = QDateTime::currentMSecsSinceEpoch();
|
||||
// Write the frame to the gif
|
||||
GifWriteFrame(&(qApp->_animatedSnapshotGifWriter),
|
||||
(uint8_t*)frame.bits(),
|
||||
frame.width(),
|
||||
frame.height(),
|
||||
round(((float)(QDateTime::currentMSecsSinceEpoch() - qApp->_animatedSnapshotTimestamp + qApp->_animatedSnapshotLastWriteFrameDuration)) / 10));
|
||||
// Record how long it took for the current frame to pack
|
||||
qApp->_animatedSnapshotLastWriteFrameDuration = QDateTime::currentMSecsSinceEpoch() - temp;
|
||||
// Record the current frame timestamp
|
||||
qApp->_animatedSnapshotTimestamp = QDateTime::currentMSecsSinceEpoch();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Start the animatedSnapshotTimer QTimer - argument for this is in milliseconds
|
||||
qApp->animatedSnapshotTimer.start(SNAPSNOT_ANIMATED_FRAME_DELAY_MSEC);
|
||||
}
|
||||
SnapshotAnimated::saveSnapshotAnimated(includeAnimated, path, aspectRatio, qApp, DependencyManager::get<WindowScriptingInterface>());
|
||||
});
|
||||
}
|
||||
void Application::shareSnapshot(const QString& path) {
|
||||
|
|
|
@ -267,7 +267,7 @@ public:
|
|||
float getAvatarSimrate() const { return _avatarSimCounter.rate(); }
|
||||
float getAverageSimsPerSecond() const { return _simCounter.rate(); }
|
||||
|
||||
void takeSnapshot(bool notify, float aspectRatio = 0.0f);
|
||||
void takeSnapshot(bool notify, bool includeAnimated = false, float aspectRatio = 0.0f);
|
||||
void shareSnapshot(const QString& filename);
|
||||
|
||||
model::SkyboxPointer getDefaultSkybox() const { return _defaultSkybox; }
|
||||
|
@ -610,13 +610,6 @@ private:
|
|||
model::SkyboxPointer _defaultSkybox { new ProceduralSkybox() } ;
|
||||
gpu::TexturePointer _defaultSkyboxTexture;
|
||||
gpu::TexturePointer _defaultSkyboxAmbientTexture;
|
||||
|
||||
QTimer animatedSnapshotTimer;
|
||||
GifWriter _animatedSnapshotGifWriter;
|
||||
qint64 _animatedSnapshotTimestamp { 0 };
|
||||
qint64 _animatedSnapshotFirstFrameTimestamp { 0 };
|
||||
qint64 _animatedSnapshotLastWriteFrameDuration { 20 };
|
||||
QString _animatedSnapshotPath;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -199,8 +199,8 @@ void WindowScriptingInterface::copyToClipboard(const QString& text) {
|
|||
QApplication::clipboard()->setText(text);
|
||||
}
|
||||
|
||||
void WindowScriptingInterface::takeSnapshot(bool notify, float aspectRatio) {
|
||||
qApp->takeSnapshot(notify, aspectRatio);
|
||||
void WindowScriptingInterface::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio) {
|
||||
qApp->takeSnapshot(notify, includeAnimated, aspectRatio);
|
||||
}
|
||||
|
||||
void WindowScriptingInterface::shareSnapshot(const QString& path) {
|
||||
|
|
|
@ -52,7 +52,7 @@ public slots:
|
|||
QScriptValue save(const QString& title = "", const QString& directory = "", const QString& nameFilter = "");
|
||||
void showAssetServer(const QString& upload = "");
|
||||
void copyToClipboard(const QString& text);
|
||||
void takeSnapshot(bool notify = true, float aspectRatio = 0.0f);
|
||||
void takeSnapshot(bool notify = true, bool includeAnimated = false, float aspectRatio = 0.0f);
|
||||
void shareSnapshot(const QString& path);
|
||||
bool isPhysicsEnabled();
|
||||
|
||||
|
@ -60,7 +60,7 @@ signals:
|
|||
void domainChanged(const QString& domainHostname);
|
||||
void svoImportRequested(const QString& url);
|
||||
void domainConnectionRefused(const QString& reasonMessage, int reasonCode, const QString& extraInfo);
|
||||
void snapshotTaken(const QString& path, bool notify);
|
||||
void snapshotTaken(const QString& pathStillSnapshot, const QString& pathAnimatedSnapshot, bool notify);
|
||||
void snapshotShared(const QString& error);
|
||||
|
||||
private:
|
||||
|
|
105
interface/src/ui/SnapshotAnimated.cpp
Normal file
105
interface/src/ui/SnapshotAnimated.cpp
Normal file
|
@ -0,0 +1,105 @@
|
|||
//
|
||||
// SnapshotAnimated.cpp
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Zach Fox on 11/14/16.
|
||||
// Copyright 2016 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 <QtCore/QDateTime>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QString>
|
||||
#include <QtGui/QImage>
|
||||
|
||||
#include "SnapshotAnimated.h"
|
||||
|
||||
QTimer SnapshotAnimated::snapshotAnimatedTimer;
|
||||
GifWriter SnapshotAnimated::snapshotAnimatedGifWriter;
|
||||
qint64 SnapshotAnimated::snapshotAnimatedTimestamp = 0;
|
||||
qint64 SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp = 0;
|
||||
qint64 SnapshotAnimated::snapshotAnimatedLastWriteFrameDuration = 0;
|
||||
|
||||
void SnapshotAnimated::saveSnapshotAnimated(bool includeAnimated, QString pathStillSnapshot, float aspectRatio, Application* app, QSharedPointer<WindowScriptingInterface> dm) {
|
||||
// If we're not in the middle of capturing an animated snapshot...
|
||||
if ((snapshotAnimatedFirstFrameTimestamp == 0) && (includeAnimated))
|
||||
{
|
||||
// Define the output location of the animated snapshot
|
||||
QString pathAnimatedSnapshot(pathStillSnapshot);
|
||||
pathAnimatedSnapshot.replace("jpg", "gif");
|
||||
// Reset the current animated snapshot last frame duration
|
||||
snapshotAnimatedLastWriteFrameDuration = SNAPSNOT_ANIMATED_INITIAL_WRITE_DURATION_MSEC;
|
||||
|
||||
// Ensure the snapshot timer is Precise (attempted millisecond precision)
|
||||
snapshotAnimatedTimer.setTimerType(Qt::PreciseTimer);
|
||||
|
||||
// Connect the snapshotAnimatedTimer QTimer to the lambda slot function
|
||||
QObject::connect(&(snapshotAnimatedTimer), &QTimer::timeout, [=] {
|
||||
// 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(app->getActiveDisplayPlugin()->getScreenshot(aspectRatio));
|
||||
frame = frame.scaledToWidth(SNAPSNOT_ANIMATED_WIDTH);
|
||||
frame = frame.convertToFormat(QImage::Format_RGBA8888);
|
||||
|
||||
// If this is the first frame...
|
||||
if (snapshotAnimatedTimestamp == 0)
|
||||
{
|
||||
// Write out the header and beginning of the GIF file
|
||||
GifBegin(&(snapshotAnimatedGifWriter), qPrintable(pathAnimatedSnapshot), frame.width(), frame.height(), SNAPSNOT_ANIMATED_FRAME_DELAY_MSEC / 10);
|
||||
// Write the first to the gif
|
||||
GifWriteFrame(&(snapshotAnimatedGifWriter),
|
||||
(uint8_t*)frame.bits(),
|
||||
frame.width(),
|
||||
frame.height(),
|
||||
SNAPSNOT_ANIMATED_FRAME_DELAY_MSEC / 10);
|
||||
// Record the current frame timestamp
|
||||
snapshotAnimatedTimestamp = QDateTime::currentMSecsSinceEpoch();
|
||||
snapshotAnimatedFirstFrameTimestamp = snapshotAnimatedTimestamp;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If that was the last frame...
|
||||
if ((snapshotAnimatedTimestamp - snapshotAnimatedFirstFrameTimestamp) >= (SNAPSNOT_ANIMATED_DURATION_SECS * 1000))
|
||||
{
|
||||
// Reset the current frame timestamp
|
||||
snapshotAnimatedTimestamp = 0;
|
||||
snapshotAnimatedFirstFrameTimestamp = 0;
|
||||
// Write out the end of the GIF
|
||||
GifEnd(&(snapshotAnimatedGifWriter));
|
||||
// Stop the snapshot QTimer
|
||||
snapshotAnimatedTimer.stop();
|
||||
emit dm->snapshotTaken(pathStillSnapshot, pathAnimatedSnapshot, false);
|
||||
qDebug() << "still: " << pathStillSnapshot << "anim: " << pathAnimatedSnapshot;
|
||||
//emit dm->snapshotTaken("C:\\Users\\Zach Fox\\Desktop\\hifi-snap-by-zfox-on-2016-11-14_17-07-33.jpg", "C:\\Users\\Zach Fox\\Desktop\\hifi-snap-by-zfox-on-2016-11-14_17-10-02.gif", false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Variable used to determine how long the current frame took to pack
|
||||
qint64 framePackStartTime = QDateTime::currentMSecsSinceEpoch();
|
||||
// Write the frame to the gif
|
||||
GifWriteFrame(&(snapshotAnimatedGifWriter),
|
||||
(uint8_t*)frame.bits(),
|
||||
frame.width(),
|
||||
frame.height(),
|
||||
round(((float)(QDateTime::currentMSecsSinceEpoch() - snapshotAnimatedTimestamp + snapshotAnimatedLastWriteFrameDuration)) / 10));
|
||||
// Record how long it took for the current frame to pack
|
||||
snapshotAnimatedLastWriteFrameDuration = QDateTime::currentMSecsSinceEpoch() - framePackStartTime;
|
||||
// Record the current frame timestamp
|
||||
snapshotAnimatedTimestamp = QDateTime::currentMSecsSinceEpoch();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Start the snapshotAnimatedTimer QTimer - argument for this is in milliseconds
|
||||
snapshotAnimatedTimer.start(SNAPSNOT_ANIMATED_FRAME_DELAY_MSEC);
|
||||
}
|
||||
// If we're already in the middle of capturing an animated snapshot...
|
||||
else
|
||||
{
|
||||
// Just tell the dependency manager that the capture of the still snapshot has taken place.
|
||||
emit dm->snapshotTaken(pathStillSnapshot, "", false);
|
||||
}
|
||||
}
|
44
interface/src/ui/SnapshotAnimated.h
Normal file
44
interface/src/ui/SnapshotAnimated.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
//
|
||||
// SnapshotAnimated.h
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Zach Fox on 11/14/16.
|
||||
// Copyright 2016 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_SnapshotAnimated_h
|
||||
#define hifi_SnapshotAnimated_h
|
||||
|
||||
#include <Application.h>
|
||||
#include <DependencyManager.h>
|
||||
#include <GifCreator.h>
|
||||
#include <qtimer.h>
|
||||
#include "scripting/WindowScriptingInterface.h"
|
||||
|
||||
// If the snapshot width or the framerate are too high for the
|
||||
// application to handle, the framerate of the output GIF will drop.
|
||||
#define SNAPSNOT_ANIMATED_WIDTH (720)
|
||||
// This value should divide evenly into 100. Snapshot framerate is NOT guaranteed.
|
||||
#define SNAPSNOT_ANIMATED_TARGET_FRAMERATE (25)
|
||||
#define SNAPSNOT_ANIMATED_DURATION_SECS (3)
|
||||
|
||||
#define SNAPSNOT_ANIMATED_FRAME_DELAY_MSEC (1000/SNAPSNOT_ANIMATED_TARGET_FRAMERATE)
|
||||
// This is the fudge factor that we add to the *first* GIF frame's "delay" value
|
||||
#define SNAPSNOT_ANIMATED_INITIAL_WRITE_DURATION_MSEC (20)
|
||||
#define SNAPSNOT_ANIMATED_NUM_FRAMES (SNAPSNOT_ANIMATED_DURATION_SECS * SNAPSNOT_ANIMATED_TARGET_FRAMERATE)
|
||||
|
||||
class SnapshotAnimated {
|
||||
private:
|
||||
static QTimer snapshotAnimatedTimer;
|
||||
static GifWriter snapshotAnimatedGifWriter;
|
||||
static qint64 snapshotAnimatedTimestamp;
|
||||
static qint64 snapshotAnimatedFirstFrameTimestamp;
|
||||
static qint64 snapshotAnimatedLastWriteFrameDuration;
|
||||
public:
|
||||
static void saveSnapshotAnimated(bool includeAnimated, QString stillSnapshotPath, float aspectRatio, Application* app, QSharedPointer<WindowScriptingInterface> dm);
|
||||
};
|
||||
|
||||
#endif // hifi_SnapshotAnimated_h
|
|
@ -1,14 +1,14 @@
|
|||
<html>
|
||||
<head>
|
||||
<head>
|
||||
<title>Share</title>
|
||||
<link rel="stylesheet" type="text/css" href="css/edit-style.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/SnapshotReview.css">
|
||||
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
||||
<script type="text/javascript" src="js/eventBridgeLoader.js"></script>
|
||||
<script type="text/javascript" src="js/SnapshotReview.js"></script>
|
||||
</head>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<body>
|
||||
<div class="snapshot-container">
|
||||
<div class="snapshot-column-left">
|
||||
<div class="snapsection">
|
||||
|
@ -17,16 +17,16 @@
|
|||
<hr />
|
||||
<div class="snapsection">
|
||||
<div id="sharing">
|
||||
<div class="prompt">Would you like to share your pic in the Snapshots feed?</div>
|
||||
<div class="prompt">Would you like to share your pics in the Snapshots feed?</div>
|
||||
<div class="button">
|
||||
<span class="compound-button">
|
||||
<input type="button" class="blue" id="share" value="Share in Feed" onclick="shareSelected()"/>
|
||||
<input type="button" class="blue" id="share" value="Share in Feed" onclick="shareSelected()" />
|
||||
<span class="glyph"></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="button">
|
||||
<input type="button" class="black" id="close" value="Don't Share" onclick="doNotShare()"/>
|
||||
<input type="button" class="black" id="close" value="Don't Share" onclick="doNotShare()" />
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
|
@ -36,13 +36,13 @@
|
|||
<label for="snapshotSettings">Snapshot settings</label>
|
||||
</span>
|
||||
<span class="setting checkbox">
|
||||
<input id="openFeed" type="checkbox" checked/>
|
||||
<input id="openFeed" type="checkbox" checked />
|
||||
<label for="openFeed">Open feed after</label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="snapshot-images" class="snapshot-column-right"/>
|
||||
<div id="snapshot-images" class="snapshot-column-right">
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
var paths = [], idCounter = 0, useCheckboxes;
|
||||
function addImage(data) {
|
||||
if (data.localPath) {
|
||||
var div = document.createElement("DIV"),
|
||||
input = document.createElement("INPUT"),
|
||||
label = document.createElement("LABEL"),
|
||||
|
@ -26,7 +27,7 @@ function addImage(data) {
|
|||
label.setAttribute('for', id); // cannot do label.for =
|
||||
input.id = id;
|
||||
input.type = "checkbox";
|
||||
input.checked = true;
|
||||
input.checked = (id === "p0");
|
||||
input.addEventListener('change', toggle);
|
||||
div.class = "property checkbox";
|
||||
div.appendChild(input);
|
||||
|
@ -34,7 +35,7 @@ function addImage(data) {
|
|||
}
|
||||
document.getElementById("snapshot-images").appendChild(div);
|
||||
paths.push(data);
|
||||
|
||||
}
|
||||
}
|
||||
function handleShareButtons(shareMsg) {
|
||||
var openFeed = document.getElementById('openFeed');
|
||||
|
@ -49,7 +50,7 @@ function handleShareButtons(shareMsg) {
|
|||
window.onload = function () {
|
||||
// Something like the following will allow testing in a browser.
|
||||
//addImage({localPath: 'c:/Users/howar/OneDrive/Pictures/hifi-snap-by--on-2016-07-27_12-58-43.jpg'});
|
||||
//addImage({localPath: 'http://lorempixel.com/1512/1680'});
|
||||
//addImage({ localPath: 'http://lorempixel.com/1512/1680' });
|
||||
openEventBridge(function () {
|
||||
// Set up a handler for receiving the data, and tell the .js we are ready to receive it.
|
||||
EventBridge.scriptEventReceived.connect(function (message) {
|
||||
|
|
|
@ -58,37 +58,37 @@
|
|||
|
||||
/* global Script, Controller, Overlays, SoundArray, Quat, Vec3, MyAvatar, Menu, HMD, AudioDevice, LODManager, Settings, Camera */
|
||||
|
||||
(function() { // BEGIN LOCAL_SCOPE
|
||||
(function () { // BEGIN LOCAL_SCOPE
|
||||
|
||||
Script.include("./libraries/soundArray.js");
|
||||
Script.include("./libraries/soundArray.js");
|
||||
|
||||
var width = 340.0; //width of notification overlay
|
||||
var windowDimensions = Controller.getViewportDimensions(); // get the size of the interface window
|
||||
var overlayLocationX = (windowDimensions.x - (width + 20.0)); // positions window 20px from the right of the interface window
|
||||
var buttonLocationX = overlayLocationX + (width - 28.0);
|
||||
var locationY = 20.0; // position down from top of interface window
|
||||
var topMargin = 13.0;
|
||||
var leftMargin = 10.0;
|
||||
var textColor = { red: 228, green: 228, blue: 228}; // text color
|
||||
var backColor = { red: 2, green: 2, blue: 2}; // background color was 38,38,38
|
||||
var backgroundAlpha = 0;
|
||||
var fontSize = 12.0;
|
||||
var PERSIST_TIME_2D = 10.0; // Time in seconds before notification fades
|
||||
var PERSIST_TIME_3D = 15.0;
|
||||
var persistTime = PERSIST_TIME_2D;
|
||||
var frame = 0;
|
||||
var ourWidth = Window.innerWidth;
|
||||
var ourHeight = Window.innerHeight;
|
||||
var ctrlIsPressed = false;
|
||||
var ready = true;
|
||||
var MENU_NAME = 'Tools > Notifications';
|
||||
var PLAY_NOTIFICATION_SOUNDS_MENU_ITEM = "Play Notification Sounds";
|
||||
var NOTIFICATION_MENU_ITEM_POST = " Notifications";
|
||||
var PLAY_NOTIFICATION_SOUNDS_SETTING = "play_notification_sounds";
|
||||
var PLAY_NOTIFICATION_SOUNDS_TYPE_SETTING_PRE = "play_notification_sounds_type_";
|
||||
var lodTextID = false;
|
||||
var width = 340.0; //width of notification overlay
|
||||
var windowDimensions = Controller.getViewportDimensions(); // get the size of the interface window
|
||||
var overlayLocationX = (windowDimensions.x - (width + 20.0)); // positions window 20px from the right of the interface window
|
||||
var buttonLocationX = overlayLocationX + (width - 28.0);
|
||||
var locationY = 20.0; // position down from top of interface window
|
||||
var topMargin = 13.0;
|
||||
var leftMargin = 10.0;
|
||||
var textColor = { red: 228, green: 228, blue: 228 }; // text color
|
||||
var backColor = { red: 2, green: 2, blue: 2 }; // background color was 38,38,38
|
||||
var backgroundAlpha = 0;
|
||||
var fontSize = 12.0;
|
||||
var PERSIST_TIME_2D = 10.0; // Time in seconds before notification fades
|
||||
var PERSIST_TIME_3D = 15.0;
|
||||
var persistTime = PERSIST_TIME_2D;
|
||||
var frame = 0;
|
||||
var ourWidth = Window.innerWidth;
|
||||
var ourHeight = Window.innerHeight;
|
||||
var ctrlIsPressed = false;
|
||||
var ready = true;
|
||||
var MENU_NAME = 'Tools > Notifications';
|
||||
var PLAY_NOTIFICATION_SOUNDS_MENU_ITEM = "Play Notification Sounds";
|
||||
var NOTIFICATION_MENU_ITEM_POST = " Notifications";
|
||||
var PLAY_NOTIFICATION_SOUNDS_SETTING = "play_notification_sounds";
|
||||
var PLAY_NOTIFICATION_SOUNDS_TYPE_SETTING_PRE = "play_notification_sounds_type_";
|
||||
var lodTextID = false;
|
||||
|
||||
var NotificationType = {
|
||||
var NotificationType = {
|
||||
UNKNOWN: 0,
|
||||
SNAPSHOT: 1,
|
||||
LOD_WARNING: 2,
|
||||
|
@ -100,7 +100,7 @@ var NotificationType = {
|
|||
{ text: "Connection Refused" },
|
||||
{ text: "Edit error" }
|
||||
],
|
||||
getTypeFromMenuItem: function(menuItemName) {
|
||||
getTypeFromMenuItem: function (menuItemName) {
|
||||
if (menuItemName.substr(menuItemName.length - NOTIFICATION_MENU_ITEM_POST.length) !== NOTIFICATION_MENU_ITEM_POST) {
|
||||
return NotificationType.UNKNOWN;
|
||||
}
|
||||
|
@ -112,24 +112,24 @@ var NotificationType = {
|
|||
}
|
||||
return NotificationType.UNKNOWN;
|
||||
},
|
||||
getMenuString: function(type) {
|
||||
getMenuString: function (type) {
|
||||
return this.properties[type - 1].text + NOTIFICATION_MENU_ITEM_POST;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
var randomSounds = new SoundArray({ localOnly: true }, true);
|
||||
var numberOfSounds = 2;
|
||||
for (var i = 1; i <= numberOfSounds; i++) {
|
||||
randomSounds.addSound(Script.resolvePath("assets/sounds/notification-general"+ i + ".raw"));
|
||||
}
|
||||
var randomSounds = new SoundArray({ localOnly: true }, true);
|
||||
var numberOfSounds = 2;
|
||||
for (var i = 1; i <= numberOfSounds; i++) {
|
||||
randomSounds.addSound(Script.resolvePath("assets/sounds/notification-general" + i + ".raw"));
|
||||
}
|
||||
|
||||
var notifications = [];
|
||||
var buttons = [];
|
||||
var times = [];
|
||||
var heights = [];
|
||||
var myAlpha = [];
|
||||
var arrays = [];
|
||||
var isOnHMD = false,
|
||||
var notifications = [];
|
||||
var buttons = [];
|
||||
var times = [];
|
||||
var heights = [];
|
||||
var myAlpha = [];
|
||||
var arrays = [];
|
||||
var isOnHMD = false,
|
||||
NOTIFICATIONS_3D_DIRECTION = 0.0, // Degrees from avatar orientation.
|
||||
NOTIFICATIONS_3D_DISTANCE = 0.6, // Horizontal distance from avatar position.
|
||||
NOTIFICATIONS_3D_ELEVATION = -0.8, // Height of top middle of top notification relative to avatar eyes.
|
||||
|
@ -139,13 +139,13 @@ var isOnHMD = false,
|
|||
NOTIFICATION_3D_BUTTON_WIDTH = 40 * NOTIFICATION_3D_SCALE, // Need a little more room for button in 3D.
|
||||
overlay3DDetails = [];
|
||||
|
||||
// push data from above to the 2 dimensional array
|
||||
function createArrays(notice, button, createTime, height, myAlpha) {
|
||||
// push data from above to the 2 dimensional array
|
||||
function createArrays(notice, button, createTime, height, myAlpha) {
|
||||
arrays.push([notice, button, createTime, height, myAlpha]);
|
||||
}
|
||||
}
|
||||
|
||||
// This handles the final dismissal of a notification after fading
|
||||
function dismiss(firstNoteOut, firstButOut, firstOut) {
|
||||
// This handles the final dismissal of a notification after fading
|
||||
function dismiss(firstNoteOut, firstButOut, firstOut) {
|
||||
if (firstNoteOut == lodTextID) {
|
||||
lodTextID = false;
|
||||
}
|
||||
|
@ -158,9 +158,9 @@ function dismiss(firstNoteOut, firstButOut, firstOut) {
|
|||
heights.splice(firstOut, 1);
|
||||
myAlpha.splice(firstOut, 1);
|
||||
overlay3DDetails.splice(firstOut, 1);
|
||||
}
|
||||
}
|
||||
|
||||
function fadeIn(noticeIn, buttonIn) {
|
||||
function fadeIn(noticeIn, buttonIn) {
|
||||
var q = 0,
|
||||
qFade,
|
||||
pauseTimer = null;
|
||||
|
@ -174,10 +174,10 @@ function fadeIn(noticeIn, buttonIn) {
|
|||
Script.clearInterval(pauseTimer);
|
||||
}
|
||||
}, 10);
|
||||
}
|
||||
}
|
||||
|
||||
// this fades the notification ready for dismissal, and removes it from the arrays
|
||||
function fadeOut(noticeOut, buttonOut, arraysOut) {
|
||||
// this fades the notification ready for dismissal, and removes it from the arrays
|
||||
function fadeOut(noticeOut, buttonOut, arraysOut) {
|
||||
var r = 9.0,
|
||||
rFade,
|
||||
pauseTimer = null;
|
||||
|
@ -194,9 +194,9 @@ function fadeOut(noticeOut, buttonOut, arraysOut) {
|
|||
Script.clearInterval(pauseTimer);
|
||||
}
|
||||
}, 20);
|
||||
}
|
||||
}
|
||||
|
||||
function calculate3DOverlayPositions(noticeWidth, noticeHeight, y) {
|
||||
function calculate3DOverlayPositions(noticeWidth, noticeHeight, y) {
|
||||
// Calculates overlay positions and orientations in avatar coordinates.
|
||||
var noticeY,
|
||||
originOffset,
|
||||
|
@ -227,12 +227,12 @@ function calculate3DOverlayPositions(noticeWidth, noticeHeight, y) {
|
|||
notificationPosition: notificationPosition,
|
||||
buttonPosition: buttonPosition
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Pushes data to each array and sets up data for 2nd dimension array
|
||||
// to handle auxiliary data not carried by the overlay class
|
||||
// specifically notification "heights", "times" of creation, and .
|
||||
function notify(notice, button, height, imageProperties, image) {
|
||||
// Pushes data to each array and sets up data for 2nd dimension array
|
||||
// to handle auxiliary data not carried by the overlay class
|
||||
// specifically notification "heights", "times" of creation, and .
|
||||
function notify(notice, button, height, imageProperties, image) {
|
||||
var notificationText,
|
||||
noticeWidth,
|
||||
noticeHeight,
|
||||
|
@ -302,8 +302,10 @@ function notify(notice, button, height, imageProperties, image) {
|
|||
buttonPosition = Vec3.sum(defaultEyePosition,
|
||||
Vec3.multiplyQbyV(avatarOrientation,
|
||||
overlay3DDetails[i].buttonPosition));
|
||||
Overlays.editOverlay(notifications[i], { position: notificationPosition,
|
||||
rotation: notificationOrientation });
|
||||
Overlays.editOverlay(notifications[i], {
|
||||
position: notificationPosition,
|
||||
rotation: notificationOrientation
|
||||
});
|
||||
Overlays.editOverlay(buttons[i], { position: buttonPosition, rotation: notificationOrientation });
|
||||
}
|
||||
}
|
||||
|
@ -334,7 +336,7 @@ function notify(notice, button, height, imageProperties, image) {
|
|||
width: notice.width,
|
||||
height: imageHeight,
|
||||
subImage: { x: 0, y: 0 },
|
||||
color: { red: 255, green: 255, blue: 255},
|
||||
color: { red: 255, green: 255, blue: 255 },
|
||||
visible: true,
|
||||
imageURL: imageProperties.path,
|
||||
alpha: backgroundAlpha
|
||||
|
@ -343,12 +345,12 @@ function notify(notice, button, height, imageProperties, image) {
|
|||
}
|
||||
|
||||
return notificationText;
|
||||
}
|
||||
}
|
||||
|
||||
var CLOSE_NOTIFICATION_ICON = Script.resolvePath("assets/images/close-small-light.svg");
|
||||
var CLOSE_NOTIFICATION_ICON = Script.resolvePath("assets/images/close-small-light.svg");
|
||||
|
||||
// This function creates and sizes the overlays
|
||||
function createNotification(text, notificationType, imageProperties) {
|
||||
// This function creates and sizes the overlays
|
||||
function createNotification(text, notificationType, imageProperties) {
|
||||
var count = (text.match(/\n/g) || []).length,
|
||||
breakPoint = 43.0, // length when new line is added
|
||||
extraLine = 0,
|
||||
|
@ -382,7 +384,7 @@ function createNotification(text, notificationType, imageProperties) {
|
|||
alpha: backgroundAlpha,
|
||||
topMargin: topMargin,
|
||||
leftMargin: leftMargin,
|
||||
font: {size: fontSize},
|
||||
font: { size: fontSize },
|
||||
text: text
|
||||
};
|
||||
|
||||
|
@ -394,7 +396,7 @@ function createNotification(text, notificationType, imageProperties) {
|
|||
height: 10.0,
|
||||
subImage: { x: 0, y: 0, width: 10, height: 10 },
|
||||
imageURL: CLOSE_NOTIFICATION_ICON,
|
||||
color: { red: 255, green: 255, blue: 255},
|
||||
color: { red: 255, green: 255, blue: 255 },
|
||||
visible: true,
|
||||
alpha: backgroundAlpha
|
||||
};
|
||||
|
@ -405,9 +407,9 @@ function createNotification(text, notificationType, imageProperties) {
|
|||
}
|
||||
|
||||
return notify(noticeProperties, buttonProperties, height, imageProperties);
|
||||
}
|
||||
}
|
||||
|
||||
function deleteNotification(index) {
|
||||
function deleteNotification(index) {
|
||||
var notificationTextID = notifications[index];
|
||||
if (notificationTextID == lodTextID) {
|
||||
lodTextID = false;
|
||||
|
@ -421,10 +423,10 @@ function deleteNotification(index) {
|
|||
myAlpha.splice(index, 1);
|
||||
overlay3DDetails.splice(index, 1);
|
||||
arrays.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// wraps whole word to newline
|
||||
function stringDivider(str, slotWidth, spaceReplacer) {
|
||||
// wraps whole word to newline
|
||||
function stringDivider(str, slotWidth, spaceReplacer) {
|
||||
var left, right;
|
||||
|
||||
if (str.length > slotWidth && slotWidth > 0) {
|
||||
|
@ -433,14 +435,14 @@ function stringDivider(str, slotWidth, spaceReplacer) {
|
|||
return left + spaceReplacer + stringDivider(right, slotWidth, spaceReplacer);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
// formats string to add newline every 43 chars
|
||||
function wordWrap(str) {
|
||||
// formats string to add newline every 43 chars
|
||||
function wordWrap(str) {
|
||||
return stringDivider(str, 43.0, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
function update() {
|
||||
function update() {
|
||||
var nextOverlay,
|
||||
noticeOut,
|
||||
buttonOut,
|
||||
|
@ -491,18 +493,18 @@ function update() {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var STARTUP_TIMEOUT = 500, // ms
|
||||
var STARTUP_TIMEOUT = 500, // ms
|
||||
startingUp = true,
|
||||
startupTimer = null;
|
||||
|
||||
function finishStartup() {
|
||||
function finishStartup() {
|
||||
startingUp = false;
|
||||
Script.clearTimeout(startupTimer);
|
||||
}
|
||||
}
|
||||
|
||||
function isStartingUp() {
|
||||
function isStartingUp() {
|
||||
// Is starting up until get no checks that it is starting up for STARTUP_TIMEOUT
|
||||
if (startingUp) {
|
||||
if (startupTimer) {
|
||||
|
@ -511,29 +513,29 @@ function isStartingUp() {
|
|||
startupTimer = Script.setTimeout(finishStartup, STARTUP_TIMEOUT);
|
||||
}
|
||||
return startingUp;
|
||||
}
|
||||
}
|
||||
|
||||
function onDomainConnectionRefused(reason) {
|
||||
function onDomainConnectionRefused(reason) {
|
||||
createNotification("Connection refused: " + reason, NotificationType.CONNECTION_REFUSED);
|
||||
}
|
||||
}
|
||||
|
||||
function onEditError(msg) {
|
||||
function onEditError(msg) {
|
||||
createNotification(wordWrap(msg), NotificationType.EDIT_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function onSnapshotTaken(path, notify) {
|
||||
function onSnapshotTaken(pathStillSnapshot, pathAnimatedSnapshot, notify) {
|
||||
if (notify) {
|
||||
var imageProperties = {
|
||||
path: "file:///" + path,
|
||||
path: "file:///" + pathStillSnapshot,
|
||||
aspectRatio: Window.innerWidth / Window.innerHeight
|
||||
};
|
||||
createNotification(wordWrap("Snapshot saved to " + path), NotificationType.SNAPSHOT, imageProperties);
|
||||
createNotification(wordWrap("Snapshot saved to " + pathStillSnapshot), NotificationType.SNAPSHOT, imageProperties);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handles mouse clicks on buttons
|
||||
function mousePressEvent(event) {
|
||||
// handles mouse clicks on buttons
|
||||
function mousePressEvent(event) {
|
||||
var pickRay,
|
||||
clickedOverlay,
|
||||
i;
|
||||
|
@ -550,23 +552,23 @@ function mousePressEvent(event) {
|
|||
deleteNotification(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Control key remains active only while key is held down
|
||||
function keyReleaseEvent(key) {
|
||||
// Control key remains active only while key is held down
|
||||
function keyReleaseEvent(key) {
|
||||
if (key.key === 16777249) {
|
||||
ctrlIsPressed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Triggers notification on specific key driven events
|
||||
function keyPressEvent(key) {
|
||||
// Triggers notification on specific key driven events
|
||||
function keyPressEvent(key) {
|
||||
if (key.key === 16777249) {
|
||||
ctrlIsPressed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setup() {
|
||||
function setup() {
|
||||
Menu.addMenu(MENU_NAME);
|
||||
var checked = Settings.getValue(PLAY_NOTIFICATION_SOUNDS_SETTING);
|
||||
checked = checked === '' ? true : checked;
|
||||
|
@ -587,18 +589,18 @@ function setup() {
|
|||
isChecked: checked
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When our script shuts down, we should clean up all of our overlays
|
||||
function scriptEnding() {
|
||||
// When our script shuts down, we should clean up all of our overlays
|
||||
function scriptEnding() {
|
||||
for (var i = 0; i < notifications.length; i++) {
|
||||
Overlays.deleteOverlay(notifications[i]);
|
||||
Overlays.deleteOverlay(buttons[i]);
|
||||
}
|
||||
Menu.removeMenu(MENU_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
function menuItemEvent(menuItem) {
|
||||
function menuItemEvent(menuItem) {
|
||||
if (menuItem === PLAY_NOTIFICATION_SOUNDS_MENU_ITEM) {
|
||||
Settings.setValue(PLAY_NOTIFICATION_SOUNDS_SETTING, Menu.isOptionChecked(PLAY_NOTIFICATION_SOUNDS_MENU_ITEM));
|
||||
return;
|
||||
|
@ -607,9 +609,9 @@ function menuItemEvent(menuItem) {
|
|||
if (notificationType !== notificationType.UNKNOWN) {
|
||||
Settings.setValue(PLAY_NOTIFICATION_SOUNDS_TYPE_SETTING_PRE + notificationType, Menu.isOptionChecked(menuItem));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LODManager.LODDecreased.connect(function() {
|
||||
LODManager.LODDecreased.connect(function () {
|
||||
var warningText = "\n" +
|
||||
"Due to the complexity of the content, the \n" +
|
||||
"level of detail has been decreased. " +
|
||||
|
@ -621,18 +623,18 @@ LODManager.LODDecreased.connect(function() {
|
|||
} else {
|
||||
Overlays.editOverlay(lodTextID, { text: warningText });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Controller.keyPressEvent.connect(keyPressEvent);
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
||||
Script.update.connect(update);
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
Menu.menuItemEvent.connect(menuItemEvent);
|
||||
Window.domainConnectionRefused.connect(onDomainConnectionRefused);
|
||||
Window.snapshotTaken.connect(onSnapshotTaken);
|
||||
Window.notifyEditError = onEditError;
|
||||
Controller.keyPressEvent.connect(keyPressEvent);
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
||||
Script.update.connect(update);
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
Menu.menuItemEvent.connect(menuItemEvent);
|
||||
Window.domainConnectionRefused.connect(onDomainConnectionRefused);
|
||||
Window.snapshotTaken.connect(onSnapshotTaken);
|
||||
Window.notifyEditError = onEditError;
|
||||
|
||||
setup();
|
||||
setup();
|
||||
|
||||
}()); // END LOCAL_SCOPE
|
||||
|
|
|
@ -120,11 +120,11 @@ function onClicked() {
|
|||
|
||||
// take snapshot (with no notification)
|
||||
Script.setTimeout(function () {
|
||||
Window.takeSnapshot(false, 1.91);
|
||||
Window.takeSnapshot(false, true, 1.91);
|
||||
}, SNAPSHOT_DELAY);
|
||||
}
|
||||
|
||||
function resetButtons(path, notify) {
|
||||
function resetButtons(pathStillSnapshot, pathAnimatedSnapshot, notify) {
|
||||
// show overlays if they were on
|
||||
if (resetOverlays) {
|
||||
Menu.setIsOptionChecked("Overlays", true);
|
||||
|
@ -141,7 +141,8 @@ function resetButtons(path, notify) {
|
|||
|
||||
// last element in data array tells dialog whether we can share or not
|
||||
confirmShare([
|
||||
{ localPath: path },
|
||||
{ localPath: pathAnimatedSnapshot },
|
||||
{ localPath: pathStillSnapshot },
|
||||
{
|
||||
canShare: !!location.placename,
|
||||
openFeedAfterShare: shouldOpenFeedAfterShare()
|
||||
|
|
Loading…
Reference in a new issue