mirror of
https://github.com/lubosz/overte.git
synced 2025-04-27 06:35:32 +02:00
merge from upstream
This commit is contained in:
commit
9bcda1bb45
23 changed files with 455 additions and 98 deletions
assignment-client/src/octree
domain-server
interface
libraries
entities/src
gpu/src/gpu
octree/src
render-utils/src
plugins/openvr/src
scripts
unpublishedScripts
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -172,6 +172,7 @@ protected:
|
|||
|
||||
QString _persistFilePath;
|
||||
QString _persistAsFileType;
|
||||
QString _backupDirectoryPath;
|
||||
int _packetsPerClientPerInterval;
|
||||
int _packetsTotalPerInterval;
|
||||
OctreePointer _tree; // this IS a reaveraging tree
|
||||
|
|
|
@ -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.<br/>If this path is relative it will be relative to the application data directory.",
|
||||
"placeholder": "",
|
||||
"default": "",
|
||||
"advanced": true
|
||||
},
|
||||
{
|
||||
"name": "persistInterval",
|
||||
"label": "Save Check Interval",
|
||||
|
|
|
@ -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.";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 }
|
||||
]
|
||||
},
|
||||
|
|
|
@ -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" }
|
||||
]
|
||||
}
|
||||
}
|
|
@ -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<EntityScriptingInterface>();
|
||||
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<NodeList>()->soloNodeOfType(NodeType::EntityServer);
|
||||
return entityServerNode && !isPhysicsEnabled();
|
||||
});
|
||||
|
||||
// After all of the constructor is completed, then set firstRun to false.
|
||||
Setting::Handle<bool> firstRun{ Settings::firstRun, true };
|
||||
firstRun.set(false);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
|
||||
int EntityItem::_maxActionsDataSize = 800;
|
||||
quint64 EntityItem::_rememberDeletedActionTime = 20 * USECS_PER_SECOND;
|
||||
std::function<bool()> EntityItem::_entitiesShouldFadeFunction = [](){ return true; };
|
||||
|
||||
EntityItem::EntityItem(const EntityItemID& entityItemID) :
|
||||
SpatiallyNestable(NestableType::Entity, entityItemID),
|
||||
|
|
|
@ -432,6 +432,8 @@ public:
|
|||
QUuid getOwningAvatarID() const { return _owningAvatarID; }
|
||||
void setOwningAvatarID(const QUuid& owningAvatarID) { _owningAvatarID = owningAvatarID; }
|
||||
|
||||
static void setEntitiesShouldFadeFunction(std::function<bool()> func) { _entitiesShouldFadeFunction = func; }
|
||||
static std::function<bool()> 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<bool()> _entitiesShouldFadeFunction;
|
||||
bool _isFading { _entitiesShouldFadeFunction() };
|
||||
};
|
||||
|
||||
#endif // hifi_EntityItem_h
|
||||
|
|
|
@ -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<NodeList>();
|
||||
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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -293,10 +293,18 @@ public:
|
|||
template <typename T> Iterator<T> end() { return Iterator<T>(&edit<T>(getNum<T>()), _stride); }
|
||||
#else
|
||||
template <typename T> Iterator<const T> begin() const { return Iterator<const T>(&get<T>(), _stride); }
|
||||
template <typename T> Iterator<const T> end() const { return Iterator<const T>(&get<T>(getNum<T>()), _stride); }
|
||||
template <typename T> Iterator<const T> end() const {
|
||||
// reimplement get<T> without bounds checking
|
||||
Resource::Size elementOffset = getNum<T>() * _stride + _offset;
|
||||
return Iterator<const T>((reinterpret_cast<const T*> (_buffer->getData() + elementOffset)), _stride);
|
||||
}
|
||||
#endif
|
||||
template <typename T> Iterator<const T> cbegin() const { return Iterator<const T>(&get<T>(), _stride); }
|
||||
template <typename T> Iterator<const T> cend() const { return Iterator<const T>(&get<T>(getNum<T>()), _stride); }
|
||||
template <typename T> Iterator<const T> cend() const {
|
||||
// reimplement get<T> without bounds checking
|
||||
Resource::Size elementOffset = getNum<T>() * _stride + _offset;
|
||||
return Iterator<const T>((reinterpret_cast<const T*> (_buffer->getData() + elementOffset)), _stride);
|
||||
}
|
||||
|
||||
// the number of elements of the specified type fitting in the view size
|
||||
template <typename T> Index getNum() const {
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -110,7 +110,6 @@ public:
|
|||
private:
|
||||
quint64 _fadeStartTime { 0 };
|
||||
bool _hasStartedFade { false };
|
||||
mutable bool _prevHasStartedFade{ false };
|
||||
mutable bool _hasFinishedFade { false };
|
||||
mutable bool _isFading { false };
|
||||
};
|
||||
|
|
|
@ -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<uint32_t>(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<mat4, 2> 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);
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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");
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
});
|
Loading…
Reference in a new issue