Merge branch 'master' of github.com:highfidelity/hifi into 21976-implementScriptAPIFor_gpuTextureMemSizeStable
|
@ -52,6 +52,9 @@ else()
|
|||
set(MOBILE 0)
|
||||
endif()
|
||||
|
||||
# Use default time server if none defined in environment
|
||||
set_from_env(TIMESERVER_URL TIMESERVER_URL "http://sha256timestamp.ws.symantec.com/sha256/timestamp")
|
||||
|
||||
set(HIFI_USE_OPTIMIZED_IK OFF)
|
||||
set(BUILD_CLIENT_OPTION ON)
|
||||
set(BUILD_SERVER_OPTION ON)
|
||||
|
|
1008
CODING_STANDARD.md
Normal file
|
@ -16,7 +16,7 @@ Contributing
|
|||
git checkout -b new_branch_name
|
||||
```
|
||||
4. Code
|
||||
* Follow the [coding standard](https://docs.highfidelity.com/build-guide/coding-standards)
|
||||
* Follow the [coding standard](CODING_STANDARD.md)
|
||||
5. Commit
|
||||
* Use [well formed commit messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
|
||||
6. Update your branch
|
||||
|
|
|
@ -64,6 +64,10 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
|
|||
"UDP port for this assignment client (or monitor)", "port");
|
||||
parser.addOption(portOption);
|
||||
|
||||
const QCommandLineOption minChildListenPort(ASSIGNMENT_MONITOR_MIN_CHILDREN_LISTEN_PORT_OPTION,
|
||||
"Minimum UDP listen port", "port");
|
||||
parser.addOption(minChildListenPort);
|
||||
|
||||
const QCommandLineOption walletDestinationOption(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION,
|
||||
"set wallet destination", "wallet-uuid");
|
||||
parser.addOption(walletDestinationOption);
|
||||
|
@ -195,6 +199,11 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
|
|||
assignmentServerPort = parser.value(assignmentServerPortOption).toInt();
|
||||
}
|
||||
|
||||
quint16 childMinListenPort = 0;
|
||||
if (argumentVariantMap.contains(ASSIGNMENT_MONITOR_MIN_CHILDREN_LISTEN_PORT_OPTION)) {
|
||||
childMinListenPort = argumentVariantMap.value(ASSIGNMENT_MONITOR_MIN_CHILDREN_LISTEN_PORT_OPTION).toUInt();
|
||||
}
|
||||
|
||||
// check for an overidden listen port
|
||||
quint16 listenPort = 0;
|
||||
if (argumentVariantMap.contains(ASSIGNMENT_CLIENT_LISTEN_PORT_OPTION)) {
|
||||
|
@ -234,8 +243,8 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
|
|||
|
||||
if (numForks || minForks || maxForks) {
|
||||
AssignmentClientMonitor* monitor = new AssignmentClientMonitor(numForks, minForks, maxForks,
|
||||
requestAssignmentType, assignmentPool,
|
||||
listenPort, walletUUID, assignmentServerHostname,
|
||||
requestAssignmentType, assignmentPool, listenPort,
|
||||
childMinListenPort, walletUUID, assignmentServerHostname,
|
||||
assignmentServerPort, httpStatusPort, logDirectory);
|
||||
monitor->setParent(this);
|
||||
connect(this, &QCoreApplication::aboutToQuit, monitor, &AssignmentClientMonitor::aboutToQuit);
|
||||
|
|
|
@ -20,6 +20,7 @@ const QString ASSIGNMENT_POOL_OPTION = "pool";
|
|||
const QString ASSIGNMENT_CLIENT_LISTEN_PORT_OPTION = "p";
|
||||
const QString ASSIGNMENT_WALLET_DESTINATION_ID_OPTION = "wallet";
|
||||
const QString CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION = "a";
|
||||
const QString ASSIGNMENT_MONITOR_MIN_CHILDREN_LISTEN_PORT_OPTION = "min-listen-port";
|
||||
const QString CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION = "server-port";
|
||||
const QString ASSIGNMENT_NUM_FORKS_OPTION = "n";
|
||||
const QString ASSIGNMENT_MIN_FORKS_OPTION = "min";
|
||||
|
|
|
@ -40,7 +40,7 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen
|
|||
const unsigned int minAssignmentClientForks,
|
||||
const unsigned int maxAssignmentClientForks,
|
||||
Assignment::Type requestAssignmentType, QString assignmentPool,
|
||||
quint16 listenPort, QUuid walletUUID, QString assignmentServerHostname,
|
||||
quint16 listenPort, quint16 childMinListenPort, QUuid walletUUID, QString assignmentServerHostname,
|
||||
quint16 assignmentServerPort, quint16 httpStatusServerPort, QString logDirectory) :
|
||||
_httpManager(QHostAddress::LocalHost, httpStatusServerPort, "", this),
|
||||
_numAssignmentClientForks(numAssignmentClientForks),
|
||||
|
@ -50,8 +50,8 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen
|
|||
_assignmentPool(assignmentPool),
|
||||
_walletUUID(walletUUID),
|
||||
_assignmentServerHostname(assignmentServerHostname),
|
||||
_assignmentServerPort(assignmentServerPort)
|
||||
|
||||
_assignmentServerPort(assignmentServerPort),
|
||||
_childMinListenPort(childMinListenPort)
|
||||
{
|
||||
qDebug() << "_requestAssignmentType =" << _requestAssignmentType;
|
||||
|
||||
|
@ -100,8 +100,13 @@ void AssignmentClientMonitor::simultaneousWaitOnChildren(int waitMsecs) {
|
|||
}
|
||||
}
|
||||
|
||||
void AssignmentClientMonitor::childProcessFinished(qint64 pid, int exitCode, QProcess::ExitStatus exitStatus) {
|
||||
auto message = "Child process " + QString::number(pid) + " has %1 with exit code " + QString::number(exitCode) + ".";
|
||||
void AssignmentClientMonitor::childProcessFinished(qint64 pid, quint16 listenPort, int exitCode, QProcess::ExitStatus exitStatus) {
|
||||
auto message = "Child process " + QString::number(pid) + " on port " + QString::number(listenPort) +
|
||||
"has %1 with exit code " + QString::number(exitCode) + ".";
|
||||
|
||||
if (listenPort) {
|
||||
_childListenPorts.remove(listenPort);
|
||||
}
|
||||
|
||||
if (_childProcesses.remove(pid)) {
|
||||
message.append(" Removed from internal map.");
|
||||
|
@ -153,6 +158,23 @@ void AssignmentClientMonitor::aboutToQuit() {
|
|||
void AssignmentClientMonitor::spawnChildClient() {
|
||||
QProcess* assignmentClient = new QProcess(this);
|
||||
|
||||
quint16 listenPort = 0;
|
||||
// allocate a port
|
||||
|
||||
if (_childMinListenPort) {
|
||||
for (listenPort = _childMinListenPort; _childListenPorts.contains(listenPort); listenPort++) {
|
||||
if (_maxAssignmentClientForks &&
|
||||
(listenPort >= _maxAssignmentClientForks + _childMinListenPort)) {
|
||||
listenPort = 0;
|
||||
qDebug() << "Insufficient listen ports";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (listenPort) {
|
||||
_childListenPorts.insert(listenPort);
|
||||
}
|
||||
|
||||
// unparse the parts of the command-line that the child cares about
|
||||
QStringList _childArguments;
|
||||
if (_assignmentPool != "") {
|
||||
|
@ -176,6 +198,11 @@ void AssignmentClientMonitor::spawnChildClient() {
|
|||
_childArguments.append(QString::number(_requestAssignmentType));
|
||||
}
|
||||
|
||||
if (listenPort) {
|
||||
_childArguments.append("-" + ASSIGNMENT_CLIENT_LISTEN_PORT_OPTION);
|
||||
_childArguments.append(QString::number(listenPort));
|
||||
}
|
||||
|
||||
// tell children which assignment monitor port to use
|
||||
// for now they simply talk to us on localhost
|
||||
_childArguments.append("--" + ASSIGNMENT_CLIENT_MONITOR_PORT_OPTION);
|
||||
|
@ -247,8 +274,8 @@ void AssignmentClientMonitor::spawnChildClient() {
|
|||
auto pid = assignmentClient->processId();
|
||||
// make sure we hear that this process has finished when it does
|
||||
connect(assignmentClient, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
|
||||
this, [this, pid](int exitCode, QProcess::ExitStatus exitStatus) {
|
||||
childProcessFinished(pid, exitCode, exitStatus);
|
||||
this, [this, listenPort, pid](int exitCode, QProcess::ExitStatus exitStatus) {
|
||||
childProcessFinished(pid, listenPort, exitCode, exitStatus);
|
||||
});
|
||||
|
||||
qDebug() << "Spawned a child client with PID" << assignmentClient->processId();
|
||||
|
|
|
@ -37,14 +37,15 @@ class AssignmentClientMonitor : public QObject, public HTTPRequestHandler {
|
|||
public:
|
||||
AssignmentClientMonitor(const unsigned int numAssignmentClientForks, const unsigned int minAssignmentClientForks,
|
||||
const unsigned int maxAssignmentClientForks, Assignment::Type requestAssignmentType,
|
||||
QString assignmentPool, quint16 listenPort, QUuid walletUUID, QString assignmentServerHostname,
|
||||
quint16 assignmentServerPort, quint16 httpStatusServerPort, QString logDirectory);
|
||||
QString assignmentPool, quint16 listenPort, quint16 childMinListenPort, QUuid walletUUID,
|
||||
QString assignmentServerHostname, quint16 assignmentServerPort, quint16 httpStatusServerPort,
|
||||
QString logDirectory);
|
||||
~AssignmentClientMonitor();
|
||||
|
||||
void stopChildProcesses();
|
||||
private slots:
|
||||
void checkSpares();
|
||||
void childProcessFinished(qint64 pid, int exitCode, QProcess::ExitStatus exitStatus);
|
||||
void childProcessFinished(qint64 pid, quint16 port, int exitCode, QProcess::ExitStatus exitStatus);
|
||||
void handleChildStatusPacket(QSharedPointer<ReceivedMessage> message);
|
||||
|
||||
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false) override;
|
||||
|
@ -75,6 +76,9 @@ private:
|
|||
|
||||
QMap<qint64, ACProcess> _childProcesses;
|
||||
|
||||
quint16 _childMinListenPort;
|
||||
QSet<quint16> _childListenPorts;
|
||||
|
||||
bool _wantsChildFileLogging { false };
|
||||
};
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
#include <NodeType.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <PathUtils.h>
|
||||
#include <image/Image.h>
|
||||
#include <image/TextureProcessing.h>
|
||||
|
||||
#include "AssetServerLogging.h"
|
||||
#include "BakeAssetTask.h"
|
||||
|
@ -107,6 +107,10 @@ BakeVersion currentBakeVersionForAssetType(BakedAssetType type) {
|
|||
}
|
||||
}
|
||||
|
||||
QString getBakeMapping(const AssetUtils::AssetHash& hash, const QString& relativeFilePath) {
|
||||
return AssetUtils::HIDDEN_BAKED_CONTENT_FOLDER + hash + "/" + relativeFilePath;
|
||||
}
|
||||
|
||||
const QString ASSET_SERVER_LOGGING_TARGET_NAME = "asset-server";
|
||||
|
||||
void AssetServer::bakeAsset(const AssetUtils::AssetHash& assetHash, const AssetUtils::AssetPath& assetPath, const QString& filePath) {
|
||||
|
@ -141,26 +145,27 @@ std::pair<AssetUtils::BakingStatus, QString> AssetServer::getAssetStatus(const A
|
|||
return { AssetUtils::Baked, "" };
|
||||
}
|
||||
|
||||
auto dotIndex = path.lastIndexOf(".");
|
||||
if (dotIndex == -1) {
|
||||
BakedAssetType type = assetTypeForFilename(path);
|
||||
if (type == BakedAssetType::Undefined) {
|
||||
return { AssetUtils::Irrelevant, "" };
|
||||
}
|
||||
|
||||
auto extension = path.mid(dotIndex + 1);
|
||||
bool loaded;
|
||||
AssetMeta meta;
|
||||
std::tie(loaded, meta) = readMetaFile(hash);
|
||||
|
||||
QString bakedFilename;
|
||||
|
||||
if (BAKEABLE_MODEL_EXTENSIONS.contains(extension)) {
|
||||
bakedFilename = BAKED_MODEL_SIMPLE_NAME;
|
||||
} else if (BAKEABLE_TEXTURE_EXTENSIONS.contains(extension.toLocal8Bit()) && hasMetaFile(hash)) {
|
||||
bakedFilename = BAKED_TEXTURE_SIMPLE_NAME;
|
||||
} else if (BAKEABLE_SCRIPT_EXTENSIONS.contains(extension)) {
|
||||
bakedFilename = BAKED_SCRIPT_SIMPLE_NAME;
|
||||
} else {
|
||||
// We create a meta file for Skyboxes at runtime when they get requested
|
||||
// Otherwise, textures don't get baked by themselves.
|
||||
if (type == BakedAssetType::Texture && !loaded) {
|
||||
return { AssetUtils::Irrelevant, "" };
|
||||
}
|
||||
|
||||
auto bakedPath = AssetUtils::HIDDEN_BAKED_CONTENT_FOLDER + hash + "/" + bakedFilename;
|
||||
QString bakedFilename = bakedFilenameForAssetType(type);
|
||||
auto bakedPath = getBakeMapping(hash, bakedFilename);
|
||||
if (loaded && !meta.redirectTarget.isEmpty()) {
|
||||
bakedPath = meta.redirectTarget;
|
||||
}
|
||||
|
||||
auto jt = _fileMappings.find(bakedPath);
|
||||
if (jt != _fileMappings.end()) {
|
||||
if (jt->second == hash) {
|
||||
|
@ -168,14 +173,8 @@ std::pair<AssetUtils::BakingStatus, QString> AssetServer::getAssetStatus(const A
|
|||
} else {
|
||||
return { AssetUtils::Baked, "" };
|
||||
}
|
||||
} else {
|
||||
bool loaded;
|
||||
AssetMeta meta;
|
||||
|
||||
std::tie(loaded, meta) = readMetaFile(hash);
|
||||
if (loaded && meta.failedLastBake) {
|
||||
return { AssetUtils::Error, meta.lastBakeErrors };
|
||||
}
|
||||
} else if (loaded && meta.failedLastBake) {
|
||||
return { AssetUtils::Error, meta.lastBakeErrors };
|
||||
}
|
||||
|
||||
return { AssetUtils::Pending, "" };
|
||||
|
@ -227,8 +226,16 @@ bool AssetServer::needsToBeBaked(const AssetUtils::AssetPath& path, const AssetU
|
|||
return false;
|
||||
}
|
||||
|
||||
bool loaded;
|
||||
AssetMeta meta;
|
||||
std::tie(loaded, meta) = readMetaFile(assetHash);
|
||||
|
||||
QString bakedFilename = bakedFilenameForAssetType(type);
|
||||
auto bakedPath = AssetUtils::HIDDEN_BAKED_CONTENT_FOLDER + assetHash + "/" + bakedFilename;
|
||||
auto bakedPath = getBakeMapping(assetHash, bakedFilename);
|
||||
if (loaded && !meta.redirectTarget.isEmpty()) {
|
||||
bakedPath = meta.redirectTarget;
|
||||
}
|
||||
|
||||
auto mappingIt = _fileMappings.find(bakedPath);
|
||||
bool bakedMappingExists = mappingIt != _fileMappings.end();
|
||||
|
||||
|
@ -238,10 +245,8 @@ bool AssetServer::needsToBeBaked(const AssetUtils::AssetPath& path, const AssetU
|
|||
return false;
|
||||
}
|
||||
|
||||
bool loaded;
|
||||
AssetMeta meta;
|
||||
std::tie(loaded, meta) = readMetaFile(assetHash);
|
||||
|
||||
// We create a meta file for Skyboxes at runtime when they get requested
|
||||
// Otherwise, textures don't get baked by themselves.
|
||||
if (type == BakedAssetType::Texture && !loaded) {
|
||||
return false;
|
||||
}
|
||||
|
@ -633,36 +638,33 @@ void AssetServer::handleGetMappingOperation(ReceivedMessage& message, NLPacketLi
|
|||
if (it != _fileMappings.end()) {
|
||||
|
||||
// check if we should re-direct to a baked asset
|
||||
|
||||
// first, figure out from the mapping extension what type of file this is
|
||||
auto assetPathExtension = assetPath.mid(assetPath.lastIndexOf('.') + 1).toLower();
|
||||
|
||||
auto type = assetTypeForFilename(assetPath);
|
||||
QString bakedRootFile = bakedFilenameForAssetType(type);
|
||||
|
||||
auto originalAssetHash = it->second;
|
||||
QString redirectedAssetHash;
|
||||
QString bakedAssetPath;
|
||||
quint8 wasRedirected = false;
|
||||
bool bakingDisabled = false;
|
||||
|
||||
if (!bakedRootFile.isEmpty()) {
|
||||
// we ran into an asset for which we could have a baked version, let's check if it's ready
|
||||
bakedAssetPath = AssetUtils::HIDDEN_BAKED_CONTENT_FOLDER + originalAssetHash + "/" + bakedRootFile;
|
||||
auto bakedIt = _fileMappings.find(bakedAssetPath);
|
||||
bool loaded;
|
||||
AssetMeta meta;
|
||||
std::tie(loaded, meta) = readMetaFile(originalAssetHash);
|
||||
|
||||
if (bakedIt != _fileMappings.end()) {
|
||||
if (bakedIt->second != originalAssetHash) {
|
||||
qDebug() << "Did find baked version for: " << originalAssetHash << assetPath;
|
||||
// we found a baked version of the requested asset to serve, redirect to that
|
||||
redirectedAssetHash = bakedIt->second;
|
||||
wasRedirected = true;
|
||||
} else {
|
||||
qDebug() << "Did not find baked version for: " << originalAssetHash << assetPath << " (disabled)";
|
||||
bakingDisabled = true;
|
||||
}
|
||||
auto type = assetTypeForFilename(assetPath);
|
||||
QString bakedRootFile = bakedFilenameForAssetType(type);
|
||||
QString bakedAssetPath = getBakeMapping(originalAssetHash, bakedRootFile);
|
||||
|
||||
if (loaded && !meta.redirectTarget.isEmpty()) {
|
||||
bakedAssetPath = meta.redirectTarget;
|
||||
}
|
||||
|
||||
auto bakedIt = _fileMappings.find(bakedAssetPath);
|
||||
if (bakedIt != _fileMappings.end()) {
|
||||
if (bakedIt->second != originalAssetHash) {
|
||||
qDebug() << "Did find baked version for: " << originalAssetHash << assetPath;
|
||||
// we found a baked version of the requested asset to serve, redirect to that
|
||||
redirectedAssetHash = bakedIt->second;
|
||||
wasRedirected = true;
|
||||
} else {
|
||||
qDebug() << "Did not find baked version for: " << originalAssetHash << assetPath;
|
||||
qDebug() << "Did not find baked version for: " << originalAssetHash << assetPath << " (disabled)";
|
||||
bakingDisabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -684,20 +686,13 @@ void AssetServer::handleGetMappingOperation(ReceivedMessage& message, NLPacketLi
|
|||
|
||||
auto query = QUrlQuery(url.query());
|
||||
bool isSkybox = query.hasQueryItem("skybox");
|
||||
if (isSkybox) {
|
||||
bool loaded;
|
||||
AssetMeta meta;
|
||||
std::tie(loaded, meta) = readMetaFile(originalAssetHash);
|
||||
|
||||
if (!loaded) {
|
||||
AssetMeta needsBakingMeta;
|
||||
needsBakingMeta.bakeVersion = NEEDS_BAKING_BAKE_VERSION;
|
||||
|
||||
writeMetaFile(originalAssetHash, needsBakingMeta);
|
||||
if (!bakingDisabled) {
|
||||
maybeBake(assetPath, originalAssetHash);
|
||||
}
|
||||
if (isSkybox && !loaded) {
|
||||
AssetMeta needsBakingMeta;
|
||||
needsBakingMeta.bakeVersion = NEEDS_BAKING_BAKE_VERSION;
|
||||
|
||||
writeMetaFile(originalAssetHash, needsBakingMeta);
|
||||
if (!bakingDisabled) {
|
||||
maybeBake(assetPath, originalAssetHash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1297,14 +1292,6 @@ bool AssetServer::renameMapping(AssetUtils::AssetPath oldPath, AssetUtils::Asset
|
|||
}
|
||||
}
|
||||
|
||||
static const QString BAKED_ASSET_SIMPLE_FBX_NAME = "asset.fbx";
|
||||
static const QString BAKED_ASSET_SIMPLE_TEXTURE_NAME = "texture.ktx";
|
||||
static const QString BAKED_ASSET_SIMPLE_JS_NAME = "asset.js";
|
||||
|
||||
QString getBakeMapping(const AssetUtils::AssetHash& hash, const QString& relativeFilePath) {
|
||||
return AssetUtils::HIDDEN_BAKED_CONTENT_FOLDER + hash + "/" + relativeFilePath;
|
||||
}
|
||||
|
||||
void AssetServer::handleFailedBake(QString originalAssetHash, QString assetPath, QString errors) {
|
||||
qDebug() << "Failed to bake: " << originalAssetHash << assetPath << "(" << errors << ")";
|
||||
|
||||
|
@ -1326,12 +1313,78 @@ void AssetServer::handleFailedBake(QString originalAssetHash, QString assetPath,
|
|||
}
|
||||
|
||||
void AssetServer::handleCompletedBake(QString originalAssetHash, QString originalAssetPath,
|
||||
QString bakedTempOutputDir, QVector<QString> bakedFilePaths) {
|
||||
QString bakedTempOutputDir) {
|
||||
auto reportCompletion = [this, originalAssetPath, originalAssetHash](bool errorCompletingBake,
|
||||
QString errorReason,
|
||||
QString redirectTarget) {
|
||||
auto type = assetTypeForFilename(originalAssetPath);
|
||||
auto currentTypeVersion = currentBakeVersionForAssetType(type);
|
||||
|
||||
AssetMeta meta;
|
||||
meta.bakeVersion = currentTypeVersion;
|
||||
meta.failedLastBake = errorCompletingBake;
|
||||
meta.redirectTarget = redirectTarget;
|
||||
|
||||
if (errorCompletingBake) {
|
||||
qWarning() << "Could not complete bake for" << originalAssetHash;
|
||||
meta.lastBakeErrors = errorReason;
|
||||
}
|
||||
|
||||
writeMetaFile(originalAssetHash, meta);
|
||||
|
||||
_pendingBakes.remove(originalAssetHash);
|
||||
};
|
||||
|
||||
bool errorCompletingBake { false };
|
||||
QString errorReason;
|
||||
QString redirectTarget;
|
||||
|
||||
qDebug() << "Completing bake for " << originalAssetHash;
|
||||
|
||||
// Find the directory containing the baked content
|
||||
QDir outputDir(bakedTempOutputDir);
|
||||
QString outputDirName = outputDir.dirName();
|
||||
auto directories = outputDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
QString bakedDirectoryPath;
|
||||
for (const auto& dirName : directories) {
|
||||
outputDir.cd(dirName);
|
||||
if (outputDir.exists("baked") && outputDir.exists("original")) {
|
||||
bakedDirectoryPath = outputDir.filePath("baked");
|
||||
break;
|
||||
}
|
||||
outputDir.cdUp();
|
||||
}
|
||||
if (bakedDirectoryPath.isEmpty()) {
|
||||
errorCompletingBake = true;
|
||||
errorReason = "Failed to find baking output";
|
||||
|
||||
// Cleanup temporary output directory
|
||||
PathUtils::deleteMyTemporaryDir(outputDirName);
|
||||
reportCompletion(errorCompletingBake, errorReason, redirectTarget);
|
||||
return;
|
||||
}
|
||||
|
||||
// Compile list of all the baked files
|
||||
QDirIterator it(bakedDirectoryPath, QDirIterator::Subdirectories);
|
||||
QVector<QString> bakedFilePaths;
|
||||
while (it.hasNext()) {
|
||||
it.next();
|
||||
if (it.fileInfo().isFile()) {
|
||||
bakedFilePaths.push_back(it.filePath());
|
||||
}
|
||||
}
|
||||
if (bakedFilePaths.isEmpty()) {
|
||||
errorCompletingBake = true;
|
||||
errorReason = "Baking output has no files";
|
||||
|
||||
// Cleanup temporary output directory
|
||||
PathUtils::deleteMyTemporaryDir(outputDirName);
|
||||
reportCompletion(errorCompletingBake, errorReason, redirectTarget);
|
||||
return;
|
||||
}
|
||||
|
||||
QDir bakedDirectory(bakedDirectoryPath);
|
||||
|
||||
for (auto& filePath : bakedFilePaths) {
|
||||
// figure out the hash for the contents of this file
|
||||
QFile file(filePath);
|
||||
|
@ -1340,89 +1393,72 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina
|
|||
|
||||
AssetUtils::AssetHash bakedFileHash;
|
||||
|
||||
if (file.open(QIODevice::ReadOnly)) {
|
||||
QCryptographicHash hasher(QCryptographicHash::Sha256);
|
||||
|
||||
if (hasher.addData(&file)) {
|
||||
bakedFileHash = hasher.result().toHex();
|
||||
} else {
|
||||
// stop handling this bake, couldn't hash the contents of the file
|
||||
errorCompletingBake = true;
|
||||
errorReason = "Failed to finalize bake";
|
||||
break;
|
||||
}
|
||||
|
||||
// first check that we don't already have this bake file in our list
|
||||
auto bakeFileDestination = _filesDirectory.absoluteFilePath(bakedFileHash);
|
||||
if (!QFile::exists(bakeFileDestination)) {
|
||||
// copy each to our files folder (with the hash as their filename)
|
||||
if (!file.copy(_filesDirectory.absoluteFilePath(bakedFileHash))) {
|
||||
// stop handling this bake, couldn't copy the bake file into our files directory
|
||||
errorCompletingBake = true;
|
||||
errorReason = "Failed to copy baked assets to asset server";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// setup the mapping for this bake file
|
||||
auto relativeFilePath = QUrl(filePath).fileName();
|
||||
qDebug() << "Relative file path is: " << relativeFilePath;
|
||||
if (relativeFilePath.endsWith(".fbx", Qt::CaseInsensitive)) {
|
||||
// for an FBX file, we replace the filename with the simple name
|
||||
// (to handle the case where two mapped assets have the same hash but different names)
|
||||
relativeFilePath = BAKED_ASSET_SIMPLE_FBX_NAME;
|
||||
} else if (relativeFilePath.endsWith(".js", Qt::CaseInsensitive)) {
|
||||
relativeFilePath = BAKED_ASSET_SIMPLE_JS_NAME;
|
||||
} else if (!originalAssetPath.endsWith(".fbx", Qt::CaseInsensitive)) {
|
||||
relativeFilePath = BAKED_ASSET_SIMPLE_TEXTURE_NAME;
|
||||
}
|
||||
|
||||
QString bakeMapping = getBakeMapping(originalAssetHash, relativeFilePath);
|
||||
|
||||
// add a mapping (under the hidden baked folder) for this file resulting from the bake
|
||||
if (setMapping(bakeMapping, bakedFileHash)) {
|
||||
qDebug() << "Added" << bakeMapping << "for bake file" << bakedFileHash << "from bake of" << originalAssetHash;
|
||||
} else {
|
||||
qDebug() << "Failed to set mapping";
|
||||
// stop handling this bake, couldn't add a mapping for this bake file
|
||||
errorCompletingBake = true;
|
||||
errorReason = "Failed to finalize bake";
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
qDebug() << "Failed to open baked file: " << filePath;
|
||||
// stop handling this bake, we couldn't open one of the files for reading
|
||||
errorCompletingBake = true;
|
||||
errorReason = "Failed to finalize bake";
|
||||
errorReason = "Could not open baked file " + file.fileName();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& filePath : bakedFilePaths) {
|
||||
QFile file(filePath);
|
||||
if (!file.remove()) {
|
||||
qWarning() << "Failed to remove temporary file:" << filePath;
|
||||
QCryptographicHash hasher(QCryptographicHash::Sha256);
|
||||
|
||||
if (!hasher.addData(&file)) {
|
||||
// stop handling this bake, couldn't hash the contents of the file
|
||||
errorCompletingBake = true;
|
||||
errorReason = "Could not hash data for " + file.fileName();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!QDir(bakedTempOutputDir).rmdir(".")) {
|
||||
qWarning() << "Failed to remove temporary directory:" << bakedTempOutputDir;
|
||||
|
||||
bakedFileHash = hasher.result().toHex();
|
||||
|
||||
// first check that we don't already have this bake file in our list
|
||||
auto bakeFileDestination = _filesDirectory.absoluteFilePath(bakedFileHash);
|
||||
if (!QFile::exists(bakeFileDestination)) {
|
||||
// copy each to our files folder (with the hash as their filename)
|
||||
if (!file.copy(_filesDirectory.absoluteFilePath(bakedFileHash))) {
|
||||
// stop handling this bake, couldn't copy the bake file into our files directory
|
||||
errorCompletingBake = true;
|
||||
errorReason = "Failed to copy baked assets to asset server";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// setup the mapping for this bake file
|
||||
auto relativeFilePath = bakedDirectory.relativeFilePath(filePath);
|
||||
|
||||
QString bakeMapping = getBakeMapping(originalAssetHash, relativeFilePath);
|
||||
|
||||
// Check if this is the file we should redirect to when someone asks for the original asset
|
||||
if ((relativeFilePath.endsWith(".baked.fst", Qt::CaseInsensitive) && originalAssetPath.endsWith(".fbx")) ||
|
||||
(relativeFilePath.endsWith(".texmeta.json", Qt::CaseInsensitive) && !originalAssetPath.endsWith(".fbx"))) {
|
||||
if (!redirectTarget.isEmpty()) {
|
||||
qWarning() << "Found multiple baked redirect target for" << originalAssetPath;
|
||||
}
|
||||
redirectTarget = bakeMapping;
|
||||
}
|
||||
|
||||
// add a mapping (under the hidden baked folder) for this file resulting from the bake
|
||||
if (!setMapping(bakeMapping, bakedFileHash)) {
|
||||
qDebug() << "Failed to set mapping";
|
||||
// stop handling this bake, couldn't add a mapping for this bake file
|
||||
errorCompletingBake = true;
|
||||
errorReason = "Failed to set mapping for baked file " + file.fileName();
|
||||
break;
|
||||
}
|
||||
|
||||
qDebug() << "Added" << bakeMapping << "for bake file" << bakedFileHash << "from bake of" << originalAssetHash;
|
||||
}
|
||||
|
||||
auto type = assetTypeForFilename(originalAssetPath);
|
||||
auto currentTypeVersion = currentBakeVersionForAssetType(type);
|
||||
|
||||
AssetMeta meta;
|
||||
meta.bakeVersion = currentTypeVersion;
|
||||
meta.failedLastBake = errorCompletingBake;
|
||||
|
||||
if (errorCompletingBake) {
|
||||
qWarning() << "Could not complete bake for" << originalAssetHash;
|
||||
meta.lastBakeErrors = errorReason;
|
||||
if (redirectTarget.isEmpty()) {
|
||||
errorCompletingBake = true;
|
||||
errorReason = "Could not find root file for baked output";
|
||||
}
|
||||
|
||||
writeMetaFile(originalAssetHash, meta);
|
||||
|
||||
_pendingBakes.remove(originalAssetHash);
|
||||
// Cleanup temporary output directory
|
||||
PathUtils::deleteMyTemporaryDir(outputDirName);
|
||||
reportCompletion(errorCompletingBake, errorReason, redirectTarget);
|
||||
}
|
||||
|
||||
void AssetServer::handleAbortedBake(QString originalAssetHash, QString assetPath) {
|
||||
|
@ -1435,6 +1471,7 @@ void AssetServer::handleAbortedBake(QString originalAssetHash, QString assetPath
|
|||
static const QString BAKE_VERSION_KEY = "bake_version";
|
||||
static const QString FAILED_LAST_BAKE_KEY = "failed_last_bake";
|
||||
static const QString LAST_BAKE_ERRORS_KEY = "last_bake_errors";
|
||||
static const QString REDIRECT_TARGET_KEY = "redirect_target";
|
||||
|
||||
std::pair<bool, AssetMeta> AssetServer::readMetaFile(AssetUtils::AssetHash hash) {
|
||||
auto metaFilePath = AssetUtils::HIDDEN_BAKED_CONTENT_FOLDER + hash + "/" + "meta.json";
|
||||
|
@ -1461,6 +1498,7 @@ std::pair<bool, AssetMeta> AssetServer::readMetaFile(AssetUtils::AssetHash hash)
|
|||
auto bakeVersion = root[BAKE_VERSION_KEY];
|
||||
auto failedLastBake = root[FAILED_LAST_BAKE_KEY];
|
||||
auto lastBakeErrors = root[LAST_BAKE_ERRORS_KEY];
|
||||
auto redirectTarget = root[REDIRECT_TARGET_KEY];
|
||||
|
||||
if (bakeVersion.isDouble()
|
||||
&& failedLastBake.isBool()
|
||||
|
@ -1470,6 +1508,7 @@ std::pair<bool, AssetMeta> AssetServer::readMetaFile(AssetUtils::AssetHash hash)
|
|||
meta.bakeVersion = bakeVersion.toInt();
|
||||
meta.failedLastBake = failedLastBake.toBool();
|
||||
meta.lastBakeErrors = lastBakeErrors.toString();
|
||||
meta.redirectTarget = redirectTarget.toString();
|
||||
|
||||
return { true, meta };
|
||||
} else {
|
||||
|
@ -1488,6 +1527,7 @@ bool AssetServer::writeMetaFile(AssetUtils::AssetHash originalAssetHash, const A
|
|||
metaFileObject[BAKE_VERSION_KEY] = (int)meta.bakeVersion;
|
||||
metaFileObject[FAILED_LAST_BAKE_KEY] = meta.failedLastBake;
|
||||
metaFileObject[LAST_BAKE_ERRORS_KEY] = meta.lastBakeErrors;
|
||||
metaFileObject[REDIRECT_TARGET_KEY] = meta.redirectTarget;
|
||||
|
||||
QJsonDocument metaFileDoc;
|
||||
metaFileDoc.setObject(metaFileObject);
|
||||
|
@ -1521,10 +1561,18 @@ bool AssetServer::setBakingEnabled(const AssetUtils::AssetPathList& paths, bool
|
|||
if (type == BakedAssetType::Undefined) {
|
||||
continue;
|
||||
}
|
||||
QString bakedFilename = bakedFilenameForAssetType(type);
|
||||
|
||||
auto hash = it->second;
|
||||
|
||||
bool loaded;
|
||||
AssetMeta meta;
|
||||
std::tie(loaded, meta) = readMetaFile(hash);
|
||||
|
||||
QString bakedFilename = bakedFilenameForAssetType(type);
|
||||
auto bakedMapping = getBakeMapping(hash, bakedFilename);
|
||||
if (loaded && !meta.redirectTarget.isEmpty()) {
|
||||
bakedMapping = meta.redirectTarget;
|
||||
}
|
||||
|
||||
auto it = _fileMappings.find(bakedMapping);
|
||||
bool currentlyDisabled = (it != _fileMappings.end() && it->second == hash);
|
||||
|
|
|
@ -62,12 +62,10 @@ enum class ScriptBakeVersion : BakeVersion {
|
|||
};
|
||||
|
||||
struct AssetMeta {
|
||||
AssetMeta() {
|
||||
}
|
||||
|
||||
BakeVersion bakeVersion { INITIAL_BAKE_VERSION };
|
||||
bool failedLastBake { false };
|
||||
QString lastBakeErrors;
|
||||
QString redirectTarget;
|
||||
};
|
||||
|
||||
class BakeAssetTask;
|
||||
|
@ -139,8 +137,7 @@ private:
|
|||
void bakeAsset(const AssetUtils::AssetHash& assetHash, const AssetUtils::AssetPath& assetPath, const QString& filePath);
|
||||
|
||||
/// Move baked content for asset to baked directory and update baked status
|
||||
void handleCompletedBake(QString originalAssetHash, QString assetPath, QString bakedTempOutputDir,
|
||||
QVector<QString> bakedFilePaths);
|
||||
void handleCompletedBake(QString originalAssetHash, QString assetPath, QString bakedTempOutputDir);
|
||||
void handleFailedBake(QString originalAssetHash, QString assetPath, QString errors);
|
||||
void handleAbortedBake(QString originalAssetHash, QString assetPath);
|
||||
|
||||
|
|
|
@ -36,33 +36,38 @@ BakeAssetTask::BakeAssetTask(const AssetUtils::AssetHash& assetHash, const Asset
|
|||
});
|
||||
}
|
||||
|
||||
void cleanupTempFiles(QString tempOutputDir, std::vector<QString> files) {
|
||||
for (const auto& filename : files) {
|
||||
QFile f { filename };
|
||||
if (!f.remove()) {
|
||||
qDebug() << "Failed to remove:" << filename;
|
||||
}
|
||||
}
|
||||
if (!tempOutputDir.isEmpty()) {
|
||||
QDir dir { tempOutputDir };
|
||||
if (!dir.rmdir(".")) {
|
||||
qDebug() << "Failed to remove temporary directory:" << tempOutputDir;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void BakeAssetTask::run() {
|
||||
if (_isBaking.exchange(true)) {
|
||||
qWarning() << "Tried to start bake asset task while already baking";
|
||||
return;
|
||||
}
|
||||
|
||||
// Make a new temporary directory for the Oven to work in
|
||||
QString tempOutputDir = PathUtils::generateTemporaryDir();
|
||||
QString tempOutputDirName = QDir(tempOutputDir).dirName();
|
||||
if (tempOutputDir.isEmpty()) {
|
||||
QString errors = "Could not create temporary working directory";
|
||||
emit bakeFailed(_assetHash, _assetPath, errors);
|
||||
PathUtils::deleteMyTemporaryDir(tempOutputDirName);
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy file to bake the temporary dir and give a name the oven can work with
|
||||
auto assetName = _assetPath.split("/").last();
|
||||
auto tempAssetPath = tempOutputDir + "/" + assetName;
|
||||
auto success = QFile::copy(_filePath, tempAssetPath);
|
||||
if (!success) {
|
||||
QString errors = "Couldn't copy file to bake to temporary directory";
|
||||
emit bakeFailed(_assetHash, _assetPath, errors);
|
||||
PathUtils::deleteMyTemporaryDir(tempOutputDirName);
|
||||
return;
|
||||
}
|
||||
|
||||
auto base = QFileInfo(QCoreApplication::applicationFilePath()).absoluteDir();
|
||||
QString path = base.absolutePath() + "/oven";
|
||||
QString extension = _assetPath.mid(_assetPath.lastIndexOf('.') + 1);
|
||||
QStringList args {
|
||||
"-i", _filePath,
|
||||
"-i", tempAssetPath,
|
||||
"-o", tempOutputDir,
|
||||
"-t", extension,
|
||||
};
|
||||
|
@ -72,10 +77,11 @@ void BakeAssetTask::run() {
|
|||
QEventLoop loop;
|
||||
|
||||
connect(_ovenProcess.get(), static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
|
||||
this, [&loop, this, tempOutputDir](int exitCode, QProcess::ExitStatus exitStatus) {
|
||||
this, [&loop, this, tempOutputDir, tempAssetPath, tempOutputDirName](int exitCode, QProcess::ExitStatus exitStatus) {
|
||||
qDebug() << "Baking process finished: " << exitCode << exitStatus;
|
||||
|
||||
if (exitStatus == QProcess::CrashExit) {
|
||||
PathUtils::deleteMyTemporaryDir(tempOutputDirName);
|
||||
if (_wasAborted) {
|
||||
emit bakeAborted(_assetHash, _assetPath);
|
||||
} else {
|
||||
|
@ -83,16 +89,10 @@ void BakeAssetTask::run() {
|
|||
emit bakeFailed(_assetHash, _assetPath, errors);
|
||||
}
|
||||
} else if (exitCode == OVEN_STATUS_CODE_SUCCESS) {
|
||||
QDir outputDir = tempOutputDir;
|
||||
auto files = outputDir.entryInfoList(QDir::Files);
|
||||
QVector<QString> outputFiles;
|
||||
for (auto& file : files) {
|
||||
outputFiles.push_back(file.absoluteFilePath());
|
||||
}
|
||||
|
||||
emit bakeComplete(_assetHash, _assetPath, tempOutputDir, outputFiles);
|
||||
emit bakeComplete(_assetHash, _assetPath, tempOutputDir);
|
||||
} else if (exitStatus == QProcess::NormalExit && exitCode == OVEN_STATUS_CODE_ABORT) {
|
||||
_wasAborted.store(true);
|
||||
PathUtils::deleteMyTemporaryDir(tempOutputDirName);
|
||||
emit bakeAborted(_assetHash, _assetPath);
|
||||
} else {
|
||||
QString errors;
|
||||
|
@ -107,6 +107,7 @@ void BakeAssetTask::run() {
|
|||
errors = "Unknown error occurred while baking";
|
||||
}
|
||||
}
|
||||
PathUtils::deleteMyTemporaryDir(tempOutputDirName);
|
||||
emit bakeFailed(_assetHash, _assetPath, errors);
|
||||
}
|
||||
|
||||
|
@ -115,7 +116,10 @@ void BakeAssetTask::run() {
|
|||
|
||||
qDebug() << "Starting oven for " << _assetPath;
|
||||
_ovenProcess->start(path, args, QIODevice::ReadOnly);
|
||||
if (!_ovenProcess->waitForStarted(-1)) {
|
||||
qDebug() << "Running:" << path << args;
|
||||
if (!_ovenProcess->waitForStarted()) {
|
||||
PathUtils::deleteMyTemporaryDir(tempOutputDirName);
|
||||
|
||||
QString errors = "Oven process failed to start";
|
||||
emit bakeFailed(_assetHash, _assetPath, errors);
|
||||
return;
|
||||
|
|
|
@ -37,7 +37,7 @@ public slots:
|
|||
void abort();
|
||||
|
||||
signals:
|
||||
void bakeComplete(QString assetHash, QString assetPath, QString tempOutputDir, QVector<QString> outputFiles);
|
||||
void bakeComplete(QString assetHash, QString assetPath, QString tempOutputDir);
|
||||
void bakeFailed(QString assetHash, QString assetPath, QString errors);
|
||||
void bakeAborted(QString assetHash, QString assetPath);
|
||||
|
||||
|
|
|
@ -98,7 +98,8 @@ AudioMixer::AudioMixer(ReceivedMessage& message) :
|
|||
PacketType::RequestsDomainListData,
|
||||
PacketType::PerAvatarGainSet,
|
||||
PacketType::InjectorGainSet,
|
||||
PacketType::AudioSoloRequest },
|
||||
PacketType::AudioSoloRequest,
|
||||
PacketType::StopInjector },
|
||||
this, "queueAudioPacket");
|
||||
|
||||
// packets whose consequences are global should be processed on the main thread
|
||||
|
@ -246,7 +247,8 @@ void AudioMixer::removeHRTFsForFinishedInjector(const QUuid& streamID) {
|
|||
|
||||
if (injectorClientData) {
|
||||
// stage the removal of this stream, workers handle when preparing mixes for listeners
|
||||
_workerSharedData.removedStreams.emplace_back(injectorClientData->getNodeID(), injectorClientData->getNodeLocalID(),
|
||||
_workerSharedData.removedStreams.emplace_back(injectorClientData->getNodeID(),
|
||||
injectorClientData->getNodeLocalID(),
|
||||
streamID);
|
||||
}
|
||||
}
|
||||
|
@ -586,8 +588,8 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) {
|
|||
// check the payload to see if we have asked for dynamicJitterBuffer support
|
||||
const QString DYNAMIC_JITTER_BUFFER_JSON_KEY = "dynamic_jitter_buffer";
|
||||
bool enableDynamicJitterBuffer = audioBufferGroupObject[DYNAMIC_JITTER_BUFFER_JSON_KEY].toBool();
|
||||
if (enableDynamicJitterBuffer) {
|
||||
qCDebug(audio) << "Enabling dynamic jitter buffers.";
|
||||
if (!enableDynamicJitterBuffer) {
|
||||
qCDebug(audio) << "Disabling dynamic jitter buffers.";
|
||||
|
||||
bool ok;
|
||||
const QString DESIRED_JITTER_BUFFER_FRAMES_KEY = "static_desired_jitter_buffer_frames";
|
||||
|
@ -597,7 +599,7 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) {
|
|||
}
|
||||
qCDebug(audio) << "Static desired jitter buffer frames:" << _numStaticJitterFrames;
|
||||
} else {
|
||||
qCDebug(audio) << "Disabling dynamic jitter buffers.";
|
||||
qCDebug(audio) << "Enabling dynamic jitter buffers.";
|
||||
_numStaticJitterFrames = DISABLE_STATIC_JITTER_FRAMES;
|
||||
}
|
||||
|
||||
|
|
|
@ -104,6 +104,9 @@ int AudioMixerClientData::processPackets(ConcurrentAddedStreams& addedStreams) {
|
|||
case PacketType::AudioSoloRequest:
|
||||
parseSoloRequest(packet, node);
|
||||
break;
|
||||
case PacketType::StopInjector:
|
||||
parseStopInjectorPacket(packet);
|
||||
break;
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
@ -574,6 +577,19 @@ int AudioMixerClientData::checkBuffersBeforeFrameSend() {
|
|||
return (int)_audioStreams.size();
|
||||
}
|
||||
|
||||
void AudioMixerClientData::parseStopInjectorPacket(QSharedPointer<ReceivedMessage> packet) {
|
||||
auto streamID = QUuid::fromRfc4122(packet->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
auto it = std::find_if(std::begin(_audioStreams), std::end(_audioStreams), [&](auto stream) {
|
||||
return streamID == stream->getStreamIdentifier();
|
||||
});
|
||||
|
||||
if (it != std::end(_audioStreams)) {
|
||||
_audioStreams.erase(it);
|
||||
emit injectorStreamFinished(streamID);
|
||||
}
|
||||
}
|
||||
|
||||
bool AudioMixerClientData::shouldSendStats(int frameNumber) {
|
||||
return frameNumber == _frameToSendStats;
|
||||
}
|
||||
|
|
|
@ -67,12 +67,11 @@ public:
|
|||
void parseNodeIgnoreRequest(QSharedPointer<ReceivedMessage> message, const SharedNodePointer& node);
|
||||
void parseRadiusIgnoreRequest(QSharedPointer<ReceivedMessage> message, const SharedNodePointer& node);
|
||||
void parseSoloRequest(QSharedPointer<ReceivedMessage> message, const SharedNodePointer& node);
|
||||
void parseStopInjectorPacket(QSharedPointer<ReceivedMessage> packet);
|
||||
|
||||
// attempt to pop a frame from each audio stream, and return the number of streams from this client
|
||||
int checkBuffersBeforeFrameSend();
|
||||
|
||||
void removeDeadInjectedStreams();
|
||||
|
||||
QJsonObject getAudioStreamStats();
|
||||
|
||||
void sendAudioStreamStatsPackets(const SharedNodePointer& destinationNode);
|
||||
|
@ -163,7 +162,7 @@ public:
|
|||
// end of methods called non-concurrently from single AudioMixerSlave
|
||||
|
||||
signals:
|
||||
void injectorStreamFinished(const QUuid& streamIdentifier);
|
||||
void injectorStreamFinished(const QUuid& streamID);
|
||||
|
||||
public slots:
|
||||
void handleMismatchAudioFormat(SharedNodePointer node, const QString& currentCodec, const QString& recievedCodec);
|
||||
|
|
|
@ -549,38 +549,28 @@ void AudioMixerSlave::addStream(AudioMixerClientData::MixableStream& mixableStre
|
|||
// grab the stream from the ring buffer
|
||||
AudioRingBuffer::ConstIterator streamPopOutput = streamToAdd->getLastPopOutput();
|
||||
|
||||
// stereo sources are not passed through HRTF
|
||||
if (streamToAdd->isStereo()) {
|
||||
|
||||
// apply the avatar gain adjustment
|
||||
gain *= mixableStream.hrtf->getGainAdjustment();
|
||||
streamPopOutput.readSamples(_bufferSamples, AudioConstants::NETWORK_FRAME_SAMPLES_STEREO);
|
||||
|
||||
const float scale = 1 / 32768.0f; // int16_t to float
|
||||
|
||||
for (int i = 0; i < AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL; i++) {
|
||||
_mixSamples[2*i+0] += (float)streamPopOutput[2*i+0] * gain * scale;
|
||||
_mixSamples[2*i+1] += (float)streamPopOutput[2*i+1] * gain * scale;
|
||||
}
|
||||
// stereo sources are not passed through HRTF
|
||||
mixableStream.hrtf->mixStereo(_bufferSamples, _mixSamples, gain, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL);
|
||||
|
||||
++stats.manualStereoMixes;
|
||||
} else if (isEcho) {
|
||||
|
||||
streamPopOutput.readSamples(_bufferSamples, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL);
|
||||
|
||||
// echo sources are not passed through HRTF
|
||||
|
||||
const float scale = 1/32768.0f; // int16_t to float
|
||||
|
||||
for (int i = 0; i < AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL; i++) {
|
||||
float sample = (float)streamPopOutput[i] * gain * scale;
|
||||
_mixSamples[2*i+0] += sample;
|
||||
_mixSamples[2*i+1] += sample;
|
||||
}
|
||||
mixableStream.hrtf->mixMono(_bufferSamples, _mixSamples, gain, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL);
|
||||
|
||||
++stats.manualEchoMixes;
|
||||
} else {
|
||||
|
||||
streamPopOutput.readSamples(_bufferSamples, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL);
|
||||
|
||||
mixableStream.hrtf->render(_bufferSamples, _mixSamples, HRTF_DATASET_INDEX, azimuth, distance, gain,
|
||||
AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL);
|
||||
|
||||
++stats.hrtfRenders;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
#include <ScriptCache.h>
|
||||
#include <EntityEditFilters.h>
|
||||
#include <NetworkingConstants.h>
|
||||
#include <AddressManager.h>
|
||||
#include <hfm/ModelFormatRegistry.h>
|
||||
|
||||
#include "../AssignmentDynamicFactory.h"
|
||||
|
@ -471,77 +470,7 @@ void EntityServer::startDynamicDomainVerification() {
|
|||
qCDebug(entities) << "Starting Dynamic Domain Verification...";
|
||||
|
||||
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
|
||||
QHash<QString, EntityItemID> localMap(tree->getEntityCertificateIDMap());
|
||||
|
||||
QHashIterator<QString, EntityItemID> i(localMap);
|
||||
qCDebug(entities) << localMap.size() << "entities in _entityCertificateIDMap";
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
const auto& certificateID = i.key();
|
||||
const auto& entityID = i.value();
|
||||
|
||||
EntityItemPointer entity = tree->findEntityByEntityItemID(entityID);
|
||||
|
||||
if (entity) {
|
||||
if (!entity->getProperties().verifyStaticCertificateProperties()) {
|
||||
qCDebug(entities) << "During Dynamic Domain Verification, a certified entity with ID" << entityID << "failed"
|
||||
<< "static certificate verification.";
|
||||
// Delete the entity if it doesn't pass static certificate verification
|
||||
tree->withWriteLock([&] {
|
||||
tree->deleteEntity(entityID, true);
|
||||
});
|
||||
} else {
|
||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkRequest networkRequest;
|
||||
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL();
|
||||
requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/location");
|
||||
QJsonObject request;
|
||||
request["certificate_id"] = certificateID;
|
||||
networkRequest.setUrl(requestURL);
|
||||
|
||||
QNetworkReply* networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson());
|
||||
|
||||
connect(networkReply, &QNetworkReply::finished, this, [this, entityID, networkReply] {
|
||||
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
|
||||
|
||||
QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object();
|
||||
jsonObject = jsonObject["data"].toObject();
|
||||
|
||||
if (networkReply->error() == QNetworkReply::NoError) {
|
||||
QString thisDomainID = DependencyManager::get<AddressManager>()->getDomainID().remove(QRegExp("\\{|\\}"));
|
||||
if (jsonObject["domain_id"].toString() != thisDomainID) {
|
||||
EntityItemPointer entity = tree->findEntityByEntityItemID(entityID);
|
||||
if (!entity) {
|
||||
qCDebug(entities) << "Entity undergoing dynamic domain verification is no longer available:" << entityID;
|
||||
networkReply->deleteLater();
|
||||
return;
|
||||
}
|
||||
if (entity->getAge() > (_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS/MSECS_PER_SECOND)) {
|
||||
qCDebug(entities) << "Entity's cert's domain ID" << jsonObject["domain_id"].toString()
|
||||
<< "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << entityID;
|
||||
tree->withWriteLock([&] {
|
||||
tree->deleteEntity(entityID, true);
|
||||
});
|
||||
} else {
|
||||
qCDebug(entities) << "Entity failed dynamic domain verification, but was created too recently to necessitate deletion:" << entityID;
|
||||
}
|
||||
} else {
|
||||
qCDebug(entities) << "Entity passed dynamic domain verification:" << entityID;
|
||||
}
|
||||
} else {
|
||||
qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; NOT deleting entity" << entityID
|
||||
<< "More info:" << jsonObject;
|
||||
}
|
||||
|
||||
networkReply->deleteLater();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
qCWarning(entities) << "During DDV, an entity with ID" << entityID << "was NOT found in the Entity Tree!";
|
||||
}
|
||||
}
|
||||
tree->startDynamicDomainVerificationOnServer((float) _MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS / MSECS_PER_SECOND);
|
||||
|
||||
int nextInterval = qrand() % ((_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS + 1) - _MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS) + _MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS;
|
||||
qCDebug(entities) << "Restarting Dynamic Domain Verification timer for" << nextInterval / 1000 << "seconds";
|
||||
|
|
4
cmake/externals/LibOVR/CMakeLists.txt
vendored
|
@ -17,8 +17,8 @@ if (WIN32)
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://public.highfidelity.com/dependencies/ovr_sdk_win_1.26.0_public.zip
|
||||
URL_MD5 06804ff9727b910dcd04a37c800053b5
|
||||
URL https://hifi-public.s3.amazonaws.com/dependencies/ovr_sdk_win_1.35.0.zip
|
||||
URL_MD5 1e3e8b2101387af07ff9c841d0ea285e
|
||||
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
|
||||
PATCH_COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/LibOVRCMakeLists.txt" <SOURCE_DIR>/CMakeLists.txt
|
||||
LOG_DOWNLOAD 1
|
||||
|
|
|
@ -53,10 +53,5 @@ macro(add_crashpad)
|
|||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CRASHPAD_HANDLER_EXE_PATH} "$<TARGET_FILE_DIR:${TARGET_NAME}>/"
|
||||
)
|
||||
install(
|
||||
PROGRAMS ${CRASHPAD_HANDLER_EXE_PATH}
|
||||
DESTINATION ${INTERFACE_INSTALL_DIR}
|
||||
COMPONENT ${CLIENT_COMPONENT}
|
||||
)
|
||||
endif ()
|
||||
endmacro()
|
||||
|
|
|
@ -22,7 +22,7 @@ macro(optional_win_executable_signing)
|
|||
# setup a post build command to sign the executable
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND ${SIGNTOOL_EXECUTABLE} sign /fd sha256 /f %HF_PFX_FILE% /p %HF_PFX_PASSPHRASE% /tr http://sha256timestamp.ws.symantec.com/sha256/timestamp /td SHA256 ${EXECUTABLE_PATH}
|
||||
COMMAND ${SIGNTOOL_EXECUTABLE} sign /fd sha256 /f %HF_PFX_FILE% /p %HF_PFX_PASSPHRASE% /tr ${TIMESERVER_URL} /td SHA256 ${EXECUTABLE_PATH}
|
||||
)
|
||||
else ()
|
||||
message(FATAL_ERROR "HF_PFX_PASSPHRASE must be set for executables to be signed.")
|
||||
|
|
74
cmake/macros/TargetOpenEXR.cmake
Normal file
|
@ -0,0 +1,74 @@
|
|||
#
|
||||
# Copyright 2015 High Fidelity, Inc.
|
||||
# Created by Olivier Prat on 2019/03/26
|
||||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
macro(TARGET_OPENEXR)
|
||||
if (NOT ANDROID)
|
||||
set(openexr_config_file "${VCPKG_INSTALL_ROOT}/include/OpenEXR/OpenEXRConfig.h")
|
||||
if(EXISTS ${openexr_config_file})
|
||||
file(STRINGS
|
||||
${openexr_config_file}
|
||||
TMP
|
||||
REGEX "#define OPENEXR_VERSION_STRING.*$")
|
||||
string(REGEX MATCHALL "[0-9.]+" OPENEXR_VERSION ${TMP})
|
||||
|
||||
file(STRINGS
|
||||
${openexr_config_file}
|
||||
TMP
|
||||
REGEX "#define OPENEXR_VERSION_MAJOR.*$")
|
||||
string(REGEX MATCHALL "[0-9]" OPENEXR_MAJOR_VERSION ${TMP})
|
||||
|
||||
file(STRINGS
|
||||
${openexr_config_file}
|
||||
TMP
|
||||
REGEX "#define OPENEXR_VERSION_MINOR.*$")
|
||||
string(REGEX MATCHALL "[0-9]" OPENEXR_MINOR_VERSION ${TMP})
|
||||
endif()
|
||||
|
||||
foreach(OPENEXR_LIB
|
||||
IlmImf
|
||||
IlmImfUtil
|
||||
Half
|
||||
Iex
|
||||
IexMath
|
||||
Imath
|
||||
IlmThread)
|
||||
|
||||
# OpenEXR libraries may be suffixed with the version number, so we search
|
||||
# using both versioned and unversioned names.
|
||||
find_library(OPENEXR_${OPENEXR_LIB}_LIBRARY_RELEASE
|
||||
NAMES
|
||||
${OPENEXR_LIB}-${OPENEXR_MAJOR_VERSION}_${OPENEXR_MINOR_VERSION}_s
|
||||
${OPENEXR_LIB}_s
|
||||
|
||||
PATHS ${VCPKG_INSTALL_ROOT}/lib NO_DEFAULT_PATH
|
||||
)
|
||||
#mark_as_advanced(OPENEXR_${OPENEXR_LIB}_LIBRARY)
|
||||
|
||||
if(OPENEXR_${OPENEXR_LIB}_LIBRARY_RELEASE)
|
||||
list(APPEND OPENEXR_LIBRARY_RELEASE ${OPENEXR_${OPENEXR_LIB}_LIBRARY_RELEASE})
|
||||
endif()
|
||||
|
||||
# OpenEXR libraries may be suffixed with the version number, so we search
|
||||
# using both versioned and unversioned names.
|
||||
find_library(OPENEXR_${OPENEXR_LIB}_LIBRARY_DEBUG
|
||||
NAMES
|
||||
${OPENEXR_LIB}-${OPENEXR_MAJOR_VERSION}_${OPENEXR_MINOR_VERSION}_s_d
|
||||
${OPENEXR_LIB}_s_d
|
||||
|
||||
PATHS ${VCPKG_INSTALL_ROOT}/debug/lib NO_DEFAULT_PATH
|
||||
)
|
||||
#mark_as_advanced(OPENEXR_${OPENEXR_LIB}_DEBUG_LIBRARY)
|
||||
|
||||
if(OPENEXR_${OPENEXR_LIB}_LIBRARY_DEBUG)
|
||||
list(APPEND OPENEXR_LIBRARY_DEBUG ${OPENEXR_${OPENEXR_LIB}_LIBRARY_DEBUG})
|
||||
endif()
|
||||
endforeach(OPENEXR_LIB)
|
||||
|
||||
select_library_configurations(OPENEXR)
|
||||
target_link_libraries(${TARGET_NAME} ${OPENEXR_LIBRARY})
|
||||
endif()
|
||||
endmacro()
|
|
@ -1,4 +1,4 @@
|
|||
Source: hifi-deps
|
||||
Version: 0
|
||||
Version: 0.1
|
||||
Description: Collected dependencies for High Fidelity applications
|
||||
Build-Depends: bullet3, draco, etc2comp, glm, nvtt, openssl (windows), tbb (!android&!osx), zlib
|
||||
Build-Depends: bullet3, draco, etc2comp, glm, nvtt, openexr (!android), openssl (windows), tbb (!android&!osx), zlib
|
||||
|
|
4
cmake/ports/openexr/CONTROL
Normal file
|
@ -0,0 +1,4 @@
|
|||
Source: openexr
|
||||
Version: 2.3.0-2
|
||||
Description: OpenEXR is a high dynamic-range (HDR) image file format developed by Industrial Light & Magic for use in computer imaging applications
|
||||
Build-Depends: zlib
|
87
cmake/ports/openexr/FindOpenEXR.cmake
Normal file
|
@ -0,0 +1,87 @@
|
|||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
find_path(OpenEXR_INCLUDE_DIRS OpenEXR/OpenEXRConfig.h)
|
||||
find_path(OPENEXR_INCLUDE_PATHS NAMES ImfRgbaFile.h PATH_SUFFIXES OpenEXR)
|
||||
|
||||
file(STRINGS "${OpenEXR_INCLUDE_DIRS}/OpenEXR/OpenEXRConfig.h" OPENEXR_CONFIG_H)
|
||||
|
||||
string(REGEX REPLACE "^.*define OPENEXR_VERSION_MAJOR ([0-9]+).*$" "\\1" OpenEXR_VERSION_MAJOR "${OPENEXR_CONFIG_H}")
|
||||
string(REGEX REPLACE "^.*define OPENEXR_VERSION_MINOR ([0-9]+).*$" "\\1" OpenEXR_VERSION_MINOR "${OPENEXR_CONFIG_H}")
|
||||
set(OpenEXR_LIB_SUFFIX "${OpenEXR_VERSION_MAJOR}_${OpenEXR_VERSION_MINOR}")
|
||||
|
||||
include(SelectLibraryConfigurations)
|
||||
|
||||
if(NOT OpenEXR_BASE_LIBRARY)
|
||||
find_library(OpenEXR_BASE_LIBRARY_RELEASE NAMES IlmImf-${OpenEXR_LIB_SUFFIX})
|
||||
find_library(OpenEXR_BASE_LIBRARY_DEBUG NAMES IlmImf-${OpenEXR_LIB_SUFFIX}_d)
|
||||
select_library_configurations(OpenEXR_BASE)
|
||||
endif()
|
||||
|
||||
if(NOT OpenEXR_UTIL_LIBRARY)
|
||||
find_library(OpenEXR_UTIL_LIBRARY_RELEASE NAMES IlmImfUtil-${OpenEXR_LIB_SUFFIX})
|
||||
find_library(OpenEXR_UTIL_LIBRARY_DEBUG NAMES IlmImfUtil-${OpenEXR_LIB_SUFFIX}_d)
|
||||
select_library_configurations(OpenEXR_UTIL)
|
||||
endif()
|
||||
|
||||
if(NOT OpenEXR_HALF_LIBRARY)
|
||||
find_library(OpenEXR_HALF_LIBRARY_RELEASE NAMES Half-${OpenEXR_LIB_SUFFIX})
|
||||
find_library(OpenEXR_HALF_LIBRARY_DEBUG NAMES Half-${OpenEXR_LIB_SUFFIX}_d)
|
||||
select_library_configurations(OpenEXR_HALF)
|
||||
endif()
|
||||
|
||||
if(NOT OpenEXR_IEX_LIBRARY)
|
||||
find_library(OpenEXR_IEX_LIBRARY_RELEASE NAMES Iex-${OpenEXR_LIB_SUFFIX})
|
||||
find_library(OpenEXR_IEX_LIBRARY_DEBUG NAMES Iex-${OpenEXR_LIB_SUFFIX}_d)
|
||||
select_library_configurations(OpenEXR_IEX)
|
||||
endif()
|
||||
|
||||
if(NOT OpenEXR_MATH_LIBRARY)
|
||||
find_library(OpenEXR_MATH_LIBRARY_RELEASE NAMES Imath-${OpenEXR_LIB_SUFFIX})
|
||||
find_library(OpenEXR_MATH_LIBRARY_DEBUG NAMES Imath-${OpenEXR_LIB_SUFFIX}_d)
|
||||
select_library_configurations(OpenEXR_MATH)
|
||||
endif()
|
||||
|
||||
if(NOT OpenEXR_THREAD_LIBRARY)
|
||||
find_library(OpenEXR_THREAD_LIBRARY_RELEASE NAMES IlmThread-${OpenEXR_LIB_SUFFIX})
|
||||
find_library(OpenEXR_THREAD_LIBRARY_DEBUG NAMES IlmThread-${OpenEXR_LIB_SUFFIX}_d)
|
||||
select_library_configurations(OpenEXR_THREAD)
|
||||
endif()
|
||||
|
||||
if(NOT OpenEXR_IEXMATH_LIBRARY)
|
||||
find_library(OpenEXR_IEXMATH_LIBRARY_RELEASE NAMES IexMath-${OpenEXR_LIB_SUFFIX})
|
||||
find_library(OpenEXR_IEXMATH_LIBRARY_DEBUG NAMES IexMath-${OpenEXR_LIB_SUFFIX}d)
|
||||
select_library_configurations(OpenEXR_IEXMATH)
|
||||
endif()
|
||||
|
||||
set(OPENEXR_HALF_LIBRARY "${OpenEXR_HALF_LIBRARY}")
|
||||
set(OPENEXR_IEX_LIBRARY "${OpenEXR_IEX_LIBRARY}")
|
||||
set(OPENEXR_IMATH_LIBRARY "${OpenEXR_MATH_LIBRARY}")
|
||||
set(OPENEXR_ILMIMF_LIBRARY "${OpenEXR_BASE_LIBRARY}")
|
||||
set(OPENEXR_ILMIMFUTIL_LIBRARY "${OpenEXR_UTIL_LIBRARY}")
|
||||
set(OPENEXR_ILMTHREAD_LIBRARY "${OpenEXR_THREAD_LIBRARY}")
|
||||
|
||||
set(OpenEXR_LIBRARY "${OpenEXR_BASE_LIBRARY}")
|
||||
|
||||
set(OpenEXR_LIBRARIES
|
||||
${OpenEXR_LIBRARY}
|
||||
${OpenEXR_MATH_LIBRARY}
|
||||
${OpenEXR_IEXMATH_LIBRARY}
|
||||
${OpenEXR_UTIL_LIBRARY}
|
||||
${OpenEXR_HALF_LIBRARY}
|
||||
${OpenEXR_IEX_LIBRARY}
|
||||
${OpenEXR_THREAD_LIBRARY}
|
||||
)
|
||||
|
||||
set(OPENEXR_LIBRARIES
|
||||
${OPENEXR_HALF_LIBRARY}
|
||||
${OPENEXR_IEX_LIBRARY}
|
||||
${OPENEXR_IMATH_LIBRARY}
|
||||
${OPENEXR_ILMIMF_LIBRARY}
|
||||
${OPENEXR_ILMTHREAD_LIBRARY}
|
||||
)
|
||||
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(OpenEXR REQUIRED_VARS OpenEXR_LIBRARIES OpenEXR_INCLUDE_DIRS)
|
||||
|
||||
if(OpenEXR_FOUND)
|
||||
set(OPENEXR_FOUND 1)
|
||||
endif()
|
19
cmake/ports/openexr/fix_install_ilmimf.patch
Normal file
|
@ -0,0 +1,19 @@
|
|||
diff --git a/OpenEXR/IlmImf/CMakeLists.txt b/OpenEXR/IlmImf/CMakeLists.txt
|
||||
index e1a8740..d31cf68 100644
|
||||
--- a/OpenEXR/IlmImf/CMakeLists.txt
|
||||
+++ b/OpenEXR/IlmImf/CMakeLists.txt
|
||||
@@ -2,14 +2,6 @@
|
||||
|
||||
SET(CMAKE_INCLUDE_CURRENT_DIR 1)
|
||||
|
||||
-IF (WIN32)
|
||||
- SET(RUNTIME_DIR ${OPENEXR_PACKAGE_PREFIX}/bin)
|
||||
- SET(WORKING_DIR ${RUNTIME_DIR})
|
||||
-ELSE ()
|
||||
- SET(RUNTIME_DIR ${OPENEXR_PACKAGE_PREFIX}/lib)
|
||||
- SET(WORKING_DIR .)
|
||||
-ENDIF ()
|
||||
-
|
||||
SET(BUILD_B44EXPLOGTABLE OFF)
|
||||
IF (NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/b44ExpLogTable.h")
|
||||
SET(BUILD_B44EXPLOGTABLE ON)
|
74
cmake/ports/openexr/portfile.cmake
Normal file
|
@ -0,0 +1,74 @@
|
|||
include(vcpkg_common_functions)
|
||||
|
||||
set(OPENEXR_VERSION 2.3.0)
|
||||
set(OPENEXR_HASH 268ae64b40d21d662f405fba97c307dad1456b7d996a447aadafd41b640ca736d4851d9544b4741a94e7b7c335fe6e9d3b16180e710671abfc0c8b2740b147b2)
|
||||
|
||||
vcpkg_from_github(
|
||||
OUT_SOURCE_PATH SOURCE_PATH
|
||||
REPO openexr/openexr
|
||||
REF v${OPENEXR_VERSION}
|
||||
SHA512 ${OPENEXR_HASH}
|
||||
HEAD_REF master
|
||||
PATCHES "fix_install_ilmimf.patch"
|
||||
)
|
||||
|
||||
set(OPENEXR_STATIC ON)
|
||||
set(OPENEXR_SHARED OFF)
|
||||
|
||||
vcpkg_configure_cmake(SOURCE_PATH ${SOURCE_PATH}
|
||||
PREFER_NINJA
|
||||
OPTIONS
|
||||
-DOPENEXR_BUILD_PYTHON_LIBS=OFF
|
||||
-DOPENEXR_BUILD_VIEWERS=OFF
|
||||
-DOPENEXR_ENABLE_TESTS=OFF
|
||||
-DOPENEXR_RUN_FUZZ_TESTS=OFF
|
||||
-DOPENEXR_BUILD_SHARED=${OPENEXR_SHARED}
|
||||
-DOPENEXR_BUILD_STATIC=${OPENEXR_STATIC}
|
||||
OPTIONS_DEBUG
|
||||
-DILMBASE_PACKAGE_PREFIX=${CURRENT_INSTALLED_DIR}/debug
|
||||
OPTIONS_RELEASE
|
||||
-DILMBASE_PACKAGE_PREFIX=${CURRENT_INSTALLED_DIR})
|
||||
|
||||
vcpkg_install_cmake()
|
||||
|
||||
file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/include)
|
||||
file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/share)
|
||||
|
||||
# NOTE: Only use ".exe" extension on Windows executables.
|
||||
# Is there a cleaner way to do this?
|
||||
if(WIN32)
|
||||
set(EXECUTABLE_SUFFIX ".exe")
|
||||
else()
|
||||
set(EXECUTABLE_SUFFIX "")
|
||||
endif()
|
||||
file(REMOVE ${CURRENT_PACKAGES_DIR}/debug/bin/exrenvmap${EXECUTABLE_SUFFIX})
|
||||
file(REMOVE ${CURRENT_PACKAGES_DIR}/debug/bin/exrheader${EXECUTABLE_SUFFIX})
|
||||
file(REMOVE ${CURRENT_PACKAGES_DIR}/debug/bin/exrmakepreview${EXECUTABLE_SUFFIX})
|
||||
file(REMOVE ${CURRENT_PACKAGES_DIR}/debug/bin/exrmaketiled${EXECUTABLE_SUFFIX})
|
||||
file(REMOVE ${CURRENT_PACKAGES_DIR}/debug/bin/exrmultipart${EXECUTABLE_SUFFIX})
|
||||
file(REMOVE ${CURRENT_PACKAGES_DIR}/debug/bin/exrmultiview${EXECUTABLE_SUFFIX})
|
||||
file(REMOVE ${CURRENT_PACKAGES_DIR}/debug/bin/exrstdattr${EXECUTABLE_SUFFIX})
|
||||
file(REMOVE ${CURRENT_PACKAGES_DIR}/bin/exrenvmap${EXECUTABLE_SUFFIX})
|
||||
file(REMOVE ${CURRENT_PACKAGES_DIR}/bin/exrheader${EXECUTABLE_SUFFIX})
|
||||
file(REMOVE ${CURRENT_PACKAGES_DIR}/bin/exrmakepreview${EXECUTABLE_SUFFIX})
|
||||
file(REMOVE ${CURRENT_PACKAGES_DIR}/bin/exrmaketiled${EXECUTABLE_SUFFIX})
|
||||
file(REMOVE ${CURRENT_PACKAGES_DIR}/bin/exrmultipart${EXECUTABLE_SUFFIX})
|
||||
file(REMOVE ${CURRENT_PACKAGES_DIR}/bin/exrmultiview${EXECUTABLE_SUFFIX})
|
||||
file(REMOVE ${CURRENT_PACKAGES_DIR}/bin/exrstdattr${EXECUTABLE_SUFFIX})
|
||||
|
||||
vcpkg_copy_pdbs()
|
||||
|
||||
if (OPENEXR_STATIC)
|
||||
file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/bin ${CURRENT_PACKAGES_DIR}/debug/bin)
|
||||
endif()
|
||||
|
||||
if (VCPKG_CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
set(OPENEXR_PORT_DIR "openexr")
|
||||
else()
|
||||
set(OPENEXR_PORT_DIR "OpenEXR")
|
||||
endif()
|
||||
|
||||
file(COPY ${SOURCE_PATH}/LICENSE DESTINATION ${CURRENT_PACKAGES_DIR}/share/${OPENEXR_PORT_DIR})
|
||||
file(RENAME ${CURRENT_PACKAGES_DIR}/share/${OPENEXR_PORT_DIR}/LICENSE ${CURRENT_PACKAGES_DIR}/share/${OPENEXR_PORT_DIR}/copyright)
|
||||
|
||||
file(COPY ${CMAKE_CURRENT_LIST_DIR}/FindOpenEXR.cmake DESTINATION ${CURRENT_PACKAGES_DIR}/share/${OPENEXR_PORT_DIR})
|
|
@ -481,3 +481,15 @@ function prepareAccessTokenPrompt(callback) {
|
|||
swal.close();
|
||||
});
|
||||
}
|
||||
|
||||
function getMetaverseUrl(callback) {
|
||||
$.ajax('/api/metaverse_info', {
|
||||
success: function(data) {
|
||||
callback(data.metaverse_url);
|
||||
},
|
||||
error: function() {
|
||||
callback(URLs.METAVERSE_URL);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -16,47 +16,55 @@ $(document).ready(function(){
|
|||
|
||||
Settings.extraGroupsAtEnd = Settings.extraDomainGroupsAtEnd;
|
||||
Settings.extraGroupsAtIndex = Settings.extraDomainGroupsAtIndex;
|
||||
var METAVERSE_URL = URLs.METAVERSE_URL;
|
||||
|
||||
Settings.afterReloadActions = function() {
|
||||
// call our method to setup the HF account button
|
||||
setupHFAccountButton();
|
||||
|
||||
// call our method to setup the place names table
|
||||
setupPlacesTable();
|
||||
getMetaverseUrl(function(metaverse_url) {
|
||||
METAVERSE_URL = metaverse_url;
|
||||
|
||||
setupDomainNetworkingSettings();
|
||||
// setupDomainLabelSetting();
|
||||
// call our method to setup the HF account button
|
||||
setupHFAccountButton();
|
||||
|
||||
setupSettingsBackup();
|
||||
// call our method to setup the place names table
|
||||
setupPlacesTable();
|
||||
|
||||
if (domainIDIsSet()) {
|
||||
// now, ask the API for what places, if any, point to this domain
|
||||
reloadDomainInfo();
|
||||
setupDomainNetworkingSettings();
|
||||
// setupDomainLabelSetting();
|
||||
|
||||
// we need to ask the API what a shareable name for this domain is
|
||||
getShareName(function(success, shareName) {
|
||||
if (success) {
|
||||
var shareLink = "https://hifi.place/" + shareName;
|
||||
$('#visit-domain-link').attr("href", shareLink).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
setupSettingsBackup();
|
||||
|
||||
if (Settings.data.values.wizard.cloud_domain) {
|
||||
$('#manage-cloud-domains-link').show();
|
||||
if (domainIDIsSet()) {
|
||||
// now, ask the API for what places, if any, point to this domain
|
||||
reloadDomainInfo();
|
||||
|
||||
var cloudWizardExit = qs["cloud-wizard-exit"];
|
||||
if (cloudWizardExit != undefined) {
|
||||
$('#cloud-domains-alert').show();
|
||||
// we need to ask the API what a shareable name for this domain is
|
||||
getShareName(function(success, shareName) {
|
||||
if (success) {
|
||||
var shareLink = "https://hifi.place/" + shareName;
|
||||
$('#visit-domain-link').attr("href", shareLink).show();
|
||||
}
|
||||
});
|
||||
} else if (accessTokenIsSet()) {
|
||||
$('#' + Settings.GET_TEMPORARY_NAME_BTN_ID).show();
|
||||
}
|
||||
|
||||
$(Settings.DOMAIN_ID_SELECTOR).siblings('span').append("</br><strong>Changing the domain ID for a Cloud Domain may result in an incorrect status for the domain on your Cloud Domains page.</strong>");
|
||||
} else {
|
||||
// append the domain selection modal
|
||||
appendDomainIDButtons();
|
||||
}
|
||||
if (Settings.data.values.wizard.cloud_domain) {
|
||||
$('#manage-cloud-domains-link').show();
|
||||
|
||||
handleAction();
|
||||
var cloudWizardExit = qs["cloud-wizard-exit"];
|
||||
if (cloudWizardExit != undefined) {
|
||||
$('#cloud-domains-alert').show();
|
||||
}
|
||||
|
||||
$(Settings.DOMAIN_ID_SELECTOR).siblings('span').append("</br><strong>Changing the domain ID for a Cloud Domain may result in an incorrect status for the domain on your Cloud Domains page.</strong>");
|
||||
} else {
|
||||
// append the domain selection modal
|
||||
appendDomainIDButtons();
|
||||
}
|
||||
|
||||
handleAction();
|
||||
});
|
||||
}
|
||||
|
||||
Settings.handlePostSettings = function(formJSON) {
|
||||
|
@ -258,7 +266,7 @@ $(document).ready(function(){
|
|||
buttonSetting.button_label = "Connect High Fidelity Account";
|
||||
buttonSetting.html_id = Settings.CONNECT_ACCOUNT_BTN_ID;
|
||||
|
||||
buttonSetting.href = URLs.METAVERSE_URL + "/user/tokens/new?for_domain_server=true";
|
||||
buttonSetting.href = METAVERSE_URL + "/user/tokens/new?for_domain_server=true";
|
||||
|
||||
// since we do not have an access token we change hide domain ID and auto networking settings
|
||||
// without an access token niether of them can do anything
|
||||
|
@ -645,7 +653,7 @@ $(document).ready(function(){
|
|||
label: 'Places',
|
||||
html_id: Settings.PLACES_TABLE_ID,
|
||||
help: "The following places currently point to this domain.</br>To point places to this domain, "
|
||||
+ " go to the <a href='" + URLs.METAVERSE_URL + "/user/places'>My Places</a> "
|
||||
+ " go to the <a href='" + METAVERSE_URL + "/user/places'>My Places</a> "
|
||||
+ "page in your High Fidelity Metaverse account.",
|
||||
read_only: true,
|
||||
can_add_new_rows: false,
|
||||
|
@ -678,12 +686,9 @@ $(document).ready(function(){
|
|||
var errorEl = createDomainLoadingError("There was an error retrieving your places.");
|
||||
$("#" + Settings.PLACES_TABLE_ID).after(errorEl);
|
||||
|
||||
// do we have a domain ID?
|
||||
if (!domainIDIsSet()) {
|
||||
// we don't have a domain ID - add a button to offer the user a chance to get a temporary one
|
||||
var temporaryPlaceButton = dynamicButton(Settings.GET_TEMPORARY_NAME_BTN_ID, 'Get a temporary place name');
|
||||
$('#' + Settings.PLACES_TABLE_ID).after(temporaryPlaceButton);
|
||||
}
|
||||
var temporaryPlaceButton = dynamicButton(Settings.GET_TEMPORARY_NAME_BTN_ID, 'Get a temporary place name');
|
||||
temporaryPlaceButton.hide();
|
||||
$('#' + Settings.PLACES_TABLE_ID).after(temporaryPlaceButton);
|
||||
if (accessTokenIsSet()) {
|
||||
appendAddButtonToPlacesTable();
|
||||
}
|
||||
|
@ -774,8 +779,9 @@ $(document).ready(function(){
|
|||
|
||||
// check if we have owner_places (for a real domain) or a name (for a temporary domain)
|
||||
if (data.status == "success") {
|
||||
$('#' + Settings.GET_TEMPORARY_NAME_BTN_ID).hide();
|
||||
$('.domain-loading-hide').show();
|
||||
if (data.domain.owner_places) {
|
||||
if (data.domain.owner_places && data.domain.owner_places.length > 0) {
|
||||
// add a table row for each of these names
|
||||
_.each(data.domain.owner_places, function(place){
|
||||
$('#' + Settings.PLACES_TABLE_ID + " tbody").append(placeTableRowForPlaceObject(place));
|
||||
|
@ -783,8 +789,9 @@ $(document).ready(function(){
|
|||
} else if (data.domain.name) {
|
||||
// add a table row for this temporary domain name
|
||||
$('#' + Settings.PLACES_TABLE_ID + " tbody").append(placeTableRow(data.domain.name, '/', true));
|
||||
} else {
|
||||
$('#' + Settings.GET_TEMPORARY_NAME_BTN_ID).show();
|
||||
}
|
||||
|
||||
// Update label
|
||||
if (showOrHideLabel()) {
|
||||
var label = data.domain.label;
|
||||
|
@ -953,7 +960,7 @@ $(document).ready(function(){
|
|||
modal_buttons["success"] = {
|
||||
label: 'Create new domain',
|
||||
callback: function() {
|
||||
window.open(URLs.METAVERSE_URL + "/user/domains", '_blank');
|
||||
window.open(METAVERSE_URL + "/user/domains", '_blank');
|
||||
}
|
||||
}
|
||||
modal_body = "<p>You do not have any domains in your High Fidelity account." +
|
||||
|
@ -1001,7 +1008,7 @@ $(document).ready(function(){
|
|||
showSpinnerAlert('Creating temporary place name');
|
||||
|
||||
// make a get request to get a temporary domain
|
||||
$.post(URLs.METAVERSE_URL + '/api/v1/domains/temporary', function(data){
|
||||
$.post(METAVERSE_URL + '/api/v1/domains/temporary', function(data){
|
||||
if (data.status == "success") {
|
||||
var domain = data.data.domain;
|
||||
|
||||
|
|
|
@ -1010,7 +1010,7 @@ void DomainGatekeeper::refreshGroupsCache() {
|
|||
nodeList->eachNode([this](const SharedNodePointer& node) {
|
||||
if (!node->getPermissions().isAssignment) {
|
||||
// this node is an agent
|
||||
const QString& verifiedUserName = node->getPermissions().getVerifiedUserName();
|
||||
QString verifiedUserName = node->getPermissions().getVerifiedUserName();
|
||||
if (!verifiedUserName.isEmpty()) {
|
||||
getGroupMemberships(verifiedUserName);
|
||||
}
|
||||
|
|
|
@ -1734,7 +1734,7 @@ void DomainServer::processOctreeDataPersistMessage(QSharedPointer<ReceivedMessag
|
|||
f.write(data);
|
||||
OctreeUtils::RawEntityData entityData;
|
||||
if (entityData.readOctreeDataInfoFromData(data)) {
|
||||
qCDebug(domain_server) << "Wrote new entities file" << entityData.id << entityData.version;
|
||||
qCDebug(domain_server) << "Wrote new entities file" << entityData.id << entityData.dataVersion;
|
||||
} else {
|
||||
qCDebug(domain_server) << "Failed to read new octree data info";
|
||||
}
|
||||
|
@ -1916,6 +1916,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
const QString URI_SETTINGS = "/settings";
|
||||
const QString URI_CONTENT_UPLOAD = "/content/upload";
|
||||
const QString URI_RESTART = "/restart";
|
||||
const QString URI_API_METAVERSE_INFO = "/api/metaverse_info";
|
||||
const QString URI_API_PLACES = "/api/places";
|
||||
const QString URI_API_DOMAINS = "/api/domains";
|
||||
const QString URI_API_DOMAINS_ID = "/api/domains/";
|
||||
|
@ -2164,6 +2165,15 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
} else if (url.path() == URI_RESTART) {
|
||||
connection->respond(HTTPConnection::StatusCode200);
|
||||
restart();
|
||||
return true;
|
||||
} else if (url.path() == URI_API_METAVERSE_INFO) {
|
||||
QJsonObject rootJSON {
|
||||
{ "metaverse_url", NetworkingConstants::METAVERSE_SERVER_URL().toString() }
|
||||
};
|
||||
|
||||
QJsonDocument docJSON{ rootJSON };
|
||||
connectionPtr->respond(HTTPConnection::StatusCode200, docJSON.toJson(), JSON_MIME_TYPE.toUtf8());
|
||||
|
||||
return true;
|
||||
} else if (url.path() == URI_API_DOMAINS) {
|
||||
return forwardMetaverseAPIRequest(connection, "/api/v1/domains", "");
|
||||
|
|
|
@ -197,260 +197,100 @@
|
|||
"id": "rightHandStateMachine",
|
||||
"type": "stateMachine",
|
||||
"data": {
|
||||
"currentState": "rightHandGrasp",
|
||||
"currentState": "rightHandAnimNone",
|
||||
"states": [
|
||||
{
|
||||
"id": "rightHandGrasp",
|
||||
"interpTarget": 3,
|
||||
"id": "rightHandAnimNone",
|
||||
"interpTarget": 1,
|
||||
"interpDuration": 3,
|
||||
"transitions": [
|
||||
{ "var": "isRightIndexPoint", "state": "rightIndexPoint" },
|
||||
{ "var": "isRightThumbRaise", "state": "rightThumbRaise" },
|
||||
{ "var": "isRightIndexPointAndThumbRaise", "state": "rightIndexPointAndThumbRaise" }
|
||||
{ "var": "rightHandAnimA", "state": "rightHandAnimA" },
|
||||
{ "var": "rightHandAnimB", "state": "rightHandAnimB" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "rightIndexPoint",
|
||||
"interpTarget": 15,
|
||||
"id": "rightHandAnimA",
|
||||
"interpTarget": 1,
|
||||
"interpDuration": 3,
|
||||
"transitions": [
|
||||
{ "var": "isRightHandGrasp", "state": "rightHandGrasp" },
|
||||
{ "var": "isRightThumbRaise", "state": "rightThumbRaise" },
|
||||
{ "var": "isRightIndexPointAndThumbRaise", "state": "rightIndexPointAndThumbRaise" }
|
||||
{ "var": "rightHandAnimNone", "state": "rightHandAnimNone" },
|
||||
{ "var": "rightHandAnimB", "state": "rightHandAnimB" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "rightThumbRaise",
|
||||
"interpTarget": 15,
|
||||
"id": "rightHandAnimB",
|
||||
"interpTarget": 1,
|
||||
"interpDuration": 3,
|
||||
"transitions": [
|
||||
{ "var": "isRightHandGrasp", "state": "rightHandGrasp" },
|
||||
{ "var": "isRightIndexPoint", "state": "rightIndexPoint" },
|
||||
{ "var": "isRightIndexPointAndThumbRaise", "state": "rightIndexPointAndThumbRaise" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "rightIndexPointAndThumbRaise",
|
||||
"interpTarget": 15,
|
||||
"interpDuration": 3,
|
||||
"transitions": [
|
||||
{ "var": "isRightHandGrasp", "state": "rightHandGrasp" },
|
||||
{ "var": "isRightIndexPoint", "state": "rightIndexPoint" },
|
||||
{ "var": "isRightThumbRaise", "state": "rightThumbRaise" }
|
||||
{ "var": "rightHandAnimNone", "state": "rightHandAnimNone" },
|
||||
{ "var": "rightHandAnimA", "state": "rightHandAnimA" }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "rightHandGrasp",
|
||||
"type": "blendLinear",
|
||||
"data": {
|
||||
"alpha": 0.0,
|
||||
"alphaVar": "rightHandGraspAlpha"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "rightHandGraspOpen",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "qrc:///avatar/animations/hydra_pose_open_right.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 0.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "rightHandGraspClosed",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "qrc:///avatar/animations/hydra_pose_closed_right.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 0.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "rightIndexPoint",
|
||||
"type": "blendLinear",
|
||||
"data": {
|
||||
"alpha": 0.0,
|
||||
"alphaVar": "rightHandGraspAlpha"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "rightIndexPointOpen",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "qrc:///avatar/animations/touch_point_open_right.fbx",
|
||||
"startFrame": 15.0,
|
||||
"endFrame": 15.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "rightIndexPointClosed",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "qrc:///avatar/animations/touch_point_closed_right.fbx",
|
||||
"startFrame": 15.0,
|
||||
"endFrame": 15.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "rightThumbRaise",
|
||||
"type": "blendLinear",
|
||||
"data": {
|
||||
"alpha": 0.0,
|
||||
"alphaVar": "rightHandGraspAlpha"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "rightThumbRaiseOpen",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "qrc:///avatar/animations/touch_thumb_open_right.fbx",
|
||||
"startFrame": 15.0,
|
||||
"endFrame": 15.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "rightThumbRaiseClosed",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "qrc:///avatar/animations/touch_thumb_closed_right.fbx",
|
||||
"startFrame": 15.0,
|
||||
"endFrame": 15.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "rightIndexPointAndThumbRaise",
|
||||
"type": "blendLinear",
|
||||
"data": {
|
||||
"alpha": 0.0,
|
||||
"alphaVar": "rightHandGraspAlpha"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "rightIndexPointAndThumbRaiseOpen",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "qrc:///avatar/animations/touch_thumb_point_open_right.fbx",
|
||||
"startFrame": 15.0,
|
||||
"endFrame": 15.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "rightIndexPointAndThumbRaiseClosed",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "qrc:///avatar/animations/touch_thumb_point_closed_right.fbx",
|
||||
"startFrame": 15.0,
|
||||
"endFrame": 15.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "leftHandOverlay",
|
||||
"type": "overlay",
|
||||
"data": {
|
||||
"alpha": 0.0,
|
||||
"boneSet": "leftHand",
|
||||
"alphaVar": "leftHandOverlayAlpha"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "leftHandStateMachine",
|
||||
"id": "rightHandAnimNone",
|
||||
"type": "stateMachine",
|
||||
"data": {
|
||||
"currentState": "leftHandGrasp",
|
||||
"currentState": "rightHandGrasp",
|
||||
"states": [
|
||||
{
|
||||
"id": "leftHandGrasp",
|
||||
"id": "rightHandGrasp",
|
||||
"interpTarget": 3,
|
||||
"interpDuration": 3,
|
||||
"transitions": [
|
||||
{ "var": "isLeftIndexPoint", "state": "leftIndexPoint" },
|
||||
{ "var": "isLeftThumbRaise", "state": "leftThumbRaise" },
|
||||
{ "var": "isLeftIndexPointAndThumbRaise", "state": "leftIndexPointAndThumbRaise" }
|
||||
{ "var": "isRightIndexPoint", "state": "rightIndexPoint" },
|
||||
{ "var": "isRightThumbRaise", "state": "rightThumbRaise" },
|
||||
{ "var": "isRightIndexPointAndThumbRaise", "state": "rightIndexPointAndThumbRaise" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "leftIndexPoint",
|
||||
"id": "rightIndexPoint",
|
||||
"interpTarget": 15,
|
||||
"interpDuration": 3,
|
||||
"transitions": [
|
||||
{ "var": "isLeftHandGrasp", "state": "leftHandGrasp" },
|
||||
{ "var": "isLeftThumbRaise", "state": "leftThumbRaise" },
|
||||
{ "var": "isLeftIndexPointAndThumbRaise", "state": "leftIndexPointAndThumbRaise" }
|
||||
{ "var": "isRightHandGrasp", "state": "rightHandGrasp" },
|
||||
{ "var": "isRightThumbRaise", "state": "rightThumbRaise" },
|
||||
{ "var": "isRightIndexPointAndThumbRaise", "state": "rightIndexPointAndThumbRaise" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "leftThumbRaise",
|
||||
"id": "rightThumbRaise",
|
||||
"interpTarget": 15,
|
||||
"interpDuration": 3,
|
||||
"transitions": [
|
||||
{ "var": "isLeftHandGrasp", "state": "leftHandGrasp" },
|
||||
{ "var": "isLeftIndexPoint", "state": "leftIndexPoint" },
|
||||
{ "var": "isLeftIndexPointAndThumbRaise", "state": "leftIndexPointAndThumbRaise" }
|
||||
{ "var": "isRightHandGrasp", "state": "rightHandGrasp" },
|
||||
{ "var": "isRightIndexPoint", "state": "rightIndexPoint" },
|
||||
{ "var": "isRightIndexPointAndThumbRaise", "state": "rightIndexPointAndThumbRaise" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "leftIndexPointAndThumbRaise",
|
||||
"id": "rightIndexPointAndThumbRaise",
|
||||
"interpTarget": 15,
|
||||
"interpDuration": 3,
|
||||
"transitions": [
|
||||
{ "var": "isLeftHandGrasp", "state": "leftHandGrasp" },
|
||||
{ "var": "isLeftIndexPoint", "state": "leftIndexPoint" },
|
||||
{ "var": "isLeftThumbRaise", "state": "leftThumbRaise" }
|
||||
{ "var": "isRightHandGrasp", "state": "rightHandGrasp" },
|
||||
{ "var": "isRightIndexPoint", "state": "rightIndexPoint" },
|
||||
{ "var": "isRightThumbRaise", "state": "rightThumbRaise" }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "leftHandGrasp",
|
||||
"id": "rightHandGrasp",
|
||||
"type": "blendLinear",
|
||||
"data": {
|
||||
"alpha": 0.0,
|
||||
"alphaVar": "leftHandGraspAlpha"
|
||||
"alphaVar": "rightHandGraspAlpha"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "leftHandGraspOpen",
|
||||
"id": "rightHandGraspOpen",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "qrc:///avatar/animations/hydra_pose_open_left.fbx",
|
||||
"url": "qrc:///avatar/animations/hydra_pose_open_right.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 0.0,
|
||||
"timeScale": 1.0,
|
||||
|
@ -459,12 +299,12 @@
|
|||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "leftHandGraspClosed",
|
||||
"id": "rightHandGraspClosed",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "qrc:///avatar/animations/hydra_pose_closed_left.fbx",
|
||||
"startFrame": 10.0,
|
||||
"endFrame": 10.0,
|
||||
"url": "qrc:///avatar/animations/hydra_pose_closed_right.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 0.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
|
@ -473,18 +313,18 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"id": "leftIndexPoint",
|
||||
"id": "rightIndexPoint",
|
||||
"type": "blendLinear",
|
||||
"data": {
|
||||
"data": {
|
||||
"alpha": 0.0,
|
||||
"alphaVar": "leftHandGraspAlpha"
|
||||
"alphaVar": "rightHandGraspAlpha"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "leftIndexPointOpen",
|
||||
"id": "rightIndexPointOpen",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "qrc:///avatar/animations/touch_point_open_left.fbx",
|
||||
"url": "qrc:///avatar/animations/touch_point_open_right.fbx",
|
||||
"startFrame": 15.0,
|
||||
"endFrame": 15.0,
|
||||
"timeScale": 1.0,
|
||||
|
@ -493,10 +333,10 @@
|
|||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "leftIndexPointClosed",
|
||||
"id": "rightIndexPointClosed",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "qrc:///avatar/animations/touch_point_closed_left.fbx",
|
||||
"url": "qrc:///avatar/animations/touch_point_closed_right.fbx",
|
||||
"startFrame": 15.0,
|
||||
"endFrame": 15.0,
|
||||
"timeScale": 1.0,
|
||||
|
@ -507,18 +347,18 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"id": "leftThumbRaise",
|
||||
"id": "rightThumbRaise",
|
||||
"type": "blendLinear",
|
||||
"data": {
|
||||
"data": {
|
||||
"alpha": 0.0,
|
||||
"alphaVar": "leftHandGraspAlpha"
|
||||
"alphaVar": "rightHandGraspAlpha"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "leftThumbRaiseOpen",
|
||||
"id": "rightThumbRaiseOpen",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "qrc:///avatar/animations/touch_thumb_open_left.fbx",
|
||||
"url": "qrc:///avatar/animations/touch_thumb_open_right.fbx",
|
||||
"startFrame": 15.0,
|
||||
"endFrame": 15.0,
|
||||
"timeScale": 1.0,
|
||||
|
@ -527,10 +367,10 @@
|
|||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "leftThumbRaiseClosed",
|
||||
"id": "rightThumbRaiseClosed",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "qrc:///avatar/animations/touch_thumb_closed_left.fbx",
|
||||
"url": "qrc:///avatar/animations/touch_thumb_closed_right.fbx",
|
||||
"startFrame": 15.0,
|
||||
"endFrame": 15.0,
|
||||
"timeScale": 1.0,
|
||||
|
@ -541,18 +381,18 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"id": "leftIndexPointAndThumbRaise",
|
||||
"id": "rightIndexPointAndThumbRaise",
|
||||
"type": "blendLinear",
|
||||
"data": {
|
||||
"data": {
|
||||
"alpha": 0.0,
|
||||
"alphaVar": "leftHandGraspAlpha"
|
||||
"alphaVar": "rightHandGraspAlpha"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "leftIndexPointAndThumbRaiseOpen",
|
||||
"id": "rightIndexPointAndThumbRaiseOpen",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "qrc:///avatar/animations/touch_thumb_point_open_left.fbx",
|
||||
"url": "qrc:///avatar/animations/touch_thumb_point_open_right.fbx",
|
||||
"startFrame": 15.0,
|
||||
"endFrame": 15.0,
|
||||
"timeScale": 1.0,
|
||||
|
@ -561,10 +401,10 @@
|
|||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "leftIndexPointAndThumbRaiseClosed",
|
||||
"id": "rightIndexPointAndThumbRaiseClosed",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "qrc:///avatar/animations/touch_thumb_point_closed_left.fbx",
|
||||
"url": "qrc:///avatar/animations/touch_thumb_point_closed_right.fbx",
|
||||
"startFrame": 15.0,
|
||||
"endFrame": 15.0,
|
||||
"timeScale": 1.0,
|
||||
|
@ -577,6 +417,290 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"id": "rightHandAnimA",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "qrc:///avatar/animations/touch_thumb_point_open_right.fbx",
|
||||
"startFrame": 15.0,
|
||||
"endFrame": 15.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "rightHandAnimB",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "qrc:///avatar/animations/touch_thumb_point_open_right.fbx",
|
||||
"startFrame": 15.0,
|
||||
"endFrame": 15.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "leftHandOverlay",
|
||||
"type": "overlay",
|
||||
"data": {
|
||||
"alpha": 0.0,
|
||||
"boneSet": "leftHand",
|
||||
"alphaVar": "leftHandOverlayAlpha"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "leftHandStateMachine",
|
||||
"type": "stateMachine",
|
||||
"data": {
|
||||
"currentState": "leftHandAnimNone",
|
||||
"states": [
|
||||
{
|
||||
"id": "leftHandAnimNone",
|
||||
"interpTarget": 1,
|
||||
"interpDuration": 3,
|
||||
"transitions": [
|
||||
{ "var": "leftHandAnimA", "state": "leftHandAnimA" },
|
||||
{ "var": "leftHandAnimB", "state": "leftHandAnimB" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "leftHandAnimA",
|
||||
"interpTarget": 1,
|
||||
"interpDuration": 3,
|
||||
"transitions": [
|
||||
{ "var": "leftHandAnimNone", "state": "leftHandAnimNone" },
|
||||
{ "var": "leftHandAnimB", "state": "leftHandAnimB" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "leftHandAnimB",
|
||||
"interpTarget": 1,
|
||||
"interpDuration": 3,
|
||||
"transitions": [
|
||||
{ "var": "leftHandAnimNone", "state": "leftHandAnimNone" },
|
||||
{ "var": "leftHandAnimA", "state": "leftHandAnimA" }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "leftHandAnimNone",
|
||||
"type": "stateMachine",
|
||||
"data": {
|
||||
"currentState": "leftHandGrasp",
|
||||
"states": [
|
||||
{
|
||||
"id": "leftHandGrasp",
|
||||
"interpTarget": 3,
|
||||
"interpDuration": 3,
|
||||
"transitions": [
|
||||
{ "var": "isLeftIndexPoint", "state": "leftIndexPoint" },
|
||||
{ "var": "isLeftThumbRaise", "state": "leftThumbRaise" },
|
||||
{ "var": "isLeftIndexPointAndThumbRaise", "state": "leftIndexPointAndThumbRaise" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "leftIndexPoint",
|
||||
"interpTarget": 15,
|
||||
"interpDuration": 3,
|
||||
"transitions": [
|
||||
{ "var": "isLeftHandGrasp", "state": "leftHandGrasp" },
|
||||
{ "var": "isLeftThumbRaise", "state": "leftThumbRaise" },
|
||||
{ "var": "isLeftIndexPointAndThumbRaise", "state": "leftIndexPointAndThumbRaise" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "leftThumbRaise",
|
||||
"interpTarget": 15,
|
||||
"interpDuration": 3,
|
||||
"transitions": [
|
||||
{ "var": "isLeftHandGrasp", "state": "leftHandGrasp" },
|
||||
{ "var": "isLeftIndexPoint", "state": "leftIndexPoint" },
|
||||
{ "var": "isLeftIndexPointAndThumbRaise", "state": "leftIndexPointAndThumbRaise" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "leftIndexPointAndThumbRaise",
|
||||
"interpTarget": 15,
|
||||
"interpDuration": 3,
|
||||
"transitions": [
|
||||
{ "var": "isLeftHandGrasp", "state": "leftHandGrasp" },
|
||||
{ "var": "isLeftIndexPoint", "state": "leftIndexPoint" },
|
||||
{ "var": "isLeftThumbRaise", "state": "leftThumbRaise" }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "leftHandGrasp",
|
||||
"type": "blendLinear",
|
||||
"data": {
|
||||
"alpha": 0.0,
|
||||
"alphaVar": "leftHandGraspAlpha"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "leftHandGraspOpen",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "qrc:///avatar/animations/hydra_pose_open_left.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 0.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "leftHandGraspClosed",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "qrc:///avatar/animations/hydra_pose_closed_left.fbx",
|
||||
"startFrame": 10.0,
|
||||
"endFrame": 10.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "leftIndexPoint",
|
||||
"type": "blendLinear",
|
||||
"data": {
|
||||
"alpha": 0.0,
|
||||
"alphaVar": "leftHandGraspAlpha"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "leftIndexPointOpen",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "qrc:///avatar/animations/touch_point_open_left.fbx",
|
||||
"startFrame": 15.0,
|
||||
"endFrame": 15.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "leftIndexPointClosed",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "qrc:///avatar/animations/touch_point_closed_left.fbx",
|
||||
"startFrame": 15.0,
|
||||
"endFrame": 15.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "leftThumbRaise",
|
||||
"type": "blendLinear",
|
||||
"data": {
|
||||
"alpha": 0.0,
|
||||
"alphaVar": "leftHandGraspAlpha"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "leftThumbRaiseOpen",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "qrc:///avatar/animations/touch_thumb_open_left.fbx",
|
||||
"startFrame": 15.0,
|
||||
"endFrame": 15.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "leftThumbRaiseClosed",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "qrc:///avatar/animations/touch_thumb_closed_left.fbx",
|
||||
"startFrame": 15.0,
|
||||
"endFrame": 15.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "leftIndexPointAndThumbRaise",
|
||||
"type": "blendLinear",
|
||||
"data": {
|
||||
"alpha": 0.0,
|
||||
"alphaVar": "leftHandGraspAlpha"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "leftIndexPointAndThumbRaiseOpen",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "qrc:///avatar/animations/touch_thumb_point_open_left.fbx",
|
||||
"startFrame": 15.0,
|
||||
"endFrame": 15.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "leftIndexPointAndThumbRaiseClosed",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "qrc:///avatar/animations/touch_thumb_point_closed_left.fbx",
|
||||
"startFrame": 15.0,
|
||||
"endFrame": 15.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "leftHandAnimA",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "qrc:///avatar/animations/touch_thumb_point_open_left.fbx",
|
||||
"startFrame": 15.0,
|
||||
"endFrame": 15.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "leftHandAnimB",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "qrc:///avatar/animations/touch_thumb_point_open_left.fbx",
|
||||
"startFrame": 15.0,
|
||||
"endFrame": 15.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "mainStateMachine",
|
||||
"type": "stateMachine",
|
||||
"data": {
|
||||
|
@ -1594,4 +1718,4 @@
|
|||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,11 +13,11 @@
|
|||
|
||||
{ "from": "OculusTouch.LY", "to": "Standard.LY",
|
||||
"filters": [
|
||||
{ "type": "deadZone", "min": 0.7 },
|
||||
{ "type": "deadZone", "min": 0.15 },
|
||||
"invert"
|
||||
]
|
||||
},
|
||||
{ "from": "OculusTouch.LX", "filters": { "type": "deadZone", "min": 0.7 }, "to": "Standard.LX" },
|
||||
{ "from": "OculusTouch.LX", "filters": { "type": "deadZone", "min": 0.15 }, "to": "Standard.LX" },
|
||||
{ "from": "OculusTouch.LT", "to": "Standard.LTClick",
|
||||
"peek": true,
|
||||
"filters": [ { "type": "hysteresis", "min": 0.85, "max": 0.9 } ]
|
||||
|
@ -29,11 +29,11 @@
|
|||
|
||||
{ "from": "OculusTouch.RY", "to": "Standard.RY",
|
||||
"filters": [
|
||||
{ "type": "deadZone", "min": 0.7 },
|
||||
{ "type": "deadZone", "min": 0.15 },
|
||||
"invert"
|
||||
]
|
||||
},
|
||||
{ "from": "OculusTouch.RX", "filters": { "type": "deadZone", "min": 0.7 }, "to": "Standard.RX" },
|
||||
{ "from": "OculusTouch.RX", "filters": { "type": "deadZone", "min": 0.15 }, "to": "Standard.RX" },
|
||||
{ "from": "OculusTouch.RT", "to": "Standard.RTClick",
|
||||
"peek": true,
|
||||
"filters": [ { "type": "hysteresis", "min": 0.85, "max": 0.9 } ]
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
{
|
||||
"name": "Standard to Action",
|
||||
"channels": [
|
||||
{ "from": "Standard.LY", "to": "Actions.TranslateZ" },
|
||||
{ "from": "Standard.LY",
|
||||
"when": ["Application.RightHandDominant", "!Standard.RY"],
|
||||
"to": "Actions.TranslateZ"
|
||||
},
|
||||
|
||||
{ "from": "Standard.LX",
|
||||
"when": [
|
||||
"Application.InHMD", "!Application.AdvancedMovement",
|
||||
"Application.InHMD", "!Application.AdvancedMovement", "Application.RightHandDominant",
|
||||
"Application.SnapTurn", "!Standard.RX"
|
||||
],
|
||||
"to": "Actions.StepYaw",
|
||||
|
@ -18,14 +21,14 @@
|
|||
]
|
||||
},
|
||||
{ "from": "Standard.LX", "to": "Actions.TranslateX",
|
||||
"when": [ "Application.AdvancedMovement" ]
|
||||
"when": [ "Application.AdvancedMovement", "Application.StrafeEnabled", "Application.RightHandDominant" ]
|
||||
},
|
||||
{ "from": "Standard.LX", "to": "Actions.Yaw",
|
||||
"when": [ "!Application.AdvancedMovement", "!Application.SnapTurn" ]
|
||||
"when": [ "!Application.AdvancedMovement", "!Application.SnapTurn", "Application.RightHandDominant" ]
|
||||
},
|
||||
|
||||
{ "from": "Standard.RX",
|
||||
"when": [ "Application.SnapTurn" ],
|
||||
"when": [ "Application.SnapTurn", "Application.RightHandDominant" ],
|
||||
"to": "Actions.StepYaw",
|
||||
"filters":
|
||||
[
|
||||
|
@ -36,20 +39,69 @@
|
|||
]
|
||||
},
|
||||
{ "from": "Standard.RX", "to": "Actions.Yaw",
|
||||
"when": [ "!Application.SnapTurn" ]
|
||||
"when": [ "!Application.SnapTurn", "Application.RightHandDominant" ]
|
||||
},
|
||||
|
||||
{ "from": "Standard.LeftSecondaryThumb",
|
||||
"when": [ "Application.Grounded", "Application.LeftHandDominant" ],
|
||||
"to": "Actions.Up"
|
||||
},
|
||||
|
||||
{ "from": "Standard.LeftSecondaryThumb",
|
||||
"when": "Application.LeftHandDominant",
|
||||
"to": "Actions.Up"
|
||||
},
|
||||
|
||||
{ "from": "Standard.RY",
|
||||
"when": "Application.Grounded",
|
||||
"to": "Actions.Up",
|
||||
"when": ["Application.LeftHandDominant", "!Standard.LY"],
|
||||
"to": "Actions.TranslateZ"
|
||||
},
|
||||
|
||||
{ "from": "Standard.RX",
|
||||
"when": [
|
||||
"Application.InHMD", "!Application.AdvancedMovement", "Application.LeftHandDominant",
|
||||
"Application.SnapTurn", "!Standard.RX"
|
||||
],
|
||||
"to": "Actions.StepYaw",
|
||||
"filters":
|
||||
[
|
||||
{ "type": "deadZone", "min": 0.6 },
|
||||
"invert"
|
||||
{ "type": "deadZone", "min": 0.15 },
|
||||
"constrainToInteger",
|
||||
{ "type": "pulse", "interval": 0.25 },
|
||||
{ "type": "scale", "scale": 22.5 }
|
||||
]
|
||||
},
|
||||
{ "from": "Standard.RX", "to": "Actions.TranslateX",
|
||||
"when": [ "Application.AdvancedMovement", "Application.StrafeEnabled", "Application.LeftHandDominant" ]
|
||||
},
|
||||
{ "from": "Standard.RX", "to": "Actions.Yaw",
|
||||
"when": [ "!Application.AdvancedMovement", "!Application.SnapTurn", "Application.LeftHandDominant" ]
|
||||
},
|
||||
|
||||
{ "from": "Standard.RY", "to": "Actions.Up", "filters": "invert"},
|
||||
{ "from": "Standard.LX",
|
||||
"when": [ "Application.SnapTurn", "Application.LeftHandDominant" ],
|
||||
"to": "Actions.StepYaw",
|
||||
"filters":
|
||||
[
|
||||
{ "type": "deadZone", "min": 0.15 },
|
||||
"constrainToInteger",
|
||||
{ "type": "pulse", "interval": 0.25 },
|
||||
{ "type": "scale", "scale": 22.5 }
|
||||
]
|
||||
},
|
||||
{ "from": "Standard.LX", "to": "Actions.Yaw",
|
||||
"when": [ "!Application.SnapTurn", "Application.LeftHandDominant" ]
|
||||
},
|
||||
|
||||
{ "from": "Standard.RightSecondaryThumb",
|
||||
"when": [ "Application.Grounded", "Application.RightHandDominant" ],
|
||||
"to": "Actions.Up"
|
||||
},
|
||||
|
||||
{ "from": "Standard.RightSecondaryThumb",
|
||||
"when": "Application.RightHandDominant",
|
||||
"to": "Actions.Up"
|
||||
},
|
||||
|
||||
{ "from": "Standard.Back", "to": "Actions.CycleCamera" },
|
||||
{ "from": "Standard.Start", "to": "Actions.ContextMenu" },
|
||||
|
@ -128,4 +180,4 @@
|
|||
{ "from": "Standard.TrackedObject14", "to" : "Actions.TrackedObject14" },
|
||||
{ "from": "Standard.TrackedObject15", "to" : "Actions.TrackedObject15" }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,8 +10,9 @@
|
|||
"filters": [ { "type": "hysteresis", "min": 0.7, "max": 0.75 } ]
|
||||
},
|
||||
|
||||
{ "from": "Vive.LY", "when": "Vive.LSY", "filters": ["invert"], "to": "Standard.LY" },
|
||||
{ "from": "Vive.LX", "when": "Vive.LSX", "to": "Standard.LX" },
|
||||
{ "from": "Vive.LY", "when": "Vive.LS", "filters": [ { "type": "deadZone", "min": 0.15 }, "invert" ], "to": "Standard.LY" },
|
||||
{ "from": "Vive.LX", "when": ["Vive.LS", "Application.RightHandDominant"], "filters": { "type": "deadZone", "min": 0.15 }, "to": "Standard.LX" },
|
||||
{ "from": "Vive.LX", "when": ["Vive.LS", "Vive.LSX", "!Vive.LSY", "Application.LeftHandDominant"], "filters": { "type": "deadZone", "min": 0.15 }, "to": "Standard.LX" },
|
||||
{
|
||||
"from": "Vive.LT", "to": "Standard.LT",
|
||||
"filters": [
|
||||
|
@ -28,8 +29,9 @@
|
|||
},
|
||||
{ "from": "Vive.LSTouch", "to": "Standard.LSTouch" },
|
||||
|
||||
{ "from": "Vive.RY", "when": "Vive.RSY", "filters": ["invert"], "to": "Standard.RY" },
|
||||
{ "from": "Vive.RX", "when": "Vive.RSX", "to": "Standard.RX" },
|
||||
{ "from": "Vive.RY", "when": "Vive.RS", "filters": [ { "type": "deadZone", "min": 0.15 }, "invert" ], "to": "Standard.RY" },
|
||||
{ "from": "Vive.RX", "when": ["Vive.RS", "Application.LeftHandDominant"], "filters": { "type": "deadZone", "min": 0.15 }, "to": "Standard.RX" },
|
||||
{ "from": "Vive.RX", "when": ["Vive.RS", "Vive.RSX", "!Vive.RSY", "Application.RightHandDominant"], "filters": { "type": "deadZone", "min": 0.15 }, "to": "Standard.RX" },
|
||||
{
|
||||
"from": "Vive.RT", "to": "Standard.RT",
|
||||
"filters": [
|
||||
|
|
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 246 KiB |
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 331 KiB |
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 308 KiB |
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 229 KiB |
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 267 KiB |
|
@ -53,6 +53,16 @@
|
|||
position: absolute;
|
||||
top: 0; left: 0; bottom: 0; right: 0;
|
||||
}
|
||||
|
||||
#image_button {
|
||||
position: absolute;
|
||||
width: 463;
|
||||
height: 410;
|
||||
top: 155;
|
||||
left: 8;
|
||||
right: 8;
|
||||
bottom: 146;
|
||||
}
|
||||
|
||||
#report_problem {
|
||||
position: fixed;
|
||||
|
@ -67,17 +77,23 @@
|
|||
var handControllerImageURL = null;
|
||||
var index = 0;
|
||||
var count = 3;
|
||||
var handControllerRefURL = "https://docs.highfidelity.com/en/rc81/explore/get-started/vr-controls.html#vr-controls";
|
||||
var keyboardRefURL = "https://docs.highfidelity.com/en/rc81/explore/get-started/desktop.html#movement-controls";
|
||||
var gamepadRefURL = "https://docs.highfidelity.com/en/rc81/explore/get-started/vr-controls.html#gamepad";
|
||||
|
||||
function showKbm() {
|
||||
document.getElementById("main_image").setAttribute("src", "img/tablet-help-keyboard.jpg");
|
||||
document.getElementById("image_button").setAttribute("href", keyboardRefURL);
|
||||
}
|
||||
|
||||
function showHandControllers() {
|
||||
document.getElementById("main_image").setAttribute("src", handControllerImageURL);
|
||||
document.getElementById("image_button").setAttribute("href", handControllerRefURL);
|
||||
}
|
||||
|
||||
function showGamepad() {
|
||||
document.getElementById("main_image").setAttribute("src", "img/tablet-help-gamepad.jpg");
|
||||
document.getElementById("image_button").setAttribute("href", gamepadRefURL);
|
||||
}
|
||||
|
||||
function cycleRight() {
|
||||
|
@ -171,6 +187,7 @@
|
|||
<img id="main_image" src="img/tablet-help-keyboard.jpg" width="480px" height="720px"></img>
|
||||
<a href="#" id="left_button" onmousedown="cycleLeft()"></a>
|
||||
<a href="#" id="right_button" onmousedown="cycleRight()"></a>
|
||||
<a href="#" id="image_button"></a>
|
||||
</div>
|
||||
<a href="mailto:support@highfidelity.com" id="report_problem">Report Problem</a>
|
||||
</body>
|
||||
|
|
10
interface/resources/icons/tablet-icons/mic-clip-i.svg
Normal file
|
@ -0,0 +1,10 @@
|
|||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M25.3383 4.13446L23.8713 5.60152C21.9374 3.46761 19.2033 2.06723 16.0691 2.06723C13.0683 2.06723 10.4009 3.40093 8.46707 5.40147L7 3.9344C9.26728 1.53375 12.5348 0 16.0691 0C19.7368 0 23.071 1.60044 25.3383 4.13446ZM21.9376 7.53584L20.4705 9.0029C19.4703 7.66921 17.8698 6.86899 16.0693 6.86899C14.4022 6.86899 12.9351 7.66921 11.8682 8.80285L10.4011 7.33578C11.8015 5.80203 13.802 4.80176 16.0693 4.80176C18.4033 4.80176 20.5372 5.86871 21.9376 7.53584ZM17.9575 30.1572C17.9575 31.1771 17.1307 32.0039 16.1108 32.0039C15.0909 32.0039 14.2642 31.1771 14.2642 30.1572C14.2642 29.1373 15.0909 28.3105 16.1108 28.3105C17.1307 28.3105 17.9575 29.1373 17.9575 30.1572ZM18.3632 11.0801H14.1597L15.0116 25.8539H17.4867L18.3632 11.0801Z" fill="#EA4C5F"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="32" height="32" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 1 KiB |
3
interface/resources/icons/tablet-icons/mic-gate-i.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.1999 4.72587C17.5049 4.72587 18.5628 3.66795 18.5628 2.36294C18.5628 1.05792 17.5049 0 16.1999 0C14.8948 0 13.8369 1.05792 13.8369 2.36294C13.8369 3.66795 14.8948 4.72587 16.1999 4.72587ZM18.6667 11.8004V14.7337H13.7334V11.8004C13.7334 10.467 14.8667 9.40039 16.2 9.40039C17.6 9.40039 18.6667 10.467 18.6667 11.8004ZM13.7334 20.1332V17.2666H18.6667V20.1332C18.6667 21.4665 17.5333 22.5332 16.2 22.5332C14.8667 22.5332 13.7334 21.4665 13.7334 20.1332ZM23.6665 20.6V17.0667C23.6665 16.4 23.0665 15.9334 22.4665 15.9334C21.7998 15.9334 21.3332 16.4667 21.3332 17.1333V20.6C21.3332 23.0666 19.0665 25.0666 16.3332 25.0666C13.5999 25.0666 11.3333 23.0666 11.3333 20.6V17.0667C11.3333 16.4 10.8666 15.8667 10.2666 15.8667C9.59999 15.8 9 16.2667 9 16.9333V20.6C9 23.9999 11.6666 26.7999 15.1333 27.3332V29.5998H12.2666C11.6 29.5998 11.0666 30.1332 11.0666 30.7998C11.0666 31.4665 11.6 31.9998 12.2666 31.9998H20.4665C21.1332 31.9998 21.6665 31.4665 21.6665 30.7998C21.6665 30.1332 21.1332 29.5998 20.4665 29.5998H17.5332V27.3332C20.9998 26.7332 23.6665 23.9999 23.6665 20.6Z" fill="#00B4EF" fill-opacity="0.7"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
|
@ -1,25 +1,3 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1"
|
||||
id="svg2" inkscape:version="0.91 r13725" sodipodi:docname="mic-mute-a.svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 50 50"
|
||||
style="enable-background:new 0 0 50 50;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#EA4C5F;}
|
||||
</style>
|
||||
<sodipodi:namedview bordercolor="#666666" borderopacity="1" gridtolerance="10" guidetolerance="10" id="namedview18" inkscape:current-layer="svg2" inkscape:cx="25" inkscape:cy="25" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-height="480" inkscape:window-maximized="0" inkscape:window-width="852" inkscape:window-x="0" inkscape:window-y="0" inkscape:zoom="4.72" objecttolerance="10" pagecolor="#ff0000" showgrid="false">
|
||||
</sodipodi:namedview>
|
||||
<g id="Layer_2">
|
||||
</g>
|
||||
<g id="Layer_1">
|
||||
<path id="path8" class="st0" d="M28.9,17.1v-0.5c0-2-1.7-3.6-3.7-3.6l0,0c-2,0-3.7,1.6-3.7,3.6v6.9L28.9,17.1z"/>
|
||||
<path id="path10" class="st0" d="M21.5,29.2v0.2c0,2,1.6,3.6,3.7,3.6l0,0c2,0,3.7-1.6,3.7-3.6v-6.6L21.5,29.2z"/>
|
||||
<path id="path12" class="st0" d="M39.1,16.8L13.6,39.1c-0.7,0.6-1.8,0.5-2.4-0.2L11,38.7c-0.6-0.7-0.5-1.8,0.2-2.4l25.4-22.4
|
||||
c0.7-0.6,1.8-0.5,2.4,0.2l0.2,0.2C39.8,15.1,39.7,16.1,39.1,16.8z"/>
|
||||
<path id="path14" class="st0" d="M23.4,40.2v3.4h-4.3c-1,0-1.8,0.8-1.8,1.8s0.8,1.8,1.8,1.8h12.3c1,0,1.8-0.8,1.8-1.8
|
||||
s-0.8-1.8-1.8-1.8H27v-3.4c5.2-0.8,9.2-5,9.2-10.1c0-0.1,0-5.1,0-5.3c0-1-0.9-1.7-1.8-1.7c-1,0-1.7,0.8-1.7,1.8c0,0.3,0,4.8,0,5.2
|
||||
c0,3.7-3.4,6.7-7.5,6.7c-3.6,0-6.7-2.3-7.3-5.4L15,34C16.4,37.2,19.6,39.7,23.4,40.2z"/>
|
||||
<path id="path16" class="st0" d="M17.7,24.9c0-1-0.7-1.8-1.6-1.8c-1-0.1-1.8,0.7-1.9,1.6c0,0.2,0,4.2,0,5.3l3.5-3.1
|
||||
C17.7,25.9,17.7,25,17.7,24.9z"/>
|
||||
</g>
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M25.6441 4.13328L24.1775 5.59992C22.2442 3.46662 19.5109 2.06664 16.3776 2.06664C13.3776 2.06664 10.711 3.39995 8.77768 5.39993L7.31104 3.93328C9.57767 1.53331 12.8443 0 16.3776 0C20.0442 0 23.3775 1.59998 25.6441 4.13328ZM20.7777 9.00072L22.2444 7.53408C20.8444 5.86743 18.7111 4.80078 16.3778 4.80078C14.1111 4.80078 12.1112 5.80077 10.7112 7.33408L12.1778 8.80073C13.2445 7.66741 14.7111 6.86742 16.3778 6.86742C18.1777 6.86742 19.7777 7.66741 20.7777 9.00072ZM18.8803 12.0758V11.7496C18.8803 10.4445 17.7763 9.40039 16.4775 9.40039C15.1787 9.40039 14.0747 10.4445 14.0747 11.7496V16.2521L18.8803 12.0758ZM14.543 21.5129L12.6113 23.2103C13.4959 24.2599 14.9141 24.9311 16.4774 24.9311C19.14 24.9311 21.348 22.9735 21.348 20.559V17.1658C21.348 16.5132 21.8026 15.9912 22.452 15.9912C23.0364 15.9912 23.6209 16.448 23.6209 17.1005V20.559C23.6209 23.887 21.0233 26.6277 17.6464 27.1498V29.3684H20.5038C21.1532 29.3684 21.6727 29.8905 21.6727 30.543C21.6727 31.1956 21.1532 31.7176 20.5038 31.7176H12.5161C11.8667 31.7176 11.3471 31.1956 11.3471 30.543C11.3471 29.8905 11.8667 29.3684 12.5161 29.3684H15.3085V27.1498C13.5328 26.915 11.9589 26.0045 10.8771 24.7343L8.9443 26.4327C8.48972 26.8242 7.77537 26.759 7.38573 26.3022L7.25585 26.1717C6.8662 25.7149 6.93114 24.9971 7.38573 24.6056L23.8806 9.98853C24.3352 9.597 25.0495 9.66226 25.4392 10.119L25.5691 10.2495C25.9587 10.7716 25.8938 11.4241 25.5041 11.8809L18.8803 17.7015V20.1017C18.8803 21.4068 17.7763 22.4509 16.4775 22.4509C15.6689 22.4509 14.9744 22.0838 14.543 21.5129ZM10.5679 15.9919C11.1523 15.9919 11.6069 16.5139 11.6069 17.1664V18.4715L9.33398 20.4944V17.0359C9.39892 16.4486 9.91845 15.9266 10.5679 15.9919Z" fill="#EA4C5F"/>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 1.8 KiB |
|
@ -1,25 +1,3 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1"
|
||||
id="svg2" inkscape:version="0.91 r13725" sodipodi:docname="mic-mute-a.svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 50 50"
|
||||
style="enable-background:new 0 0 50 50;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#EA4C5F;}
|
||||
</style>
|
||||
<sodipodi:namedview bordercolor="#666666" borderopacity="1" gridtolerance="10" guidetolerance="10" id="namedview18" inkscape:current-layer="svg2" inkscape:cx="25" inkscape:cy="25" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-height="480" inkscape:window-maximized="0" inkscape:window-width="852" inkscape:window-x="0" inkscape:window-y="0" inkscape:zoom="4.72" objecttolerance="10" pagecolor="#ff0000" showgrid="false">
|
||||
</sodipodi:namedview>
|
||||
<g id="Layer_2">
|
||||
</g>
|
||||
<g id="Layer_1">
|
||||
<path id="path8" class="st0" d="M28.9,17.1v-0.5c0-2-1.7-3.6-3.7-3.6l0,0c-2,0-3.7,1.6-3.7,3.6v6.9L28.9,17.1z"/>
|
||||
<path id="path10" class="st0" d="M21.5,29.2v0.2c0,2,1.6,3.6,3.7,3.6l0,0c2,0,3.7-1.6,3.7-3.6v-6.6L21.5,29.2z"/>
|
||||
<path id="path12" class="st0" d="M39.1,16.8L13.6,39.1c-0.7,0.6-1.8,0.5-2.4-0.2L11,38.7c-0.6-0.7-0.5-1.8,0.2-2.4l25.4-22.4
|
||||
c0.7-0.6,1.8-0.5,2.4,0.2l0.2,0.2C39.8,15.1,39.7,16.1,39.1,16.8z"/>
|
||||
<path id="path14" class="st0" d="M23.4,40.2v3.4h-4.3c-1,0-1.8,0.8-1.8,1.8s0.8,1.8,1.8,1.8h12.3c1,0,1.8-0.8,1.8-1.8
|
||||
s-0.8-1.8-1.8-1.8H27v-3.4c5.2-0.8,9.2-5,9.2-10.1c0-0.1,0-5.1,0-5.3c0-1-0.9-1.7-1.8-1.7c-1,0-1.7,0.8-1.7,1.8c0,0.3,0,4.8,0,5.2
|
||||
c0,3.7-3.4,6.7-7.5,6.7c-3.6,0-6.7-2.3-7.3-5.4L15,34C16.4,37.2,19.6,39.7,23.4,40.2z"/>
|
||||
<path id="path16" class="st0" d="M17.7,24.9c0-1-0.7-1.8-1.6-1.8c-1-0.1-1.8,0.7-1.9,1.6c0,0.2,0,4.2,0,5.3l3.5-3.1
|
||||
C17.7,25.9,17.7,25,17.7,24.9z"/>
|
||||
</g>
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M25.6441 4.13328L24.1775 5.59992C22.2442 3.46662 19.5109 2.06664 16.3776 2.06664C13.3776 2.06664 10.711 3.39995 8.77768 5.39993L7.31104 3.93328C9.57767 1.53331 12.8443 0 16.3776 0C20.0442 0 23.3775 1.59998 25.6441 4.13328ZM20.7777 9.00072L22.2444 7.53408C20.8444 5.86743 18.7111 4.80078 16.3778 4.80078C14.1111 4.80078 12.1112 5.80077 10.7112 7.33408L12.1778 8.80073C13.2445 7.66741 14.7111 6.86742 16.3778 6.86742C18.1777 6.86742 19.7777 7.66741 20.7777 9.00072ZM18.8803 12.0758V11.7496C18.8803 10.4445 17.7763 9.40039 16.4775 9.40039C15.1787 9.40039 14.0747 10.4445 14.0747 11.7496V16.2521L18.8803 12.0758ZM14.543 21.5129L12.6113 23.2103C13.4959 24.2599 14.9141 24.9311 16.4774 24.9311C19.14 24.9311 21.348 22.9735 21.348 20.559V17.1658C21.348 16.5132 21.8026 15.9912 22.452 15.9912C23.0364 15.9912 23.6209 16.448 23.6209 17.1005V20.559C23.6209 23.887 21.0233 26.6277 17.6464 27.1498V29.3684H20.5038C21.1532 29.3684 21.6727 29.8905 21.6727 30.543C21.6727 31.1956 21.1532 31.7176 20.5038 31.7176H12.5161C11.8667 31.7176 11.3471 31.1956 11.3471 30.543C11.3471 29.8905 11.8667 29.3684 12.5161 29.3684H15.3085V27.1498C13.5328 26.915 11.9589 26.0045 10.8771 24.7343L8.9443 26.4327C8.48972 26.8242 7.77537 26.759 7.38573 26.3022L7.25585 26.1717C6.8662 25.7149 6.93114 24.9971 7.38573 24.6056L23.8806 9.98853C24.3352 9.597 25.0495 9.66226 25.4392 10.119L25.5691 10.2495C25.9587 10.7716 25.8938 11.4241 25.5041 11.8809L18.8803 17.7015V20.1017C18.8803 21.4068 17.7763 22.4509 16.4775 22.4509C15.6689 22.4509 14.9744 22.0838 14.543 21.5129ZM10.5679 15.9919C11.1523 15.9919 11.6069 16.5139 11.6069 17.1664V18.4715L9.33398 20.4944V17.0359C9.39893 16.4486 9.91845 15.9266 10.5679 15.9919Z" fill="#EA4C5F"/>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 1.8 KiB |
|
@ -1,60 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="Layer_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 40 40"
|
||||
style="enable-background:new 0 0 40 40;"
|
||||
xml:space="preserve"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="mic-mute.svg"><metadata
|
||||
id="metadata6958"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs6956" /><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1824"
|
||||
inkscape:window-height="1057"
|
||||
id="namedview6954"
|
||||
showgrid="false"
|
||||
inkscape:zoom="5.9"
|
||||
inkscape:cx="-40.338983"
|
||||
inkscape:cy="20"
|
||||
inkscape:window-x="88"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Layer_1" /><style
|
||||
type="text/css"
|
||||
id="style6942">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style><ellipse
|
||||
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path8048"
|
||||
cx="20.1"
|
||||
cy="20.5"
|
||||
rx="15.967586"
|
||||
ry="15.967585" /><rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#feffff;stroke-width:0;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect8065"
|
||||
width="30.1991"
|
||||
height="2.9999897"
|
||||
x="13.432917"
|
||||
y="-1.2235159"
|
||||
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)" /></svg>
|
Before Width: | Height: | Size: 2.2 KiB |
|
@ -1,70 +1,3 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 50 50"
|
||||
style="enable-background:new 0 0 50 50;"
|
||||
xml:space="preserve"
|
||||
id="svg2"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="mic-unmute-a.svg"><metadata
|
||||
id="metadata22"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs20" /><sodipodi:namedview
|
||||
pagecolor="#ff0000"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="852"
|
||||
inkscape:window-height="480"
|
||||
id="namedview18"
|
||||
showgrid="false"
|
||||
inkscape:zoom="4.72"
|
||||
inkscape:cx="25"
|
||||
inkscape:cy="25"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg2" /><style
|
||||
type="text/css"
|
||||
id="style4">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style><g
|
||||
id="Layer_2" /><g
|
||||
id="Layer_1"
|
||||
style="fill:#000000;fill-opacity:1"><path
|
||||
class="st0"
|
||||
d="M31.4,14.1l2.2-2.2c-2.1-2.5-5.3-4.1-8.8-4.1c-3.4,0-6.4,1.5-8.5,3.8c0.7,0.7,1.5,1.5,2.2,2.2 c1.6-1.7,3.8-2.9,6.3-2.9C27.5,10.9,29.9,12.1,31.4,14.1z"
|
||||
id="path8"
|
||||
style="fill:#000000;fill-opacity:1" /><path
|
||||
class="st0"
|
||||
d="M36.5,9l2.2-2.2C35.3,3,30.3,0.6,24.8,0.6c-5.3,0-10.2,2.3-13.6,5.9c0.7,0.7,1.5,1.5,2.2,2.2 c2.9-3,6.9-5,11.4-5C29.5,3.7,33.6,5.8,36.5,9z"
|
||||
id="path10"
|
||||
style="fill:#000000;fill-opacity:1" /><path
|
||||
class="st0"
|
||||
d="M28.5,22.7v-4.4c0-2-1.6-3.6-3.7-3.6h0c-2,0-3.7,1.6-3.7,3.6v4.4H28.5z"
|
||||
id="path12"
|
||||
style="fill:#000000;fill-opacity:1" /><path
|
||||
class="st0"
|
||||
d="M21.1,26.5v4.3c0,2,1.7,3.6,3.7,3.6h0c2,0,3.7-1.6,3.7-3.6v-4.3H21.1z"
|
||||
id="path14"
|
||||
style="fill:#000000;fill-opacity:1" /><path
|
||||
class="st0"
|
||||
d="M36,31.5c0-0.1,0-5.1,0-5.3c0-1-0.9-1.7-1.8-1.7c-1,0-1.7,0.8-1.7,1.8c0,0.3,0,4.8,0,5.2 c0,3.7-3.4,6.7-7.5,6.7c-4.1,0-7.5-3-7.5-6.7c0-0.4,0-4.9,0-5.3c0-1-0.7-1.8-1.6-1.8C14.9,24.3,14,25,14,26c0,0.3,0,5.4,0,5.5 c0,5.1,4,9.3,9.2,10.1l0,3.4h-4.3c-1,0-1.8,0.8-1.8,1.8c0,1,0.8,1.8,1.8,1.8h12.3c1,0,1.8-0.8,1.8-1.8c0-1-0.8-1.8-1.8-1.8h-4.4 l0-3.4C32,40.7,36,36.6,36,31.5z"
|
||||
id="path16"
|
||||
style="fill:#000000;fill-opacity:1" /></g></svg>
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M23.8664 5.59992L25.3331 4.13328C23.0664 1.59998 19.7332 0 16.0665 0C12.5333 0 9.26664 1.53331 7 3.93328L8.46665 5.39993C10.4 3.39995 13.0666 2.06664 16.0665 2.06664C19.1998 2.06664 21.9331 3.46662 23.8664 5.59992ZM20.4664 8.99975L21.9331 7.5331C20.5331 5.86646 18.3998 4.7998 16.0665 4.7998C13.7999 4.7998 11.7999 5.79979 10.3999 7.3331L11.8665 8.79975C12.9332 7.66643 14.3998 6.86644 16.0665 6.86644C17.8665 6.86644 19.4664 7.66643 20.4664 8.99975ZM18.5334 11.8004V14.7337H13.6001V11.8004C13.6001 10.467 14.7334 9.40039 16.0667 9.40039C17.4667 9.40039 18.5334 10.467 18.5334 11.8004ZM13.6001 17.2666V20.1332C13.6001 21.4665 14.7334 22.5332 16.0667 22.5332C17.4 22.5332 18.5334 21.4665 18.5334 20.1332V17.2666H13.6001ZM23.5332 17.0667V20.6C23.5332 23.9999 20.8665 26.7332 17.3999 27.3332V29.5998H20.3332C20.9999 29.5998 21.5332 30.1332 21.5332 30.7998C21.5332 31.4665 20.9999 31.9998 20.3332 31.9998H12.1333C11.4667 31.9998 10.9333 31.4665 10.9333 30.7998C10.9333 30.1332 11.4667 29.5998 12.1333 29.5998H14.9999V27.3332C11.5333 26.7999 8.8667 23.9999 8.8667 20.6V16.9333C8.8667 16.2667 9.46669 15.8 10.1333 15.8667C10.7333 15.8667 11.2 16.4 11.2 17.0667V20.6C11.2 23.0666 13.4666 25.0666 16.1999 25.0666C18.9332 25.0666 21.1999 23.0666 21.1999 20.6V17.1333C21.1999 16.4667 21.6665 15.9334 22.3332 15.9334C22.9332 15.9334 23.5332 16.4 23.5332 17.0667Z" fill="white"/>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 1.5 KiB |
|
@ -1,22 +1,3 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g id="Layer_2">
|
||||
</g>
|
||||
<g id="Layer_1">
|
||||
<path class="st0" d="M31.4,14.1l2.2-2.2c-2.1-2.5-5.3-4.1-8.8-4.1c-3.4,0-6.4,1.5-8.5,3.8c0.7,0.7,1.5,1.5,2.2,2.2
|
||||
c1.6-1.7,3.8-2.9,6.3-2.9C27.5,10.9,29.9,12.1,31.4,14.1z"/>
|
||||
<path class="st0" d="M36.5,9l2.2-2.2C35.3,3,30.3,0.6,24.8,0.6c-5.3,0-10.2,2.3-13.6,5.9c0.7,0.7,1.5,1.5,2.2,2.2
|
||||
c2.9-3,6.9-5,11.4-5C29.5,3.7,33.6,5.8,36.5,9z"/>
|
||||
<path class="st0" d="M28.5,22.7v-4.4c0-2-1.6-3.6-3.7-3.6h0c-2,0-3.7,1.6-3.7,3.6v4.4H28.5z"/>
|
||||
<path class="st0" d="M21.1,26.5v4.3c0,2,1.7,3.6,3.7,3.6h0c2,0,3.7-1.6,3.7-3.6v-4.3H21.1z"/>
|
||||
<path class="st0" d="M36,31.5c0-0.1,0-5.1,0-5.3c0-1-0.9-1.7-1.8-1.7c-1,0-1.7,0.8-1.7,1.8c0,0.3,0,4.8,0,5.2
|
||||
c0,3.7-3.4,6.7-7.5,6.7c-4.1,0-7.5-3-7.5-6.7c0-0.4,0-4.9,0-5.3c0-1-0.7-1.8-1.6-1.8C14.9,24.3,14,25,14,26c0,0.3,0,5.4,0,5.5
|
||||
c0,5.1,4,9.3,9.2,10.1l0,3.4h-4.3c-1,0-1.8,0.8-1.8,1.8c0,1,0.8,1.8,1.8,1.8h12.3c1,0,1.8-0.8,1.8-1.8c0-1-0.8-1.8-1.8-1.8h-4.4
|
||||
l0-3.4C32,40.7,36,36.6,36,31.5z"/>
|
||||
</g>
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M23.8664 5.59992L25.3331 4.13328C23.0664 1.59998 19.7332 0 16.0665 0C12.5333 0 9.26664 1.53331 7 3.93328L8.46665 5.39993C10.4 3.39995 13.0666 2.06664 16.0665 2.06664C19.1998 2.06664 21.9331 3.46662 23.8664 5.59992ZM20.4664 8.99975L21.9331 7.5331C20.5331 5.86646 18.3998 4.7998 16.0665 4.7998C13.7999 4.7998 11.7999 5.79979 10.3999 7.3331L11.8665 8.79975C12.9332 7.66643 14.3998 6.86644 16.0665 6.86644C17.8665 6.86644 19.4664 7.66643 20.4664 8.99975ZM18.5334 11.8004V14.7337H13.6001V11.8004C13.6001 10.467 14.7334 9.40039 16.0667 9.40039C17.4667 9.40039 18.5334 10.467 18.5334 11.8004ZM13.6001 17.2666V20.1332C13.6001 21.4665 14.7334 22.5332 16.0667 22.5332C17.4 22.5332 18.5334 21.4665 18.5334 20.1332V17.2666H13.6001ZM23.5332 17.0667V20.6C23.5332 23.9999 20.8665 26.7332 17.3999 27.3332V29.5998H20.3332C20.9999 29.5998 21.5332 30.1332 21.5332 30.7998C21.5332 31.4665 20.9999 31.9998 20.3332 31.9998H12.1333C11.4667 31.9998 10.9333 31.4665 10.9333 30.7998C10.9333 30.1332 11.4667 29.5998 12.1333 29.5998H14.9999V27.3332C11.5333 26.7999 8.8667 23.9999 8.8667 20.6V16.9333C8.8667 16.2667 9.46669 15.8 10.1333 15.8667C10.7333 15.8667 11.2 16.4 11.2 17.0667V20.6C11.2 23.0666 13.4666 25.0666 16.1999 25.0666C18.9332 25.0666 21.1999 23.0666 21.1999 20.6V17.1333C21.1999 16.4667 21.6665 15.9334 22.3332 15.9334C22.9332 15.9334 23.5332 16.4 23.5332 17.0667Z" fill="white"/>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.5 KiB |
|
@ -59,4 +59,4 @@
|
|||
d="m 27.9,20.9 c 0,0 0,-3.6 0,-3.8 0,-0.7 -0.6,-1.2 -1.3,-1.2 -0.7,0 -1.2,0.6 -1.2,1.3 0,0.2 0,3.4 0,3.7 0,2.6 -2.4,4.8 -5.3,4.8 -2.9,0 -5.3,-2.1 -5.3,-4.8 0,-0.3 0,-3.5 0,-3.8 0,-0.7 -0.5,-1.3 -1.2,-1.3 -0.7,0 -1.3,0.5 -1.3,1.2 0,0.2 0,3.9 0,3.9 0,3.6 2.9,6.6 6.6,7.2 l 0,2.4 -3.1,0 c -0.7,0 -1.3,0.6 -1.3,1.3 0,0.7 0.6,1.3 1.3,1.3 l 8.8,0 c 0.7,0 1.3,-0.6 1.3,-1.3 0,-0.7 -0.6,-1.3 -1.3,-1.3 l -3.2,0 0,-2.4 c 3.6,-0.5 6.5,-3.5 6.5,-7.2 z"
|
||||
id="path6952"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#ffffff" /></svg>
|
||||
style="fill:#ffffff" /></svg>
|
||||
|
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
|
@ -7,24 +7,57 @@
|
|||
//
|
||||
|
||||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.4
|
||||
import QtQuick 2.5
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
import "./hifi/audio" as HifiAudio
|
||||
|
||||
import TabletScriptingInterface 1.0
|
||||
|
||||
Item {
|
||||
id: root;
|
||||
objectName: "AvatarInputsBar"
|
||||
property int modality: Qt.NonModal
|
||||
width: audio.width;
|
||||
height: audio.height;
|
||||
x: 10; y: 5;
|
||||
|
||||
readonly property bool ignoreRadiusEnabled: AvatarInputs.ignoreRadiusEnabled;
|
||||
x: 10;
|
||||
y: 5;
|
||||
readonly property bool shouldReposition: true;
|
||||
property bool hmdActive: HMD.active;
|
||||
width: hmdActive ? audio.width : audioApplication.width;
|
||||
height: hmdActive ? audio.height : audioApplication.height;
|
||||
|
||||
Timer {
|
||||
id: hmdActiveCheckTimer;
|
||||
interval: 500;
|
||||
repeat: true;
|
||||
onTriggered: {
|
||||
root.hmdActive = HMD.active;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
HifiAudio.MicBar {
|
||||
id: audio;
|
||||
visible: AvatarInputs.showAudioTools;
|
||||
visible: AvatarInputs.showAudioTools && root.hmdActive;
|
||||
standalone: true;
|
||||
dragTarget: parent;
|
||||
dragTarget: parent;
|
||||
}
|
||||
|
||||
HifiAudio.MicBarApplication {
|
||||
id: audioApplication;
|
||||
visible: AvatarInputs.showAudioTools && !root.hmdActive;
|
||||
standalone: true;
|
||||
dragTarget: parent;
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
HMD.displayModeChanged.connect(function(isHmdMode) {
|
||||
root.hmdActive = isHmdMode;
|
||||
});
|
||||
}
|
||||
|
||||
BubbleIcon {
|
||||
dragTarget: parent
|
||||
visible: !root.hmdActive;
|
||||
}
|
||||
}
|
||||
|
|
100
interface/resources/qml/BubbleIcon.qml
Normal file
|
@ -0,0 +1,100 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2015/06/19
|
||||
// Copyright 2015 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
|
||||
//
|
||||
|
||||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.5
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
import "./hifi/audio" as HifiAudio
|
||||
|
||||
import TabletScriptingInterface 1.0
|
||||
|
||||
Rectangle {
|
||||
id: bubbleRect
|
||||
width: bubbleIcon.width + 10
|
||||
height: bubbleIcon.height + 10
|
||||
radius: 5;
|
||||
property var dragTarget: null;
|
||||
property bool ignoreRadiusEnabled: AvatarInputs.ignoreRadiusEnabled;
|
||||
|
||||
function updateOpacity() {
|
||||
var rectOpacity = ignoreRadiusEnabled ? 1.0 : (mouseArea.containsMouse ? 1.0 : 0.7);
|
||||
bubbleRect.opacity = rectOpacity;
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
updateOpacity();
|
||||
AvatarInputs.ignoreRadiusEnabledChanged.connect(function() {
|
||||
ignoreRadiusEnabled = AvatarInputs.ignoreRadiusEnabled;
|
||||
});
|
||||
}
|
||||
|
||||
onIgnoreRadiusEnabledChanged: {
|
||||
updateOpacity();
|
||||
}
|
||||
|
||||
color: "#00000000";
|
||||
border {
|
||||
width: mouseArea.containsMouse || mouseArea.containsPress ? 2 : 0;
|
||||
color: "#80FFFFFF";
|
||||
}
|
||||
anchors {
|
||||
left: dragTarget ? dragTarget.right : undefined
|
||||
top: dragTarget ? dragTarget.top : undefined
|
||||
}
|
||||
|
||||
// borders are painted over fill, so reduce the fill to fit inside the border
|
||||
Rectangle {
|
||||
color: "#55000000";
|
||||
width: 40;
|
||||
height: 40;
|
||||
|
||||
radius: 5;
|
||||
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter;
|
||||
horizontalCenter: parent.horizontalCenter;
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea;
|
||||
anchors.fill: parent
|
||||
|
||||
hoverEnabled: true;
|
||||
scrollGestureEnabled: false;
|
||||
onClicked: {
|
||||
Tablet.playSound(TabletEnums.ButtonClick);
|
||||
Users.toggleIgnoreRadius();
|
||||
}
|
||||
drag.target: dragTarget;
|
||||
onContainsMouseChanged: {
|
||||
if (containsMouse) {
|
||||
Tablet.playSound(TabletEnums.ButtonHover);
|
||||
}
|
||||
var rectOpacity = ignoreRadiusEnabled ? 1.0 : (mouseArea.containsMouse ? 1.0 : 0.7);
|
||||
bubbleRect.opacity = rectOpacity;
|
||||
}
|
||||
}
|
||||
Image {
|
||||
id: bubbleIcon
|
||||
source: "../icons/tablet-icons/bubble-i.svg";
|
||||
sourceSize: Qt.size(32, 32);
|
||||
smooth: true;
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: (parent.height - bubbleIcon.height) / 2
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: (parent.width - bubbleIcon.width) / 2
|
||||
}
|
||||
ColorOverlay {
|
||||
id: bubbleIconOverlay
|
||||
anchors.fill: bubbleIcon
|
||||
source: bubbleIcon
|
||||
color: "#FFFFFF";
|
||||
}
|
||||
}
|
|
@ -398,7 +398,7 @@ Item {
|
|||
lineHeight: 1
|
||||
lineHeightMode: Text.ProportionalHeight
|
||||
|
||||
onLinkActivated: loginDialog.openUrl(link);
|
||||
onLinkActivated: Window.openUrl(link);
|
||||
|
||||
Component.onCompleted: {
|
||||
if (termsTextMetrics.width > root.bannerWidth) {
|
||||
|
|
|
@ -363,7 +363,7 @@ Item {
|
|||
linkColor: hifi.colors.blueAccent
|
||||
onLinkActivated: {
|
||||
Tablet.playSound(TabletEnums.ButtonClick);
|
||||
loginDialog.openUrl(link);
|
||||
Window.openUrl(link);
|
||||
lightboxPopup.titleText = "Can't Access Account";
|
||||
lightboxPopup.bodyText = lightboxPopup.cantAccessBodyText;
|
||||
lightboxPopup.button2text = "CLOSE";
|
||||
|
|
|
@ -411,7 +411,7 @@ Item {
|
|||
lineHeight: 1
|
||||
lineHeightMode: Text.ProportionalHeight
|
||||
|
||||
onLinkActivated: loginDialog.openUrl(link);
|
||||
onLinkActivated: Window.openUrl(link);
|
||||
|
||||
Component.onCompleted: {
|
||||
if (termsTextMetrics.width > root.bannerWidth) {
|
||||
|
|
|
@ -234,7 +234,7 @@ Item {
|
|||
lineHeight: 1
|
||||
lineHeightMode: Text.ProportionalHeight
|
||||
|
||||
onLinkActivated: loginDialog.openUrl(link);
|
||||
onLinkActivated: Window.openUrl(link);
|
||||
|
||||
Component.onCompleted: {
|
||||
if (termsTextMetrics.width > root.bannerWidth) {
|
||||
|
|
|
@ -24,6 +24,7 @@ CheckBox {
|
|||
leftPadding: 0
|
||||
property int colorScheme: hifi.colorSchemes.light
|
||||
property string color: hifi.colors.lightGrayText
|
||||
property int fontSize: hifi.fontSizes.inputLabel
|
||||
readonly property bool isLightColorScheme: colorScheme === hifi.colorSchemes.light
|
||||
property bool isRedCheck: false
|
||||
property bool isRound: false
|
||||
|
@ -109,7 +110,7 @@ CheckBox {
|
|||
|
||||
contentItem: Text {
|
||||
id: root
|
||||
font.pixelSize: hifi.fontSizes.inputLabel
|
||||
font.pixelSize: fontSize;
|
||||
font.family: "Raleway"
|
||||
font.weight: Font.DemiBold
|
||||
text: checkBox.text
|
||||
|
|
|
@ -21,6 +21,7 @@ Item {
|
|||
property int switchWidth: 70;
|
||||
readonly property int switchRadius: height/2;
|
||||
property string labelTextOff: "";
|
||||
property int labelTextSize: hifi.fontSizes.inputLabel;
|
||||
property string labelGlyphOffText: "";
|
||||
property int labelGlyphOffSize: 32;
|
||||
property string labelTextOn: "";
|
||||
|
@ -89,7 +90,7 @@ Item {
|
|||
RalewaySemiBold {
|
||||
id: labelOff;
|
||||
text: labelTextOff;
|
||||
size: hifi.fontSizes.inputLabel;
|
||||
size: labelTextSize;
|
||||
color: originalSwitch.checked ? hifi.colors.lightGrayText : "#FFFFFF";
|
||||
anchors.top: parent.top;
|
||||
anchors.right: parent.right;
|
||||
|
@ -130,7 +131,7 @@ Item {
|
|||
RalewaySemiBold {
|
||||
id: labelOn;
|
||||
text: labelTextOn;
|
||||
size: hifi.fontSizes.inputLabel;
|
||||
size: labelTextSize;
|
||||
color: originalSwitch.checked ? "#FFFFFF" : hifi.colors.lightGrayText;
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
|
|
152
interface/resources/qml/hifi/EditAvatarInputsBar.qml
Normal file
|
@ -0,0 +1,152 @@
|
|||
//
|
||||
// EditAvatarInputsBar.qml
|
||||
// qml/hifi
|
||||
//
|
||||
// Audio setup
|
||||
//
|
||||
// Created by Wayne Chen on 3/20/2019
|
||||
// Copyright 2019 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
|
||||
//
|
||||
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.3
|
||||
|
||||
import stylesUit 1.0
|
||||
import controlsUit 1.0 as HifiControlsUit
|
||||
import "../windows"
|
||||
|
||||
Rectangle {
|
||||
id: editRect
|
||||
|
||||
HifiConstants { id: hifi; }
|
||||
|
||||
color: hifi.colors.baseGray;
|
||||
|
||||
signal sendToScript(var message);
|
||||
function emitSendToScript(message) {
|
||||
sendToScript(message);
|
||||
}
|
||||
|
||||
function fromScript(message) {
|
||||
}
|
||||
|
||||
RalewayRegular {
|
||||
id: title;
|
||||
color: hifi.colors.white;
|
||||
text: qsTr("Avatar Inputs Persistent UI Settings")
|
||||
size: 20
|
||||
font.bold: true
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
leftMargin: (parent.width - width) / 2
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.Slider {
|
||||
id: xSlider
|
||||
anchors {
|
||||
top: title.bottom
|
||||
topMargin: 50
|
||||
left: parent.left
|
||||
leftMargin: 20
|
||||
}
|
||||
label: "X OFFSET: " + value.toFixed(2);
|
||||
maximumValue: 1.0
|
||||
minimumValue: -1.0
|
||||
stepSize: 0.05
|
||||
value: -0.2
|
||||
width: 300
|
||||
onValueChanged: {
|
||||
emitSendToScript({
|
||||
"method": "reposition",
|
||||
"x": value
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.Slider {
|
||||
id: ySlider
|
||||
anchors {
|
||||
top: xSlider.bottom
|
||||
topMargin: 50
|
||||
left: parent.left
|
||||
leftMargin: 20
|
||||
}
|
||||
label: "Y OFFSET: " + value.toFixed(2);
|
||||
maximumValue: 1.0
|
||||
minimumValue: -1.0
|
||||
stepSize: 0.05
|
||||
value: -0.125
|
||||
width: 300
|
||||
onValueChanged: {
|
||||
emitSendToScript({
|
||||
"method": "reposition",
|
||||
"y": value
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.Slider {
|
||||
id: zSlider
|
||||
anchors {
|
||||
top: ySlider.bottom
|
||||
topMargin: 50
|
||||
left: parent.left
|
||||
leftMargin: 20
|
||||
}
|
||||
label: "Z OFFSET: " + value.toFixed(2);
|
||||
maximumValue: 0.0
|
||||
minimumValue: -1.0
|
||||
stepSize: 0.05
|
||||
value: -0.5
|
||||
width: 300
|
||||
onValueChanged: {
|
||||
emitSendToScript({
|
||||
"method": "reposition",
|
||||
"z": value
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.Button {
|
||||
id: setVisibleButton;
|
||||
text: setVisible ? "SET INVISIBLE" : "SET VISIBLE";
|
||||
width: 300;
|
||||
property bool setVisible: true;
|
||||
anchors {
|
||||
top: zSlider.bottom
|
||||
topMargin: 50
|
||||
left: parent.left
|
||||
leftMargin: 20
|
||||
}
|
||||
onClicked: {
|
||||
setVisible = !setVisible;
|
||||
emitSendToScript({
|
||||
"method": "setVisible",
|
||||
"visible": setVisible
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.Button {
|
||||
id: printButton;
|
||||
text: "PRINT POSITIONS";
|
||||
width: 300;
|
||||
anchors {
|
||||
top: setVisibleButton.bottom
|
||||
topMargin: 50
|
||||
left: parent.left
|
||||
leftMargin: 20
|
||||
}
|
||||
onClicked: {
|
||||
emitSendToScript({
|
||||
"method": "print",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -129,6 +129,7 @@ Item {
|
|||
height: 40
|
||||
// Anchors
|
||||
anchors.top: avatarImage.top
|
||||
anchors.topMargin: avatarImage.visible ? 18 : 0;
|
||||
anchors.left: avatarImage.right
|
||||
anchors.leftMargin: avatarImage.visible ? 5 : 0;
|
||||
anchors.rightMargin: 5;
|
||||
|
|
|
@ -31,6 +31,8 @@ Rectangle {
|
|||
property string title: "Audio Settings"
|
||||
property int switchHeight: 16
|
||||
property int switchWidth: 40
|
||||
property bool pushToTalk: (bar.currentIndex === 0) ? AudioScriptingInterface.pushToTalkDesktop : AudioScriptingInterface.pushToTalkHMD;
|
||||
property bool muted: (bar.currentIndex === 0) ? AudioScriptingInterface.mutedDesktop : AudioScriptingInterface.mutedHMD;
|
||||
readonly property real verticalScrollWidth: 10
|
||||
readonly property real verticalScrollShaft: 8
|
||||
signal sendToScript(var message);
|
||||
|
@ -44,7 +46,7 @@ Rectangle {
|
|||
|
||||
|
||||
property bool isVR: AudioScriptingInterface.context === "VR"
|
||||
property real rightMostInputLevelPos: 440
|
||||
property real rightMostInputLevelPos: root.width
|
||||
//placeholder for control sizes and paddings
|
||||
//recalculates dynamically in case of UI size is changed
|
||||
QtObject {
|
||||
|
@ -103,7 +105,9 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: enablePeakValues();
|
||||
Component.onCompleted: {
|
||||
enablePeakValues();
|
||||
}
|
||||
|
||||
Flickable {
|
||||
id: flickView;
|
||||
|
@ -178,15 +182,25 @@ Rectangle {
|
|||
height: root.switchHeight;
|
||||
switchWidth: root.switchWidth;
|
||||
labelTextOn: "Mute microphone";
|
||||
labelTextSize: 16;
|
||||
backgroundOnColor: "#E3E3E3";
|
||||
checked: AudioScriptingInterface.muted;
|
||||
checked: muted;
|
||||
onClicked: {
|
||||
if (AudioScriptingInterface.pushToTalk && !checked) {
|
||||
if (pushToTalk && !checked) {
|
||||
// disable push to talk if unmuting
|
||||
AudioScriptingInterface.pushToTalk = false;
|
||||
if (bar.currentIndex === 0) {
|
||||
AudioScriptingInterface.pushToTalkDesktop = false;
|
||||
}
|
||||
else {
|
||||
AudioScriptingInterface.pushToTalkHMD = false;
|
||||
}
|
||||
}
|
||||
if (bar.currentIndex === 0) {
|
||||
AudioScriptingInterface.mutedDesktop = checked;
|
||||
}
|
||||
else {
|
||||
AudioScriptingInterface.mutedHMD = checked;
|
||||
}
|
||||
AudioScriptingInterface.muted = checked;
|
||||
checked = Qt.binding(function() { return AudioScriptingInterface.muted; }); // restore binding
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -198,6 +212,7 @@ Rectangle {
|
|||
anchors.topMargin: 24
|
||||
anchors.left: parent.left
|
||||
labelTextOn: "Noise Reduction";
|
||||
labelTextSize: 16;
|
||||
backgroundOnColor: "#E3E3E3";
|
||||
checked: AudioScriptingInterface.noiseReduction;
|
||||
onCheckedChanged: {
|
||||
|
@ -213,7 +228,8 @@ Rectangle {
|
|||
anchors.top: noiseReductionSwitch.bottom
|
||||
anchors.topMargin: 24
|
||||
anchors.left: parent.left
|
||||
labelTextOn: qsTr("Push To Talk (T)");
|
||||
labelTextOn: (bar.currentIndex === 0) ? qsTr("Push To Talk (T)") : qsTr("Push To Talk");
|
||||
labelTextSize: 16;
|
||||
backgroundOnColor: "#E3E3E3";
|
||||
checked: (bar.currentIndex === 0) ? AudioScriptingInterface.pushToTalkDesktop : AudioScriptingInterface.pushToTalkHMD;
|
||||
onCheckedChanged: {
|
||||
|
@ -222,13 +238,6 @@ Rectangle {
|
|||
} else {
|
||||
AudioScriptingInterface.pushToTalkHMD = checked;
|
||||
}
|
||||
checked = Qt.binding(function() {
|
||||
if (bar.currentIndex === 0) {
|
||||
return AudioScriptingInterface.pushToTalkDesktop;
|
||||
} else {
|
||||
return AudioScriptingInterface.pushToTalkHMD;
|
||||
}
|
||||
}); // restore binding
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -245,7 +254,8 @@ Rectangle {
|
|||
switchWidth: root.switchWidth;
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
labelTextOn: qsTr("Warn when muted");
|
||||
labelTextOn: qsTr("Warn when muted in HMD");
|
||||
labelTextSize: 16;
|
||||
backgroundOnColor: "#E3E3E3";
|
||||
checked: AudioScriptingInterface.warnWhenMuted;
|
||||
onClicked: {
|
||||
|
@ -263,6 +273,7 @@ Rectangle {
|
|||
anchors.topMargin: 24
|
||||
anchors.left: parent.left
|
||||
labelTextOn: qsTr("Audio Level Meter");
|
||||
labelTextSize: 16;
|
||||
backgroundOnColor: "#E3E3E3";
|
||||
checked: AvatarInputs.showAudioTools;
|
||||
onCheckedChanged: {
|
||||
|
@ -279,6 +290,7 @@ Rectangle {
|
|||
anchors.topMargin: 24
|
||||
anchors.left: parent.left
|
||||
labelTextOn: qsTr("Stereo input");
|
||||
labelTextSize: 16;
|
||||
backgroundOnColor: "#E3E3E3";
|
||||
checked: AudioScriptingInterface.isStereoInput;
|
||||
onCheckedChanged: {
|
||||
|
@ -314,7 +326,7 @@ Rectangle {
|
|||
|
||||
Separator {
|
||||
id: secondSeparator;
|
||||
anchors.top: pttTextContainer.bottom;
|
||||
anchors.top: pttTextContainer.visible ? pttTextContainer.bottom : switchesContainer.bottom;
|
||||
anchors.topMargin: 10;
|
||||
}
|
||||
|
||||
|
@ -341,7 +353,7 @@ Rectangle {
|
|||
width: margins.sizeText + margins.sizeLevel;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: margins.sizeCheckBox;
|
||||
size: 16;
|
||||
size: 22;
|
||||
color: hifi.colors.white;
|
||||
text: qsTr("Choose input device");
|
||||
}
|
||||
|
@ -349,16 +361,17 @@ Rectangle {
|
|||
|
||||
ListView {
|
||||
id: inputView;
|
||||
width: parent.width - margins.paddings*2;
|
||||
width: rightMostInputLevelPos;
|
||||
anchors.top: inputDeviceHeader.bottom;
|
||||
anchors.topMargin: 10;
|
||||
x: margins.paddings
|
||||
interactive: false;
|
||||
height: contentHeight;
|
||||
spacing: 4;
|
||||
clip: true;
|
||||
model: AudioScriptingInterface.devices.input;
|
||||
delegate: Item {
|
||||
width: rightMostInputLevelPos
|
||||
width: rightMostInputLevelPos - margins.paddings*2
|
||||
height: margins.sizeCheckBox > checkBoxInput.implicitHeight ?
|
||||
margins.sizeCheckBox : checkBoxInput.implicitHeight
|
||||
|
||||
|
@ -374,6 +387,7 @@ Rectangle {
|
|||
boxSize: margins.sizeCheckBox / 2
|
||||
isRound: true
|
||||
text: devicename
|
||||
fontSize: 16;
|
||||
onPressed: {
|
||||
if (!checked) {
|
||||
stereoInput.checked = false;
|
||||
|
@ -407,7 +421,7 @@ Rectangle {
|
|||
|
||||
Separator {
|
||||
id: thirdSeparator;
|
||||
anchors.top: loopbackAudio.bottom;
|
||||
anchors.top: loopbackAudio.visible ? loopbackAudio.bottom : inputView.bottom;
|
||||
anchors.topMargin: 10;
|
||||
}
|
||||
|
||||
|
@ -434,7 +448,7 @@ Rectangle {
|
|||
anchors.left: parent.left
|
||||
anchors.leftMargin: margins.sizeCheckBox
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
size: 16;
|
||||
size: 22;
|
||||
color: hifi.colors.white;
|
||||
text: qsTr("Choose output device");
|
||||
}
|
||||
|
@ -443,7 +457,8 @@ Rectangle {
|
|||
ListView {
|
||||
id: outputView
|
||||
width: parent.width - margins.paddings*2
|
||||
x: margins.paddings
|
||||
x: margins.paddings;
|
||||
interactive: false;
|
||||
height: contentHeight;
|
||||
anchors.top: outputDeviceHeader.bottom;
|
||||
anchors.topMargin: 10;
|
||||
|
@ -464,6 +479,7 @@ Rectangle {
|
|||
checked: bar.currentIndex === 0 ? selectedDesktop : selectedHMD;
|
||||
checkable: !checked
|
||||
text: devicename
|
||||
fontSize: 16
|
||||
onPressed: {
|
||||
if (!checked) {
|
||||
AudioScriptingInterface.setOutputDevice(info, bar.currentIndex === 1);
|
||||
|
@ -526,7 +542,7 @@ Rectangle {
|
|||
RalewayRegular {
|
||||
// The slider for my card is special, it controls the master gain
|
||||
id: avatarGainSliderText;
|
||||
text: "Avatar volume";
|
||||
text: "People volume";
|
||||
size: 16;
|
||||
anchors.left: parent.left;
|
||||
color: hifi.colors.white;
|
||||
|
|
|
@ -12,24 +12,26 @@
|
|||
import QtQuick 2.5
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
Rectangle {
|
||||
Item {
|
||||
property var peak;
|
||||
|
||||
width: 70;
|
||||
height: 8;
|
||||
|
||||
color: "transparent";
|
||||
|
||||
Item {
|
||||
QtObject {
|
||||
id: colors;
|
||||
|
||||
readonly property string unmuted: "#FFF";
|
||||
readonly property string muted: "#E2334D";
|
||||
readonly property string gutter: "#575757";
|
||||
readonly property string greenStart: "#39A38F";
|
||||
readonly property string greenEnd: "#1FC6A6";
|
||||
readonly property string yellow: "#C0C000";
|
||||
readonly property string red: colors.muted;
|
||||
readonly property string fill: "#55000000";
|
||||
}
|
||||
|
||||
|
||||
Text {
|
||||
id: status;
|
||||
|
||||
|
@ -79,23 +81,19 @@ Rectangle {
|
|||
anchors { fill: mask }
|
||||
source: mask
|
||||
start: Qt.point(0, 0);
|
||||
end: Qt.point(70, 0);
|
||||
end: Qt.point(bar.width, 0);
|
||||
gradient: Gradient {
|
||||
GradientStop {
|
||||
position: 0;
|
||||
color: colors.greenStart;
|
||||
}
|
||||
GradientStop {
|
||||
position: 0.8;
|
||||
position: 0.5;
|
||||
color: colors.greenEnd;
|
||||
}
|
||||
GradientStop {
|
||||
position: 0.801;
|
||||
color: colors.red;
|
||||
}
|
||||
GradientStop {
|
||||
position: 1;
|
||||
color: colors.red;
|
||||
color: colors.yellow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,11 +60,11 @@ RowLayout {
|
|||
}
|
||||
}
|
||||
|
||||
// RalewayRegular {
|
||||
// Layout.leftMargin: 2;
|
||||
// size: 14;
|
||||
// color: "white";
|
||||
// font.italic: true
|
||||
// text: audioLoopedBack ? qsTr("Speak in your input") : "";
|
||||
// }
|
||||
RalewayRegular {
|
||||
Layout.leftMargin: 2;
|
||||
size: 18;
|
||||
color: "white";
|
||||
font.italic: true
|
||||
text: audioLoopedBack ? qsTr("Speak in your input") : "";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,14 +16,33 @@ import stylesUit 1.0
|
|||
import TabletScriptingInterface 1.0
|
||||
|
||||
Rectangle {
|
||||
id: micBar
|
||||
HifiConstants { id: hifi; }
|
||||
|
||||
property var muted: AudioScriptingInterface.muted;
|
||||
readonly property var level: AudioScriptingInterface.inputLevel;
|
||||
readonly property var clipping: AudioScriptingInterface.clipping;
|
||||
property var pushToTalk: AudioScriptingInterface.pushToTalk;
|
||||
property var pushingToTalk: AudioScriptingInterface.pushingToTalk;
|
||||
|
||||
readonly property var userSpeakingLevel: 0.4;
|
||||
property bool gated: false;
|
||||
Component.onCompleted: {
|
||||
AudioScriptingInterface.noiseGateOpened.connect(function() { gated = false; });
|
||||
AudioScriptingInterface.noiseGateClosed.connect(function() { gated = true; });
|
||||
HMD.displayModeChanged.connect(function() {
|
||||
muted = AudioScriptingInterface.muted;
|
||||
pushToTalk = AudioScriptingInterface.pushToTalk;
|
||||
});
|
||||
AudioScriptingInterface.mutedChanged.connect(function() {
|
||||
muted = AudioScriptingInterface.muted;
|
||||
});
|
||||
AudioScriptingInterface.pushToTalkChanged.connect(function() {
|
||||
pushToTalk = AudioScriptingInterface.pushToTalk;
|
||||
});
|
||||
AudioScriptingInterface.pushingToTalkChanged.connect(function() {
|
||||
pushingToTalk = AudioScriptingInterface.pushingToTalk;
|
||||
});
|
||||
}
|
||||
|
||||
property bool standalone: false;
|
||||
|
@ -67,10 +86,10 @@ Rectangle {
|
|||
hoverEnabled: true;
|
||||
scrollGestureEnabled: false;
|
||||
onClicked: {
|
||||
if (AudioScriptingInterface.pushToTalk) {
|
||||
if (pushToTalk) {
|
||||
return;
|
||||
}
|
||||
AudioScriptingInterface.muted = !AudioScriptingInterface.muted;
|
||||
AudioScriptingInterface.muted = !muted;
|
||||
Tablet.playSound(TabletEnums.ButtonClick);
|
||||
}
|
||||
drag.target: dragTarget;
|
||||
|
@ -84,16 +103,16 @@ Rectangle {
|
|||
QtObject {
|
||||
id: colors;
|
||||
|
||||
readonly property string unmuted: "#FFF";
|
||||
readonly property string muted: "#E2334D";
|
||||
readonly property string unmutedColor: "#FFF";
|
||||
readonly property string mutedColor: "#E2334D";
|
||||
readonly property string gutter: "#575757";
|
||||
readonly property string greenStart: "#39A38F";
|
||||
readonly property string greenEnd: "#1FC6A6";
|
||||
readonly property string yellow: "#C0C000";
|
||||
readonly property string red: colors.muted;
|
||||
readonly property string red: colors.mutedColor;
|
||||
readonly property string fill: "#55000000";
|
||||
readonly property string border: standalone ? "#80FFFFFF" : "#55FFFFFF";
|
||||
readonly property string icon: AudioScriptingInterface.muted ? muted : unmuted;
|
||||
readonly property string icon: muted ? colors.mutedColor : unmutedColor;
|
||||
}
|
||||
|
||||
Item {
|
||||
|
@ -113,9 +132,12 @@ Rectangle {
|
|||
readonly property string unmutedIcon: "../../../icons/tablet-icons/mic-unmute-i.svg";
|
||||
readonly property string mutedIcon: "../../../icons/tablet-icons/mic-mute-i.svg";
|
||||
readonly property string pushToTalkIcon: "../../../icons/tablet-icons/mic-ptt-i.svg";
|
||||
readonly property string clippingIcon: "../../../icons/tablet-icons/mic-clip-i.svg";
|
||||
readonly property string gatedIcon: "../../../icons/tablet-icons/mic-gate-i.svg";
|
||||
|
||||
id: image;
|
||||
source: (AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk) ? pushToTalkIcon : AudioScriptingInterface.muted ? mutedIcon : unmutedIcon;
|
||||
source: (pushToTalk && !pushingToTalk) ? pushToTalkIcon : muted ? mutedIcon :
|
||||
clipping ? clippingIcon : gated ? gatedIcon : unmutedIcon;
|
||||
|
||||
width: 30;
|
||||
height: 30;
|
||||
|
@ -138,9 +160,7 @@ Rectangle {
|
|||
Item {
|
||||
id: status;
|
||||
|
||||
readonly property string color: AudioScriptingInterface.muted ? colors.muted : colors.unmuted;
|
||||
|
||||
visible: (AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk) || AudioScriptingInterface.muted;
|
||||
visible: (pushToTalk && !pushingToTalk) || muted;
|
||||
|
||||
anchors {
|
||||
left: parent.left;
|
||||
|
@ -157,9 +177,9 @@ Rectangle {
|
|||
verticalCenter: parent.verticalCenter;
|
||||
}
|
||||
|
||||
color: parent.color;
|
||||
color: colors.icon;
|
||||
|
||||
text: (AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk) ? (HMD.active ? "MUTED PTT" : "MUTED PTT-(T)") : (AudioScriptingInterface.muted ? "MUTED" : "MUTE");
|
||||
text: (pushToTalk && !pushingToTalk) ? (HMD.active ? "MUTED PTT" : "MUTED PTT-(T)") : (muted ? "MUTED" : "MUTE");
|
||||
font.pointSize: 12;
|
||||
}
|
||||
|
||||
|
@ -169,9 +189,9 @@ Rectangle {
|
|||
verticalCenter: parent.verticalCenter;
|
||||
}
|
||||
|
||||
width: AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk ? (HMD.active ? 27 : 25) : 50;
|
||||
width: pushToTalk && !pushingToTalk ? (HMD.active ? 27 : 25) : 50;
|
||||
height: 4;
|
||||
color: parent.color;
|
||||
color: colors.icon;
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
|
@ -180,9 +200,9 @@ Rectangle {
|
|||
verticalCenter: parent.verticalCenter;
|
||||
}
|
||||
|
||||
width: AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk ? (HMD.active ? 27 : 25) : 50;
|
||||
width: pushToTalk && !pushingToTalk ? (HMD.active ? 27 : 25) : 50;
|
||||
height: 4;
|
||||
color: parent.color;
|
||||
color: colors.icon;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
255
interface/resources/qml/hifi/audio/MicBarApplication.qml
Normal file
|
@ -0,0 +1,255 @@
|
|||
//
|
||||
// MicBarApplication.qml
|
||||
// qml/hifi/audio
|
||||
//
|
||||
// Created by Zach Pomerantz on 6/14/2017
|
||||
// Copyright 2017 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
|
||||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
import stylesUit 1.0
|
||||
import TabletScriptingInterface 1.0
|
||||
|
||||
Rectangle {
|
||||
id: micBar;
|
||||
readonly property var level: AudioScriptingInterface.inputLevel;
|
||||
readonly property var clipping: AudioScriptingInterface.clipping;
|
||||
property var muted: AudioScriptingInterface.muted;
|
||||
property var pushToTalk: AudioScriptingInterface.pushToTalk;
|
||||
property var pushingToTalk: AudioScriptingInterface.pushingToTalk;
|
||||
readonly property var userSpeakingLevel: 0.4;
|
||||
property bool gated: false;
|
||||
Component.onCompleted: {
|
||||
AudioScriptingInterface.noiseGateOpened.connect(function() { gated = false; });
|
||||
AudioScriptingInterface.noiseGateClosed.connect(function() { gated = true; });
|
||||
HMD.displayModeChanged.connect(function() {
|
||||
muted = AudioScriptingInterface.muted;
|
||||
pushToTalk = AudioScriptingInterface.pushToTalk;
|
||||
});
|
||||
AudioScriptingInterface.mutedChanged.connect(function() {
|
||||
muted = AudioScriptingInterface.muted;
|
||||
});
|
||||
AudioScriptingInterface.pushToTalkChanged.connect(function() {
|
||||
pushToTalk = AudioScriptingInterface.pushToTalk;
|
||||
});
|
||||
}
|
||||
|
||||
readonly property string unmutedIcon: "../../../icons/tablet-icons/mic-unmute-i.svg";
|
||||
readonly property string mutedIcon: "../../../icons/tablet-icons/mic-mute-i.svg";
|
||||
readonly property string pushToTalkIcon: "../../../icons/tablet-icons/mic-ptt-i.svg";
|
||||
readonly property string clippingIcon: "../../../icons/tablet-icons/mic-clip-i.svg";
|
||||
readonly property string gatedIcon: "../../../icons/tablet-icons/mic-gate-i.svg";
|
||||
property bool standalone: false;
|
||||
property var dragTarget: null;
|
||||
|
||||
width: 44;
|
||||
height: 44;
|
||||
|
||||
radius: 5;
|
||||
opacity: 0.7;
|
||||
|
||||
onLevelChanged: {
|
||||
var rectOpacity = (muted && (level >= userSpeakingLevel)) ? 1.0 : 0.7;
|
||||
if (pushToTalk && !pushingToTalk) {
|
||||
rectOpacity = (mouseArea.containsMouse) ? 1.0 : 0.7;
|
||||
} else if (mouseArea.containsMouse && rectOpacity != 1.0) {
|
||||
rectOpacity = 1.0;
|
||||
}
|
||||
micBar.opacity = rectOpacity;
|
||||
}
|
||||
|
||||
color: "#00000000";
|
||||
border {
|
||||
width: mouseArea.containsMouse || mouseArea.containsPress ? 2 : 0;
|
||||
color: colors.border;
|
||||
}
|
||||
|
||||
// borders are painted over fill, so reduce the fill to fit inside the border
|
||||
Rectangle {
|
||||
color: standalone ? colors.fill : "#00000000";
|
||||
width: 40;
|
||||
height: 40;
|
||||
|
||||
radius: 5;
|
||||
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter;
|
||||
horizontalCenter: parent.horizontalCenter;
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea;
|
||||
|
||||
anchors {
|
||||
left: icon.left;
|
||||
right: bar.right;
|
||||
top: icon.top;
|
||||
bottom: icon.bottom;
|
||||
}
|
||||
|
||||
hoverEnabled: true;
|
||||
scrollGestureEnabled: false;
|
||||
onClicked: {
|
||||
if (pushToTalk) {
|
||||
return;
|
||||
}
|
||||
AudioScriptingInterface.muted = !muted;
|
||||
Tablet.playSound(TabletEnums.ButtonClick);
|
||||
muted = Qt.binding(function() { return AudioScriptingInterface.muted; }); // restore binding
|
||||
}
|
||||
drag.target: dragTarget;
|
||||
onContainsMouseChanged: {
|
||||
if (containsMouse) {
|
||||
Tablet.playSound(TabletEnums.ButtonHover);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: colors;
|
||||
|
||||
readonly property string unmutedColor: "#FFF";
|
||||
readonly property string gatedColor: "#00BDFF";
|
||||
readonly property string mutedColor: "#E2334D";
|
||||
readonly property string gutter: "#575757";
|
||||
readonly property string greenStart: "#39A38F";
|
||||
readonly property string greenEnd: "#1FC6A6";
|
||||
readonly property string yellow: "#C0C000";
|
||||
readonly property string fill: "#55000000";
|
||||
readonly property string border: standalone ? "#80FFFFFF" : "#55FFFFFF";
|
||||
readonly property string icon: (muted || clipping) ? mutedColor : gated ? gatedColor : unmutedColor;
|
||||
}
|
||||
|
||||
Item {
|
||||
id: icon;
|
||||
|
||||
anchors {
|
||||
left: parent.left;
|
||||
top: parent.top;
|
||||
}
|
||||
|
||||
width: 40;
|
||||
height: 40;
|
||||
|
||||
Item {
|
||||
Image {
|
||||
id: image;
|
||||
source: (pushToTalk) ? pushToTalkIcon : muted ? mutedIcon :
|
||||
clipping ? clippingIcon : gated ? gatedIcon : unmutedIcon;
|
||||
width: 29;
|
||||
height: 32;
|
||||
anchors {
|
||||
left: parent.left;
|
||||
top: parent.top;
|
||||
topMargin: 5;
|
||||
}
|
||||
}
|
||||
|
||||
ColorOverlay {
|
||||
id: imageOverlay
|
||||
anchors { fill: image }
|
||||
source: image;
|
||||
color: pushToTalk ? (pushingToTalk ? colors.unmutedColor : colors.mutedColor) : colors.icon;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: status;
|
||||
|
||||
visible: pushToTalk || (muted && (level >= userSpeakingLevel));
|
||||
|
||||
anchors {
|
||||
left: parent.left;
|
||||
top: icon.bottom;
|
||||
topMargin: 2;
|
||||
}
|
||||
|
||||
width: parent.width;
|
||||
height: statusTextMetrics.height;
|
||||
|
||||
TextMetrics {
|
||||
id: statusTextMetrics
|
||||
text: statusText.text
|
||||
font: statusText.font
|
||||
}
|
||||
|
||||
RalewaySemiBold {
|
||||
id: statusText
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter;
|
||||
verticalCenter: parent.verticalCenter;
|
||||
}
|
||||
|
||||
color: pushToTalk ? (pushingToTalk ? colors.unmutedColor : colors.mutedColor) : (level >= userSpeakingLevel && muted) ? colors.mutedColor : colors.unmutedColor;
|
||||
font.bold: true
|
||||
|
||||
text: pushToTalk ? (HMD.active ? "PTT" : "PTT-(T)") : (muted ? "MUTED" : "MUTE");
|
||||
size: 12;
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: bar;
|
||||
|
||||
anchors {
|
||||
right: parent.right;
|
||||
rightMargin: 7;
|
||||
top: parent.top
|
||||
topMargin: 5
|
||||
}
|
||||
|
||||
width: 8;
|
||||
height: 32;
|
||||
|
||||
Rectangle { // base
|
||||
id: baseBar
|
||||
radius: 4;
|
||||
anchors { fill: parent }
|
||||
color: colors.gutter;
|
||||
}
|
||||
|
||||
Rectangle { // mask
|
||||
id: mask;
|
||||
visible: (!(pushToTalk && !pushingToTalk))
|
||||
height: parent.height * level;
|
||||
width: parent.width;
|
||||
radius: 5;
|
||||
anchors {
|
||||
bottom: parent.bottom;
|
||||
bottomMargin: 0;
|
||||
left: parent.left;
|
||||
leftMargin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
LinearGradient {
|
||||
anchors { fill: mask }
|
||||
visible: (!(pushToTalk && !pushingToTalk))
|
||||
source: mask
|
||||
start: Qt.point(0, 0);
|
||||
end: Qt.point(0, bar.height);
|
||||
rotation: 180
|
||||
gradient: Gradient {
|
||||
GradientStop {
|
||||
position: 0.0;
|
||||
color: colors.greenStart;
|
||||
}
|
||||
GradientStop {
|
||||
position: 0.5;
|
||||
color: colors.greenEnd;
|
||||
}
|
||||
GradientStop {
|
||||
position: 1.0;
|
||||
color: colors.yellow;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -64,11 +64,11 @@ RowLayout {
|
|||
height: 32;
|
||||
}
|
||||
|
||||
// RalewayRegular {
|
||||
// Layout.leftMargin: 2;
|
||||
// size: 14;
|
||||
// color: "white";
|
||||
// font.italic: true
|
||||
// text: isPlaying ? qsTr("Listen to your output") : "";
|
||||
// }
|
||||
RalewayRegular {
|
||||
Layout.leftMargin: 2;
|
||||
size: 18;
|
||||
color: "white";
|
||||
font.italic: true
|
||||
text: isPlaying ? qsTr("Listen to your output") : "";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -336,6 +336,8 @@ Rectangle {
|
|||
case Qt.Key_Return:
|
||||
case Qt.Key_Enter:
|
||||
event.accepted = true;
|
||||
keypressTimer.stop();
|
||||
root.searchString = searchField.text;
|
||||
searchField.text = "";
|
||||
|
||||
getMarketplaceItems();
|
||||
|
@ -950,7 +952,7 @@ Rectangle {
|
|||
text: "LOG IN"
|
||||
|
||||
onClicked: {
|
||||
sendToScript({method: 'needsLogIn_loginClicked'});
|
||||
sendToScript({method: 'marketplace_loginClicked'});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -137,7 +137,7 @@ Item {
|
|||
width: parent.width/2 - anchors.leftMargin*2;
|
||||
text: "Cancel"
|
||||
onClicked: {
|
||||
sendToScript({method: 'needsLogIn_cancelClicked'});
|
||||
sendToScript({method: 'passphrasePopup_cancelClicked'});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -155,7 +155,7 @@ Item {
|
|||
width: parent.width/2 - anchors.rightMargin*2;
|
||||
text: "Log In"
|
||||
onClicked: {
|
||||
sendToScript({method: 'needsLogIn_loginClicked'});
|
||||
sendToScript({method: 'marketplace_loginClicked'});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
HifiAudio.MicBar {
|
||||
HifiAudio.MicBarApplication {
|
||||
anchors {
|
||||
left: parent.left
|
||||
leftMargin: 30
|
||||
|
|
|
@ -338,6 +338,10 @@ Setting::Handle<int> maxOctreePacketsPerSecond{"maxOctreePPS", DEFAULT_MAX_OCTRE
|
|||
|
||||
Setting::Handle<bool> loginDialogPoppedUp{"loginDialogPoppedUp", false};
|
||||
|
||||
static const QUrl AVATAR_INPUTS_BAR_QML = PathUtils::qmlUrl("AvatarInputsBar.qml");
|
||||
static const QUrl MIC_BAR_APPLICATION_QML = PathUtils::qmlUrl("hifi/audio/MicBarApplication.qml");
|
||||
static const QUrl BUBBLE_ICON_QML = PathUtils::qmlUrl("BubbleIcon.qml");
|
||||
|
||||
static const QString STANDARD_TO_ACTION_MAPPING_NAME = "Standard to Action";
|
||||
static const QString NO_MOVEMENT_MAPPING_NAME = "Standard to Action (No Movement)";
|
||||
static const QString NO_MOVEMENT_MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/standard_nomovement.json";
|
||||
|
@ -676,6 +680,8 @@ private:
|
|||
* <tr><td><code>InHMD</code></td><td>number</td><td>number</td><td>The user is in HMD mode.</td></tr>
|
||||
* <tr><td><code>AdvancedMovement</code></td><td>number</td><td>number</td><td>Advanced movement controls are enabled.
|
||||
* </td></tr>
|
||||
* <tr><td><code>LeftHandDominant</code></td><td>number</td><td>number</td><td>Dominant hand set to left.</td></tr>
|
||||
* <tr><td><code>RightHandDominant</code></td><td>number</td><td>number</td><td>Dominant hand set to right.</td></tr>
|
||||
* <tr><td><code>SnapTurn</code></td><td>number</td><td>number</td><td>Snap turn is enabled.</td></tr>
|
||||
* <tr><td><code>Grounded</code></td><td>number</td><td>number</td><td>The user's avatar is on the ground.</td></tr>
|
||||
* <tr><td><code>NavigationFocused</code></td><td>number</td><td>number</td><td><em>Not used.</em></td></tr>
|
||||
|
@ -697,6 +703,9 @@ static const QString STATE_NAV_FOCUSED = "NavigationFocused";
|
|||
static const QString STATE_PLATFORM_WINDOWS = "PlatformWindows";
|
||||
static const QString STATE_PLATFORM_MAC = "PlatformMac";
|
||||
static const QString STATE_PLATFORM_ANDROID = "PlatformAndroid";
|
||||
static const QString STATE_LEFT_HAND_DOMINANT = "LeftHandDominant";
|
||||
static const QString STATE_RIGHT_HAND_DOMINANT = "RightHandDominant";
|
||||
static const QString STATE_STRAFE_ENABLED = "StrafeEnabled";
|
||||
|
||||
// Statically provided display and input plugins
|
||||
extern DisplayPluginList getDisplayPlugins();
|
||||
|
@ -898,7 +907,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
|
|||
controller::StateController::setStateVariables({ { STATE_IN_HMD, STATE_CAMERA_FULL_SCREEN_MIRROR,
|
||||
STATE_CAMERA_FIRST_PERSON, STATE_CAMERA_THIRD_PERSON, STATE_CAMERA_ENTITY, STATE_CAMERA_INDEPENDENT,
|
||||
STATE_SNAP_TURN, STATE_ADVANCED_MOVEMENT_CONTROLS, STATE_GROUNDED, STATE_NAV_FOCUSED,
|
||||
STATE_PLATFORM_WINDOWS, STATE_PLATFORM_MAC, STATE_PLATFORM_ANDROID } });
|
||||
STATE_PLATFORM_WINDOWS, STATE_PLATFORM_MAC, STATE_PLATFORM_ANDROID, STATE_LEFT_HAND_DOMINANT, STATE_RIGHT_HAND_DOMINANT, STATE_STRAFE_ENABLED } });
|
||||
DependencyManager::set<UserInputMapper>();
|
||||
DependencyManager::set<controller::ScriptingInterface, ControllerScriptingInterface>();
|
||||
DependencyManager::set<InterfaceParentFinder>();
|
||||
|
@ -1442,6 +1451,34 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
_overlays.init(); // do this before scripts load
|
||||
DependencyManager::set<ContextOverlayInterface>();
|
||||
|
||||
auto offscreenUi = getOffscreenUI();
|
||||
connect(offscreenUi.data(), &OffscreenUi::desktopReady, []() {
|
||||
// Now that we've loaded the menu and thus switched to the previous display plugin
|
||||
// we can unlock the desktop repositioning code, since all the positions will be
|
||||
// relative to the desktop size for this plugin
|
||||
auto offscreenUi = getOffscreenUI();
|
||||
auto desktop = offscreenUi->getDesktop();
|
||||
if (desktop) {
|
||||
desktop->setProperty("repositionLocked", false);
|
||||
}
|
||||
});
|
||||
|
||||
connect(offscreenUi.data(), &OffscreenUi::keyboardFocusActive, [this]() {
|
||||
#if !defined(Q_OS_ANDROID) && !defined(DISABLE_QML)
|
||||
// Do not show login dialog if requested not to on the command line
|
||||
QString hifiNoLoginCommandLineKey = QString("--").append(HIFI_NO_LOGIN_COMMAND_LINE_KEY);
|
||||
int index = arguments().indexOf(hifiNoLoginCommandLineKey);
|
||||
if (index != -1) {
|
||||
resumeAfterLoginDialogActionTaken();
|
||||
return;
|
||||
}
|
||||
|
||||
showLoginScreen();
|
||||
#else
|
||||
resumeAfterLoginDialogActionTaken();
|
||||
#endif
|
||||
});
|
||||
|
||||
// Initialize the user interface and menu system
|
||||
// Needs to happen AFTER the render engine initialization to access its configuration
|
||||
initializeUi();
|
||||
|
@ -1736,6 +1773,15 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
_applicationStateDevice->setInputVariant(STATE_ADVANCED_MOVEMENT_CONTROLS, []() -> float {
|
||||
return qApp->getMyAvatar()->useAdvancedMovementControls() ? 1 : 0;
|
||||
});
|
||||
_applicationStateDevice->setInputVariant(STATE_LEFT_HAND_DOMINANT, []() -> float {
|
||||
return qApp->getMyAvatar()->getDominantHand() == "left" ? 1 : 0;
|
||||
});
|
||||
_applicationStateDevice->setInputVariant(STATE_RIGHT_HAND_DOMINANT, []() -> float {
|
||||
return qApp->getMyAvatar()->getDominantHand() == "right" ? 1 : 0;
|
||||
});
|
||||
_applicationStateDevice->setInputVariant(STATE_STRAFE_ENABLED, []() -> float {
|
||||
return qApp->getMyAvatar()->getStrafeEnabled() ? 1 : 0;
|
||||
});
|
||||
|
||||
_applicationStateDevice->setInputVariant(STATE_GROUNDED, []() -> float {
|
||||
return qApp->getMyAvatar()->getCharacterController()->onGround() ? 1 : 0;
|
||||
|
@ -1787,34 +1833,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
|
||||
updateVerboseLogging();
|
||||
|
||||
// Now that we've loaded the menu and thus switched to the previous display plugin
|
||||
// we can unlock the desktop repositioning code, since all the positions will be
|
||||
// relative to the desktop size for this plugin
|
||||
auto offscreenUi = getOffscreenUI();
|
||||
connect(offscreenUi.data(), &OffscreenUi::desktopReady, []() {
|
||||
auto offscreenUi = getOffscreenUI();
|
||||
auto desktop = offscreenUi->getDesktop();
|
||||
if (desktop) {
|
||||
desktop->setProperty("repositionLocked", false);
|
||||
}
|
||||
});
|
||||
|
||||
connect(offscreenUi.data(), &OffscreenUi::keyboardFocusActive, [this]() {
|
||||
#if !defined(Q_OS_ANDROID) && !defined(DISABLE_QML)
|
||||
// Do not show login dialog if requested not to on the command line
|
||||
QString hifiNoLoginCommandLineKey = QString("--").append(HIFI_NO_LOGIN_COMMAND_LINE_KEY);
|
||||
int index = arguments().indexOf(hifiNoLoginCommandLineKey);
|
||||
if (index != -1) {
|
||||
resumeAfterLoginDialogActionTaken();
|
||||
return;
|
||||
}
|
||||
|
||||
showLoginScreen();
|
||||
#else
|
||||
resumeAfterLoginDialogActionTaken();
|
||||
#endif
|
||||
});
|
||||
|
||||
// Make sure we don't time out during slow operations at startup
|
||||
updateHeartbeat();
|
||||
QTimer* settingsTimer = new QTimer();
|
||||
|
@ -1843,12 +1861,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson)) {
|
||||
getMyAvatar()->setBoomLength(MyAvatar::ZOOM_MIN); // So that camera doesn't auto-switch to third person.
|
||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::IndependentMode)) {
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, true);
|
||||
cameraMenuChanged();
|
||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::CameraEntityMode)) {
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, true);
|
||||
cameraMenuChanged();
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -2319,7 +2331,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
DependencyManager::get<PickManager>()->setPrecisionPicking(rayPickID, value);
|
||||
});
|
||||
|
||||
EntityItem::setBillboardRotationOperator([this](const glm::vec3& position, const glm::quat& rotation, BillboardMode billboardMode, const glm::vec3& frustumPos) {
|
||||
EntityItem::setBillboardRotationOperator([](const glm::vec3& position, const glm::quat& rotation, BillboardMode billboardMode, const glm::vec3& frustumPos) {
|
||||
if (billboardMode == BillboardMode::YAW) {
|
||||
//rotate about vertical to face the camera
|
||||
glm::vec3 dPosition = frustumPos - position;
|
||||
|
@ -2347,7 +2359,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
|
||||
DependencyManager::get<UsersScriptingInterface>()->setKickConfirmationOperator([this] (const QUuid& nodeID) { userKickConfirmation(nodeID); });
|
||||
|
||||
render::entities::WebEntityRenderer::setAcquireWebSurfaceOperator([this](const QString& url, bool htmlContent, QSharedPointer<OffscreenQmlSurface>& webSurface, bool& cachedWebSurface) {
|
||||
render::entities::WebEntityRenderer::setAcquireWebSurfaceOperator([=](const QString& url, bool htmlContent, QSharedPointer<OffscreenQmlSurface>& webSurface, bool& cachedWebSurface) {
|
||||
bool isTablet = url == TabletScriptingInterface::QML;
|
||||
if (htmlContent) {
|
||||
webSurface = DependencyManager::get<OffscreenQmlSurfaceCache>()->acquire(render::entities::WebEntityRenderer::QML);
|
||||
|
@ -2372,7 +2384,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
});
|
||||
});
|
||||
auto rootItemLoadedFunctor = [webSurface, url, isTablet] {
|
||||
Application::setupQmlSurface(webSurface->getSurfaceContext(), isTablet || url == LOGIN_DIALOG.toString());
|
||||
Application::setupQmlSurface(webSurface->getSurfaceContext(), isTablet || url == LOGIN_DIALOG.toString() || url == AVATAR_INPUTS_BAR_QML.toString() ||
|
||||
url == BUBBLE_ICON_QML.toString());
|
||||
};
|
||||
if (webSurface->getRootItem()) {
|
||||
rootItemLoadedFunctor();
|
||||
|
@ -2386,7 +2399,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
const uint8_t TABLET_FPS = 90;
|
||||
webSurface->setMaxFps(isTablet ? TABLET_FPS : DEFAULT_MAX_FPS);
|
||||
});
|
||||
render::entities::WebEntityRenderer::setReleaseWebSurfaceOperator([this](QSharedPointer<OffscreenQmlSurface>& webSurface, bool& cachedWebSurface, std::vector<QMetaObject::Connection>& connections) {
|
||||
render::entities::WebEntityRenderer::setReleaseWebSurfaceOperator([=](QSharedPointer<OffscreenQmlSurface>& webSurface, bool& cachedWebSurface, std::vector<QMetaObject::Connection>& connections) {
|
||||
QQuickItem* rootItem = webSurface->getRootItem();
|
||||
|
||||
// Fix for crash in QtWebEngineCore when rapidly switching domains
|
||||
|
@ -2424,6 +2437,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
DependencyManager::get<TabletScriptingInterface>()->preloadSounds();
|
||||
DependencyManager::get<Keyboard>()->createKeyboard();
|
||||
|
||||
FileDialogHelper::setOpenDirectoryOperator([this](const QString& path) { openDirectory(path); });
|
||||
QDesktopServices::setUrlHandler("file", this, "showUrlHandler");
|
||||
QDesktopServices::setUrlHandler("", this, "showUrlHandler");
|
||||
auto drives = QDir::drives();
|
||||
for (auto drive : drives) {
|
||||
QDesktopServices::setUrlHandler(QUrl(drive.absolutePath()).scheme(), this, "showUrlHandler");
|
||||
}
|
||||
|
||||
_pendingIdleEvent = false;
|
||||
_graphicsEngine.startup();
|
||||
|
||||
|
@ -2878,11 +2899,19 @@ void Application::initializeGL() {
|
|||
}
|
||||
|
||||
#if !defined(DISABLE_QML)
|
||||
QStringList chromiumFlags;
|
||||
// Bug 21993: disable microphone and camera input
|
||||
chromiumFlags << "--use-fake-device-for-media-stream";
|
||||
// Disable signed distance field font rendering on ATI/AMD GPUs, due to
|
||||
// https://highfidelity.manuscript.com/f/cases/13677/Text-showing-up-white-on-Marketplace-app
|
||||
std::string vendor{ (const char*)glGetString(GL_VENDOR) };
|
||||
if ((vendor.find("AMD") != std::string::npos) || (vendor.find("ATI") != std::string::npos)) {
|
||||
qputenv("QTWEBENGINE_CHROMIUM_FLAGS", QByteArray("--disable-distance-field-text"));
|
||||
chromiumFlags << "--disable-distance-field-text";
|
||||
}
|
||||
|
||||
// Ensure all Qt webengine processes launched from us have the appropriate command line flags
|
||||
if (!chromiumFlags.empty()) {
|
||||
qputenv("QTWEBENGINE_CHROMIUM_FLAGS", chromiumFlags.join(' ').toLocal8Bit());
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -3291,6 +3320,7 @@ void Application::onDesktopRootItemCreated(QQuickItem* rootItem) {
|
|||
auto qml = PathUtils::qmlUrl("AvatarInputsBar.qml");
|
||||
offscreenUi->show(qml, "AvatarInputsBar");
|
||||
#endif
|
||||
_desktopRootItemCreated = true;
|
||||
}
|
||||
|
||||
void Application::userKickConfirmation(const QUuid& nodeID) {
|
||||
|
@ -3722,14 +3752,11 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
|
|||
|
||||
// If this is a first run we short-circuit the address passed in
|
||||
if (_firstRun.get()) {
|
||||
#if !defined(Q_OS_ANDROID)
|
||||
DependencyManager::get<AddressManager>()->goToEntry();
|
||||
sentTo = SENT_TO_ENTRY;
|
||||
#endif
|
||||
_firstRun.set(false);
|
||||
|
||||
} else {
|
||||
#if !defined(Q_OS_ANDROID)
|
||||
QString goingTo = "";
|
||||
if (addressLookupString.isEmpty()) {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::HomeLocation)) {
|
||||
|
@ -3743,7 +3770,6 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
|
|||
qCDebug(interfaceapp) << "Not first run... going to" << qPrintable(!goingTo.isEmpty() ? goingTo : addressLookupString);
|
||||
DependencyManager::get<AddressManager>()->loadSettings(addressLookupString);
|
||||
sentTo = SENT_TO_PREVIOUS_LOCATION;
|
||||
#endif
|
||||
}
|
||||
|
||||
UserActivityLogger::getInstance().logAction("startup_sent_to", {
|
||||
|
@ -3957,6 +3983,15 @@ static void dumpEventQueue(QThread* thread) {
|
|||
}
|
||||
#endif // DEBUG_EVENT_QUEUE
|
||||
|
||||
bool Application::notify(QObject * object, QEvent * event) {
|
||||
if (thread() == QThread::currentThread()) {
|
||||
PROFILE_RANGE_IF_LONGER(app, "notify", 2)
|
||||
return QApplication::notify(object, event);
|
||||
}
|
||||
|
||||
return QApplication::notify(object, event);
|
||||
}
|
||||
|
||||
bool Application::event(QEvent* event) {
|
||||
|
||||
if (_aboutToQuit) {
|
||||
|
@ -5448,6 +5483,13 @@ void Application::pauseUntilLoginDetermined() {
|
|||
// disconnect domain handler.
|
||||
nodeList->getDomainHandler().disconnect();
|
||||
|
||||
// From now on, it's permissible to call resumeAfterLoginDialogActionTaken()
|
||||
_resumeAfterLoginDialogActionTaken_SafeToRun = true;
|
||||
|
||||
if (_resumeAfterLoginDialogActionTaken_WasPostponed) {
|
||||
// resumeAfterLoginDialogActionTaken() was already called, but it aborted. Now it's safe to call it again.
|
||||
resumeAfterLoginDialogActionTaken();
|
||||
}
|
||||
}
|
||||
|
||||
void Application::resumeAfterLoginDialogActionTaken() {
|
||||
|
@ -5456,6 +5498,11 @@ void Application::resumeAfterLoginDialogActionTaken() {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!_resumeAfterLoginDialogActionTaken_SafeToRun) {
|
||||
_resumeAfterLoginDialogActionTaken_WasPostponed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isHMDMode() && getDesktopTabletBecomesToolbarSetting()) {
|
||||
auto toolbar = DependencyManager::get<ToolbarScriptingInterface>()->getToolbar("com.highfidelity.interface.toolbar.system");
|
||||
toolbar->writeProperty("visible", true);
|
||||
|
@ -5712,9 +5759,6 @@ void Application::cycleCamera() {
|
|||
menu->setIsOptionChecked(MenuOption::ThirdPerson, false);
|
||||
menu->setIsOptionChecked(MenuOption::FullscreenMirror, true);
|
||||
|
||||
} else if (menu->isOptionChecked(MenuOption::IndependentMode) || menu->isOptionChecked(MenuOption::CameraEntityMode)) {
|
||||
// do nothing if in independent or camera entity modes
|
||||
return;
|
||||
}
|
||||
cameraMenuChanged(); // handle the menu change
|
||||
}
|
||||
|
@ -5730,12 +5774,6 @@ void Application::cameraModeChanged() {
|
|||
case CAMERA_MODE_MIRROR:
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, true);
|
||||
break;
|
||||
case CAMERA_MODE_INDEPENDENT:
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::IndependentMode, true);
|
||||
break;
|
||||
case CAMERA_MODE_ENTITY:
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::CameraEntityMode, true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -5779,14 +5817,6 @@ void Application::cameraMenuChanged() {
|
|||
getMyAvatar()->setBoomLength(MyAvatar::ZOOM_DEFAULT);
|
||||
}
|
||||
}
|
||||
} else if (menu->isOptionChecked(MenuOption::IndependentMode)) {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_INDEPENDENT) {
|
||||
_myCamera.setMode(CAMERA_MODE_INDEPENDENT);
|
||||
}
|
||||
} else if (menu->isOptionChecked(MenuOption::CameraEntityMode)) {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_ENTITY) {
|
||||
_myCamera.setMode(CAMERA_MODE_ENTITY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6701,6 +6731,11 @@ void Application::updateRenderArgs(float deltaTime) {
|
|||
}
|
||||
}
|
||||
|
||||
appRenderArgs._renderArgs._stencilMaskMode = getActiveDisplayPlugin()->getStencilMaskMode();
|
||||
if (appRenderArgs._renderArgs._stencilMaskMode == StencilMaskMode::MESH) {
|
||||
appRenderArgs._renderArgs._stencilMaskOperator = getActiveDisplayPlugin()->getStencilMaskMeshOperator();
|
||||
}
|
||||
|
||||
{
|
||||
QMutexLocker viewLocker(&_viewMutex);
|
||||
_myCamera.loadViewFrustum(_displayViewFrustum);
|
||||
|
@ -8235,19 +8270,6 @@ void Application::packageModel() {
|
|||
ModelPackager::package();
|
||||
}
|
||||
|
||||
void Application::openUrl(const QUrl& url) const {
|
||||
if (!url.isEmpty()) {
|
||||
if (url.scheme() == URL_SCHEME_HIFI) {
|
||||
DependencyManager::get<AddressManager>()->handleLookupString(url.toString());
|
||||
} else if (url.scheme() == URL_SCHEME_HIFIAPP) {
|
||||
DependencyManager::get<QmlCommerce>()->openSystemApp(url.path());
|
||||
} else {
|
||||
// address manager did not handle - ask QDesktopServices to handle
|
||||
QDesktopServices::openUrl(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Application::loadDialog() {
|
||||
ModalDialogListener* dlg = OffscreenUi::getOpenFileNameAsync(_glWidget, tr("Open Script"),
|
||||
getPreviousScriptLocation(),
|
||||
|
@ -8388,11 +8410,23 @@ void Application::loadAvatarBrowser() const {
|
|||
DependencyManager::get<HMDScriptingInterface>()->openTablet();
|
||||
}
|
||||
|
||||
void Application::addSnapshotOperator(const SnapshotOperator& snapshotOperator) {
|
||||
std::lock_guard<std::mutex> lock(_snapshotMutex);
|
||||
_snapshotOperators.push(snapshotOperator);
|
||||
_hasPrimarySnapshot = _hasPrimarySnapshot || std::get<2>(snapshotOperator);
|
||||
}
|
||||
|
||||
bool Application::takeSnapshotOperators(std::queue<SnapshotOperator>& snapshotOperators) {
|
||||
std::lock_guard<std::mutex> lock(_snapshotMutex);
|
||||
bool hasPrimarySnapshot = _hasPrimarySnapshot;
|
||||
_hasPrimarySnapshot = false;
|
||||
_snapshotOperators.swap(snapshotOperators);
|
||||
return hasPrimarySnapshot;
|
||||
}
|
||||
|
||||
void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio, const QString& filename) {
|
||||
postLambdaEvent([notify, includeAnimated, aspectRatio, filename, this] {
|
||||
// Get a screenshot and save it
|
||||
QString path = DependencyManager::get<Snapshot>()->saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio), filename,
|
||||
TestScriptingInterface::getInstance()->getTestResultsLocation());
|
||||
addSnapshotOperator(std::make_tuple([notify, includeAnimated, aspectRatio, filename](const QImage& snapshot) {
|
||||
QString path = DependencyManager::get<Snapshot>()->saveSnapshot(snapshot, filename, TestScriptingInterface::getInstance()->getTestResultsLocation());
|
||||
|
||||
// If we're not doing an animated snapshot as well...
|
||||
if (!includeAnimated) {
|
||||
|
@ -8401,19 +8435,20 @@ void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRa
|
|||
emit DependencyManager::get<WindowScriptingInterface>()->stillSnapshotTaken(path, notify);
|
||||
}
|
||||
} else if (!SnapshotAnimated::isAlreadyTakingSnapshotAnimated()) {
|
||||
// Get an animated GIF snapshot and save it
|
||||
SnapshotAnimated::saveSnapshotAnimated(path, aspectRatio, qApp, DependencyManager::get<WindowScriptingInterface>());
|
||||
qApp->postLambdaEvent([path, aspectRatio] {
|
||||
// Get an animated GIF snapshot and save it
|
||||
SnapshotAnimated::saveSnapshotAnimated(path, aspectRatio, DependencyManager::get<WindowScriptingInterface>());
|
||||
});
|
||||
}
|
||||
});
|
||||
}, aspectRatio, true));
|
||||
}
|
||||
|
||||
void Application::takeSecondaryCameraSnapshot(const bool& notify, const QString& filename) {
|
||||
postLambdaEvent([notify, filename, this] {
|
||||
QString snapshotPath = DependencyManager::get<Snapshot>()->saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot(), filename,
|
||||
TestScriptingInterface::getInstance()->getTestResultsLocation());
|
||||
addSnapshotOperator(std::make_tuple([notify, filename](const QImage& snapshot) {
|
||||
QString snapshotPath = DependencyManager::get<Snapshot>()->saveSnapshot(snapshot, filename, TestScriptingInterface::getInstance()->getTestResultsLocation());
|
||||
|
||||
emit DependencyManager::get<WindowScriptingInterface>()->stillSnapshotTaken(snapshotPath, notify);
|
||||
});
|
||||
}, 0.0f, false));
|
||||
}
|
||||
|
||||
void Application::takeSecondaryCamera360Snapshot(const glm::vec3& cameraPosition, const bool& cubemapOutputFormat, const bool& notify, const QString& filename) {
|
||||
|
@ -8972,6 +9007,38 @@ void Application::updateLoginDialogPosition() {
|
|||
}
|
||||
}
|
||||
|
||||
void Application::createAvatarInputsBar() {
|
||||
const glm::vec3 LOCAL_POSITION { 0.0, 0.0, -1.0 };
|
||||
// DEFAULT_DPI / tablet scale percentage
|
||||
const float DPI = 31.0f / (75.0f / 100.0f);
|
||||
|
||||
EntityItemProperties properties;
|
||||
properties.setType(EntityTypes::Web);
|
||||
properties.setName("AvatarInputsBarEntity");
|
||||
properties.setSourceUrl(AVATAR_INPUTS_BAR_QML.toString());
|
||||
properties.setParentID(getMyAvatar()->getSelfID());
|
||||
properties.setParentJointIndex(getMyAvatar()->getJointIndex("_CAMERA_MATRIX"));
|
||||
properties.setPosition(LOCAL_POSITION);
|
||||
properties.setLocalRotation(Quaternions::IDENTITY);
|
||||
//properties.setDimensions(LOGIN_DIMENSIONS);
|
||||
properties.setPrimitiveMode(PrimitiveMode::SOLID);
|
||||
properties.getGrab().setGrabbable(false);
|
||||
properties.setIgnorePickIntersection(false);
|
||||
properties.setAlpha(1.0f);
|
||||
properties.setDPI(DPI);
|
||||
properties.setVisible(true);
|
||||
|
||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||
_avatarInputsBarID = entityScriptingInterface->addEntityInternal(properties, entity::HostType::LOCAL);
|
||||
}
|
||||
|
||||
void Application::destroyAvatarInputsBar() {
|
||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||
if (!_avatarInputsBarID.isNull()) {
|
||||
entityScriptingInterface->deleteEntity(_avatarInputsBarID);
|
||||
}
|
||||
}
|
||||
|
||||
bool Application::hasRiftControllers() {
|
||||
return PluginUtils::isOculusTouchControllerAvailable();
|
||||
}
|
||||
|
@ -9084,7 +9151,7 @@ void Application::readArgumentsFromLocalSocket() const {
|
|||
|
||||
// If we received a message, try to open it as a URL
|
||||
if (message.length() > 0) {
|
||||
qApp->openUrl(QString::fromUtf8(message));
|
||||
DependencyManager::get<WindowScriptingInterface>()->openUrl(QString::fromUtf8(message));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9201,6 +9268,44 @@ QString Application::getGraphicsCardType() {
|
|||
return GPUIdent::getInstance()->getName();
|
||||
}
|
||||
|
||||
void Application::openDirectory(const QString& path) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "openDirectory", Q_ARG(const QString&, path));
|
||||
return;
|
||||
}
|
||||
|
||||
QString dirPath = path;
|
||||
const QString FILE_SCHEME = "file:///";
|
||||
if (dirPath.startsWith(FILE_SCHEME)) {
|
||||
dirPath.remove(0, FILE_SCHEME.length());
|
||||
}
|
||||
QFileInfo fileInfo(dirPath);
|
||||
if (fileInfo.isDir()) {
|
||||
auto scheme = QUrl(path).scheme();
|
||||
QDesktopServices::unsetUrlHandler(scheme);
|
||||
QDesktopServices::openUrl(path);
|
||||
QDesktopServices::setUrlHandler(scheme, this, "showUrlHandler");
|
||||
}
|
||||
}
|
||||
|
||||
void Application::showUrlHandler(const QUrl& url) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "showUrlHandler", Q_ARG(const QUrl&, url));
|
||||
return;
|
||||
}
|
||||
|
||||
ModalDialogListener* dlg = OffscreenUi::asyncQuestion("Confirm openUrl", "Do you recognize this path or code and want to open or execute it: " + url.toDisplayString());
|
||||
QObject::connect(dlg, &ModalDialogListener::response, this, [=](QVariant answer) {
|
||||
QObject::disconnect(dlg, &ModalDialogListener::response, this, nullptr);
|
||||
if (QMessageBox::Yes == static_cast<QMessageBox::StandardButton>(answer.toInt())) {
|
||||
// Unset the handler, open the URL, and the reset the handler
|
||||
QDesktopServices::unsetUrlHandler(url.scheme());
|
||||
QDesktopServices::openUrl(url);
|
||||
QDesktopServices::setUrlHandler(url.scheme(), this, "showUrlHandler");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
void Application::beforeEnterBackground() {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
@ -9243,6 +9348,3 @@ void Application::toggleAwayMode(){
|
|||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#include "Application.moc"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//
|
||||
//
|
||||
// Application.h
|
||||
// interface/src
|
||||
//
|
||||
|
@ -156,6 +156,7 @@ public:
|
|||
void updateCamera(RenderArgs& renderArgs, float deltaTime);
|
||||
void resizeGL();
|
||||
|
||||
bool notify(QObject *, QEvent *) override;
|
||||
bool event(QEvent* event) override;
|
||||
bool eventFilter(QObject* object, QEvent* event) override;
|
||||
|
||||
|
@ -330,6 +331,9 @@ public:
|
|||
void createLoginDialog();
|
||||
void updateLoginDialogPosition();
|
||||
|
||||
void createAvatarInputsBar();
|
||||
void destroyAvatarInputsBar();
|
||||
|
||||
// Check if a headset is connected
|
||||
bool hasRiftControllers();
|
||||
bool hasViveControllers();
|
||||
|
@ -341,6 +345,12 @@ public:
|
|||
void toggleAwayMode();
|
||||
#endif
|
||||
|
||||
using SnapshotOperator = std::tuple<std::function<void(const QImage&)>, float, bool>;
|
||||
void addSnapshotOperator(const SnapshotOperator& snapshotOperator);
|
||||
bool takeSnapshotOperators(std::queue<SnapshotOperator>& snapshotOperators);
|
||||
|
||||
void openDirectory(const QString& path);
|
||||
|
||||
signals:
|
||||
void svoImportRequested(const QString& url);
|
||||
|
||||
|
@ -400,8 +410,6 @@ public slots:
|
|||
|
||||
static void packageModel();
|
||||
|
||||
void openUrl(const QUrl& url) const;
|
||||
|
||||
void resetSensors(bool andReload = false);
|
||||
void setActiveFaceTracker() const;
|
||||
|
||||
|
@ -469,6 +477,7 @@ public slots:
|
|||
QString getGraphicsCardType();
|
||||
|
||||
bool gpuTextureMemSizeStable();
|
||||
void showUrlHandler(const QUrl& url);
|
||||
|
||||
private slots:
|
||||
void onDesktopRootItemCreated(QQuickItem* qmlContext);
|
||||
|
@ -705,12 +714,14 @@ private:
|
|||
int _maxOctreePPS = DEFAULT_MAX_OCTREE_PPS;
|
||||
bool _interstitialModeEnabled{ false };
|
||||
|
||||
bool _loginDialogPoppedUp = false;
|
||||
bool _loginDialogPoppedUp{ false };
|
||||
bool _desktopRootItemCreated{ false };
|
||||
bool _developerMenuVisible{ false };
|
||||
QString _previousAvatarSkeletonModel;
|
||||
float _previousAvatarTargetScale;
|
||||
CameraMode _previousCameraMode;
|
||||
QUuid _loginDialogID;
|
||||
QUuid _avatarInputsBarID;
|
||||
LoginStateManager _loginStateManager;
|
||||
|
||||
quint64 _lastFaceTrackerUpdate;
|
||||
|
@ -784,6 +795,9 @@ private:
|
|||
AudioInjectorPointer _snapshotSoundInjector;
|
||||
SharedSoundPointer _snapshotSound;
|
||||
SharedSoundPointer _sampleSound;
|
||||
std::mutex _snapshotMutex;
|
||||
std::queue<SnapshotOperator> _snapshotOperators;
|
||||
bool _hasPrimarySnapshot { false };
|
||||
|
||||
DisplayPluginPointer _autoSwitchDisplayModeSupportedHMDPlugin;
|
||||
QString _autoSwitchDisplayModeSupportedHMDPluginName;
|
||||
|
@ -803,5 +817,8 @@ private:
|
|||
|
||||
bool _showTrackedObjects { false };
|
||||
bool _prevShowTrackedObjects { false };
|
||||
|
||||
bool _resumeAfterLoginDialogActionTaken_WasPostponed { false };
|
||||
bool _resumeAfterLoginDialogActionTaken_SafeToRun { false };
|
||||
};
|
||||
#endif // hifi_Application_h
|
||||
|
|
|
@ -19,14 +19,22 @@ class FancyCamera : public Camera {
|
|||
Q_OBJECT
|
||||
|
||||
/**jsdoc
|
||||
* @namespace
|
||||
* @augments Camera
|
||||
*/
|
||||
|
||||
// FIXME: JSDoc 3.5.5 doesn't augment @property definitions. The following definition is repeated in Camera.h.
|
||||
/**jsdoc
|
||||
* @property {Uuid} cameraEntity The ID of the entity that the camera position and orientation follow when the camera is in
|
||||
* entity mode.
|
||||
* The <code>Camera</code> API provides access to the "camera" that defines your view in desktop and HMD display modes.
|
||||
*
|
||||
* @namespace Camera
|
||||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
*
|
||||
* @property {Vec3} position - The position of the camera. You can set this value only when the camera is in independent
|
||||
* mode.
|
||||
* @property {Quat} orientation - The orientation of the camera. You can set this value only when the camera is in
|
||||
* independent mode.
|
||||
* @property {Camera.Mode} mode - The camera mode.
|
||||
* @property {ViewFrustum} frustum - The camera frustum.
|
||||
* @property {Uuid} cameraEntity - The ID of the entity that is used for the camera position and orientation when the
|
||||
* camera is in entity mode.
|
||||
*/
|
||||
Q_PROPERTY(QUuid cameraEntity READ getCameraEntity WRITE setCameraEntity)
|
||||
|
||||
|
@ -38,25 +46,25 @@ public:
|
|||
|
||||
|
||||
public slots:
|
||||
/**jsdoc
|
||||
* Get the ID of the entity that the camera is set to use the position and orientation from when it's in entity mode. You can
|
||||
* also get the entity ID using the <code>Camera.cameraEntity</code> property.
|
||||
* @function Camera.getCameraEntity
|
||||
* @returns {Uuid} The ID of the entity that the camera is set to follow when in entity mode; <code>null</code> if no camera
|
||||
* entity has been set.
|
||||
*/
|
||||
/**jsdoc
|
||||
* Gets the ID of the entity that the camera is set to follow (i.e., use the position and orientation from) when it's in
|
||||
* entity mode. You can also get the entity ID using the {@link Camera|Camera.cameraEntity} property.
|
||||
* @function Camera.getCameraEntity
|
||||
* @returns {Uuid} The ID of the entity that the camera is set to follow when in entity mode; <code>null</code> if no
|
||||
* camera entity has been set.
|
||||
*/
|
||||
QUuid getCameraEntity() const;
|
||||
|
||||
/**jsdoc
|
||||
* Set the entity that the camera should use the position and orientation from when it's in entity mode. You can also set the
|
||||
* entity using the <code>Camera.cameraEntity</code> property.
|
||||
* @function Camera.setCameraEntity
|
||||
* @param {Uuid} entityID The entity that the camera should follow when it's in entity mode.
|
||||
* @example <caption>Move your camera to the position and orientation of the closest entity.</caption>
|
||||
* Camera.setModeString("entity");
|
||||
* var entity = Entities.findClosestEntity(MyAvatar.position, 100.0);
|
||||
* Camera.setCameraEntity(entity);
|
||||
*/
|
||||
* Sets the entity that the camera should follow (i.e., use the position and orientation from) when it's in entity mode.
|
||||
* You can also set the entity using the {@link Camera|Camera.cameraEntity} property.
|
||||
* @function Camera.setCameraEntity
|
||||
* @param {Uuid} entityID - The entity that the camera should follow when it's in entity mode.
|
||||
* @example <caption>Move your camera to the position and orientation of the closest entity.</caption>
|
||||
* Camera.setModeString("entity");
|
||||
* var entity = Entities.findClosestEntity(MyAvatar.position, 100.0);
|
||||
* Camera.setCameraEntity(entity);
|
||||
*/
|
||||
void setCameraEntity(QUuid entityID);
|
||||
|
||||
private:
|
||||
|
|
|
@ -116,7 +116,7 @@ Menu::Menu() {
|
|||
// Edit > Delete
|
||||
auto deleteAction = addActionToQMenuAndActionHash(editMenu, "Delete", QKeySequence::Delete);
|
||||
connect(deleteAction, &QAction::triggered, [] {
|
||||
QKeyEvent* keyEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_Delete, Qt::ControlModifier);
|
||||
QKeyEvent* keyEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_Delete, Qt::NoModifier);
|
||||
QCoreApplication::postEvent(QCoreApplication::instance(), keyEvent);
|
||||
});
|
||||
|
||||
|
@ -194,20 +194,6 @@ Menu::Menu() {
|
|||
|
||||
viewMirrorAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup));
|
||||
|
||||
// View > Independent
|
||||
auto viewIndependentAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||
MenuOption::IndependentMode, 0,
|
||||
false, qApp, SLOT(cameraMenuChanged())));
|
||||
|
||||
viewIndependentAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup));
|
||||
|
||||
// View > Entity Camera
|
||||
auto viewEntityCameraAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||
MenuOption::CameraEntityMode, 0,
|
||||
false, qApp, SLOT(cameraMenuChanged())));
|
||||
|
||||
viewEntityCameraAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup));
|
||||
|
||||
viewMenu->addSeparator();
|
||||
|
||||
// View > Center Player In View
|
||||
|
@ -707,8 +693,7 @@ Menu::Menu() {
|
|||
// Developer > Timing >>>
|
||||
MenuWrapper* timingMenu = developerMenu->addMenu("Timing");
|
||||
MenuWrapper* perfTimerMenu = timingMenu->addMenu("Performance Timer");
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayDebugTimingDetails, 0, false,
|
||||
qApp, SLOT(enablePerfStats(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayDebugTimingDetails);
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::OnlyDisplayTopTen, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandUpdateTiming, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandSimulationTiming, 0, false);
|
||||
|
|
|
@ -53,7 +53,6 @@ namespace MenuOption {
|
|||
const QString BookmarkAvatarEntities = "Bookmark Avatar Entities";
|
||||
const QString BookmarkLocation = "Bookmark Location";
|
||||
const QString CalibrateCamera = "Calibrate Camera";
|
||||
const QString CameraEntityMode = "Entity Mode";
|
||||
const QString CenterPlayerInView = "Center Player In View";
|
||||
const QString Chat = "Chat...";
|
||||
const QString ClearDiskCache = "Clear Disk Cache";
|
||||
|
@ -120,7 +119,6 @@ namespace MenuOption {
|
|||
const QString Help = "Help...";
|
||||
const QString HomeLocation = "Home ";
|
||||
const QString IncreaseAvatarSize = "Increase Avatar Size";
|
||||
const QString IndependentMode = "Independent Mode";
|
||||
const QString ActionMotorControl = "Enable Default Motor Control";
|
||||
const QString LastLocation = "Last Location";
|
||||
const QString LoadScript = "Open and Run Script File...";
|
||||
|
|
|
@ -18,9 +18,6 @@
|
|||
#include <QPushButton>
|
||||
#include <QStandardPaths>
|
||||
|
||||
static const QString AVATAR_HEAD_AND_BODY_STRING = "Avatar Body with Head";
|
||||
static const QString ENTITY_MODEL_STRING = "Entity Model";
|
||||
|
||||
ModelSelector::ModelSelector() {
|
||||
QFormLayout* form = new QFormLayout(this);
|
||||
|
||||
|
|
|
@ -152,10 +152,12 @@ public:
|
|||
_cachedArgsPointer->_viewport = args->_viewport;
|
||||
_cachedArgsPointer->_displayMode = args->_displayMode;
|
||||
_cachedArgsPointer->_renderMode = args->_renderMode;
|
||||
_cachedArgsPointer->_stencilMaskMode = args->_stencilMaskMode;
|
||||
args->_blitFramebuffer = destFramebuffer;
|
||||
args->_viewport = glm::ivec4(0, 0, destFramebuffer->getWidth(), destFramebuffer->getHeight());
|
||||
args->_displayMode = RenderArgs::MONO;
|
||||
args->_renderMode = RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE;
|
||||
args->_stencilMaskMode = StencilMaskMode::NONE;
|
||||
|
||||
gpu::doInBatch("SecondaryCameraJob::run", args->_context, [&](gpu::Batch& batch) {
|
||||
batch.disableContextStereo();
|
||||
|
@ -255,10 +257,11 @@ public:
|
|||
void run(const render::RenderContextPointer& renderContext, const RenderArgsPointer& cachedArgs) {
|
||||
auto args = renderContext->args;
|
||||
if (cachedArgs) {
|
||||
args->_blitFramebuffer = cachedArgs->_blitFramebuffer;
|
||||
args->_viewport = cachedArgs->_viewport;
|
||||
args->_displayMode = cachedArgs->_displayMode;
|
||||
args->_renderMode = cachedArgs->_renderMode;
|
||||
args->_blitFramebuffer = cachedArgs->_blitFramebuffer;
|
||||
args->_viewport = cachedArgs->_viewport;
|
||||
args->_displayMode = cachedArgs->_displayMode;
|
||||
args->_renderMode = cachedArgs->_renderMode;
|
||||
args->_stencilMaskMode = cachedArgs->_stencilMaskMode;
|
||||
}
|
||||
args->popViewFrustum();
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include <UsersScriptingInterface.h>
|
||||
#include <UUID.h>
|
||||
#include <shared/ConicalViewFrustum.h>
|
||||
#include <ui/AvatarInputs.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "InterfaceLogging.h"
|
||||
|
@ -84,7 +85,6 @@ AvatarManager::AvatarManager(QObject* parent) :
|
|||
|
||||
AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer) {
|
||||
AvatarSharedPointer avatar = AvatarHashMap::addAvatar(sessionUUID, mixerWeakPointer);
|
||||
|
||||
const auto otherAvatar = std::static_pointer_cast<OtherAvatar>(avatar);
|
||||
if (otherAvatar && _space) {
|
||||
std::unique_lock<std::mutex> lock(_spaceLock);
|
||||
|
@ -210,7 +210,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
{
|
||||
// lock the hash for read to check the size
|
||||
QReadLocker lock(&_hashLock);
|
||||
if (_avatarHash.size() < 2 && _avatarsToFadeOut.isEmpty()) {
|
||||
if (_avatarHash.size() < 2) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -375,19 +375,12 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
qApp->getMain3DScene()->enqueueTransaction(renderTransaction);
|
||||
}
|
||||
|
||||
if (!_spaceProxiesToDelete.empty() && _space) {
|
||||
std::unique_lock<std::mutex> lock(_spaceLock);
|
||||
workloadTransaction.remove(_spaceProxiesToDelete);
|
||||
_spaceProxiesToDelete.clear();
|
||||
}
|
||||
_space->enqueueTransaction(workloadTransaction);
|
||||
|
||||
_numAvatarsUpdated = numAvatarsUpdated;
|
||||
_numAvatarsNotUpdated = numAvatarsNotUpdated;
|
||||
_numHeroAvatarsUpdated = numHerosUpdated;
|
||||
|
||||
simulateAvatarFades(deltaTime);
|
||||
|
||||
_avatarSimulationTime = (float)(usecTimestampNow() - startTime) / (float)USECS_PER_MSEC;
|
||||
}
|
||||
|
||||
|
@ -400,31 +393,6 @@ void AvatarManager::postUpdate(float deltaTime, const render::ScenePointer& scen
|
|||
}
|
||||
}
|
||||
|
||||
void AvatarManager::simulateAvatarFades(float deltaTime) {
|
||||
if (_avatarsToFadeOut.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QReadLocker locker(&_hashLock);
|
||||
QVector<AvatarSharedPointer>::iterator avatarItr = _avatarsToFadeOut.begin();
|
||||
const render::ScenePointer& scene = qApp->getMain3DScene();
|
||||
render::Transaction transaction;
|
||||
while (avatarItr != _avatarsToFadeOut.end()) {
|
||||
auto avatar = std::static_pointer_cast<Avatar>(*avatarItr);
|
||||
avatar->updateFadingStatus();
|
||||
if (!avatar->isFading()) {
|
||||
// fading to zero is such a rare event we push a unique transaction for each
|
||||
if (avatar->isInScene()) {
|
||||
avatar->removeFromScene(*avatarItr, scene, transaction);
|
||||
}
|
||||
avatarItr = _avatarsToFadeOut.erase(avatarItr);
|
||||
} else {
|
||||
++avatarItr;
|
||||
}
|
||||
}
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
|
||||
AvatarSharedPointer AvatarManager::newSharedAvatar(const QUuid& sessionUUID) {
|
||||
auto otherAvatar = new OtherAvatar(qApp->thread());
|
||||
otherAvatar->setSessionUUID(sessionUUID);
|
||||
|
@ -452,7 +420,6 @@ void AvatarManager::buildPhysicsTransaction(PhysicsEngine::Transaction& transact
|
|||
transaction.objectsToRemove.push_back(mState);
|
||||
}
|
||||
avatar->resetDetailedMotionStates();
|
||||
|
||||
} else {
|
||||
if (avatar->getDetailedMotionStates().size() == 0) {
|
||||
avatar->createDetailedMotionStates(avatar);
|
||||
|
@ -520,10 +487,6 @@ void AvatarManager::removeDeadAvatarEntities(const SetOfEntities& deadEntities)
|
|||
|
||||
void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) {
|
||||
auto avatar = std::static_pointer_cast<OtherAvatar>(removedAvatar);
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_spaceLock);
|
||||
_spaceProxiesToDelete.push_back(avatar->getSpaceIndex());
|
||||
}
|
||||
AvatarHashMap::handleRemovedAvatar(avatar, removalReason);
|
||||
avatar->tearDownGrabs();
|
||||
|
||||
|
@ -534,16 +497,41 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar
|
|||
// it might not fire until after we create a new instance for the same remote avatar, which creates a race
|
||||
// on the creation of entities for that avatar instance and the deletion of entities for this instance
|
||||
avatar->removeAvatarEntitiesFromTree();
|
||||
if (removalReason != KillAvatarReason::AvatarDisconnected) {
|
||||
if (removalReason == KillAvatarReason::TheirAvatarEnteredYourBubble) {
|
||||
emit AvatarInputs::getInstance()->avatarEnteredIgnoreRadius(avatar->getSessionUUID());
|
||||
emit DependencyManager::get<UsersScriptingInterface>()->enteredIgnoreRadius();
|
||||
}
|
||||
|
||||
if (removalReason == KillAvatarReason::TheirAvatarEnteredYourBubble) {
|
||||
emit DependencyManager::get<UsersScriptingInterface>()->enteredIgnoreRadius();
|
||||
} else if (removalReason == KillAvatarReason::AvatarDisconnected) {
|
||||
workload::Transaction workloadTransaction;
|
||||
workloadTransaction.remove(avatar->getSpaceIndex());
|
||||
_space->enqueueTransaction(workloadTransaction);
|
||||
|
||||
const render::ScenePointer& scene = qApp->getMain3DScene();
|
||||
render::Transaction transaction;
|
||||
avatar->removeFromScene(avatar, scene, transaction);
|
||||
scene->enqueueTransaction(transaction);
|
||||
} else {
|
||||
// remove from node sets, if present
|
||||
DependencyManager::get<NodeList>()->removeFromIgnoreMuteSets(avatar->getSessionUUID());
|
||||
DependencyManager::get<UsersScriptingInterface>()->avatarDisconnected(avatar->getSessionUUID());
|
||||
avatar->fadeOut(qApp->getMain3DScene(), removalReason);
|
||||
render::Transaction transaction;
|
||||
auto scene = qApp->getMain3DScene();
|
||||
avatar->fadeOut(transaction, removalReason);
|
||||
|
||||
workload::SpacePointer space = _space;
|
||||
transaction.transitionFinishedOperator(avatar->getRenderItemID(), [space, avatar]() {
|
||||
const render::ScenePointer& scene = qApp->getMain3DScene();
|
||||
render::Transaction transaction;
|
||||
avatar->removeFromScene(avatar, scene, transaction);
|
||||
scene->enqueueTransaction(transaction);
|
||||
|
||||
workload::Transaction workloadTransaction;
|
||||
workloadTransaction.remove(avatar->getSpaceIndex());
|
||||
space->enqueueTransaction(workloadTransaction);
|
||||
});
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
_avatarsToFadeOut.push_back(removedAvatar);
|
||||
}
|
||||
|
||||
void AvatarManager::clearOtherAvatars() {
|
||||
|
@ -739,7 +727,7 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic
|
|||
boxHit._distance = FLT_MAX;
|
||||
|
||||
for (size_t i = 0; i < hit._boundJoints.size(); i++) {
|
||||
assert(hit._boundJoints[i] < multiSpheres.size());
|
||||
assert(hit._boundJoints[i] < (int)multiSpheres.size());
|
||||
auto &mSphere = multiSpheres[hit._boundJoints[i]];
|
||||
if (mSphere.isValid()) {
|
||||
float boundDistance = FLT_MAX;
|
||||
|
@ -946,6 +934,18 @@ void AvatarManager::setAvatarSortCoefficient(const QString& name, const QScriptV
|
|||
}
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* PAL (People Access List) data for an avatar.
|
||||
* @typedef {object} AvatarManager.PalData
|
||||
* @property {Uuid} sessionUUID - The avatar's session ID. <code>""</code> if the avatar is your own.
|
||||
* @property {string} sessionDisplayName - The avatar's display name, sanitized and versioned, as defined by the avatar mixer.
|
||||
* It is unique among all avatars present in the domain at the time.
|
||||
* @property {number} audioLoudness - The instantaneous loudness of the audio input that the avatar is injecting into the
|
||||
* domain.
|
||||
* @property {boolean} isReplicated - <strong>Deprecated.</strong>
|
||||
* @property {Vec3} position - The position of the avatar.
|
||||
* @property {number} palOrbOffset - The vertical offset from the avatar's position that an overlay orb should be displayed at.
|
||||
*/
|
||||
QVariantMap AvatarManager::getPalData(const QStringList& specificAvatarIdentifiers) {
|
||||
QJsonArray palData;
|
||||
|
||||
|
|
|
@ -37,10 +37,11 @@
|
|||
using SortedAvatar = std::pair<float, std::shared_ptr<Avatar>>;
|
||||
|
||||
/**jsdoc
|
||||
* The AvatarManager API has properties and methods which manage Avatars within the same domain.
|
||||
* The <code>AvatarManager</code> API provides information about avatars within the current domain. The avatars available are
|
||||
* those that Interface has displayed and therefore knows about.
|
||||
*
|
||||
* <p><strong>Note:</strong> This API is also provided to Interface and client entity scripts as the synonym,
|
||||
* <code>AvatarList</code>. For assignment client scripts, see the separate {@link AvatarList} API.
|
||||
* <p><strong>Warning:</strong> This API is also provided to Interface, client entity, and avatar scripts as the synonym,
|
||||
* "<code>AvatarList</code>". For assignment client scripts, see the separate {@link AvatarList} API.</p>
|
||||
*
|
||||
* @namespace AvatarManager
|
||||
*
|
||||
|
@ -48,8 +49,9 @@ using SortedAvatar = std::pair<float, std::shared_ptr<Avatar>>;
|
|||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
*
|
||||
* @borrows AvatarList.getAvatarIdentifiers as getAvatarIdentifiers
|
||||
* @borrows AvatarList.getAvatarsInRange as getAvatarsInRange
|
||||
* @borrows AvatarList.getAvatar as getAvatar
|
||||
* @comment AvatarList.getAvatarIdentifiers as getAvatarIdentifiers - Don't borrow because behavior is slightly different.
|
||||
* @comment AvatarList.getAvatarsInRange as getAvatarsInRange - Don't borrow because behavior is slightly different.
|
||||
* @borrows AvatarList.avatarAddedEvent as avatarAddedEvent
|
||||
* @borrows AvatarList.avatarRemovedEvent as avatarRemovedEvent
|
||||
* @borrows AvatarList.avatarSessionChangedEvent as avatarSessionChangedEvent
|
||||
|
@ -67,6 +69,31 @@ class AvatarManager : public AvatarHashMap {
|
|||
|
||||
public:
|
||||
|
||||
/**jsdoc
|
||||
* Gets the IDs of all avatars known about in the domain.
|
||||
* Your own avatar is included in the list as a <code>null</code> value.
|
||||
* @function AvatarManager.getAvatarIdentifiers
|
||||
* @returns {Uuid[]} The IDs of all known avatars in the domain.
|
||||
* @example <caption>Report the IDS of all avatars within the domain.</caption>
|
||||
* var avatars = AvatarManager.getAvatarIdentifiers();
|
||||
* print("Avatars in the domain: " + JSON.stringify(avatars));
|
||||
* // A null item is included for your avatar.
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* Gets the IDs of all avatars known about within a specified distance from a point.
|
||||
* Your own avatar's ID is included in the list if it is in range.
|
||||
* @function AvatarManager.getAvatarsInRange
|
||||
* @param {Vec3} position - The point about which the search is performed.
|
||||
* @param {number} range - The search radius.
|
||||
* @returns {Uuid[]} The IDs of all known avatars within the search distance from the position.
|
||||
* @example <caption>Report the IDs of all avatars within 10m of your avatar.</caption>
|
||||
* var RANGE = 10;
|
||||
* var avatars = AvatarManager.getAvatarsInRange(MyAvatar.position, RANGE);
|
||||
* print("Nearby avatars: " + JSON.stringify(avatars));
|
||||
* print("Own avatar: " + MyAvatar.sessionUUID);
|
||||
*/
|
||||
|
||||
/// Registers the script types associated with the avatar manager.
|
||||
static void registerMetaTypes(QScriptEngine* engine);
|
||||
|
||||
|
@ -79,9 +106,7 @@ public:
|
|||
glm::vec3 getMyAvatarPosition() const { return _myAvatar->getWorldPosition(); }
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarManager.getAvatar
|
||||
* @param {Uuid} avatarID
|
||||
* @returns {AvatarData}
|
||||
* @comment Uses the base class's JSDoc.
|
||||
*/
|
||||
// Null/Default-constructed QUuids will return MyAvatar
|
||||
Q_INVOKABLE virtual ScriptAvatarData* getAvatar(QUuid avatarID) override { return new ScriptAvatar(getAvatarBySessionID(avatarID)); }
|
||||
|
@ -112,36 +137,53 @@ public:
|
|||
void handleCollisionEvents(const CollisionEvents& collisionEvents);
|
||||
|
||||
/**jsdoc
|
||||
* Gets the amount of avatar mixer data being generated by an avatar other than your own.
|
||||
* @function AvatarManager.getAvatarDataRate
|
||||
* @param {Uuid} sessionID
|
||||
* @param {string} [rateName=""]
|
||||
* @returns {number}
|
||||
* @param {Uuid} sessionID - The ID of the avatar whose data rate you're retrieving.
|
||||
* @param {AvatarDataRate} [rateName=""] - The type of avatar mixer data to get the data rate of.
|
||||
* @returns {number} The data rate in kbps; <code>0</code> if the avatar is your own.
|
||||
*/
|
||||
Q_INVOKABLE float getAvatarDataRate(const QUuid& sessionID, const QString& rateName = QString("")) const;
|
||||
|
||||
/**jsdoc
|
||||
* Gets the update rate of avatar mixer data being generated by an avatar other than your own.
|
||||
* @function AvatarManager.getAvatarUpdateRate
|
||||
* @param {Uuid} sessionID
|
||||
* @param {string} [rateName=""]
|
||||
* @returns {number}
|
||||
* @param {Uuid} sessionID - The ID of the avatar whose update rate you're retrieving.
|
||||
* @param {AvatarUpdateRate} [rateName=""] - The type of avatar mixer data to get the update rate of.
|
||||
* @returns {number} The update rate in Hz; <code>0</code> if the avatar is your own.
|
||||
*/
|
||||
Q_INVOKABLE float getAvatarUpdateRate(const QUuid& sessionID, const QString& rateName = QString("")) const;
|
||||
|
||||
/**jsdoc
|
||||
* Gets the simulation rate of an avatar other than your own.
|
||||
* @function AvatarManager.getAvatarSimulationRate
|
||||
* @param {Uuid} sessionID
|
||||
* @param {string} [rateName=""]
|
||||
* @returns {number}
|
||||
* @param {Uuid} sessionID - The ID of the avatar whose simulation you're retrieving.
|
||||
* @param {AvatarSimulationRate} [rateName=""] - The type of avatar data to get the simulation rate of.
|
||||
* @returns {number} The simulation rate in Hz; <code>0</code> if the avatar is your own.
|
||||
*/
|
||||
Q_INVOKABLE float getAvatarSimulationRate(const QUuid& sessionID, const QString& rateName = QString("")) const;
|
||||
|
||||
/**jsdoc
|
||||
* Find the first avatar intersected by a {@link PickRay}.
|
||||
* @function AvatarManager.findRayIntersection
|
||||
* @param {PickRay} ray
|
||||
* @param {Uuid[]} [avatarsToInclude=[]]
|
||||
* @param {Uuid[]} [avatarsToDiscard=[]]
|
||||
* @param {boolean} pickAgainstMesh
|
||||
* @returns {RayToAvatarIntersectionResult}
|
||||
* @param {PickRay} ray - The ray to use for finding avatars.
|
||||
* @param {Uuid[]} [avatarsToInclude=[]] - If not empty then search is restricted to these avatars.
|
||||
* @param {Uuid[]} [avatarsToDiscard=[]] - Avatars to ignore in the search.
|
||||
* @param {boolean} [pickAgainstMesh=true] - If <code>true</code> then the exact intersection with the avatar mesh is
|
||||
* calculated, if <code>false</code> then the intersection is approximate.
|
||||
* @returns {RayToAvatarIntersectionResult} The result of the search for the first intersected avatar.
|
||||
* @example <caption>Find the first avatar directly in front of you.</caption>
|
||||
* var pickRay = {
|
||||
* origin: MyAvatar.position,
|
||||
* direction: Quat.getFront(MyAvatar.orientation)
|
||||
* };
|
||||
*
|
||||
* var intersection = AvatarManager.findRayIntersection(pickRay);
|
||||
* if (intersection.intersects) {
|
||||
* print("Avatar found: " + JSON.stringify(intersection));
|
||||
* } else {
|
||||
* print("No avatar found.");
|
||||
* }
|
||||
*/
|
||||
Q_INVOKABLE RayToAvatarIntersectionResult findRayIntersection(const PickRay& ray,
|
||||
const QScriptValue& avatarIdsToInclude = QScriptValue(),
|
||||
|
@ -149,11 +191,12 @@ public:
|
|||
bool pickAgainstMesh = true);
|
||||
/**jsdoc
|
||||
* @function AvatarManager.findRayIntersectionVector
|
||||
* @param {PickRay} ray
|
||||
* @param {Uuid[]} avatarsToInclude
|
||||
* @param {Uuid[]} avatarsToDiscard
|
||||
* @param {boolean} pickAgainstMesh
|
||||
* @returns {RayToAvatarIntersectionResult}
|
||||
* @param {PickRay} ray - Ray.
|
||||
* @param {Uuid[]} avatarsToInclude - Avatars to include.
|
||||
* @param {Uuid[]} avatarsToDiscard - Avatars to discard.
|
||||
* @param {boolean} pickAgainstMesh - Pick against mesh.
|
||||
* @returns {RayToAvatarIntersectionResult} Intersection result.
|
||||
* @deprecated This function is deprecated and will be removed.
|
||||
*/
|
||||
Q_INVOKABLE RayToAvatarIntersectionResult findRayIntersectionVector(const PickRay& ray,
|
||||
const QVector<EntityItemID>& avatarsToInclude,
|
||||
|
@ -162,10 +205,11 @@ public:
|
|||
|
||||
/**jsdoc
|
||||
* @function AvatarManager.findParabolaIntersectionVector
|
||||
* @param {PickParabola} pick
|
||||
* @param {Uuid[]} avatarsToInclude
|
||||
* @param {Uuid[]} avatarsToDiscard
|
||||
* @returns {ParabolaToAvatarIntersectionResult}
|
||||
* @param {PickParabola} pick - Pick.
|
||||
* @param {Uuid[]} avatarsToInclude - Avatars to include.
|
||||
* @param {Uuid[]} avatarsToDiscard - Avatars to discard.
|
||||
* @returns {ParabolaToAvatarIntersectionResult} Intersection result.
|
||||
* @deprecated This function is deprecated and will be removed.
|
||||
*/
|
||||
Q_INVOKABLE ParabolaToAvatarIntersectionResult findParabolaIntersectionVector(const PickParabola& pick,
|
||||
const QVector<EntityItemID>& avatarsToInclude,
|
||||
|
@ -173,27 +217,31 @@ public:
|
|||
|
||||
/**jsdoc
|
||||
* @function AvatarManager.getAvatarSortCoefficient
|
||||
* @param {string} name
|
||||
* @returns {number}
|
||||
* @param {string} name - Name.
|
||||
* @returns {number} Value.
|
||||
* @deprecated This function is deprecated and will be removed.
|
||||
*/
|
||||
// TODO: remove this HACK once we settle on optimal default sort coefficients
|
||||
Q_INVOKABLE float getAvatarSortCoefficient(const QString& name);
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarManager.setAvatarSortCoefficient
|
||||
* @param {string} name
|
||||
* @param {number} value
|
||||
* @param {string} name - Name
|
||||
* @param {number} value - Value.
|
||||
* @deprecated This function is deprecated and will be removed.
|
||||
*/
|
||||
Q_INVOKABLE void setAvatarSortCoefficient(const QString& name, const QScriptValue& value);
|
||||
|
||||
/**jsdoc
|
||||
* Used in the PAL for getting PAL-related data about avatars nearby. Using this method is faster
|
||||
* than iterating over each avatar and obtaining data about them in JavaScript, as that method
|
||||
* locks and unlocks each avatar's data structure potentially hundreds of times per update tick.
|
||||
* Gets PAL (People Access List) data for one or more avatars. Using this method is faster than iterating over each avatar
|
||||
* and obtaining data about each individually.
|
||||
* @function AvatarManager.getPalData
|
||||
* @param {string[]} [specificAvatarIdentifiers=[]] - The list of IDs of the avatars you want the PAL data for.
|
||||
* If an empty list, the PAL data for all nearby avatars is returned.
|
||||
* @returns {object[]} An array of objects, each object being the PAL data for an avatar.
|
||||
* @param {string[]} [avatarIDs=[]] - The IDs of the avatars to get the PAL data for. If empty, then PAL data is obtained
|
||||
* for all avatars.
|
||||
* @returns {object<"data", AvatarManager.PalData[]>} An array of objects, each object being the PAL data for an avatar.
|
||||
* @example <caption>Report the PAL data for an avatar nearby.</caption>
|
||||
* var palData = AvatarManager.getPalData();
|
||||
* print("PAL data for one avatar: " + JSON.stringify(palData.data[0]));
|
||||
*/
|
||||
Q_INVOKABLE QVariantMap getPalData(const QStringList& specificAvatarIdentifiers = QStringList());
|
||||
|
||||
|
@ -209,7 +257,8 @@ public:
|
|||
public slots:
|
||||
/**jsdoc
|
||||
* @function AvatarManager.updateAvatarRenderStatus
|
||||
* @param {boolean} shouldRenderAvatars
|
||||
* @param {boolean} shouldRenderAvatars - Should render avatars.
|
||||
* @deprecated This function is deprecated and will be removed.
|
||||
*/
|
||||
void updateAvatarRenderStatus(bool shouldRenderAvatars);
|
||||
|
||||
|
@ -220,8 +269,6 @@ private:
|
|||
explicit AvatarManager(QObject* parent = 0);
|
||||
explicit AvatarManager(const AvatarManager& other);
|
||||
|
||||
void simulateAvatarFades(float deltaTime);
|
||||
|
||||
AvatarSharedPointer newSharedAvatar(const QUuid& sessionUUID) override;
|
||||
|
||||
// called only from the AvatarHashMap thread - cannot be called while this thread holds the
|
||||
|
@ -231,8 +278,6 @@ private:
|
|||
KillAvatarReason removalReason = KillAvatarReason::NoReason) override;
|
||||
void handleTransitAnimations(AvatarTransit::Status status);
|
||||
|
||||
QVector<AvatarSharedPointer> _avatarsToFadeOut;
|
||||
|
||||
using SetOfOtherAvatars = std::set<OtherAvatarPointer>;
|
||||
SetOfOtherAvatars _avatarsToChangeInPhysics;
|
||||
|
||||
|
@ -252,7 +297,6 @@ private:
|
|||
|
||||
mutable std::mutex _spaceLock;
|
||||
workload::SpacePointer _space;
|
||||
std::vector<int32_t> _spaceProxiesToDelete;
|
||||
|
||||
AvatarTransit::TransitConfig _transitConfig;
|
||||
};
|
||||
|
|
|
@ -155,6 +155,7 @@ MyAvatar::MyAvatar(QThread* thread) :
|
|||
_prevShouldDrawHead(true),
|
||||
_audioListenerMode(FROM_HEAD),
|
||||
_dominantHandSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "dominantHand", DOMINANT_RIGHT_HAND),
|
||||
_strafeEnabledSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "strafeEnabled", DEFAULT_STRAFE_ENABLED),
|
||||
_hmdAvatarAlignmentTypeSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "hmdAvatarAlignmentType", DEFAULT_HMD_AVATAR_ALIGNMENT_TYPE),
|
||||
_headPitchSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "", 0.0f),
|
||||
_scaleSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "scale", _targetScale),
|
||||
|
@ -169,7 +170,16 @@ MyAvatar::MyAvatar(QThread* thread) :
|
|||
_useSnapTurnSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "useSnapTurn", _useSnapTurn),
|
||||
_userHeightSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "userHeight", DEFAULT_AVATAR_HEIGHT),
|
||||
_flyingHMDSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "flyingHMD", _flyingPrefHMD),
|
||||
_movementReferenceSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "movementReference", _movementReference),
|
||||
_avatarEntityCountSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "avatarEntityData" << "size", 0),
|
||||
_driveGear1Setting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "driveGear1", _driveGear1),
|
||||
_driveGear2Setting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "driveGear2", _driveGear2),
|
||||
_driveGear3Setting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "driveGear3", _driveGear3),
|
||||
_driveGear4Setting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "driveGear4", _driveGear4),
|
||||
_driveGear5Setting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "driveGear5", _driveGear5),
|
||||
_analogWalkSpeedSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "analogWalkSpeed", _analogWalkSpeed.get()),
|
||||
_analogPlusWalkSpeedSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "analogPlusWalkSpeed", _analogPlusWalkSpeed.get()),
|
||||
_controlSchemeIndexSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "controlSchemeIndex", _controlSchemeIndex),
|
||||
_userRecenterModelSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "userRecenterModel", USER_RECENTER_MODEL_AUTO)
|
||||
{
|
||||
_clientTraitsHandler.reset(new ClientTraitsHandler(this));
|
||||
|
@ -322,6 +332,14 @@ QString MyAvatar::getDominantHand() const {
|
|||
return _dominantHand.get();
|
||||
}
|
||||
|
||||
void MyAvatar::setStrafeEnabled(bool enabled) {
|
||||
_strafeEnabled.set(enabled);
|
||||
}
|
||||
|
||||
bool MyAvatar::getStrafeEnabled() const {
|
||||
return _strafeEnabled.get();
|
||||
}
|
||||
|
||||
void MyAvatar::setDominantHand(const QString& hand) {
|
||||
if (hand == DOMINANT_LEFT_HAND || hand == DOMINANT_RIGHT_HAND) {
|
||||
bool changed = (hand != _dominantHand.get());
|
||||
|
@ -800,20 +818,8 @@ void MyAvatar::simulate(float deltaTime, bool inView) {
|
|||
if (_cauterizationNeedsUpdate) {
|
||||
_cauterizationNeedsUpdate = false;
|
||||
|
||||
// Redisplay cauterized entities that are no longer children of the avatar.
|
||||
auto cauterizedChild = _cauterizedChildrenOfHead.begin();
|
||||
if (cauterizedChild != _cauterizedChildrenOfHead.end()) {
|
||||
auto children = getChildren();
|
||||
while (cauterizedChild != _cauterizedChildrenOfHead.end()) {
|
||||
if (!children.contains(*cauterizedChild)) {
|
||||
updateChildCauterization(*cauterizedChild, false);
|
||||
cauterizedChild = _cauterizedChildrenOfHead.erase(cauterizedChild);
|
||||
} else {
|
||||
++cauterizedChild;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto objectsToUncauterize = _cauterizedChildrenOfHead;
|
||||
_cauterizedChildrenOfHead.clear();
|
||||
// Update cauterization of entities that are children of the avatar.
|
||||
auto headBoneSet = _skeletonModel->getCauterizeBoneSet();
|
||||
forEachChild([&](SpatiallyNestablePointer object) {
|
||||
|
@ -825,15 +831,19 @@ void MyAvatar::simulate(float deltaTime, bool inView) {
|
|||
updateChildCauterization(descendant, !_prevShouldDrawHead);
|
||||
});
|
||||
_cauterizedChildrenOfHead.insert(object);
|
||||
} else if (_cauterizedChildrenOfHead.find(object) != _cauterizedChildrenOfHead.end()) {
|
||||
// Redisplay cauterized children that are not longer children of the head.
|
||||
updateChildCauterization(object, false);
|
||||
objectsToUncauterize.erase(object);
|
||||
} else if (objectsToUncauterize.find(object) == objectsToUncauterize.end()) {
|
||||
objectsToUncauterize.insert(object);
|
||||
object->forEachDescendant([&](SpatiallyNestablePointer descendant) {
|
||||
updateChildCauterization(descendant, false);
|
||||
objectsToUncauterize.insert(descendant);
|
||||
});
|
||||
_cauterizedChildrenOfHead.erase(object);
|
||||
}
|
||||
});
|
||||
|
||||
// Redisplay cauterized entities that are no longer children of the avatar.
|
||||
for (auto cauterizedChild = objectsToUncauterize.begin(); cauterizedChild != objectsToUncauterize.end(); cauterizedChild++) {
|
||||
updateChildCauterization(*cauterizedChild, false);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -942,8 +952,6 @@ void MyAvatar::simulate(float deltaTime, bool inView) {
|
|||
}
|
||||
|
||||
handleChangedAvatarEntityData();
|
||||
|
||||
updateFadingStatus();
|
||||
}
|
||||
|
||||
// As far as I know no HMD system supports a play area of a kilometer in radius.
|
||||
|
@ -1191,6 +1199,15 @@ void MyAvatar::overrideAnimation(const QString& url, float fps, bool loop, float
|
|||
_skeletonModel->getRig().overrideAnimation(url, fps, loop, firstFrame, lastFrame);
|
||||
}
|
||||
|
||||
void MyAvatar::overrideHandAnimation(bool isLeft, const QString& url, float fps, bool loop, float firstFrame, float lastFrame) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "overrideHandAnimation", Q_ARG(bool, isLeft), Q_ARG(const QString&, url), Q_ARG(float, fps),
|
||||
Q_ARG(bool, loop), Q_ARG(float, firstFrame), Q_ARG(float, lastFrame));
|
||||
return;
|
||||
}
|
||||
_skeletonModel->getRig().overrideHandAnimation(isLeft, url, fps, loop, firstFrame, lastFrame);
|
||||
}
|
||||
|
||||
void MyAvatar::restoreAnimation() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "restoreAnimation");
|
||||
|
@ -1199,6 +1216,14 @@ void MyAvatar::restoreAnimation() {
|
|||
_skeletonModel->getRig().restoreAnimation();
|
||||
}
|
||||
|
||||
void MyAvatar::restoreHandAnimation(bool isLeft) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "restoreHandAnimation", Q_ARG(bool, isLeft));
|
||||
return;
|
||||
}
|
||||
_skeletonModel->getRig().restoreHandAnimation(isLeft);
|
||||
}
|
||||
|
||||
QStringList MyAvatar::getAnimationRoles() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QStringList result;
|
||||
|
@ -1258,6 +1283,7 @@ void MyAvatar::resizeAvatarEntitySettingHandles(uint32_t maxIndex) {
|
|||
|
||||
void MyAvatar::saveData() {
|
||||
_dominantHandSetting.set(getDominantHand());
|
||||
_strafeEnabledSetting.set(getStrafeEnabled());
|
||||
_hmdAvatarAlignmentTypeSetting.set(getHmdAvatarAlignmentType());
|
||||
_headPitchSetting.set(getHead()->getBasePitch());
|
||||
_scaleSetting.set(_targetScale);
|
||||
|
@ -1281,6 +1307,15 @@ void MyAvatar::saveData() {
|
|||
_useSnapTurnSetting.set(_useSnapTurn);
|
||||
_userHeightSetting.set(getUserHeight());
|
||||
_flyingHMDSetting.set(getFlyingHMDPref());
|
||||
_movementReferenceSetting.set(getMovementReference());
|
||||
_driveGear1Setting.set(getDriveGear1());
|
||||
_driveGear2Setting.set(getDriveGear2());
|
||||
_driveGear3Setting.set(getDriveGear3());
|
||||
_driveGear4Setting.set(getDriveGear4());
|
||||
_driveGear5Setting.set(getDriveGear5());
|
||||
_analogWalkSpeedSetting.set(getAnalogWalkSpeed());
|
||||
_analogPlusWalkSpeedSetting.set(getAnalogPlusWalkSpeed());
|
||||
_controlSchemeIndexSetting.set(getControlSchemeIndex());
|
||||
_userRecenterModelSetting.set(userRecenterModelToString(getUserRecenterModel()));
|
||||
|
||||
auto hmdInterface = DependencyManager::get<HMDScriptingInterface>();
|
||||
|
@ -1595,7 +1630,9 @@ void MyAvatar::handleChangedAvatarEntityData() {
|
|||
if (!skip) {
|
||||
sanitizeAvatarEntityProperties(properties);
|
||||
entityTree->withWriteLock([&] {
|
||||
entityTree->updateEntity(id, properties);
|
||||
if (entityTree->updateEntity(id, properties)) {
|
||||
packetSender->queueEditAvatarEntityMessage(entityTree, id);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1858,12 +1895,22 @@ void MyAvatar::loadData() {
|
|||
// Flying preferences must be loaded before calling setFlyingEnabled()
|
||||
Setting::Handle<bool> firstRunVal { Settings::firstRun, true };
|
||||
setFlyingHMDPref(firstRunVal.get() ? false : _flyingHMDSetting.get());
|
||||
setMovementReference(firstRunVal.get() ? false : _movementReferenceSetting.get());
|
||||
setDriveGear1(firstRunVal.get() ? DEFAULT_GEAR_1 : _driveGear1Setting.get());
|
||||
setDriveGear2(firstRunVal.get() ? DEFAULT_GEAR_2 : _driveGear2Setting.get());
|
||||
setDriveGear3(firstRunVal.get() ? DEFAULT_GEAR_3 : _driveGear3Setting.get());
|
||||
setDriveGear4(firstRunVal.get() ? DEFAULT_GEAR_4 : _driveGear4Setting.get());
|
||||
setDriveGear5(firstRunVal.get() ? DEFAULT_GEAR_5 : _driveGear5Setting.get());
|
||||
setControlSchemeIndex(firstRunVal.get() ? LocomotionControlsMode::CONTROLS_DEFAULT : _controlSchemeIndexSetting.get());
|
||||
setAnalogWalkSpeed(firstRunVal.get() ? ANALOG_AVATAR_MAX_WALKING_SPEED : _analogWalkSpeedSetting.get());
|
||||
setAnalogPlusWalkSpeed(firstRunVal.get() ? ANALOG_PLUS_AVATAR_MAX_WALKING_SPEED : _analogPlusWalkSpeedSetting.get());
|
||||
setFlyingEnabled(getFlyingEnabled());
|
||||
|
||||
setDisplayName(_displayNameSetting.get());
|
||||
setCollisionSoundURL(_collisionSoundURLSetting.get(QUrl(DEFAULT_AVATAR_COLLISION_SOUND_URL)).toString());
|
||||
setSnapTurn(_useSnapTurnSetting.get());
|
||||
setDominantHand(_dominantHandSetting.get(DOMINANT_RIGHT_HAND).toLower());
|
||||
setStrafeEnabled(_strafeEnabledSetting.get(DEFAULT_STRAFE_ENABLED));
|
||||
setHmdAvatarAlignmentType(_hmdAvatarAlignmentTypeSetting.get(DEFAULT_HMD_AVATAR_ALIGNMENT_TYPE).toLower());
|
||||
setUserHeight(_userHeightSetting.get(DEFAULT_AVATAR_HEIGHT));
|
||||
setTargetScale(_scaleSetting.get());
|
||||
|
@ -2521,6 +2568,12 @@ controller::Pose MyAvatar::getControllerPoseInAvatarFrame(controller::Action act
|
|||
}
|
||||
}
|
||||
|
||||
glm::quat MyAvatar::getOffHandRotation() const {
|
||||
auto hand = (getDominantHand() == DOMINANT_RIGHT_HAND) ? controller::Action::LEFT_HAND : controller::Action::RIGHT_HAND;
|
||||
auto pose = getControllerPoseInAvatarFrame(hand);
|
||||
return pose.rotation;
|
||||
}
|
||||
|
||||
void MyAvatar::updateMotors() {
|
||||
_characterController.clearMotors();
|
||||
glm::quat motorRotation;
|
||||
|
@ -3138,17 +3191,40 @@ int MyAvatar::sendAvatarDataPacket(bool sendAll) {
|
|||
return bytesSent;
|
||||
}
|
||||
|
||||
const float RENDER_HEAD_CUTOFF_DISTANCE = 0.47f;
|
||||
|
||||
bool MyAvatar::cameraInsideHead(const glm::vec3& cameraPosition) const {
|
||||
if (!_skeletonModel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// transform cameraPosition into rig coordinates
|
||||
AnimPose rigToWorld = AnimPose(getWorldOrientation() * Quaternions::Y_180, getWorldPosition());
|
||||
AnimPose worldToRig = rigToWorld.inverse();
|
||||
glm::vec3 rigCameraPosition = worldToRig * cameraPosition;
|
||||
|
||||
// use head k-dop shape to determine if camera is inside head.
|
||||
const Rig& rig = _skeletonModel->getRig();
|
||||
int headJointIndex = rig.indexOfJoint("Head");
|
||||
if (headJointIndex >= 0) {
|
||||
const HFMModel& hfmModel = _skeletonModel->getHFMModel();
|
||||
AnimPose headPose;
|
||||
if (rig.getAbsoluteJointPoseInRigFrame(headJointIndex, headPose)) {
|
||||
glm::vec3 displacement;
|
||||
const HFMJointShapeInfo& headShapeInfo = hfmModel.joints[headJointIndex].shapeInfo;
|
||||
return findPointKDopDisplacement(rigCameraPosition, headPose, headShapeInfo, displacement);
|
||||
}
|
||||
}
|
||||
|
||||
// fall back to simple distance check.
|
||||
const float RENDER_HEAD_CUTOFF_DISTANCE = 0.47f;
|
||||
return glm::length(cameraPosition - getHeadPosition()) < (RENDER_HEAD_CUTOFF_DISTANCE * getModelScale());
|
||||
}
|
||||
|
||||
bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const {
|
||||
bool defaultMode = renderArgs->_renderMode == RenderArgs::DEFAULT_RENDER_MODE;
|
||||
bool firstPerson = qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON;
|
||||
bool overrideAnim = _skeletonModel ? _skeletonModel->getRig().isPlayingOverrideAnimation() : false;
|
||||
bool insideHead = cameraInsideHead(renderArgs->getViewFrustum().getPosition());
|
||||
return !defaultMode || !firstPerson || !insideHead;
|
||||
return !defaultMode || (!firstPerson && !insideHead) || (overrideAnim && !insideHead);
|
||||
}
|
||||
|
||||
void MyAvatar::setHasScriptedBlendshapes(bool hasScriptedBlendshapes) {
|
||||
|
@ -3287,21 +3363,131 @@ void MyAvatar::updateOrientation(float deltaTime) {
|
|||
}
|
||||
}
|
||||
|
||||
static float scaleSpeedByDirection(const glm::vec2 velocityDirection, const float forwardSpeed, const float backwardSpeed) {
|
||||
// for the elipse function --> (x^2)/(backwardSpeed*backwardSpeed) + y^2/(forwardSpeed*forwardSpeed) = 1, scale == y^2 when x is 0
|
||||
float fwdScale = forwardSpeed * forwardSpeed;
|
||||
float backScale = backwardSpeed * backwardSpeed;
|
||||
float scaledX = velocityDirection.x * backwardSpeed;
|
||||
float scaledSpeed = forwardSpeed;
|
||||
if (velocityDirection.y < 0.0f) {
|
||||
if (backScale > 0.0f) {
|
||||
float yValue = sqrtf(fwdScale * (1.0f - ((scaledX * scaledX) / backScale)));
|
||||
scaledSpeed = sqrtf((scaledX * scaledX) + (yValue * yValue));
|
||||
float MyAvatar::calculateGearedSpeed(const float driveKey) {
|
||||
float absDriveKey = abs(driveKey);
|
||||
float sign = (driveKey < 0.0f) ? -1.0f : 1.0f;
|
||||
if (absDriveKey > getDriveGear5()) {
|
||||
return sign * 1.0f;
|
||||
}
|
||||
else if (absDriveKey > getDriveGear4()) {
|
||||
return sign * 0.8f;
|
||||
}
|
||||
else if (absDriveKey > getDriveGear3()) {
|
||||
return sign * 0.6f;
|
||||
}
|
||||
else if (absDriveKey > getDriveGear2()) {
|
||||
return sign * 0.4f;
|
||||
}
|
||||
else if (absDriveKey > getDriveGear1()) {
|
||||
return sign * 0.2f;
|
||||
}
|
||||
else {
|
||||
return sign * 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec3 MyAvatar::scaleMotorSpeed(const glm::vec3 forward, const glm::vec3 right) {
|
||||
float stickFullOn = 0.85f;
|
||||
auto zSpeed = getDriveKey(TRANSLATE_Z);
|
||||
auto xSpeed = getDriveKey(TRANSLATE_X);
|
||||
glm::vec3 direction;
|
||||
if (!useAdvancedMovementControls() && qApp->isHMDMode()) {
|
||||
// Walking disabled in settings.
|
||||
return Vectors::ZERO;
|
||||
} else if (qApp->isHMDMode()) {
|
||||
// HMD advanced movement controls.
|
||||
switch (_controlSchemeIndex) {
|
||||
case LocomotionControlsMode::CONTROLS_DEFAULT:
|
||||
// No acceleration curve for this one, constant speed.
|
||||
if (zSpeed || xSpeed) {
|
||||
direction = (zSpeed * forward) + (xSpeed * right);
|
||||
// Normalize direction.
|
||||
auto length = glm::length(direction);
|
||||
if (length > EPSILON) {
|
||||
direction /= length;
|
||||
}
|
||||
return getSensorToWorldScale() * direction * getSprintSpeed() * _walkSpeedScalar;
|
||||
} else {
|
||||
return Vectors::ZERO;
|
||||
}
|
||||
case LocomotionControlsMode::CONTROLS_ANALOG:
|
||||
case LocomotionControlsMode::CONTROLS_ANALOG_PLUS:
|
||||
if (zSpeed || xSpeed) {
|
||||
glm::vec3 scaledForward = getSensorToWorldScale() * calculateGearedSpeed(zSpeed) * _walkSpeedScalar * ((zSpeed >= stickFullOn) ? getSprintSpeed() : getWalkSpeed()) * forward;
|
||||
glm::vec3 scaledRight = getSensorToWorldScale() * calculateGearedSpeed(xSpeed) * _walkSpeedScalar * ((xSpeed > stickFullOn) ? getSprintSpeed() : getWalkSpeed()) * right;
|
||||
direction = scaledForward + scaledRight;
|
||||
return direction;
|
||||
} else {
|
||||
return Vectors::ZERO;
|
||||
}
|
||||
default:
|
||||
qDebug() << "Invalid control scheme index.";
|
||||
return Vectors::ZERO;
|
||||
}
|
||||
} else {
|
||||
scaledSpeed = backwardSpeed;
|
||||
// Desktop mode.
|
||||
direction = (zSpeed * forward) + (xSpeed * right);
|
||||
auto length = glm::length(direction);
|
||||
if (length > EPSILON) {
|
||||
direction /= length;
|
||||
}
|
||||
direction *= getWalkSpeed() * _walkSpeedScalar;
|
||||
return direction;
|
||||
}
|
||||
return scaledSpeed;
|
||||
}
|
||||
|
||||
glm::vec3 MyAvatar::calculateScaledDirection(){
|
||||
CharacterController::State state = _characterController.getState();
|
||||
|
||||
// compute action input
|
||||
// Determine if we're head or controller relative...
|
||||
glm::vec3 forward, right;
|
||||
|
||||
if (qApp->isHMDMode()) {
|
||||
auto handRotation = getOffHandRotation();
|
||||
glm::vec3 controllerForward(0.0f, 1.0f, 0.0f);
|
||||
glm::vec3 controllerRight(0.0f, 0.0f, (getDominantHand() == DOMINANT_RIGHT_HAND ? 1.0f : -1.0f));
|
||||
glm::vec3 transform;
|
||||
switch (getMovementReference()) {
|
||||
case LocomotionRelativeMovementMode::MOVEMENT_HAND_RELATIVE:
|
||||
forward = (handRotation * controllerForward);
|
||||
right = (handRotation * controllerRight);
|
||||
break;
|
||||
case LocomotionRelativeMovementMode::MOVEMENT_HAND_RELATIVE_LEVELED:
|
||||
forward = (handRotation * controllerForward);
|
||||
transform = forward - (glm::dot(forward, Vectors::UNIT_Y) * Vectors::UNIT_Y);
|
||||
if (glm::length(transform) > EPSILON) {
|
||||
forward = glm::normalize(transform);
|
||||
} else {
|
||||
forward = Vectors::ZERO;
|
||||
}
|
||||
right = (handRotation * controllerRight);
|
||||
transform = right - (glm::dot(right, Vectors::UNIT_Y) * Vectors::UNIT_Y);
|
||||
if (glm::length(transform) > EPSILON) {
|
||||
right = glm::normalize(transform);
|
||||
} else {
|
||||
right = Vectors::ZERO;
|
||||
}
|
||||
break;
|
||||
case LocomotionRelativeMovementMode::MOVEMENT_HMD_RELATIVE:
|
||||
default:
|
||||
forward = IDENTITY_FORWARD;
|
||||
right = IDENTITY_RIGHT;
|
||||
}
|
||||
} else {
|
||||
forward = IDENTITY_FORWARD;
|
||||
right = IDENTITY_RIGHT;
|
||||
}
|
||||
|
||||
glm::vec3 direction = scaleMotorSpeed(forward, right);
|
||||
|
||||
if (state == CharacterController::State::Hover ||
|
||||
_characterController.computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS) {
|
||||
glm::vec3 up = (getDriveKey(TRANSLATE_Y)) * IDENTITY_UP;
|
||||
direction += up;
|
||||
}
|
||||
|
||||
return direction;
|
||||
}
|
||||
|
||||
void MyAvatar::updateActionMotor(float deltaTime) {
|
||||
|
@ -3321,25 +3507,13 @@ void MyAvatar::updateActionMotor(float deltaTime) {
|
|||
|
||||
CharacterController::State state = _characterController.getState();
|
||||
|
||||
// compute action input
|
||||
glm::vec3 forward = (getDriveKey(TRANSLATE_Z)) * IDENTITY_FORWARD;
|
||||
glm::vec3 right = (getDriveKey(TRANSLATE_X)) * IDENTITY_RIGHT;
|
||||
|
||||
glm::vec3 direction = forward + right;
|
||||
if (state == CharacterController::State::Hover ||
|
||||
_characterController.computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS) {
|
||||
glm::vec3 up = (getDriveKey(TRANSLATE_Y)) * IDENTITY_UP;
|
||||
direction += up;
|
||||
}
|
||||
glm::vec3 direction = calculateScaledDirection();
|
||||
|
||||
_wasPushing = _isPushing;
|
||||
float directionLength = glm::length(direction);
|
||||
_isPushing = directionLength > EPSILON;
|
||||
|
||||
// normalize direction
|
||||
if (_isPushing) {
|
||||
direction /= directionLength;
|
||||
} else {
|
||||
if (!_isPushing) {
|
||||
direction = Vectors::ZERO;
|
||||
}
|
||||
|
||||
|
@ -3355,6 +3529,7 @@ void MyAvatar::updateActionMotor(float deltaTime) {
|
|||
const float maxBoostSpeed = sensorToWorldScale * MAX_BOOST_SPEED;
|
||||
|
||||
if (_isPushing) {
|
||||
direction /= directionLength;
|
||||
if (motorSpeed < maxBoostSpeed) {
|
||||
// an active action motor should never be slower than this
|
||||
float boostCoefficient = (maxBoostSpeed - motorSpeed) / maxBoostSpeed;
|
||||
|
@ -3365,11 +3540,17 @@ void MyAvatar::updateActionMotor(float deltaTime) {
|
|||
}
|
||||
_actionMotorVelocity = motorSpeed * direction;
|
||||
} else {
|
||||
// we're interacting with a floor --> simple horizontal speed and exponential decay
|
||||
const glm::vec2 currentVel = { direction.x, direction.z };
|
||||
float scaledSpeed = scaleSpeedByDirection(currentVel, _walkSpeed.get(), _walkBackwardSpeed.get());
|
||||
// _walkSpeedScalar is a multiplier if we are in sprint mode, otherwise 1.0
|
||||
_actionMotorVelocity = sensorToWorldScale * (scaledSpeed * _walkSpeedScalar) * direction;
|
||||
_actionMotorVelocity = direction;
|
||||
}
|
||||
|
||||
float previousBoomLength = _boomLength;
|
||||
float boomChange = getDriveKey(ZOOM);
|
||||
_boomLength += 2.0f * _boomLength * boomChange + boomChange * boomChange;
|
||||
_boomLength = glm::clamp<float>(_boomLength, ZOOM_MIN, ZOOM_MAX);
|
||||
|
||||
// May need to change view if boom length has changed
|
||||
if (previousBoomLength != _boomLength) {
|
||||
qApp->changeViewAsNeeded(_boomLength);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3742,7 +3923,8 @@ bool MyAvatar::requiresSafeLanding(const glm::vec3& positionIn, glm::vec3& bette
|
|||
// See https://highfidelity.fogbugz.com/f/cases/5003/findRayIntersection-has-option-to-use-collidableOnly-but-doesn-t-actually-use-colliders
|
||||
QVariantMap extraInfo;
|
||||
EntityItemID entityID = entityTree->evalRayIntersection(startPointIn, directionIn, include, ignore,
|
||||
PickFilter(PickFilter::getBitMask(PickFilter::FlagBit::COLLIDABLE) | PickFilter::getBitMask(PickFilter::FlagBit::PRECISE)),
|
||||
PickFilter(PickFilter::getBitMask(PickFilter::FlagBit::COLLIDABLE) | PickFilter::getBitMask(PickFilter::FlagBit::PRECISE)
|
||||
| PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES)), // exclude Local entities
|
||||
element, distance, face, normalOut, extraInfo, lockType, accurateResult);
|
||||
if (entityID.isNull()) {
|
||||
return false;
|
||||
|
@ -3882,6 +4064,136 @@ void MyAvatar::setFlyingHMDPref(bool enabled) {
|
|||
_flyingPrefHMD = enabled;
|
||||
}
|
||||
|
||||
void MyAvatar::setMovementReference(int enabled) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setMovementReference", Q_ARG(bool, enabled));
|
||||
return;
|
||||
}
|
||||
_movementReference = enabled;
|
||||
}
|
||||
|
||||
int MyAvatar::getMovementReference() {
|
||||
return _movementReference;
|
||||
}
|
||||
|
||||
void MyAvatar::setControlSchemeIndex(int index){
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setControlSchemeIndex", Q_ARG(int, index));
|
||||
return;
|
||||
}
|
||||
// Need to add checks for valid indices.
|
||||
_controlSchemeIndex = index;
|
||||
}
|
||||
|
||||
int MyAvatar::getControlSchemeIndex() {
|
||||
return _controlSchemeIndex;
|
||||
}
|
||||
|
||||
void MyAvatar::setDriveGear1(float shiftPoint) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setDriveGear1", Q_ARG(float, shiftPoint));
|
||||
return;
|
||||
}
|
||||
if (shiftPoint > 1.0f || shiftPoint < 0.0f) return;
|
||||
_driveGear1 = (shiftPoint < _driveGear2) ? shiftPoint : _driveGear1;
|
||||
}
|
||||
|
||||
float MyAvatar::getDriveGear1() {
|
||||
switch (_controlSchemeIndex) {
|
||||
case LocomotionControlsMode::CONTROLS_ANALOG:
|
||||
return ANALOG_AVATAR_GEAR_1;
|
||||
case LocomotionControlsMode::CONTROLS_ANALOG_PLUS:
|
||||
return _driveGear1;
|
||||
case LocomotionControlsMode::CONTROLS_DEFAULT:
|
||||
default:
|
||||
return 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::setDriveGear2(float shiftPoint) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setDriveGear2", Q_ARG(float, shiftPoint));
|
||||
return;
|
||||
}
|
||||
if (shiftPoint > 1.0f || shiftPoint < 0.0f) return;
|
||||
_driveGear2 = (shiftPoint < _driveGear3 && shiftPoint >= _driveGear1) ? shiftPoint : _driveGear2;
|
||||
}
|
||||
|
||||
float MyAvatar::getDriveGear2() {
|
||||
switch (_controlSchemeIndex) {
|
||||
case LocomotionControlsMode::CONTROLS_ANALOG:
|
||||
return ANALOG_AVATAR_GEAR_2;
|
||||
case LocomotionControlsMode::CONTROLS_ANALOG_PLUS:
|
||||
return _driveGear2;
|
||||
case LocomotionControlsMode::CONTROLS_DEFAULT:
|
||||
default:
|
||||
return 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::setDriveGear3(float shiftPoint) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setDriveGear3", Q_ARG(float, shiftPoint));
|
||||
return;
|
||||
}
|
||||
if (shiftPoint > 1.0f || shiftPoint < 0.0f) return;
|
||||
_driveGear3 = (shiftPoint < _driveGear4 && shiftPoint >= _driveGear2) ? shiftPoint : _driveGear3;
|
||||
}
|
||||
|
||||
float MyAvatar::getDriveGear3() {
|
||||
switch (_controlSchemeIndex) {
|
||||
case LocomotionControlsMode::CONTROLS_ANALOG:
|
||||
return ANALOG_AVATAR_GEAR_3;
|
||||
case LocomotionControlsMode::CONTROLS_ANALOG_PLUS:
|
||||
return _driveGear3;
|
||||
case LocomotionControlsMode::CONTROLS_DEFAULT:
|
||||
default:
|
||||
return 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::setDriveGear4(float shiftPoint) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setDriveGear4", Q_ARG(float, shiftPoint));
|
||||
return;
|
||||
}
|
||||
if (shiftPoint > 1.0f || shiftPoint < 0.0f) return;
|
||||
_driveGear4 = (shiftPoint < _driveGear5 && shiftPoint >= _driveGear3) ? shiftPoint : _driveGear4;
|
||||
}
|
||||
|
||||
float MyAvatar::getDriveGear4() {
|
||||
switch (_controlSchemeIndex) {
|
||||
case LocomotionControlsMode::CONTROLS_ANALOG:
|
||||
return ANALOG_AVATAR_GEAR_4;
|
||||
case LocomotionControlsMode::CONTROLS_ANALOG_PLUS:
|
||||
return _driveGear4;
|
||||
case LocomotionControlsMode::CONTROLS_DEFAULT:
|
||||
default:
|
||||
return 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::setDriveGear5(float shiftPoint) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setDriveGear5", Q_ARG(float, shiftPoint));
|
||||
return;
|
||||
}
|
||||
if (shiftPoint > 1.0f || shiftPoint < 0.0f) return;
|
||||
_driveGear5 = (shiftPoint > _driveGear4) ? shiftPoint : _driveGear5;
|
||||
}
|
||||
|
||||
float MyAvatar::getDriveGear5() {
|
||||
switch (_controlSchemeIndex) {
|
||||
case LocomotionControlsMode::CONTROLS_ANALOG:
|
||||
return ANALOG_AVATAR_GEAR_5;
|
||||
case LocomotionControlsMode::CONTROLS_ANALOG_PLUS:
|
||||
return _driveGear5;
|
||||
case LocomotionControlsMode::CONTROLS_DEFAULT:
|
||||
default:
|
||||
return 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
bool MyAvatar::getFlyingHMDPref() {
|
||||
return _flyingPrefHMD;
|
||||
}
|
||||
|
@ -4490,11 +4802,37 @@ bool MyAvatar::getIsSitStandStateLocked() const {
|
|||
}
|
||||
|
||||
float MyAvatar::getWalkSpeed() const {
|
||||
return _walkSpeed.get() * _walkSpeedScalar;
|
||||
if (qApp->isHMDMode()) {
|
||||
switch (_controlSchemeIndex) {
|
||||
case LocomotionControlsMode::CONTROLS_ANALOG:
|
||||
return _analogWalkSpeed.get();
|
||||
case LocomotionControlsMode::CONTROLS_ANALOG_PLUS:
|
||||
return _analogPlusWalkSpeed.get();
|
||||
case LocomotionControlsMode::CONTROLS_DEFAULT:
|
||||
default:
|
||||
return _defaultWalkSpeed.get();
|
||||
}
|
||||
} else {
|
||||
return _defaultWalkSpeed.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
float MyAvatar::getWalkBackwardSpeed() const {
|
||||
return _walkSpeed.get() * _walkSpeedScalar;
|
||||
if (qApp->isHMDMode()) {
|
||||
switch (_controlSchemeIndex) {
|
||||
case LocomotionControlsMode::CONTROLS_ANALOG:
|
||||
return _analogWalkBackwardSpeed.get();
|
||||
case LocomotionControlsMode::CONTROLS_ANALOG_PLUS:
|
||||
return _analogPlusWalkBackwardSpeed.get();
|
||||
case LocomotionControlsMode::CONTROLS_DEFAULT:
|
||||
default:
|
||||
return _defaultWalkBackwardSpeed.get();
|
||||
}
|
||||
} else {
|
||||
return _defaultWalkBackwardSpeed.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool MyAvatar::isReadyForPhysics() const {
|
||||
|
@ -4502,7 +4840,12 @@ bool MyAvatar::isReadyForPhysics() const {
|
|||
}
|
||||
|
||||
void MyAvatar::setSprintMode(bool sprint) {
|
||||
_walkSpeedScalar = sprint ? _sprintSpeed.get() : AVATAR_WALK_SPEED_SCALAR;
|
||||
if (qApp->isHMDMode()) {
|
||||
_walkSpeedScalar = sprint ? AVATAR_DESKTOP_SPRINT_SPEED_SCALAR : AVATAR_WALK_SPEED_SCALAR;
|
||||
}
|
||||
else {
|
||||
_walkSpeedScalar = sprint ? AVATAR_HMD_SPRINT_SPEED_SCALAR : AVATAR_WALK_SPEED_SCALAR;
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::setIsInWalkingState(bool isWalking) {
|
||||
|
@ -4565,19 +4908,103 @@ void MyAvatar::setIsSitStandStateLocked(bool isLocked) {
|
|||
}
|
||||
|
||||
void MyAvatar::setWalkSpeed(float value) {
|
||||
_walkSpeed.set(value);
|
||||
switch (_controlSchemeIndex) {
|
||||
case LocomotionControlsMode::CONTROLS_DEFAULT:
|
||||
_defaultWalkSpeed.set(value);
|
||||
break;
|
||||
case LocomotionControlsMode::CONTROLS_ANALOG:
|
||||
_analogWalkSpeed.set(value);
|
||||
break;
|
||||
case LocomotionControlsMode::CONTROLS_ANALOG_PLUS:
|
||||
_analogPlusWalkSpeed.set(value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::setWalkBackwardSpeed(float value) {
|
||||
_walkBackwardSpeed.set(value);
|
||||
switch (_controlSchemeIndex) {
|
||||
case LocomotionControlsMode::CONTROLS_DEFAULT:
|
||||
_defaultWalkBackwardSpeed.set(value);
|
||||
break;
|
||||
case LocomotionControlsMode::CONTROLS_ANALOG:
|
||||
_analogWalkBackwardSpeed.set(value);
|
||||
break;
|
||||
case LocomotionControlsMode::CONTROLS_ANALOG_PLUS:
|
||||
_analogPlusWalkBackwardSpeed.set(value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::setSprintSpeed(float value) {
|
||||
_sprintSpeed.set(value);
|
||||
switch (_controlSchemeIndex) {
|
||||
case LocomotionControlsMode::CONTROLS_DEFAULT:
|
||||
_defaultSprintSpeed.set(value);
|
||||
break;
|
||||
case LocomotionControlsMode::CONTROLS_ANALOG:
|
||||
_analogSprintSpeed.set(value);
|
||||
break;
|
||||
case LocomotionControlsMode::CONTROLS_ANALOG_PLUS:
|
||||
_analogPlusSprintSpeed.set(value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
float MyAvatar::getSprintSpeed() const {
|
||||
return _sprintSpeed.get();
|
||||
if (qApp->isHMDMode()) {
|
||||
switch (_controlSchemeIndex) {
|
||||
case LocomotionControlsMode::CONTROLS_ANALOG:
|
||||
return _analogSprintSpeed.get();
|
||||
case LocomotionControlsMode::CONTROLS_ANALOG_PLUS:
|
||||
return _analogPlusSprintSpeed.get();
|
||||
case LocomotionControlsMode::CONTROLS_DEFAULT:
|
||||
default:
|
||||
return _defaultSprintSpeed.get();
|
||||
}
|
||||
} else {
|
||||
return _defaultSprintSpeed.get();
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::setAnalogWalkSpeed(float value) {
|
||||
_analogWalkSpeed.set(value);
|
||||
// Sprint speed for Analog should be double walk speed.
|
||||
_analogSprintSpeed.set(value * 2.0f);
|
||||
}
|
||||
|
||||
float MyAvatar::getAnalogWalkSpeed() const {
|
||||
return _analogWalkSpeed.get();
|
||||
}
|
||||
|
||||
void MyAvatar::setAnalogSprintSpeed(float value) {
|
||||
_analogSprintSpeed.set(value);
|
||||
}
|
||||
|
||||
float MyAvatar::getAnalogSprintSpeed() const {
|
||||
return _analogSprintSpeed.get();
|
||||
}
|
||||
|
||||
void MyAvatar::setAnalogPlusWalkSpeed(float value) {
|
||||
_analogPlusWalkSpeed.set(value);
|
||||
// Sprint speed for Analog Plus should be double walk speed.
|
||||
_analogPlusSprintSpeed.set(value * 2.0f);
|
||||
}
|
||||
|
||||
float MyAvatar::getAnalogPlusWalkSpeed() const {
|
||||
return _analogPlusWalkSpeed.get();
|
||||
}
|
||||
|
||||
void MyAvatar::setAnalogPlusSprintSpeed(float value) {
|
||||
_analogPlusSprintSpeed.set(value);
|
||||
}
|
||||
|
||||
float MyAvatar::getAnalogPlusSprintSpeed() const {
|
||||
return _analogPlusSprintSpeed.get();
|
||||
}
|
||||
|
||||
void MyAvatar::setSitStandStateChange(bool stateChanged) {
|
||||
|
@ -5386,12 +5813,19 @@ void MyAvatar::releaseGrab(const QUuid& grabID) {
|
|||
}
|
||||
|
||||
void MyAvatar::addAvatarHandsToFlow(const std::shared_ptr<Avatar>& otherAvatar) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "addAvatarHandsToFlow",
|
||||
Q_ARG(const std::shared_ptr<Avatar>&, otherAvatar));
|
||||
return;
|
||||
}
|
||||
auto &flow = _skeletonModel->getRig().getFlow();
|
||||
for (auto &handJointName : HAND_COLLISION_JOINTS) {
|
||||
int jointIndex = otherAvatar->getJointIndex(handJointName);
|
||||
if (jointIndex != -1) {
|
||||
glm::vec3 position = otherAvatar->getJointPosition(jointIndex);
|
||||
flow.setOthersCollision(otherAvatar->getID(), jointIndex, position);
|
||||
if (otherAvatar != nullptr && flow.getActive()) {
|
||||
for (auto &handJointName : HAND_COLLISION_JOINTS) {
|
||||
int jointIndex = otherAvatar->getJointIndex(handJointName);
|
||||
if (jointIndex != -1) {
|
||||
glm::vec3 position = otherAvatar->getJointPosition(jointIndex);
|
||||
flow.setOthersCollision(otherAvatar->getID(), jointIndex, position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,18 @@ class ModelItemID;
|
|||
class MyHead;
|
||||
class DetailedMotionState;
|
||||
|
||||
enum LocomotionControlsMode {
|
||||
CONTROLS_DEFAULT = 0,
|
||||
CONTROLS_ANALOG,
|
||||
CONTROLS_ANALOG_PLUS
|
||||
};
|
||||
|
||||
enum LocomotionRelativeMovementMode {
|
||||
MOVEMENT_HMD_RELATIVE = 0,
|
||||
MOVEMENT_HAND_RELATIVE,
|
||||
MOVEMENT_HAND_RELATIVE_LEVELED
|
||||
};
|
||||
|
||||
enum eyeContactTarget {
|
||||
LEFT_EYE,
|
||||
RIGHT_EYE,
|
||||
|
@ -371,6 +383,13 @@ class MyAvatar : public Avatar {
|
|||
using Clock = std::chrono::system_clock;
|
||||
using TimePoint = Clock::time_point;
|
||||
|
||||
const float DEFAULT_GEAR_1 = 0.2f;
|
||||
const float DEFAULT_GEAR_2 = 0.4f;
|
||||
const float DEFAULT_GEAR_3 = 0.8f;
|
||||
const float DEFAULT_GEAR_4 = 0.9f;
|
||||
const float DEFAULT_GEAR_5 = 1.0f;
|
||||
|
||||
const bool DEFAULT_STRAFE_ENABLED = true;
|
||||
public:
|
||||
|
||||
/**jsdoc
|
||||
|
@ -578,6 +597,26 @@ public:
|
|||
*/
|
||||
Q_INVOKABLE void overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame);
|
||||
|
||||
/**jsdoc
|
||||
* <code>overrideHandAnimation()</code> Gets the overrides the default hand poses that are triggered with controller buttons.
|
||||
* use {@link MyAvatar.restoreHandAnimation}.</p> to restore the default poses.
|
||||
* @function MyAvatar.overrideHandAnimation
|
||||
* @param isLeft {boolean} Set true if using the left hand
|
||||
* @param url {string} The URL to the animation file. Animation files need to be FBX format, but only need to contain the
|
||||
* avatar skeleton and animation data.
|
||||
* @param fps {number} The frames per second (FPS) rate for the animation playback. 30 FPS is normal speed.
|
||||
* @param loop {boolean} Set to true if the animation should loop.
|
||||
* @param firstFrame {number} The frame the animation should start at.
|
||||
* @param lastFrame {number} The frame the animation should end at
|
||||
* @example <caption> Override left hand animation for three seconds. </caption>
|
||||
* // Override the left hand pose then restore the default pose.
|
||||
* MyAvatar.overrideHandAnimation(isLeft, ANIM_URL, 30, true, 0, 53);
|
||||
* Script.setTimeout(function () {
|
||||
* MyAvatar.restoreHandAnimation();
|
||||
* }, 3000);
|
||||
*/
|
||||
Q_INVOKABLE void overrideHandAnimation(bool isLeft, const QString& url, float fps, bool loop, float firstFrame, float lastFrame);
|
||||
|
||||
/**jsdoc
|
||||
* Restores the default animations.
|
||||
* <p>The avatar animation system includes a set of default animations along with rules for how those animations are blended
|
||||
|
@ -596,6 +635,24 @@ public:
|
|||
*/
|
||||
Q_INVOKABLE void restoreAnimation();
|
||||
|
||||
/**jsdoc
|
||||
* Restores the default hand animation state machine that is driven by the state machine in the avatar-animation json.
|
||||
* <p>The avatar animation system includes a set of default animations along with rules for how those animations are blended
|
||||
* together with procedural data (such as look at vectors, hand sensors etc.). Playing your own custom animations will
|
||||
* override the default animations. <code>restoreHandAnimation()</code> is used to restore the default hand poses
|
||||
* If you aren't currently playing an override hand
|
||||
* animation, this function has no effect.</p>
|
||||
* @function MyAvatar.restoreHandAnimation
|
||||
* @param isLeft {boolean} Set to true if using the left hand
|
||||
* @example <caption> Override left hand animation for three seconds. </caption>
|
||||
* // Override the left hand pose then restore the default pose.
|
||||
* MyAvatar.overrideHandAnimation(isLeft, ANIM_URL, 30, true, 0, 53);
|
||||
* Script.setTimeout(function () {
|
||||
* MyAvatar.restoreHandAnimation();
|
||||
* }, 3000);
|
||||
*/
|
||||
Q_INVOKABLE void restoreHandAnimation(bool isLeft);
|
||||
|
||||
/**jsdoc
|
||||
* Gets the current animation roles.
|
||||
* <p>Each avatar has an avatar-animation.json file that defines which animations are used and how they are blended together
|
||||
|
@ -729,7 +786,17 @@ public:
|
|||
*/
|
||||
Q_INVOKABLE void setSnapTurn(bool on) { _useSnapTurn = on; }
|
||||
|
||||
/**
|
||||
* @function MyAvatar.getControlScheme
|
||||
* @returns {number}
|
||||
*/
|
||||
Q_INVOKABLE int getControlScheme() const { return _controlSchemeIndex; }
|
||||
|
||||
/**
|
||||
* @function MyAvatar.setControlScheme
|
||||
* @param {number} index
|
||||
*/
|
||||
Q_INVOKABLE void setControlScheme(int index) { _controlSchemeIndex = (index >= 0 && index <= 2) ? index : 0; }
|
||||
/**jsdoc
|
||||
* Sets the avatar's dominant hand.
|
||||
* @function MyAvatar.setDominantHand
|
||||
|
@ -744,7 +811,16 @@ public:
|
|||
* @returns {string} <code>"left"</code> for the left hand, <code>"right"</code> for the right hand.
|
||||
*/
|
||||
Q_INVOKABLE QString getDominantHand() const;
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAVatar.setStrafeEnabled
|
||||
* @param {bool} enabled
|
||||
*/
|
||||
Q_INVOKABLE void setStrafeEnabled(bool enabled);
|
||||
/**jsdoc
|
||||
* @function MyAvatar.getStrafeEnabled
|
||||
* @returns {bool}
|
||||
*/
|
||||
Q_INVOKABLE bool getStrafeEnabled() const;
|
||||
/**jsdoc
|
||||
* @function MyAvatar.setHmdAvatarAlignmentType
|
||||
* @param {string} type - <code>"head"</code> to align your head and your avatar's head, <code>"eyes"</code> to align your
|
||||
|
@ -1235,6 +1311,7 @@ public:
|
|||
controller::Pose getControllerPoseInSensorFrame(controller::Action action) const;
|
||||
controller::Pose getControllerPoseInWorldFrame(controller::Action action) const;
|
||||
controller::Pose getControllerPoseInAvatarFrame(controller::Action action) const;
|
||||
glm::quat getOffHandRotation() const;
|
||||
|
||||
bool hasDriveInput() const;
|
||||
|
||||
|
@ -1317,6 +1394,106 @@ public:
|
|||
*/
|
||||
Q_INVOKABLE bool getFlyingHMDPref();
|
||||
|
||||
/**jsdoc
|
||||
* Set your preference for hand-relative movement.
|
||||
* @function MyAvatar.setHandRelativeMovement
|
||||
* @param {number} enabled - Set <code>true</code> if you want to enable hand-relative movement, otherwise set to <code>false</code>.
|
||||
*
|
||||
*/
|
||||
Q_INVOKABLE void setMovementReference(int enabled);
|
||||
|
||||
/**jsdoc
|
||||
* Get your preference for hand-relative movement.
|
||||
* @function MyAvatar.getHandRelativeMovement
|
||||
* @returns {number} <code>true</code> if your preference is for user locomotion to be relative to the direction your
|
||||
* controller is pointing, otherwise <code>false</code>.
|
||||
*/
|
||||
Q_INVOKABLE int getMovementReference();
|
||||
|
||||
/**jsdoc
|
||||
* Set the first 'shifting point' for acceleration step function.
|
||||
* @function MyAvatar.setDriveGear1
|
||||
* @param {number} shiftPoint - Set the first shift point for analog movement acceleration step function, between [0.0, 1.0]. Must be less than or equal to Gear 2.
|
||||
*/
|
||||
Q_INVOKABLE void setDriveGear1(float shiftPoint);
|
||||
|
||||
/**jsdoc
|
||||
* Get the first 'shifting point' for acceleration step function.
|
||||
* @function MyAvatar.getDriveGear1
|
||||
* @returns {number} Value between [0.0, 1.0].
|
||||
*/
|
||||
Q_INVOKABLE float getDriveGear1();
|
||||
|
||||
/**jsdoc
|
||||
* Set the second 'shifting point' for acceleration step function.
|
||||
* @function MyAvatar.setDriveGear2
|
||||
* @param {number} shiftPoint - Defines the second shift point for analog movement acceleration step function, between [0, 1]. Must be greater than or equal to Gear 1 and less than or equal to Gear 2.
|
||||
*/
|
||||
Q_INVOKABLE void setDriveGear2(float shiftPoint);
|
||||
|
||||
/**jsdoc
|
||||
* Get the second 'shifting point' for acceleration step function.
|
||||
* @function MyAvatar.getDriveGear2
|
||||
* @returns {number} Value between [0.0, 1.0].
|
||||
*/
|
||||
Q_INVOKABLE float getDriveGear2();
|
||||
|
||||
/**jsdoc
|
||||
* Set the third 'shifting point' for acceleration step function.
|
||||
* @function MyAvatar.setDriveGear3
|
||||
* @param {number} shiftPoint - Defines the third shift point for analog movement acceleration step function, between [0, 1]. Must be greater than or equal to Gear 2 and less than or equal to Gear 4.
|
||||
*/
|
||||
Q_INVOKABLE void setDriveGear3(float shiftPoint);
|
||||
|
||||
/**jsdoc
|
||||
* Get the third 'shifting point' for acceleration step function.
|
||||
* @function MyAvatar.getDriveGear3
|
||||
* @returns {number} Value between [0.0, 1.0].
|
||||
*/
|
||||
Q_INVOKABLE float getDriveGear3();
|
||||
|
||||
/**jsdoc
|
||||
* Set the fourth 'shifting point' for acceleration step function.
|
||||
* @function MyAvatar.setDriveGear4
|
||||
* @param {number} shiftPoint - Defines the fourth shift point for analog movement acceleration step function, between [0, 1]. Must be greater than Gear 3 and less than Gear 5.
|
||||
*/
|
||||
Q_INVOKABLE void setDriveGear4(float shiftPoint);
|
||||
|
||||
/**jsdoc
|
||||
* Get the fourth 'shifting point' for acceleration step function.
|
||||
* @function MyAvatar.getDriveGear4
|
||||
* @returns {number} Value between [0.0, 1.0].
|
||||
*/
|
||||
Q_INVOKABLE float getDriveGear4();
|
||||
|
||||
/**jsdoc
|
||||
* Set the fifth 'shifting point' for acceleration step function.
|
||||
* @function MyAvatar.setDriveGear5
|
||||
* @param {number} shiftPoint - Defines the fifth shift point for analog movement acceleration step function, between [0, 1]. Must be greater than or equal to Gear 4.
|
||||
*/
|
||||
Q_INVOKABLE void setDriveGear5(float shiftPoint);
|
||||
|
||||
/**jsdoc
|
||||
* Get the fifth 'shifting point' for acceleration step function.
|
||||
* @function MyAvatar.getDriveGear5
|
||||
* @returns {number} Value between [0.0, 1.0].
|
||||
*/
|
||||
Q_INVOKABLE float getDriveGear5();
|
||||
|
||||
/**jsdoc
|
||||
* Choose the control scheme.
|
||||
* @function MyAvatar.setControlSchemeIndex
|
||||
* @param {number} Choose the control scheme to be used.
|
||||
*/
|
||||
void setControlSchemeIndex(int index);
|
||||
|
||||
/**jsdoc
|
||||
* Check what control scheme is in use.
|
||||
* @function MyAvatar.getControlSchemeIndex
|
||||
* @returns {number} Returns the index associated with a given control scheme.
|
||||
*/
|
||||
int getControlSchemeIndex();
|
||||
|
||||
/**jsdoc
|
||||
* Gets the target scale of the avatar. The target scale is the desired scale of the avatar without any restrictions on
|
||||
* permissible scale values imposed by the domain.
|
||||
|
@ -1490,6 +1667,14 @@ public:
|
|||
float getWalkBackwardSpeed() const;
|
||||
void setSprintSpeed(float value);
|
||||
float getSprintSpeed() const;
|
||||
void setAnalogWalkSpeed(float value);
|
||||
float getAnalogWalkSpeed() const;
|
||||
void setAnalogSprintSpeed(float value);
|
||||
float getAnalogSprintSpeed() const;
|
||||
void setAnalogPlusWalkSpeed(float value);
|
||||
float getAnalogPlusWalkSpeed() const;
|
||||
void setAnalogPlusSprintSpeed(float value);
|
||||
float getAnalogPlusSprintSpeed() const;
|
||||
void setSitStandStateChange(bool stateChanged);
|
||||
float getSitStandStateChange() const;
|
||||
void updateSitStandState(float newHeightReading, float dt);
|
||||
|
@ -2230,6 +2415,13 @@ private:
|
|||
float _boomLength { ZOOM_DEFAULT };
|
||||
float _yawSpeed; // degrees/sec
|
||||
float _pitchSpeed; // degrees/sec
|
||||
float _driveGear1 { DEFAULT_GEAR_1 };
|
||||
float _driveGear2 { DEFAULT_GEAR_2 };
|
||||
float _driveGear3 { DEFAULT_GEAR_3 };
|
||||
float _driveGear4 { DEFAULT_GEAR_4 };
|
||||
float _driveGear5 { DEFAULT_GEAR_5 };
|
||||
int _controlSchemeIndex { CONTROLS_DEFAULT };
|
||||
int _movementReference{ 0 };
|
||||
|
||||
glm::vec3 _thrust { 0.0f }; // impulse accumulator for outside sources
|
||||
|
||||
|
@ -2270,6 +2462,9 @@ private:
|
|||
|
||||
// private methods
|
||||
void updateOrientation(float deltaTime);
|
||||
glm::vec3 calculateScaledDirection();
|
||||
float calculateGearedSpeed(const float driveKey);
|
||||
glm::vec3 scaleMotorSpeed(const glm::vec3 forward, const glm::vec3 right);
|
||||
void updateActionMotor(float deltaTime);
|
||||
void updatePosition(float deltaTime);
|
||||
void updateViewBoom();
|
||||
|
@ -2287,6 +2482,7 @@ private:
|
|||
bool _useSnapTurn { true };
|
||||
ThreadSafeValueCache<QString> _dominantHand { DOMINANT_RIGHT_HAND };
|
||||
ThreadSafeValueCache<QString> _hmdAvatarAlignmentType { DEFAULT_HMD_AVATAR_ALIGNMENT_TYPE };
|
||||
ThreadSafeValueCache<bool> _strafeEnabled{ DEFAULT_STRAFE_ENABLED };
|
||||
|
||||
const float ROLL_CONTROL_DEAD_ZONE_DEFAULT = 8.0f; // degrees
|
||||
const float ROLL_CONTROL_RATE_DEFAULT = 114.0f; // degrees / sec
|
||||
|
@ -2438,9 +2634,16 @@ private:
|
|||
ThreadSafeValueCache<bool> _lockSitStandState { false };
|
||||
|
||||
// max unscaled forward movement speed
|
||||
ThreadSafeValueCache<float> _walkSpeed { DEFAULT_AVATAR_MAX_WALKING_SPEED };
|
||||
ThreadSafeValueCache<float> _walkBackwardSpeed { DEFAULT_AVATAR_MAX_WALKING_BACKWARD_SPEED };
|
||||
ThreadSafeValueCache<float> _sprintSpeed { AVATAR_SPRINT_SPEED_SCALAR };
|
||||
ThreadSafeValueCache<float> _defaultWalkSpeed { DEFAULT_AVATAR_MAX_WALKING_SPEED };
|
||||
ThreadSafeValueCache<float> _defaultWalkBackwardSpeed { DEFAULT_AVATAR_MAX_WALKING_BACKWARD_SPEED };
|
||||
ThreadSafeValueCache<float> _defaultSprintSpeed { DEFAULT_AVATAR_MAX_SPRINT_SPEED };
|
||||
ThreadSafeValueCache<float> _analogWalkSpeed { ANALOG_AVATAR_MAX_WALKING_SPEED };
|
||||
ThreadSafeValueCache<float> _analogWalkBackwardSpeed { ANALOG_AVATAR_MAX_WALKING_BACKWARD_SPEED };
|
||||
ThreadSafeValueCache<float> _analogSprintSpeed { ANALOG_AVATAR_MAX_SPRINT_SPEED };
|
||||
ThreadSafeValueCache<float> _analogPlusWalkSpeed { ANALOG_PLUS_AVATAR_MAX_WALKING_SPEED };
|
||||
ThreadSafeValueCache<float> _analogPlusWalkBackwardSpeed { ANALOG_PLUS_AVATAR_MAX_WALKING_BACKWARD_SPEED };
|
||||
ThreadSafeValueCache<float> _analogPlusSprintSpeed { ANALOG_PLUS_AVATAR_MAX_SPRINT_SPEED };
|
||||
|
||||
float _walkSpeedScalar { AVATAR_WALK_SPEED_SCALAR };
|
||||
bool _isInWalkingState { false };
|
||||
ThreadSafeValueCache<bool> _isInSittingState { false };
|
||||
|
@ -2460,6 +2663,7 @@ private:
|
|||
TimePoint _nextTraitsSendWindow;
|
||||
|
||||
Setting::Handle<QString> _dominantHandSetting;
|
||||
Setting::Handle<bool> _strafeEnabledSetting;
|
||||
Setting::Handle<QString> _hmdAvatarAlignmentTypeSetting;
|
||||
Setting::Handle<float> _headPitchSetting;
|
||||
Setting::Handle<float> _scaleSetting;
|
||||
|
@ -2473,8 +2677,17 @@ private:
|
|||
Setting::Handle<bool> _useSnapTurnSetting;
|
||||
Setting::Handle<float> _userHeightSetting;
|
||||
Setting::Handle<bool> _flyingHMDSetting;
|
||||
Setting::Handle<int> _movementReferenceSetting;
|
||||
Setting::Handle<int> _avatarEntityCountSetting;
|
||||
Setting::Handle<bool> _allowTeleportingSetting { "allowTeleporting", true };
|
||||
Setting::Handle<float> _driveGear1Setting;
|
||||
Setting::Handle<float> _driveGear2Setting;
|
||||
Setting::Handle<float> _driveGear3Setting;
|
||||
Setting::Handle<float> _driveGear4Setting;
|
||||
Setting::Handle<float> _driveGear5Setting;
|
||||
Setting::Handle<float> _analogWalkSpeedSetting;
|
||||
Setting::Handle<float> _analogPlusWalkSpeedSetting;
|
||||
Setting::Handle<int> _controlSchemeIndexSetting;
|
||||
std::vector<Setting::Handle<QUuid>> _avatarEntityIDSettings;
|
||||
std::vector<Setting::Handle<QByteArray>> _avatarEntityDataSettings;
|
||||
Setting::Handle<QString> _userRecenterModelSetting;
|
||||
|
|
|
@ -334,7 +334,9 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
|||
eyeParams.leftEyeJointIndex = _rig.indexOfJoint("LeftEye");
|
||||
eyeParams.rightEyeJointIndex = _rig.indexOfJoint("RightEye");
|
||||
|
||||
_rig.updateFromEyeParameters(eyeParams);
|
||||
if (_owningAvatar->getHasProceduralEyeFaceMovement()) {
|
||||
_rig.updateFromEyeParameters(eyeParams);
|
||||
}
|
||||
|
||||
updateFingers();
|
||||
}
|
||||
|
|
|
@ -356,7 +356,6 @@ void OtherAvatar::simulate(float deltaTime, bool inView) {
|
|||
PROFILE_RANGE(simulation, "grabs");
|
||||
applyGrabChanges();
|
||||
}
|
||||
updateFadingStatus();
|
||||
}
|
||||
|
||||
void OtherAvatar::handleChangedAvatarEntityData() {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <QJsonObject>
|
||||
#include <DependencyManager.h>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
#include <EntityItemID.h>
|
||||
#include "AccountManager.h"
|
||||
|
||||
|
||||
|
@ -65,7 +66,7 @@ signals:
|
|||
void availableUpdatesResult(QJsonObject result);
|
||||
void updateItemResult(QJsonObject result);
|
||||
|
||||
void updateCertificateStatus(const QString& certID, uint certStatus);
|
||||
void updateCertificateStatus(const EntityItemID& entityID, uint certStatus);
|
||||
|
||||
public slots:
|
||||
void buySuccess(QNetworkReply* reply);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include <QPixmap>
|
||||
|
||||
#include <EntityItemID.h>
|
||||
#include <DependencyManager.h>
|
||||
|
||||
class QmlCommerce : public QObject, public Dependency {
|
||||
|
@ -49,7 +50,7 @@ signals:
|
|||
void availableUpdatesResult(QJsonObject result);
|
||||
void updateItemResult(QJsonObject result);
|
||||
|
||||
void updateCertificateStatus(const QString& certID, uint certStatus);
|
||||
void updateCertificateStatus(const EntityItemID& entityID, uint certStatus);
|
||||
|
||||
void transferAssetToNodeResult(QJsonObject result);
|
||||
void transferAssetToUsernameResult(QJsonObject result);
|
||||
|
|
|
@ -41,307 +41,246 @@
|
|||
#include "ui/SecurityImageProvider.h"
|
||||
#include "scripting/HMDScriptingInterface.h"
|
||||
|
||||
static const char* KEY_FILE = "hifikey";
|
||||
static const char* INSTRUCTIONS_FILE = "backup_instructions.html";
|
||||
static const char* IMAGE_HEADER = "-----BEGIN SECURITY IMAGE-----\n";
|
||||
static const char* IMAGE_FOOTER = "-----END SECURITY IMAGE-----\n";
|
||||
namespace {
|
||||
const char* KEY_FILE = "hifikey";
|
||||
const char* INSTRUCTIONS_FILE = "backup_instructions.html";
|
||||
const char* IMAGE_HEADER = "-----BEGIN SECURITY IMAGE-----\n";
|
||||
const char* IMAGE_FOOTER = "-----END SECURITY IMAGE-----\n";
|
||||
|
||||
void initialize() {
|
||||
static bool initialized = false;
|
||||
if (!initialized) {
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
OpenSSL_add_all_algorithms();
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
QString keyFilePath() {
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
return PathUtils::getAppDataFilePath(QString("%1.%2").arg(accountManager->getAccountInfo().getUsername(), KEY_FILE));
|
||||
}
|
||||
bool Wallet::copyKeyFileFrom(const QString& pathname) {
|
||||
QString existing = getKeyFilePath();
|
||||
qCDebug(commerce) << "Old keyfile" << existing;
|
||||
if (!existing.isEmpty()) {
|
||||
QString backup = QString(existing).insert(existing.indexOf(KEY_FILE) - 1,
|
||||
QDateTime::currentDateTime().toString(Qt::ISODate).replace(":", ""));
|
||||
qCDebug(commerce) << "Renaming old keyfile to" << backup;
|
||||
if (!QFile::rename(existing, backup)) {
|
||||
qCCritical(commerce) << "Unable to backup" << existing << "to" << backup;
|
||||
return false;
|
||||
void initialize() {
|
||||
static bool initialized = false;
|
||||
if (!initialized) {
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
OpenSSL_add_all_algorithms();
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
QString destination = keyFilePath();
|
||||
bool result = QFile::copy(pathname, destination);
|
||||
qCDebug(commerce) << "copy" << pathname << "to" << destination << "=>" << result;
|
||||
return result;
|
||||
}
|
||||
|
||||
// use the cached _passphrase if it exists, otherwise we need to prompt
|
||||
int passwordCallback(char* password, int maxPasswordSize, int rwFlag, void* u) {
|
||||
// just return a hardcoded pwd for now
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
auto passphrase = wallet->getPassphrase();
|
||||
if (passphrase && !passphrase->isEmpty()) {
|
||||
QString saltedPassphrase(*passphrase);
|
||||
saltedPassphrase.append(wallet->getSalt());
|
||||
strcpy(password, saltedPassphrase.toUtf8().constData());
|
||||
return static_cast<int>(passphrase->size());
|
||||
} else {
|
||||
// this shouldn't happen - so lets log it to tell us we have
|
||||
// a problem with the flow...
|
||||
qCCritical(commerce) << "no cached passphrase while decrypting!";
|
||||
return 0;
|
||||
QString keyFilePath() {
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
return PathUtils::getAppDataFilePath(QString("%1.%2").arg(accountManager->getAccountInfo().getUsername(), KEY_FILE));
|
||||
}
|
||||
}
|
||||
|
||||
EC_KEY* readKeys(const char* filename) {
|
||||
FILE* fp;
|
||||
EC_KEY *key = NULL;
|
||||
if ((fp = fopen(filename, "rt"))) {
|
||||
// file opened successfully
|
||||
qCDebug(commerce) << "opened key file" << filename;
|
||||
// use the cached _passphrase if it exists, otherwise we need to prompt
|
||||
int passwordCallback(char* password, int maxPasswordSize, int rwFlag, void* u) {
|
||||
// just return a hardcoded pwd for now
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
auto passphrase = wallet->getPassphrase();
|
||||
if (passphrase && !passphrase->isEmpty()) {
|
||||
QString saltedPassphrase(*passphrase);
|
||||
saltedPassphrase.append(wallet->getSalt());
|
||||
strcpy(password, saltedPassphrase.toUtf8().constData());
|
||||
return static_cast<int>(passphrase->size());
|
||||
} else {
|
||||
// this shouldn't happen - so lets log it to tell us we have
|
||||
// a problem with the flow...
|
||||
qCCritical(commerce) << "no cached passphrase while decrypting!";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ((key = PEM_read_EC_PUBKEY(fp, NULL, NULL, NULL))) {
|
||||
// now read private key
|
||||
EC_KEY* readKeys(QString filename) {
|
||||
QFile file(filename);
|
||||
EC_KEY* key = NULL;
|
||||
if (file.open(QFile::ReadOnly)) {
|
||||
// file opened successfully
|
||||
qCDebug(commerce) << "opened key file" << filename;
|
||||
|
||||
qCDebug(commerce) << "read public key";
|
||||
QByteArray pemKeyBytes = file.readAll();
|
||||
BIO* bufio = BIO_new_mem_buf((void*)pemKeyBytes.constData(), pemKeyBytes.length());
|
||||
if ((key = PEM_read_bio_EC_PUBKEY(bufio, NULL, NULL, NULL))) {
|
||||
// now read private key
|
||||
|
||||
if ((key = PEM_read_ECPrivateKey(fp, &key, passwordCallback, NULL))) {
|
||||
qCDebug(commerce) << "read private key";
|
||||
fclose(fp);
|
||||
return key;
|
||||
qCDebug(commerce) << "read public key";
|
||||
|
||||
if ((key = PEM_read_bio_ECPrivateKey(bufio, &key, passwordCallback, NULL))) {
|
||||
qCDebug(commerce) << "read private key";
|
||||
} else {
|
||||
qCDebug(commerce) << "failed to read private key";
|
||||
}
|
||||
} else {
|
||||
qCDebug(commerce) << "failed to read public key";
|
||||
}
|
||||
qCDebug(commerce) << "failed to read private key";
|
||||
BIO_free(bufio);
|
||||
file.close();
|
||||
} else {
|
||||
qCDebug(commerce) << "failed to read public key";
|
||||
qCDebug(commerce) << "failed to open key file" << filename;
|
||||
}
|
||||
fclose(fp);
|
||||
} else {
|
||||
qCDebug(commerce) << "failed to open key file" << filename;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
bool Wallet::writeBackupInstructions() {
|
||||
QString inputFilename(PathUtils::resourcesPath() + "html/commerce/backup_instructions.html");
|
||||
QString outputFilename = PathUtils::getAppDataFilePath(INSTRUCTIONS_FILE);
|
||||
QFile inputFile(inputFilename);
|
||||
QFile outputFile(outputFilename);
|
||||
bool retval = false;
|
||||
|
||||
if (getKeyFilePath().isEmpty())
|
||||
{
|
||||
return false;
|
||||
return key;
|
||||
}
|
||||
|
||||
if (QFile::exists(inputFilename) && inputFile.open(QIODevice::ReadOnly)) {
|
||||
if (outputFile.open(QIODevice::ReadWrite)) {
|
||||
// Read the data from the original file, then close it
|
||||
QByteArray fileData = inputFile.readAll();
|
||||
inputFile.close();
|
||||
|
||||
// Translate the data from the original file into a QString
|
||||
QString text(fileData);
|
||||
|
||||
// Replace the necessary string
|
||||
text.replace(QString("HIFIKEY_PATH_REPLACEME"), keyFilePath());
|
||||
|
||||
// Write the new text back to the file
|
||||
outputFile.write(text.toUtf8());
|
||||
|
||||
// Close the output file
|
||||
outputFile.close();
|
||||
|
||||
retval = true;
|
||||
qCDebug(commerce) << "wrote html file successfully";
|
||||
} else {
|
||||
qCDebug(commerce) << "failed to open output html file" << outputFilename;
|
||||
}
|
||||
} else {
|
||||
qCDebug(commerce) << "failed to open input html file" << inputFilename;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool writeKeys(const char* filename, EC_KEY* keys) {
|
||||
FILE* fp;
|
||||
bool retval = false;
|
||||
if ((fp = fopen(filename, "wt"))) {
|
||||
if (!PEM_write_EC_PUBKEY(fp, keys)) {
|
||||
fclose(fp);
|
||||
bool writeKeys(QString filename, EC_KEY* keys) {
|
||||
BIO* bio = BIO_new(BIO_s_mem());
|
||||
bool retval = false;
|
||||
if (!PEM_write_bio_EC_PUBKEY(bio, keys)) {
|
||||
BIO_free(bio);
|
||||
qCCritical(commerce) << "failed to write public key";
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (!PEM_write_ECPrivateKey(fp, keys, EVP_des_ede3_cbc(), NULL, 0, passwordCallback, NULL)) {
|
||||
fclose(fp);
|
||||
if (!PEM_write_bio_ECPrivateKey(bio, keys, EVP_des_ede3_cbc(), NULL, 0, passwordCallback, NULL)) {
|
||||
BIO_free(bio);
|
||||
qCCritical(commerce) << "failed to write private key";
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = true;
|
||||
qCDebug(commerce) << "wrote keys successfully";
|
||||
fclose(fp);
|
||||
} else {
|
||||
qCDebug(commerce) << "failed to open key file" << filename;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
QFile file(filename);
|
||||
if (file.open(QIODevice::WriteOnly)) {
|
||||
const char* bio_data;
|
||||
long bio_size = BIO_get_mem_data(bio, &bio_data);
|
||||
|
||||
bool Wallet::setWallet(const QByteArray& wallet) {
|
||||
QFile file(keyFilePath());
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
qCCritical(commerce) << "Unable to open wallet for write in" << keyFilePath();
|
||||
return false;
|
||||
}
|
||||
if (file.write(wallet) != wallet.count()) {
|
||||
qCCritical(commerce) << "Unable to write wallet in" << keyFilePath();
|
||||
return false;
|
||||
}
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
QByteArray Wallet::getWallet() {
|
||||
QFile file(keyFilePath());
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
qCInfo(commerce) << "No existing wallet in" << keyFilePath();
|
||||
return QByteArray();
|
||||
}
|
||||
QByteArray wallet = file.readAll();
|
||||
file.close();
|
||||
return wallet;
|
||||
}
|
||||
|
||||
QPair<QByteArray*, QByteArray*> generateECKeypair() {
|
||||
|
||||
EC_KEY* keyPair = EC_KEY_new_by_curve_name(NID_secp256k1);
|
||||
QPair<QByteArray*, QByteArray*> retval{};
|
||||
|
||||
EC_KEY_set_asn1_flag(keyPair, OPENSSL_EC_NAMED_CURVE);
|
||||
if (!EC_KEY_generate_key(keyPair)) {
|
||||
qCDebug(commerce) << "Error generating EC Keypair -" << ERR_get_error();
|
||||
QByteArray keyBytes(bio_data, bio_size);
|
||||
file.write(keyBytes);
|
||||
retval = true;
|
||||
qCDebug(commerce) << "wrote keys successfully";
|
||||
file.close();
|
||||
} else {
|
||||
qCDebug(commerce) << "failed to open key file" << filename;
|
||||
}
|
||||
BIO_free(bio);
|
||||
return retval;
|
||||
}
|
||||
|
||||
// grab the public key and private key from the file
|
||||
unsigned char* publicKeyDER = NULL;
|
||||
int publicKeyLength = i2d_EC_PUBKEY(keyPair, &publicKeyDER);
|
||||
QPair<QByteArray*, QByteArray*> generateECKeypair() {
|
||||
EC_KEY* keyPair = EC_KEY_new_by_curve_name(NID_secp256k1);
|
||||
QPair<QByteArray*, QByteArray*> retval {};
|
||||
|
||||
unsigned char* privateKeyDER = NULL;
|
||||
int privateKeyLength = i2d_ECPrivateKey(keyPair, &privateKeyDER);
|
||||
EC_KEY_set_asn1_flag(keyPair, OPENSSL_EC_NAMED_CURVE);
|
||||
if (!EC_KEY_generate_key(keyPair)) {
|
||||
qCDebug(commerce) << "Error generating EC Keypair -" << ERR_get_error();
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (publicKeyLength <= 0 || privateKeyLength <= 0) {
|
||||
qCDebug(commerce) << "Error getting DER public or private key from EC struct -" << ERR_get_error();
|
||||
// grab the public key and private key from the file
|
||||
unsigned char* publicKeyDER = NULL;
|
||||
int publicKeyLength = i2d_EC_PUBKEY(keyPair, &publicKeyDER);
|
||||
|
||||
unsigned char* privateKeyDER = NULL;
|
||||
int privateKeyLength = i2d_ECPrivateKey(keyPair, &privateKeyDER);
|
||||
|
||||
if (publicKeyLength <= 0 || privateKeyLength <= 0) {
|
||||
qCDebug(commerce) << "Error getting DER public or private key from EC struct -" << ERR_get_error();
|
||||
|
||||
// cleanup the EC struct
|
||||
EC_KEY_free(keyPair);
|
||||
|
||||
// cleanup the public and private key DER data, if required
|
||||
if (publicKeyLength > 0) {
|
||||
OPENSSL_free(publicKeyDER);
|
||||
}
|
||||
|
||||
if (privateKeyLength > 0) {
|
||||
OPENSSL_free(privateKeyDER);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (!writeKeys(keyFilePath(), keyPair)) {
|
||||
qCDebug(commerce) << "couldn't save keys!";
|
||||
return retval;
|
||||
}
|
||||
|
||||
// cleanup the EC struct
|
||||
EC_KEY_free(keyPair);
|
||||
|
||||
// cleanup the public and private key DER data, if required
|
||||
if (publicKeyLength > 0) {
|
||||
OPENSSL_free(publicKeyDER);
|
||||
}
|
||||
|
||||
if (privateKeyLength > 0) {
|
||||
OPENSSL_free(privateKeyDER);
|
||||
}
|
||||
// prepare the return values. TODO: Fix this - we probably don't really even want the
|
||||
// private key at all (better to read it when we need it?). Or maybe we do, when we have
|
||||
// multiple keys?
|
||||
retval.first = new QByteArray(reinterpret_cast<char*>(publicKeyDER), publicKeyLength);
|
||||
retval.second = new QByteArray(reinterpret_cast<char*>(privateKeyDER), privateKeyLength);
|
||||
|
||||
// cleanup the publicKeyDER and publicKeyDER data
|
||||
OPENSSL_free(publicKeyDER);
|
||||
OPENSSL_free(privateKeyDER);
|
||||
return retval;
|
||||
}
|
||||
// END copied code (which will soon change)
|
||||
|
||||
// the public key can just go into a byte array
|
||||
QByteArray readPublicKey(QString filename) {
|
||||
QByteArray retval;
|
||||
QFile file(filename);
|
||||
if (file.open(QIODevice::ReadOnly)) {
|
||||
// file opened successfully
|
||||
qCDebug(commerce) << "opened key file" << filename;
|
||||
|
||||
if (!writeKeys(keyFilePath().toStdString().c_str(), keyPair)) {
|
||||
qCDebug(commerce) << "couldn't save keys!";
|
||||
return retval;
|
||||
}
|
||||
QByteArray pemKeyBytes = file.readAll();
|
||||
BIO* bufio = BIO_new_mem_buf((void*)pemKeyBytes.constData(), pemKeyBytes.length());
|
||||
|
||||
EC_KEY_free(keyPair);
|
||||
EC_KEY* key = PEM_read_bio_EC_PUBKEY(bufio, NULL, NULL, NULL);
|
||||
if (key) {
|
||||
// file read successfully
|
||||
unsigned char* publicKeyDER = NULL;
|
||||
int publicKeyLength = i2d_EC_PUBKEY(key, &publicKeyDER);
|
||||
// TODO: check for 0 length?
|
||||
|
||||
// prepare the return values. TODO: Fix this - we probably don't really even want the
|
||||
// private key at all (better to read it when we need it?). Or maybe we do, when we have
|
||||
// multiple keys?
|
||||
retval.first = new QByteArray(reinterpret_cast<char*>(publicKeyDER), publicKeyLength);
|
||||
retval.second = new QByteArray(reinterpret_cast<char*>(privateKeyDER), privateKeyLength);
|
||||
// cleanup
|
||||
EC_KEY_free(key);
|
||||
|
||||
// cleanup the publicKeyDER and publicKeyDER data
|
||||
OPENSSL_free(publicKeyDER);
|
||||
OPENSSL_free(privateKeyDER);
|
||||
return retval;
|
||||
}
|
||||
// END copied code (which will soon change)
|
||||
qCDebug(commerce) << "parsed public key file successfully";
|
||||
|
||||
// the public key can just go into a byte array
|
||||
QByteArray readPublicKey(const char* filename) {
|
||||
FILE* fp;
|
||||
EC_KEY* key = NULL;
|
||||
if ((fp = fopen(filename, "r"))) {
|
||||
// file opened successfully
|
||||
qCDebug(commerce) << "opened key file" << filename;
|
||||
if ((key = PEM_read_EC_PUBKEY(fp, NULL, NULL, NULL))) {
|
||||
// file read successfully
|
||||
unsigned char* publicKeyDER = NULL;
|
||||
int publicKeyLength = i2d_EC_PUBKEY(key, &publicKeyDER);
|
||||
// TODO: check for 0 length?
|
||||
|
||||
// cleanup
|
||||
EC_KEY_free(key);
|
||||
fclose(fp);
|
||||
|
||||
qCDebug(commerce) << "parsed public key file successfully";
|
||||
|
||||
QByteArray retval((char*)publicKeyDER, publicKeyLength);
|
||||
OPENSSL_free(publicKeyDER);
|
||||
return retval;
|
||||
QByteArray retval((char*)publicKeyDER, publicKeyLength);
|
||||
OPENSSL_free(publicKeyDER);
|
||||
BIO_free(bufio);
|
||||
file.close();
|
||||
return retval;
|
||||
} else {
|
||||
qCDebug(commerce) << "couldn't parse" << filename;
|
||||
}
|
||||
BIO_free(bufio);
|
||||
file.close();
|
||||
} else {
|
||||
qCDebug(commerce) << "couldn't parse" << filename;
|
||||
qCDebug(commerce) << "couldn't open" << filename;
|
||||
}
|
||||
fclose(fp);
|
||||
} else {
|
||||
qCDebug(commerce) << "couldn't open" << filename;
|
||||
return QByteArray();
|
||||
}
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
// the private key should be read/copied into heap memory. For now, we need the EC_KEY struct
|
||||
// so I'll return that.
|
||||
EC_KEY* readPrivateKey(const char* filename) {
|
||||
FILE* fp;
|
||||
EC_KEY* key = NULL;
|
||||
if ((fp = fopen(filename, "r"))) {
|
||||
// file opened successfully
|
||||
qCDebug(commerce) << "opened key file" << filename;
|
||||
if ((key = PEM_read_ECPrivateKey(fp, &key, passwordCallback, NULL))) {
|
||||
qCDebug(commerce) << "parsed private key file successfully";
|
||||
// the private key should be read/copied into heap memory. For now, we need the EC_KEY struct
|
||||
// so I'll return that.
|
||||
EC_KEY* readPrivateKey(QString filename) {
|
||||
QFile file(filename);
|
||||
EC_KEY* key = NULL;
|
||||
if (file.open(QIODevice::ReadOnly)) {
|
||||
// file opened successfully
|
||||
qCDebug(commerce) << "opened key file" << filename;
|
||||
|
||||
QByteArray pemKeyBytes = file.readAll();
|
||||
BIO* bufio = BIO_new_mem_buf((void*)pemKeyBytes.constData(), pemKeyBytes.length());
|
||||
|
||||
if ((key = PEM_read_bio_ECPrivateKey(bufio, &key, passwordCallback, NULL))) {
|
||||
qCDebug(commerce) << "parsed private key file successfully";
|
||||
|
||||
} else {
|
||||
qCDebug(commerce) << "couldn't parse" << filename;
|
||||
// if the passphrase is wrong, then let's not cache it
|
||||
DependencyManager::get<Wallet>()->setPassphrase("");
|
||||
}
|
||||
BIO_free(bufio);
|
||||
file.close();
|
||||
} else {
|
||||
qCDebug(commerce) << "couldn't parse" << filename;
|
||||
// if the passphrase is wrong, then let's not cache it
|
||||
DependencyManager::get<Wallet>()->setPassphrase("");
|
||||
qCDebug(commerce) << "couldn't open" << filename;
|
||||
}
|
||||
fclose(fp);
|
||||
} else {
|
||||
qCDebug(commerce) << "couldn't open" << filename;
|
||||
return key;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
// QT's QByteArray will convert to Base64 without any embedded newlines. This just
|
||||
// writes it with embedded newlines, which is more readable.
|
||||
void outputBase64WithNewlines(QFile& file, const QByteArray& b64Array) {
|
||||
for (int i = 0; i < b64Array.size(); i += 64) {
|
||||
file.write(b64Array.mid(i, 64));
|
||||
file.write("\n");
|
||||
// QT's QByteArray will convert to Base64 without any embedded newlines. This just
|
||||
// writes it with embedded newlines, which is more readable.
|
||||
void outputBase64WithNewlines(QFile& file, const QByteArray& b64Array) {
|
||||
for (int i = 0; i < b64Array.size(); i += 64) {
|
||||
file.write(b64Array.mid(i, 64));
|
||||
file.write("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void initializeAESKeys(unsigned char* ivec, unsigned char* ckey, const QByteArray& salt) {
|
||||
// use the ones in the wallet
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
memcpy(ivec, wallet->getIv(), 16);
|
||||
memcpy(ckey, wallet->getCKey(), 32);
|
||||
}
|
||||
void initializeAESKeys(unsigned char* ivec, unsigned char* ckey, const QByteArray& salt) {
|
||||
// use the ones in the wallet
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
memcpy(ivec, wallet->getIv(), 16);
|
||||
memcpy(ckey, wallet->getCKey(), 32);
|
||||
}
|
||||
|
||||
} // close unnamed namespace
|
||||
|
||||
Wallet::Wallet() {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
@ -361,7 +300,7 @@ Wallet::Wallet() {
|
|||
if (wallet->getKeyFilePath().isEmpty() || !wallet->getSecurityImage()) {
|
||||
if (keyStatus == "preexisting") {
|
||||
status = (uint) WalletStatus::WALLET_STATUS_PREEXISTING;
|
||||
} else{
|
||||
} else {
|
||||
status = (uint) WalletStatus::WALLET_STATUS_NOT_SET_UP;
|
||||
}
|
||||
} else if (!wallet->walletIsAuthenticatedWithPassphrase()) {
|
||||
|
@ -371,7 +310,6 @@ Wallet::Wallet() {
|
|||
} else {
|
||||
status = (uint) WalletStatus::WALLET_STATUS_READY;
|
||||
}
|
||||
|
||||
walletScriptingInterface->setWalletStatus(status);
|
||||
});
|
||||
|
||||
|
@ -405,6 +343,88 @@ Wallet::~Wallet() {
|
|||
}
|
||||
}
|
||||
|
||||
bool Wallet::setWallet(const QByteArray& wallet) {
|
||||
QFile file(keyFilePath());
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
qCCritical(commerce) << "Unable to open wallet for write in" << keyFilePath();
|
||||
return false;
|
||||
}
|
||||
if (file.write(wallet) != wallet.count()) {
|
||||
qCCritical(commerce) << "Unable to write wallet in" << keyFilePath();
|
||||
return false;
|
||||
}
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
QByteArray Wallet::getWallet() {
|
||||
QFile file(keyFilePath());
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
qCInfo(commerce) << "No existing wallet in" << keyFilePath();
|
||||
return QByteArray();
|
||||
}
|
||||
QByteArray wallet = file.readAll();
|
||||
file.close();
|
||||
return wallet;
|
||||
}
|
||||
|
||||
bool Wallet::copyKeyFileFrom(const QString& pathname) {
|
||||
QString existing = getKeyFilePath();
|
||||
qCDebug(commerce) << "Old keyfile" << existing;
|
||||
if (!existing.isEmpty()) {
|
||||
QString backup = QString(existing).insert(existing.indexOf(KEY_FILE) - 1,
|
||||
QDateTime::currentDateTime().toString(Qt::ISODate).replace(":", ""));
|
||||
qCDebug(commerce) << "Renaming old keyfile to" << backup;
|
||||
if (!QFile::rename(existing, backup)) {
|
||||
qCCritical(commerce) << "Unable to backup" << existing << "to" << backup;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
QString destination = keyFilePath();
|
||||
bool result = QFile::copy(pathname, destination);
|
||||
qCDebug(commerce) << "copy" << pathname << "to" << destination << "=>" << result;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Wallet::writeBackupInstructions() {
|
||||
QString inputFilename(PathUtils::resourcesPath() + "html/commerce/backup_instructions.html");
|
||||
QString outputFilename = PathUtils::getAppDataFilePath(INSTRUCTIONS_FILE);
|
||||
QFile inputFile(inputFilename);
|
||||
QFile outputFile(outputFilename);
|
||||
bool retval = false;
|
||||
|
||||
if (getKeyFilePath().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (QFile::exists(inputFilename) && inputFile.open(QIODevice::ReadOnly)) {
|
||||
if (outputFile.open(QIODevice::ReadWrite)) {
|
||||
// Read the data from the original file, then close it
|
||||
QByteArray fileData = inputFile.readAll();
|
||||
inputFile.close();
|
||||
|
||||
// Translate the data from the original file into a QString
|
||||
QString text(fileData);
|
||||
|
||||
// Replace the necessary string
|
||||
text.replace(QString("HIFIKEY_PATH_REPLACEME"), keyFilePath());
|
||||
|
||||
// Write the new text back to the file
|
||||
outputFile.write(text.toUtf8());
|
||||
|
||||
// Close the output file
|
||||
outputFile.close();
|
||||
|
||||
retval = true;
|
||||
qCDebug(commerce) << "wrote html file successfully";
|
||||
} else {
|
||||
qCDebug(commerce) << "failed to open output html file" << outputFilename;
|
||||
}
|
||||
} else {
|
||||
qCDebug(commerce) << "failed to open input html file" << inputFilename;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool Wallet::setPassphrase(const QString& passphrase) {
|
||||
if (_passphrase) {
|
||||
delete _passphrase;
|
||||
|
@ -569,10 +589,10 @@ bool Wallet::walletIsAuthenticatedWithPassphrase() {
|
|||
}
|
||||
|
||||
// otherwise, we have a passphrase but no keys, so we have to check
|
||||
auto publicKey = readPublicKey(keyFilePath().toStdString().c_str());
|
||||
auto publicKey = readPublicKey(keyFilePath());
|
||||
|
||||
if (publicKey.size() > 0) {
|
||||
if (auto key = readPrivateKey(keyFilePath().toStdString().c_str())) {
|
||||
if (auto key = readPrivateKey(keyFilePath())) {
|
||||
EC_KEY_free(key);
|
||||
|
||||
// be sure to add the public key so we don't do this over and over
|
||||
|
@ -631,8 +651,7 @@ QStringList Wallet::listPublicKeys() {
|
|||
QString Wallet::signWithKey(const QByteArray& text, const QString& key) {
|
||||
EC_KEY* ecPrivateKey = NULL;
|
||||
|
||||
auto keyFilePathString = keyFilePath().toStdString();
|
||||
if ((ecPrivateKey = readPrivateKey(keyFilePath().toStdString().c_str()))) {
|
||||
if ((ecPrivateKey = readPrivateKey(keyFilePath()))) {
|
||||
unsigned char* sig = new unsigned char[ECDSA_size(ecPrivateKey)];
|
||||
|
||||
unsigned int signatureBytes = 0;
|
||||
|
@ -641,12 +660,8 @@ QString Wallet::signWithKey(const QByteArray& text, const QString& key) {
|
|||
|
||||
QByteArray hashedPlaintext = QCryptographicHash::hash(text, QCryptographicHash::Sha256);
|
||||
|
||||
|
||||
int retrn = ECDSA_sign(0,
|
||||
reinterpret_cast<const unsigned char*>(hashedPlaintext.constData()),
|
||||
hashedPlaintext.size(),
|
||||
sig,
|
||||
&signatureBytes, ecPrivateKey);
|
||||
int retrn = ECDSA_sign(0, reinterpret_cast<const unsigned char*>(hashedPlaintext.constData()), hashedPlaintext.size(),
|
||||
sig, &signatureBytes, ecPrivateKey);
|
||||
|
||||
EC_KEY_free(ecPrivateKey);
|
||||
QByteArray signature(reinterpret_cast<const char*>(sig), signatureBytes);
|
||||
|
@ -682,7 +697,6 @@ void Wallet::updateImageProvider() {
|
|||
}
|
||||
|
||||
void Wallet::chooseSecurityImage(const QString& filename) {
|
||||
|
||||
if (_securityImage) {
|
||||
delete _securityImage;
|
||||
}
|
||||
|
@ -754,7 +768,7 @@ QString Wallet::getKeyFilePath() {
|
|||
}
|
||||
|
||||
bool Wallet::writeWallet(const QString& newPassphrase) {
|
||||
EC_KEY* keys = readKeys(keyFilePath().toStdString().c_str());
|
||||
EC_KEY* keys = readKeys(keyFilePath());
|
||||
auto ledger = DependencyManager::get<Ledger>();
|
||||
// Remove any existing locker, because it will be out of date.
|
||||
if (!_publicKeys.isEmpty() && !ledger->receiveAt(_publicKeys.first(), _publicKeys.first(), QByteArray())) {
|
||||
|
@ -768,7 +782,7 @@ bool Wallet::writeWallet(const QString& newPassphrase) {
|
|||
setPassphrase(newPassphrase);
|
||||
}
|
||||
|
||||
if (writeKeys(tempFileName.toStdString().c_str(), keys)) {
|
||||
if (writeKeys(tempFileName, keys)) {
|
||||
if (writeSecurityImage(_securityImage, tempFileName)) {
|
||||
// ok, now move the temp file to the correct spot
|
||||
QFile(QString(keyFilePath())).remove();
|
||||
|
@ -816,28 +830,28 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer<ReceivedMessage> pack
|
|||
|
||||
bool challengeOriginatedFromClient = packet->getType() == PacketType::ChallengeOwnershipRequest;
|
||||
int status;
|
||||
int certIDByteArraySize;
|
||||
int idByteArraySize;
|
||||
int textByteArraySize;
|
||||
int challengingNodeUUIDByteArraySize;
|
||||
|
||||
packet->readPrimitive(&certIDByteArraySize);
|
||||
packet->readPrimitive(&idByteArraySize);
|
||||
packet->readPrimitive(&textByteArraySize); // returns a cast char*, size
|
||||
if (challengeOriginatedFromClient) {
|
||||
packet->readPrimitive(&challengingNodeUUIDByteArraySize);
|
||||
}
|
||||
|
||||
// "encryptedText" is now a series of random bytes, a nonce
|
||||
QByteArray certID = packet->read(certIDByteArraySize);
|
||||
QByteArray id = packet->read(idByteArraySize);
|
||||
QByteArray text = packet->read(textByteArraySize);
|
||||
QByteArray challengingNodeUUID;
|
||||
if (challengeOriginatedFromClient) {
|
||||
challengingNodeUUID = packet->read(challengingNodeUUIDByteArraySize);
|
||||
}
|
||||
|
||||
EC_KEY* ec = readKeys(keyFilePath().toStdString().c_str());
|
||||
EC_KEY* ec = readKeys(keyFilePath());
|
||||
QString sig;
|
||||
|
||||
if (ec) {
|
||||
if (ec) {
|
||||
ERR_clear_error();
|
||||
sig = signWithKey(text, ""); // base64 signature, QByteArray cast (on return) to QString FIXME should pass ec as string so we can tell which key to sign with
|
||||
status = 1;
|
||||
|
@ -853,32 +867,32 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer<ReceivedMessage> pack
|
|||
textByteArray = sig.toUtf8();
|
||||
}
|
||||
textByteArraySize = textByteArray.size();
|
||||
int certIDSize = certID.size();
|
||||
int idSize = id.size();
|
||||
// setup the packet
|
||||
if (challengeOriginatedFromClient) {
|
||||
auto textPacket = NLPacket::create(PacketType::ChallengeOwnershipReply,
|
||||
certIDSize + textByteArraySize + challengingNodeUUIDByteArraySize + 3 * sizeof(int),
|
||||
idSize + textByteArraySize + challengingNodeUUIDByteArraySize + 3 * sizeof(int),
|
||||
true);
|
||||
|
||||
textPacket->writePrimitive(certIDSize);
|
||||
textPacket->writePrimitive(idSize);
|
||||
textPacket->writePrimitive(textByteArraySize);
|
||||
textPacket->writePrimitive(challengingNodeUUIDByteArraySize);
|
||||
textPacket->write(certID);
|
||||
textPacket->write(id);
|
||||
textPacket->write(textByteArray);
|
||||
textPacket->write(challengingNodeUUID);
|
||||
|
||||
qCDebug(commerce) << "Sending ChallengeOwnershipReply Packet containing signed text" << textByteArray << "for CertID" << certID;
|
||||
qCDebug(commerce) << "Sending ChallengeOwnershipReply Packet containing signed text" << textByteArray << "for id" << id;
|
||||
|
||||
nodeList->sendPacket(std::move(textPacket), *sendingNode);
|
||||
} else {
|
||||
auto textPacket = NLPacket::create(PacketType::ChallengeOwnership, certIDSize + textByteArraySize + 2 * sizeof(int), true);
|
||||
auto textPacket = NLPacket::create(PacketType::ChallengeOwnership, idSize + textByteArraySize + 2 * sizeof(int), true);
|
||||
|
||||
textPacket->writePrimitive(certIDSize);
|
||||
textPacket->writePrimitive(idSize);
|
||||
textPacket->writePrimitive(textByteArraySize);
|
||||
textPacket->write(certID);
|
||||
textPacket->write(id);
|
||||
textPacket->write(textByteArray);
|
||||
|
||||
qCDebug(commerce) << "Sending ChallengeOwnership Packet containing signed text" << textByteArray << "for CertID" << certID;
|
||||
qCDebug(commerce) << "Sending ChallengeOwnership Packet containing signed text" << textByteArray << "for id" << id;
|
||||
|
||||
nodeList->sendPacket(std::move(textPacket), *sendingNode);
|
||||
}
|
||||
|
|
|
@ -244,6 +244,7 @@ void GraphicsEngine::render_performFrame() {
|
|||
finalFramebuffer = framebufferCache->getFramebuffer();
|
||||
}
|
||||
|
||||
std::queue<Application::SnapshotOperator> snapshotOperators;
|
||||
if (!_programsCompiled.load()) {
|
||||
gpu::doInBatch("splashFrame", _gpuContext, [&](gpu::Batch& batch) {
|
||||
batch.setFramebuffer(finalFramebuffer);
|
||||
|
@ -271,6 +272,7 @@ void GraphicsEngine::render_performFrame() {
|
|||
PROFILE_RANGE(render, "/runRenderFrame");
|
||||
renderArgs._hudOperator = displayPlugin->getHUDOperator();
|
||||
renderArgs._hudTexture = qApp->getApplicationOverlay().getOverlayTexture();
|
||||
renderArgs._takingSnapshot = qApp->takeSnapshotOperators(snapshotOperators);
|
||||
renderArgs._blitFramebuffer = finalFramebuffer;
|
||||
render_runRenderFrame(&renderArgs);
|
||||
}
|
||||
|
@ -285,6 +287,7 @@ void GraphicsEngine::render_performFrame() {
|
|||
frameBufferCache->releaseFramebuffer(framebuffer);
|
||||
}
|
||||
};
|
||||
frame->snapshotOperators = snapshotOperators;
|
||||
// deliver final scene rendering commands to the display plugin
|
||||
{
|
||||
PROFILE_RANGE(render, "/pluginOutput");
|
||||
|
|
|
@ -302,8 +302,11 @@ int main(int argc, const char* argv[]) {
|
|||
PROFILE_SYNC_BEGIN(startup, "app full ctor", "");
|
||||
Application app(argcExtended, const_cast<char**>(argvExtended.data()), startupTime, runningMarkerExisted);
|
||||
PROFILE_SYNC_END(startup, "app full ctor", "");
|
||||
|
||||
|
||||
|
||||
#if defined(Q_OS_LINUX)
|
||||
app.setWindowIcon(QIcon(PathUtils::resourcesPath() + "images/hifi-logo.svg"));
|
||||
#endif
|
||||
|
||||
QTimer exitTimer;
|
||||
if (traceDuration > 0.0f) {
|
||||
exitTimer.setSingleShot(true);
|
||||
|
|
|
@ -233,16 +233,19 @@ PointerEvent LaserPointer::buildPointerEvent(const PickedObject& target, const P
|
|||
|
||||
// If we just started triggering and we haven't moved too much, don't update intersection and pos2D
|
||||
TriggerState& state = hover ? _latestState : _states[button];
|
||||
float sensorToWorldScale = DependencyManager::get<AvatarManager>()->getMyAvatar()->getSensorToWorldScale();
|
||||
float deadspotSquared = TOUCH_PRESS_TO_MOVE_DEADSPOT_SQUARED * sensorToWorldScale * sensorToWorldScale;
|
||||
bool withinDeadspot = usecTimestampNow() - state.triggerStartTime < POINTER_MOVE_DELAY && glm::distance2(pos2D, state.triggerPos2D) < deadspotSquared;
|
||||
if ((state.triggering || state.wasTriggering) && !state.deadspotExpired && withinDeadspot) {
|
||||
pos2D = state.triggerPos2D;
|
||||
intersection = state.intersection;
|
||||
surfaceNormal = state.surfaceNormal;
|
||||
}
|
||||
if (!withinDeadspot) {
|
||||
state.deadspotExpired = true;
|
||||
auto avatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
if (avatar) {
|
||||
float sensorToWorldScale = avatar->getSensorToWorldScale();
|
||||
float deadspotSquared = TOUCH_PRESS_TO_MOVE_DEADSPOT_SQUARED * sensorToWorldScale * sensorToWorldScale;
|
||||
bool withinDeadspot = usecTimestampNow() - state.triggerStartTime < POINTER_MOVE_DELAY && glm::distance2(pos2D, state.triggerPos2D) < deadspotSquared;
|
||||
if ((state.triggering || state.wasTriggering) && !state.deadspotExpired && withinDeadspot) {
|
||||
pos2D = state.triggerPos2D;
|
||||
intersection = state.intersection;
|
||||
surfaceNormal = state.surfaceNormal;
|
||||
}
|
||||
if (!withinDeadspot) {
|
||||
state.deadspotExpired = true;
|
||||
}
|
||||
}
|
||||
|
||||
return PointerEvent(pos2D, intersection, surfaceNormal, direction);
|
||||
|
|
|
@ -88,44 +88,44 @@ void Audio::setMuted(bool isMuted) {
|
|||
void Audio::setMutedDesktop(bool isMuted) {
|
||||
bool changed = false;
|
||||
withWriteLock([&] {
|
||||
if (_desktopMuted != isMuted) {
|
||||
if (_mutedDesktop != isMuted) {
|
||||
changed = true;
|
||||
_desktopMuted = isMuted;
|
||||
_mutedDesktop = isMuted;
|
||||
auto client = DependencyManager::get<AudioClient>().data();
|
||||
QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false));
|
||||
}
|
||||
});
|
||||
if (changed) {
|
||||
emit mutedChanged(isMuted);
|
||||
emit desktopMutedChanged(isMuted);
|
||||
emit mutedDesktopChanged(isMuted);
|
||||
}
|
||||
}
|
||||
|
||||
bool Audio::getMutedDesktop() const {
|
||||
return resultWithReadLock<bool>([&] {
|
||||
return _desktopMuted;
|
||||
return _mutedDesktop;
|
||||
});
|
||||
}
|
||||
|
||||
void Audio::setMutedHMD(bool isMuted) {
|
||||
bool changed = false;
|
||||
withWriteLock([&] {
|
||||
if (_hmdMuted != isMuted) {
|
||||
if (_mutedHMD != isMuted) {
|
||||
changed = true;
|
||||
_hmdMuted = isMuted;
|
||||
_mutedHMD = isMuted;
|
||||
auto client = DependencyManager::get<AudioClient>().data();
|
||||
QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false));
|
||||
}
|
||||
});
|
||||
if (changed) {
|
||||
emit mutedChanged(isMuted);
|
||||
emit hmdMutedChanged(isMuted);
|
||||
emit mutedHMDChanged(isMuted);
|
||||
}
|
||||
}
|
||||
|
||||
bool Audio::getMutedHMD() const {
|
||||
return resultWithReadLock<bool>([&] {
|
||||
return _hmdMuted;
|
||||
return _mutedHMD;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -174,14 +174,10 @@ void Audio::setPTTDesktop(bool enabled) {
|
|||
_pttDesktop = enabled;
|
||||
}
|
||||
});
|
||||
if (!enabled) {
|
||||
// Set to default behavior (unmuted for Desktop) on Push-To-Talk disable.
|
||||
setMutedDesktop(true);
|
||||
} else {
|
||||
// Should be muted when not pushing to talk while PTT is enabled.
|
||||
if (enabled || _settingsLoaded) {
|
||||
// Set to default behavior (muted for Desktop) on Push-To-Talk disable or when enabled. Settings also need to be loaded.
|
||||
setMutedDesktop(true);
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
emit pushToTalkChanged(enabled);
|
||||
emit pushToTalkDesktopChanged(enabled);
|
||||
|
@ -202,12 +198,9 @@ void Audio::setPTTHMD(bool enabled) {
|
|||
_pttHMD = enabled;
|
||||
}
|
||||
});
|
||||
if (!enabled) {
|
||||
// Set to default behavior (unmuted for HMD) on Push-To-Talk disable.
|
||||
setMutedHMD(false);
|
||||
} else {
|
||||
// Should be muted when not pushing to talk while PTT is enabled.
|
||||
setMutedHMD(true);
|
||||
if (enabled || _settingsLoaded) {
|
||||
// Set to default behavior (unmuted for HMD) on Push-To-Talk disable or muted for when PTT is enabled.
|
||||
setMutedHMD(enabled);
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
|
@ -217,20 +210,21 @@ void Audio::setPTTHMD(bool enabled) {
|
|||
}
|
||||
|
||||
void Audio::saveData() {
|
||||
_desktopMutedSetting.set(getMutedDesktop());
|
||||
_hmdMutedSetting.set(getMutedHMD());
|
||||
_mutedDesktopSetting.set(getMutedDesktop());
|
||||
_mutedHMDSetting.set(getMutedHMD());
|
||||
_pttDesktopSetting.set(getPTTDesktop());
|
||||
_pttHMDSetting.set(getPTTHMD());
|
||||
}
|
||||
|
||||
void Audio::loadData() {
|
||||
_desktopMuted = _desktopMutedSetting.get();
|
||||
_hmdMuted = _hmdMutedSetting.get();
|
||||
_pttDesktop = _pttDesktopSetting.get();
|
||||
_pttHMD = _pttHMDSetting.get();
|
||||
setMutedDesktop(_mutedDesktopSetting.get());
|
||||
setMutedHMD(_mutedHMDSetting.get());
|
||||
setPTTDesktop(_pttDesktopSetting.get());
|
||||
setPTTHMD(_pttHMDSetting.get());
|
||||
|
||||
auto client = DependencyManager::get<AudioClient>().data();
|
||||
QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted()), Q_ARG(bool, false));
|
||||
_settingsLoaded = true;
|
||||
}
|
||||
|
||||
bool Audio::getPTTHMD() const {
|
||||
|
@ -357,10 +351,12 @@ void Audio::onContextChanged() {
|
|||
changed = true;
|
||||
}
|
||||
});
|
||||
if (isHMD) {
|
||||
setMuted(getMutedHMD());
|
||||
} else {
|
||||
setMuted(getMutedDesktop());
|
||||
if (_settingsLoaded) {
|
||||
bool isMuted = isHMD ? getMutedHMD() : getMutedDesktop();
|
||||
setMuted(isMuted);
|
||||
// always set audio client muted state on context changed - sometimes setMuted does not catch it.
|
||||
auto client = DependencyManager::get<AudioClient>().data();
|
||||
QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false));
|
||||
}
|
||||
if (changed) {
|
||||
emit contextChanged(isHMD ? Audio::HMD : Audio::DESKTOP);
|
||||
|
|
|
@ -41,6 +41,7 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
|
|||
* @hifi-assignment-client
|
||||
*
|
||||
* @property {boolean} muted - <code>true</code> if the audio input is muted, otherwise <code>false</code>.
|
||||
* @property {boolean} mutedDesktop - <code>true</code> if the audio input is muted, otherwise <code>false</code>.
|
||||
* @property {boolean} noiseReduction - <code>true</code> if noise reduction is enabled, otherwise <code>false</code>. When
|
||||
* enabled, the input audio signal is blocked (fully attenuated) when it falls below an adaptive threshold set just
|
||||
* above the noise floor.
|
||||
|
@ -68,8 +69,8 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
|
|||
Q_PROPERTY(bool clipping READ isClipping NOTIFY clippingChanged)
|
||||
Q_PROPERTY(QString context READ getContext NOTIFY contextChanged)
|
||||
Q_PROPERTY(AudioDevices* devices READ getDevices NOTIFY nop)
|
||||
Q_PROPERTY(bool desktopMuted READ getMutedDesktop WRITE setMutedDesktop NOTIFY desktopMutedChanged)
|
||||
Q_PROPERTY(bool hmdMuted READ getMutedHMD WRITE setMutedHMD NOTIFY hmdMutedChanged)
|
||||
Q_PROPERTY(bool mutedDesktop READ getMutedDesktop WRITE setMutedDesktop NOTIFY mutedDesktopChanged)
|
||||
Q_PROPERTY(bool mutedHMD READ getMutedHMD WRITE setMutedHMD NOTIFY mutedHMDChanged)
|
||||
Q_PROPERTY(bool pushToTalk READ getPTT WRITE setPTT NOTIFY pushToTalkChanged);
|
||||
Q_PROPERTY(bool pushToTalkDesktop READ getPTTDesktop WRITE setPTTDesktop NOTIFY pushToTalkDesktopChanged)
|
||||
Q_PROPERTY(bool pushToTalkHMD READ getPTTHMD WRITE setPTTHMD NOTIFY pushToTalkHMDChanged)
|
||||
|
@ -287,19 +288,19 @@ signals:
|
|||
|
||||
/**jsdoc
|
||||
* Triggered when desktop audio input is muted or unmuted.
|
||||
* @function Audio.desktopMutedChanged
|
||||
* @function Audio.mutedDesktopChanged
|
||||
* @param {boolean} isMuted - <code>true</code> if the audio input is muted for desktop mode, otherwise <code>false</code>.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void desktopMutedChanged(bool isMuted);
|
||||
void mutedDesktopChanged(bool isMuted);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when HMD audio input is muted or unmuted.
|
||||
* @function Audio.hmdMutedChanged
|
||||
* @function Audio.mutedHMDChanged
|
||||
* @param {boolean} isMuted - <code>true</code> if the audio input is muted for HMD mode, otherwise <code>false</code>.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void hmdMutedChanged(bool isMuted);
|
||||
void mutedHMDChanged(bool isMuted);
|
||||
|
||||
/**
|
||||
* Triggered when Push-to-Talk has been enabled or disabled.
|
||||
|
@ -408,6 +409,7 @@ protected:
|
|||
|
||||
private:
|
||||
|
||||
bool _settingsLoaded { false };
|
||||
float _inputVolume { 1.0f };
|
||||
float _inputLevel { 0.0f };
|
||||
float _localInjectorGain { 0.0f }; // in dB
|
||||
|
@ -418,12 +420,12 @@ private:
|
|||
bool _contextIsHMD { false };
|
||||
AudioDevices* getDevices() { return &_devices; }
|
||||
AudioDevices _devices;
|
||||
Setting::Handle<bool> _desktopMutedSetting{ QStringList { Audio::AUDIO, "desktopMuted" }, true };
|
||||
Setting::Handle<bool> _hmdMutedSetting{ QStringList { Audio::AUDIO, "hmdMuted" }, true };
|
||||
Setting::Handle<bool> _mutedDesktopSetting{ QStringList { Audio::AUDIO, "mutedDesktop" }, true };
|
||||
Setting::Handle<bool> _mutedHMDSetting{ QStringList { Audio::AUDIO, "mutedHMD" }, true };
|
||||
Setting::Handle<bool> _pttDesktopSetting{ QStringList { Audio::AUDIO, "pushToTalkDesktop" }, false };
|
||||
Setting::Handle<bool> _pttHMDSetting{ QStringList { Audio::AUDIO, "pushToTalkHMD" }, false };
|
||||
bool _desktopMuted{ true };
|
||||
bool _hmdMuted{ false };
|
||||
bool _mutedDesktop{ true };
|
||||
bool _mutedHMD{ false };
|
||||
bool _pttDesktop{ false };
|
||||
bool _pttHMD{ false };
|
||||
bool _pushingToTalk{ false };
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "MainWindow.h"
|
||||
#include "Menu.h"
|
||||
#include "OffscreenUi.h"
|
||||
#include "commerce/QmlCommerce.h"
|
||||
|
||||
static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
|
||||
static const QString LAST_BROWSE_LOCATION_SETTING = "LastBrowseLocation";
|
||||
|
@ -134,15 +135,17 @@ void WindowScriptingInterface::disconnectedFromDomain() {
|
|||
|
||||
void WindowScriptingInterface::openUrl(const QUrl& url) {
|
||||
if (!url.isEmpty()) {
|
||||
if (url.scheme() == URL_SCHEME_HIFI) {
|
||||
auto scheme = url.scheme();
|
||||
if (scheme == URL_SCHEME_HIFI) {
|
||||
DependencyManager::get<AddressManager>()->handleLookupString(url.toString());
|
||||
} else if (scheme == URL_SCHEME_HIFIAPP) {
|
||||
DependencyManager::get<QmlCommerce>()->openSystemApp(url.path());
|
||||
} else {
|
||||
#if defined(Q_OS_ANDROID)
|
||||
QMap<QString, QString> args;
|
||||
args["url"] = url.toString();
|
||||
AndroidHelper::instance().requestActivity("WebView", true, args);
|
||||
#else
|
||||
// address manager did not handle - ask QDesktopServices to handle
|
||||
QDesktopServices::openUrl(url);
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -535,9 +535,10 @@ public slots:
|
|||
int openMessageBox(QString title, QString text, int buttons, int defaultButton);
|
||||
|
||||
/**jsdoc
|
||||
* Open a URL in the Interface window or other application, depending on the URL's scheme. If the URL starts with
|
||||
* <code>hifi://</code> then that URL is navigated to in Interface, otherwise the URL is opened in the application the OS
|
||||
* associates with the URL's scheme (e.g., a Web browser for <code>http://</code>).
|
||||
* Open a URL in the Interface window or other application, depending on the URL's scheme. The following schemes are supported:
|
||||
* <code>hifi</code> (navigate to the URL in Interface), <code>hifiapp<code> (open a system app in Interface). Other schemes will either be handled by the OS
|
||||
* (e.g. <code>http</code>, <code>https</code>, <code>mailto</code>) or will create a confirmation dialog asking the user to confirm that they want to try to open
|
||||
* the URL.
|
||||
* @function Window.openUrl
|
||||
* @param {string} url - The URL to open.
|
||||
*/
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <AudioClient.h>
|
||||
#include <SettingHandle.h>
|
||||
#include <trackers/FaceTracker.h>
|
||||
#include <UsersScriptingInterface.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Menu.h"
|
||||
|
@ -30,6 +31,10 @@ AvatarInputs* AvatarInputs::getInstance() {
|
|||
|
||||
AvatarInputs::AvatarInputs(QObject* parent) : QObject(parent) {
|
||||
_showAudioTools = showAudioToolsSetting.get();
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto usersScriptingInterface = DependencyManager::get<UsersScriptingInterface>();
|
||||
connect(nodeList.data(), &NodeList::ignoreRadiusEnabledChanged, this, &AvatarInputs::ignoreRadiusEnabledChanged);
|
||||
connect(usersScriptingInterface.data(), &UsersScriptingInterface::enteredIgnoreRadius, this, &AvatarInputs::enteredIgnoreRadiusChanged);
|
||||
}
|
||||
|
||||
#define AI_UPDATE(name, src) \
|
||||
|
@ -83,6 +88,10 @@ void AvatarInputs::setShowAudioTools(bool showAudioTools) {
|
|||
emit showAudioToolsChanged(_showAudioTools);
|
||||
}
|
||||
|
||||
bool AvatarInputs::getIgnoreRadiusEnabled() const {
|
||||
return DependencyManager::get<NodeList>()->getIgnoreRadiusEnabled();
|
||||
}
|
||||
|
||||
void AvatarInputs::toggleCameraMute() {
|
||||
FaceTracker* faceTracker = qApp->getSelectedFaceTracker();
|
||||
if (faceTracker) {
|
||||
|
|
|
@ -42,6 +42,8 @@ class AvatarInputs : public QObject {
|
|||
AI_PROPERTY(bool, isHMD, false)
|
||||
|
||||
Q_PROPERTY(bool showAudioTools READ showAudioTools WRITE setShowAudioTools NOTIFY showAudioToolsChanged)
|
||||
Q_PROPERTY(bool ignoreRadiusEnabled READ getIgnoreRadiusEnabled NOTIFY ignoreRadiusEnabledChanged)
|
||||
//Q_PROPERTY(bool enteredIgnoreRadius READ getEnteredIgnoreRadius NOTIFY enteredIgnoreRadiusChanged)
|
||||
|
||||
public:
|
||||
static AvatarInputs* getInstance();
|
||||
|
@ -55,7 +57,9 @@ public:
|
|||
|
||||
AvatarInputs(QObject* parent = nullptr);
|
||||
void update();
|
||||
bool showAudioTools() const { return _showAudioTools; }
|
||||
bool showAudioTools() const { return _showAudioTools; }
|
||||
bool getIgnoreRadiusEnabled() const;
|
||||
//bool getEnteredIgnoreRadius() const;
|
||||
|
||||
public slots:
|
||||
|
||||
|
@ -93,6 +97,34 @@ signals:
|
|||
*/
|
||||
void showAudioToolsChanged(bool show);
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarInputs.avatarEnteredIgnoreRadius
|
||||
* @param {QUuid} avatarID
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void avatarEnteredIgnoreRadius(QUuid avatarID);
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarInputs.avatarLeftIgnoreRadius
|
||||
* @param {QUuid} avatarID
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void avatarLeftIgnoreRadius(QUuid avatarID);
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarInputs.ignoreRadiusEnabledChanged
|
||||
* @param {boolean} enabled
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void ignoreRadiusEnabledChanged(bool enabled);
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarInputs.enteredIgnoreRadiusChanged
|
||||
* @param {boolean} enabled
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void enteredIgnoreRadiusChanged();
|
||||
|
||||
protected:
|
||||
|
||||
/**jsdoc
|
||||
|
@ -106,6 +138,8 @@ protected:
|
|||
Q_INVOKABLE void toggleCameraMute();
|
||||
|
||||
private:
|
||||
void onAvatarEnteredIgnoreRadius();
|
||||
void onAvatarLeftIgnoreRadius();
|
||||
float _trailingAudioLoudness{ 0 };
|
||||
bool _showAudioTools { false };
|
||||
};
|
||||
|
|
|
@ -156,10 +156,10 @@ void DialogsManager::hmdTools(bool showTools) {
|
|||
}
|
||||
_hmdToolsDialog->show();
|
||||
_hmdToolsDialog->raise();
|
||||
qApp->getWindow()->activateWindow();
|
||||
} else {
|
||||
hmdToolsClosed();
|
||||
}
|
||||
qApp->getWindow()->activateWindow();
|
||||
}
|
||||
|
||||
void DialogsManager::hmdToolsClosed() {
|
||||
|
@ -207,4 +207,4 @@ void DialogsManager::showDomainConnectionDialog() {
|
|||
|
||||
_domainConnectionDialog->show();
|
||||
_domainConnectionDialog->raise();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -910,6 +910,9 @@ void Keyboard::loadKeyboardFile(const QString& keyboardFile) {
|
|||
});
|
||||
_layerIndex = 0;
|
||||
addIncludeItemsToMallets();
|
||||
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
scaleKeyboard(myAvatar->getSensorToWorldScale());
|
||||
});
|
||||
|
||||
request->send();
|
||||
|
|
|
@ -98,6 +98,7 @@ public:
|
|||
bool isPassword() const;
|
||||
void setPassword(bool password);
|
||||
void enableRightMallet();
|
||||
void scaleKeyboard(float sensorToWorldScale);
|
||||
void enableLeftMallet();
|
||||
void disableRightMallet();
|
||||
void disableLeftMallet();
|
||||
|
@ -122,7 +123,6 @@ public slots:
|
|||
void handleTriggerContinue(const QUuid& id, const PointerEvent& event);
|
||||
void handleHoverBegin(const QUuid& id, const PointerEvent& event);
|
||||
void handleHoverEnd(const QUuid& id, const PointerEvent& event);
|
||||
void scaleKeyboard(float sensorToWorldScale);
|
||||
|
||||
private:
|
||||
struct Anchor {
|
||||
|
|
|
@ -138,7 +138,7 @@ void LoginDialog::login(const QString& username, const QString& password) const
|
|||
void LoginDialog::loginThroughOculus() {
|
||||
qDebug() << "Attempting to login through Oculus";
|
||||
if (auto oculusPlatformPlugin = PluginManager::getInstance()->getOculusPlatformPlugin()) {
|
||||
oculusPlatformPlugin->requestNonceAndUserID([this] (QString nonce, QString oculusID) {
|
||||
oculusPlatformPlugin->requestNonceAndUserID([] (QString nonce, QString oculusID) {
|
||||
DependencyManager::get<AccountManager>()->requestAccessTokenWithOculus(nonce, oculusID);
|
||||
});
|
||||
}
|
||||
|
@ -279,10 +279,6 @@ void LoginDialog::createAccountFromSteam(QString username) {
|
|||
}
|
||||
}
|
||||
|
||||
void LoginDialog::openUrl(const QString& url) const {
|
||||
QDesktopServices::openUrl(QUrl(url));
|
||||
}
|
||||
|
||||
void LoginDialog::linkCompleted(QNetworkReply* reply) {
|
||||
emit handleLinkCompleted();
|
||||
}
|
||||
|
|
|
@ -80,8 +80,6 @@ protected slots:
|
|||
|
||||
Q_INVOKABLE void signup(const QString& email, const QString& username, const QString& password);
|
||||
|
||||
Q_INVOKABLE void openUrl(const QString& url) const;
|
||||
|
||||
Q_INVOKABLE bool getLoginDialogPoppedUp() const;
|
||||
};
|
||||
|
||||
|
|
|
@ -266,6 +266,11 @@ void setupPreferences() {
|
|||
auto preference = new CheckPreference(VR_MOVEMENT, "Walking", getter, setter);
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
{
|
||||
auto getter = [myAvatar]()->bool { return myAvatar->getStrafeEnabled(); };
|
||||
auto setter = [myAvatar](bool value) { myAvatar->setStrafeEnabled(value); };
|
||||
preferences->addPreference(new CheckPreference(VR_MOVEMENT, "Strafing", getter, setter));
|
||||
}
|
||||
{
|
||||
auto getter = [myAvatar]()->bool { return myAvatar->getFlyingHMDPref(); };
|
||||
auto setter = [myAvatar](bool value) { myAvatar->setFlyingHMDPref(value); };
|
||||
|
@ -273,6 +278,22 @@ void setupPreferences() {
|
|||
preference->setIndented(true);
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
{
|
||||
auto getter = [myAvatar]()->int { return myAvatar->getMovementReference(); };
|
||||
auto setter = [myAvatar](int value) { myAvatar->setMovementReference(value); };
|
||||
//auto preference = new CheckPreference(VR_MOVEMENT, "Hand-Relative Movement", getter, setter);
|
||||
auto preference = new RadioButtonsPreference(VR_MOVEMENT, "Movement Direction", getter, setter);
|
||||
QStringList items;
|
||||
items << "HMD-Relative" << "Hand-Relative" << "Hand-Relative (Leveled)";
|
||||
preference->setHeading("Movement Direction");
|
||||
preference->setItems(items);
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
{
|
||||
auto getter = [myAvatar]()->QString { return myAvatar->getDominantHand(); };
|
||||
auto setter = [myAvatar](const QString& value) { myAvatar->setDominantHand(value); };
|
||||
preferences->addPreference(new PrimaryHandPreference(VR_MOVEMENT, "Dominant Hand", getter, setter));
|
||||
}
|
||||
{
|
||||
auto getter = [myAvatar]()->int { return myAvatar->getSnapTurn() ? 0 : 1; };
|
||||
auto setter = [myAvatar](int value) { myAvatar->setSnapTurn(value == 0); };
|
||||
|
@ -283,6 +304,26 @@ void setupPreferences() {
|
|||
preference->setItems(items);
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
{
|
||||
auto getter = [myAvatar]()->int { return myAvatar->getControlScheme(); };
|
||||
auto setter = [myAvatar](int index) { myAvatar->setControlScheme(index); };
|
||||
auto preference = new RadioButtonsPreference(VR_MOVEMENT, "Control Scheme", getter, setter);
|
||||
QStringList items;
|
||||
items << "Default" << "Analog" << "Analog++";
|
||||
preference->setHeading("Control Scheme Selection");
|
||||
preference->setItems(items);
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
{
|
||||
auto getter = [myAvatar]()->float { return myAvatar->getAnalogPlusWalkSpeed(); };
|
||||
auto setter = [myAvatar](float value) { myAvatar->setAnalogPlusWalkSpeed(value); };
|
||||
auto preference = new SpinnerSliderPreference(VR_MOVEMENT, "Analog++ Walk Speed", getter, setter);
|
||||
preference->setMin(6.0f);
|
||||
preference->setMax(30.0f);
|
||||
preference->setStep(1);
|
||||
preference->setDecimals(2);
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
{
|
||||
auto getter = [myAvatar]()->bool { return myAvatar->getShowPlayArea(); };
|
||||
auto setter = [myAvatar](bool value) { myAvatar->setShowPlayArea(value); };
|
||||
|
|
|
@ -159,47 +159,57 @@ void Snapshot::save360Snapshot(const glm::vec3& cameraPosition,
|
|||
secondaryCameraRenderConfig->setOrientation(CAMERA_ORIENTATION_DOWN);
|
||||
|
||||
_snapshotIndex = 0;
|
||||
_taking360Snapshot = true;
|
||||
|
||||
_snapshotTimer.start(SNAPSHOT_360_TIMER_INTERVAL);
|
||||
}
|
||||
|
||||
void Snapshot::takeNextSnapshot() {
|
||||
SecondaryCameraJobConfig* config =
|
||||
static_cast<SecondaryCameraJobConfig*>(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCamera"));
|
||||
if (_taking360Snapshot) {
|
||||
if (!_waitingOnSnapshot) {
|
||||
_waitingOnSnapshot = true;
|
||||
qApp->addSnapshotOperator(std::make_tuple([this](const QImage& snapshot) {
|
||||
// Order is:
|
||||
// 0. Down
|
||||
// 1. Front
|
||||
// 2. Left
|
||||
// 3. Back
|
||||
// 4. Right
|
||||
// 5. Up
|
||||
if (_snapshotIndex < 6) {
|
||||
_imageArray[_snapshotIndex] = snapshot;
|
||||
}
|
||||
|
||||
// Order is:
|
||||
// 0. Down
|
||||
// 1. Front
|
||||
// 2. Left
|
||||
// 3. Back
|
||||
// 4. Right
|
||||
// 5. Up
|
||||
if (_snapshotIndex < 6) {
|
||||
_imageArray[_snapshotIndex] = qApp->getActiveDisplayPlugin()->getSecondaryCameraScreenshot();
|
||||
}
|
||||
SecondaryCameraJobConfig* config = static_cast<SecondaryCameraJobConfig*>(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCamera"));
|
||||
if (_snapshotIndex == 0) {
|
||||
// Setup for Front Image capture
|
||||
config->setOrientation(CAMERA_ORIENTATION_FRONT);
|
||||
} else if (_snapshotIndex == 1) {
|
||||
// Setup for Left Image capture
|
||||
config->setOrientation(CAMERA_ORIENTATION_LEFT);
|
||||
} else if (_snapshotIndex == 2) {
|
||||
// Setup for Back Image capture
|
||||
config->setOrientation(CAMERA_ORIENTATION_BACK);
|
||||
} else if (_snapshotIndex == 3) {
|
||||
// Setup for Right Image capture
|
||||
config->setOrientation(CAMERA_ORIENTATION_RIGHT);
|
||||
} else if (_snapshotIndex == 4) {
|
||||
// Setup for Up Image capture
|
||||
config->setOrientation(CAMERA_ORIENTATION_UP);
|
||||
} else if (_snapshotIndex == 5) {
|
||||
_taking360Snapshot = false;
|
||||
}
|
||||
|
||||
if (_snapshotIndex == 0) {
|
||||
// Setup for Front Image capture
|
||||
config->setOrientation(CAMERA_ORIENTATION_FRONT);
|
||||
} else if (_snapshotIndex == 1) {
|
||||
// Setup for Left Image capture
|
||||
config->setOrientation(CAMERA_ORIENTATION_LEFT);
|
||||
} else if (_snapshotIndex == 2) {
|
||||
// Setup for Back Image capture
|
||||
config->setOrientation(CAMERA_ORIENTATION_BACK);
|
||||
} else if (_snapshotIndex == 3) {
|
||||
// Setup for Right Image capture
|
||||
config->setOrientation(CAMERA_ORIENTATION_RIGHT);
|
||||
} else if (_snapshotIndex == 4) {
|
||||
// Setup for Up Image capture
|
||||
config->setOrientation(CAMERA_ORIENTATION_UP);
|
||||
} else if (_snapshotIndex > 5) {
|
||||
_waitingOnSnapshot = false;
|
||||
_snapshotIndex++;
|
||||
}, 0.0f, false));
|
||||
}
|
||||
} else {
|
||||
_snapshotTimer.stop();
|
||||
|
||||
// Reset secondary camera render config
|
||||
static_cast<ToneMappingConfig*>(
|
||||
qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCameraJob.ToneMapping"))
|
||||
->setCurve(1);
|
||||
SecondaryCameraJobConfig* config = static_cast<SecondaryCameraJobConfig*>(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCamera"));
|
||||
static_cast<ToneMappingConfig*>(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCameraJob.ToneMapping"))->setCurve(1);
|
||||
config->resetSizeSpectatorCamera(qApp->getWindow()->geometry().width(), qApp->getWindow()->geometry().height());
|
||||
config->setProperty("attachedEntityId", _oldAttachedEntityId);
|
||||
config->setProperty("vFoV", _oldvFoV);
|
||||
|
@ -217,8 +227,6 @@ void Snapshot::takeNextSnapshot() {
|
|||
QtConcurrent::run([this]() { convertToEquirectangular(); });
|
||||
}
|
||||
}
|
||||
|
||||
_snapshotIndex++;
|
||||
}
|
||||
|
||||
void Snapshot::convertToCubemap() {
|
||||
|
|
|
@ -97,6 +97,8 @@ private:
|
|||
bool _cubemapOutputFormat;
|
||||
QTimer _snapshotTimer;
|
||||
qint16 _snapshotIndex;
|
||||
bool _waitingOnSnapshot { false };
|
||||
bool _taking360Snapshot { false };
|
||||
bool _oldEnabled;
|
||||
QVariant _oldAttachedEntityId;
|
||||
QVariant _oldOrientation;
|
||||
|
|
|
@ -27,7 +27,6 @@ QString SnapshotAnimated::snapshotAnimatedPath;
|
|||
QString SnapshotAnimated::snapshotStillPath;
|
||||
QVector<QImage> SnapshotAnimated::snapshotAnimatedFrameVector;
|
||||
QVector<qint64> SnapshotAnimated::snapshotAnimatedFrameDelayVector;
|
||||
Application* SnapshotAnimated::app;
|
||||
float SnapshotAnimated::aspectRatio;
|
||||
QSharedPointer<WindowScriptingInterface> SnapshotAnimated::snapshotAnimatedDM;
|
||||
GifWriter SnapshotAnimated::snapshotAnimatedGifWriter;
|
||||
|
@ -36,12 +35,11 @@ GifWriter SnapshotAnimated::snapshotAnimatedGifWriter;
|
|||
Setting::Handle<bool> SnapshotAnimated::alsoTakeAnimatedSnapshot("alsoTakeAnimatedSnapshot", true);
|
||||
Setting::Handle<float> SnapshotAnimated::snapshotAnimatedDuration("snapshotAnimatedDuration", SNAPSNOT_ANIMATED_DURATION_SECS);
|
||||
|
||||
void SnapshotAnimated::saveSnapshotAnimated(QString pathStill, float aspectRatio, Application* app, QSharedPointer<WindowScriptingInterface> dm) {
|
||||
void SnapshotAnimated::saveSnapshotAnimated(QString pathStill, float aspectRatio, QSharedPointer<WindowScriptingInterface> dm) {
|
||||
// If we're not in the middle of capturing an animated snapshot...
|
||||
if (SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp == 0) {
|
||||
SnapshotAnimated::snapshotAnimatedTimer = new QTimer();
|
||||
SnapshotAnimated::aspectRatio = aspectRatio;
|
||||
SnapshotAnimated::app = app;
|
||||
SnapshotAnimated::snapshotAnimatedDM = dm;
|
||||
// Define the output location of the still and animated snapshots.
|
||||
SnapshotAnimated::snapshotStillPath = pathStill;
|
||||
|
@ -62,44 +60,45 @@ void SnapshotAnimated::saveSnapshotAnimated(QString pathStill, float aspectRatio
|
|||
|
||||
void SnapshotAnimated::captureFrames() {
|
||||
if (SnapshotAnimated::snapshotAnimatedTimerRunning) {
|
||||
// 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(SnapshotAnimated::app->getActiveDisplayPlugin()->getScreenshot(SnapshotAnimated::aspectRatio));
|
||||
frame = frame.scaledToWidth(SNAPSNOT_ANIMATED_WIDTH);
|
||||
SnapshotAnimated::snapshotAnimatedFrameVector.append(frame);
|
||||
qApp->addSnapshotOperator(std::make_tuple([](const QImage& snapshot) {
|
||||
// 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 = snapshot.scaledToWidth(SNAPSNOT_ANIMATED_WIDTH);
|
||||
SnapshotAnimated::snapshotAnimatedFrameVector.append(frame);
|
||||
|
||||
// If that was the first frame...
|
||||
if (SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp == 0) {
|
||||
// Record the current frame timestamp
|
||||
SnapshotAnimated::snapshotAnimatedTimestamp = QDateTime::currentMSecsSinceEpoch();
|
||||
// Record the first frame timestamp
|
||||
SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp = SnapshotAnimated::snapshotAnimatedTimestamp;
|
||||
SnapshotAnimated::snapshotAnimatedFrameDelayVector.append(SNAPSNOT_ANIMATED_FRAME_DELAY_MSEC / 10);
|
||||
// If this is an intermediate or the final frame...
|
||||
} else {
|
||||
// Push the current frame delay onto the vector
|
||||
SnapshotAnimated::snapshotAnimatedFrameDelayVector.append(round(((float)(QDateTime::currentMSecsSinceEpoch() - SnapshotAnimated::snapshotAnimatedTimestamp)) / 10));
|
||||
// Record the current frame timestamp
|
||||
SnapshotAnimated::snapshotAnimatedTimestamp = QDateTime::currentMSecsSinceEpoch();
|
||||
// If that was the first frame...
|
||||
if (SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp == 0) {
|
||||
// Record the current frame timestamp
|
||||
SnapshotAnimated::snapshotAnimatedTimestamp = QDateTime::currentMSecsSinceEpoch();
|
||||
// Record the first frame timestamp
|
||||
SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp = SnapshotAnimated::snapshotAnimatedTimestamp;
|
||||
SnapshotAnimated::snapshotAnimatedFrameDelayVector.append(SNAPSNOT_ANIMATED_FRAME_DELAY_MSEC / 10);
|
||||
// If this is an intermediate or the final frame...
|
||||
} else {
|
||||
// Push the current frame delay onto the vector
|
||||
SnapshotAnimated::snapshotAnimatedFrameDelayVector.append(round(((float)(QDateTime::currentMSecsSinceEpoch() - SnapshotAnimated::snapshotAnimatedTimestamp)) / 10));
|
||||
// Record the current frame timestamp
|
||||
SnapshotAnimated::snapshotAnimatedTimestamp = QDateTime::currentMSecsSinceEpoch();
|
||||
|
||||
// If that was the last frame...
|
||||
if ((SnapshotAnimated::snapshotAnimatedTimestamp - SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp) >= (SnapshotAnimated::snapshotAnimatedDuration.get() * MSECS_PER_SECOND)) {
|
||||
SnapshotAnimated::snapshotAnimatedTimerRunning = false;
|
||||
|
||||
// Notify the user that we're processing the snapshot
|
||||
// This also pops up the "Share" dialog. The unprocessed GIF will be visualized as a loading icon until processingGifCompleted() is called.
|
||||
emit SnapshotAnimated::snapshotAnimatedDM->processingGifStarted(SnapshotAnimated::snapshotStillPath);
|
||||
|
||||
// Kick off the thread that'll pack the frames into the GIF
|
||||
QtConcurrent::run(processFrames);
|
||||
// Stop the snapshot QTimer. This action by itself DOES NOT GUARANTEE
|
||||
// that the slot will not be called again in the future.
|
||||
// See: http://lists.qt-project.org/pipermail/qt-interest-old/2009-October/013926.html
|
||||
SnapshotAnimated::snapshotAnimatedTimer->stop();
|
||||
delete SnapshotAnimated::snapshotAnimatedTimer;
|
||||
// If that was the last frame...
|
||||
if ((SnapshotAnimated::snapshotAnimatedTimestamp - SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp) >= (SnapshotAnimated::snapshotAnimatedDuration.get() * MSECS_PER_SECOND)) {
|
||||
SnapshotAnimated::snapshotAnimatedTimerRunning = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}, SnapshotAnimated::aspectRatio, true));
|
||||
} else {
|
||||
// Notify the user that we're processing the snapshot
|
||||
// This also pops up the "Share" dialog. The unprocessed GIF will be visualized as a loading icon until processingGifCompleted() is called.
|
||||
emit SnapshotAnimated::snapshotAnimatedDM->processingGifStarted(SnapshotAnimated::snapshotStillPath);
|
||||
|
||||
// Kick off the thread that'll pack the frames into the GIF
|
||||
QtConcurrent::run(processFrames);
|
||||
// Stop the snapshot QTimer. This action by itself DOES NOT GUARANTEE
|
||||
// that the slot will not be called again in the future.
|
||||
// See: http://lists.qt-project.org/pipermail/qt-interest-old/2009-October/013926.html
|
||||
SnapshotAnimated::snapshotAnimatedTimer->stop();
|
||||
delete SnapshotAnimated::snapshotAnimatedTimer;
|
||||
}
|
||||
}
|
||||
|
||||
|
|