Huge progress today. Buggy behavior with multiple snapshots remains

This commit is contained in:
Zach Fox 2016-11-14 17:41:46 -08:00
parent 8f9ffd2bc5
commit a81289a0d7
10 changed files with 735 additions and 678 deletions

View file

@ -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) {

View file

@ -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;
};

View file

@ -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) {

View file

@ -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:

View 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);
}
}

View 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

View file

@ -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>

View file

@ -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) {

View file

@ -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

View file

@ -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()