mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Merge branch 'master' of https://github.com/highfidelity/hifi into bugz1046
This commit is contained in:
commit
2e5127fa98
158 changed files with 8788 additions and 4060 deletions
|
@ -97,8 +97,10 @@ public:
|
|||
/**jsdoc
|
||||
* Starts playing an animation on the avatar.
|
||||
* @function Avatar.startAnimation
|
||||
* @param {string} url - The animation file's URL. Animation files need to be in the FBX format but only need to contain
|
||||
* the avatar skeleton and animation data.
|
||||
* @param {string} url - The animation file's URL. Animation files need to be in glTF or FBX format but only need to
|
||||
* contain the avatar skeleton and animation data. glTF models may be in JSON or binary format (".gltf" or ".glb" URLs
|
||||
* respectively).
|
||||
* <p><strong>Warning:</strong> glTF animations currently do not always animate correctly.</p>
|
||||
* @param {number} [fps=30] - The frames per second (FPS) rate for the animation playback. 30 FPS is normal speed.
|
||||
* @param {number} [priority=1] - <em>Not used.</em>
|
||||
* @param {boolean} [loop=false] - <code>true</code> if the animation should loop, <code>false</code> if it shouldn't.
|
||||
|
|
24
cmake/macros/TargetWebRTC.cmake
Normal file
24
cmake/macros/TargetWebRTC.cmake
Normal file
|
@ -0,0 +1,24 @@
|
|||
#
|
||||
# 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
|
||||
#
|
||||
macro(TARGET_WEBRTC)
|
||||
if (ANDROID)
|
||||
# I don't yet have working libwebrtc for android
|
||||
# include(SelectLibraryConfigurations)
|
||||
# set(INSTALL_DIR ${HIFI_ANDROID_PRECOMPILED}/webrtc/webrtc)
|
||||
# set(WEBRTC_INCLUDE_DIRS "${INSTALL_DIR}/include/webrtc")
|
||||
# set(WEBRTC_LIBRARY_DEBUG ${INSTALL_DIR}/debug/lib/libwebrtc.a)
|
||||
# set(WEBRTC_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libwebrtc.a)
|
||||
# select_library_configurations(WEBRTC)
|
||||
else()
|
||||
set(WEBRTC_INCLUDE_DIRS "${VCPKG_INSTALL_ROOT}/include/webrtc")
|
||||
find_library(WEBRTC_LIBRARY NAMES webrtc PATHS ${VCPKG_INSTALL_ROOT}/lib/ NO_DEFAULT_PATH)
|
||||
target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${WEBRTC_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET_NAME} ${WEBRTC_LIBRARY})
|
||||
endif()
|
||||
|
||||
|
||||
endmacro()
|
|
@ -1,4 +1,4 @@
|
|||
Source: hifi-deps
|
||||
Version: 0.1
|
||||
Description: Collected dependencies for High Fidelity applications
|
||||
Build-Depends: bullet3, draco, etc2comp, glm, nvtt, openexr (!android), openssl (windows), tbb (!android&!osx), zlib
|
||||
Build-Depends: bullet3, draco, etc2comp, glm, nvtt, openexr (!android), openssl (windows), tbb (!android&!osx), zlib, webrtc (!android)
|
||||
|
|
3
cmake/ports/webrtc/CONTROL
Normal file
3
cmake/ports/webrtc/CONTROL
Normal file
|
@ -0,0 +1,3 @@
|
|||
Source: webrtc
|
||||
Version: 20190626
|
||||
Description: WebRTC
|
36
cmake/ports/webrtc/portfile.cmake
Normal file
36
cmake/ports/webrtc/portfile.cmake
Normal file
|
@ -0,0 +1,36 @@
|
|||
include(vcpkg_common_functions)
|
||||
set(WEBRTC_VERSION 20190626)
|
||||
set(MASTER_COPY_SOURCE_PATH ${CURRENT_BUILDTREES_DIR}/src)
|
||||
|
||||
if (ANDROID)
|
||||
# this is handled by hifi_android.py
|
||||
elseif (WIN32)
|
||||
vcpkg_download_distfile(
|
||||
WEBRTC_SOURCE_ARCHIVE
|
||||
URLS https://hifi-public.s3.amazonaws.com/seth/webrtc-20190626-windows.zip
|
||||
SHA512 c0848eddb1579b3bb0496b8785e24f30470f3c477145035fd729264a326a467b9467ae9f426aa5d72d168ad9e9bf2c279150744832736bdf39064d24b04de1a3
|
||||
FILENAME webrtc-20190626-windows.zip
|
||||
)
|
||||
elseif (APPLE)
|
||||
vcpkg_download_distfile(
|
||||
WEBRTC_SOURCE_ARCHIVE
|
||||
URLS https://hifi-public.s3.amazonaws.com/seth/webrtc-20190626-osx.tar.gz
|
||||
SHA512 fc70cec1b5ee87395137b7090f424e2fc2300fc17d744d5ffa1cf7aa0e0f1a069a9d72ba1ad2fb4a640ebeb6c218bda24351ba0083e1ff96c4a4b5032648a9d2
|
||||
FILENAME webrtc-20190626-osx.tar.gz
|
||||
)
|
||||
else ()
|
||||
# else Linux desktop
|
||||
vcpkg_download_distfile(
|
||||
WEBRTC_SOURCE_ARCHIVE
|
||||
URLS https://hifi-public.s3.amazonaws.com/seth/webrtc-20190626-linux.tar.gz
|
||||
SHA512 07d7776551aa78cb09a3ef088a8dee7762735c168c243053b262083d90a1d258cec66dc386f6903da5c4461921a3c2db157a1ee106a2b47e7756cb424b66cc43
|
||||
FILENAME webrtc-20190626-linux.tar.gz
|
||||
)
|
||||
endif ()
|
||||
|
||||
vcpkg_extract_source_archive(${WEBRTC_SOURCE_ARCHIVE})
|
||||
|
||||
file(COPY ${MASTER_COPY_SOURCE_PATH}/webrtc/include DESTINATION ${CURRENT_PACKAGES_DIR})
|
||||
file(COPY ${MASTER_COPY_SOURCE_PATH}/webrtc/lib DESTINATION ${CURRENT_PACKAGES_DIR})
|
||||
file(COPY ${MASTER_COPY_SOURCE_PATH}/webrtc/share DESTINATION ${CURRENT_PACKAGES_DIR})
|
||||
file(COPY ${MASTER_COPY_SOURCE_PATH}/webrtc/debug DESTINATION ${CURRENT_PACKAGES_DIR})
|
|
@ -130,12 +130,12 @@ $(document).ready(function(){
|
|||
html += "<td class='data'><strong>File Name</strong></td>";
|
||||
html += "<td class='data'><strong>Created</strong></td>";
|
||||
html += "<td class='data'><strong>Installed</strong></td>";
|
||||
//html += "<td class='data'><strong>Installed By</strong></td></tr>";
|
||||
html += "<td class='data'><strong>Installed By</strong></td></tr>";
|
||||
html += "<tr><td class='data' id='" + INSTALLED_CONTENT_NAME_ID + "'/>";
|
||||
html += "<td class='data' id='" + INSTALLED_CONTENT_FILENAME_ID + "'/>";
|
||||
html += "<td class='data' id='" + INSTALLED_CONTENT_CREATED_ID + "'/>";
|
||||
html += "<td class='data' id='" + INSTALLED_CONTENT_INSTALLED_ID + "'/>";
|
||||
//html += "<td class='data' id='" + INSTALLED_CONTENT_INSTALLED_BY_ID + "'/></tr>";
|
||||
html += "<td class='data' id='" + INSTALLED_CONTENT_INSTALLED_BY_ID + "'/></tr>";
|
||||
html += "</tbody></table>";
|
||||
$('#' + Settings.INSTALLED_CONTENT + ' .panel-body').html(html);
|
||||
}
|
||||
|
@ -379,7 +379,7 @@ $(document).ready(function(){
|
|||
$('#' + INSTALLED_CONTENT_FILENAME_ID).text(data.installed_content.filename);
|
||||
$('#' + INSTALLED_CONTENT_CREATED_ID).text(data.installed_content.creation_time ? moment(data.installed_content.creation_time).format('lll') : "");
|
||||
$('#' + INSTALLED_CONTENT_INSTALLED_ID).text(data.installed_content.install_time ? moment(data.installed_content.install_time).format('lll') : "");
|
||||
//$('#' + INSTALLED_CONTENT_INSTALLED_BY_ID).text(data.installed_content.installed_by);
|
||||
$('#' + INSTALLED_CONTENT_INSTALLED_BY_ID).text(data.installed_content.installed_by);
|
||||
|
||||
// update the progress bars for current restore status
|
||||
if (data.status.isRecovering) {
|
||||
|
|
|
@ -278,7 +278,7 @@ void AssetsBackupHandler::createBackup(const QString& backupName, QuaZip& zip) {
|
|||
_backups.emplace_back(backupName, mappings, false);
|
||||
}
|
||||
|
||||
std::pair<bool, QString> AssetsBackupHandler::recoverBackup(const QString& backupName, QuaZip& zip, const QString& sourceFilename) {
|
||||
std::pair<bool, QString> AssetsBackupHandler::recoverBackup(const QString& backupName, QuaZip& zip, const QString& username, const QString& sourceFilename) {
|
||||
Q_ASSERT(QThread::currentThread() == thread());
|
||||
|
||||
if (operationInProgress()) {
|
||||
|
|
|
@ -38,7 +38,7 @@ public:
|
|||
void loadBackup(const QString& backupName, QuaZip& zip) override;
|
||||
void loadingComplete() override;
|
||||
void createBackup(const QString& backupName, QuaZip& zip) override;
|
||||
std::pair<bool, QString> recoverBackup(const QString& backupName, QuaZip& zip, const QString& sourceFilename) override;
|
||||
std::pair<bool, QString> recoverBackup(const QString& backupName, QuaZip& zip, const QString& username, const QString& sourceFilename) override;
|
||||
void deleteBackup(const QString& backupName) override;
|
||||
void consolidateBackup(const QString& backupName, QuaZip& zip) override;
|
||||
bool isCorruptedBackup(const QString& backupName) override;
|
||||
|
|
|
@ -30,7 +30,7 @@ public:
|
|||
virtual void loadBackup(const QString& backupName, QuaZip& zip) = 0;
|
||||
virtual void loadingComplete() = 0;
|
||||
virtual void createBackup(const QString& backupName, QuaZip& zip) = 0;
|
||||
virtual std::pair<bool, QString> recoverBackup(const QString& backupName, QuaZip& zip, const QString& sourceFilename) = 0;
|
||||
virtual std::pair<bool, QString> recoverBackup(const QString& backupName, QuaZip& zip, const QString& username, const QString& sourceFilename) = 0;
|
||||
virtual void deleteBackup(const QString& backupName) = 0;
|
||||
virtual void consolidateBackup(const QString& backupName, QuaZip& zip) = 0;
|
||||
virtual bool isCorruptedBackup(const QString& backupName) = 0;
|
||||
|
|
|
@ -84,7 +84,7 @@ void ContentSettingsBackupHandler::createBackup(const QString& backupName, QuaZi
|
|||
}
|
||||
}
|
||||
|
||||
std::pair<bool, QString> ContentSettingsBackupHandler::recoverBackup(const QString& backupName, QuaZip& zip, const QString& sourceFilename) {
|
||||
std::pair<bool, QString> ContentSettingsBackupHandler::recoverBackup(const QString& backupName, QuaZip& zip, const QString& username, const QString& sourceFilename) {
|
||||
if (!zip.setCurrentFile(CONTENT_SETTINGS_BACKUP_FILENAME)) {
|
||||
QString errorStr("Failed to find " + CONTENT_SETTINGS_BACKUP_FILENAME + " while recovering backup");
|
||||
qWarning() << errorStr;
|
||||
|
@ -117,7 +117,7 @@ std::pair<bool, QString> ContentSettingsBackupHandler::recoverBackup(const QStri
|
|||
{ INSTALLED_CONTENT_NAME, archiveJson[INSTALLED_CONTENT_NAME].toString()},
|
||||
{ INSTALLED_CONTENT_CREATION_TIME, archiveJson[INSTALLED_CONTENT_CREATION_TIME].toVariant().toLongLong() },
|
||||
{ INSTALLED_CONTENT_INSTALL_TIME, QDateTime::currentDateTime().currentMSecsSinceEpoch() },
|
||||
{ INSTALLED_CONTENT_INSTALLED_BY, "" }
|
||||
{ INSTALLED_CONTENT_INSTALLED_BY, username }
|
||||
};
|
||||
|
||||
jsonObject.insert(INSTALLED_CONTENT, installed_content);
|
||||
|
|
|
@ -28,7 +28,7 @@ public:
|
|||
|
||||
void createBackup(const QString& backupName, QuaZip& zip) override;
|
||||
|
||||
std::pair<bool, QString> recoverBackup(const QString& backupName, QuaZip& zip, const QString& sourceFilename) override;
|
||||
std::pair<bool, QString> recoverBackup(const QString& backupName, QuaZip& zip, const QString& username, const QString& sourceFilename) override;
|
||||
|
||||
void deleteBackup(const QString& backupName) override {}
|
||||
|
||||
|
|
|
@ -279,7 +279,7 @@ void DomainContentBackupManager::deleteBackup(MiniPromise::Promise promise, cons
|
|||
});
|
||||
}
|
||||
|
||||
bool DomainContentBackupManager::recoverFromBackupZip(const QString& backupName, QuaZip& zip, const QString& sourceFilename, bool rollingBack) {
|
||||
bool DomainContentBackupManager::recoverFromBackupZip(const QString& backupName, QuaZip& zip, const QString& username, const QString& sourceFilename, bool rollingBack) {
|
||||
if (!zip.open(QuaZip::Mode::mdUnzip)) {
|
||||
qWarning() << "Failed to unzip file: " << backupName;
|
||||
return false;
|
||||
|
@ -290,7 +290,7 @@ bool DomainContentBackupManager::recoverFromBackupZip(const QString& backupName,
|
|||
for (auto& handler : _backupHandlers) {
|
||||
bool success;
|
||||
QString errorStr;
|
||||
std::tie(success, errorStr) = handler->recoverBackup(backupName, zip, sourceFilename);
|
||||
std::tie(success, errorStr) = handler->recoverBackup(backupName, zip, username, sourceFilename);
|
||||
if (!success) {
|
||||
if (!rollingBack) {
|
||||
_recoveryError = errorStr;
|
||||
|
@ -304,7 +304,7 @@ bool DomainContentBackupManager::recoverFromBackupZip(const QString& backupName,
|
|||
}
|
||||
}
|
||||
|
||||
void DomainContentBackupManager::recoverFromBackup(MiniPromise::Promise promise, const QString& backupName) {
|
||||
void DomainContentBackupManager::recoverFromBackup(MiniPromise::Promise promise, const QString& backupName, const QString& username) {
|
||||
if (_isRecovering) {
|
||||
promise->resolve({
|
||||
{ "success", false }
|
||||
|
@ -314,7 +314,7 @@ void DomainContentBackupManager::recoverFromBackup(MiniPromise::Promise promise,
|
|||
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "recoverFromBackup", Q_ARG(MiniPromise::Promise, promise),
|
||||
Q_ARG(const QString&, backupName));
|
||||
Q_ARG(const QString&, backupName), Q_ARG(const QString&, username));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -327,7 +327,7 @@ void DomainContentBackupManager::recoverFromBackup(MiniPromise::Promise promise,
|
|||
if (backupFile.open(QIODevice::ReadOnly)) {
|
||||
QuaZip zip { &backupFile };
|
||||
|
||||
success = recoverFromBackupZip(backupName, zip, backupName);
|
||||
success = recoverFromBackupZip(backupName, zip, username, backupName);
|
||||
|
||||
backupFile.close();
|
||||
} else {
|
||||
|
@ -340,11 +340,11 @@ void DomainContentBackupManager::recoverFromBackup(MiniPromise::Promise promise,
|
|||
});
|
||||
}
|
||||
|
||||
void DomainContentBackupManager::recoverFromUploadedBackup(MiniPromise::Promise promise, QByteArray uploadedBackup) {
|
||||
void DomainContentBackupManager::recoverFromUploadedBackup(MiniPromise::Promise promise, QByteArray uploadedBackup, QString username) {
|
||||
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "recoverFromUploadedBackup", Q_ARG(MiniPromise::Promise, promise),
|
||||
Q_ARG(QByteArray, uploadedBackup));
|
||||
Q_ARG(QByteArray, uploadedBackup), Q_ARG(QString, username));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -355,17 +355,17 @@ void DomainContentBackupManager::recoverFromUploadedBackup(MiniPromise::Promise
|
|||
QuaZip uploadedZip { &uploadedBackupBuffer };
|
||||
|
||||
QString backupName = MANUAL_BACKUP_PREFIX + "uploaded.zip";
|
||||
bool success = recoverFromBackupZip(backupName, uploadedZip, QString());
|
||||
bool success = recoverFromBackupZip(backupName, uploadedZip, username, QString());
|
||||
|
||||
promise->resolve({
|
||||
{ "success", success }
|
||||
});
|
||||
}
|
||||
|
||||
void DomainContentBackupManager::recoverFromUploadedFile(MiniPromise::Promise promise, QString uploadedFilename, QString sourceFilename) {
|
||||
void DomainContentBackupManager::recoverFromUploadedFile(MiniPromise::Promise promise, QString uploadedFilename, const QString username, QString sourceFilename) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "recoverFromUploadedFile", Q_ARG(MiniPromise::Promise, promise),
|
||||
Q_ARG(QString, uploadedFilename), Q_ARG(QString, sourceFilename));
|
||||
Q_ARG(QString, uploadedFilename), Q_ARG(QString, username), Q_ARG(QString, sourceFilename));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -382,7 +382,7 @@ void DomainContentBackupManager::recoverFromUploadedFile(MiniPromise::Promise pr
|
|||
|
||||
QString backupName = MANUAL_BACKUP_PREFIX + "uploaded.zip";
|
||||
|
||||
bool success = recoverFromBackupZip(backupName, uploadedZip, sourceFilename);
|
||||
bool success = recoverFromBackupZip(backupName, uploadedZip, username, sourceFilename);
|
||||
|
||||
if (!success) {
|
||||
|
||||
|
@ -394,7 +394,7 @@ void DomainContentBackupManager::recoverFromUploadedFile(MiniPromise::Promise pr
|
|||
QuaZip uploadedZip { &uploadedFile };
|
||||
|
||||
QString backupName = MANUAL_BACKUP_PREFIX + "uploaded.zip";
|
||||
recoverFromBackupZip(backupName, uploadedZip, sourceFilename, true);
|
||||
recoverFromBackupZip(backupName, uploadedZip, username, sourceFilename, true);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -95,9 +95,9 @@ public:
|
|||
public slots:
|
||||
void getAllBackupsAndStatus(MiniPromise::Promise promise);
|
||||
void createManualBackup(MiniPromise::Promise promise, const QString& name);
|
||||
void recoverFromBackup(MiniPromise::Promise promise, const QString& backupName);
|
||||
void recoverFromUploadedBackup(MiniPromise::Promise promise, QByteArray uploadedBackup);
|
||||
void recoverFromUploadedFile(MiniPromise::Promise promise, QString uploadedFilename, QString sourceFilename);
|
||||
void recoverFromBackup(MiniPromise::Promise promise, const QString& backupName, const QString& username);
|
||||
void recoverFromUploadedBackup(MiniPromise::Promise promise, QByteArray uploadedBackup, QString username);
|
||||
void recoverFromUploadedFile(MiniPromise::Promise promise, QString uploadedFilename, QString username, QString sourceFilename);
|
||||
void deleteBackup(MiniPromise::Promise promise, const QString& backupName);
|
||||
|
||||
signals:
|
||||
|
@ -119,7 +119,7 @@ protected:
|
|||
|
||||
std::pair<bool, QString> createBackup(const QString& prefix, const QString& name);
|
||||
|
||||
bool recoverFromBackupZip(const QString& backupName, QuaZip& backupZip, const QString& sourceFilename, bool rollingBack = false);
|
||||
bool recoverFromBackupZip(const QString& backupName, QuaZip& backupZip, const QString& username, const QString& sourceFilename, bool rollingBack = false);
|
||||
|
||||
private slots:
|
||||
void removeOldConsolidatedBackups();
|
||||
|
|
|
@ -1957,6 +1957,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
QPointer<HTTPConnection> connectionPtr { connection };
|
||||
|
||||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||
QString username;
|
||||
|
||||
auto getSetting = [this](QString keyPath, QVariant& value) -> bool {
|
||||
|
||||
|
@ -2024,7 +2025,9 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
}
|
||||
|
||||
// all requests below require a cookie to prove authentication so check that first
|
||||
if (!isAuthenticatedRequest(connection, url)) {
|
||||
bool isAuthenticated { false };
|
||||
std::tie(isAuthenticated, username) = isAuthenticatedRequest(connection);
|
||||
if (!isAuthenticated) {
|
||||
// this is not an authenticated request
|
||||
// return true from the handler since it was handled with a 401 or re-direct to auth
|
||||
return true;
|
||||
|
@ -2361,7 +2364,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
connectionPtr->respond(success ? HTTPConnection::StatusCode200 : HTTPConnection::StatusCode400, docJSON.toJson(),
|
||||
JSON_MIME_TYPE.toUtf8());
|
||||
});
|
||||
_contentManager->recoverFromBackup(deferred, id);
|
||||
_contentManager->recoverFromBackup(deferred, id, username);
|
||||
return true;
|
||||
}
|
||||
} else if (connection->requestOperation() == QNetworkAccessManager::PutOperation) {
|
||||
|
@ -2557,6 +2560,9 @@ bool DomainServer::processPendingContent(HTTPConnection* connection, QString ite
|
|||
int sessionId = sessionIdBytes.toInt();
|
||||
|
||||
bool newUpload = itemName == "restore-file" || itemName == "restore-file-chunk-initial" || itemName == "restore-file-chunk-only";
|
||||
bool isAuthenticated;
|
||||
QString username;
|
||||
std::tie(isAuthenticated, username) = isAuthenticatedRequest(connection);
|
||||
|
||||
if (filename.endsWith(".zip", Qt::CaseInsensitive)) {
|
||||
static const QString TEMPORARY_CONTENT_FILEPATH { QDir::tempPath() + "/hifiUploadContent_XXXXXX.zip" };
|
||||
|
@ -2591,7 +2597,7 @@ bool DomainServer::processPendingContent(HTTPConnection* connection, QString ite
|
|||
_pendingContentFiles.erase(sessionId);
|
||||
});
|
||||
|
||||
_contentManager->recoverFromUploadedFile(deferred, _pendingFileContent.fileName(), filename);
|
||||
_contentManager->recoverFromUploadedFile(deferred, _pendingFileContent.fileName(), username, filename);
|
||||
}
|
||||
} else if (filename.endsWith(".json", Qt::CaseInsensitive)
|
||||
|| filename.endsWith(".json.gz", Qt::CaseInsensitive)) {
|
||||
|
@ -2604,7 +2610,7 @@ bool DomainServer::processPendingContent(HTTPConnection* connection, QString ite
|
|||
|
||||
if (itemName == "restore-file" || itemName == "restore-file-chunk-final" || itemName == "restore-file-chunk-only") {
|
||||
// invoke our method to hand the new octree file off to the octree server
|
||||
if (!handleOctreeFileReplacement(_pendingUploadedContent, filename, QString())) {
|
||||
if (!handleOctreeFileReplacement(_pendingUploadedContent, filename, QString(), username)) {
|
||||
connection->respond(HTTPConnection::StatusCode400);
|
||||
return false;
|
||||
}
|
||||
|
@ -2680,7 +2686,7 @@ void DomainServer::profileRequestFinished() {
|
|||
}
|
||||
}
|
||||
|
||||
bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl& url) {
|
||||
std::pair<bool, QString> DomainServer::isAuthenticatedRequest(HTTPConnection* connection) {
|
||||
|
||||
static const QByteArray HTTP_COOKIE_HEADER_KEY = "Cookie";
|
||||
static const QString ADMIN_USERS_CONFIG_KEY = "admin-users";
|
||||
|
@ -2717,7 +2723,7 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl
|
|||
|
||||
if (_settingsManager.valueForKeyPath(ADMIN_USERS_CONFIG_KEY).toStringList().contains(profileUsername)) {
|
||||
// this is an authenticated user
|
||||
return true;
|
||||
return { true, profileUsername };
|
||||
}
|
||||
|
||||
// loop the roles of this user and see if they are in the admin-roles array
|
||||
|
@ -2727,7 +2733,7 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl
|
|||
foreach(const QString& userRole, sessionData.getRoles()) {
|
||||
if (adminRolesArray.contains(userRole)) {
|
||||
// this user has a role that allows them to administer the domain-server
|
||||
return true;
|
||||
return { true, profileUsername };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2735,7 +2741,7 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl
|
|||
connection->respond(HTTPConnection::StatusCode401, UNAUTHENTICATED_BODY);
|
||||
|
||||
// the user does not have allowed username or role, return 401
|
||||
return false;
|
||||
return { false, QString() };
|
||||
} else {
|
||||
static const QByteArray REQUESTED_WITH_HEADER = "X-Requested-With";
|
||||
static const QString XML_REQUESTED_WITH = "XMLHttpRequest";
|
||||
|
@ -2764,7 +2770,7 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl
|
|||
}
|
||||
|
||||
// we don't know about this user yet, so they are not yet authenticated
|
||||
return false;
|
||||
return { false, QString() };
|
||||
}
|
||||
} else if (_settingsManager.valueForKeyPath(BASIC_AUTH_USERNAME_KEY_PATH).isValid()) {
|
||||
// config file contains username and password combinations for basic auth
|
||||
|
@ -2793,7 +2799,7 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl
|
|||
"" : QCryptographicHash::hash(headerPassword.toUtf8(), QCryptographicHash::Sha256).toHex();
|
||||
|
||||
if (settingsUsername == headerUsername && hexHeaderPassword == settingsPassword) {
|
||||
return true;
|
||||
return { true, headerUsername };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2815,11 +2821,11 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl
|
|||
HTTPConnection::DefaultContentType, basicAuthHeader);
|
||||
|
||||
// not authenticated, bubble up false
|
||||
return false;
|
||||
return { false, QString() };
|
||||
|
||||
} else {
|
||||
// we don't have an OAuth URL + admin roles/usernames, so all users are authenticated
|
||||
return true;
|
||||
return { true, QString() };
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3493,7 +3499,7 @@ void DomainServer::maybeHandleReplacementEntityFile() {
|
|||
}
|
||||
}
|
||||
|
||||
bool DomainServer::handleOctreeFileReplacement(QByteArray octreeFile, QString sourceFilename, QString name) {
|
||||
bool DomainServer::handleOctreeFileReplacement(QByteArray octreeFile, QString sourceFilename, QString name, QString username) {
|
||||
OctreeUtils::RawEntityData data;
|
||||
if (data.readOctreeDataInfoFromData(octreeFile)) {
|
||||
data.resetIdAndVersion();
|
||||
|
@ -3514,7 +3520,7 @@ bool DomainServer::handleOctreeFileReplacement(QByteArray octreeFile, QString so
|
|||
{ INSTALLED_CONTENT_NAME, name },
|
||||
{ INSTALLED_CONTENT_CREATION_TIME, 0 },
|
||||
{ INSTALLED_CONTENT_INSTALL_TIME, QDateTime::currentDateTime().currentMSecsSinceEpoch() },
|
||||
{ INSTALLED_CONTENT_INSTALLED_BY, "" }
|
||||
{ INSTALLED_CONTENT_INSTALLED_BY, username }
|
||||
};
|
||||
|
||||
QJsonObject jsonObject { { INSTALLED_CONTENT, installed_content } };
|
||||
|
@ -3539,6 +3545,11 @@ void DomainServer::handleDomainContentReplacementFromURLRequest(QSharedPointer<R
|
|||
qInfo() << "Received request to replace content from a url";
|
||||
auto node = DependencyManager::get<LimitedNodeList>()->findNodeWithAddr(message->getSenderSockAddr());
|
||||
if (node && node->getCanReplaceContent()) {
|
||||
DomainServerNodeData* nodeData = static_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||
QString username;
|
||||
if (nodeData) {
|
||||
username = nodeData->getUsername();
|
||||
}
|
||||
// Convert message data into our URL
|
||||
QString url(message->getMessage());
|
||||
QUrl modelsURL = QUrl(url, QUrl::StrictMode);
|
||||
|
@ -3548,17 +3559,17 @@ void DomainServer::handleDomainContentReplacementFromURLRequest(QSharedPointer<R
|
|||
|
||||
qDebug() << "Downloading JSON from: " << modelsURL.toString(QUrl::FullyEncoded);
|
||||
|
||||
connect(reply, &QNetworkReply::finished, [this, reply, modelsURL]() {
|
||||
connect(reply, &QNetworkReply::finished, [this, reply, modelsURL, username]() {
|
||||
QNetworkReply::NetworkError networkError = reply->error();
|
||||
if (networkError == QNetworkReply::NoError) {
|
||||
if (modelsURL.fileName().endsWith(".json.gz")) {
|
||||
QUrlQuery urlQuery(modelsURL.query(QUrl::FullyEncoded));
|
||||
|
||||
QString itemName = urlQuery.queryItemValue(CONTENT_SET_NAME_QUERY_PARAM);
|
||||
handleOctreeFileReplacement(reply->readAll(), modelsURL.fileName(), itemName);
|
||||
handleOctreeFileReplacement(reply->readAll(), modelsURL.fileName(), itemName, username);
|
||||
} else if (modelsURL.fileName().endsWith(".zip")) {
|
||||
auto deferred = makePromise("recoverFromUploadedBackup");
|
||||
_contentManager->recoverFromUploadedBackup(deferred, reply->readAll());
|
||||
_contentManager->recoverFromUploadedBackup(deferred, reply->readAll(), username);
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Error downloading JSON from specified file: " << modelsURL;
|
||||
|
@ -3569,7 +3580,12 @@ void DomainServer::handleDomainContentReplacementFromURLRequest(QSharedPointer<R
|
|||
|
||||
void DomainServer::handleOctreeFileReplacementRequest(QSharedPointer<ReceivedMessage> message) {
|
||||
auto node = DependencyManager::get<NodeList>()->nodeWithLocalID(message->getSourceID());
|
||||
if (node->getCanReplaceContent()) {
|
||||
handleOctreeFileReplacement(message->readAll(), QString(), QString());
|
||||
if (node && node->getCanReplaceContent()) {
|
||||
QString username;
|
||||
DomainServerNodeData* nodeData = static_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||
if (nodeData) {
|
||||
username = nodeData->getUsername();
|
||||
}
|
||||
handleOctreeFileReplacement(message->readAll(), QString(), QString(), username);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -99,7 +99,7 @@ private slots:
|
|||
|
||||
void handleDomainContentReplacementFromURLRequest(QSharedPointer<ReceivedMessage> message);
|
||||
void handleOctreeFileReplacementRequest(QSharedPointer<ReceivedMessage> message);
|
||||
bool handleOctreeFileReplacement(QByteArray octreeFile, QString sourceFilename, QString name);
|
||||
bool handleOctreeFileReplacement(QByteArray octreeFile, QString sourceFilename, QString name, QString username);
|
||||
|
||||
void processOctreeDataRequestMessage(QSharedPointer<ReceivedMessage> message);
|
||||
void processOctreeDataPersistMessage(QSharedPointer<ReceivedMessage> message);
|
||||
|
@ -194,7 +194,7 @@ private:
|
|||
QUrl oauthRedirectURL();
|
||||
QUrl oauthAuthorizationURL(const QUuid& stateUUID = QUuid::createUuid());
|
||||
|
||||
bool isAuthenticatedRequest(HTTPConnection* connection, const QUrl& url);
|
||||
std::pair<bool, QString> isAuthenticatedRequest(HTTPConnection* connection);
|
||||
|
||||
QNetworkReply* profileRequestGivenTokenReply(QNetworkReply* tokenReply);
|
||||
Headers setupCookieHeadersFromProfileReply(QNetworkReply* profileReply);
|
||||
|
|
|
@ -57,7 +57,7 @@ void EntitiesBackupHandler::createBackup(const QString& backupName, QuaZip& zip)
|
|||
}
|
||||
}
|
||||
|
||||
std::pair<bool, QString> EntitiesBackupHandler::recoverBackup(const QString& backupName, QuaZip& zip, const QString& sourceFilename) {
|
||||
std::pair<bool, QString> EntitiesBackupHandler::recoverBackup(const QString& backupName, QuaZip& zip, const QString& username, const QString& sourceFilename) {
|
||||
if (!zip.setCurrentFile(ENTITIES_BACKUP_FILENAME)) {
|
||||
QString errorStr("Failed to find " + ENTITIES_BACKUP_FILENAME + " while recovering backup");
|
||||
qWarning() << errorStr;
|
||||
|
|
|
@ -29,7 +29,7 @@ public:
|
|||
void createBackup(const QString& backupName, QuaZip& zip) override;
|
||||
|
||||
// Recover from a full backup
|
||||
std::pair<bool, QString> recoverBackup(const QString& backupName, QuaZip& zip, const QString& sourceFilename) override;
|
||||
std::pair<bool, QString> recoverBackup(const QString& backupName, QuaZip& zip, const QString& username, const QString& sourceFilename) override;
|
||||
|
||||
// Delete a skeleton backup
|
||||
void deleteBackup(const QString& backupName) override {}
|
||||
|
|
|
@ -94,6 +94,10 @@ ANDROID_PACKAGES = {
|
|||
'checksum': 'ddcb23df336b08017042ba4786db1d9e',
|
||||
'sharedLibFolder': 'lib',
|
||||
'includeLibs': {'libbreakpad_client.a'}
|
||||
},
|
||||
'webrtc': {
|
||||
'file': 'webrtc-20190626-android.tar.gz',
|
||||
'checksum': 'e2dccd3d8efdcba6d428c87ba7fb2a53'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Binary file not shown.
BIN
interface/resources/avatar/animations/emote_agree_headnod.fbx
Normal file
BIN
interface/resources/avatar/animations/emote_agree_headnod.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/emote_agree_headnodyes.fbx
Normal file
BIN
interface/resources/avatar/animations/emote_agree_headnodyes.fbx
Normal file
Binary file not shown.
Binary file not shown.
BIN
interface/resources/avatar/animations/emote_agree_thisorthat.fbx
Normal file
BIN
interface/resources/avatar/animations/emote_agree_thisorthat.fbx
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
interface/resources/avatar/animations/emote_clap02_all.fbx
Normal file
BIN
interface/resources/avatar/animations/emote_clap02_all.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/emote_clap03_all.fbx
Normal file
BIN
interface/resources/avatar/animations/emote_clap03_all.fbx
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
interface/resources/avatar/animations/emote_disagree_no.fbx
Normal file
BIN
interface/resources/avatar/animations/emote_disagree_no.fbx
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
interface/resources/avatar/animations/emote_raisehand03_all.fbx
Normal file
BIN
interface/resources/avatar/animations/emote_raisehand03_all.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/emote_raisehand04_all.fbx
Normal file
BIN
interface/resources/avatar/animations/emote_raisehand04_all.fbx
Normal file
Binary file not shown.
Binary file not shown.
BIN
interface/resources/avatar/animations/sitting_idle03.fbx
Normal file
BIN
interface/resources/avatar/animations/sitting_idle03.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/sitting_talk02.fbx
Normal file
BIN
interface/resources/avatar/animations/sitting_talk02.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/sitting_talk03.fbx
Normal file
BIN
interface/resources/avatar/animations/sitting_talk03.fbx
Normal file
Binary file not shown.
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -166,16 +166,16 @@ Rectangle {
|
|||
x: 2 * margins.paddings;
|
||||
width: parent.width;
|
||||
// switch heights + 2 * top margins
|
||||
height: (root.switchHeight) * 3 + 48;
|
||||
height: (root.switchHeight) * 6 + 48;
|
||||
anchors.top: firstSeparator.bottom;
|
||||
anchors.topMargin: 10;
|
||||
|
||||
// mute is in its own row
|
||||
Item {
|
||||
id: switchContainer;
|
||||
x: margins.paddings;
|
||||
width: parent.width / 2;
|
||||
height: parent.height;
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left;
|
||||
HifiControlsUit.Switch {
|
||||
id: muteMic;
|
||||
|
@ -222,12 +222,29 @@ Rectangle {
|
|||
}
|
||||
|
||||
HifiControlsUit.Switch {
|
||||
id: pttSwitch
|
||||
id: acousticEchoCancellationSwitch;
|
||||
height: root.switchHeight;
|
||||
switchWidth: root.switchWidth;
|
||||
anchors.top: noiseReductionSwitch.bottom
|
||||
anchors.topMargin: 24
|
||||
anchors.left: parent.left
|
||||
labelTextOn: "Echo Cancellation";
|
||||
labelTextSize: 16;
|
||||
backgroundOnColor: "#E3E3E3";
|
||||
checked: AudioScriptingInterface.acousticEchoCancellation;
|
||||
onCheckedChanged: {
|
||||
AudioScriptingInterface.acousticEchoCancellation = checked;
|
||||
checked = Qt.binding(function() { return AudioScriptingInterface.acousticEchoCancellation; });
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.Switch {
|
||||
id: pttSwitch
|
||||
height: root.switchHeight;
|
||||
switchWidth: root.switchWidth;
|
||||
anchors.top: acousticEchoCancellationSwitch.bottom;
|
||||
anchors.topMargin: 24
|
||||
anchors.left: parent.left
|
||||
labelTextOn: (bar.currentIndex === 0) ? qsTr("Push To Talk (T)") : qsTr("Push To Talk");
|
||||
labelTextSize: 16;
|
||||
backgroundOnColor: "#E3E3E3";
|
||||
|
@ -298,7 +315,6 @@ Rectangle {
|
|||
checked = Qt.binding(function() { return AudioScriptingInterface.isStereoInput; }); // restore binding
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -184,7 +184,7 @@ Flickable {
|
|||
wrapMode: Text.Wrap
|
||||
|
||||
Component.onCompleted: {
|
||||
var gpu = JSON.parse(PlatformInfo.getGPU(0));
|
||||
var gpu = JSON.parse(PlatformInfo.getGPU(PlatformInfo.getMasterGPU()));
|
||||
var gpuModel = gpu.model;
|
||||
if (gpuModel.length === 0) {
|
||||
gpuModel = "Unknown";
|
||||
|
@ -313,7 +313,7 @@ Flickable {
|
|||
textToCopy += "# CPU Cores: " + PlatformInfo.getNumLogicalCores() + "\n";
|
||||
textToCopy += "RAM: " + PlatformInfo.getTotalSystemMemoryMB() + " MB\n";
|
||||
|
||||
var gpu = JSON.parse(PlatformInfo.getGPU(0));
|
||||
var gpu = JSON.parse(PlatformInfo.getGPU(PlatformInfo.getMasterGPU()));
|
||||
var gpuModel = gpu.model;
|
||||
if (gpuModel.length === 0) {
|
||||
gpuModel = "Unknown";
|
||||
|
|
|
@ -222,6 +222,17 @@ Flickable {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
SimplifiedControls.Switch {
|
||||
id: acousticEchoCancellationSwitch
|
||||
Layout.preferredHeight: 18
|
||||
Layout.preferredWidth: parent.width
|
||||
labelTextOn: "Acoustic Echo Cancellation"
|
||||
checked: AudioScriptingInterface.acousticEchoCancellation
|
||||
onClicked: {
|
||||
AudioScriptingInterface.acousticEchoCancellation = !AudioScriptingInterface.acousticEchoCancellation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1659,7 +1659,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
// The value will be 0 if the user blew away settings this session, which is both a feature and a bug.
|
||||
static const QString TESTER = "HIFI_TESTER";
|
||||
auto gpuIdent = GPUIdent::getInstance();
|
||||
auto glContextData = getGLContextData();
|
||||
auto glContextData = gl::ContextInfo::get();
|
||||
QJsonObject properties = {
|
||||
{ "version", applicationVersion() },
|
||||
{ "tester", QProcessEnvironment::systemEnvironment().contains(TESTER) || isTester },
|
||||
|
@ -1676,11 +1676,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
{ "gpu_name", gpuIdent->getName() },
|
||||
{ "gpu_driver", gpuIdent->getDriver() },
|
||||
{ "gpu_memory", static_cast<qint64>(gpuIdent->getMemory()) },
|
||||
{ "gl_version_int", glVersionToInteger(glContextData.value("version").toString()) },
|
||||
{ "gl_version", glContextData["version"] },
|
||||
{ "gl_vender", glContextData["vendor"] },
|
||||
{ "gl_sl_version", glContextData["sl_version"] },
|
||||
{ "gl_renderer", glContextData["renderer"] },
|
||||
{ "gl_version_int", glVersionToInteger(glContextData.version.c_str()) },
|
||||
{ "gl_version", glContextData.version.c_str() },
|
||||
{ "gl_vender", glContextData.vendor.c_str() },
|
||||
{ "gl_sl_version", glContextData.shadingLanguageVersion.c_str() },
|
||||
{ "gl_renderer", glContextData.renderer.c_str() },
|
||||
{ "ideal_thread_count", QThread::idealThreadCount() }
|
||||
};
|
||||
auto macVersion = QSysInfo::macVersion();
|
||||
|
@ -2282,8 +2282,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
properties["active_display_plugin"] = getActiveDisplayPlugin()->getName();
|
||||
properties["using_hmd"] = isHMDMode();
|
||||
|
||||
auto glInfo = getGLContextData();
|
||||
properties["gl_info"] = glInfo;
|
||||
auto contextInfo = gl::ContextInfo::get();
|
||||
properties["gl_info"] = QJsonObject{
|
||||
{ "version", contextInfo.version.c_str() },
|
||||
{ "sl_version", contextInfo.shadingLanguageVersion.c_str() },
|
||||
{ "vendor", contextInfo.vendor.c_str() },
|
||||
{ "renderer", contextInfo.renderer.c_str() },
|
||||
};
|
||||
properties["gpu_used_memory"] = (int)BYTES_TO_MB(gpu::Context::getUsedGPUMemSize());
|
||||
properties["gpu_free_memory"] = (int)BYTES_TO_MB(gpu::Context::getFreeGPUMemSize());
|
||||
properties["gpu_frame_time"] = (float)(qApp->getGPUContext()->getFrameTimerGPUAverage());
|
||||
|
@ -2996,6 +3001,9 @@ void Application::initializeGL() {
|
|||
qCWarning(interfaceapp, "Unable to make window context current");
|
||||
}
|
||||
|
||||
// Populate the global OpenGL context based on the information for the primary window GL context
|
||||
gl::ContextInfo::get(true);
|
||||
|
||||
#if !defined(DISABLE_QML)
|
||||
QStringList chromiumFlags;
|
||||
// HACK: re-expose mic and camera to prevent crash on domain-change in chromium's media::FakeAudioInputStream::ReadAudioFromSource()
|
||||
|
|
|
@ -125,16 +125,25 @@ QString userRecenterModelToString(MyAvatar::SitStandModelType model) {
|
|||
}
|
||||
}
|
||||
|
||||
static const QStringList REACTION_NAMES = {
|
||||
static const QStringList TRIGGER_REACTION_NAMES = {
|
||||
QString("positive"),
|
||||
QString("negative"),
|
||||
QString("negative")
|
||||
};
|
||||
|
||||
static const QStringList BEGIN_END_REACTION_NAMES = {
|
||||
QString("raiseHand"),
|
||||
QString("applaud"),
|
||||
QString("point")
|
||||
};
|
||||
|
||||
static int reactionNameToIndex(const QString& reactionName) {
|
||||
return REACTION_NAMES.indexOf(reactionName);
|
||||
static int triggerReactionNameToIndex(const QString& reactionName) {
|
||||
assert(NUM_AVATAR_TRIGGER_REACTIONS == TRIGGER_REACTION_NAMES.size());
|
||||
return TRIGGER_REACTION_NAMES.indexOf(reactionName);
|
||||
}
|
||||
|
||||
static int beginEndReactionNameToIndex(const QString& reactionName) {
|
||||
assert(NUM_AVATAR_BEGIN_END_REACTIONS == TRIGGER_REACTION_NAMES.size());
|
||||
return BEGIN_END_REACTION_NAMES.indexOf(reactionName);
|
||||
}
|
||||
|
||||
MyAvatar::MyAvatar(QThread* thread) :
|
||||
|
@ -5824,13 +5833,17 @@ void MyAvatar::setModelScale(float scale) {
|
|||
}
|
||||
}
|
||||
|
||||
QStringList MyAvatar::getReactions() const {
|
||||
return REACTION_NAMES;
|
||||
QStringList MyAvatar::getBeginEndReactions() const {
|
||||
return BEGIN_END_REACTION_NAMES;
|
||||
}
|
||||
|
||||
QStringList MyAvatar::getTriggerReactions() const {
|
||||
return TRIGGER_REACTION_NAMES;
|
||||
}
|
||||
|
||||
bool MyAvatar::triggerReaction(QString reactionName) {
|
||||
int reactionIndex = reactionNameToIndex(reactionName);
|
||||
if (reactionIndex >= 0 && reactionIndex < (int)NUM_AVATAR_REACTIONS) {
|
||||
int reactionIndex = triggerReactionNameToIndex(reactionName);
|
||||
if (reactionIndex >= 0 && reactionIndex < (int)NUM_AVATAR_TRIGGER_REACTIONS) {
|
||||
std::lock_guard<std::mutex> guard(_reactionLock);
|
||||
_reactionTriggers[reactionIndex] = true;
|
||||
return true;
|
||||
|
@ -5839,8 +5852,8 @@ bool MyAvatar::triggerReaction(QString reactionName) {
|
|||
}
|
||||
|
||||
bool MyAvatar::beginReaction(QString reactionName) {
|
||||
int reactionIndex = reactionNameToIndex(reactionName);
|
||||
if (reactionIndex >= 0 && reactionIndex < (int)NUM_AVATAR_REACTIONS) {
|
||||
int reactionIndex = beginEndReactionNameToIndex(reactionName);
|
||||
if (reactionIndex >= 0 && reactionIndex < (int)NUM_AVATAR_BEGIN_END_REACTIONS) {
|
||||
std::lock_guard<std::mutex> guard(_reactionLock);
|
||||
_reactionEnabledRefCounts[reactionIndex]++;
|
||||
return true;
|
||||
|
@ -5849,8 +5862,8 @@ bool MyAvatar::beginReaction(QString reactionName) {
|
|||
}
|
||||
|
||||
bool MyAvatar::endReaction(QString reactionName) {
|
||||
int reactionIndex = reactionNameToIndex(reactionName);
|
||||
if (reactionIndex >= 0 && reactionIndex < (int)NUM_AVATAR_REACTIONS) {
|
||||
int reactionIndex = beginEndReactionNameToIndex(reactionName);
|
||||
if (reactionIndex >= 0 && reactionIndex < (int)NUM_AVATAR_BEGIN_END_REACTIONS) {
|
||||
std::lock_guard<std::mutex> guard(_reactionLock);
|
||||
_reactionEnabledRefCounts[reactionIndex]--;
|
||||
return true;
|
||||
|
@ -5860,12 +5873,17 @@ bool MyAvatar::endReaction(QString reactionName) {
|
|||
|
||||
void MyAvatar::updateRigControllerParameters(Rig::ControllerParameters& params) {
|
||||
std::lock_guard<std::mutex> guard(_reactionLock);
|
||||
for (int i = 0; i < NUM_AVATAR_REACTIONS; i++) {
|
||||
|
||||
for (int i = 0; i < TRIGGER_REACTION_NAMES.size(); i++) {
|
||||
params.reactionTriggers[i] = _reactionTriggers[i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < BEGIN_END_REACTION_NAMES.size(); i++) {
|
||||
// copy current state into params.
|
||||
params.reactionEnabledFlags[i] = _reactionEnabledRefCounts[i] > 0;
|
||||
params.reactionTriggers[i] = _reactionTriggers[i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < TRIGGER_REACTION_NAMES.size(); i++) {
|
||||
// clear reaction triggers here as well
|
||||
_reactionTriggers[i] = false;
|
||||
}
|
||||
|
|
|
@ -609,8 +609,10 @@ public:
|
|||
* the avatar will move in unpredictable ways. For more information about avatar joint orientation standards, see
|
||||
* <a href="https://docs.highfidelity.com/create/avatars/avatar-standards">Avatar Standards</a>.</p>
|
||||
* @function MyAvatar.overrideAnimation
|
||||
* @param {string} url - 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 {string} url - The URL to the animation file. Animation files may be in glTF or FBX format, but only need to
|
||||
* contain the avatar skeleton and animation data. glTF models may be in JSON or binary format (".gltf" or ".glb" URLs
|
||||
* respectively).
|
||||
* <p><strong>Warning:</strong> glTF animations currently do not always animate correctly.</p>
|
||||
* @param {number} fps - The frames per second (FPS) rate for the animation playback. 30 FPS is normal speed.
|
||||
* @param {boolean} loop - <code>true</code> if the animation should loop, <code>false</code> if it shouldn't.
|
||||
* @param {number} firstFrame - The frame to start the animation at.
|
||||
|
@ -630,8 +632,10 @@ public:
|
|||
* Use {@link MyAvatar.restoreHandAnimation} to restore the default poses.
|
||||
* @function MyAvatar.overrideHandAnimation
|
||||
* @param isLeft {boolean} <code>true</code> to override the left hand, <code>false</code> to override the right hand.
|
||||
* @param {string} url - The URL of the animation file. Animation files need to be FBX format, but only need to contain the
|
||||
* avatar skeleton and animation data.
|
||||
* @param {string} url - The URL of the animation file. Animation files need to be in glTF or FBX format, but only need to
|
||||
* contain the avatar skeleton and animation data. glTF models may be in JSON or binary format (".gltf" or ".glb" URLs
|
||||
* respectively).
|
||||
* <p><strong>Warning:</strong> glTF animations currently do not always animate correctly.</p>
|
||||
* @param {number} fps - The frames per second (FPS) rate for the animation playback. 30 FPS is normal speed.
|
||||
* @param {boolean} loop - <code>true</code> if the animation should loop, <code>false</code> if it shouldn't.
|
||||
* @param {number} firstFrame - The frame to start the animation at.
|
||||
|
@ -702,19 +706,22 @@ public:
|
|||
* <p>Each avatar has an avatar-animation.json file that defines a set of animation roles. Animation roles map to easily
|
||||
* understandable actions that the avatar can perform, such as <code>"idleStand"</code>, <code>"idleTalk"</code>, or
|
||||
* <code>"walkFwd"</code>. To get the full list of roles, use {@ link MyAvatar.getAnimationRoles}.
|
||||
* For each role, the avatar-animation.json defines when the animation is used, the animation clip (FBX) used, and how
|
||||
* animations are blended together with procedural data (such as look at vectors, hand sensors etc.).
|
||||
* <code>overrideRoleAnimation()</code> is used to change the animation clip (FBX) associated with a specified animation
|
||||
* role. To end the role animation and restore the default, use {@link MyAvatar.restoreRoleAnimation}.</p>
|
||||
* <p>Note: Hand roles only affect the hand. Other 'main' roles, like 'idleStand', 'idleTalk', 'takeoffStand' are full body.</p>
|
||||
* For each role, the avatar-animation.json defines when the animation is used, the animation clip (glTF or FBX) used, and
|
||||
* how animations are blended together with procedural data (such as look at vectors, hand sensors etc.).
|
||||
* <code>overrideRoleAnimation()</code> is used to change the animation clip (glTF or FBX) associated with a specified
|
||||
* animation role. To end the role animation and restore the default, use {@link MyAvatar.restoreRoleAnimation}.</p>
|
||||
* <p>Note: Hand roles only affect the hand. Other "main" roles, like "idleStand", "idleTalk", and "takeoffStand", are full
|
||||
* body.</p>
|
||||
* <p>Note: When using pre-built animation data, it's critical that the joint orientation of the source animation and target
|
||||
* rig are equivalent, since the animation data applies absolute values onto the joints. If the orientations are different,
|
||||
* the avatar will move in unpredictable ways. For more information about avatar joint orientation standards, see
|
||||
* <a href="https://docs.highfidelity.com/create/avatars/avatar-standards">Avatar Standards</a>.
|
||||
* @function MyAvatar.overrideRoleAnimation
|
||||
* @param {string} role - The animation role to override
|
||||
* @param {string} url - The URL to the animation file. Animation files need to be in FBX format, but only need to contain
|
||||
* the avatar skeleton and animation data.
|
||||
* @param {string} url - The URL to the animation file. Animation files need to be in glTF or FBX format, but only need to
|
||||
* contain the avatar skeleton and animation data. glTF models may be in JSON or binary format (".gltf" or ".glb" URLs
|
||||
* respectively).
|
||||
* <p><strong>Warning:</strong> glTF animations currently do not always animate correctly.</p>
|
||||
* @param {number} fps - The frames per second (FPS) rate for the animation playback. 30 FPS is normal speed.
|
||||
* @param {boolean} loop - <code>true</code> if the animation should loop, <code>false</code> if it shouldn't.
|
||||
* @param {number} firstFrame - The frame the animation should start at.
|
||||
|
@ -739,9 +746,9 @@ public:
|
|||
* <p>Each avatar has an avatar-animation.json file that defines a set of animation roles. Animation roles map to easily
|
||||
* understandable actions that the avatar can perform, such as <code>"idleStand"</code>, <code>"idleTalk"</code>, or
|
||||
* <code>"walkFwd"</code>. To get the full list of roles, use {@link MyAvatar.getAnimationRoles}. For each role,
|
||||
* the avatar-animation.json defines when the animation is used, the animation clip (FBX) used, and how animations are
|
||||
* blended together with procedural data (such as look-at vectors, hand sensors etc.). You can change the animation clip
|
||||
* (FBX) associated with a specified animation role using {@link MyAvatar.overrideRoleAnimation}.
|
||||
* the avatar-animation.json defines when the animation is used, the animation clip (glTF or FBX) used, and how animations
|
||||
* are blended together with procedural data (such as look-at vectors, hand sensors etc.). You can change the animation
|
||||
* clip (glTF or FBX) associated with a specified animation role using {@link MyAvatar.overrideRoleAnimation}.
|
||||
* <code>restoreRoleAnimation()</code> is used to restore a specified animation role's default animation clip. If you have
|
||||
* not specified an override animation for the specified role, this function has no effect.
|
||||
* @function MyAvatar.restoreRoleAnimation
|
||||
|
@ -2042,17 +2049,19 @@ public slots:
|
|||
void setEnableDebugDrawDefaultPose(bool isEnabled);
|
||||
|
||||
/**jsdoc
|
||||
* Displays animation debug graphics. By default it shows the animation poses used for rendering.
|
||||
* However, the property MyAvatar.setDebugDrawAnimPoseName can be used to draw a specific animation node.
|
||||
* Displays animation debug graphics. By default, the animation poses used for rendering are displayed. However,
|
||||
* {@link MyAvatar.setDebugDrawAnimPoseName} can be used to set a specific animation node to display.
|
||||
* @function MyAvatar.setEnableDebugDrawAnimPose
|
||||
* @param {boolean} enabled - <code>true</code> to show the debug graphics, <code>false</code> to hide.
|
||||
*/
|
||||
void setEnableDebugDrawAnimPose(bool isEnabled);
|
||||
|
||||
/**jsdoc
|
||||
* If set it determines which animation debug graphics to draw, when MyAvatar.setEnableDebugDrawAnimPose is set to true.
|
||||
* Sets the animation node to display when animation debug graphics are enabled with
|
||||
* {@link MyAvatar.setEnableDebugDrawAnimPose}.
|
||||
* @function MyAvatar.setDebugDrawAnimPoseName
|
||||
* @param {boolean} enabled - <code>true</code> to show the debug graphics, <code>false</code> to hide.
|
||||
* @param {string} poseName - The name of the animation node to display debug graphics for. Use <code>""</code> to reset to
|
||||
* default.
|
||||
*/
|
||||
void setDebugDrawAnimPoseName(QString poseName);
|
||||
|
||||
|
@ -2213,13 +2222,23 @@ public slots:
|
|||
virtual void setModelScale(float scale) override;
|
||||
|
||||
/**jsdoc
|
||||
* MyAvatar.getReactions
|
||||
* MyAvatar.getTriggerReactions
|
||||
* Returns a list of reactions names that can be triggered using MyAvatar.triggerReaction().
|
||||
* @returns {string[]} Array of reaction names.
|
||||
*/
|
||||
QStringList getReactions() const;
|
||||
QStringList getTriggerReactions() const;
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* MyAvatar.getBeginReactions
|
||||
* Returns a list of reactions names that can be enabled using MyAvatar.beginReaction() and MyAvatar.endReaction().
|
||||
* @returns {string[]} Array of reaction names.
|
||||
*/
|
||||
QStringList getBeginEndReactions() const;
|
||||
|
||||
/**jsdoc
|
||||
* MyAvatar.triggerReaction
|
||||
* Plays the given reaction on the avatar, once the reaction is complete it will automatically complete. Only reaction names returned from MyAvatar.getTriggerReactions() are available.
|
||||
* @param {string} reactionName - reaction name
|
||||
* @returns {bool} false if the given reaction is not supported.
|
||||
*/
|
||||
|
@ -2227,6 +2246,9 @@ public slots:
|
|||
|
||||
/**jsdoc
|
||||
* MyAvatar.beginReaction
|
||||
* Plays the given reaction on the avatar. The avatar will continue to play the reaction until stopped via the MyAvatar.endReaction() call or superseeded by another reaction.
|
||||
* Only reaction names returned from MyAvatar.getBeginEndReactions() are available.
|
||||
* NOTE: the caller is responsible for calling the corresponding MyAvatar.endReaction(), otherwise the avatar might become stuck in the reaction forever.
|
||||
* @param {string} reactionName - reaction name
|
||||
* @returns {bool} false if the given reaction is not supported.
|
||||
*/
|
||||
|
@ -2234,6 +2256,7 @@ public slots:
|
|||
|
||||
/**jsdoc
|
||||
* MyAvatar.endReaction
|
||||
* Used to stop a given reaction that was started via MyAvatar.beginReaction().
|
||||
* @param {string} reactionName - reaction name
|
||||
* @returns {bool} false if the given reaction is not supported.
|
||||
*/
|
||||
|
@ -2872,8 +2895,9 @@ private:
|
|||
QScriptEngine* _scriptEngine { nullptr };
|
||||
bool _needToSaveAvatarEntitySettings { false };
|
||||
|
||||
int _reactionEnabledRefCounts[NUM_AVATAR_REACTIONS] { 0, 0, 0, 0, 0 };
|
||||
bool _reactionTriggers[NUM_AVATAR_REACTIONS] { false, false, false, false, false };
|
||||
bool _reactionTriggers[NUM_AVATAR_TRIGGER_REACTIONS] { false, false };
|
||||
int _reactionEnabledRefCounts[NUM_AVATAR_BEGIN_END_REACTIONS] { 0, 0, 0 };
|
||||
|
||||
mutable std::mutex _reactionLock;
|
||||
};
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ QString Audio::HMD { "VR" };
|
|||
|
||||
Setting::Handle<bool> enableNoiseReductionSetting { QStringList { Audio::AUDIO, "NoiseReduction" }, true };
|
||||
Setting::Handle<bool> enableWarnWhenMutedSetting { QStringList { Audio::AUDIO, "WarnWhenMuted" }, true };
|
||||
Setting::Handle<bool> enableAcousticEchoCancellationSetting { QStringList { Audio::AUDIO, "AcousticEchoCancellation" }, true };
|
||||
|
||||
|
||||
float Audio::loudnessToLevel(float loudness) {
|
||||
|
@ -40,12 +41,14 @@ Audio::Audio() : _devices(_contextIsHMD) {
|
|||
connect(client, &AudioClient::muteToggled, this, &Audio::setMuted);
|
||||
connect(client, &AudioClient::noiseReductionChanged, this, &Audio::enableNoiseReduction);
|
||||
connect(client, &AudioClient::warnWhenMutedChanged, this, &Audio::enableWarnWhenMuted);
|
||||
connect(client, &AudioClient::acousticEchoCancellationChanged, this, &Audio::enableAcousticEchoCancellation);
|
||||
connect(client, &AudioClient::inputLoudnessChanged, this, &Audio::onInputLoudnessChanged);
|
||||
connect(client, &AudioClient::inputVolumeChanged, this, &Audio::setInputVolume);
|
||||
connect(this, &Audio::contextChanged, &_devices, &AudioDevices::onContextChanged);
|
||||
connect(this, &Audio::pushingToTalkChanged, this, &Audio::handlePushedToTalk);
|
||||
enableNoiseReduction(enableNoiseReductionSetting.get());
|
||||
enableWarnWhenMuted(enableWarnWhenMutedSetting.get());
|
||||
enableAcousticEchoCancellation(enableAcousticEchoCancellationSetting.get());
|
||||
onContextChanged();
|
||||
}
|
||||
|
||||
|
@ -210,6 +213,11 @@ void Audio::setPTTHMD(bool enabled) {
|
|||
}
|
||||
|
||||
void Audio::saveData() {
|
||||
_avatarGainSetting.set(getAvatarGain());
|
||||
_injectorGainSetting.set(getInjectorGain());
|
||||
_localInjectorGainSetting.set(getLocalInjectorGain());
|
||||
_systemInjectorGainSetting.set(getSystemInjectorGain());
|
||||
|
||||
_mutedDesktopSetting.set(getMutedDesktop());
|
||||
_mutedHMDSetting.set(getMutedHMD());
|
||||
_pttDesktopSetting.set(getPTTDesktop());
|
||||
|
@ -217,6 +225,11 @@ void Audio::saveData() {
|
|||
}
|
||||
|
||||
void Audio::loadData() {
|
||||
setAvatarGain(_avatarGainSetting.get());
|
||||
setInjectorGain(_injectorGainSetting.get());
|
||||
setLocalInjectorGain(_localInjectorGainSetting.get());
|
||||
setSystemInjectorGain(_systemInjectorGainSetting.get());
|
||||
|
||||
setMutedDesktop(_mutedDesktopSetting.get());
|
||||
setMutedHMD(_mutedHMDSetting.get());
|
||||
setPTTDesktop(_pttDesktopSetting.get());
|
||||
|
@ -277,6 +290,28 @@ void Audio::enableWarnWhenMuted(bool enable) {
|
|||
}
|
||||
}
|
||||
|
||||
bool Audio::acousticEchoCancellationEnabled() const {
|
||||
return resultWithReadLock<bool>([&] {
|
||||
return _enableAcousticEchoCancellation;
|
||||
});
|
||||
}
|
||||
|
||||
void Audio::enableAcousticEchoCancellation(bool enable) {
|
||||
bool changed = false;
|
||||
withWriteLock([&] {
|
||||
if (_enableAcousticEchoCancellation != enable) {
|
||||
_enableAcousticEchoCancellation = enable;
|
||||
auto client = DependencyManager::get<AudioClient>().data();
|
||||
QMetaObject::invokeMethod(client, "setAcousticEchoCancellation", Q_ARG(bool, enable), Q_ARG(bool, false));
|
||||
enableAcousticEchoCancellationSetting.set(enable);
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
if (changed) {
|
||||
emit acousticEchoCancellationChanged(enable);
|
||||
}
|
||||
}
|
||||
|
||||
float Audio::getInputVolume() const {
|
||||
return resultWithReadLock<bool>([&] {
|
||||
return _inputVolume;
|
||||
|
|
|
@ -66,12 +66,19 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
|
|||
* @property {boolean} pushToTalkHMD - <code>true</code> if HMD push-to-talk is enabled, otherwise <code>false</code>.
|
||||
* @property {boolean} pushingToTalk - <code>true</code> if the user is currently pushing-to-talk, otherwise
|
||||
* <code>false</code>.
|
||||
* @property {number} avatarGain - The gain (relative volume) that avatars' voices are played at. This gain is used at the server.
|
||||
* @property {number} localInjectorGain - The gain (relative volume) that local injectors (local environment sounds) are played at.
|
||||
* @property {number} serverInjectorGain - The gain (relative volume) that server injectors (server environment sounds) are played at. This gain is used at the server.
|
||||
* @property {number} systemInjectorGain - The gain (relative volume) that system sounds are played at.
|
||||
* @property {number} pushingToTalkOutputGainDesktop - The gain (relative volume) that all sounds are played at when the user is holding
|
||||
* the push-to-talk key in Desktop mode.
|
||||
|
||||
* @property {number} avatarGain - The gain (relative volume in dB) that avatars' voices are played at. This gain is used
|
||||
* at the server.
|
||||
* @property {number} localInjectorGain - The gain (relative volume in dB) that local injectors (local environment sounds)
|
||||
* are played at.
|
||||
* @property {number} serverInjectorGain - The gain (relative volume in dB) that server injectors (server environment
|
||||
* sounds) are played at. This gain is used at the server.
|
||||
* @property {number} systemInjectorGain - The gain (relative volume in dB) that system sounds are played at.
|
||||
* @property {number} pushingToTalkOutputGainDesktop - The gain (relative volume in dB) that all sounds are played at when
|
||||
* the user is holding the push-to-talk key in desktop mode.
|
||||
* @property {boolean} acousticEchoCancellation - <code>true</code> if acoustic echo cancellation is enabled, otherwise
|
||||
* <code>false</code>. When enabled, sound from the audio output is suppressed when it echos back to the input audio
|
||||
* signal.
|
||||
*
|
||||
* @comment The following properties are from AudioScriptingInterface.h.
|
||||
* @property {boolean} isStereoInput - <code>true</code> if the input audio is being used in stereo, otherwise
|
||||
|
@ -85,6 +92,8 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
|
|||
Q_PROPERTY(bool muted READ isMuted WRITE setMuted NOTIFY mutedChanged)
|
||||
Q_PROPERTY(bool noiseReduction READ noiseReductionEnabled WRITE enableNoiseReduction NOTIFY noiseReductionChanged)
|
||||
Q_PROPERTY(bool warnWhenMuted READ warnWhenMutedEnabled WRITE enableWarnWhenMuted NOTIFY warnWhenMutedChanged)
|
||||
Q_PROPERTY(bool acousticEchoCancellation
|
||||
READ acousticEchoCancellationEnabled WRITE enableAcousticEchoCancellation NOTIFY acousticEchoCancellationChanged)
|
||||
Q_PROPERTY(float inputVolume READ getInputVolume WRITE setInputVolume NOTIFY inputVolumeChanged)
|
||||
Q_PROPERTY(float inputLevel READ getInputLevel NOTIFY inputLevelChanged)
|
||||
Q_PROPERTY(bool clipping READ isClipping NOTIFY clippingChanged)
|
||||
|
@ -115,6 +124,7 @@ public:
|
|||
bool isMuted() const;
|
||||
bool noiseReductionEnabled() const;
|
||||
bool warnWhenMutedEnabled() const;
|
||||
bool acousticEchoCancellationEnabled() const;
|
||||
float getInputVolume() const;
|
||||
float getInputLevel() const;
|
||||
bool isClipping() const;
|
||||
|
@ -295,18 +305,18 @@ public:
|
|||
Q_INVOKABLE bool getRecording();
|
||||
|
||||
/**jsdoc
|
||||
* Sets the output volume gain that will be used when the user is holding the Push to Talk key.
|
||||
* Sets the output volume gain that will be used when the user is holding the push-to-talk key.
|
||||
* Should be negative.
|
||||
* @function Audio.setPushingToTalkOutputGainDesktop
|
||||
* @param {number} gain - The output volume gain (dB) while using PTT.
|
||||
* @param {number} gain - The output volume gain (dB) while using push-to-talk.
|
||||
*/
|
||||
Q_INVOKABLE void setPushingToTalkOutputGainDesktop(float gain);
|
||||
|
||||
/**jsdoc
|
||||
* Gets the output volume gain that is used when the user is holding the Push to Talk key.
|
||||
* Gets the output volume gain that is used when the user is holding the push-to-talk key.
|
||||
* Should be negative.
|
||||
* @function Audio.getPushingToTalkOutputGainDesktop
|
||||
* @returns {number} gain - The output volume gain (dB) while using PTT.
|
||||
* @returns {number} gain - The output volume gain (dB) while using push-to-talk.
|
||||
*/
|
||||
Q_INVOKABLE float getPushingToTalkOutputGainDesktop();
|
||||
|
||||
|
@ -396,6 +406,14 @@ signals:
|
|||
*/
|
||||
void warnWhenMutedChanged(bool isEnabled);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when acoustic echo cancellation is enabled or disabled.
|
||||
* @function Audio.acousticEchoCancellationChanged
|
||||
* @param {boolean} isEnabled - <code>true</code> if acoustic echo cancellation is enabled, otherwise <code>false</code>.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void acousticEchoCancellationChanged(bool isEnabled);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when the input audio volume changes.
|
||||
* @function Audio.inputVolumeChanged
|
||||
|
@ -443,7 +461,7 @@ signals:
|
|||
/**jsdoc
|
||||
* Triggered when the avatar gain changes.
|
||||
* @function Audio.avatarGainChanged
|
||||
* @param {number} gain - The new avatar gain value.
|
||||
* @param {number} gain - The new avatar gain value (dB).
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void avatarGainChanged(float gain);
|
||||
|
@ -451,7 +469,7 @@ signals:
|
|||
/**jsdoc
|
||||
* Triggered when the local injector gain changes.
|
||||
* @function Audio.localInjectorGainChanged
|
||||
* @param {number} gain - The new local injector gain value.
|
||||
* @param {number} gain - The new local injector gain value (dB).
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void localInjectorGainChanged(float gain);
|
||||
|
@ -459,7 +477,7 @@ signals:
|
|||
/**jsdoc
|
||||
* Triggered when the server injector gain changes.
|
||||
* @function Audio.serverInjectorGainChanged
|
||||
* @param {number} gain - The new server injector gain value.
|
||||
* @param {number} gain - The new server injector gain value (dB).
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void serverInjectorGainChanged(float gain);
|
||||
|
@ -467,7 +485,7 @@ signals:
|
|||
/**jsdoc
|
||||
* Triggered when the system injector gain changes.
|
||||
* @function Audio.systemInjectorGainChanged
|
||||
* @param {number} gain - The new system injector gain value.
|
||||
* @param {number} gain - The new system injector gain value (dB).
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void systemInjectorGainChanged(float gain);
|
||||
|
@ -475,7 +493,7 @@ signals:
|
|||
/**jsdoc
|
||||
* Triggered when the push to talk gain changes.
|
||||
* @function Audio.pushingToTalkOutputGainDesktopChanged
|
||||
* @param {number} gain - The new output gain value.
|
||||
* @param {number} gain - The new output gain value (dB).
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void pushingToTalkOutputGainDesktopChanged(float gain);
|
||||
|
@ -494,6 +512,7 @@ private slots:
|
|||
void setMuted(bool muted);
|
||||
void enableNoiseReduction(bool enable);
|
||||
void enableWarnWhenMuted(bool enable);
|
||||
void enableAcousticEchoCancellation(bool enable);
|
||||
void setInputVolume(float volume);
|
||||
void onInputLoudnessChanged(float loudness, bool isClipping);
|
||||
|
||||
|
@ -506,12 +525,17 @@ private:
|
|||
bool _settingsLoaded { false };
|
||||
float _inputVolume { 1.0f };
|
||||
float _inputLevel { 0.0f };
|
||||
Setting::Handle<float> _avatarGainSetting { QStringList { Audio::AUDIO, "AvatarGain" }, 0.0f };
|
||||
Setting::Handle<float> _injectorGainSetting { QStringList { Audio::AUDIO, "InjectorGain" }, 0.0f };
|
||||
Setting::Handle<float> _localInjectorGainSetting { QStringList { Audio::AUDIO, "LocalInjectorGain" }, 0.0f };
|
||||
Setting::Handle<float> _systemInjectorGainSetting { QStringList { Audio::AUDIO, "SystemInjectorGain" }, 0.0f };
|
||||
float _localInjectorGain { 0.0f }; // in dB
|
||||
float _systemInjectorGain { 0.0f }; // in dB
|
||||
float _pttOutputGainDesktop { 0.0f }; // in dB
|
||||
bool _isClipping { false };
|
||||
bool _enableNoiseReduction { true }; // Match default value of AudioClient::_isNoiseGateEnabled.
|
||||
bool _enableWarnWhenMuted { true };
|
||||
bool _enableAcousticEchoCancellation { true }; // AudioClient::_isAECEnabled
|
||||
bool _contextIsHMD { false };
|
||||
AudioDevices* getDevices() { return &_devices; }
|
||||
AudioDevices _devices;
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
class QScriptValue;
|
||||
|
||||
/**jsdoc
|
||||
* The <code>PlatformInfo</code> API provides information about the computer and controllers being used.
|
||||
* The <code>PlatformInfo</code> API provides information about the hardware platform being used.
|
||||
*
|
||||
* @namespace PlatformInfo
|
||||
*
|
||||
|
@ -31,6 +31,21 @@ public:
|
|||
PlatformInfoScriptingInterface();
|
||||
virtual ~PlatformInfoScriptingInterface();
|
||||
|
||||
/**jsdoc
|
||||
* <p>The platform tier of a computer is an indication of its rendering capability.</p>
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Value</th><th>Name</th><th>Description</th></tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td><code>0</code></td><td>UNKNOWN</td><td>Unknown rendering capability.</td></tr>
|
||||
* <tr><td><code>1</code></td><td>LOW</td><td>Low-end PC, capable of rendering low-quality graphics.</td></tr>
|
||||
* <tr><td><code>2</code></td><td>MID</td><td>Business-class PC, capable of rendering medium-quality graphics.</td></tr>
|
||||
* <tr><td><code>3</code></td><td>HIGH</td><td>High-end PC, capable of rendering high-quality graphics.</td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef {number} PlatformInfo.PlatformTier
|
||||
*/
|
||||
// Platform tier enum type
|
||||
enum PlatformTier {
|
||||
UNKNOWN = platform::Profiler::Tier::UNKNOWN,
|
||||
|
@ -50,23 +65,18 @@ public slots:
|
|||
/**jsdoc
|
||||
* Gets the operating system type.
|
||||
* @function PlatformInfo.getOperatingSystemType
|
||||
* @returns {string} <code>"WINDOWS"</code>, <code>"MACOS"</code>, or <code>"UNKNOWN"</code>.
|
||||
* @returns {string} The operating system type: <code>"WINDOWS"</code>, <code>"MACOS"</code>, or <code>"UNKNOWN"</code>.
|
||||
* @deprecated This function is deprecated and will be removed.
|
||||
* use getComputer()["OS"] instead
|
||||
* Use <code>JSON.parse({@link PlatformInfo.getComputer|PlatformInfo.getComputer()}).OS</code> instead.
|
||||
*/
|
||||
QString getOperatingSystemType();
|
||||
|
||||
/**jsdoc
|
||||
* Gets information on the CPU.
|
||||
* Gets information on the CPU model.
|
||||
* @function PlatformInfo.getCPUBrand
|
||||
* @returns {string} Information on the CPU.
|
||||
* @example <caption>Report the CPU being used.</caption>
|
||||
* print("CPU: " + PlatformInfo.getCPUBrand());
|
||||
* // Example: Intel(R) Core(TM) i7-7820HK CPU @ 2.90GHz
|
||||
* @deprecated This function is deprecated and will be removed.
|
||||
* use getNumCPUs() to know the number of CPUs in the hardware, at least one is expected
|
||||
* use getCPU(0)["vendor"] to get the brand of the vendor
|
||||
* use getCPU(0)["model"] to get the model name of the cpu
|
||||
* Use <code>JSON.parse({@link PlatformInfo.getCPU|PlatformInfo.getCPU(0)}).model</code> instead.
|
||||
*/
|
||||
QString getCPUBrand();
|
||||
|
||||
|
@ -75,27 +85,27 @@ public slots:
|
|||
* @function PlatformInfo.getNumLogicalCores
|
||||
* @returns {number} The number of logical CPU cores.
|
||||
* @deprecated This function is deprecated and will be removed.
|
||||
* use getCPU(0)["numCores"] instead
|
||||
* Use <code>JSON.parse({@link PlatformInfo.getCPU|PlatformInfo.getCPU(0)}).numCores</code> instead.
|
||||
*/
|
||||
unsigned int getNumLogicalCores();
|
||||
|
||||
/**jsdoc
|
||||
* Returns the total system memory in megabytes.
|
||||
* Gets the total amount of usable physical memory, in MB.
|
||||
* @function PlatformInfo.getTotalSystemMemoryMB
|
||||
* @returns {number} The total system memory in megabytes.
|
||||
* @deprecated This function is deprecated and will be removed.
|
||||
* use getMemory()["memTotal"] instead
|
||||
* Use <code>JSON.parse({@link PlatformInfo.getMemory|PlatformInfo.getMemory()}).memTotal</code> instead.
|
||||
*/
|
||||
int getTotalSystemMemoryMB();
|
||||
|
||||
/**jsdoc
|
||||
* Gets the graphics card type.
|
||||
* Gets the model of the graphics card currently being used.
|
||||
* @function PlatformInfo.getGraphicsCardType
|
||||
* @returns {string} The graphics card type.
|
||||
* @returns {string} The model of the graphics card currently being used.
|
||||
* @deprecated This function is deprecated and will be removed.
|
||||
* use getNumGPUs() to know the number of GPUs in the hardware, at least one is expected
|
||||
* use getGPU(getMasterGPU())["vendor"] to get the brand of the vendor
|
||||
* use getGPU(getMasterGPU())["model"] to get the model name of the gpu
|
||||
* Use <code>JSON.parse({@link PlatformInfo.getGPU|PlatformInfo.getGPU(}
|
||||
* {@link PlatformInfo.getMasterGPU|PlatformInfo.getMasterGPU() )}).model</code>
|
||||
* instead.
|
||||
*/
|
||||
QString getGraphicsCardType();
|
||||
|
||||
|
@ -117,139 +127,150 @@ public slots:
|
|||
* Checks whether HTML on 3D surfaces (e.g., Web entities) is supported.
|
||||
* @function PlatformInfo.has3DHTML
|
||||
* @returns {boolean} <code>true</code> if the current display supports HTML on 3D surfaces, <code>false</code> if it
|
||||
* doesn't.
|
||||
* doesn't.
|
||||
*/
|
||||
bool has3DHTML();
|
||||
|
||||
/**jsdoc
|
||||
* Checks whether Interface is running on a stand-alone HMD device (CPU incorporated into the HMD display).
|
||||
* @function PlatformInfo.isStandalone
|
||||
* @returns {boolean} <code>true</code> if Interface is running on a stand-alone device, <code>false</code> if it isn't.
|
||||
* @returns {boolean} <code>true</code> if Interface is running on a stand-alone HMD device, <code>false</code> if it isn't.
|
||||
*/
|
||||
bool isStandalone();
|
||||
|
||||
/**jsdoc
|
||||
* Get the number of CPUs.
|
||||
* @function PlatformInfo.getNumCPUs
|
||||
* @returns {number} The number of CPUs detected on the hardware platform.
|
||||
*/
|
||||
* Gets the number of CPUs.
|
||||
* @function PlatformInfo.getNumCPUs
|
||||
* @returns {number} The number of CPUs.
|
||||
*/
|
||||
int getNumCPUs();
|
||||
|
||||
/**jsdoc
|
||||
* Get the index of the master CPU.
|
||||
* @function PlatformInfo.getMasterCPU
|
||||
* @returns {number} The index of the master CPU detected on the hardware platform.
|
||||
*/
|
||||
* Gets the index number of the master CPU.
|
||||
* @function PlatformInfo.getMasterCPU
|
||||
* @returns {number} The index of the master CPU.
|
||||
*/
|
||||
int getMasterCPU();
|
||||
|
||||
/**jsdoc
|
||||
* Get the description of the CPU at the index parameter
|
||||
* expected fields are:
|
||||
* - cpuVendor...
|
||||
* @param index The index of the CPU of the platform
|
||||
* @function PlatformInfo.getCPU
|
||||
* @returns {string} The CPU description json field
|
||||
*/
|
||||
* Gets the platform description of a CPU.
|
||||
* @function PlatformInfo.getCPU
|
||||
* @param {number} index - The index number of the CPU.
|
||||
* @returns {string} The CPU's {@link PlatformInfo.CPUDescription|CPUDescription} information as a JSON string.
|
||||
* @example <caption>Report details of the computer's CPUs.</caption>
|
||||
* var numCPUs = PlatformInfo.getNumCPUs();
|
||||
* print("Number of CPUs: " + numCPUs);
|
||||
* for (var i = 0; i < numCPUs; i++) {
|
||||
* var cpuDescription = PlatformInfo.getCPU(i);
|
||||
* print("CPU " + i + ": " + cpuDescription);
|
||||
* }
|
||||
*/
|
||||
QString getCPU(int index);
|
||||
|
||||
/**jsdoc
|
||||
* Get the number of GPUs.
|
||||
* Gets the number of GPUs.
|
||||
* @function PlatformInfo.getNumGPUs
|
||||
* @returns {number} The number of GPUs detected on the hardware platform.
|
||||
* @returns {number} The number of GPUs.
|
||||
*/
|
||||
int getNumGPUs();
|
||||
|
||||
/**jsdoc
|
||||
* Get the index of the master GPU.
|
||||
* @function PlatformInfo.getMasterGPU
|
||||
* @returns {number} The index of the master GPU detected on the hardware platform.
|
||||
*/
|
||||
* Gets the index number of the master GPU.
|
||||
* @function PlatformInfo.getMasterGPU
|
||||
* @returns {number} The index of the master GPU.
|
||||
*/
|
||||
int getMasterGPU();
|
||||
|
||||
/**jsdoc
|
||||
* Get the description of the GPU at the index parameter
|
||||
* expected fields are:
|
||||
* - vendor, model...
|
||||
* @param index The index of the GPU of the platform
|
||||
* Gets the platform description of a GPU.
|
||||
* @param {number} index - The index number of the GPU.
|
||||
* @function PlatformInfo.getGPU
|
||||
* @returns {string} The GPU description json field
|
||||
* @returns {string} The GPU's {@link PlatformInfo.GPUDescription|GPUDescription} information as a JSON string.
|
||||
* @example <caption>Report details of the computer's GPUs.</caption>
|
||||
* var numGPUs = PlatformInfo.getNumGPUs();
|
||||
* print("Number of GPUs: " + numGPUs);
|
||||
* for (var i = 0; i < numGPUs; i++) {
|
||||
* var gpuDescription = PlatformInfo.getGPU(i);
|
||||
* print("GPU " + i + ": " + gpuDescription);
|
||||
* }
|
||||
*/
|
||||
QString getGPU(int index);
|
||||
|
||||
/**jsdoc
|
||||
* Get the number of Displays.
|
||||
* @function PlatformInfo.getNumDisplays
|
||||
* @returns {number} The number of Displays detected on the hardware platform.
|
||||
*/
|
||||
* Gets the number of displays.
|
||||
* @function PlatformInfo.getNumDisplays
|
||||
* @returns {number} The number of displays.
|
||||
*/
|
||||
int getNumDisplays();
|
||||
|
||||
/**jsdoc
|
||||
* Get the index of the master Display.
|
||||
* @function PlatformInfo.getMasterDisplay
|
||||
* @returns {number} The index of the master Display detected on the hardware platform.
|
||||
*/
|
||||
* Gets the index number of the master display.
|
||||
* @function PlatformInfo.getMasterDisplay
|
||||
* @returns {number} The index of the master display.
|
||||
*/
|
||||
int getMasterDisplay();
|
||||
|
||||
/**jsdoc
|
||||
* Get the description of the Display at the index parameter
|
||||
* expected fields are:
|
||||
* - DisplayVendor...
|
||||
* @param index The index of the Display of the platform
|
||||
* @function PlatformInfo.getDisplay
|
||||
* @returns {string} The Display description json field
|
||||
*/
|
||||
* Gets the platform description of a display.
|
||||
* @param {number} index - The index number of the display.
|
||||
* @function PlatformInfo.getDisplay
|
||||
* @returns {string} The display's {@link PlatformInfo.DisplayDescription|DisplayDescription} information as a JSON string.
|
||||
* @example <caption>Report details of the systems's displays.</caption>
|
||||
* var numDisplays = PlatformInfo.getNumDisplays();
|
||||
* print("Number of displays: " + numDisplays);
|
||||
* for (var i = 0; i < numDisplays; i++) {
|
||||
* var displayDescription = PlatformInfo.getDisplay(i);
|
||||
* print("Display " + i + ": " + displayDescription);
|
||||
* }
|
||||
*/
|
||||
QString getDisplay(int index);
|
||||
|
||||
/**jsdoc
|
||||
* Get the description of the Memory
|
||||
* expected fields are:
|
||||
* - MemoryVendor...
|
||||
* @function PlatformInfo.getMemory
|
||||
* @returns {string} The Memory description json field
|
||||
*/
|
||||
* Gets the platform description of computer memory.
|
||||
* @function PlatformInfo.getMemory
|
||||
* @returns {string} The computer's {@link PlatformInfo.MemoryDescription|MemoryDescription} information as a JSON string.
|
||||
* @example <caption>Report details of the computer's memory.</caption>
|
||||
* print("Memory: " + PlatformInfo.getMemory());
|
||||
*/
|
||||
QString getMemory();
|
||||
|
||||
/**jsdoc
|
||||
* Get the description of the Computer
|
||||
* expected fields are:
|
||||
* - ComputerVendor...
|
||||
* @function PlatformInfo.getComputer
|
||||
* @returns {string} The Computer description json field
|
||||
*/
|
||||
* Gets the platform description of the computer.
|
||||
* @function PlatformInfo.getComputer
|
||||
* @returns {string} The {@link PlatformInfo.ComputerDescription|ComputerDescription} information as a JSON string.
|
||||
*/
|
||||
QString getComputer();
|
||||
|
||||
/**jsdoc
|
||||
* Get the complete description of the Platform as an aggregated Json
|
||||
* The expected object description is:
|
||||
* { "computer": {...}, "memory": {...}, "cpus": [{...}, ...], "gpus": [{...}, ...], "displays": [{...}, ...] }
|
||||
* @function PlatformInfo.getPlatform
|
||||
* @returns {string} The Platform description json field
|
||||
*/
|
||||
* Gets the complete description of the computer as a whole.
|
||||
* @function PlatformInfo.getPlatform
|
||||
* @returns {string} The {@link PlatformInfo.PlatformDescription|PlatformDescription} information as a JSON string.
|
||||
*/
|
||||
QString getPlatform();
|
||||
|
||||
/**jsdoc
|
||||
* Get the Platform TIer profiled on startup of the Computer
|
||||
* Platform Tier is an integer/enum value:
|
||||
* UNKNOWN = 0, LOW = 1, MID = 2, HIGH = 3
|
||||
* @function PlatformInfo.getTierProfiled
|
||||
* @returns {number} The Platform Tier profiled on startup.
|
||||
*/
|
||||
* Gets the platform tier of the computer, profiled at Interface start-up.
|
||||
* @function PlatformInfo.getTierProfiled
|
||||
* @returns {PlatformInfo.PlatformTier} The platform tier of the computer.
|
||||
* @example <caption>Report the platform tier of the computer.</caption>
|
||||
* var platformTier = PlatformInfo.getTierProfiled();
|
||||
* var platformTierName = PlatformInfo.getPlatformTierNames()[platformTier];
|
||||
* print("Platform tier: " + platformTier + ", " + platformTierName);
|
||||
*/
|
||||
PlatformTier getTierProfiled();
|
||||
|
||||
/**jsdoc
|
||||
* Get the Platform Tier possible Names as an array of strings
|
||||
* Platform Tier names are:
|
||||
* [ "UNKNOWN", "LOW", "MID", "HIGH" ]
|
||||
* @function PlatformInfo.getPlatformTierNames
|
||||
* @returns {string} The array of names matching the number returned from PlatformInfo.getTierProfiled
|
||||
*/
|
||||
* Gets the names of the possible platform tiers, per {@link PlatformInfo.PlatformTier}.
|
||||
* @function PlatformInfo.getPlatformTierNames
|
||||
* @returns {string[]} The names of the possible platform tiers.
|
||||
*/
|
||||
QStringList getPlatformTierNames();
|
||||
|
||||
/**jsdoc
|
||||
* Gets whether the current hardware can render using the Deferred method.
|
||||
* @function PlatformInfo.isRenderMethodDeferredCapable
|
||||
* @returns {bool} <code>true</code> if the current hardware can render using the Deferred method; <code>false</code> otherwise.
|
||||
*/
|
||||
* Gets whether the current hardware can use deferred rendering.
|
||||
* @function PlatformInfo.isRenderMethodDeferredCapable
|
||||
* @returns {boolean} <code>true</code> if the current hardware can use deferred rendering, <code>false</code> if it can't.
|
||||
*/
|
||||
bool isRenderMethodDeferredCapable();
|
||||
};
|
||||
|
||||
|
|
|
@ -15,13 +15,20 @@
|
|||
#include "RenderForward.h"
|
||||
|
||||
/**jsdoc
|
||||
* The <code>Render</code> API allows you to configure the graphics engine
|
||||
* The <code>Render</code> API enables you to configure the graphics engine.
|
||||
*
|
||||
* @namespace Render
|
||||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
*
|
||||
* @property {Render.RenderMethod} renderMethod - The render method being used.
|
||||
* @property {boolean} shadowsEnabled - <code>true</code> if shadows are enabled, <code>false</code> if they're disabled.
|
||||
* @property {boolean} ambientOcclusionEnabled - <code>true</code> if ambient occlusion is enabled, <code>false</code> if it's
|
||||
* disabled.
|
||||
* @property {boolean} antialiasingEnabled - <code>true</code> if anti-aliasing is enabled, <code>false</code> if it's disabled.
|
||||
* @property {number} viewportResolutionScale - The view port resolution scale, <code>> 0.0</code>.
|
||||
*/
|
||||
class RenderScriptingInterface : public QObject {
|
||||
Q_OBJECT
|
||||
|
@ -36,6 +43,21 @@ public:
|
|||
|
||||
static RenderScriptingInterface* getInstance();
|
||||
|
||||
/**jsdoc
|
||||
* <p>The rendering method is specified by the following values:</p>
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Value</th><th>Name</th><th>Description</th>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td><code>0</code></td><td>DEFERRED</td><td>More complex rendering pipeline where lighting is applied to the
|
||||
* scene as a whole after all objects have been rendered.</td></tr>
|
||||
* <tr><td><code>1</code></td><td>FORWARD</td><td>Simpler rendering pipeline where each object in the scene, in turn,
|
||||
* is rendered and has lighting applied.</td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef {number} Render.RenderMethod
|
||||
*/
|
||||
// RenderMethod enum type
|
||||
enum class RenderMethod {
|
||||
DEFERRED = render::Args::RenderMethod::DEFERRED,
|
||||
|
@ -52,95 +74,114 @@ public:
|
|||
|
||||
public slots:
|
||||
/**jsdoc
|
||||
* Get a config for a job by name
|
||||
* Gets the configuration for a rendering job by name.
|
||||
* <p><strong>Warning:</strong> For internal, debugging purposes. Subject to change.</p>
|
||||
* @function Render.getConfig
|
||||
* @param {string} name - Can be:
|
||||
* - <job_name>: Search for the first job named job_name traversing the the sub graph of task and jobs (from this task as root)
|
||||
* - <parent_name>.[<sub_parent_names>.]<job_name>: Allows you to first look for the parent_name job (from this task as root) and then search from there for the
|
||||
* optional sub_parent_names and finally from there looking for the job_name (assuming every job in the path is found)
|
||||
* @returns {object} The sub job config.
|
||||
* @param {string} name - The name of the rendering job.
|
||||
* @returns {object} The configuration for the rendering job.
|
||||
*/
|
||||
QObject* getConfig(const QString& name) { return qApp->getRenderEngine()->getConfiguration()->getConfig(name); }
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* Gets the current render method
|
||||
* Gets the render method being used.
|
||||
* @function Render.getRenderMethod
|
||||
* @returns {number} <code>"DEFERRED"</code> or <code>"FORWARD"</code>
|
||||
* @returns {Render.RenderMethod} The render method being used.
|
||||
* @example <caption>Report the current render method.</caption>
|
||||
* var renderMethod = Render.getRenderMethod();
|
||||
* print("Current render method: " + Render.getRenderMethodNames()[renderMethod]);
|
||||
*/
|
||||
RenderMethod getRenderMethod() const;
|
||||
|
||||
/**jsdoc
|
||||
* Sets the current render method
|
||||
* Sets the render method to use.
|
||||
* @function Render.setRenderMethod
|
||||
* @param {number} renderMethod - <code>"DEFERRED"</code> or <code>"FORWARD"</code>
|
||||
* @param {Render.RenderMethod} renderMethod - The render method to use.
|
||||
*/
|
||||
void setRenderMethod(RenderMethod renderMethod);
|
||||
|
||||
/**jsdoc
|
||||
* Gets the possible enum names of the RenderMethod type
|
||||
* @function Render.getRenderMethodNames
|
||||
* @returns [string] [ <code>"DEFERRED"</code>, <code>"FORWARD"</code> ]
|
||||
*/
|
||||
* Gets the names of the possible render methods, per {@link Render.RenderMethod}.
|
||||
* @function Render.getRenderMethodNames
|
||||
* @returns {string[]} The names of the possible render methods.
|
||||
* @example <caption>Report the names of the possible render methods.</caption>
|
||||
* var renderMethods = Render.getRenderMethodNames();
|
||||
* print("Render methods:");
|
||||
* for (var i = 0; i < renderMethods.length; i++) {
|
||||
* print("- " + renderMethods[i]);
|
||||
* }
|
||||
*/
|
||||
QStringList getRenderMethodNames() const;
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* Whether or not shadows are enabled
|
||||
* Gets whether or not shadows are enabled.
|
||||
* @function Render.getShadowsEnabled
|
||||
* @returns {bool} <code>true</code> if shadows are enabled, otherwise <code>false</code>
|
||||
* @returns {boolean} <code>true</code> if shadows are enabled, <code>false</code> if they're disabled.
|
||||
*/
|
||||
bool getShadowsEnabled() const;
|
||||
|
||||
/**jsdoc
|
||||
* Enables or disables shadows
|
||||
* Sets whether or not shadows are enabled.
|
||||
* @function Render.setShadowsEnabled
|
||||
* @param {bool} enabled - <code>true</code> to enable shadows, <code>false</code> to disable them
|
||||
* @param {boolean} enabled - <code>true</code> to enable shadows, <code>false</code> to disable.
|
||||
*/
|
||||
void setShadowsEnabled(bool enabled);
|
||||
|
||||
/**jsdoc
|
||||
* Whether or not ambient occlusion is enabled
|
||||
* Gets whether or not ambient occlusion is enabled.
|
||||
* @function Render.getAmbientOcclusionEnabled
|
||||
* @returns {bool} <code>true</code> if ambient occlusion is enabled, otherwise <code>false</code>
|
||||
* @returns {boolean} <code>true</code> if ambient occlusion is enabled, <code>false</code> if it's disabled.
|
||||
*/
|
||||
bool getAmbientOcclusionEnabled() const;
|
||||
|
||||
/**jsdoc
|
||||
* Enables or disables ambient occlusion
|
||||
* Sets whether or not ambient occlusion is enabled.
|
||||
* @function Render.setAmbientOcclusionEnabled
|
||||
* @param {bool} enabled - <code>true</code> to enable ambient occlusion, <code>false</code> to disable it
|
||||
* @param {boolean} enabled - <code>true</code> to enable ambient occlusion, <code>false</code> to disable.
|
||||
*/
|
||||
void setAmbientOcclusionEnabled(bool enabled);
|
||||
|
||||
/**jsdoc
|
||||
* Whether or not anti-aliasing is enabled
|
||||
* Gets whether or not anti-aliasing is enabled.
|
||||
* @function Render.getAntialiasingEnabled
|
||||
* @returns {bool} <code>true</code> if anti-aliasing is enabled, otherwise <code>false</code>
|
||||
* @returns {boolean} <code>true</code> if anti-aliasing is enabled, <code>false</code> if it's disabled.
|
||||
*/
|
||||
bool getAntialiasingEnabled() const;
|
||||
|
||||
/**jsdoc
|
||||
* Enables or disables anti-aliasing
|
||||
* Sets whether or not anti-aliasing is enabled.
|
||||
* @function Render.setAntialiasingEnabled
|
||||
* @param {bool} enabled - <code>true</code> to enable anti-aliasing, <code>false</code> to disable it
|
||||
* @param {boolean} enabled - <code>true</code> to enable anti-aliasing, <code>false</code> to disable.
|
||||
*/
|
||||
void setAntialiasingEnabled(bool enabled);
|
||||
|
||||
/**jsdoc
|
||||
* Gets the current viewport resolution scale
|
||||
* Gets the view port resolution scale.
|
||||
* @function Render.getViewportResolutionScale
|
||||
* @returns {number}
|
||||
* @returns {number} The view port resolution scale, <code>> 0.0</code>.
|
||||
*/
|
||||
float getViewportResolutionScale() const;
|
||||
|
||||
/**jsdoc
|
||||
* Sets the current viewport resolution scale
|
||||
* Sets the view port resolution scale.
|
||||
* @function Render.setViewportResolutionScale
|
||||
* @param {number} resolutionScale - between epsilon and 1.0
|
||||
* @param {number} resolutionScale - The view port resolution scale to set, <code>> 0.0</code>.
|
||||
*/
|
||||
void setViewportResolutionScale(float resolutionScale);
|
||||
|
||||
signals:
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when one of the <code>Render</code> API's properties changes.
|
||||
* @function Render.settingsChanged
|
||||
* @returns {Signal}
|
||||
* @example <caption>Report when a render setting changes.</caption>
|
||||
* Render.settingsChanged.connect(function () {
|
||||
* print("Render setting changed");
|
||||
* });
|
||||
* // Toggle Developer > Render > Shadows or similar to trigger.
|
||||
*/
|
||||
void settingsChanged();
|
||||
|
||||
private:
|
||||
|
|
|
@ -32,6 +32,7 @@ AvatarInputs* AvatarInputs::getInstance() {
|
|||
|
||||
AvatarInputs::AvatarInputs(QObject* parent) : QObject(parent) {
|
||||
_showAudioTools = showAudioToolsSetting.get();
|
||||
_showBubbleTools = showBubbleToolsSetting.get();
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto usersScriptingInterface = DependencyManager::get<UsersScriptingInterface>();
|
||||
connect(nodeList.data(), &NodeList::ignoreRadiusEnabledChanged, this, &AvatarInputs::ignoreRadiusEnabledChanged);
|
||||
|
|
|
@ -194,8 +194,8 @@ private:
|
|||
void onAvatarEnteredIgnoreRadius();
|
||||
void onAvatarLeftIgnoreRadius();
|
||||
float _trailingAudioLoudness{ 0 };
|
||||
bool _showAudioTools { false };
|
||||
bool _showBubbleTools{ false };
|
||||
bool _showAudioTools { true };
|
||||
bool _showBubbleTools{ true };
|
||||
};
|
||||
|
||||
#endif // hifi_AvatarInputs_h
|
||||
|
|
|
@ -11,11 +11,52 @@
|
|||
#include "ResourceImageItem.h"
|
||||
|
||||
#include <gl/Config.h>
|
||||
|
||||
#include <gl/GLHelpers.h>
|
||||
#include <QOpenGLFramebufferObjectFormat>
|
||||
#include <QOpenGLShaderProgram>
|
||||
|
||||
#include <plugins/DisplayPlugin.h>
|
||||
|
||||
|
||||
static const char* VERTEX_SHADER = R"SHADER(
|
||||
#version 450 core
|
||||
|
||||
out vec2 vTexCoord;
|
||||
|
||||
void main(void) {
|
||||
const float depth = 0.0;
|
||||
const vec4 UNIT_QUAD[4] = vec4[4](
|
||||
vec4(-1.0, -1.0, depth, 1.0),
|
||||
vec4(1.0, -1.0, depth, 1.0),
|
||||
vec4(-1.0, 1.0, depth, 1.0),
|
||||
vec4(1.0, 1.0, depth, 1.0)
|
||||
);
|
||||
vec4 pos = UNIT_QUAD[gl_VertexID];
|
||||
|
||||
gl_Position = pos;
|
||||
vTexCoord = (pos.xy + 1.0) * 0.5;
|
||||
}
|
||||
)SHADER";
|
||||
|
||||
static const char* FRAGMENT_SHADER = R"SHADER(
|
||||
#version 450 core
|
||||
|
||||
uniform sampler2D sampler;
|
||||
|
||||
in vec2 vTexCoord;
|
||||
|
||||
out vec4 FragColor;
|
||||
|
||||
vec3 color_LinearTosRGB(vec3 lrgb) {
|
||||
return mix(vec3(1.055) * pow(vec3(lrgb), vec3(0.41666)) - vec3(0.055), vec3(lrgb) * vec3(12.92), vec3(lessThan(lrgb, vec3(0.0031308))));
|
||||
}
|
||||
|
||||
void main() {
|
||||
FragColor = vec4(color_LinearTosRGB(texture(sampler, vTexCoord).rgb), 1.0);
|
||||
}
|
||||
)SHADER";
|
||||
|
||||
|
||||
ResourceImageItem::ResourceImageItem() : QQuickFramebufferObject() {
|
||||
auto textureCache = DependencyManager::get<TextureCache>();
|
||||
connect(textureCache.data(), SIGNAL(spectatorCameraFramebufferReset()), this, SLOT(update()));
|
||||
|
@ -95,16 +136,29 @@ void ResourceImageItemRenderer::render() {
|
|||
}
|
||||
if (_ready) {
|
||||
_fboMutex.lock();
|
||||
_copyFbo->bind();
|
||||
QOpenGLFramebufferObject::blitFramebuffer(framebufferObject(), _copyFbo, GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT, GL_NEAREST);
|
||||
|
||||
// this clears the copyFbo texture
|
||||
// so next frame starts fresh - helps
|
||||
// when aspect ratio changes
|
||||
_copyFbo->takeTexture();
|
||||
|
||||
if (!_shader) {
|
||||
_shader = new QOpenGLShaderProgram();
|
||||
_shader->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, VERTEX_SHADER);
|
||||
_shader->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, FRAGMENT_SHADER);
|
||||
_shader->link();
|
||||
glGenVertexArrays(1, &_vao);
|
||||
}
|
||||
framebufferObject()->bind();
|
||||
_shader->bind();
|
||||
|
||||
auto sourceTextureId = _copyFbo->takeTexture();
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, sourceTextureId);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glBindVertexArray(_vao);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
glDeleteTextures(1, &sourceTextureId);
|
||||
|
||||
_copyFbo->bind();
|
||||
_copyFbo->release();
|
||||
|
||||
_fboMutex.unlock();
|
||||
}
|
||||
glFlush();
|
||||
|
|
|
@ -22,6 +22,9 @@
|
|||
|
||||
#include <TextureCache.h>
|
||||
|
||||
class QOpenGLFramebufferObject;
|
||||
class QOpenGLShaderProgram;
|
||||
|
||||
class ResourceImageItemRenderer : public QObject, public QQuickFramebufferObject::Renderer {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
@ -30,14 +33,16 @@ public:
|
|||
void synchronize(QQuickFramebufferObject* item) override;
|
||||
void render() override;
|
||||
private:
|
||||
bool _ready;
|
||||
bool _ready{ false };
|
||||
QString _url;
|
||||
bool _visible;
|
||||
bool _visible{ false };
|
||||
|
||||
NetworkTexturePointer _networkTexture;
|
||||
QQuickWindow* _window;
|
||||
QQuickWindow* _window{ nullptr };
|
||||
QMutex _fboMutex;
|
||||
uint32_t _vao{ 0 };
|
||||
QOpenGLFramebufferObject* _copyFbo { nullptr };
|
||||
QOpenGLShaderProgram* _shader{ nullptr };
|
||||
GLsync _fenceSync { 0 };
|
||||
QTimer _updateTimer;
|
||||
public slots:
|
||||
|
|
|
@ -41,7 +41,6 @@
|
|||
#include "MainWindow.h"
|
||||
#include "Snapshot.h"
|
||||
#include "SnapshotUploader.h"
|
||||
#include "ToneMappingEffect.h"
|
||||
|
||||
// filename format: hifi-snap-by-%username%-on-%date%_%time%_@-%location%.jpg
|
||||
// %1 <= username, %2 <= date and time, %3 <= current location
|
||||
|
|
|
@ -1740,7 +1740,9 @@ QVector<QUuid> Overlays::findOverlays(const glm::vec3& center, float radius) {
|
|||
* parented to if <code>parentID</code> is set. Use 65535 or -1 to parent to the parent's position and orientation rather
|
||||
* than a joint.
|
||||
*
|
||||
* @property {string} url - The URL of the FBX or OBJ model used for the overlay.
|
||||
* @property {string} url - The URL of the glTF, FBX, or OBJ model used for the overlay. glTF models may be in JSON or binary
|
||||
* format (".gltf" or ".glb" URLs respectively). Baked models' URLs have ".baked" before the file type. Model files may
|
||||
* also be compressed in GZ format, in which case the URL ends in ".gz".
|
||||
* @property {number} loadPriority=0.0 - The priority for loading and displaying the overlay. Overlays with higher values load
|
||||
* first. <em>Currently not used.</em>
|
||||
* @property {Object.<string, string>|string} textures - Texture name, URL pairs used when rendering the model in place of the
|
||||
|
|
|
@ -9,18 +9,10 @@
|
|||
#include "QmlOverlay.h"
|
||||
|
||||
#include <QQuickItem>
|
||||
#include <QThread>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
#include <GeometryCache.h>
|
||||
#include <GLMHelpers.h>
|
||||
#include <OffscreenUi.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <TextureCache.h>
|
||||
#include <ViewFrustum.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "text/FontFamilies.h"
|
||||
|
||||
QmlOverlay::QmlOverlay(const QUrl& url) {
|
||||
buildQmlElement(url);
|
||||
|
|
|
@ -9,10 +9,6 @@
|
|||
#ifndef hifi_QmlOverlay_h
|
||||
#define hifi_QmlOverlay_h
|
||||
|
||||
#include <QString>
|
||||
#include <memory>
|
||||
|
||||
#include <SharedUtil.h>
|
||||
#include "Overlay2D.h"
|
||||
|
||||
class QQuickItem;
|
||||
|
@ -33,7 +29,7 @@ private:
|
|||
Q_INVOKABLE void buildQmlElement(const QUrl& url);
|
||||
|
||||
protected:
|
||||
QQuickItem* _qmlElement{ nullptr };
|
||||
QQuickItem* _qmlElement { nullptr };
|
||||
};
|
||||
|
||||
#endif // hifi_QmlOverlay_h
|
||||
|
|
|
@ -11,18 +11,9 @@
|
|||
#include "TextOverlay.h"
|
||||
|
||||
#include <QQuickItem>
|
||||
#include <QFontMetrics>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
#include <GeometryCache.h>
|
||||
#include <GLMHelpers.h>
|
||||
#include <OffscreenUi.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <TextureCache.h>
|
||||
#include <ViewFrustum.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "text/FontFamilies.h"
|
||||
#include "FontFamilies.h"
|
||||
|
||||
QString const TextOverlay::TYPE = "text";
|
||||
QUrl const TextOverlay::URL(QString("hifi/overlays/TextOverlay.qml"));
|
||||
|
@ -44,7 +35,7 @@ QSizeF TextOverlay::textSize(const QString& text) const {
|
|||
++lines;
|
||||
}
|
||||
}
|
||||
QFont font(SANS_FONT_FAMILY);
|
||||
QFont font(ROBOTO_FONT_FAMILY);
|
||||
font.setPixelSize(18);
|
||||
QFontMetrics fm(font);
|
||||
QSizeF result = QSizeF(fm.width(text), 18 * lines);
|
||||
|
|
|
@ -20,6 +20,8 @@ set(src_files
|
|||
src/DownloadInterface.m
|
||||
src/DownloadDomainContent.h
|
||||
src/DownloadDomainContent.m
|
||||
src/DownloadLauncher.h
|
||||
src/DownloadLauncher.m
|
||||
src/DownloadScripts.h
|
||||
src/DownloadScripts.m
|
||||
src/CredentialsRequest.h
|
||||
|
@ -34,6 +36,8 @@ set(src_files
|
|||
src/Interface.m
|
||||
src/ErrorViewController.h
|
||||
src/ErrorViewController.m
|
||||
src/LauncherCommandlineArgs.h
|
||||
src/LauncherCommandlineArgs.m
|
||||
src/Settings.h
|
||||
src/Settings.m
|
||||
src/LaunchInterface.h
|
||||
|
@ -47,6 +51,9 @@ set(src_files
|
|||
nib/ProcessScreen.xib
|
||||
nib/DisplayNameScreen.xib)
|
||||
|
||||
set(updater_src_files
|
||||
src/updater/main.m)
|
||||
|
||||
set(APP_NAME "HQ Launcher")
|
||||
|
||||
set(CMAKE_C_FLAGS "-x objective-c")
|
||||
|
@ -72,6 +79,7 @@ endfunction()
|
|||
set_packaging_parameters()
|
||||
|
||||
add_executable(${PROJECT_NAME} MACOSX_BUNDLE ${src_files})
|
||||
add_executable("updater" ${updater_src_files})
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME ${APP_NAME}
|
||||
MACOSX_BUNDLE_BUNDLE_NAME ${APP_NAME})
|
||||
set_from_env(LAUNCHER_HMAC_SECRET LAUNCHER_HMAC_SECRET "")
|
||||
|
@ -102,6 +110,10 @@ add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD
|
|||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_SOURCE_DIR}/images "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${APP_NAME}.app/Contents/Resources/")
|
||||
|
||||
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
||||
COMMAND updater
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/updater" "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${APP_NAME}.app/Contents/Resources/")
|
||||
|
||||
install(
|
||||
TARGETS HQLauncher
|
||||
BUNDLE DESTINATION "."
|
||||
|
|
8
launchers/darwin/src/DownloadLauncher.h
Normal file
8
launchers/darwin/src/DownloadLauncher.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface DownloadLauncher : NSObject<NSURLSessionDataDelegate, NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLDownloadDelegate> {
|
||||
}
|
||||
|
||||
- (void) downloadLauncher:(NSString*) launcherUrl;
|
||||
|
||||
@end
|
68
launchers/darwin/src/DownloadLauncher.m
Normal file
68
launchers/darwin/src/DownloadLauncher.m
Normal file
|
@ -0,0 +1,68 @@
|
|||
#import "DownloadLauncher.h"
|
||||
#import "Launcher.h"
|
||||
|
||||
|
||||
@implementation DownloadLauncher
|
||||
|
||||
- (void) downloadLauncher:(NSString*)launcherUrl {
|
||||
NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:launcherUrl]
|
||||
cachePolicy:NSURLRequestUseProtocolCachePolicy
|
||||
timeoutInterval:60.0];
|
||||
|
||||
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||
NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate: self delegateQueue: [NSOperationQueue mainQueue]];
|
||||
NSURLSessionDownloadTask *downloadTask = [defaultSession downloadTaskWithRequest:request];
|
||||
[downloadTask resume];
|
||||
}
|
||||
|
||||
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
|
||||
CGFloat prog = (float)totalBytesWritten/totalBytesExpectedToWrite;
|
||||
NSLog(@"Launcher downloaded %f", (100.0*prog));
|
||||
|
||||
}
|
||||
|
||||
-(void)URLSession:(NSURLSession*)session downloadTask:(NSURLSessionDownloadTask*)downloadTask didFinishDownloadingToURL:(NSURL*)location {
|
||||
NSLog(@"Did finish downloading to url");
|
||||
NSError* error = nil;
|
||||
NSFileManager* fileManager = [NSFileManager defaultManager];
|
||||
NSString* destinationFileName = downloadTask.originalRequest.URL.lastPathComponent;
|
||||
NSString* finalFilePath = [[[Launcher sharedLauncher] getDownloadPathForContentAndScripts] stringByAppendingPathComponent:destinationFileName];
|
||||
NSURL *destinationURL = [NSURL URLWithString: [finalFilePath stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]] relativeToURL: [NSURL URLWithString:@"file://"]];
|
||||
NSLog(@"desintation %@", destinationURL);
|
||||
if([fileManager fileExistsAtPath:[destinationURL path]]) {
|
||||
[fileManager removeItemAtURL:destinationURL error:nil];
|
||||
}
|
||||
|
||||
NSLog(@"location: %@", location.path);
|
||||
NSLog(@"destination: %@", destinationURL);
|
||||
BOOL success = [fileManager moveItemAtURL:location toURL:destinationURL error:&error];
|
||||
|
||||
|
||||
NSLog(success ? @"TRUE" : @"FALSE");
|
||||
Launcher* sharedLauncher = [Launcher sharedLauncher];
|
||||
|
||||
if (error) {
|
||||
NSLog(@"Download Launcher: failed to move file to destintation -> error: %@", error);
|
||||
[sharedLauncher displayErrorPage];
|
||||
return;
|
||||
}
|
||||
NSLog(@"extracting Launcher file");
|
||||
BOOL extractionSuccessful = [sharedLauncher extractZipFileAtDestination:[sharedLauncher getDownloadPathForContentAndScripts] :[[sharedLauncher getDownloadPathForContentAndScripts] stringByAppendingString:destinationFileName]];
|
||||
|
||||
if (!extractionSuccessful) {
|
||||
[sharedLauncher displayErrorPage];
|
||||
return;
|
||||
}
|
||||
NSLog(@"finished extracting Launcher file");
|
||||
|
||||
|
||||
[[Launcher sharedLauncher] runAutoupdater];
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession*)session task:(NSURLSessionTask*)task didCompleteWithError:(NSError*)error {
|
||||
NSLog(@"completed; error: %@", error);
|
||||
if (error) {
|
||||
[[Launcher sharedLauncher] displayErrorPage];
|
||||
}
|
||||
}
|
||||
@end
|
|
@ -9,8 +9,8 @@
|
|||
NSInteger currentVersion;
|
||||
@try {
|
||||
NSString* interfaceAppPath = [[Launcher.sharedLauncher getAppPath] stringByAppendingString:@"interface.app"];
|
||||
NSError * error = nil;
|
||||
Interface * interface = [[Interface alloc] initWith:interfaceAppPath];
|
||||
NSError* error = nil;
|
||||
Interface* interface = [[Interface alloc] initWith:interfaceAppPath];
|
||||
currentVersion = [interface getVersion:&error];
|
||||
if (currentVersion == 0 && error != nil) {
|
||||
NSLog(@"can't get version from interface, falling back to settings: %@", error);
|
||||
|
@ -24,17 +24,17 @@
|
|||
}
|
||||
|
||||
- (void) requestLatestBuildInfo {
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest new];
|
||||
NSMutableURLRequest* request = [NSMutableURLRequest new];
|
||||
[request setURL:[NSURL URLWithString:@"https://thunder.highfidelity.com/builds/api/tags/latest?format=json"]];
|
||||
[request setHTTPMethod:@"GET"];
|
||||
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
|
||||
|
||||
// We're using an ephermeral session here to ensure the tags api response is never cached.
|
||||
NSURLSession * session = [NSURLSession sessionWithConfiguration:NSURLSessionConfiguration.ephemeralSessionConfiguration];
|
||||
NSURLSession* session = [NSURLSession sessionWithConfiguration:NSURLSessionConfiguration.ephemeralSessionConfiguration];
|
||||
NSURLSessionDataTask* dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
||||
NSLog(@"Latest Build Request error: %@", error);
|
||||
NSLog(@"Latest Build Request Data: %@", data);
|
||||
NSHTTPURLResponse *ne = (NSHTTPURLResponse *)response;
|
||||
NSHTTPURLResponse* ne = (NSHTTPURLResponse *)response;
|
||||
NSLog(@"Latest Build Request Response: %ld", [ne statusCode]);
|
||||
Launcher* sharedLauncher = [Launcher sharedLauncher];
|
||||
|
||||
|
@ -47,9 +47,9 @@
|
|||
NSMutableData* webData = [NSMutableData data];
|
||||
[webData appendData:data];
|
||||
NSString* jsonString = [[NSString alloc] initWithBytes: [webData mutableBytes] length:[data length] encoding:NSUTF8StringEncoding];
|
||||
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSData* jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSLog(@"Latest Build Request -> json string: %@", jsonString);
|
||||
NSError *jsonError = nil;
|
||||
NSError* jsonError = nil;
|
||||
id json = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&jsonError];
|
||||
|
||||
if (jsonError) {
|
||||
|
@ -57,10 +57,12 @@
|
|||
}
|
||||
|
||||
NSFileManager* fileManager = [NSFileManager defaultManager];
|
||||
NSArray *values = [json valueForKey:@"results"];
|
||||
NSDictionary *value = [values objectAtIndex:0];
|
||||
|
||||
NSArray* values = [json valueForKey:@"results"];
|
||||
NSDictionary* launcherValues = [json valueForKey:@"launcher"];
|
||||
NSDictionary* value = [values objectAtIndex:0];
|
||||
|
||||
NSString* launcherVersion = [launcherValues valueForKey:@"version"];
|
||||
NSString* launcherUrl = [[launcherValues valueForKey:@"mac"] valueForKey:@"url"];
|
||||
NSString* buildNumber = [value valueForKey:@"latest_version"];
|
||||
NSDictionary* installers = [value objectForKey:@"installers"];
|
||||
NSDictionary* macInstallerObject = [installers objectForKey:@"mac"];
|
||||
|
@ -71,16 +73,22 @@
|
|||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
|
||||
NSInteger currentVersion = [self getCurrentVersion];
|
||||
NSInteger currentLauncherVersion = atoi(LAUNCHER_BUILD_VERSION);
|
||||
NSLog(@"Latest Build Request -> current launcher version %ld", currentLauncherVersion);
|
||||
NSLog(@"Latest Build Request -> latest launcher version %ld", launcherVersion.integerValue);
|
||||
NSLog(@"Latest Build Request -> launcher url %@", launcherUrl);
|
||||
NSLog(@"Latest Build Request -> does build directory exist: %@", appDirectoryExist ? @"TRUE" : @"FALSE");
|
||||
NSLog(@"Latest Build Request -> current version: %ld", currentVersion);
|
||||
NSLog(@"Latest Build Request -> latest version: %ld", buildNumber.integerValue);
|
||||
NSLog(@"Latest Build Request -> mac url: %@", macInstallerUrl);
|
||||
BOOL latestVersionAvailable = (currentVersion != buildNumber.integerValue);
|
||||
BOOL latestLauncherVersionAvailable = (currentLauncherVersion != launcherVersion.integerValue);
|
||||
[[Settings sharedSettings] buildVersion:buildNumber.integerValue];
|
||||
|
||||
BOOL shouldDownloadInterface = (latestVersionAvailable || !appDirectoryExist);
|
||||
NSLog(@"Latest Build Request -> SHOULD DOWNLOAD: %@", shouldDownloadInterface ? @"TRUE" : @"FALSE");
|
||||
[sharedLauncher shouldDownloadLatestBuild:shouldDownloadInterface :macInstallerUrl];
|
||||
[sharedLauncher shouldDownloadLatestBuild:shouldDownloadInterface :macInstallerUrl
|
||||
:latestLauncherVersionAvailable :launcherUrl];
|
||||
});
|
||||
}];
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#import "DownloadInterface.h"
|
||||
#import "CredentialsRequest.h"
|
||||
#import "DownloadDomainContent.h"
|
||||
#import "DownloadLauncher.h"
|
||||
#import "LatestBuildRequest.h"
|
||||
#import "OrganizationRequest.h"
|
||||
#import "DownloadScripts.h"
|
||||
|
@ -44,6 +45,7 @@ struct LatestBuildInfo {
|
|||
@property (nonatomic, retain) DownloadInterface* downloadInterface;
|
||||
@property (nonatomic, retain) CredentialsRequest* credentialsRequest;
|
||||
@property (nonatomic, retain) DownloadDomainContent* downloadDomainContent;
|
||||
@property (nonatomic, retain) DownloadLauncher* downloadLauncher;
|
||||
@property (nonatomic, retain) DownloadScripts* downloadScripts;
|
||||
@property (nonatomic, retain) LatestBuildRequest* latestBuildRequest;
|
||||
@property (nonatomic, retain) OrganizationRequest* organizationRequest;
|
||||
|
@ -74,11 +76,12 @@ struct LatestBuildInfo {
|
|||
- (void) showLoginScreen;
|
||||
- (void) restart;
|
||||
- (NSString*) getLauncherPath;
|
||||
- (void) runAutoupdater;
|
||||
- (ProcessState) currentProccessState;
|
||||
- (void) setCurrentProcessState:(ProcessState) aProcessState;
|
||||
- (void) setLoginErrorState:(LoginError) aLoginError;
|
||||
- (LoginError) getLoginErrorState;
|
||||
- (void) shouldDownloadLatestBuild:(BOOL) shouldDownload :(NSString*) downloadUrl;
|
||||
- (void) shouldDownloadLatestBuild:(BOOL) shouldDownload :(NSString*) downloadUrl :(BOOL) newLauncherAvailable :(NSString*) launcherUrl;
|
||||
- (void) interfaceFinishedDownloading;
|
||||
- (NSString*) getDownloadPathForContentAndScripts;
|
||||
- (void) launchInterface;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#import "SplashScreen.h"
|
||||
#import "LoginScreen.h"
|
||||
#import "DisplayNameScreen.h"
|
||||
#import "LauncherCommandlineArgs.h"
|
||||
#import "ProcessScreen.h"
|
||||
#import "ErrorViewController.h"
|
||||
#import "Settings.h"
|
||||
|
@ -32,6 +33,7 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
|
|||
self.username = [[NSString alloc] initWithString:@"Default Property Value"];
|
||||
self.downloadInterface = [DownloadInterface alloc];
|
||||
self.downloadDomainContent = [DownloadDomainContent alloc];
|
||||
self.downloadLauncher = [DownloadLauncher alloc];
|
||||
self.credentialsRequest = [CredentialsRequest alloc];
|
||||
self.latestBuildRequest = [LatestBuildRequest alloc];
|
||||
self.organizationRequest = [OrganizationRequest alloc];
|
||||
|
@ -362,28 +364,44 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
|
|||
[[[[NSApplication sharedApplication] windows] objectAtIndex:0] setContentViewController: loginScreen];
|
||||
}
|
||||
|
||||
- (void) shouldDownloadLatestBuild:(BOOL) shouldDownload :(NSString*) downloadUrl
|
||||
- (void) shouldDownloadLatestBuild:(BOOL) shouldDownload :(NSString*) downloadUrl :(BOOL) newLauncherAvailable :(NSString*) launcherUrl
|
||||
{
|
||||
self.shouldDownloadInterface = shouldDownload;
|
||||
self.interfaceDownloadUrl = downloadUrl;
|
||||
self.latestBuildRequestFinished = TRUE;
|
||||
if ([self isLoadedIn]) {
|
||||
Launcher* sharedLauncher = [Launcher sharedLauncher];
|
||||
[sharedLauncher setCurrentProcessState:CHECKING_UPDATE];
|
||||
if (shouldDownload) {
|
||||
ProcessScreen* processScreen = [[ProcessScreen alloc] initWithNibName:@"ProcessScreen" bundle:nil];
|
||||
[[[[NSApplication sharedApplication] windows] objectAtIndex:0] setContentViewController: processScreen];
|
||||
[self startUpdateProgressIndicatorTimer];
|
||||
[self.downloadInterface downloadInterface: downloadUrl];
|
||||
return;
|
||||
}
|
||||
[self interfaceFinishedDownloading];
|
||||
NSDictionary* launcherArguments = [LauncherCommandlineArgs arguments];
|
||||
if (newLauncherAvailable && ![launcherArguments valueForKey: @"--noUpdate"]) {
|
||||
[self.downloadLauncher downloadLauncher: launcherUrl];
|
||||
} else {
|
||||
[[NSApplication sharedApplication] activateIgnoringOtherApps:TRUE];
|
||||
[self showLoginScreen];
|
||||
self.shouldDownloadInterface = shouldDownload;
|
||||
self.interfaceDownloadUrl = downloadUrl;
|
||||
self.latestBuildRequestFinished = TRUE;
|
||||
if ([self isLoadedIn]) {
|
||||
Launcher* sharedLauncher = [Launcher sharedLauncher];
|
||||
[sharedLauncher setCurrentProcessState:CHECKING_UPDATE];
|
||||
if (shouldDownload) {
|
||||
ProcessScreen* processScreen = [[ProcessScreen alloc] initWithNibName:@"ProcessScreen" bundle:nil];
|
||||
[[[[NSApplication sharedApplication] windows] objectAtIndex:0] setContentViewController: processScreen];
|
||||
[self startUpdateProgressIndicatorTimer];
|
||||
[self.downloadInterface downloadInterface: downloadUrl];
|
||||
return;
|
||||
}
|
||||
[self interfaceFinishedDownloading];
|
||||
} else {
|
||||
[[NSApplication sharedApplication] activateIgnoringOtherApps:TRUE];
|
||||
[self showLoginScreen];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-(void)runAutoupdater
|
||||
{
|
||||
NSTask* task = [[NSTask alloc] init];
|
||||
NSString* newLauncher = [[[Launcher sharedLauncher] getDownloadPathForContentAndScripts] stringByAppendingPathComponent: @"HQ Launcher.app"];
|
||||
task.launchPath = [newLauncher stringByAppendingString:@"/Contents/Resources/updater"];
|
||||
task.arguments = @[[[NSBundle mainBundle] bundlePath], newLauncher];
|
||||
[task launch];
|
||||
|
||||
[NSApp terminate:self];
|
||||
}
|
||||
|
||||
-(void)onSplashScreenTimerFinished:(NSTimer *)timer
|
||||
{
|
||||
[self.latestBuildRequest requestLatestBuildInfo];
|
||||
|
|
8
launchers/darwin/src/LauncherCommandlineArgs.h
Normal file
8
launchers/darwin/src/LauncherCommandlineArgs.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface LauncherCommandlineArgs : NSObject {
|
||||
}
|
||||
+(NSDictionary*) arguments;
|
||||
@end
|
||||
|
29
launchers/darwin/src/LauncherCommandlineArgs.m
Normal file
29
launchers/darwin/src/LauncherCommandlineArgs.m
Normal file
|
@ -0,0 +1,29 @@
|
|||
#import "LauncherCommandlineArgs.h"
|
||||
|
||||
@implementation LauncherCommandlineArgs
|
||||
+(NSDictionary*) arguments {
|
||||
|
||||
NSArray* arguments = [[NSProcessInfo processInfo] arguments];
|
||||
if (arguments.count < 2)
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableDictionary* argsDict = [[NSMutableDictionary alloc] init];
|
||||
NSMutableArray* args = [arguments mutableCopy];
|
||||
|
||||
for (NSString* arg in args)
|
||||
{
|
||||
if ([arg rangeOfString:@"="].location != NSNotFound && [arg rangeOfString:@"--"].location != NSNotFound) {
|
||||
NSArray* components = [arg componentsSeparatedByString:@"="];
|
||||
NSString* key = [components objectAtIndex:0];
|
||||
NSString* value = [components objectAtIndex:1];
|
||||
[argsDict setObject:value forKey:key];
|
||||
} else if ([arg rangeOfString:@"--"].location != NSNotFound) {
|
||||
[argsDict setObject:@TRUE forKey:arg];
|
||||
}
|
||||
}
|
||||
NSLog(@"AGS: %@", argsDict);
|
||||
return argsDict;
|
||||
}
|
||||
@end
|
|
@ -1,5 +1,6 @@
|
|||
#import "Launcher.h"
|
||||
#import "Settings.h"
|
||||
#import "LauncherCommandlineArgs.h"
|
||||
|
||||
void redirectLogToDocuments()
|
||||
{
|
||||
|
|
35
launchers/darwin/src/updater/main.m
Normal file
35
launchers/darwin/src/updater/main.m
Normal file
|
@ -0,0 +1,35 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface UpdaterHelper : NSObject
|
||||
+(NSURL*) NSStringToNSURL: (NSString*) path;
|
||||
@end
|
||||
|
||||
@implementation UpdaterHelper
|
||||
+(NSURL*) NSStringToNSURL: (NSString*) path
|
||||
{
|
||||
return [NSURL URLWithString: [path stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]] relativeToURL: [NSURL URLWithString:@"file://"]];
|
||||
}
|
||||
@end
|
||||
|
||||
int main(int argc, char const* argv[]) {
|
||||
if (argc < 3) {
|
||||
NSLog(@"Error: wrong number of arguments");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int index = 0; index < argc; index++) {
|
||||
NSLog(@"argv at index %d = %s", index, argv[index]);
|
||||
}
|
||||
NSString* oldLauncher = [NSString stringWithUTF8String:argv[1]];
|
||||
NSString* newLauncher = [NSString stringWithUTF8String:argv[2]];
|
||||
NSURL* destinationUrl = [UpdaterHelper NSStringToNSURL:newLauncher];
|
||||
NSFileManager* fileManager = [NSFileManager defaultManager];
|
||||
[fileManager replaceItemAtURL:[UpdaterHelper NSStringToNSURL:oldLauncher] withItemAtURL:[UpdaterHelper NSStringToNSURL:newLauncher] backupItemName:nil options:NSFileManagerItemReplacementUsingNewMetadataOnly resultingItemURL:&destinationUrl error:nil];
|
||||
NSWorkspace* workspace = [NSWorkspace sharedWorkspace];
|
||||
NSURL* applicationURL = [UpdaterHelper NSStringToNSURL: [oldLauncher stringByAppendingString: @"/Contents/MacOS/HQ Launcher"]];
|
||||
NSArray* arguments =@[];
|
||||
NSLog(@"Launcher agruments: %@", arguments);
|
||||
[workspace launchApplicationAtURL:applicationURL options:NSWorkspaceLaunchNewInstance configuration:[NSDictionary dictionaryWithObject:arguments forKey:NSWorkspaceLaunchConfigurationArguments] error:nil];
|
||||
return 0;
|
||||
}
|
|
@ -42,8 +42,7 @@ BOOL CLauncherApp::InitInstance() {
|
|||
bool uninstalling = false;
|
||||
bool restarting = false;
|
||||
bool noUpdate = false;
|
||||
bool continueUpdating = false;
|
||||
bool skipSplash = false;
|
||||
LauncherManager::ContinueActionOnStart continueAction = LauncherManager::ContinueActionOnStart::ContinueNone;
|
||||
if (iNumOfArgs > 1) {
|
||||
for (int i = 1; i < iNumOfArgs; i++) {
|
||||
CString curArg = CString(pArgs[i]);
|
||||
|
@ -53,10 +52,10 @@ BOOL CLauncherApp::InitInstance() {
|
|||
restarting = true;
|
||||
} else if (curArg.Compare(_T("--noUpdate")) == 0) {
|
||||
noUpdate = true;
|
||||
} else if (curArg.Compare(_T("--continueUpdating")) == 0) {
|
||||
continueUpdating = true;
|
||||
} else if (curArg.Compare(_T("--skipSplash")) == 0) {
|
||||
skipSplash = true;
|
||||
} else if (curArg.Compare(_T("--continueAction")) == 0) {
|
||||
if (i + 1 < iNumOfArgs) {
|
||||
continueAction = LauncherManager::getContinueActionFromParam(pArgs[i + 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -71,11 +70,9 @@ BOOL CLauncherApp::InitInstance() {
|
|||
if (uninstalling) {
|
||||
_manager.uninstall();
|
||||
} else {
|
||||
_manager.init(!noUpdate, continueUpdating, skipSplash);
|
||||
_manager.init(!noUpdate, continueAction);
|
||||
}
|
||||
if (!_manager.hasFailed() && !_manager.installLauncher()) {
|
||||
return FALSE;
|
||||
}
|
||||
_manager.tryToInstallLauncher();
|
||||
installFont(IDR_FONT_REGULAR);
|
||||
installFont(IDR_FONT_BOLD);
|
||||
CWinApp::InitInstance();
|
||||
|
|
|
@ -357,7 +357,7 @@ void CLauncherDlg::drawVoxel(CHwndRenderTarget* pRenderTarget) {
|
|||
}
|
||||
|
||||
void CLauncherDlg::drawProgress(CHwndRenderTarget* pRenderTarget, float progress, const D2D1::ColorF& color) {
|
||||
auto size = pRenderTarget->GetPixelSize();
|
||||
auto size = pRenderTarget->GetSize();
|
||||
if (progress == 0.0f) {
|
||||
return;
|
||||
} else {
|
||||
|
@ -656,7 +656,6 @@ BOOL CLauncherDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
|
|||
|
||||
void CLauncherDlg::OnTimer(UINT_PTR nIDEvent) {
|
||||
|
||||
|
||||
if (theApp._manager.hasFailed() && _drawStep != DrawStep::DrawError) {
|
||||
theApp._manager.saveErrorLog();
|
||||
prepareProcess(DrawStep::DrawError);
|
||||
|
@ -693,22 +692,26 @@ void CLauncherDlg::OnTimer(UINT_PTR nIDEvent) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
LauncherManager::ContinueActionOnStart continueAction = theApp._manager.getContinueAction();
|
||||
if (_showSplash) {
|
||||
if (_splashStep == 0) {
|
||||
if (theApp._manager.needsUninstall()) {
|
||||
theApp._manager.addToLog(_T("Waiting to uninstall"));
|
||||
setDrawDialog(DrawStep::DrawProcessUninstall);
|
||||
} else if (theApp._manager.shouldContinueUpdating()) {
|
||||
_splashStep = SPLASH_DURATION;
|
||||
} else if (continueAction == LauncherManager::ContinueActionOnStart::ContinueUpdate) {
|
||||
setDrawDialog(DrawStep::DrawProcessUpdate);
|
||||
theApp._manager.updateProgress(LauncherManager::ProcessType::Uninstall, 0.0f);
|
||||
} else if (continueAction == LauncherManager::ContinueActionOnStart::ContinueLogIn) {
|
||||
_splashStep = SPLASH_DURATION;
|
||||
} else if (continueAction == LauncherManager::ContinueActionOnStart::ContinueFinish) {
|
||||
theApp._manager.updateProgress(LauncherManager::ProcessType::Uninstall, 1.0f);
|
||||
setDrawDialog(DrawStep::DrawProcessFinishUpdate);
|
||||
_splashStep = SPLASH_DURATION;
|
||||
_showSplash = false;
|
||||
} else {
|
||||
if (theApp._manager.shouldSkipSplashScreen()) {
|
||||
_splashStep = SPLASH_DURATION;
|
||||
} else {
|
||||
theApp._manager.addToLog(_T("Start splash screen"));
|
||||
setDrawDialog(DrawStep::DrawLogo);
|
||||
}
|
||||
theApp._manager.addToLog(_T("Start splash screen"));
|
||||
setDrawDialog(DrawStep::DrawLogo);
|
||||
}
|
||||
} else if (_splashStep > SPLASH_DURATION && !theApp._manager.needsToWait()) {
|
||||
_showSplash = false;
|
||||
|
@ -753,6 +756,9 @@ void CLauncherDlg::OnTimer(UINT_PTR nIDEvent) {
|
|||
_applicationWND = theApp._manager.launchApplication();
|
||||
}
|
||||
}
|
||||
if (theApp._manager.needsToSelfInstall()) {
|
||||
theApp._manager.tryToInstallLauncher(TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
void CLauncherDlg::setVerticalElement(CWnd* element, int verticalOffset, int heightOffset, bool fromMainWindowBottom) {
|
||||
|
|
|
@ -16,27 +16,55 @@
|
|||
|
||||
|
||||
LauncherManager::LauncherManager() {
|
||||
int tokenPos = 0;
|
||||
_launcherVersion = CString(LAUNCHER_BUILD_VERSION).Tokenize(_T("-"), tokenPos);
|
||||
}
|
||||
|
||||
LauncherManager::~LauncherManager() {
|
||||
}
|
||||
|
||||
void LauncherManager::init(BOOL allowUpdate, BOOL continueUpdating, BOOL skipSplashScreen) {
|
||||
void LauncherManager::init(BOOL allowUpdate, ContinueActionOnStart continueAction) {
|
||||
initLog();
|
||||
int tokenPos = 0;
|
||||
_updateLauncherAllowed = allowUpdate;
|
||||
_continueUpdating = continueUpdating;
|
||||
_skipSplashScreen = skipSplashScreen;
|
||||
_shouldWait = !skipSplashScreen;
|
||||
if (_continueUpdating) {
|
||||
_continueAction = continueAction;
|
||||
CString msg;
|
||||
msg.Format(_T("Start Screen: %s"), getContinueActionParam(continueAction));
|
||||
addToLog(msg);
|
||||
_shouldWait = _continueAction == ContinueActionOnStart::ContinueNone;
|
||||
if (_continueAction == ContinueActionOnStart::ContinueUpdate) {
|
||||
_progressOffset = CONTINUE_UPDATING_GLOBAL_OFFSET;
|
||||
}
|
||||
_launcherVersion = CString(LAUNCHER_BUILD_VERSION).Tokenize(_T("-"), tokenPos);
|
||||
addToLog(_T("Launcher is running version: " + _launcherVersion));
|
||||
addToLog(_T("Getting most recent builds"));
|
||||
getMostRecentBuilds(_latestLauncherURL, _latestLauncherVersion, _latestApplicationURL, _latestVersion);
|
||||
}
|
||||
|
||||
CString LauncherManager::getContinueActionParam(LauncherManager::ContinueActionOnStart continueAction) {
|
||||
switch (continueAction) {
|
||||
case LauncherManager::ContinueActionOnStart::ContinueNone:
|
||||
return _T("");
|
||||
case LauncherManager::ContinueActionOnStart::ContinueLogIn:
|
||||
return _T("LogIn");
|
||||
case LauncherManager::ContinueActionOnStart::ContinueUpdate:
|
||||
return _T("Update");
|
||||
case LauncherManager::ContinueActionOnStart::ContinueFinish:
|
||||
return _T("Finish");
|
||||
default:
|
||||
return _T("");
|
||||
}
|
||||
}
|
||||
|
||||
LauncherManager::ContinueActionOnStart LauncherManager::getContinueActionFromParam(const CString& param) {
|
||||
if (param.Compare(_T("LogIn")) == 0) {
|
||||
return ContinueActionOnStart::ContinueLogIn;
|
||||
} else if (param.Compare(_T("Update")) == 0) {
|
||||
return ContinueActionOnStart::ContinueUpdate;
|
||||
} else if (param.Compare(_T("Finish")) == 0) {
|
||||
return ContinueActionOnStart::ContinueFinish;
|
||||
} else {
|
||||
return ContinueActionOnStart::ContinueNone;
|
||||
}
|
||||
}
|
||||
BOOL LauncherManager::initLog() {
|
||||
CString logPath;
|
||||
auto result = getAndCreatePaths(PathType::Launcher_Directory, logPath);
|
||||
|
@ -80,7 +108,7 @@ void LauncherManager::saveErrorLog() {
|
|||
}
|
||||
}
|
||||
|
||||
BOOL LauncherManager::installLauncher() {
|
||||
void LauncherManager::tryToInstallLauncher(BOOL retry) {
|
||||
CString appPath;
|
||||
BOOL result = getAndCreatePaths(PathType::Running_Path, appPath);
|
||||
if (!result) {
|
||||
|
@ -98,26 +126,49 @@ BOOL LauncherManager::installLauncher() {
|
|||
if (!_shouldUninstall) {
|
||||
// The installer is not running on the desired location and has to be installed
|
||||
// Kill of running before self-copy
|
||||
addToLog(_T("Installing Launcher."));
|
||||
addToLog(_T("Trying to install launcher."));
|
||||
int launcherPID = -1;
|
||||
if (LauncherUtils::isProcessRunning(LAUNCHER_EXE_FILENAME, launcherPID)) {
|
||||
if (!LauncherUtils::shutdownProcess(launcherPID, 0)) {
|
||||
addToLog(_T("Error shutting down the Launcher"));
|
||||
}
|
||||
}
|
||||
CopyFile(appPath, instalationPath, FALSE);
|
||||
const int LAUNCHER_INSTALL_RETRYS = 10;
|
||||
const int WAIT_BETWEEN_RETRYS_MS = 10;
|
||||
int installTrys = retry ? LAUNCHER_INSTALL_RETRYS : 0;
|
||||
for (int i = 0; i <= installTrys; i++) {
|
||||
_retryLauncherInstall = !CopyFile(appPath, instalationPath, FALSE);
|
||||
if (!_retryLauncherInstall) {
|
||||
addToLog(_T("Launcher installed successfully."));
|
||||
break;
|
||||
} else if (i < installTrys) {
|
||||
CString msg;
|
||||
msg.Format(_T("Installing launcher try: %d"), i);
|
||||
addToLog(msg);
|
||||
Sleep(WAIT_BETWEEN_RETRYS_MS);
|
||||
} else if (installTrys > 0) {
|
||||
addToLog(_T("Error installing launcher."));
|
||||
_retryLauncherInstall = false;
|
||||
_hasFailed = true;
|
||||
} else {
|
||||
addToLog(_T("Old launcher is still running. Install could not be completed."));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (_shouldUninstall) {
|
||||
addToLog(_T("Launching Uninstall mode."));
|
||||
CString tempPath;
|
||||
if (getAndCreatePaths(PathType::Temp_Directory, tempPath)) {
|
||||
tempPath += _T("\\HQ_uninstaller_tmp.exe");
|
||||
CopyFile(instalationPath, tempPath, false);
|
||||
LauncherUtils::launchApplication(tempPath, _T(" --uninstall"));
|
||||
exit(0);
|
||||
if (!CopyFile(instalationPath, tempPath, false)) {
|
||||
addToLog(_T("Error copying uninstaller to tmp directory."));
|
||||
_hasFailed = true;
|
||||
} else {
|
||||
LauncherUtils::launchApplication(tempPath, _T(" --uninstall"));
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL LauncherManager::restartLauncher() {
|
||||
|
@ -432,7 +483,8 @@ void LauncherManager::onMostRecentBuildsReceived(const CString& response, Launch
|
|||
addToLog(updatingMsg);
|
||||
_shouldUpdateLauncher = TRUE;
|
||||
_shouldDownloadLauncher = TRUE;
|
||||
_willContinueUpdating = isInstalled && newInterfaceVersion;
|
||||
_keepLoggingIn = !isInstalled;
|
||||
_keepUpdating = isInstalled && newInterfaceVersion;
|
||||
} else {
|
||||
if (_updateLauncherAllowed) {
|
||||
addToLog(_T("Already running most recent build. Launching interface.exe"));
|
||||
|
@ -612,11 +664,15 @@ void LauncherManager::onFileDownloaded(ProcessType type) {
|
|||
|
||||
void LauncherManager::restartNewLauncher() {
|
||||
closeLog();
|
||||
if (_willContinueUpdating) {
|
||||
LauncherUtils::launchApplication(_tempLauncherPath, _T(" --restart --noUpdate --continueUpdating"));
|
||||
} else {
|
||||
LauncherUtils::launchApplication(_tempLauncherPath, _T(" --restart --noUpdate --skipSplash"));
|
||||
ContinueActionOnStart continueAction = ContinueActionOnStart::ContinueFinish;
|
||||
if (_keepUpdating) {
|
||||
continueAction = ContinueActionOnStart::ContinueUpdate;
|
||||
} else if (_keepLoggingIn) {
|
||||
continueAction = ContinueActionOnStart::ContinueLogIn;
|
||||
}
|
||||
CStringW params;
|
||||
params.Format(_T(" --restart --noUpdate --continueAction %s"), getContinueActionParam(continueAction));
|
||||
LauncherUtils::launchApplication(_tempLauncherPath, params.GetBuffer());
|
||||
Sleep(500);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "LauncherUtils.h"
|
||||
#include "LauncherDlg.h"
|
||||
|
||||
const CString DIRECTORY_NAME_APP = _T("HQ");
|
||||
const CString DIRECTORY_NAME_DOWNLOADS = _T("downloads");
|
||||
|
@ -27,8 +28,7 @@ const float DOWNLOAD_APPLICATION_UPDATE_WEIGHT = 0.75f;
|
|||
const float EXTRACT_APPLICATION_UPDATE_WEIGHT = 0.25f;
|
||||
const float CONTINUE_UPDATING_GLOBAL_OFFSET = 0.2f;
|
||||
|
||||
class LauncherManager
|
||||
{
|
||||
class LauncherManager {
|
||||
public:
|
||||
enum PathType {
|
||||
Running_Path = 0,
|
||||
|
@ -57,22 +57,33 @@ public:
|
|||
UnzipApplication,
|
||||
Uninstall
|
||||
};
|
||||
enum ContinueActionOnStart {
|
||||
ContinueNone = 0,
|
||||
ContinueLogIn,
|
||||
ContinueUpdate,
|
||||
ContinueFinish
|
||||
};
|
||||
|
||||
LauncherManager();
|
||||
~LauncherManager();
|
||||
void init(BOOL allowUpdate, BOOL continueUpdating, BOOL skipSplashScreen);
|
||||
void init(BOOL allowUpdate, ContinueActionOnStart continueAction);
|
||||
static CString getContinueActionParam(ContinueActionOnStart continueAction);
|
||||
static ContinueActionOnStart getContinueActionFromParam(const CString& param);
|
||||
BOOL initLog();
|
||||
BOOL addToLog(const CString& line);
|
||||
void closeLog();
|
||||
void saveErrorLog();
|
||||
BOOL getAndCreatePaths(PathType type, CString& outPath);
|
||||
BOOL getInstalledVersion(const CString& path, CString& version);
|
||||
BOOL isApplicationInstalled(CString& version, CString& domain,
|
||||
BOOL isApplicationInstalled(CString& version, CString& domain,
|
||||
CString& content, bool& loggedIn);
|
||||
LauncherUtils::ResponseError getAccessTokenForCredentials(const CString& username, const CString& password);
|
||||
void getMostRecentBuilds(CString& launcherUrlOut, CString& launcherVersionOut,
|
||||
CString& interfaceUrlOut, CString& interfaceVersionOut);
|
||||
void getMostRecentBuilds(CString& launcherUrlOut,
|
||||
CString& launcherVersionOut,
|
||||
CString& interfaceUrlOut,
|
||||
CString& interfaceVersionOut);
|
||||
LauncherUtils::ResponseError readOrganizationJSON(const CString& hash);
|
||||
LauncherUtils::ResponseError readConfigJSON(CString& version, CString& domain,
|
||||
LauncherUtils::ResponseError readConfigJSON(CString& version, CString& domain,
|
||||
CString& content, bool& loggedIn);
|
||||
BOOL createConfigJSON();
|
||||
BOOL createApplicationRegistryKeys(int size);
|
||||
|
@ -81,7 +92,7 @@ public:
|
|||
BOOL deleteShortcuts();
|
||||
HWND launchApplication();
|
||||
BOOL uninstallApplication();
|
||||
BOOL installLauncher();
|
||||
void tryToInstallLauncher(BOOL retry = FALSE);
|
||||
BOOL restartLauncher();
|
||||
|
||||
// getters
|
||||
|
@ -90,7 +101,6 @@ public:
|
|||
const CString& getVersion() const { return _version; }
|
||||
BOOL shouldShutDown() const { return _shouldShutdown; }
|
||||
BOOL shouldLaunch() const { return _shouldLaunch; }
|
||||
BOOL shouldSkipSplashScreen() const { return _skipSplashScreen; }
|
||||
BOOL needsUpdate() const { return _shouldUpdate; }
|
||||
BOOL needsSelfUpdate() const { return _shouldUpdateLauncher; }
|
||||
BOOL needsSelfDownload() const { return _shouldDownloadLauncher; }
|
||||
|
@ -98,14 +108,18 @@ public:
|
|||
BOOL needsInstall() const { return _shouldInstall; }
|
||||
BOOL needsToWait() const { return _shouldWait; }
|
||||
BOOL needsRestartNewLauncher() const { return _shouldRestartNewLauncher; }
|
||||
BOOL shouldContinueUpdating() const { return _continueUpdating; }
|
||||
BOOL willContinueUpdating() const { return _willContinueUpdating; }
|
||||
BOOL needsToSelfInstall() const { return _retryLauncherInstall; }
|
||||
BOOL willContinueUpdating() const { return _keepUpdating; }
|
||||
ContinueActionOnStart getContinueAction() { return _continueAction; }
|
||||
void setDisplayName(const CString& displayName) { _displayName = displayName; }
|
||||
bool isLoggedIn() const { return _loggedIn; }
|
||||
bool hasFailed() const { return _hasFailed; }
|
||||
void setFailed(bool hasFailed) { _hasFailed = hasFailed; }
|
||||
const CString& getLatestInterfaceURL() const { return _latestApplicationURL; }
|
||||
void uninstall() { _shouldUninstall = true; _shouldWait = false; };
|
||||
void uninstall() {
|
||||
_shouldUninstall = true;
|
||||
_shouldWait = false;
|
||||
};
|
||||
|
||||
BOOL downloadFile(ProcessType type, const CString& url, CString& localPath);
|
||||
BOOL downloadContent();
|
||||
|
@ -149,11 +163,11 @@ private:
|
|||
BOOL _shouldDownloadLauncher { FALSE };
|
||||
BOOL _updateLauncherAllowed { TRUE };
|
||||
BOOL _shouldRestartNewLauncher { FALSE };
|
||||
BOOL _continueUpdating { FALSE };
|
||||
BOOL _willContinueUpdating { FALSE };
|
||||
BOOL _skipSplashScreen { FALSE };
|
||||
BOOL _keepLoggingIn { FALSE };
|
||||
BOOL _keepUpdating { FALSE };
|
||||
BOOL _retryLauncherInstall { FALSE };
|
||||
ContinueActionOnStart _continueAction;
|
||||
float _progressOffset { 0.0f };
|
||||
float _progress { 0.0f };
|
||||
CStdioFile _logFile;
|
||||
};
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "AnimTwoBoneIK.h"
|
||||
#include "AnimSplineIK.h"
|
||||
#include "AnimPoleVectorConstraint.h"
|
||||
#include "AnimUtil.h"
|
||||
|
||||
using NodeLoaderFunc = AnimNode::Pointer (*)(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
using NodeProcessFunc = bool (*)(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
|
@ -91,6 +92,8 @@ static AnimStateMachine::InterpType stringToInterpType(const QString& str) {
|
|||
return AnimStateMachine::InterpType::SnapshotBoth;
|
||||
} else if (str == "snapshotPrev") {
|
||||
return AnimStateMachine::InterpType::SnapshotPrev;
|
||||
} else if (str == "evaluateBoth") {
|
||||
return AnimStateMachine::InterpType::EvaluateBoth;
|
||||
} else {
|
||||
return AnimStateMachine::InterpType::NumTypes;
|
||||
}
|
||||
|
@ -101,11 +104,63 @@ static AnimRandomSwitch::InterpType stringToRandomInterpType(const QString& str)
|
|||
return AnimRandomSwitch::InterpType::SnapshotBoth;
|
||||
} else if (str == "snapshotPrev") {
|
||||
return AnimRandomSwitch::InterpType::SnapshotPrev;
|
||||
} else if (str == "evaluateBoth") {
|
||||
return AnimRandomSwitch::InterpType::EvaluateBoth;
|
||||
} else {
|
||||
return AnimRandomSwitch::InterpType::NumTypes;
|
||||
}
|
||||
}
|
||||
|
||||
static EasingType stringToEasingType(const QString& str) {
|
||||
if (str == "linear") {
|
||||
return EasingType_Linear;
|
||||
} else if (str == "easeInSine") {
|
||||
return EasingType_EaseInSine;
|
||||
} else if (str == "easeOutSine") {
|
||||
return EasingType_EaseOutSine;
|
||||
} else if (str == "easeInOutSine") {
|
||||
return EasingType_EaseInOutSine;
|
||||
} else if (str == "easeInQuad") {
|
||||
return EasingType_EaseInQuad;
|
||||
} else if (str == "easeOutQuad") {
|
||||
return EasingType_EaseOutQuad;
|
||||
} else if (str == "easeInOutQuad") {
|
||||
return EasingType_EaseInOutQuad;
|
||||
} else if (str == "easeInCubic") {
|
||||
return EasingType_EaseInCubic;
|
||||
} else if (str == "easeOutCubic") {
|
||||
return EasingType_EaseOutCubic;
|
||||
} else if (str == "easeInOutCubic") {
|
||||
return EasingType_EaseInOutCubic;
|
||||
} else if (str == "easeInQuart") {
|
||||
return EasingType_EaseInQuart;
|
||||
} else if (str == "easeOutQuart") {
|
||||
return EasingType_EaseOutQuart;
|
||||
} else if (str == "easeInOutQuart") {
|
||||
return EasingType_EaseInOutQuart;
|
||||
} else if (str == "easeInQuint") {
|
||||
return EasingType_EaseInQuint;
|
||||
} else if (str == "easeOutQuint") {
|
||||
return EasingType_EaseOutQuint;
|
||||
} else if (str == "easeInOutQuint") {
|
||||
return EasingType_EaseInOutQuint;
|
||||
} else if (str == "easeInExpo") {
|
||||
return EasingType_EaseInExpo;
|
||||
} else if (str == "easeOutExpo") {
|
||||
return EasingType_EaseOutExpo;
|
||||
} else if (str == "easeInOutExpo") {
|
||||
return EasingType_EaseInOutExpo;
|
||||
} else if (str == "easeInCirc") {
|
||||
return EasingType_EaseInCirc;
|
||||
} else if (str == "easeOutCirc") {
|
||||
return EasingType_EaseOutCirc;
|
||||
} else if (str == "easeInOutCirc") {
|
||||
return EasingType_EaseInOutCirc;
|
||||
} else {
|
||||
return EasingType_NumTypes;
|
||||
}
|
||||
}
|
||||
|
||||
static const char* animManipulatorJointVarTypeToString(AnimManipulator::JointVar::Type type) {
|
||||
switch (type) {
|
||||
case AnimManipulator::JointVar::Type::Absolute: return "absolute";
|
||||
|
@ -723,6 +778,7 @@ bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj,
|
|||
READ_FLOAT(interpTarget, stateObj, nodeId, jsonUrl, false);
|
||||
READ_FLOAT(interpDuration, stateObj, nodeId, jsonUrl, false);
|
||||
READ_OPTIONAL_STRING(interpType, stateObj);
|
||||
READ_OPTIONAL_STRING(easingType, stateObj);
|
||||
|
||||
READ_OPTIONAL_STRING(interpTargetVar, stateObj);
|
||||
READ_OPTIONAL_STRING(interpDurationVar, stateObj);
|
||||
|
@ -743,7 +799,16 @@ bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj,
|
|||
}
|
||||
}
|
||||
|
||||
auto statePtr = std::make_shared<AnimStateMachine::State>(id, iter->second, interpTarget, interpDuration, interpTypeEnum);
|
||||
EasingType easingTypeEnum = EasingType_Linear; // default value
|
||||
if (!easingType.isEmpty()) {
|
||||
easingTypeEnum = stringToEasingType(easingType);
|
||||
if (easingTypeEnum == EasingType_NumTypes) {
|
||||
qCCritical(animation) << "AnimNodeLoader, bad easingType on stateMachine state, nodeId = " << nodeId << "stateId =" << id;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto statePtr = std::make_shared<AnimStateMachine::State>(id, iter->second, interpTarget, interpDuration, interpTypeEnum, easingTypeEnum);
|
||||
assert(statePtr);
|
||||
|
||||
if (!interpTargetVar.isEmpty()) {
|
||||
|
@ -845,6 +910,7 @@ bool processRandomSwitchStateMachineNode(AnimNode::Pointer node, const QJsonObje
|
|||
READ_FLOAT(interpTarget, stateObj, nodeId, jsonUrl, false);
|
||||
READ_FLOAT(interpDuration, stateObj, nodeId, jsonUrl, false);
|
||||
READ_OPTIONAL_STRING(interpType, stateObj);
|
||||
READ_OPTIONAL_STRING(easingType, stateObj);
|
||||
READ_FLOAT(priority, stateObj, nodeId, jsonUrl, false);
|
||||
READ_BOOL(resume, stateObj, nodeId, jsonUrl, false);
|
||||
|
||||
|
@ -867,7 +933,16 @@ bool processRandomSwitchStateMachineNode(AnimNode::Pointer node, const QJsonObje
|
|||
}
|
||||
}
|
||||
|
||||
auto randomStatePtr = std::make_shared<AnimRandomSwitch::RandomSwitchState>(id, iter->second, interpTarget, interpDuration, interpTypeEnum, priority, resume);
|
||||
EasingType easingTypeEnum = EasingType_Linear; // default value
|
||||
if (!easingType.isEmpty()) {
|
||||
easingTypeEnum = stringToEasingType(easingType);
|
||||
if (easingTypeEnum == EasingType_NumTypes) {
|
||||
qCCritical(animation) << "AnimNodeLoader, bad easingType on randomSwitch state, nodeId = " << nodeId << "stateId =" << id;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto randomStatePtr = std::make_shared<AnimRandomSwitch::RandomSwitchState>(id, iter->second, interpTarget, interpDuration, interpTypeEnum, easingTypeEnum, priority, resume);
|
||||
if (priority > 0.0f) {
|
||||
smNode->addToPrioritySum(priority);
|
||||
}
|
||||
|
|
|
@ -54,15 +54,20 @@ const AnimPoseVec& AnimOverlay::evaluate(const AnimVariantMap& animVars, const A
|
|||
|
||||
if (_children.size() >= 2) {
|
||||
auto& underPoses = _children[1]->evaluate(animVars, context, dt, triggersOut);
|
||||
auto& overPoses = _children[0]->overlay(animVars, context, dt, triggersOut, underPoses);
|
||||
|
||||
if (underPoses.size() > 0 && underPoses.size() == overPoses.size()) {
|
||||
_poses.resize(underPoses.size());
|
||||
assert(_boneSetVec.size() == _poses.size());
|
||||
if (_alpha == 0.0f) {
|
||||
_poses = underPoses;
|
||||
} else {
|
||||
auto& overPoses = _children[0]->overlay(animVars, context, dt, triggersOut, underPoses);
|
||||
|
||||
for (size_t i = 0; i < _poses.size(); i++) {
|
||||
float alpha = _boneSetVec[i] * _alpha;
|
||||
::blend(1, &underPoses[i], &overPoses[i], alpha, &_poses[i]);
|
||||
if (underPoses.size() > 0 && underPoses.size() == overPoses.size()) {
|
||||
_poses.resize(underPoses.size());
|
||||
assert(_boneSetVec.size() == _poses.size());
|
||||
|
||||
for (size_t i = 0; i < _poses.size(); i++) {
|
||||
float alpha = _boneSetVec[i] * _alpha;
|
||||
::blend(1, &underPoses[i], &overPoses[i], alpha, &_poses[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,6 +89,7 @@ const AnimPoseVec& AnimRandomSwitch::evaluate(const AnimVariantMap& animVars, co
|
|||
|
||||
assert(_currentState);
|
||||
auto currentStateNode = _children[_currentState->getChildIndex()];
|
||||
auto previousStateNode = _children[_previousState->getChildIndex()];
|
||||
assert(currentStateNode);
|
||||
|
||||
if (_duringInterp) {
|
||||
|
@ -97,6 +98,7 @@ const AnimPoseVec& AnimRandomSwitch::evaluate(const AnimVariantMap& animVars, co
|
|||
AnimPoseVec* nextPoses = nullptr;
|
||||
AnimPoseVec* prevPoses = nullptr;
|
||||
AnimPoseVec localNextPoses;
|
||||
AnimPoseVec localPrevPoses;
|
||||
if (_interpType == InterpType::SnapshotBoth) {
|
||||
// interp between both snapshots
|
||||
prevPoses = &_prevPoses;
|
||||
|
@ -107,13 +109,18 @@ const AnimPoseVec& AnimRandomSwitch::evaluate(const AnimVariantMap& animVars, co
|
|||
localNextPoses = currentStateNode->evaluate(animVars, context, dt, triggersOut);
|
||||
prevPoses = &_prevPoses;
|
||||
nextPoses = &localNextPoses;
|
||||
} else if (_interpType == InterpType::EvaluateBoth) {
|
||||
localPrevPoses = previousStateNode->evaluate(animVars, context, dt, triggersOut);
|
||||
localNextPoses = currentStateNode->evaluate(animVars, context, dt, triggersOut);
|
||||
prevPoses = &localPrevPoses;
|
||||
nextPoses = &localNextPoses;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
if (_poses.size() > 0 && nextPoses && prevPoses && nextPoses->size() > 0 && prevPoses->size() > 0) {
|
||||
::blend(_poses.size(), &(prevPoses->at(0)), &(nextPoses->at(0)), _alpha, &_poses[0]);
|
||||
::blend(_poses.size(), &(prevPoses->at(0)), &(nextPoses->at(0)), easingFunc(_alpha, _easingType), &_poses[0]);
|
||||
}
|
||||
context.setDebugAlpha(_currentState->getID(), _alpha * parentDebugAlpha, _children[_currentState->getChildIndex()]->getType());
|
||||
context.setDebugAlpha(_currentState->getID(), easingFunc(_alpha, _easingType) * parentDebugAlpha, _children[_currentState->getChildIndex()]->getType());
|
||||
} else {
|
||||
_duringInterp = false;
|
||||
_prevPoses.clear();
|
||||
|
@ -160,6 +167,7 @@ void AnimRandomSwitch::switchRandomState(const AnimVariantMap& animVars, const A
|
|||
float duration = std::max(0.001f, animVars.lookup(desiredState->_interpDurationVar, desiredState->_interpDuration));
|
||||
_alphaVel = FRAMES_PER_SECOND / duration;
|
||||
_interpType = (InterpType)animVars.lookup(desiredState->_interpTypeVar, (int)desiredState->_interpType);
|
||||
_easingType = desiredState->_easingType;
|
||||
|
||||
// because dt is 0, we should not encounter any triggers
|
||||
const float dt = 0.0f;
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
#include "AnimNode.h"
|
||||
#include "AnimUtil.h"
|
||||
|
||||
// Random Switch State Machine for random transitioning between children AnimNodes
|
||||
//
|
||||
|
@ -51,6 +52,7 @@ public:
|
|||
enum class InterpType {
|
||||
SnapshotBoth = 0,
|
||||
SnapshotPrev,
|
||||
EvaluateBoth,
|
||||
NumTypes
|
||||
};
|
||||
|
||||
|
@ -73,12 +75,13 @@ protected:
|
|||
RandomSwitchState::Pointer _randomSwitchState;
|
||||
};
|
||||
|
||||
RandomSwitchState(const QString& id, int childIndex, float interpTarget, float interpDuration, InterpType interpType, float priority, bool resume) :
|
||||
RandomSwitchState(const QString& id, int childIndex, float interpTarget, float interpDuration, InterpType interpType, EasingType easingType, float priority, bool resume) :
|
||||
_id(id),
|
||||
_childIndex(childIndex),
|
||||
_interpTarget(interpTarget),
|
||||
_interpDuration(interpDuration),
|
||||
_interpType(interpType),
|
||||
_easingType(easingType),
|
||||
_priority(priority),
|
||||
_resume(resume){
|
||||
}
|
||||
|
@ -106,6 +109,7 @@ protected:
|
|||
float _interpTarget; // frames
|
||||
float _interpDuration; // frames
|
||||
InterpType _interpType;
|
||||
EasingType _easingType;
|
||||
float _priority {0.0f};
|
||||
bool _resume {false};
|
||||
|
||||
|
@ -154,7 +158,8 @@ protected:
|
|||
int _randomSwitchEvaluationCount { 0 };
|
||||
// interpolation state
|
||||
bool _duringInterp = false;
|
||||
InterpType _interpType{ InterpType::SnapshotPrev };
|
||||
InterpType _interpType { InterpType::SnapshotPrev };
|
||||
EasingType _easingType { EasingType_Linear };
|
||||
float _alphaVel = 0.0f;
|
||||
float _alpha = 0.0f;
|
||||
AnimPoseVec _prevPoses;
|
||||
|
|
|
@ -48,6 +48,7 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co
|
|||
|
||||
assert(_currentState);
|
||||
auto currentStateNode = _children[_currentState->getChildIndex()];
|
||||
auto previousStateNode = _children[_previousState->getChildIndex()];
|
||||
assert(currentStateNode);
|
||||
|
||||
if (_duringInterp) {
|
||||
|
@ -56,6 +57,8 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co
|
|||
AnimPoseVec* nextPoses = nullptr;
|
||||
AnimPoseVec* prevPoses = nullptr;
|
||||
AnimPoseVec localNextPoses;
|
||||
AnimPoseVec localPrevPoses;
|
||||
|
||||
if (_interpType == InterpType::SnapshotBoth) {
|
||||
// interp between both snapshots
|
||||
prevPoses = &_prevPoses;
|
||||
|
@ -66,13 +69,18 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co
|
|||
localNextPoses = currentStateNode->evaluate(animVars, context, dt, triggersOut);
|
||||
prevPoses = &_prevPoses;
|
||||
nextPoses = &localNextPoses;
|
||||
} else if (_interpType == InterpType::EvaluateBoth) {
|
||||
localPrevPoses = previousStateNode->evaluate(animVars, context, dt, triggersOut);
|
||||
localNextPoses = currentStateNode->evaluate(animVars, context, dt, triggersOut);
|
||||
prevPoses = &localPrevPoses;
|
||||
nextPoses = &localNextPoses;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
if (_poses.size() > 0 && nextPoses && prevPoses && nextPoses->size() > 0 && prevPoses->size() > 0) {
|
||||
::blend(_poses.size(), &(prevPoses->at(0)), &(nextPoses->at(0)), _alpha, &_poses[0]);
|
||||
::blend(_poses.size(), &(prevPoses->at(0)), &(nextPoses->at(0)), easingFunc(_alpha, _easingType), &_poses[0]);
|
||||
}
|
||||
context.setDebugAlpha(_currentState->getID(), _alpha * parentDebugAlpha, _children[_currentState->getChildIndex()]->getType());
|
||||
context.setDebugAlpha(_currentState->getID(), easingFunc(_alpha, _easingType) * parentDebugAlpha, _children[_currentState->getChildIndex()]->getType());
|
||||
} else {
|
||||
_duringInterp = false;
|
||||
_prevPoses.clear();
|
||||
|
@ -95,6 +103,15 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co
|
|||
return _poses;
|
||||
}
|
||||
|
||||
const QString& AnimStateMachine::getCurrentStateID() const {
|
||||
if (_currentState) {
|
||||
return _currentState->getID();
|
||||
} else {
|
||||
static QString emptyString;
|
||||
return emptyString;
|
||||
}
|
||||
}
|
||||
|
||||
void AnimStateMachine::setCurrentState(State::Pointer state) {
|
||||
_previousState = _currentState ? _currentState : state;
|
||||
_currentState = state;
|
||||
|
@ -116,6 +133,7 @@ void AnimStateMachine::switchState(const AnimVariantMap& animVars, const AnimCon
|
|||
float duration = std::max(0.001f, animVars.lookup(desiredState->_interpDurationVar, desiredState->_interpDuration));
|
||||
_alphaVel = FRAMES_PER_SECOND / duration;
|
||||
_interpType = (InterpType)animVars.lookup(desiredState->_interpTypeVar, (int)desiredState->_interpType);
|
||||
_easingType = desiredState->_easingType;
|
||||
|
||||
// because dt is 0, we should not encounter any triggers
|
||||
const float dt = 0.0f;
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
#include "AnimNode.h"
|
||||
#include "AnimUtil.h"
|
||||
|
||||
// State Machine for transitioning between children AnimNodes
|
||||
//
|
||||
|
@ -47,6 +48,7 @@ public:
|
|||
enum class InterpType {
|
||||
SnapshotBoth = 0,
|
||||
SnapshotPrev,
|
||||
EvaluateBoth,
|
||||
NumTypes
|
||||
};
|
||||
|
||||
|
@ -69,12 +71,13 @@ protected:
|
|||
State::Pointer _state;
|
||||
};
|
||||
|
||||
State(const QString& id, int childIndex, float interpTarget, float interpDuration, InterpType interpType) :
|
||||
State(const QString& id, int childIndex, float interpTarget, float interpDuration, InterpType interpType, EasingType easingType) :
|
||||
_id(id),
|
||||
_childIndex(childIndex),
|
||||
_interpTarget(interpTarget),
|
||||
_interpDuration(interpDuration),
|
||||
_interpType(interpType) {}
|
||||
_interpType(interpType),
|
||||
_easingType(easingType) {}
|
||||
|
||||
void setInterpTargetVar(const QString& interpTargetVar) { _interpTargetVar = interpTargetVar; }
|
||||
void setInterpDurationVar(const QString& interpDurationVar) { _interpDurationVar = interpDurationVar; }
|
||||
|
@ -95,6 +98,7 @@ protected:
|
|||
float _interpTarget; // frames
|
||||
float _interpDuration; // frames
|
||||
InterpType _interpType;
|
||||
EasingType _easingType;
|
||||
|
||||
QString _interpTargetVar;
|
||||
QString _interpDurationVar;
|
||||
|
@ -116,6 +120,7 @@ public:
|
|||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
||||
|
||||
void setCurrentStateVar(QString& currentStateVar) { _currentStateVar = currentStateVar; }
|
||||
const QString& getCurrentStateID() const;
|
||||
|
||||
protected:
|
||||
|
||||
|
@ -134,6 +139,7 @@ protected:
|
|||
// interpolation state
|
||||
bool _duringInterp = false;
|
||||
InterpType _interpType { InterpType::SnapshotPrev };
|
||||
EasingType _easingType { EasingType_Linear };
|
||||
float _alphaVel = 0.0f;
|
||||
float _alpha = 0.0f;
|
||||
AnimPoseVec _prevPoses;
|
||||
|
|
|
@ -211,3 +211,86 @@ bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& shapePose
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// See https://easings.net/en# for a graphical visualiztion of easing types.
|
||||
float easingFunc(float alpha, EasingType type) {
|
||||
switch (type) {
|
||||
case EasingType_Linear:
|
||||
return alpha;
|
||||
case EasingType_EaseInSine:
|
||||
return sinf((alpha - 1.0f) * PI_OVER_TWO) + 1.0f;
|
||||
case EasingType_EaseOutSine:
|
||||
return sinf(alpha * PI_OVER_TWO);
|
||||
case EasingType_EaseInOutSine:
|
||||
return 0.5f * (1.0f - cosf(alpha * PI));
|
||||
case EasingType_EaseInQuad:
|
||||
return alpha * alpha;
|
||||
case EasingType_EaseOutQuad:
|
||||
return -(alpha * (alpha - 2.0f));
|
||||
case EasingType_EaseInOutQuad:
|
||||
return (alpha < 0.5f) ? (2.0f * alpha * alpha) : ((-2.0f * alpha * alpha) + (4.0f * alpha) - 1.0f);
|
||||
case EasingType_EaseInCubic:
|
||||
return alpha * alpha * alpha;
|
||||
case EasingType_EaseOutCubic: {
|
||||
float temp = alpha - 1.0f;
|
||||
return temp * temp * temp + 1.0f;
|
||||
}
|
||||
case EasingType_EaseInOutCubic:
|
||||
if (alpha < 0.5f) {
|
||||
return 4.0f * alpha * alpha * alpha;
|
||||
} else {
|
||||
float temp = ((2.0f * alpha) - 2.0f);
|
||||
return 0.5f * temp * temp * temp + 1.0f;
|
||||
}
|
||||
case EasingType_EaseInQuart:
|
||||
return alpha * alpha * alpha * alpha;
|
||||
case EasingType_EaseOutQuart: {
|
||||
float temp = alpha - 1.0f;
|
||||
return temp * temp * temp * (1.0f - alpha) + 1.0f;
|
||||
}
|
||||
case EasingType_EaseInOutQuart:
|
||||
if (alpha < 0.5f) {
|
||||
return 8.0f * alpha * alpha * alpha * alpha;
|
||||
} else {
|
||||
float temp = alpha - 1.0f;
|
||||
return -8.0f * temp * temp * temp * temp + 1.0f;
|
||||
}
|
||||
case EasingType_EaseInQuint:
|
||||
return alpha * alpha * alpha * alpha * alpha;
|
||||
case EasingType_EaseOutQuint: {
|
||||
float temp = (alpha - 1.0f);
|
||||
return temp * temp * temp * temp * temp + 1.0f;
|
||||
}
|
||||
case EasingType_EaseInOutQuint:
|
||||
if (alpha < 0.5f) {
|
||||
return 16.0f * alpha * alpha * alpha * alpha * alpha;
|
||||
} else {
|
||||
float temp = ((2.0f * alpha) - 2.0f);
|
||||
return 0.5f * temp * temp * temp * temp * temp + 1.0f;
|
||||
}
|
||||
case EasingType_EaseInExpo:
|
||||
return (alpha == 0.0f) ? alpha : powf(2.0f, 10.0f * (alpha - 1.0f));
|
||||
case EasingType_EaseOutExpo:
|
||||
return (alpha == 1.0f) ? alpha : 1.0f - powf(2.0f, -10.0f * alpha);
|
||||
case EasingType_EaseInOutExpo:
|
||||
if (alpha == 0.0f || alpha == 1.0f)
|
||||
return alpha;
|
||||
else if (alpha < 0.5f) {
|
||||
return 0.5f * powf(2.0f, (20.0f * alpha) - 10.0f);
|
||||
} else {
|
||||
return -0.5f * powf(2.0f, (-20.0f * alpha) + 10.0f) + 1.0f;
|
||||
}
|
||||
case EasingType_EaseInCirc:
|
||||
return 1.0f - sqrtf(1.0f - (alpha * alpha));
|
||||
case EasingType_EaseOutCirc:
|
||||
return sqrtf((2.0f - alpha) * alpha);
|
||||
case EasingType_EaseInOutCirc:
|
||||
if (alpha < 0.5f) {
|
||||
return 0.5f * (1.0f - sqrtf(1.0f - 4.0f * (alpha * alpha)));
|
||||
} else {
|
||||
return 0.5f * (sqrtf(-((2.0f * alpha) - 3.0f) * ((2.0f * alpha) - 1.0f)) + 1.0f);
|
||||
}
|
||||
default:
|
||||
return alpha;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -128,10 +128,37 @@ protected:
|
|||
bool _snapshotValid { false };
|
||||
};
|
||||
|
||||
|
||||
// returns true if the given point lies inside of the k-dop, specified by shapeInfo & shapePose.
|
||||
// if the given point does lie within the k-dop, it also returns the amount of displacement necessary to push that point outward
|
||||
// such that it lies on the surface of the kdop.
|
||||
bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& shapePose, const HFMJointShapeInfo& shapeInfo, glm::vec3& displacementOut);
|
||||
|
||||
enum EasingType {
|
||||
EasingType_Linear,
|
||||
EasingType_EaseInSine,
|
||||
EasingType_EaseOutSine,
|
||||
EasingType_EaseInOutSine,
|
||||
EasingType_EaseInQuad,
|
||||
EasingType_EaseOutQuad,
|
||||
EasingType_EaseInOutQuad,
|
||||
EasingType_EaseInCubic,
|
||||
EasingType_EaseOutCubic,
|
||||
EasingType_EaseInOutCubic,
|
||||
EasingType_EaseInQuart,
|
||||
EasingType_EaseOutQuart,
|
||||
EasingType_EaseInOutQuart,
|
||||
EasingType_EaseInQuint,
|
||||
EasingType_EaseOutQuint,
|
||||
EasingType_EaseInOutQuint,
|
||||
EasingType_EaseInExpo,
|
||||
EasingType_EaseOutExpo,
|
||||
EasingType_EaseInOutExpo,
|
||||
EasingType_EaseInCirc,
|
||||
EasingType_EaseOutCirc,
|
||||
EasingType_EaseInOutCirc,
|
||||
EasingType_NumTypes
|
||||
};
|
||||
|
||||
float easingFunc(float alpha, EasingType type);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "AnimInverseKinematics.h"
|
||||
#include "AnimOverlay.h"
|
||||
#include "AnimSkeleton.h"
|
||||
#include "AnimStateMachine.h"
|
||||
#include "AnimUtil.h"
|
||||
#include "AvatarConstants.h"
|
||||
#include "IKTarget.h"
|
||||
|
@ -1906,28 +1907,7 @@ void Rig::updateFeet(bool leftFootEnabled, bool rightFootEnabled, bool headEnabl
|
|||
|
||||
void Rig::updateReactions(const ControllerParameters& params) {
|
||||
|
||||
// enable/disable animVars
|
||||
bool enabled = params.reactionEnabledFlags[AVATAR_REACTION_POSITIVE];
|
||||
_animVars.set("reactionPositiveEnabled", enabled);
|
||||
_animVars.set("reactionPositiveDisabled", !enabled);
|
||||
|
||||
enabled = params.reactionEnabledFlags[AVATAR_REACTION_NEGATIVE];
|
||||
_animVars.set("reactionNegativeEnabled", enabled);
|
||||
_animVars.set("reactionNegativeDisabled", !enabled);
|
||||
|
||||
enabled = params.reactionEnabledFlags[AVATAR_REACTION_RAISE_HAND];
|
||||
_animVars.set("reactionRaiseHandEnabled", enabled);
|
||||
_animVars.set("reactionRaiseHandDisabled", !enabled);
|
||||
|
||||
enabled = params.reactionEnabledFlags[AVATAR_REACTION_APPLAUD];
|
||||
_animVars.set("reactionApplaudEnabled", enabled);
|
||||
_animVars.set("reactionApplaudDisabled", !enabled);
|
||||
|
||||
enabled = params.reactionEnabledFlags[AVATAR_REACTION_POINT];
|
||||
_animVars.set("reactionPointEnabled", enabled);
|
||||
_animVars.set("reactionPointDisabled", !enabled);
|
||||
|
||||
// trigger animVars
|
||||
// trigger reactions
|
||||
if (params.reactionTriggers[AVATAR_REACTION_POSITIVE]) {
|
||||
_animVars.set("reactionPositiveTrigger", true);
|
||||
} else {
|
||||
|
@ -1940,22 +1920,38 @@ void Rig::updateReactions(const ControllerParameters& params) {
|
|||
_animVars.set("reactionNegativeTrigger", false);
|
||||
}
|
||||
|
||||
if (params.reactionTriggers[AVATAR_REACTION_RAISE_HAND]) {
|
||||
_animVars.set("reactionRaiseHandTrigger", true);
|
||||
} else {
|
||||
_animVars.set("reactionRaiseHandTrigger", false);
|
||||
}
|
||||
// begin end reactions
|
||||
bool enabled = params.reactionEnabledFlags[AVATAR_REACTION_RAISE_HAND];
|
||||
_animVars.set("reactionRaiseHandEnabled", enabled);
|
||||
_animVars.set("reactionRaiseHandDisabled", !enabled);
|
||||
|
||||
if (params.reactionTriggers[AVATAR_REACTION_APPLAUD]) {
|
||||
_animVars.set("reactionApplaudTrigger", true);
|
||||
} else {
|
||||
_animVars.set("reactionApplaudTrigger", false);
|
||||
}
|
||||
enabled = params.reactionEnabledFlags[AVATAR_REACTION_APPLAUD];
|
||||
_animVars.set("reactionApplaudEnabled", enabled);
|
||||
_animVars.set("reactionApplaudDisabled", !enabled);
|
||||
|
||||
if (params.reactionTriggers[AVATAR_REACTION_POINT]) {
|
||||
_animVars.set("reactionPointTrigger", true);
|
||||
} else {
|
||||
_animVars.set("reactionPointTrigger", false);
|
||||
enabled = params.reactionEnabledFlags[AVATAR_REACTION_POINT];
|
||||
_animVars.set("reactionPointEnabled", enabled);
|
||||
_animVars.set("reactionPointDisabled", !enabled);
|
||||
|
||||
// determine if we should ramp off IK
|
||||
if (_enableInverseKinematics) {
|
||||
bool reactionPlaying = false;
|
||||
std::shared_ptr<AnimStateMachine> mainStateMachine = std::dynamic_pointer_cast<AnimStateMachine>(_animNode->findByName("mainStateMachine"));
|
||||
std::shared_ptr<AnimStateMachine> idleStateMachine = std::dynamic_pointer_cast<AnimStateMachine>(_animNode->findByName("idle"));
|
||||
if (mainStateMachine && mainStateMachine->getCurrentStateID() == "idle" && idleStateMachine) {
|
||||
reactionPlaying = idleStateMachine->getCurrentStateID().startsWith("reaction");
|
||||
}
|
||||
|
||||
bool isSeated = _state == RigRole::Seated;
|
||||
bool hipsEnabled = params.primaryControllerFlags[PrimaryControllerType_Hips] & (uint8_t)ControllerFlags::Enabled;
|
||||
bool hipsEstimated = params.primaryControllerFlags[PrimaryControllerType_Hips] & (uint8_t)ControllerFlags::Estimated;
|
||||
bool hmdMode = hipsEnabled && !hipsEstimated;
|
||||
|
||||
if ((reactionPlaying || isSeated) && !hmdMode) {
|
||||
// TODO: make this smooth.
|
||||
// disable head IK while reaction is playing, but only in "desktop" mode.
|
||||
_animVars.set("headType", (int)IKTarget::Type::Unknown);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2115,9 +2111,9 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo
|
|||
_talkIdleInterpTime = 1.0f;
|
||||
}
|
||||
float easeOutInValue = _talkIdleInterpTime < 0.5f ? 4.0f * powf(_talkIdleInterpTime, 3.0f) : 4.0f * powf((_talkIdleInterpTime - 1.0f), 3.0f) + 1.0f;
|
||||
_animVars.set("idleOverlayAlpha", easeOutInValue);
|
||||
_animVars.set("talkOverlayAlpha", easeOutInValue);
|
||||
} else {
|
||||
_animVars.set("idleOverlayAlpha", 1.0f);
|
||||
_animVars.set("talkOverlayAlpha", 1.0f);
|
||||
}
|
||||
} else {
|
||||
if (_talkIdleInterpTime < 1.0f) {
|
||||
|
@ -2127,9 +2123,9 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo
|
|||
}
|
||||
float easeOutInValue = _talkIdleInterpTime < 0.5f ? 4.0f * powf(_talkIdleInterpTime, 3.0f) : 4.0f * powf((_talkIdleInterpTime - 1.0f), 3.0f) + 1.0f;
|
||||
float talkAlpha = 1.0f - easeOutInValue;
|
||||
_animVars.set("idleOverlayAlpha", talkAlpha);
|
||||
_animVars.set("talkOverlayAlpha", talkAlpha);
|
||||
} else {
|
||||
_animVars.set("idleOverlayAlpha", 0.0f);
|
||||
_animVars.set("talkOverlayAlpha", 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -88,8 +88,8 @@ public:
|
|||
AnimPose secondaryControllerPoses[NumSecondaryControllerTypes]; // rig space
|
||||
uint8_t secondaryControllerFlags[NumSecondaryControllerTypes];
|
||||
bool isTalking;
|
||||
bool reactionEnabledFlags[NUM_AVATAR_REACTIONS];
|
||||
bool reactionTriggers[NUM_AVATAR_REACTIONS];
|
||||
bool reactionEnabledFlags[NUM_AVATAR_BEGIN_END_REACTIONS];
|
||||
bool reactionTriggers[NUM_AVATAR_TRIGGER_REACTIONS];
|
||||
HFMJointShapeInfo hipsShapeInfo;
|
||||
HFMJointShapeInfo spineShapeInfo;
|
||||
HFMJointShapeInfo spine1ShapeInfo;
|
||||
|
|
|
@ -7,6 +7,11 @@ link_hifi_libraries(audio plugins)
|
|||
include_hifi_library_headers(shared)
|
||||
include_hifi_library_headers(networking)
|
||||
|
||||
if (ANDROID)
|
||||
else ()
|
||||
target_webrtc()
|
||||
endif ()
|
||||
|
||||
# append audio includes to our list of includes to bubble
|
||||
target_include_directories(${TARGET_NAME} PUBLIC "${HIFI_LIBRARY_DIR}/audio/src")
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#include <windows.h>
|
||||
#include <Mmsystem.h>
|
||||
#include <mmdeviceapi.h>
|
||||
|
@ -286,6 +286,7 @@ AudioClient::AudioClient() :
|
|||
_shouldEchoLocally(false),
|
||||
_shouldEchoToServer(false),
|
||||
_isNoiseGateEnabled(true),
|
||||
_isAECEnabled(true),
|
||||
_reverb(false),
|
||||
_reverbOptions(&_scriptReverbOptions),
|
||||
_inputToNetworkResampler(NULL),
|
||||
|
@ -302,6 +303,7 @@ AudioClient::AudioClient() :
|
|||
_isHeadsetPluggedIn(false),
|
||||
#endif
|
||||
_orientationGetter(DEFAULT_ORIENTATION_GETTER) {
|
||||
|
||||
// avoid putting a lock in the device callback
|
||||
assert(_localSamplesAvailable.is_lock_free());
|
||||
|
||||
|
@ -353,6 +355,10 @@ AudioClient::AudioClient() :
|
|||
|
||||
configureReverb();
|
||||
|
||||
#if defined(WEBRTC_ENABLED)
|
||||
configureWebrtc();
|
||||
#endif
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto& packetReceiver = nodeList->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::AudioStreamStats, &_stats, "processStreamStatsPacket");
|
||||
|
@ -591,11 +597,17 @@ bool AudioClient::getNamedAudioDeviceForModeExists(QAudio::Mode mode, const QStr
|
|||
|
||||
|
||||
// attempt to use the native sample rate and channel count
|
||||
bool nativeFormatForAudioDevice(const QAudioDeviceInfo& audioDevice,
|
||||
QAudioFormat& audioFormat) {
|
||||
bool nativeFormatForAudioDevice(const QAudioDeviceInfo& audioDevice, QAudioFormat& audioFormat) {
|
||||
|
||||
audioFormat = audioDevice.preferredFormat();
|
||||
|
||||
// converting to/from this rate must produce an integral number of samples
|
||||
if ((audioFormat.sampleRate() <= 0) ||
|
||||
(audioFormat.sampleRate() * AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL % AudioConstants::SAMPLE_RATE != 0)) {
|
||||
qCWarning(audioclient) << "The native sample rate [" << audioFormat.sampleRate() << "] is not supported.";
|
||||
return false;
|
||||
}
|
||||
|
||||
audioFormat.setCodec("audio/pcm");
|
||||
audioFormat.setSampleSize(16);
|
||||
audioFormat.setSampleType(QAudioFormat::SignedInt);
|
||||
|
@ -603,12 +615,17 @@ bool nativeFormatForAudioDevice(const QAudioDeviceInfo& audioDevice,
|
|||
|
||||
if (!audioDevice.isFormatSupported(audioFormat)) {
|
||||
qCWarning(audioclient) << "The native format is" << audioFormat << "but isFormatSupported() failed.";
|
||||
return false;
|
||||
}
|
||||
// converting to/from this rate must produce an integral number of samples
|
||||
if (audioFormat.sampleRate() * AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL % AudioConstants::SAMPLE_RATE != 0) {
|
||||
qCWarning(audioclient) << "The native sample rate [" << audioFormat.sampleRate() << "] is not supported.";
|
||||
return false;
|
||||
|
||||
// attempt the native sample rate, with channels forced to 2
|
||||
audioFormat.setChannelCount(2);
|
||||
if (!audioDevice.isFormatSupported(audioFormat)) {
|
||||
|
||||
// attempt the native sample rate, with channels forced to 1
|
||||
audioFormat.setChannelCount(1);
|
||||
if (!audioDevice.isFormatSupported(audioFormat)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1084,6 +1101,131 @@ void AudioClient::setReverbOptions(const AudioEffectOptions* options) {
|
|||
}
|
||||
}
|
||||
|
||||
#if defined(WEBRTC_ENABLED)
|
||||
|
||||
static void deinterleaveToFloat(const int16_t* src, float* const* dst, int numFrames, int numChannels) {
|
||||
for (int i = 0; i < numFrames; i++) {
|
||||
for (int ch = 0; ch < numChannels; ch++) {
|
||||
float f = *src++;
|
||||
f *= (1/32768.0f); // scale
|
||||
dst[ch][i] = f; // deinterleave
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void interleaveToInt16(const float* const* src, int16_t* dst, int numFrames, int numChannels) {
|
||||
for (int i = 0; i < numFrames; i++) {
|
||||
for (int ch = 0; ch < numChannels; ch++) {
|
||||
float f = src[ch][i];
|
||||
f *= 32768.0f; // scale
|
||||
f += (f < 0.0f) ? -0.5f : 0.5f; // round
|
||||
f = std::max(std::min(f, 32767.0f), -32768.0f); // saturate
|
||||
*dst++ = (int16_t)f; // interleave
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioClient::configureWebrtc() {
|
||||
_apm = webrtc::AudioProcessingBuilder().Create();
|
||||
|
||||
webrtc::AudioProcessing::Config config;
|
||||
|
||||
config.pre_amplifier.enabled = false;
|
||||
config.high_pass_filter.enabled = false;
|
||||
config.echo_canceller.enabled = true;
|
||||
config.echo_canceller.mobile_mode = false;
|
||||
config.echo_canceller.use_legacy_aec = false;
|
||||
config.noise_suppression.enabled = false;
|
||||
config.noise_suppression.level = webrtc::AudioProcessing::Config::NoiseSuppression::kModerate;
|
||||
config.voice_detection.enabled = false;
|
||||
config.gain_controller1.enabled = false;
|
||||
config.gain_controller2.enabled = false;
|
||||
config.gain_controller2.fixed_digital.gain_db = 0.0f;
|
||||
config.gain_controller2.adaptive_digital.enabled = false;
|
||||
config.residual_echo_detector.enabled = true;
|
||||
config.level_estimation.enabled = false;
|
||||
|
||||
_apm->ApplyConfig(config);
|
||||
}
|
||||
|
||||
// rebuffer into 10ms chunks
|
||||
void AudioClient::processWebrtcFarEnd(const int16_t* samples, int numFrames, int numChannels, int sampleRate) {
|
||||
|
||||
const webrtc::StreamConfig streamConfig = webrtc::StreamConfig(sampleRate, numChannels);
|
||||
const int numChunk = (int)streamConfig.num_frames();
|
||||
|
||||
if (sampleRate > WEBRTC_SAMPLE_RATE_MAX) {
|
||||
qCWarning(audioclient) << "WebRTC does not support" << sampleRate << "output sample rate.";
|
||||
return;
|
||||
}
|
||||
if (numChannels > WEBRTC_CHANNELS_MAX) {
|
||||
qCWarning(audioclient) << "WebRTC does not support" << numChannels << "output channels.";
|
||||
return;
|
||||
}
|
||||
|
||||
while (numFrames > 0) {
|
||||
|
||||
// number of frames to fill
|
||||
int numFill = std::min(numFrames, numChunk - _numFifoFarEnd);
|
||||
|
||||
// refill fifo
|
||||
memcpy(&_fifoFarEnd[_numFifoFarEnd], samples, numFill * numChannels * sizeof(int16_t));
|
||||
samples += numFill * numChannels;
|
||||
numFrames -= numFill;
|
||||
_numFifoFarEnd += numFill;
|
||||
|
||||
if (_numFifoFarEnd == numChunk) {
|
||||
|
||||
// convert audio format
|
||||
float buffer[WEBRTC_CHANNELS_MAX][WEBRTC_FRAMES_MAX];
|
||||
float* const buffers[WEBRTC_CHANNELS_MAX] = { buffer[0], buffer[1] };
|
||||
deinterleaveToFloat(_fifoFarEnd, buffers, numChunk, numChannels);
|
||||
|
||||
// process one chunk
|
||||
int error = _apm->ProcessReverseStream(buffers, streamConfig, streamConfig, buffers);
|
||||
if (error != _apm->kNoError) {
|
||||
qCWarning(audioclient) << "WebRTC ProcessReverseStream() returned ERROR:" << error;
|
||||
}
|
||||
_numFifoFarEnd = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioClient::processWebrtcNearEnd(int16_t* samples, int numFrames, int numChannels, int sampleRate) {
|
||||
|
||||
const webrtc::StreamConfig streamConfig = webrtc::StreamConfig(sampleRate, numChannels);
|
||||
const int numChunk = (int)streamConfig.num_frames();
|
||||
|
||||
if (sampleRate > WEBRTC_SAMPLE_RATE_MAX) {
|
||||
qCWarning(audioclient) << "WebRTC does not support" << sampleRate << "input sample rate.";
|
||||
return;
|
||||
}
|
||||
if (numChannels > WEBRTC_CHANNELS_MAX) {
|
||||
qCWarning(audioclient) << "WebRTC does not support" << numChannels << "input channels.";
|
||||
return;
|
||||
}
|
||||
if (numFrames != numChunk) {
|
||||
qCWarning(audioclient) << "WebRTC requires exactly 10ms of input.";
|
||||
return;
|
||||
}
|
||||
|
||||
// convert audio format
|
||||
float buffer[WEBRTC_CHANNELS_MAX][WEBRTC_FRAMES_MAX];
|
||||
float* const buffers[WEBRTC_CHANNELS_MAX] = { buffer[0], buffer[1] };
|
||||
deinterleaveToFloat(samples, buffers, numFrames, numChannels);
|
||||
|
||||
// process one chunk
|
||||
int error = _apm->ProcessStream(buffers, streamConfig, streamConfig, buffers);
|
||||
if (error != _apm->kNoError) {
|
||||
qCWarning(audioclient) << "WebRTC ProcessStream() returned ERROR:" << error;
|
||||
} else {
|
||||
// modify samples in-place
|
||||
interleaveToInt16(buffers, samples, numFrames, numChannels);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // WEBRTC_ENABLED
|
||||
|
||||
void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) {
|
||||
// If there is server echo, reverb will be applied to the recieved audio stream so no need to have it here.
|
||||
bool hasReverb = _reverb || _receivedAudioStream.hasReverb();
|
||||
|
@ -1262,6 +1404,13 @@ void AudioClient::handleMicAudioInput() {
|
|||
|
||||
_inputRingBuffer.readSamples(inputAudioSamples.get(), inputSamplesRequired);
|
||||
|
||||
#if defined(WEBRTC_ENABLED)
|
||||
if (_isAECEnabled) {
|
||||
processWebrtcNearEnd(inputAudioSamples.get(), inputSamplesRequired / _inputFormat.channelCount(),
|
||||
_inputFormat.channelCount(), _inputFormat.sampleRate());
|
||||
}
|
||||
#endif
|
||||
|
||||
// detect loudness and clipping on the raw input
|
||||
bool isClipping = false;
|
||||
float loudness = computeLoudness(inputAudioSamples.get(), inputSamplesRequired, _inputFormat.channelCount(), isClipping);
|
||||
|
@ -1574,6 +1723,15 @@ void AudioClient::setWarnWhenMuted(bool enable, bool emitSignal) {
|
|||
}
|
||||
}
|
||||
|
||||
void AudioClient::setAcousticEchoCancellation(bool enable, bool emitSignal) {
|
||||
if (_isAECEnabled != enable) {
|
||||
_isAECEnabled = enable;
|
||||
if (emitSignal) {
|
||||
emit acousticEchoCancellationChanged(_isAECEnabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AudioClient::setIsStereoInput(bool isStereoInput) {
|
||||
bool stereoInputChanged = false;
|
||||
if (isStereoInput != _isStereoInput && _inputDeviceInfo.supportedChannelCounts().contains(2)) {
|
||||
|
@ -2107,15 +2265,16 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) {
|
|||
return maxSize;
|
||||
}
|
||||
|
||||
// samples requested from OUTPUT_CHANNEL_COUNT
|
||||
// max samples requested from OUTPUT_CHANNEL_COUNT
|
||||
int deviceChannelCount = _audio->_outputFormat.channelCount();
|
||||
int samplesRequested = (int)(maxSize / AudioConstants::SAMPLE_SIZE) * OUTPUT_CHANNEL_COUNT / deviceChannelCount;
|
||||
int maxSamplesRequested = (int)(maxSize / AudioConstants::SAMPLE_SIZE) * OUTPUT_CHANNEL_COUNT / deviceChannelCount;
|
||||
// restrict samplesRequested to the size of our mix/scratch buffers
|
||||
samplesRequested = std::min(samplesRequested, _audio->_outputPeriod);
|
||||
maxSamplesRequested = std::min(maxSamplesRequested, _audio->_outputPeriod);
|
||||
|
||||
int16_t* scratchBuffer = _audio->_outputScratchBuffer;
|
||||
float* mixBuffer = _audio->_outputMixBuffer;
|
||||
|
||||
int samplesRequested = maxSamplesRequested;
|
||||
int networkSamplesPopped;
|
||||
if ((networkSamplesPopped = _receivedAudioStream.popSamples(samplesRequested, false)) > 0) {
|
||||
qCDebug(audiostream, "Read %d samples from buffer (%d available, %d requested)", networkSamplesPopped, _receivedAudioStream.getSamplesAvailable(), samplesRequested);
|
||||
|
@ -2160,45 +2319,45 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) {
|
|||
});
|
||||
|
||||
int samplesPopped = std::max(networkSamplesPopped, injectorSamplesPopped);
|
||||
int framesPopped = samplesPopped / AudioConstants::STEREO;
|
||||
int bytesWritten;
|
||||
if (samplesPopped > 0) {
|
||||
|
||||
// apply output gain
|
||||
float newGain = _audio->_outputGain.load(std::memory_order_acquire);
|
||||
float oldGain = _audio->_lastOutputGain;
|
||||
_audio->_lastOutputGain = newGain;
|
||||
|
||||
applyGainSmoothing<OUTPUT_CHANNEL_COUNT>(mixBuffer, framesPopped, oldGain, newGain);
|
||||
|
||||
if (deviceChannelCount == OUTPUT_CHANNEL_COUNT) {
|
||||
// limit the audio
|
||||
_audio->_audioLimiter.render(mixBuffer, (int16_t*)data, framesPopped);
|
||||
} else {
|
||||
_audio->_audioLimiter.render(mixBuffer, scratchBuffer, framesPopped);
|
||||
|
||||
// upmix or downmix to deviceChannelCount
|
||||
if (deviceChannelCount > OUTPUT_CHANNEL_COUNT) {
|
||||
int extraChannels = deviceChannelCount - OUTPUT_CHANNEL_COUNT;
|
||||
channelUpmix(scratchBuffer, (int16_t*)data, samplesPopped, extraChannels);
|
||||
} else {
|
||||
channelDownmix(scratchBuffer, (int16_t*)data, samplesPopped);
|
||||
}
|
||||
}
|
||||
|
||||
bytesWritten = framesPopped * AudioConstants::SAMPLE_SIZE * deviceChannelCount;
|
||||
assert(bytesWritten <= maxSize);
|
||||
|
||||
} else {
|
||||
// nothing on network, don't grab anything from injectors, and just return 0s
|
||||
memset(data, 0, maxSize);
|
||||
bytesWritten = maxSize;
|
||||
if (samplesPopped == 0) {
|
||||
// nothing on network, don't grab anything from injectors, and fill with silence
|
||||
samplesPopped = maxSamplesRequested;
|
||||
memset(mixBuffer, 0, samplesPopped * sizeof(float));
|
||||
}
|
||||
int framesPopped = samplesPopped / OUTPUT_CHANNEL_COUNT;
|
||||
|
||||
// apply output gain
|
||||
float newGain = _audio->_outputGain.load(std::memory_order_acquire);
|
||||
float oldGain = _audio->_lastOutputGain;
|
||||
_audio->_lastOutputGain = newGain;
|
||||
|
||||
applyGainSmoothing<OUTPUT_CHANNEL_COUNT>(mixBuffer, framesPopped, oldGain, newGain);
|
||||
|
||||
// limit the audio
|
||||
_audio->_audioLimiter.render(mixBuffer, scratchBuffer, framesPopped);
|
||||
|
||||
#if defined(WEBRTC_ENABLED)
|
||||
if (_audio->_isAECEnabled) {
|
||||
_audio->processWebrtcFarEnd(scratchBuffer, framesPopped, OUTPUT_CHANNEL_COUNT, _audio->_outputFormat.sampleRate());
|
||||
}
|
||||
#endif
|
||||
|
||||
// if required, upmix or downmix to deviceChannelCount
|
||||
if (deviceChannelCount == OUTPUT_CHANNEL_COUNT) {
|
||||
memcpy(data, scratchBuffer, samplesPopped * AudioConstants::SAMPLE_SIZE);
|
||||
} else if (deviceChannelCount > OUTPUT_CHANNEL_COUNT) {
|
||||
int extraChannels = deviceChannelCount - OUTPUT_CHANNEL_COUNT;
|
||||
channelUpmix(scratchBuffer, (int16_t*)data, samplesPopped, extraChannels);
|
||||
} else {
|
||||
channelDownmix(scratchBuffer, (int16_t*)data, samplesPopped);
|
||||
}
|
||||
int bytesWritten = framesPopped * AudioConstants::SAMPLE_SIZE * deviceChannelCount;
|
||||
assert(bytesWritten <= maxSize);
|
||||
|
||||
// send output buffer for recording
|
||||
if (_audio->_isRecording) {
|
||||
Lock lock(_recordMutex);
|
||||
_audio->_audioFileWav.addRawAudioChunk(reinterpret_cast<char*>(scratchBuffer), bytesWritten);
|
||||
_audio->_audioFileWav.addRawAudioChunk(data, bytesWritten);
|
||||
}
|
||||
|
||||
int bytesAudioOutputUnplayed = _audio->_audioOutput->bufferSize() - _audio->_audioOutput->bytesFree();
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <AbstractAudioInterface.h>
|
||||
#include <AudioEffectOptions.h>
|
||||
#include <AudioStreamStats.h>
|
||||
#include <shared/WebRTC.h>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
#include <HifiSockAddr.h>
|
||||
|
@ -215,6 +216,9 @@ public slots:
|
|||
void setWarnWhenMuted(bool isNoiseGateEnabled, bool emitSignal = true);
|
||||
bool isWarnWhenMutedEnabled() const { return _warnWhenMuted; }
|
||||
|
||||
void setAcousticEchoCancellation(bool isAECEnabled, bool emitSignal = true);
|
||||
bool isAcousticEchoCancellationEnabled() const { return _isAECEnabled; }
|
||||
|
||||
virtual bool getLocalEcho() override { return _shouldEchoLocally; }
|
||||
virtual void setLocalEcho(bool localEcho) override { _shouldEchoLocally = localEcho; }
|
||||
virtual void toggleLocalEcho() override { _shouldEchoLocally = !_shouldEchoLocally; }
|
||||
|
@ -256,6 +260,7 @@ signals:
|
|||
void muteToggled(bool muted);
|
||||
void noiseReductionChanged(bool noiseReductionEnabled);
|
||||
void warnWhenMutedChanged(bool warnWhenMutedEnabled);
|
||||
void acousticEchoCancellationChanged(bool acousticEchoCancellationEnabled);
|
||||
void mutedByMixer();
|
||||
void inputReceived(const QByteArray& inputSamples);
|
||||
void inputLoudnessChanged(float loudness, bool isClipping);
|
||||
|
@ -377,6 +382,7 @@ private:
|
|||
bool _shouldEchoToServer;
|
||||
bool _isNoiseGateEnabled;
|
||||
bool _warnWhenMuted;
|
||||
bool _isAECEnabled;
|
||||
|
||||
bool _reverb;
|
||||
AudioEffectOptions _scriptReverbOptions;
|
||||
|
@ -414,9 +420,23 @@ private:
|
|||
// Adds Reverb
|
||||
void configureReverb();
|
||||
void updateReverbOptions();
|
||||
|
||||
void handleLocalEchoAndReverb(QByteArray& inputByteArray);
|
||||
|
||||
#if defined(WEBRTC_ENABLED)
|
||||
static const int WEBRTC_SAMPLE_RATE_MAX = 96000;
|
||||
static const int WEBRTC_CHANNELS_MAX = 2;
|
||||
static const int WEBRTC_FRAMES_MAX = webrtc::AudioProcessing::kChunkSizeMs * WEBRTC_SAMPLE_RATE_MAX / 1000;
|
||||
|
||||
webrtc::AudioProcessing* _apm { nullptr };
|
||||
|
||||
int16_t _fifoFarEnd[WEBRTC_CHANNELS_MAX * WEBRTC_FRAMES_MAX] {};
|
||||
int _numFifoFarEnd = 0; // numFrames saved in fifo
|
||||
|
||||
void configureWebrtc();
|
||||
void processWebrtcFarEnd(const int16_t* samples, int numFrames, int numChannels, int sampleRate);
|
||||
void processWebrtcNearEnd(int16_t* samples, int numFrames, int numChannels, int sampleRate);
|
||||
#endif
|
||||
|
||||
bool switchInputToAudioDevice(const QAudioDeviceInfo inputDeviceInfo, bool isShutdownRequest = false);
|
||||
bool switchOutputToAudioDevice(const QAudioDeviceInfo outputDeviceInfo, bool isShutdownRequest = false);
|
||||
|
||||
|
|
|
@ -598,26 +598,6 @@ void Avatar::measureMotionDerivatives(float deltaTime) {
|
|||
}
|
||||
}
|
||||
|
||||
enum TextRendererType {
|
||||
CHAT,
|
||||
DISPLAYNAME
|
||||
};
|
||||
|
||||
static TextRenderer3D* textRenderer(TextRendererType type) {
|
||||
static TextRenderer3D* chatRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY, -1,
|
||||
false, SHADOW_EFFECT);
|
||||
static TextRenderer3D* displayNameRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY);
|
||||
|
||||
switch(type) {
|
||||
case CHAT:
|
||||
return chatRenderer;
|
||||
case DISPLAYNAME:
|
||||
return displayNameRenderer;
|
||||
}
|
||||
|
||||
return displayNameRenderer;
|
||||
}
|
||||
|
||||
void Avatar::metaBlendshapeOperator(render::ItemID renderItemID, int blendshapeNumber, const QVector<BlendshapeOffset>& blendshapeOffsets,
|
||||
const QVector<int>& blendedMeshSizes, const render::ItemIDs& subItemIDs) {
|
||||
render::Transaction transaction;
|
||||
|
@ -1050,7 +1030,6 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const
|
|||
|| (glm::dot(view.getDirection(), getDisplayNamePosition() - view.getPosition()) <= CLIP_DISTANCE)) {
|
||||
return;
|
||||
}
|
||||
auto renderer = textRenderer(DISPLAYNAME);
|
||||
|
||||
// optionally render timing stats for this avatar with the display name
|
||||
QString renderedDisplayName = _displayName;
|
||||
|
@ -1065,7 +1044,8 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const
|
|||
}
|
||||
|
||||
// Compute display name extent/position offset
|
||||
const glm::vec2 extent = renderer->computeExtent(renderedDisplayName);
|
||||
static TextRenderer3D* displayNameRenderer = TextRenderer3D::getInstance(ROBOTO_FONT_FAMILY);
|
||||
const glm::vec2 extent = displayNameRenderer->computeExtent(renderedDisplayName);
|
||||
if (!glm::any(glm::isCompNull(extent, EPSILON))) {
|
||||
const QRect nameDynamicRect = QRect(0, 0, (int)extent.x, (int)extent.y);
|
||||
const int text_x = -nameDynamicRect.width() / 2;
|
||||
|
@ -1104,11 +1084,11 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const
|
|||
QByteArray nameUTF8 = renderedDisplayName.toLocal8Bit();
|
||||
|
||||
// Render text slightly in front to avoid z-fighting
|
||||
textTransform.postTranslate(glm::vec3(0.0f, 0.0f, SLIGHTLY_IN_FRONT * renderer->getFontSize()));
|
||||
textTransform.postTranslate(glm::vec3(0.0f, 0.0f, SLIGHTLY_IN_FRONT * displayNameRenderer->getFontSize()));
|
||||
batch.setModelTransform(textTransform);
|
||||
{
|
||||
PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderText");
|
||||
renderer->draw(batch, text_x, -text_y, nameUTF8.data(), textColor, glm::vec2(-1.0f), true, forward);
|
||||
displayNameRenderer->draw(batch, text_x, -text_y, glm::vec2(-1.0f), nameUTF8.data(), textColor, true, forward);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2860,7 +2860,8 @@ glm::vec3 AvatarData::getAbsoluteJointTranslationInObjectFrame(int index) const
|
|||
/**jsdoc
|
||||
* Information on an attachment worn by the avatar.
|
||||
* @typedef {object} AttachmentData
|
||||
* @property {string} modelUrl - The URL of the model file. Models can be FBX or OBJ format.
|
||||
* @property {string} modelUrl - The URL of the glTF, FBX, or OBJ model file. glTF models may be in JSON or binary format
|
||||
* (".gltf" or ".glb" URLs respectively).
|
||||
* @property {string} jointName - The name of the joint that the attachment is parented to.
|
||||
* @property {Vec3} translation - The offset from the joint that the attachment is positioned at.
|
||||
* @property {Vec3} rotation - The rotation applied to the model relative to the joint orientation.
|
||||
|
|
|
@ -117,8 +117,7 @@ const int COLLIDE_WITH_OTHER_AVATARS = 11; // 12th bit
|
|||
const int HAS_HERO_PRIORITY = 12; // 13th bit (be scared)
|
||||
|
||||
/**jsdoc
|
||||
* <p>The pointing state of the hands is specified by the following values:
|
||||
</p>
|
||||
* <p>The pointing state of the hands is specified by the following values:</p>
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Value</th><th>Description</th>
|
||||
|
@ -1268,12 +1267,11 @@ public:
|
|||
/**jsdoc
|
||||
* Attaches a model to your avatar. For example, you can give your avatar a hat to wear, a guitar to hold, or a surfboard to
|
||||
* stand on.
|
||||
* <p>Note: Attached models are models only; they are not entities and can not be manipulated using the {@link Entities} API.
|
||||
* Nor can you use this function to attach an entity (such as a sphere or a box) to your avatar.</p>
|
||||
* @function Avatar.attach
|
||||
* @param {string} modelURL - The URL of the model to attach. Models can be .FBX or .OBJ format.
|
||||
* @param {string} [jointName=""] - The name of the avatar joint (see {@link MyAvatar.getJointNames} or {@link Avatar.getJointNames}) to attach the model
|
||||
* to.
|
||||
* @param {string} modelURL - The URL of the glTF, FBX, or OBJ model to attach. glTF models may be in JSON or binary format
|
||||
* (".gltf" or ".glb" URLs respectively).
|
||||
* @param {string} [jointName=""] - The name of the avatar joint (see {@link MyAvatar.getJointNames} or
|
||||
* {@link Avatar.getJointNames}) to attach the model to.
|
||||
* @param {Vec3} [translation=Vec3.ZERO] - The offset to apply to the model relative to the joint position.
|
||||
* @param {Quat} [rotation=Quat.IDENTITY] - The rotation to apply to the model relative to the joint orientation.
|
||||
* @param {number} [scale=1.0] - The scale to apply to the model.
|
||||
|
|
|
@ -483,3 +483,26 @@ glm::vec4 EntityRenderer::calculatePulseColor(const glm::vec4& color, const Puls
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
glm::vec3 EntityRenderer::calculatePulseColor(const glm::vec3& color, const PulsePropertyGroup& pulseProperties, quint64 start) {
|
||||
if (pulseProperties.getPeriod() == 0.0f || (pulseProperties.getColorMode() == PulseMode::NONE && pulseProperties.getAlphaMode() == PulseMode::NONE)) {
|
||||
return color;
|
||||
}
|
||||
|
||||
float t = ((float)(usecTimestampNow() - start)) / ((float)USECS_PER_SECOND);
|
||||
float pulse = 0.5f * (cosf(t * (2.0f * (float)M_PI) / pulseProperties.getPeriod()) + 1.0f) * (pulseProperties.getMax() - pulseProperties.getMin()) + pulseProperties.getMin();
|
||||
float outPulse = (1.0f - pulse);
|
||||
|
||||
glm::vec3 result = color;
|
||||
if (pulseProperties.getColorMode() == PulseMode::IN_PHASE) {
|
||||
result.r *= pulse;
|
||||
result.g *= pulse;
|
||||
result.b *= pulse;
|
||||
} else if (pulseProperties.getColorMode() == PulseMode::OUT_PHASE) {
|
||||
result.r *= outPulse;
|
||||
result.g *= outPulse;
|
||||
result.b *= outPulse;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -61,6 +61,7 @@ public:
|
|||
virtual scriptable::ScriptableModelBase getScriptableModel() override { return scriptable::ScriptableModelBase(); }
|
||||
|
||||
static glm::vec4 calculatePulseColor(const glm::vec4& color, const PulsePropertyGroup& pulseProperties, quint64 start);
|
||||
static glm::vec3 calculatePulseColor(const glm::vec3& color, const PulsePropertyGroup& pulseProperties, quint64 start);
|
||||
|
||||
virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) const override;
|
||||
virtual Item::Bound getBound() override;
|
||||
|
|
|
@ -30,7 +30,7 @@ const float LINE_SCALE_RATIO = 1.2f;
|
|||
|
||||
TextEntityRenderer::TextEntityRenderer(const EntityItemPointer& entity) :
|
||||
Parent(entity),
|
||||
_textRenderer(TextRenderer3D::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE / 2.0f)) {
|
||||
_textRenderer(TextRenderer3D::getInstance(ROBOTO_FONT_FAMILY)) {
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
if (geometryCache) {
|
||||
_geometryID = geometryCache->allocateID();
|
||||
|
@ -96,7 +96,6 @@ bool TextEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint
|
|||
if (_text != entity->getText()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_lineHeight != entity->getLineHeight()) {
|
||||
return true;
|
||||
}
|
||||
|
@ -104,15 +103,12 @@ bool TextEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint
|
|||
if (_textColor != toGlm(entity->getTextColor())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_textAlpha != entity->getTextAlpha()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_backgroundColor != toGlm(entity->getBackgroundColor())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_backgroundAlpha != entity->getBackgroundAlpha()) {
|
||||
return true;
|
||||
}
|
||||
|
@ -128,15 +124,12 @@ bool TextEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint
|
|||
if (_leftMargin != entity->getLeftMargin()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_rightMargin != entity->getRightMargin()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_topMargin != entity->getTopMargin()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_bottomMargin != entity->getBottomMargin()) {
|
||||
return true;
|
||||
}
|
||||
|
@ -145,6 +138,20 @@ bool TextEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint
|
|||
return true;
|
||||
}
|
||||
|
||||
if (_font != entity->getFont()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_effect != entity->getTextEffect()) {
|
||||
return true;
|
||||
}
|
||||
if (_effectColor != toGlm(entity->getTextEffectColor())) {
|
||||
return true;
|
||||
}
|
||||
if (_effectThickness != entity->getTextEffectThickness()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_pulseProperties != entity->getPulseProperties()) {
|
||||
return true;
|
||||
}
|
||||
|
@ -179,6 +186,10 @@ void TextEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointe
|
|||
_topMargin = entity->getTopMargin();
|
||||
_bottomMargin = entity->getBottomMargin();
|
||||
_unlit = entity->getUnlit();
|
||||
_font = entity->getFont();
|
||||
_effect = entity->getTextEffect();
|
||||
_effectColor = toGlm(entity->getTextEffectColor());
|
||||
_effectThickness = entity->getTextEffectThickness();
|
||||
updateTextRenderItem();
|
||||
});
|
||||
}
|
||||
|
@ -282,7 +293,22 @@ ItemKey entities::TextPayload::getKey() const {
|
|||
auto renderable = entityTreeRenderer->renderableForEntityId(_entityID);
|
||||
if (renderable) {
|
||||
auto textRenderable = std::static_pointer_cast<TextEntityRenderer>(renderable);
|
||||
return ItemKey::Builder(textRenderable->getKey()).withoutMetaCullGroup().withSubMetaCulled();
|
||||
|
||||
// Similar to RenderableEntityItem::getKey()
|
||||
ItemKey::Builder builder = ItemKey::Builder().withTypeShape().withTypeMeta().withTagBits(textRenderable->getTagMask()).withLayer(textRenderable->getHifiRenderLayer());
|
||||
builder.withSubMetaCulled();
|
||||
|
||||
if (textRenderable->isTextTransparent()) {
|
||||
builder.withTransparent();
|
||||
} else if (textRenderable->_canCastShadow) {
|
||||
builder.withShadowCaster();
|
||||
}
|
||||
|
||||
if (!textRenderable->_visible) {
|
||||
builder.withInvisible();
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
return ItemKey::Builder::opaqueShape();
|
||||
|
@ -342,27 +368,40 @@ void entities::TextPayload::render(RenderArgs* args) {
|
|||
}
|
||||
auto textRenderable = std::static_pointer_cast<TextEntityRenderer>(renderable);
|
||||
|
||||
glm::vec4 textColor;
|
||||
Transform modelTransform;
|
||||
BillboardMode billboardMode;
|
||||
float lineHeight, leftMargin, rightMargin, topMargin, bottomMargin;
|
||||
QString text;
|
||||
glm::vec3 dimensions;
|
||||
BillboardMode billboardMode;
|
||||
|
||||
QString text;
|
||||
glm::vec4 textColor;
|
||||
QString font;
|
||||
TextEffect effect;
|
||||
glm::vec3 effectColor;
|
||||
float effectThickness;
|
||||
float lineHeight, leftMargin, rightMargin, topMargin, bottomMargin;
|
||||
bool forward;
|
||||
textRenderable->withReadLock([&] {
|
||||
modelTransform = textRenderable->_renderTransform;
|
||||
dimensions = textRenderable->_dimensions;
|
||||
billboardMode = textRenderable->_billboardMode;
|
||||
|
||||
text = textRenderable->_text;
|
||||
font = textRenderable->_font;
|
||||
effect = textRenderable->_effect;
|
||||
effectThickness = textRenderable->_effectThickness;
|
||||
|
||||
lineHeight = textRenderable->_lineHeight;
|
||||
leftMargin = textRenderable->_leftMargin;
|
||||
rightMargin = textRenderable->_rightMargin;
|
||||
topMargin = textRenderable->_topMargin;
|
||||
bottomMargin = textRenderable->_bottomMargin;
|
||||
text = textRenderable->_text;
|
||||
dimensions = textRenderable->_dimensions;
|
||||
|
||||
float fadeRatio = textRenderable->_isFading ? Interpolate::calculateFadeRatio(textRenderable->_fadeStartTime) : 1.0f;
|
||||
textColor = glm::vec4(textRenderable->_textColor, fadeRatio * textRenderable->_textAlpha);
|
||||
textColor = EntityRenderer::calculatePulseColor(textColor, textRenderable->_pulseProperties, textRenderable->_created);
|
||||
|
||||
effectColor = EntityRenderer::calculatePulseColor(textRenderable->_effectColor, textRenderable->_pulseProperties, textRenderable->_created);
|
||||
|
||||
forward = textRenderable->_renderLayer != RenderLayer::WORLD || args->_renderMethod == render::Args::FORWARD;
|
||||
});
|
||||
|
||||
|
@ -378,7 +417,9 @@ void entities::TextPayload::render(RenderArgs* args) {
|
|||
batch.setModelTransform(modelTransform);
|
||||
|
||||
glm::vec2 bounds = glm::vec2(dimensions.x - (leftMargin + rightMargin), dimensions.y - (topMargin + bottomMargin));
|
||||
textRenderer->draw(batch, leftMargin / scale, -topMargin / scale, text, textColor, bounds / scale, textRenderable->_unlit, forward);
|
||||
textRenderer->draw(batch, leftMargin / scale, -topMargin / scale, bounds / scale, scale,
|
||||
text, font, textColor, effectColor, effectThickness, effect,
|
||||
textRenderable->_unlit, forward);
|
||||
}
|
||||
|
||||
namespace render {
|
||||
|
|
|
@ -67,6 +67,11 @@ private:
|
|||
BillboardMode _billboardMode;
|
||||
glm::vec3 _dimensions;
|
||||
|
||||
QString _font { "" };
|
||||
TextEffect _effect { TextEffect::NO_EFFECT };
|
||||
glm::vec3 _effectColor { 0 };
|
||||
float _effectThickness { 0.0f };
|
||||
|
||||
int _geometryID { 0 };
|
||||
|
||||
std::shared_ptr<TextPayload> _textPayload;
|
||||
|
|
|
@ -50,8 +50,10 @@ bool operator!=(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b
|
|||
/**jsdoc
|
||||
* An animation is configured by the following properties:
|
||||
* @typedef {object} Entities.AnimationProperties
|
||||
* @property {string} url="" - The URL of the FBX file that has the animation.
|
||||
* @property {boolean} allowTranslation=true - <code>true</code> to enable translations contained in the animation to be
|
||||
* @property {string} url="" - The URL of the glTF or FBX file that has the animation. glTF files may be in JSON or binary
|
||||
* format (".gltf" or ".glb" URLs respectively).
|
||||
* <p><strong>Warning:</strong> glTF animations currently do not always animate correctly.</p>
|
||||
* @property {boolean} allowTranslation=true - <code>true</code> to enable translations contained in the animation to be
|
||||
* played, <code>false</code> to disable translations.
|
||||
* @property {number} fps=30 - The speed in frames/s that the animation is played at.
|
||||
* @property {number} firstFrame=0 - The first frame to play in the animation.
|
||||
|
|
|
@ -299,6 +299,24 @@ void EntityItemProperties::setAvatarPriorityFromString(const QString& mode) {
|
|||
}
|
||||
}
|
||||
|
||||
inline void addTextEffect(QHash<QString, TextEffect>& lookup, TextEffect effect) { lookup[TextEffectHelpers::getNameForTextEffect(effect)] = effect; }
|
||||
const QHash<QString, TextEffect> stringToTextEffectLookup = [] {
|
||||
QHash<QString, TextEffect> toReturn;
|
||||
addTextEffect(toReturn, TextEffect::NO_EFFECT);
|
||||
addTextEffect(toReturn, TextEffect::OUTLINE_EFFECT);
|
||||
addTextEffect(toReturn, TextEffect::OUTLINE_WITH_FILL_EFFECT);
|
||||
addTextEffect(toReturn, TextEffect::SHADOW_EFFECT);
|
||||
return toReturn;
|
||||
}();
|
||||
QString EntityItemProperties::getTextEffectAsString() const { return TextEffectHelpers::getNameForTextEffect(_textEffect); }
|
||||
void EntityItemProperties::setTextEffectFromString(const QString& effect) {
|
||||
auto textEffectItr = stringToTextEffectLookup.find(effect.toLower());
|
||||
if (textEffectItr != stringToTextEffectLookup.end()) {
|
||||
_textEffect = textEffectItr.value();
|
||||
_textEffectChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
QString getCollisionGroupAsString(uint16_t group) {
|
||||
switch (group) {
|
||||
case USER_COLLISION_GROUP_DYNAMIC:
|
||||
|
@ -528,6 +546,10 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
CHECK_PROPERTY_CHANGE(PROP_TOP_MARGIN, topMargin);
|
||||
CHECK_PROPERTY_CHANGE(PROP_BOTTOM_MARGIN, bottomMargin);
|
||||
CHECK_PROPERTY_CHANGE(PROP_UNLIT, unlit);
|
||||
CHECK_PROPERTY_CHANGE(PROP_FONT, font);
|
||||
CHECK_PROPERTY_CHANGE(PROP_TEXT_EFFECT, textEffect);
|
||||
CHECK_PROPERTY_CHANGE(PROP_TEXT_EFFECT_COLOR, textEffectColor);
|
||||
CHECK_PROPERTY_CHANGE(PROP_TEXT_EFFECT_THICKNESS, textEffectThickness);
|
||||
|
||||
// Zone
|
||||
changedProperties += _keyLight.getChangedProperties();
|
||||
|
@ -958,8 +980,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* value is specified then the model is automatically sized to its
|
||||
* <code>{@link Entities.EntityProperties|naturalDimensions}</code>.
|
||||
* @property {string} modelURL="" - The URL of the glTF, FBX, or OBJ model. glTF models may be in JSON or binary format
|
||||
* (".gltf" or ".glb" URLs respectively). Baked FBX models' URLs end in ".baked.fbx". Model files may also be compressed in GZ
|
||||
* format, in which case the URL ends in ".gz".
|
||||
* (".gltf" or ".glb" URLs respectively). Baked models' URLs have ".baked" before the file type. Model files may also be
|
||||
* compressed in GZ format, in which case the URL ends in ".gz".
|
||||
* @property {Vec3} modelScale - The scale factor applied to the model's dimensions.
|
||||
* <p class="important">Deprecated: This property is deprecated and will be removed.</p>
|
||||
* @property {string} textures="" - A JSON string of texture name, URL pairs used when rendering the model in place of the
|
||||
|
@ -1285,8 +1307,13 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* @property {number} rightMargin=0.0 - The right margin, in meters.
|
||||
* @property {number} topMargin=0.0 - The top margin, in meters.
|
||||
* @property {number} bottomMargin=0.0 - The bottom margin, in meters.
|
||||
* @property {boolean} unlit=false - <code>true</code> if the entity should be unaffected by lighting. Otherwise, the text
|
||||
* is lit by the keylight and local lights.
|
||||
* @property {boolean} unlit=false - <code>true</code> if the entity is unaffected by lighting, <code>false</code> if it is lit
|
||||
* by the key light and local lights.
|
||||
* @property {string} font="" - The font to render the text with. It can be one of the following: <code>"Courier"</code,
|
||||
* <code>"Inconsolata"</code>, <code>"Roboto"</code>, <code>"Timeless"</code>, or a path to a .sdff file.
|
||||
* @property {Entities.TextEffect} textEffect="none" - The effect that is applied to the text.
|
||||
* @property {Color} textEffectColor=255,255,255 - The color of the effect.
|
||||
* @property {number} textEffectThickness=0.2 - The magnitude of the text effect, range <code>0.0</code> – <code>0.5</code>.
|
||||
* @property {BillboardMode} billboardMode="none" - Whether the entity is billboarded to face the camera.
|
||||
* @property {boolean} faceCamera - <code>true</code> if <code>billboardMode</code> is <code>"yaw"</code>, <code>false</code>
|
||||
* if it isn't. Setting this property to <code>false</code> sets the <code>billboardMode</code> to <code>"none"</code>.
|
||||
|
@ -1727,6 +1754,10 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TOP_MARGIN, topMargin);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_BOTTOM_MARGIN, bottomMargin);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_UNLIT, unlit);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_FONT, font);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_TEXT_EFFECT, textEffect, getTextEffectAsString());
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_TEXT_EFFECT_COLOR, textEffectColor, u8vec3Color);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXT_EFFECT_THICKNESS, textEffectThickness);
|
||||
}
|
||||
|
||||
// Zones only
|
||||
|
@ -2103,6 +2134,10 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
|
|||
COPY_PROPERTY_FROM_QSCRIPTVALUE(topMargin, float, setTopMargin);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(bottomMargin, float, setBottomMargin);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(unlit, bool, setUnlit);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(font, QString, setFont);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(textEffect, TextEffect);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(textEffectColor, u8vec3Color, setTextEffectColor);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(textEffectThickness, float, setTextEffectThickness);
|
||||
|
||||
// Zone
|
||||
_keyLight.copyFromScriptValue(object, _defaultSettings);
|
||||
|
@ -2387,6 +2422,10 @@ void EntityItemProperties::merge(const EntityItemProperties& other) {
|
|||
COPY_PROPERTY_IF_CHANGED(topMargin);
|
||||
COPY_PROPERTY_IF_CHANGED(bottomMargin);
|
||||
COPY_PROPERTY_IF_CHANGED(unlit);
|
||||
COPY_PROPERTY_IF_CHANGED(font);
|
||||
COPY_PROPERTY_IF_CHANGED(textEffect);
|
||||
COPY_PROPERTY_IF_CHANGED(textEffectColor);
|
||||
COPY_PROPERTY_IF_CHANGED(textEffectThickness);
|
||||
|
||||
// Zone
|
||||
_keyLight.merge(other._keyLight);
|
||||
|
@ -2746,6 +2785,10 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr
|
|||
ADD_PROPERTY_TO_MAP(PROP_TOP_MARGIN, TopMargin, topMargin, float);
|
||||
ADD_PROPERTY_TO_MAP(PROP_BOTTOM_MARGIN, BottomMargin, bottomMargin, float);
|
||||
ADD_PROPERTY_TO_MAP(PROP_UNLIT, Unlit, unlit, bool);
|
||||
ADD_PROPERTY_TO_MAP(PROP_FONT, Font, font, QString);
|
||||
ADD_PROPERTY_TO_MAP(PROP_TEXT_EFFECT, TextEffect, textEffect, TextEffect);
|
||||
ADD_PROPERTY_TO_MAP(PROP_TEXT_EFFECT_COLOR, TextEffectColor, textEffectColor, u8vec3Color);
|
||||
ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_TEXT_EFFECT_THICKNESS, TextEffectThickness, textEffectThickness, float, 0.0, 0.5);
|
||||
|
||||
// Zone
|
||||
{ // Keylight
|
||||
|
@ -3178,6 +3221,10 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
|
|||
APPEND_ENTITY_PROPERTY(PROP_TOP_MARGIN, properties.getTopMargin());
|
||||
APPEND_ENTITY_PROPERTY(PROP_BOTTOM_MARGIN, properties.getBottomMargin());
|
||||
APPEND_ENTITY_PROPERTY(PROP_UNLIT, properties.getUnlit());
|
||||
APPEND_ENTITY_PROPERTY(PROP_FONT, properties.getFont());
|
||||
APPEND_ENTITY_PROPERTY(PROP_TEXT_EFFECT, (uint32_t)properties.getTextEffect());
|
||||
APPEND_ENTITY_PROPERTY(PROP_TEXT_EFFECT_COLOR, properties.getTextEffectColor());
|
||||
APPEND_ENTITY_PROPERTY(PROP_TEXT_EFFECT_THICKNESS, properties.getTextEffectThickness());
|
||||
}
|
||||
|
||||
if (properties.getType() == EntityTypes::Zone) {
|
||||
|
@ -3657,6 +3704,10 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TOP_MARGIN, float, setTopMargin);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BOTTOM_MARGIN, float, setBottomMargin);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_UNLIT, bool, setUnlit);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FONT, QString, setFont);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT_EFFECT, TextEffect, setTextEffect);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT_EFFECT_COLOR, u8vec3Color, setTextEffectColor);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT_EFFECT_THICKNESS, float, setTextEffectThickness);
|
||||
}
|
||||
|
||||
if (properties.getType() == EntityTypes::Zone) {
|
||||
|
@ -4050,6 +4101,10 @@ void EntityItemProperties::markAllChanged() {
|
|||
_topMarginChanged = true;
|
||||
_bottomMarginChanged = true;
|
||||
_unlitChanged = true;
|
||||
_fontChanged = true;
|
||||
_textEffectChanged = true;
|
||||
_textEffectColorChanged = true;
|
||||
_textEffectThicknessChanged = true;
|
||||
|
||||
// Zone
|
||||
_keyLight.markAllChanged();
|
||||
|
@ -4642,6 +4697,18 @@ QList<QString> EntityItemProperties::listChangedProperties() {
|
|||
if (unlitChanged()) {
|
||||
out += "unlit";
|
||||
}
|
||||
if (fontChanged()) {
|
||||
out += "font";
|
||||
}
|
||||
if (textEffectChanged()) {
|
||||
out += "textEffect";
|
||||
}
|
||||
if (textEffectColorChanged()) {
|
||||
out += "textEffectColor";
|
||||
}
|
||||
if (textEffectThicknessChanged()) {
|
||||
out += "textEffectThickness";
|
||||
}
|
||||
|
||||
// Zone
|
||||
getKeyLight().listChangedProperties(out);
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <OctreeConstants.h>
|
||||
#include <ShapeInfo.h>
|
||||
#include <ColorUtils.h>
|
||||
#include "FontFamilies.h"
|
||||
|
||||
#include "EntityItemID.h"
|
||||
#include "EntityItemPropertiesDefaults.h"
|
||||
|
@ -64,6 +65,7 @@
|
|||
#include "PrimitiveMode.h"
|
||||
#include "WebInputMode.h"
|
||||
#include "GizmoType.h"
|
||||
#include "TextEffect.h"
|
||||
|
||||
const quint64 UNKNOWN_CREATED_TIME = 0;
|
||||
|
||||
|
@ -315,6 +317,10 @@ public:
|
|||
DEFINE_PROPERTY_REF(PROP_TOP_MARGIN, TopMargin, topMargin, float, TextEntityItem::DEFAULT_MARGIN);
|
||||
DEFINE_PROPERTY_REF(PROP_BOTTOM_MARGIN, BottomMargin, bottomMargin, float, TextEntityItem::DEFAULT_MARGIN);
|
||||
DEFINE_PROPERTY_REF(PROP_UNLIT, Unlit, unlit, bool, false);
|
||||
DEFINE_PROPERTY_REF(PROP_FONT, Font, font, QString, ROBOTO_FONT_FAMILY);
|
||||
DEFINE_PROPERTY_REF_ENUM(PROP_TEXT_EFFECT, TextEffect, textEffect, TextEffect, TextEffect::NO_EFFECT);
|
||||
DEFINE_PROPERTY_REF(PROP_TEXT_EFFECT_COLOR, TextEffectColor, textEffectColor, u8vec3Color, TextEntityItem::DEFAULT_TEXT_COLOR);
|
||||
DEFINE_PROPERTY(PROP_TEXT_EFFECT_THICKNESS, TextEffectThickness, textEffectThickness, float, TextEntityItem::DEFAULT_TEXT_EFFECT_THICKNESS);
|
||||
|
||||
// Zone
|
||||
DEFINE_PROPERTY_GROUP(KeyLight, keyLight, KeyLightPropertyGroup);
|
||||
|
|
|
@ -244,6 +244,10 @@ enum EntityPropertyList {
|
|||
PROP_TOP_MARGIN = PROP_DERIVED_8,
|
||||
PROP_BOTTOM_MARGIN = PROP_DERIVED_9,
|
||||
PROP_UNLIT = PROP_DERIVED_10,
|
||||
PROP_FONT = PROP_DERIVED_11,
|
||||
PROP_TEXT_EFFECT = PROP_DERIVED_12,
|
||||
PROP_TEXT_EFFECT_COLOR = PROP_DERIVED_13,
|
||||
PROP_TEXT_EFFECT_THICKNESS = PROP_DERIVED_14,
|
||||
|
||||
// Zone
|
||||
// Keylight
|
||||
|
|
|
@ -78,7 +78,7 @@ public:
|
|||
bool accurate { true };
|
||||
QUuid entityID;
|
||||
float distance { 0.0f };
|
||||
BoxFace face;
|
||||
BoxFace face { UNKNOWN_FACE };
|
||||
glm::vec3 intersection;
|
||||
glm::vec3 surfaceNormal;
|
||||
QVariantMap extraInfo;
|
||||
|
@ -94,7 +94,7 @@ public:
|
|||
QUuid entityID;
|
||||
float distance { 0.0f };
|
||||
float parabolicDistance { 0.0f };
|
||||
BoxFace face;
|
||||
BoxFace face { UNKNOWN_FACE };
|
||||
glm::vec3 intersection;
|
||||
glm::vec3 surfaceNormal;
|
||||
QVariantMap extraInfo;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue