diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp
index d763d1abe7..48ffc2fdbc 100644
--- a/assignment-client/src/octree/OctreeServer.cpp
+++ b/assignment-client/src/octree/OctreeServer.cpp
@@ -1063,6 +1063,12 @@ void OctreeServer::readConfiguration() {
_wantBackup = !noBackup;
qDebug() << "wantBackup=" << _wantBackup;
+ if (!readOptionString("backupDirectoryPath", settingsSectionObject, _backupDirectoryPath)) {
+ _backupDirectoryPath = "";
+ }
+
+ qDebug() << "backupDirectoryPath=" << _backupDirectoryPath;
+
readOptionBool(QString("persistFileDownload"), settingsSectionObject, _persistFileDownload);
qDebug() << "persistFileDownload=" << _persistFileDownload;
@@ -1160,25 +1166,25 @@ void OctreeServer::domainSettingsRequestComplete() {
// If persist filename does not exist, let's see if there is one beside the application binary
// If there is, let's copy it over to our target persist directory
QDir persistPath { _persistFilePath };
- QString absoluteFilePath = persistPath.absolutePath();
+ QString persistAbsoluteFilePath = persistPath.absolutePath();
if (persistPath.isRelative()) {
// if the domain settings passed us a relative path, make an absolute path that is relative to the
// default data directory
- absoluteFilePath = QDir(ServerPathUtils::getDataFilePath("entities/")).absoluteFilePath(_persistFilePath);
+ persistAbsoluteFilePath = QDir(ServerPathUtils::getDataFilePath("entities/")).absoluteFilePath(_persistFilePath);
}
static const QString ENTITY_PERSIST_EXTENSION = ".json.gz";
// force the persist file to end with .json.gz
- if (!absoluteFilePath.endsWith(ENTITY_PERSIST_EXTENSION, Qt::CaseInsensitive)) {
- absoluteFilePath += ENTITY_PERSIST_EXTENSION;
+ if (!persistAbsoluteFilePath.endsWith(ENTITY_PERSIST_EXTENSION, Qt::CaseInsensitive)) {
+ persistAbsoluteFilePath += ENTITY_PERSIST_EXTENSION;
} else {
// make sure the casing of .json.gz is correct
- absoluteFilePath.replace(ENTITY_PERSIST_EXTENSION, ENTITY_PERSIST_EXTENSION, Qt::CaseInsensitive);
+ persistAbsoluteFilePath.replace(ENTITY_PERSIST_EXTENSION, ENTITY_PERSIST_EXTENSION, Qt::CaseInsensitive);
}
- if (!QFile::exists(absoluteFilePath)) {
+ if (!QFile::exists(persistAbsoluteFilePath)) {
qDebug() << "Persist file does not exist, checking for existence of persist file next to application";
static const QString OLD_DEFAULT_PERSIST_FILENAME = "resources/models.json.gz";
@@ -1204,7 +1210,7 @@ void OctreeServer::domainSettingsRequestComplete() {
pathToCopyFrom = oldDefaultPersistPath;
}
- QDir persistFileDirectory { QDir::cleanPath(absoluteFilePath + "/..") };
+ QDir persistFileDirectory { QDir::cleanPath(persistAbsoluteFilePath + "/..") };
if (!persistFileDirectory.exists()) {
qDebug() << "Creating data directory " << persistFileDirectory.absolutePath();
@@ -1212,16 +1218,46 @@ void OctreeServer::domainSettingsRequestComplete() {
}
if (shouldCopy) {
- qDebug() << "Old persist file found, copying from " << pathToCopyFrom << " to " << absoluteFilePath;
+ qDebug() << "Old persist file found, copying from " << pathToCopyFrom << " to " << persistAbsoluteFilePath;
- QFile::copy(pathToCopyFrom, absoluteFilePath);
+ QFile::copy(pathToCopyFrom, persistAbsoluteFilePath);
} else {
qDebug() << "No existing persist file found";
}
}
+
+ auto persistFileDirectory = QFileInfo(persistAbsoluteFilePath).absolutePath();
+ if (_backupDirectoryPath.isEmpty()) {
+ // Use the persist file's directory to store backups
+ _backupDirectoryPath = persistFileDirectory;
+ } else {
+ // The backup directory has been set.
+ // If relative, make it relative to the entities directory in the application data directory
+ // If absolute, no resolution is necessary
+ QDir backupDirectory { _backupDirectoryPath };
+ QString absoluteBackupDirectory;
+ if (backupDirectory.isRelative()) {
+ absoluteBackupDirectory = QDir(ServerPathUtils::getDataFilePath("entities/")).absoluteFilePath(_backupDirectoryPath);
+ absoluteBackupDirectory = QDir(absoluteBackupDirectory).absolutePath();
+ } else {
+ absoluteBackupDirectory = backupDirectory.absolutePath();
+ }
+ backupDirectory = QDir(absoluteBackupDirectory);
+ if (!backupDirectory.exists()) {
+ if (backupDirectory.mkpath(".")) {
+ qDebug() << "Created backup directory";
+ } else {
+ qDebug() << "ERROR creating backup directory, using persist file directory";
+ _backupDirectoryPath = persistFileDirectory;
+ }
+ } else {
+ _backupDirectoryPath = absoluteBackupDirectory;
+ }
+ }
+ qDebug() << "Backups will be stored in: " << _backupDirectoryPath;
// now set up PersistThread
- _persistThread = new OctreePersistThread(_tree, absoluteFilePath, _persistInterval,
+ _persistThread = new OctreePersistThread(_tree, persistAbsoluteFilePath, _backupDirectoryPath, _persistInterval,
_wantBackup, _settings, _debugTimestampNow, _persistAsFileType);
_persistThread->initialize(true);
}
diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h
index beea322e9b..2bcf36628d 100644
--- a/assignment-client/src/octree/OctreeServer.h
+++ b/assignment-client/src/octree/OctreeServer.h
@@ -172,6 +172,7 @@ protected:
QString _persistFilePath;
QString _persistAsFileType;
+ QString _backupDirectoryPath;
int _packetsPerClientPerInterval;
int _packetsTotalPerInterval;
OctreePointer _tree; // this IS a reaveraging tree
diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json
index c888fa301b..c9d7ea77d5 100644
--- a/domain-server/resources/describe-settings.json
+++ b/domain-server/resources/describe-settings.json
@@ -1108,6 +1108,14 @@
"default": "models.json.gz",
"advanced": true
},
+ {
+ "name": "backupDirectoryPath",
+ "label": "Entities Backup Directory Path",
+ "help": "The path to the directory to store backups in.
If this path is relative it will be relative to the application data directory.",
+ "placeholder": "",
+ "default": "",
+ "advanced": true
+ },
{
"name": "persistInterval",
"label": "Save Check Interval",
diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp
index c827e79223..23a53c3eb0 100644
--- a/domain-server/src/DomainGatekeeper.cpp
+++ b/domain-server/src/DomainGatekeeper.cpp
@@ -509,9 +509,7 @@ bool DomainGatekeeper::verifyUserSignature(const QString& username,
}
} else {
if (!senderSockAddr.isNull()) {
- qDebug() << "Insufficient data to decrypt username signature - denying connection.";
- sendConnectionDeniedPacket("Insufficient data", senderSockAddr,
- DomainHandler::ConnectionRefusedReason::LoginError);
+ qDebug() << "Insufficient data to decrypt username signature - delaying connection.";
}
}
diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json
index 222357ac9d..c9e91c8666 100644
--- a/interface/resources/controllers/standard.json
+++ b/interface/resources/controllers/standard.json
@@ -11,7 +11,7 @@
[
{ "type": "deadZone", "min": 0.15 },
"constrainToInteger",
- { "type": "pulse", "interval": 0.5 },
+ { "type": "pulse", "interval": 0.25 },
{ "type": "scale", "scale": 22.5 }
]
},
diff --git a/interface/resources/controllers/vive.json b/interface/resources/controllers/vive.json
index 79114b8141..dce3e9660c 100644
--- a/interface/resources/controllers/vive.json
+++ b/interface/resources/controllers/vive.json
@@ -4,8 +4,8 @@
{ "from": "Vive.LY", "when": "Vive.LSY", "filters": ["invert"], "to": "Standard.LY" },
{ "from": "Vive.LX", "when": "Vive.LSX", "to": "Standard.LX" },
{
- "from": "Vive.LT", "to": "Standard.LT",
- "filters": [
+ "from": "Vive.LT", "to": "Standard.LT",
+ "filters": [
{ "type": "deadZone", "min": 0.05 }
]
},
@@ -18,8 +18,8 @@
{ "from": "Vive.RY", "when": "Vive.RSY", "filters": ["invert"], "to": "Standard.RY" },
{ "from": "Vive.RX", "when": "Vive.RSX", "to": "Standard.RX" },
{
- "from": "Vive.RT", "to": "Standard.RT",
- "filters": [
+ "from": "Vive.RT", "to": "Standard.RT",
+ "filters": [
{ "type": "deadZone", "min": 0.05 }
]
},
@@ -37,4 +37,4 @@
{ "from": "Vive.LeftHand", "to": "Standard.LeftHand" },
{ "from": "Vive.RightHand", "to": "Standard.RightHand" }
]
-}
+}
\ No newline at end of file
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index c17b9963e5..836b48b3fb 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -1138,10 +1138,16 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
static int SEND_STATS_INTERVAL_MS = 10000;
static int NEARBY_AVATAR_RADIUS_METERS = 10;
+ static glm::vec3 lastAvatarPosition = getMyAvatar()->getPosition();
+ static glm::mat4 lastHMDHeadPose = getHMDSensorPose();
+ static controller::Pose lastLeftHandPose = getMyAvatar()->getLeftHandPose();
+ static controller::Pose lastRightHandPose = getMyAvatar()->getRightHandPose();
+
// Periodically send fps as a user activity event
QTimer* sendStatsTimer = new QTimer(this);
sendStatsTimer->setInterval(SEND_STATS_INTERVAL_MS);
connect(sendStatsTimer, &QTimer::timeout, this, [this]() {
+
QJsonObject properties = {};
MemoryInfo memInfo;
if (getMemoryInfo(memInfo)) {
@@ -1183,6 +1189,31 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
properties["throttled"] = _displayPlugin ? _displayPlugin->isThrottled() : false;
+ glm::vec3 avatarPosition = getMyAvatar()->getPosition();
+ properties["avatar_has_moved"] = lastAvatarPosition != avatarPosition;
+ lastAvatarPosition = avatarPosition;
+
+ auto entityScriptingInterface = DependencyManager::get();
+ auto entityActivityTracking = entityScriptingInterface->getActivityTracking();
+ entityScriptingInterface->resetActivityTracking();
+ properties["added_entity_cnt"] = entityActivityTracking.addedEntityCount;
+ properties["deleted_entity_cnt"] = entityActivityTracking.deletedEntityCount;
+ properties["edited_entity_cnt"] = entityActivityTracking.editedEntityCount;
+
+ auto hmdHeadPose = getHMDSensorPose();
+ properties["hmd_head_pose_changed"] = isHMDMode() && (hmdHeadPose != lastHMDHeadPose);
+ lastHMDHeadPose = hmdHeadPose;
+
+ auto leftHandPose = getMyAvatar()->getLeftHandPose();
+ auto rightHandPose = getMyAvatar()->getRightHandPose();
+ // controller::Pose considers two poses to be different if either are invalid. In our case, we actually
+ // want to consider the pose to be unchanged if it was invalid and still is invalid, so we check that first.
+ properties["hand_pose_changed"] =
+ ((leftHandPose.valid || lastLeftHandPose.valid) && (leftHandPose != lastLeftHandPose))
+ || ((rightHandPose.valid || lastRightHandPose.valid) && (rightHandPose != lastRightHandPose));
+ lastLeftHandPose = leftHandPose;
+ lastRightHandPose = rightHandPose;
+
UserActivityLogger::getInstance().logAction("stats", properties);
});
sendStatsTimer->start();
@@ -1237,6 +1268,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
_defaultSkybox->setCubemap(_defaultSkyboxTexture);
_defaultSkybox->setColor({ 1.0, 1.0, 1.0 });
+ EntityItem::setEntitiesShouldFadeFunction([this]() {
+ SharedNodePointer entityServerNode = DependencyManager::get()->soloNodeOfType(NodeType::EntityServer);
+ return entityServerNode && !isPhysicsEnabled();
+ });
+
// After all of the constructor is completed, then set firstRun to false.
Setting::Handle firstRun{ Settings::firstRun, true };
firstRun.set(false);
diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index 57e379a9ac..782ecbcc64 100644
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -516,13 +516,23 @@ glm::mat4 MyAvatar::getSensorToWorldMatrix() const {
return _sensorToWorldMatrixCache.get();
}
+// As far as I know no HMD system supports a play area of a kilometer in radius.
+static const float MAX_HMD_ORIGIN_DISTANCE = 1000.0f;
// Pass a recent sample of the HMD to the avatar.
// This can also update the avatar's position to follow the HMD
// as it moves through the world.
void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) {
// update the sensorMatrices based on the new hmd pose
_hmdSensorMatrix = hmdSensorMatrix;
- _hmdSensorPosition = extractTranslation(hmdSensorMatrix);
+ auto newHmdSensorPosition = extractTranslation(hmdSensorMatrix);
+
+ if (newHmdSensorPosition != _hmdSensorPosition &&
+ glm::length(newHmdSensorPosition) > MAX_HMD_ORIGIN_DISTANCE) {
+ qWarning() << "Invalid HMD sensor position " << newHmdSensorPosition;
+ // Ignore unreasonable HMD sensor data
+ return;
+ }
+ _hmdSensorPosition = newHmdSensorPosition;
_hmdSensorOrientation = glm::quat_cast(hmdSensorMatrix);
_hmdSensorFacing = getFacingDir2D(_hmdSensorOrientation);
}
diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp
index b9f384f013..29cbfd79e6 100644
--- a/libraries/entities/src/EntityItem.cpp
+++ b/libraries/entities/src/EntityItem.cpp
@@ -35,6 +35,7 @@
int EntityItem::_maxActionsDataSize = 800;
quint64 EntityItem::_rememberDeletedActionTime = 20 * USECS_PER_SECOND;
+std::function EntityItem::_entitiesShouldFadeFunction = [](){ return true; };
EntityItem::EntityItem(const EntityItemID& entityItemID) :
SpatiallyNestable(NestableType::Entity, entityItemID),
diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h
index fd2cf41b77..45e178a8dc 100644
--- a/libraries/entities/src/EntityItem.h
+++ b/libraries/entities/src/EntityItem.h
@@ -432,6 +432,8 @@ public:
QUuid getOwningAvatarID() const { return _owningAvatarID; }
void setOwningAvatarID(const QUuid& owningAvatarID) { _owningAvatarID = owningAvatarID; }
+ static void setEntitiesShouldFadeFunction(std::function func) { _entitiesShouldFadeFunction = func; }
+ static std::function getEntitiesShouldFadeFunction() { return _entitiesShouldFadeFunction; }
virtual bool isTransparent() { return _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f : false; }
protected:
@@ -564,7 +566,8 @@ protected:
quint64 _lastUpdatedAccelerationTimestamp { 0 };
quint64 _fadeStartTime { usecTimestampNow() };
- bool _isFading { true };
+ static std::function _entitiesShouldFadeFunction;
+ bool _isFading { _entitiesShouldFadeFunction() };
};
#endif // hifi_EntityItem_h
diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp
index 232b952a93..1961742c2e 100644
--- a/libraries/entities/src/EntityScriptingInterface.cpp
+++ b/libraries/entities/src/EntityScriptingInterface.cpp
@@ -40,6 +40,12 @@ void EntityScriptingInterface::queueEntityMessage(PacketType packetType,
getEntityPacketSender()->queueEditEntityMessage(packetType, _entityTree, entityID, properties);
}
+void EntityScriptingInterface::resetActivityTracking() {
+ _activityTracking.addedEntityCount = 0;
+ _activityTracking.deletedEntityCount = 0;
+ _activityTracking.editedEntityCount = 0;
+}
+
bool EntityScriptingInterface::canAdjustLocks() {
auto nodeList = DependencyManager::get();
return nodeList->isAllowedEditor();
@@ -162,6 +168,8 @@ EntityItemProperties convertLocationFromScriptSemantics(const EntityItemProperti
QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties, bool clientOnly) {
+ _activityTracking.addedEntityCount++;
+
EntityItemProperties propertiesWithSimID = convertLocationFromScriptSemantics(properties);
propertiesWithSimID.setDimensionsInitialized(properties.dimensionsChanged());
@@ -232,6 +240,8 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties
QUuid EntityScriptingInterface::addModelEntity(const QString& name, const QString& modelUrl, const QString& shapeType,
bool dynamic, const glm::vec3& position, const glm::vec3& gravity) {
+ _activityTracking.addedEntityCount++;
+
EntityItemProperties properties;
properties.setType(EntityTypes::Model);
properties.setName(name);
@@ -295,6 +305,8 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identit
}
QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& scriptSideProperties) {
+ _activityTracking.editedEntityCount++;
+
EntityItemProperties properties = scriptSideProperties;
auto dimensions = properties.getDimensions();
@@ -438,6 +450,8 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties&
}
void EntityScriptingInterface::deleteEntity(QUuid id) {
+ _activityTracking.deletedEntityCount++;
+
EntityItemID entityID(id);
bool shouldDelete = true;
diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h
index afc529bc53..e3c659cf6b 100644
--- a/libraries/entities/src/EntityScriptingInterface.h
+++ b/libraries/entities/src/EntityScriptingInterface.h
@@ -65,6 +65,13 @@ class EntityScriptingInterface : public OctreeScriptingInterface, public Depende
public:
EntityScriptingInterface(bool bidOnSimulationOwnership);
+ class ActivityTracking {
+ public:
+ int addedEntityCount { 0 };
+ int deletedEntityCount { 0 };
+ int editedEntityCount { 0 };
+ };
+
EntityEditPacketSender* getEntityPacketSender() const { return (EntityEditPacketSender*)getPacketSender(); }
virtual NodeType_t getServerNodeType() const override { return NodeType::EntityServer; }
virtual OctreeEditPacketSender* createPacketSender() override { return new EntityEditPacketSender(); }
@@ -73,6 +80,9 @@ public:
EntityTreePointer getEntityTree() { return _entityTree; }
void setEntitiesScriptEngine(EntitiesScriptEngineProvider* engine);
float calculateCost(float mass, float oldVelocity, float newVelocity);
+
+ void resetActivityTracking();
+ ActivityTracking getActivityTracking() const { return _activityTracking; }
public slots:
// returns true if the DomainServer will allow this Node/Avatar to make changes
@@ -227,6 +237,7 @@ private:
float getCurrentAvatarEnergy() { return _currentAvatarEnergy; }
void setCurrentAvatarEnergy(float energy);
+ ActivityTracking _activityTracking;
float costMultiplier = { 0.01f };
float getCostMultiplier();
void setCostMultiplier(float value);
diff --git a/libraries/gpu/src/gpu/Buffer.h b/libraries/gpu/src/gpu/Buffer.h
index d2f52180c5..5bcfb2cbab 100644
--- a/libraries/gpu/src/gpu/Buffer.h
+++ b/libraries/gpu/src/gpu/Buffer.h
@@ -293,10 +293,18 @@ public:
template Iterator end() { return Iterator(&edit(getNum()), _stride); }
#else
template Iterator begin() const { return Iterator(&get(), _stride); }
- template Iterator end() const { return Iterator(&get(getNum()), _stride); }
+ template Iterator end() const {
+ // reimplement get without bounds checking
+ Resource::Size elementOffset = getNum() * _stride + _offset;
+ return Iterator((reinterpret_cast (_buffer->getData() + elementOffset)), _stride);
+ }
#endif
template Iterator cbegin() const { return Iterator(&get(), _stride); }
- template Iterator cend() const { return Iterator(&get(getNum()), _stride); }
+ template Iterator cend() const {
+ // reimplement get without bounds checking
+ Resource::Size elementOffset = getNum() * _stride + _offset;
+ return Iterator((reinterpret_cast (_buffer->getData() + elementOffset)), _stride);
+ }
// the number of elements of the specified type fitting in the view size
template Index getNum() const {
diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp
index d48c35d542..7034790eaf 100644
--- a/libraries/octree/src/OctreePersistThread.cpp
+++ b/libraries/octree/src/OctreePersistThread.cpp
@@ -34,11 +34,12 @@
const int OctreePersistThread::DEFAULT_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds
-OctreePersistThread::OctreePersistThread(OctreePointer tree, const QString& filename, int persistInterval,
+OctreePersistThread::OctreePersistThread(OctreePointer tree, const QString& filename, const QString& backupDirectory, int persistInterval,
bool wantBackup, const QJsonObject& settings, bool debugTimestampNow,
QString persistAsFileType) :
_tree(tree),
_filename(filename),
+ _backupDirectory(backupDirectory),
_persistInterval(persistInterval),
_initialLoadComplete(false),
_loadTimeUSecs(0),
@@ -316,7 +317,7 @@ bool OctreePersistThread::getMostRecentBackup(const QString& format,
// Based on our backup file name, determine the path and file name pattern for backup files
QFileInfo persistFileInfo(_filename);
- QString path = persistFileInfo.path();
+ QString path = _backupDirectory;
QString fileNamePart = persistFileInfo.fileName();
QStringList filters;
@@ -369,10 +370,12 @@ void OctreePersistThread::rollOldBackupVersions(const BackupRule& rule) {
if (rule.maxBackupVersions > 0) {
qCDebug(octree) << "Rolling old backup versions for rule" << rule.name << "...";
+ QString backupFileName = _backupDirectory + "/" + QUrl(_filename).fileName();
+
// Delete maximum rolling file because rename() fails on Windows if target exists
QString backupMaxExtensionN = rule.extensionFormat;
backupMaxExtensionN.replace(QString("%N"), QString::number(rule.maxBackupVersions));
- QString backupMaxFilenameN = _filename + backupMaxExtensionN;
+ QString backupMaxFilenameN = backupFileName + backupMaxExtensionN;
QFile backupMaxFileN(backupMaxFilenameN);
if (backupMaxFileN.exists()) {
int result = remove(qPrintable(backupMaxFilenameN));
@@ -387,8 +390,8 @@ void OctreePersistThread::rollOldBackupVersions(const BackupRule& rule) {
backupExtensionN.replace(QString("%N"), QString::number(n));
backupExtensionNplusOne.replace(QString("%N"), QString::number(n+1));
- QString backupFilenameN = findMostRecentFileExtension(_filename, PERSIST_EXTENSIONS) + backupExtensionN;
- QString backupFilenameNplusOne = _filename + backupExtensionNplusOne;
+ QString backupFilenameN = findMostRecentFileExtension(backupFileName, PERSIST_EXTENSIONS) + backupExtensionN;
+ QString backupFilenameNplusOne = backupFileName + backupExtensionNplusOne;
QFile backupFileN(backupFilenameN);
@@ -434,21 +437,20 @@ void OctreePersistThread::backup() {
struct tm* localTime = localtime(&_lastPersistTime);
- QString backupFileName;
+ QString backupFileName = _backupDirectory + "/" + QUrl(_filename).fileName();
// check to see if they asked for version rolling format
if (rule.extensionFormat.contains("%N")) {
rollOldBackupVersions(rule); // rename all the old backup files accordingly
QString backupExtension = rule.extensionFormat;
backupExtension.replace(QString("%N"), QString("1"));
- backupFileName = _filename + backupExtension;
+ backupFileName += backupExtension;
} else {
char backupExtension[256];
strftime(backupExtension, sizeof(backupExtension), qPrintable(rule.extensionFormat), localTime);
- backupFileName = _filename + backupExtension;
+ backupFileName += backupExtension;
}
-
if (rule.maxBackupVersions > 0) {
QFile persistFile(_filename);
if (persistFile.exists()) {
diff --git a/libraries/octree/src/OctreePersistThread.h b/libraries/octree/src/OctreePersistThread.h
index d0f0f03f98..f8215fb34a 100644
--- a/libraries/octree/src/OctreePersistThread.h
+++ b/libraries/octree/src/OctreePersistThread.h
@@ -33,9 +33,9 @@ public:
static const int DEFAULT_PERSIST_INTERVAL;
- OctreePersistThread(OctreePointer tree, const QString& filename, int persistInterval = DEFAULT_PERSIST_INTERVAL,
- bool wantBackup = false, const QJsonObject& settings = QJsonObject(),
- bool debugTimestampNow = false, QString persistAsFileType="svo");
+ OctreePersistThread(OctreePointer tree, const QString& filename, const QString& backupDirectory,
+ int persistInterval = DEFAULT_PERSIST_INTERVAL, bool wantBackup = false,
+ const QJsonObject& settings = QJsonObject(), bool debugTimestampNow = false, QString persistAsFileType="svo");
bool isInitialLoadComplete() const { return _initialLoadComplete; }
quint64 getLoadElapsedTime() const { return _loadTimeUSecs; }
@@ -64,6 +64,7 @@ protected:
private:
OctreePointer _tree;
QString _filename;
+ QString _backupDirectory;
int _persistInterval;
bool _initialLoadComplete;
diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp
index 38d181e748..63082a8995 100644
--- a/libraries/render-utils/src/MeshPartPayload.cpp
+++ b/libraries/render-utils/src/MeshPartPayload.cpp
@@ -15,6 +15,7 @@
#include "DeferredLightingEffect.h"
#include "Model.h"
+#include "EntityItem.h"
using namespace render;
@@ -517,10 +518,16 @@ void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline:
}
void ModelMeshPartPayload::startFade() {
- _fadeStartTime = usecTimestampNow();
- _hasStartedFade = true;
- _prevHasStartedFade = false;
- _hasFinishedFade = false;
+ bool shouldFade = EntityItem::getEntitiesShouldFadeFunction()();
+ if (shouldFade) {
+ _fadeStartTime = usecTimestampNow();
+ _hasStartedFade = true;
+ _hasFinishedFade = false;
+ } else {
+ _isFading = true;
+ _hasStartedFade = true;
+ _hasFinishedFade = true;
+ }
}
void ModelMeshPartPayload::render(RenderArgs* args) const {
@@ -533,10 +540,11 @@ void ModelMeshPartPayload::render(RenderArgs* args) const {
// When an individual mesh parts like this finishes its fade, we will mark the Model as
// having render items that need updating
bool nextIsFading = _isFading ? isStillFading() : false;
- if (_isFading != nextIsFading || _prevHasStartedFade != _hasStartedFade) {
- _isFading = nextIsFading || _prevHasStartedFade != _hasStartedFade;
- _hasFinishedFade = _prevHasStartedFade == _hasStartedFade && !_isFading;
- _prevHasStartedFade = _hasStartedFade;
+ bool startFading = !_isFading && !_hasFinishedFade && _hasStartedFade;
+ bool endFading = _isFading && !nextIsFading;
+ if (startFading || endFading) {
+ _isFading = startFading;
+ _hasFinishedFade = endFading;
_model->setRenderItemsNeedUpdate();
}
diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h
index 67fb660f8b..29478b3b4e 100644
--- a/libraries/render-utils/src/MeshPartPayload.h
+++ b/libraries/render-utils/src/MeshPartPayload.h
@@ -110,7 +110,6 @@ public:
private:
quint64 _fadeStartTime { 0 };
bool _hasStartedFade { false };
- mutable bool _prevHasStartedFade{ false };
mutable bool _hasFinishedFade { false };
mutable bool _isFading { false };
};
diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp
index b5f360ad8d..e751427ce2 100644
--- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp
+++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp
@@ -108,19 +108,20 @@ public:
}
void updateSource() {
- Lock lock(_plugin._presentMutex);
- while (!_queue.empty()) {
- auto& front = _queue.front();
- auto result = glClientWaitSync(front.fence, 0, 0);
- if (GL_TIMEOUT_EXPIRED == result && GL_WAIT_FAILED == result) {
- break;
- }
+ _plugin.withNonPresentThreadLock([&] {
+ while (!_queue.empty()) {
+ auto& front = _queue.front();
+ auto result = glClientWaitSync(front.fence, 0, 0);
+ if (GL_TIMEOUT_EXPIRED == result && GL_WAIT_FAILED == result) {
+ break;
+ }
- glDeleteSync(front.fence);
- front.fence = 0;
- _current = front;
- _queue.pop();
- }
+ glDeleteSync(front.fence);
+ front.fence = 0;
+ _current = front;
+ _queue.pop();
+ }
+ });
}
void run() override {
@@ -170,15 +171,28 @@ public:
PoseData nextRender, nextSim;
nextRender.frameIndex = _plugin.presentCount();
vr::VRCompositor()->WaitGetPoses(nextRender.vrPoses, vr::k_unMaxTrackedDeviceCount, nextSim.vrPoses, vr::k_unMaxTrackedDeviceCount);
- {
- Lock lock(_plugin._presentMutex);
- _presentCount++;
- _presented.notify_one();
- _nextRender = nextRender;
- _nextRender.update(_plugin._sensorResetMat);
- _nextSim = nextSim;
- _nextSim.update(_plugin._sensorResetMat);
+
+ // Copy invalid poses in nextSim from nextRender
+ for (uint32_t i = 0; i < vr::k_unMaxTrackedDeviceCount; ++i) {
+ if (!nextSim.vrPoses[i].bPoseIsValid) {
+ nextSim.vrPoses[i] = nextRender.vrPoses[i];
+ }
}
+
+ mat4 sensorResetMat;
+ _plugin.withNonPresentThreadLock([&] {
+ sensorResetMat = _plugin._sensorResetMat;
+ });
+
+ nextRender.update(sensorResetMat);
+ nextSim.update(sensorResetMat);
+
+ _plugin.withNonPresentThreadLock([&] {
+ _nextRender = nextRender;
+ _nextSim = nextSim;
+ ++_presentCount;
+ _presented.notify_one();
+ });
}
_canvas.doneCurrent();
}
@@ -366,19 +380,20 @@ bool OpenVrDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
}
_currentRenderFrameInfo = FrameInfo();
+ PoseData nextSimPoseData;
withNonPresentThreadLock([&] {
- _currentRenderFrameInfo.renderPose = _nextSimPoseData.poses[vr::k_unTrackedDeviceIndex_Hmd];
+ nextSimPoseData = _nextSimPoseData;
});
// HACK: when interface is launched and steam vr is NOT running, openvr will return bad HMD poses for a few frames
// To workaround this, filter out any hmd poses that are obviously bad, i.e. beneath the floor.
- if (isBadPose(&_nextSimPoseData.vrPoses[vr::k_unTrackedDeviceIndex_Hmd].mDeviceToAbsoluteTracking)) {
+ if (isBadPose(&nextSimPoseData.vrPoses[vr::k_unTrackedDeviceIndex_Hmd].mDeviceToAbsoluteTracking)) {
qDebug() << "WARNING: ignoring bad hmd pose from openvr";
// use the last known good HMD pose
- _nextSimPoseData.vrPoses[vr::k_unTrackedDeviceIndex_Hmd].mDeviceToAbsoluteTracking = _lastGoodHMDPose;
+ nextSimPoseData.vrPoses[vr::k_unTrackedDeviceIndex_Hmd].mDeviceToAbsoluteTracking = _lastGoodHMDPose;
} else {
- _lastGoodHMDPose = _nextSimPoseData.vrPoses[vr::k_unTrackedDeviceIndex_Hmd].mDeviceToAbsoluteTracking;
+ _lastGoodHMDPose = nextSimPoseData.vrPoses[vr::k_unTrackedDeviceIndex_Hmd].mDeviceToAbsoluteTracking;
}
vr::TrackedDeviceIndex_t handIndices[2] { vr::k_unTrackedDeviceIndexInvalid, vr::k_unTrackedDeviceIndexInvalid };
@@ -387,7 +402,7 @@ bool OpenVrDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
auto trackedCount = _system->GetSortedTrackedDeviceIndicesOfClass(vr::TrackedDeviceClass_Controller, controllerIndices, 2);
// Find the left and right hand controllers, if they exist
for (uint32_t i = 0; i < std::min(trackedCount, 2); ++i) {
- if (_nextSimPoseData.vrPoses[i].bPoseIsValid) {
+ if (nextSimPoseData.vrPoses[i].bPoseIsValid) {
auto role = _system->GetControllerRoleForTrackedDeviceIndex(controllerIndices[i]);
if (vr::TrackedControllerRole_LeftHand == role) {
handIndices[0] = controllerIndices[i];
@@ -398,8 +413,7 @@ bool OpenVrDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
}
}
- _currentRenderFrameInfo.renderPose = _nextSimPoseData.poses[vr::k_unTrackedDeviceIndex_Hmd];
-
+ _currentRenderFrameInfo.renderPose = nextSimPoseData.poses[vr::k_unTrackedDeviceIndex_Hmd];
bool keyboardVisible = isOpenVrKeyboardShown();
std::array handPoses;
@@ -409,9 +423,9 @@ bool OpenVrDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
continue;
}
auto deviceIndex = handIndices[i];
- const mat4& mat = _nextSimPoseData.poses[deviceIndex];
- const vec3& linearVelocity = _nextSimPoseData.linearVelocities[deviceIndex];
- const vec3& angularVelocity = _nextSimPoseData.angularVelocities[deviceIndex];
+ const mat4& mat = nextSimPoseData.poses[deviceIndex];
+ const vec3& linearVelocity = nextSimPoseData.linearVelocities[deviceIndex];
+ const vec3& angularVelocity = nextSimPoseData.angularVelocities[deviceIndex];
auto correctedPose = openVrControllerPoseToHandPose(i == 0, mat, linearVelocity, angularVelocity);
static const glm::quat HAND_TO_LASER_ROTATION = glm::rotation(Vectors::UNIT_Z, Vectors::UNIT_NEG_Y);
handPoses[i] = glm::translate(glm::mat4(), correctedPose.translation) * glm::mat4_cast(correctedPose.rotation * HAND_TO_LASER_ROTATION);
diff --git a/plugins/openvr/src/OpenVrHelpers.h b/plugins/openvr/src/OpenVrHelpers.h
index 368b14cb1a..4279e6a6ac 100644
--- a/plugins/openvr/src/OpenVrHelpers.h
+++ b/plugins/openvr/src/OpenVrHelpers.h
@@ -66,8 +66,15 @@ struct PoseData {
vec3 linearVelocities[vr::k_unMaxTrackedDeviceCount];
vec3 angularVelocities[vr::k_unMaxTrackedDeviceCount];
+ PoseData() {
+ memset(vrPoses, 0, sizeof(vr::TrackedDevicePose_t) * vr::k_unMaxTrackedDeviceCount);
+ }
+
void update(const glm::mat4& resetMat) {
for (int i = 0; i < vr::k_unMaxTrackedDeviceCount; i++) {
+ if (!vrPoses[i].bPoseIsValid) {
+ continue;
+ }
poses[i] = resetMat * toGlm(vrPoses[i].mDeviceToAbsoluteTracking);
linearVelocities[i] = transformVectorFast(resetMat, toGlm(vrPoses[i].vVelocity));
angularVelocities[i] = transformVectorFast(resetMat, toGlm(vrPoses[i].vAngularVelocity));
diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js
index cf707c4d19..dc252afcf1 100644
--- a/scripts/defaultScripts.js
+++ b/scripts/defaultScripts.js
@@ -15,7 +15,6 @@ Script.load("system/users.js");
Script.load("system/mute.js");
Script.load("system/goto.js");
Script.load("system/hmd.js");
-Script.load("system/steam.js");
Script.load("system/marketplace.js");
Script.load("system/edit.js");
Script.load("system/mod.js");
@@ -26,5 +25,6 @@ Script.load("system/controllers/handControllerPointer.js");
Script.load("system/controllers/squeezeHands.js");
Script.load("system/controllers/grab.js");
Script.load("system/controllers/teleport.js");
+Script.load("system/controllers/toggleAdvancedMovementForHandControllers.js")
Script.load("system/dialTone.js");
-Script.load("system/firstPersonHMD.js");
+Script.load("system/firstPersonHMD.js");
\ No newline at end of file
diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js
index 5a1ae7e5ee..77d0109b9f 100644
--- a/scripts/system/controllers/teleport.js
+++ b/scripts/system/controllers/teleport.js
@@ -37,7 +37,9 @@ var COLORS_TELEPORT_TOO_CLOSE = {
blue: 73
};
-var TELEPORT_CANCEL_RANGE = 1.5;
+var TELEPORT_CANCEL_RANGE = 1;
+var USE_COOL_IN = true;
+var COOL_IN_DURATION = 500;
function ThumbPad(hand) {
this.hand = hand;
@@ -70,6 +72,8 @@ function Trigger(hand) {
};
}
+var coolInTimeout = null;
+
function Teleporter() {
var _this = this;
this.intersection = null;
@@ -81,6 +85,8 @@ function Teleporter() {
this.smoothArrivalInterval = null;
this.teleportHand = null;
this.tooClose = false;
+ this.inCoolIn = false;
+
this.initialize = function() {
this.createMappings();
@@ -99,6 +105,7 @@ function Teleporter() {
};
this.enterTeleportMode = function(hand) {
+
if (inTeleportMode === true) {
return;
}
@@ -107,6 +114,14 @@ function Teleporter() {
}
inTeleportMode = true;
+ this.inCoolIn = true;
+ if (coolInTimeout !== null) {
+ Script.clearTimeout(coolInTimeout);
+
+ }
+ coolInTimeout = Script.setTimeout(function() {
+ _this.inCoolIn = false;
+ }, COOL_IN_DURATION)
if (this.smoothArrivalInterval !== null) {
Script.clearInterval(this.smoothArrivalInterval);
@@ -119,6 +134,9 @@ function Teleporter() {
this.initialize();
Script.update.connect(this.update);
this.updateConnected = true;
+
+
+
};
this.createTargetOverlay = function() {
@@ -189,20 +207,19 @@ function Teleporter() {
if (this.updateConnected === true) {
Script.update.disconnect(this.update);
}
+
this.disableMappings();
this.turnOffOverlayBeams();
-
this.updateConnected = null;
+ this.inCoolIn = false;
+ inTeleportMode = false;
Script.setTimeout(function() {
- inTeleportMode = false;
_this.enableGrab();
- }, 100);
+ }, 200);
};
-
-
this.update = function() {
if (isDisabled === 'both') {
return;
@@ -214,7 +231,13 @@ function Teleporter() {
}
teleporter.leftRay();
if ((leftPad.buttonValue === 0) && inTeleportMode === true) {
- _this.teleport();
+ if (_this.inCoolIn === true) {
+ _this.exitTeleportMode();
+ _this.deleteTargetOverlay();
+ _this.deleteCancelOverlay();
+ } else {
+ _this.teleport();
+ }
return;
}
@@ -224,7 +247,13 @@ function Teleporter() {
}
teleporter.rightRay();
if ((rightPad.buttonValue === 0) && inTeleportMode === true) {
- _this.teleport();
+ if (_this.inCoolIn === true) {
+ _this.exitTeleportMode();
+ _this.deleteTargetOverlay();
+ _this.deleteCancelOverlay();
+ } else {
+ _this.teleport();
+ }
return;
}
}
@@ -235,7 +264,11 @@ function Teleporter() {
var pose = Controller.getPoseValue(Controller.Standard.RightHand);
var rightPosition = pose.valid ? Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, pose.translation), MyAvatar.position) : MyAvatar.getHeadPosition();
var rightRotation = pose.valid ? Quat.multiply(MyAvatar.orientation, pose.rotation) :
- Quat.multiply(MyAvatar.headOrientation, Quat.angleAxis(-90, {x: 1, y: 0, z: 0}));
+ Quat.multiply(MyAvatar.headOrientation, Quat.angleAxis(-90, {
+ x: 1,
+ y: 0,
+ z: 0
+ }));
var rightPickRay = {
origin: rightPosition,
@@ -260,15 +293,26 @@ function Teleporter() {
this.createCancelOverlay();
}
} else {
- this.deleteCancelOverlay();
-
- this.rightLineOn(rightPickRay.origin, rightIntersection.intersection, COLORS_TELEPORT_CAN_TELEPORT);
- if (this.targetOverlay !== null) {
- this.updateTargetOverlay(rightIntersection);
+ if (this.inCoolIn === true) {
+ this.deleteTargetOverlay();
+ this.rightLineOn(rightPickRay.origin, rightIntersection.intersection, COLORS_TELEPORT_TOO_CLOSE);
+ if (this.cancelOverlay !== null) {
+ this.updateCancelOverlay(rightIntersection);
+ } else {
+ this.createCancelOverlay();
+ }
} else {
- this.createTargetOverlay();
+ this.deleteCancelOverlay();
+
+ this.rightLineOn(rightPickRay.origin, rightIntersection.intersection, COLORS_TELEPORT_CAN_TELEPORT);
+ if (this.targetOverlay !== null) {
+ this.updateTargetOverlay(rightIntersection);
+ } else {
+ this.createTargetOverlay();
+ }
}
+
}
} else {
@@ -283,7 +327,11 @@ function Teleporter() {
var pose = Controller.getPoseValue(Controller.Standard.LeftHand);
var leftPosition = pose.valid ? Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, pose.translation), MyAvatar.position) : MyAvatar.getHeadPosition();
var leftRotation = pose.valid ? Quat.multiply(MyAvatar.orientation, pose.rotation) :
- Quat.multiply(MyAvatar.headOrientation, Quat.angleAxis(-90, {x: 1, y: 0, z: 0}));
+ Quat.multiply(MyAvatar.headOrientation, Quat.angleAxis(-90, {
+ x: 1,
+ y: 0,
+ z: 0
+ }));
var leftPickRay = {
origin: leftPosition,
@@ -308,15 +356,26 @@ function Teleporter() {
this.createCancelOverlay();
}
} else {
- this.deleteCancelOverlay();
- this.leftLineOn(leftPickRay.origin, leftIntersection.intersection, COLORS_TELEPORT_CAN_TELEPORT);
-
- if (this.targetOverlay !== null) {
- this.updateTargetOverlay(leftIntersection);
+ if (this.inCoolIn === true) {
+ this.deleteTargetOverlay();
+ this.leftLineOn(leftPickRay.origin, leftIntersection.intersection, COLORS_TELEPORT_TOO_CLOSE);
+ if (this.cancelOverlay !== null) {
+ this.updateCancelOverlay(leftIntersection);
+ } else {
+ this.createCancelOverlay();
+ }
} else {
- this.createTargetOverlay();
+ this.deleteCancelOverlay();
+ this.leftLineOn(leftPickRay.origin, leftIntersection.intersection, COLORS_TELEPORT_CAN_TELEPORT);
+
+ if (this.targetOverlay !== null) {
+ this.updateTargetOverlay(leftIntersection);
+ } else {
+ this.createTargetOverlay();
+ }
}
+
}
diff --git a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js
new file mode 100644
index 0000000000..3a75482770
--- /dev/null
+++ b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js
@@ -0,0 +1,141 @@
+// Created by james b. pollack @imgntn on 8/18/2016
+// Copyright 2016 High Fidelity, Inc.
+//
+//advanced movements settings are in individual controller json files
+//what we do is check the status of the 'advance movement' checkbox when you enter HMD mode
+//if 'advanced movement' is checked...we give you the defaults that are in the json.
+//if 'advanced movement' is not checked... we override the advanced controls with basic ones.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+
+var mappingName, basicMapping, isChecked;
+
+var TURN_RATE = 1000;
+var MENU_ITEM_NAME = "Advanced Movement For Hand Controllers";
+var SETTINGS_KEY = 'advancedMovementForHandControllersIsChecked';
+var previousSetting = Settings.getValue(SETTINGS_KEY);
+if (previousSetting === '' || previousSetting === false || previousSetting === 'false') {
+ previousSetting = false;
+ isChecked = false;
+}
+
+if (previousSetting === true || previousSetting === 'true') {
+ previousSetting = true;
+ isChecked = true;
+}
+
+function addAdvancedMovementItemToSettingsMenu() {
+ Menu.addMenuItem({
+ menuName: "Settings",
+ menuItemName: MENU_ITEM_NAME,
+ isCheckable: true,
+ isChecked: previousSetting
+ });
+
+}
+
+function rotate180() {
+ var newOrientation = Quat.multiply(MyAvatar.orientation, Quat.angleAxis(180, {
+ x: 0,
+ y: 1,
+ z: 0
+ }))
+ MyAvatar.orientation = newOrientation
+}
+
+var inFlipTurn = false;
+
+function registerBasicMapping() {
+ mappingName = 'Hifi-AdvancedMovement-Dev-' + Math.random();
+ basicMapping = Controller.newMapping(mappingName);
+ basicMapping.from(Controller.Standard.LY).to(function(value) {
+ var stick = Controller.getValue(Controller.Standard.LS);
+ if (value === 1 && Controller.Hardware.OculusTouch !== undefined) {
+ rotate180();
+ } else if (Controller.Hardware.Vive !== undefined) {
+ if (value > 0.75 && inFlipTurn === false) {
+ inFlipTurn = true;
+ rotate180();
+ Script.setTimeout(function() {
+ inFlipTurn = false;
+ }, TURN_RATE)
+ }
+ }
+ return;
+ });
+ basicMapping.from(Controller.Standard.LX).to(Controller.Standard.RX);
+ basicMapping.from(Controller.Standard.RY).to(function(value) {
+ var stick = Controller.getValue(Controller.Standard.RS);
+ if (value === 1 && Controller.Hardware.OculusTouch !== undefined) {
+ rotate180();
+ } else if (Controller.Hardware.Vive !== undefined) {
+ if (value > 0.75 && inFlipTurn === false) {
+ inFlipTurn = true;
+ rotate180();
+ Script.setTimeout(function() {
+ inFlipTurn = false;
+ }, TURN_RATE)
+ }
+ }
+ return;
+ })
+}
+
+
+function enableMappings() {
+ Controller.enableMapping(mappingName);
+}
+
+function disableMappings() {
+ Controller.disableMapping(mappingName);
+}
+
+function scriptEnding() {
+ Menu.removeMenuItem("Settings", MENU_ITEM_NAME);
+ disableMappings();
+}
+
+
+function menuItemEvent(menuItem) {
+ if (menuItem == MENU_ITEM_NAME) {
+ isChecked = Menu.isOptionChecked(MENU_ITEM_NAME);
+ if (isChecked === true) {
+ Settings.setValue(SETTINGS_KEY, true);
+ disableMappings();
+ } else if (isChecked === false) {
+ Settings.setValue(SETTINGS_KEY, false);
+ enableMappings();
+ }
+ }
+}
+
+addAdvancedMovementItemToSettingsMenu();
+
+Script.scriptEnding.connect(scriptEnding);
+
+Menu.menuItemEvent.connect(menuItemEvent);
+
+registerBasicMapping();
+
+Script.setTimeout(function() {
+ if (previousSetting === true) {
+ disableMappings();
+ } else {
+ enableMappings();
+ }
+}, 100)
+
+
+HMD.displayModeChanged.connect(function(isHMDMode) {
+ if (isHMDMode) {
+ if (Controller.Hardware.Vive !== undefined || Controller.Hardware.OculusTouch !== undefined) {
+ if (isChecked === true) {
+ disableMappings();
+ } else if (isChecked === false) {
+ enableMappings();
+ }
+
+ }
+ }
+});
\ No newline at end of file
diff --git a/scripts/system/steam.js b/unpublishedScripts/steam.js
similarity index 100%
rename from scripts/system/steam.js
rename to unpublishedScripts/steam.js