mirror of
https://github.com/lubosz/overte.git
synced 2025-04-27 08:55:31 +02:00
merge with master
This commit is contained in:
commit
1b84244210
92 changed files with 1711 additions and 694 deletions
cmake
domain-server
resources/web/content/js
src
interface
resources/qml/hifi
src
launchers/win32
libraries
audio-client
avatars-renderer/src/avatars-renderer
entities-renderer/src
RenderableEntityItem.cppRenderableEntityItem.hRenderableTextEntityItem.cppRenderableTextEntityItem.h
entities/src
EntityItemProperties.cppEntityItemProperties.hEntityPropertyFlags.hEntityScriptingInterface.hTextEntityItem.cppTextEntityItem.h
gl/src/gl
gpu-gl-common/src/gpu/gl
networking/src
octree/src
platform
CMakeLists.txt
src/platform
render-utils/src
BloomEffect.cppRenderCommonTask.cppRenderCommonTask.hRenderDeferredTask.cppRenderForwardTask.cppRenderForwardTask.hTextRenderer3D.cppTextRenderer3D.hToneMapAndResampleTask.cppToneMapAndResampleTask.hToneMappingEffect.cpp
render-utils
sdf_text3D.slfsdf_text3D.slhsdf_text3D.slvtext
toneMapping.slfscript-engine/src
shared/src
ui/src/ui
scripts/developer/utilities/render
tools/oven/src
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'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -72,6 +72,9 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
|
|||
* @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 {boolean} acousticEchoCancellation - <code>true</code> if audio-echo-cancellation is enabled, otherwise
|
||||
* <code>false</code>. When enabled, sound from the audio output will be 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 +88,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 +120,7 @@ public:
|
|||
bool isMuted() const;
|
||||
bool noiseReductionEnabled() const;
|
||||
bool warnWhenMutedEnabled() const;
|
||||
bool acousticEchoCancellationEnabled() const;
|
||||
float getInputVolume() const;
|
||||
float getInputLevel() const;
|
||||
bool isClipping() const;
|
||||
|
@ -396,6 +402,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
|
||||
|
@ -494,6 +508,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 +521,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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,7 +70,7 @@ BOOL CLauncherApp::InitInstance() {
|
|||
if (uninstalling) {
|
||||
_manager.uninstall();
|
||||
} else {
|
||||
_manager.init(!noUpdate, continueUpdating, skipSplash);
|
||||
_manager.init(!noUpdate, continueAction);
|
||||
}
|
||||
if (!_manager.hasFailed() && !_manager.installLauncher()) {
|
||||
return FALSE;
|
||||
|
|
|
@ -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 {
|
||||
|
@ -693,22 +693,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;
|
||||
|
|
|
@ -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);
|
||||
|
@ -432,7 +460,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 +641,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);
|
||||
|
@ -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,17 @@ 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 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 +162,10 @@ 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 };
|
||||
ContinueActionOnStart _continueAction;
|
||||
float _progressOffset { 0.0f };
|
||||
float _progress { 0.0f };
|
||||
CStdioFile _logFile;
|
||||
};
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
@ -1084,6 +1090,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 +1393,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 +1712,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 +2254,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 +2308,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -482,3 +482,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();
|
||||
|
@ -127,6 +127,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();
|
||||
});
|
||||
}
|
||||
|
@ -230,7 +234,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();
|
||||
|
@ -290,27 +309,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;
|
||||
});
|
||||
|
||||
|
@ -326,7 +358,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;
|
||||
|
|
|
@ -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();
|
||||
|
@ -1287,6 +1309,11 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* @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 {string} font="" - The text is rendered with this font. 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 {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;
|
||||
|
|
|
@ -29,6 +29,7 @@ const glm::u8vec3 TextEntityItem::DEFAULT_TEXT_COLOR = { 255, 255, 255 };
|
|||
const float TextEntityItem::DEFAULT_TEXT_ALPHA = 1.0f;
|
||||
const glm::u8vec3 TextEntityItem::DEFAULT_BACKGROUND_COLOR = { 0, 0, 0};
|
||||
const float TextEntityItem::DEFAULT_MARGIN = 0.0f;
|
||||
const float TextEntityItem::DEFAULT_TEXT_EFFECT_THICKNESS = 0.2f;
|
||||
|
||||
EntityItemPointer TextEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
EntityItemPointer entity(new TextEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); });
|
||||
|
@ -65,6 +66,10 @@ EntityItemProperties TextEntityItem::getProperties(const EntityPropertyFlags& de
|
|||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(topMargin, getTopMargin);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(bottomMargin, getBottomMargin);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(unlit, getUnlit);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(font, getFont);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(textEffect, getTextEffect);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(textEffectColor, getTextEffectColor);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(textEffectThickness, getTextEffectThickness);
|
||||
return properties;
|
||||
}
|
||||
|
||||
|
@ -90,6 +95,10 @@ bool TextEntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(topMargin, setTopMargin);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(bottomMargin, setBottomMargin);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(unlit, setUnlit);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(font, setFont);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(textEffect, setTextEffect);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(textEffectColor, setTextEffectColor);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(textEffectThickness, setTextEffectThickness);
|
||||
|
||||
if (somethingChanged) {
|
||||
bool wantDebug = false;
|
||||
|
@ -133,6 +142,10 @@ int TextEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
|||
READ_ENTITY_PROPERTY(PROP_TOP_MARGIN, float, setTopMargin);
|
||||
READ_ENTITY_PROPERTY(PROP_BOTTOM_MARGIN, float, setBottomMargin);
|
||||
READ_ENTITY_PROPERTY(PROP_UNLIT, bool, setUnlit);
|
||||
READ_ENTITY_PROPERTY(PROP_FONT, QString, setFont);
|
||||
READ_ENTITY_PROPERTY(PROP_TEXT_EFFECT, TextEffect, setTextEffect);
|
||||
READ_ENTITY_PROPERTY(PROP_TEXT_EFFECT_COLOR, glm::u8vec3, setTextEffectColor);
|
||||
READ_ENTITY_PROPERTY(PROP_TEXT_EFFECT_THICKNESS, float, setTextEffectThickness);
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
@ -154,6 +167,10 @@ EntityPropertyFlags TextEntityItem::getEntityProperties(EncodeBitstreamParams& p
|
|||
requestedProperties += PROP_TOP_MARGIN;
|
||||
requestedProperties += PROP_BOTTOM_MARGIN;
|
||||
requestedProperties += PROP_UNLIT;
|
||||
requestedProperties += PROP_FONT;
|
||||
requestedProperties += PROP_TEXT_EFFECT;
|
||||
requestedProperties += PROP_TEXT_EFFECT_COLOR;
|
||||
requestedProperties += PROP_TEXT_EFFECT_THICKNESS;
|
||||
|
||||
return requestedProperties;
|
||||
}
|
||||
|
@ -185,6 +202,10 @@ void TextEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits
|
|||
APPEND_ENTITY_PROPERTY(PROP_TOP_MARGIN, getTopMargin());
|
||||
APPEND_ENTITY_PROPERTY(PROP_BOTTOM_MARGIN, getBottomMargin());
|
||||
APPEND_ENTITY_PROPERTY(PROP_UNLIT, getUnlit());
|
||||
APPEND_ENTITY_PROPERTY(PROP_FONT, getFont());
|
||||
APPEND_ENTITY_PROPERTY(PROP_TEXT_EFFECT, (uint32_t)getTextEffect());
|
||||
APPEND_ENTITY_PROPERTY(PROP_TEXT_EFFECT_COLOR, getTextEffectColor());
|
||||
APPEND_ENTITY_PROPERTY(PROP_TEXT_EFFECT_THICKNESS, getTextEffectThickness());
|
||||
}
|
||||
|
||||
glm::vec3 TextEntityItem::getRaycastDimensions() const {
|
||||
|
@ -258,11 +279,9 @@ void TextEntityItem::setText(const QString& value) {
|
|||
}
|
||||
|
||||
QString TextEntityItem::getText() const {
|
||||
QString result;
|
||||
withReadLock([&] {
|
||||
result = _text;
|
||||
return resultWithReadLock<QString>([&] {
|
||||
return _text;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
void TextEntityItem::setLineHeight(float value) {
|
||||
|
@ -412,6 +431,58 @@ bool TextEntityItem::getUnlit() const {
|
|||
});
|
||||
}
|
||||
|
||||
void TextEntityItem::setFont(const QString& value) {
|
||||
withWriteLock([&] {
|
||||
_needsRenderUpdate |= _font != value;
|
||||
_font = value;
|
||||
});
|
||||
}
|
||||
|
||||
QString TextEntityItem::getFont() const {
|
||||
return resultWithReadLock<QString>([&] {
|
||||
return _font;
|
||||
});
|
||||
}
|
||||
|
||||
void TextEntityItem::setTextEffect(TextEffect value) {
|
||||
withWriteLock([&] {
|
||||
_needsRenderUpdate |= _effect != value;
|
||||
_effect = value;
|
||||
});
|
||||
}
|
||||
|
||||
TextEffect TextEntityItem::getTextEffect() const {
|
||||
return resultWithReadLock<TextEffect>([&] {
|
||||
return _effect;
|
||||
});
|
||||
}
|
||||
|
||||
void TextEntityItem::setTextEffectColor(const glm::u8vec3& value) {
|
||||
withWriteLock([&] {
|
||||
_needsRenderUpdate |= _effectColor != value;
|
||||
_effectColor = value;
|
||||
});
|
||||
}
|
||||
|
||||
glm::u8vec3 TextEntityItem::getTextEffectColor() const {
|
||||
return resultWithReadLock<glm::u8vec3>([&] {
|
||||
return _effectColor;
|
||||
});
|
||||
}
|
||||
|
||||
void TextEntityItem::setTextEffectThickness(float value) {
|
||||
withWriteLock([&] {
|
||||
_needsRenderUpdate |= _effectThickness != value;
|
||||
_effectThickness = value;
|
||||
});
|
||||
}
|
||||
|
||||
float TextEntityItem::getTextEffectThickness() const {
|
||||
return resultWithReadLock<float>([&] {
|
||||
return _effectThickness;
|
||||
});
|
||||
}
|
||||
|
||||
PulsePropertyGroup TextEntityItem::getPulseProperties() const {
|
||||
return resultWithReadLock<PulsePropertyGroup>([&] {
|
||||
return _pulseProperties;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "EntityItem.h"
|
||||
|
||||
#include "PulsePropertyGroup.h"
|
||||
#include "TextEffect.h"
|
||||
|
||||
class TextEntityItem : public EntityItem {
|
||||
public:
|
||||
|
@ -100,6 +101,19 @@ public:
|
|||
bool getUnlit() const;
|
||||
void setUnlit(bool value);
|
||||
|
||||
void setFont(const QString& value);
|
||||
QString getFont() const;
|
||||
|
||||
TextEffect getTextEffect() const;
|
||||
void setTextEffect(TextEffect value);
|
||||
|
||||
glm::u8vec3 getTextEffectColor() const;
|
||||
void setTextEffectColor(const glm::u8vec3& value);
|
||||
|
||||
static const float DEFAULT_TEXT_EFFECT_THICKNESS;
|
||||
float getTextEffectThickness() const;
|
||||
void setTextEffectThickness(float value);
|
||||
|
||||
PulsePropertyGroup getPulseProperties() const;
|
||||
|
||||
private:
|
||||
|
@ -117,6 +131,11 @@ private:
|
|||
float _topMargin;
|
||||
float _bottomMargin;
|
||||
bool _unlit;
|
||||
|
||||
QString _font;
|
||||
TextEffect _effect;
|
||||
glm::u8vec3 _effectColor;
|
||||
float _effectThickness;
|
||||
};
|
||||
|
||||
#endif // hifi_TextEntityItem_h
|
||||
|
|
|
@ -148,6 +148,33 @@ static bool setupPixelFormatSimple(HDC hdc) {
|
|||
|
||||
#endif
|
||||
|
||||
|
||||
const gl::ContextInfo& gl::ContextInfo::get(bool init) {
|
||||
static gl::ContextInfo INSTANCE;
|
||||
if (init) {
|
||||
static std::once_flag once;
|
||||
std::call_once(once, [&] {
|
||||
INSTANCE.init();
|
||||
});
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
gl::ContextInfo& gl::ContextInfo::init() {
|
||||
if (glGetString) {
|
||||
version = (const char*)glGetString(GL_VERSION);
|
||||
shadingLanguageVersion = (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION);
|
||||
vendor = (const char*)glGetString(GL_VENDOR);
|
||||
renderer = (const char*)glGetString(GL_RENDERER);
|
||||
GLint n = 0;
|
||||
glGetIntegerv(GL_NUM_EXTENSIONS, &n);
|
||||
for (GLint i = 0; i < n; ++i) {
|
||||
extensions.emplace_back((const char*)glGetStringi(GL_EXTENSIONS, i));
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
uint16_t gl::getAvailableVersion() {
|
||||
static uint8_t major = 0, minor = 0;
|
||||
static std::once_flag once;
|
||||
|
@ -277,25 +304,6 @@ int glVersionToInteger(QString glVersion) {
|
|||
return (majorNumber << 16) | minorNumber;
|
||||
}
|
||||
|
||||
QJsonObject getGLContextData() {
|
||||
static QJsonObject result;
|
||||
static std::once_flag once;
|
||||
std::call_once(once, [] {
|
||||
QString glVersion = QString((const char*)glGetString(GL_VERSION));
|
||||
QString glslVersion = QString((const char*) glGetString(GL_SHADING_LANGUAGE_VERSION));
|
||||
QString glVendor = QString((const char*) glGetString(GL_VENDOR));
|
||||
QString glRenderer = QString((const char*)glGetString(GL_RENDERER));
|
||||
|
||||
result = QJsonObject {
|
||||
{ "version", glVersion },
|
||||
{ "sl_version", glslVersion },
|
||||
{ "vendor", glVendor },
|
||||
{ "renderer", glRenderer },
|
||||
};
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
QThread* RENDER_THREAD = nullptr;
|
||||
|
||||
bool isRenderThread() {
|
||||
|
|
|
@ -29,7 +29,6 @@ class QGLFormat;
|
|||
size_t evalGLFormatSwapchainPixelSize(const QSurfaceFormat& format);
|
||||
|
||||
const QSurfaceFormat& getDefaultOpenGLSurfaceFormat();
|
||||
QJsonObject getGLContextData();
|
||||
int glVersionToInteger(QString glVersion);
|
||||
|
||||
bool isRenderThread();
|
||||
|
@ -39,6 +38,26 @@ bool isRenderThread();
|
|||
#define GL_GET_MAJOR_VERSION(glversion) ((glversion & 0xFF00) >> 8)
|
||||
|
||||
namespace gl {
|
||||
struct ContextInfo {
|
||||
std::string version;
|
||||
std::string shadingLanguageVersion;
|
||||
std::string vendor;
|
||||
std::string renderer;
|
||||
std::vector<std::string> extensions;
|
||||
|
||||
ContextInfo& init();
|
||||
operator bool() const { return !version.empty(); }
|
||||
|
||||
static const ContextInfo& get(bool init = false);
|
||||
};
|
||||
|
||||
#define LOG_GL_CONTEXT_INFO(category, contextInfo) \
|
||||
qCDebug(category) << "GL Version: " << contextInfo.version.c_str(); \
|
||||
qCDebug(category) << "GL Shader Language Version: " << contextInfo.shadingLanguageVersion.c_str(); \
|
||||
qCDebug(category) << "GL Vendor: " << contextInfo.vendor.c_str(); \
|
||||
qCDebug(category) << "GL Renderer: " << contextInfo.renderer.c_str();
|
||||
|
||||
|
||||
void globalLock();
|
||||
void globalRelease(bool finish = true);
|
||||
|
||||
|
|
|
@ -33,14 +33,12 @@ GLWindow::~GLWindow() {
|
|||
bool GLWindow::makeCurrent() {
|
||||
bool makeCurrentResult = _context->makeCurrent();
|
||||
Q_ASSERT(makeCurrentResult);
|
||||
|
||||
std::call_once(_reportOnce, []{
|
||||
qCDebug(glLogging) << "GL Version: " << QString((const char*) glGetString(GL_VERSION));
|
||||
qCDebug(glLogging) << "GL Shader Language Version: " << QString((const char*) glGetString(GL_SHADING_LANGUAGE_VERSION));
|
||||
qCDebug(glLogging) << "GL Vendor: " << QString((const char*) glGetString(GL_VENDOR));
|
||||
qCDebug(glLogging) << "GL Renderer: " << QString((const char*) glGetString(GL_RENDERER));
|
||||
});
|
||||
|
||||
|
||||
if (makeCurrentResult) {
|
||||
std::call_once(_reportOnce, []{
|
||||
LOG_GL_CONTEXT_INFO(glLogging, gl::ContextInfo().init());
|
||||
});
|
||||
}
|
||||
return makeCurrentResult;
|
||||
}
|
||||
|
||||
|
|
|
@ -73,13 +73,9 @@ bool OffscreenGLCanvas::create(QOpenGLContext* sharedContext) {
|
|||
|
||||
bool OffscreenGLCanvas::makeCurrent() {
|
||||
bool result = _context->makeCurrent(_offscreenSurface);
|
||||
if (glGetString) {
|
||||
if (result) {
|
||||
std::call_once(_reportOnce, [] {
|
||||
qCDebug(glLogging) << "GL Version: " << QString((const char*)glGetString(GL_VERSION));
|
||||
qCDebug(glLogging) << "GL Shader Language Version: "
|
||||
<< QString((const char*)glGetString(GL_SHADING_LANGUAGE_VERSION));
|
||||
qCDebug(glLogging) << "GL Vendor: " << QString((const char*)glGetString(GL_VENDOR));
|
||||
qCDebug(glLogging) << "GL Renderer: " << QString((const char*)glGetString(GL_RENDERER));
|
||||
LOG_GL_CONTEXT_INFO(glLogging, gl::ContextInfo().init());
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -117,9 +117,10 @@ void GLBackend::init() {
|
|||
static std::once_flag once;
|
||||
std::call_once(once, [] {
|
||||
|
||||
|
||||
QString vendor{ (const char*)glGetString(GL_VENDOR) };
|
||||
QString renderer{ (const char*)glGetString(GL_RENDERER) };
|
||||
::gl::ContextInfo contextInfo;
|
||||
contextInfo.init();
|
||||
QString vendor { contextInfo.vendor.c_str() };
|
||||
QString renderer { contextInfo.renderer.c_str() };
|
||||
|
||||
// Textures
|
||||
GL_GET_INTEGER(MAX_TEXTURE_IMAGE_UNITS);
|
||||
|
@ -131,10 +132,7 @@ void GLBackend::init() {
|
|||
GL_GET_INTEGER(MAX_UNIFORM_BLOCK_SIZE);
|
||||
GL_GET_INTEGER(UNIFORM_BUFFER_OFFSET_ALIGNMENT);
|
||||
|
||||
qCDebug(gpugllogging) << "GL Version: " << QString((const char*) glGetString(GL_VERSION));
|
||||
qCDebug(gpugllogging) << "GL Shader Language Version: " << QString((const char*) glGetString(GL_SHADING_LANGUAGE_VERSION));
|
||||
qCDebug(gpugllogging) << "GL Vendor: " << vendor;
|
||||
qCDebug(gpugllogging) << "GL Renderer: " << renderer;
|
||||
LOG_GL_CONTEXT_INFO(gpugllogging, contextInfo);
|
||||
GPUIdent* gpu = GPUIdent::getInstance(vendor, renderer);
|
||||
// From here on, GPUIdent::getInstance()->getMumble() should efficiently give the same answers.
|
||||
qCDebug(gpugllogging) << "GPU:";
|
||||
|
|
|
@ -1143,6 +1143,10 @@ void NodeList::maybeSendIgnoreSetToNode(SharedNodePointer newNode) {
|
|||
}
|
||||
|
||||
void NodeList::setAvatarGain(const QUuid& nodeID, float gain) {
|
||||
if (nodeID.isNull()) {
|
||||
_avatarGain = gain;
|
||||
}
|
||||
|
||||
// cannot set gain of yourself
|
||||
if (getSessionUUID() != nodeID) {
|
||||
auto audioMixer = soloNodeOfType(NodeType::AudioMixer);
|
||||
|
@ -1160,7 +1164,6 @@ void NodeList::setAvatarGain(const QUuid& nodeID, float gain) {
|
|||
qCDebug(networking) << "Sending Set MASTER Avatar Gain packet with Gain:" << gain;
|
||||
|
||||
sendPacket(std::move(setAvatarGainPacket), *audioMixer);
|
||||
_avatarGain = gain;
|
||||
|
||||
} else {
|
||||
qCDebug(networking) << "Sending Set Avatar Gain packet with UUID:" << uuidStringWithoutCurlyBraces(nodeID) << "Gain:" << gain;
|
||||
|
@ -1192,6 +1195,8 @@ float NodeList::getAvatarGain(const QUuid& nodeID) {
|
|||
}
|
||||
|
||||
void NodeList::setInjectorGain(float gain) {
|
||||
_injectorGain = gain;
|
||||
|
||||
auto audioMixer = soloNodeOfType(NodeType::AudioMixer);
|
||||
if (audioMixer) {
|
||||
// setup the packet
|
||||
|
@ -1203,7 +1208,6 @@ void NodeList::setInjectorGain(float gain) {
|
|||
qCDebug(networking) << "Sending Set Injector Gain packet with Gain:" << gain;
|
||||
|
||||
sendPacket(std::move(setInjectorGainPacket), *audioMixer);
|
||||
_injectorGain = gain;
|
||||
|
||||
} else {
|
||||
qWarning() << "Couldn't find audio mixer to send set gain request";
|
||||
|
|
|
@ -273,6 +273,7 @@ enum class EntityVersion : PacketVersion {
|
|||
PrivateUserData,
|
||||
TextUnlit,
|
||||
ShadowBiasAndDistance,
|
||||
TextEntityFonts,
|
||||
|
||||
// Add new versions above here
|
||||
NUM_PACKET_TYPE,
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include "WebInputMode.h"
|
||||
#include "PulseMode.h"
|
||||
#include "GizmoType.h"
|
||||
#include "TextEffect.h"
|
||||
|
||||
#include "OctreeConstants.h"
|
||||
#include "OctreeElement.h"
|
||||
|
@ -273,6 +274,7 @@ public:
|
|||
static int unpackDataFromBytes(const unsigned char* dataBytes, WebInputMode& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }
|
||||
static int unpackDataFromBytes(const unsigned char* dataBytes, PulseMode& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }
|
||||
static int unpackDataFromBytes(const unsigned char* dataBytes, GizmoType& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }
|
||||
static int unpackDataFromBytes(const unsigned char* dataBytes, TextEffect& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }
|
||||
static int unpackDataFromBytes(const unsigned char* dataBytes, glm::vec2& result);
|
||||
static int unpackDataFromBytes(const unsigned char* dataBytes, glm::vec3& result);
|
||||
static int unpackDataFromBytes(const unsigned char* dataBytes, glm::u8vec3& result);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
set(TARGET_NAME platform)
|
||||
|
||||
setup_hifi_library()
|
||||
link_hifi_libraries(shared)
|
||||
link_hifi_libraries(shared gl)
|
||||
|
||||
GroupSources("src")
|
||||
target_json()
|
||||
|
@ -15,4 +15,4 @@ if (APPLE)
|
|||
|
||||
elseif(WIN32)
|
||||
target_link_libraries(${TARGET_NAME} Iphlpapi.lib)
|
||||
endif ()
|
||||
endif()
|
|
@ -34,6 +34,43 @@ namespace platform { namespace keys{
|
|||
extern const char* displays;
|
||||
extern const char* isMaster;
|
||||
}
|
||||
namespace graphicsAPI {
|
||||
extern const char* name;
|
||||
extern const char* version;
|
||||
extern const char* apiOpenGL;
|
||||
extern const char* apiVulkan;
|
||||
extern const char* apiDirect3D11;
|
||||
extern const char* apiDirect3D12;
|
||||
extern const char* apiMetal;
|
||||
namespace gl {
|
||||
extern const char* shadingLanguageVersion;
|
||||
extern const char* vendor;
|
||||
extern const char* renderer;
|
||||
extern const char* extensions;
|
||||
}
|
||||
namespace vk {
|
||||
extern const char* devices;
|
||||
namespace device {
|
||||
extern const char* apiVersion;
|
||||
extern const char* driverVersion;
|
||||
extern const char* deviceType;
|
||||
extern const char* vendor;
|
||||
extern const char* name;
|
||||
extern const char* formats;
|
||||
extern const char* extensions;
|
||||
extern const char* queues;
|
||||
extern const char* heaps;
|
||||
namespace heap {
|
||||
extern const char* flags;
|
||||
extern const char* size;
|
||||
}
|
||||
namespace queue {
|
||||
extern const char* flags;
|
||||
extern const char* count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
namespace nic {
|
||||
extern const char* mac;
|
||||
extern const char* name;
|
||||
|
@ -78,6 +115,7 @@ namespace platform { namespace keys{
|
|||
// Keys for categories used in json returned by getAll()
|
||||
extern const char* CPUS;
|
||||
extern const char* GPUS;
|
||||
extern const char* GRAPHICS_APIS;
|
||||
extern const char* DISPLAYS;
|
||||
extern const char* NICS;
|
||||
extern const char* MEMORY;
|
||||
|
|
|
@ -322,3 +322,8 @@ void MACOSInstance::enumerateComputer(){
|
|||
|
||||
}
|
||||
|
||||
void MACOSInstance::enumerateGraphicsApis() {
|
||||
Instance::enumerateGraphicsApis();
|
||||
|
||||
// TODO imeplement Metal query
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ namespace platform {
|
|||
void enumerateGpusAndDisplays() override;
|
||||
void enumerateMemory() override;
|
||||
void enumerateComputer() override;
|
||||
void enumerateGraphicsApis() override;
|
||||
};
|
||||
|
||||
} // namespace platform
|
||||
|
|
|
@ -35,6 +35,45 @@ namespace platform { namespace keys {
|
|||
const char* displays = "displays";
|
||||
const char* isMaster = "isMaster";
|
||||
}
|
||||
namespace graphicsAPI {
|
||||
const char* name = "name";
|
||||
const char* version = "version";
|
||||
|
||||
const char* apiOpenGL = "OpenGL";
|
||||
const char* apiVulkan = "Vulkan";
|
||||
const char* apiDirect3D11 = "D3D11";
|
||||
const char* apiDirect3D12 = "D3D12";
|
||||
const char* apiMetal = "Metal";
|
||||
|
||||
namespace gl {
|
||||
const char* shadingLanguageVersion = "shadingLanguageVersion";
|
||||
const char* vendor = "vendor";
|
||||
const char* renderer = "renderer";
|
||||
const char* extensions = "extensions";
|
||||
}
|
||||
namespace vk {
|
||||
const char* devices = "devices";
|
||||
namespace device {
|
||||
const char* apiVersion = "apiVersion";
|
||||
const char* driverVersion = "driverVersion";
|
||||
const char* deviceType = "deviceType";
|
||||
const char* vendor = "vendor";
|
||||
const char* name = "name";
|
||||
const char* formats = "formats";
|
||||
const char* extensions = "extensions";
|
||||
const char* heaps = "heaps";
|
||||
namespace heap {
|
||||
const char* flags = "flags";
|
||||
const char* size = "size";
|
||||
}
|
||||
const char* queues = "queues";
|
||||
namespace queue {
|
||||
const char* flags = "flags";
|
||||
const char* count = "count";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
namespace nic {
|
||||
const char* mac = "mac";
|
||||
const char* name = "name";
|
||||
|
@ -78,6 +117,7 @@ namespace platform { namespace keys {
|
|||
|
||||
const char* CPUS = "cpus";
|
||||
const char* GPUS = "gpus";
|
||||
const char* GRAPHICS_APIS = "graphicsAPIs";
|
||||
const char* DISPLAYS = "displays";
|
||||
const char* NICS = "nics";
|
||||
const char* MEMORY = "memory";
|
||||
|
|
|
@ -10,9 +10,18 @@
|
|||
#include "PlatformInstance.h"
|
||||
#include <QNetworkInterface>
|
||||
|
||||
#include <gl/GLHelpers.h>
|
||||
#include "../PlatformKeys.h"
|
||||
#include "../Profiler.h"
|
||||
|
||||
// For testing the vulkan dump
|
||||
//#define HAVE_VULKAN 1
|
||||
//#pragma comment(lib, "C:\\VulkanSDK\\1.1.101.0\\Lib\\vulkan-1.lib")
|
||||
|
||||
#ifdef HAVE_VULKAN
|
||||
#include <vulkan/vulkan.hpp>
|
||||
#endif
|
||||
|
||||
using namespace platform;
|
||||
|
||||
bool Instance::enumeratePlatform() {
|
||||
|
@ -30,6 +39,7 @@ bool Instance::enumeratePlatform() {
|
|||
enumerateCpus();
|
||||
enumerateGpusAndDisplays();
|
||||
enumerateNics();
|
||||
enumerateGraphicsApis();
|
||||
|
||||
// eval the master index for each platform scopes
|
||||
updateMasterIndices();
|
||||
|
@ -105,34 +115,102 @@ void Instance::enumerateNics() {
|
|||
}
|
||||
}
|
||||
|
||||
json Instance::getCPU(int index) {
|
||||
assert(index <(int) _cpus.size());
|
||||
#if defined(HAVE_VULKAN)
|
||||
static std::string vkVersionToString(uint32_t version) {
|
||||
return QString("%1.%2.%3").arg(VK_VERSION_MAJOR(version)).arg(VK_VERSION_MINOR(version)).arg(VK_VERSION_PATCH(version)).toStdString();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (index < 0 || (int) _cpus.size() <= index)
|
||||
|
||||
void Instance::enumerateGraphicsApis() {
|
||||
// OpenGL rendering API is supported on all platforms
|
||||
{
|
||||
auto& glContextInfo = gl::ContextInfo::get();
|
||||
json gl;
|
||||
gl[keys::graphicsAPI::name] = keys::graphicsAPI::apiOpenGL;
|
||||
gl[keys::graphicsAPI::version] = glContextInfo.version;
|
||||
gl[keys::graphicsAPI::gl::vendor] = glContextInfo.vendor;
|
||||
gl[keys::graphicsAPI::gl::renderer] = glContextInfo.renderer;
|
||||
gl[keys::graphicsAPI::gl::shadingLanguageVersion] = glContextInfo.shadingLanguageVersion;
|
||||
gl[keys::graphicsAPI::gl::extensions] = glContextInfo.extensions;
|
||||
_graphicsApis.push_back(gl);
|
||||
}
|
||||
|
||||
#if defined(HAVE_VULKAN)
|
||||
// Vulkan rendering API is supported on all platforms (sort of)
|
||||
{
|
||||
try {
|
||||
vk::ApplicationInfo appInfo{ "Interface", 1, "Luci", 1, VK_API_VERSION_1_1 };
|
||||
auto instancePtr = vk::createInstanceUnique({ {}, &appInfo });
|
||||
if (instancePtr) {
|
||||
json vkinfo;
|
||||
const auto& vkinstance = *instancePtr;
|
||||
vkinfo[keys::graphicsAPI::name] = keys::graphicsAPI::apiVulkan;
|
||||
vkinfo[keys::graphicsAPI::version] = vkVersionToString(VK_API_VERSION_1_1);
|
||||
for (const auto& physicalDevice : vkinstance.enumeratePhysicalDevices()) {
|
||||
json vkdevice;
|
||||
auto properties = physicalDevice.getProperties();
|
||||
vkdevice[keys::graphicsAPI::vk::device::driverVersion] = vkVersionToString(properties.driverVersion);
|
||||
vkdevice[keys::graphicsAPI::vk::device::apiVersion] = vkVersionToString(properties.apiVersion);
|
||||
vkdevice[keys::graphicsAPI::vk::device::deviceType] = vk::to_string(properties.deviceType);
|
||||
vkdevice[keys::graphicsAPI::vk::device::vendor] = properties.vendorID;
|
||||
vkdevice[keys::graphicsAPI::vk::device::name] = properties.deviceName;
|
||||
for (const auto& extensionProperties : physicalDevice.enumerateDeviceExtensionProperties()) {
|
||||
vkdevice[keys::graphicsAPI::vk::device::extensions].push_back(extensionProperties.extensionName);
|
||||
}
|
||||
|
||||
for (const auto& queueFamilyProperties : physicalDevice.getQueueFamilyProperties()) {
|
||||
json vkqueuefamily;
|
||||
vkqueuefamily[keys::graphicsAPI::vk::device::queue::flags] = vk::to_string(queueFamilyProperties.queueFlags);
|
||||
vkqueuefamily[keys::graphicsAPI::vk::device::queue::count] = queueFamilyProperties.queueCount;
|
||||
vkdevice[keys::graphicsAPI::vk::device::queues].push_back(vkqueuefamily);
|
||||
}
|
||||
auto memoryProperties = physicalDevice.getMemoryProperties();
|
||||
for (uint32_t heapIndex = 0; heapIndex < memoryProperties.memoryHeapCount; ++heapIndex) {
|
||||
json vkmemoryheap;
|
||||
const auto& heap = memoryProperties.memoryHeaps[heapIndex];
|
||||
vkmemoryheap[keys::graphicsAPI::vk::device::heap::flags] = vk::to_string(heap.flags);
|
||||
vkmemoryheap[keys::graphicsAPI::vk::device::heap::size] = heap.size;
|
||||
vkdevice[keys::graphicsAPI::vk::device::heaps].push_back(vkmemoryheap);
|
||||
}
|
||||
vkinfo[keys::graphicsAPI::vk::devices].push_back(vkdevice);
|
||||
}
|
||||
_graphicsApis.push_back(vkinfo);
|
||||
}
|
||||
} catch (const std::runtime_error&) {
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
json Instance::getCPU(int index) {
|
||||
assert(index < (int)_cpus.size());
|
||||
|
||||
if (index < 0 || (int)_cpus.size() <= index)
|
||||
return json();
|
||||
|
||||
return _cpus.at(index);
|
||||
}
|
||||
|
||||
json Instance::getGPU(int index) {
|
||||
assert(index <(int) _gpus.size());
|
||||
assert(index < (int)_gpus.size());
|
||||
|
||||
if (index < 0 || (int) _gpus.size() <= index)
|
||||
if (index < 0 || (int)_gpus.size() <= index)
|
||||
return json();
|
||||
|
||||
|
||||
return _gpus.at(index);
|
||||
}
|
||||
|
||||
|
||||
json Instance::getDisplay(int index) {
|
||||
assert(index <(int) _displays.size());
|
||||
|
||||
if (index < 0 || (int) _displays.size() <= index)
|
||||
assert(index < (int)_displays.size());
|
||||
|
||||
if (index < 0 || (int)_displays.size() <= index)
|
||||
return json();
|
||||
|
||||
return _displays.at(index);
|
||||
}
|
||||
|
||||
|
||||
Instance::~Instance() {
|
||||
if (_cpus.size() > 0) {
|
||||
_cpus.clear();
|
||||
|
@ -147,7 +225,6 @@ Instance::~Instance() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
json Instance::listAllKeys() {
|
||||
json allKeys;
|
||||
allKeys.array({{
|
||||
|
@ -167,6 +244,14 @@ json Instance::listAllKeys() {
|
|||
keys::gpu::driver,
|
||||
keys::gpu::displays,
|
||||
|
||||
keys::graphicsAPI::version,
|
||||
keys::graphicsAPI::name,
|
||||
|
||||
keys::graphicsAPI::gl::shadingLanguageVersion,
|
||||
keys::graphicsAPI::gl::vendor,
|
||||
keys::graphicsAPI::gl::renderer,
|
||||
keys::graphicsAPI::gl::extensions,
|
||||
|
||||
keys::display::boundsLeft,
|
||||
keys::display::boundsRight,
|
||||
keys::display::boundsTop,
|
||||
|
@ -188,6 +273,7 @@ json Instance::listAllKeys() {
|
|||
|
||||
keys::CPUS,
|
||||
keys::GPUS,
|
||||
keys::GRAPHICS_APIS,
|
||||
keys::DISPLAYS,
|
||||
keys::MEMORY,
|
||||
keys::COMPUTER,
|
||||
|
@ -219,6 +305,7 @@ json Instance::getAll() {
|
|||
all[keys::MEMORY] = _memory;
|
||||
all[keys::CPUS] = _cpus;
|
||||
all[keys::GPUS] = _gpus;
|
||||
all[keys::GRAPHICS_APIS] = _graphicsApis;
|
||||
all[keys::DISPLAYS] = _displays;
|
||||
all[keys::NICS] = _nics;
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ public:
|
|||
void virtual enumerateNics();
|
||||
void virtual enumerateMemory() = 0;
|
||||
void virtual enumerateComputer()=0;
|
||||
virtual void enumerateGraphicsApis();
|
||||
|
||||
virtual ~Instance();
|
||||
|
||||
|
@ -57,6 +58,7 @@ protected:
|
|||
std::vector<json> _gpus;
|
||||
std::vector<json> _displays;
|
||||
std::vector<json> _nics;
|
||||
json _graphicsApis;
|
||||
json _memory;
|
||||
json _computer;
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
#include <dxgi1_3.h>
|
||||
#pragma comment(lib, "dxgi.lib")
|
||||
#include <shellscalingapi.h>
|
||||
#pragma comment(lib, "Shcore.lib")
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -118,10 +117,19 @@ void WINInstance::enumerateGpusAndDisplays() {
|
|||
// Grab the dpi info for the monitor
|
||||
UINT dpiX{ 0 };
|
||||
UINT dpiY{ 0 };
|
||||
GetDpiForMonitor(outputDesc.Monitor, MDT_RAW_DPI, &dpiX, &dpiY);
|
||||
UINT dpiXScaled{ 0 };
|
||||
UINT dpiYScaled{ 0 };
|
||||
GetDpiForMonitor(outputDesc.Monitor, MDT_EFFECTIVE_DPI, &dpiXScaled, &dpiYScaled);
|
||||
|
||||
// SHCore.dll is not available prior to Windows 8.1
|
||||
HMODULE SHCoreDLL = LoadLibraryW(L"SHCore.dll");
|
||||
if (SHCoreDLL) {
|
||||
auto _GetDpiForMonitor = reinterpret_cast<decltype(GetDpiForMonitor)*>(GetProcAddress(SHCoreDLL, "GetDpiForMonitor"));
|
||||
if (_GetDpiForMonitor) {
|
||||
_GetDpiForMonitor(outputDesc.Monitor, MDT_RAW_DPI, &dpiX, &dpiY);
|
||||
_GetDpiForMonitor(outputDesc.Monitor, MDT_EFFECTIVE_DPI, &dpiXScaled, &dpiYScaled);
|
||||
}
|
||||
FreeLibrary(SHCoreDLL);
|
||||
}
|
||||
|
||||
// Current display mode
|
||||
DEVMODEW devMode;
|
||||
|
@ -251,3 +259,9 @@ void WINInstance::enumerateNics() {
|
|||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void WINInstance::enumerateGraphicsApis() {
|
||||
Instance::enumerateGraphicsApis();
|
||||
|
||||
// TODO imeplement D3D 11/12 queries
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ namespace platform {
|
|||
void enumerateMemory() override;
|
||||
void enumerateComputer () override;
|
||||
void enumerateNics() override;
|
||||
void enumerateGraphicsApis() override;
|
||||
};
|
||||
} // namespace platform
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#include <shaders/Shaders.h>
|
||||
|
||||
#include <render/BlurTask.h>
|
||||
#include <render/ResampleTask.h>
|
||||
#include "render-utils/ShaderConstants.h"
|
||||
|
||||
#define BLOOM_BLUR_LEVEL_COUNT 3
|
||||
|
|
|
@ -148,6 +148,30 @@ void Blit::run(const RenderContextPointer& renderContext, const gpu::Framebuffer
|
|||
});
|
||||
}
|
||||
|
||||
NewFramebuffer::NewFramebuffer(gpu::Element pixelFormat) {
|
||||
_pixelFormat = pixelFormat;
|
||||
}
|
||||
|
||||
void NewFramebuffer::run(const render::RenderContextPointer& renderContext, Output& output) {
|
||||
RenderArgs* args = renderContext->args;
|
||||
glm::uvec2 frameSize(args->_viewport.z, args->_viewport.w);
|
||||
output.reset();
|
||||
|
||||
if (_outputFramebuffer && _outputFramebuffer->getSize() != frameSize) {
|
||||
_outputFramebuffer.reset();
|
||||
}
|
||||
|
||||
if (!_outputFramebuffer) {
|
||||
_outputFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("newFramebuffer.out"));
|
||||
auto colorFormat = _pixelFormat;
|
||||
auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR);
|
||||
auto colorTexture = gpu::Texture::createRenderBuffer(colorFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler);
|
||||
_outputFramebuffer->setRenderBuffer(0, colorTexture);
|
||||
}
|
||||
|
||||
output = _outputFramebuffer;
|
||||
}
|
||||
|
||||
void NewOrDefaultFramebuffer::run(const render::RenderContextPointer& renderContext, const Input& input, Output& output) {
|
||||
RenderArgs* args = renderContext->args;
|
||||
// auto frameSize = input;
|
||||
|
@ -167,7 +191,7 @@ void NewOrDefaultFramebuffer::run(const render::RenderContextPointer& renderCont
|
|||
}
|
||||
|
||||
if (!_outputFramebuffer) {
|
||||
_outputFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("newFramebuffer.out"));
|
||||
_outputFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("newOrDefaultFramebuffer.out"));
|
||||
auto colorFormat = gpu::Element::COLOR_SRGBA_32;
|
||||
auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR);
|
||||
auto colorTexture = gpu::Texture::createRenderBuffer(colorFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler);
|
||||
|
|
|
@ -83,6 +83,20 @@ public:
|
|||
void run(const render::RenderContextPointer& renderContext, const gpu::FramebufferPointer& srcFramebuffer);
|
||||
};
|
||||
|
||||
class NewFramebuffer {
|
||||
public:
|
||||
using Output = gpu::FramebufferPointer;
|
||||
using JobModel = render::Job::ModelO<NewFramebuffer, Output>;
|
||||
|
||||
NewFramebuffer(gpu::Element pixelFormat = gpu::Element::COLOR_SRGBA_32);
|
||||
|
||||
void run(const render::RenderContextPointer& renderContext, Output& output);
|
||||
protected:
|
||||
gpu::Element _pixelFormat;
|
||||
private:
|
||||
gpu::FramebufferPointer _outputFramebuffer;
|
||||
};
|
||||
|
||||
class NewOrDefaultFramebuffer {
|
||||
public:
|
||||
using Input = glm::uvec2;
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
#include <render/DrawStatus.h>
|
||||
#include <render/DrawSceneOctree.h>
|
||||
#include <render/BlurTask.h>
|
||||
#include <render/ResampleTask.h>
|
||||
|
||||
#include "RenderHifi.h"
|
||||
#include "render-utils/ShaderConstants.h"
|
||||
|
@ -51,7 +50,7 @@
|
|||
|
||||
#include "AmbientOcclusionEffect.h"
|
||||
#include "AntialiasingEffect.h"
|
||||
#include "ToneMappingEffect.h"
|
||||
#include "ToneMapAndResampleTask.h"
|
||||
#include "SubsurfaceScattering.h"
|
||||
#include "DrawHaze.h"
|
||||
#include "BloomEffect.h"
|
||||
|
@ -96,7 +95,7 @@ RenderDeferredTask::RenderDeferredTask()
|
|||
|
||||
void RenderDeferredTask::configure(const Config& config) {
|
||||
// Propagate resolution scale to sub jobs who need it
|
||||
auto preparePrimaryBufferConfig = config.getConfig<PreparePrimaryFramebuffer>("PreparePrimaryBuffer");
|
||||
auto preparePrimaryBufferConfig = config.getConfig<PreparePrimaryFramebuffer>("PreparePrimaryBufferDeferred");
|
||||
assert(preparePrimaryBufferConfig);
|
||||
preparePrimaryBufferConfig->setResolutionScale(config.resolutionScale);
|
||||
}
|
||||
|
@ -146,7 +145,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
|
|||
const auto jitter = task.addJob<JitterSample>("JitterCam");
|
||||
|
||||
// GPU jobs: Start preparing the primary, deferred and lighting buffer
|
||||
const auto scaledPrimaryFramebuffer = task.addJob<PreparePrimaryFramebuffer>("PreparePrimaryBuffer");
|
||||
const auto scaledPrimaryFramebuffer = task.addJob<PreparePrimaryFramebuffer>("PreparePrimaryBufferDeferred");
|
||||
|
||||
// Prepare deferred, generate the shared Deferred Frame Transform. Only valid with the scaled frame buffer
|
||||
const auto deferredFrameTransform = task.addJob<GenerateDeferredFrameTransform>("DeferredFrameTransform", jitter);
|
||||
|
@ -238,23 +237,22 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
|
|||
const auto bloomInputs = BloomEffect::Inputs(deferredFrameTransform, lightingFramebuffer, bloomFrame).asVarying();
|
||||
task.addJob<BloomEffect>("Bloom", bloomInputs);
|
||||
|
||||
const auto destFramebuffer = static_cast<gpu::FramebufferPointer>(nullptr);
|
||||
|
||||
// Lighting Buffer ready for tone mapping
|
||||
const auto toneMappingInputs = ToneMappingDeferred::Input(lightingFramebuffer, scaledPrimaryFramebuffer).asVarying();
|
||||
const auto toneMappedBuffer = task.addJob<ToneMappingDeferred>("ToneMapping", toneMappingInputs);
|
||||
const auto toneMappingInputs = ToneMapAndResample::Input(lightingFramebuffer, destFramebuffer).asVarying();
|
||||
const auto toneMappedBuffer = task.addJob<ToneMapAndResample>("ToneMapping", toneMappingInputs);
|
||||
|
||||
// Debugging task is happening in the "over" layer after tone mapping and just before HUD
|
||||
{ // Debug the bounds of the rendered items, still look at the zbuffer
|
||||
const auto extraDebugBuffers = RenderDeferredTaskDebug::ExtraBuffers(linearDepthTarget, surfaceGeometryFramebuffer, ambientOcclusionFramebuffer, ambientOcclusionUniforms, scatteringResource, velocityBuffer);
|
||||
const auto debugInputs = RenderDeferredTaskDebug::Input(fetchedItems, shadowTaskOutputs, lightingStageInputs, lightClusters, prepareDeferredOutputs, extraDebugBuffers,
|
||||
deferredFrameTransform, jitter, lightingModel).asVarying();
|
||||
deferredFrameTransform, jitter, lightingModel).asVarying();
|
||||
task.addJob<RenderDeferredTaskDebug>("DebugRenderDeferredTask", debugInputs);
|
||||
}
|
||||
|
||||
// Upscale to finale resolution
|
||||
const auto primaryFramebuffer = task.addJob<render::UpsampleToBlitFramebuffer>("PrimaryBufferUpscale", toneMappedBuffer);
|
||||
|
||||
// HUD Layer
|
||||
const auto renderHUDLayerInputs = RenderHUDLayerTask::Input(primaryFramebuffer, lightingModel, hudOpaque, hudTransparent, hazeFrame).asVarying();
|
||||
const auto renderHUDLayerInputs = RenderHUDLayerTask::Input(toneMappedBuffer, lightingModel, hudOpaque, hudTransparent, hazeFrame).asVarying();
|
||||
task.addJob<RenderHUDLayerTask>("RenderHUDLayer", renderHUDLayerInputs);
|
||||
}
|
||||
|
||||
|
@ -415,7 +413,6 @@ void RenderDeferredTaskDebug::build(JobModel& task, const render::Varying& input
|
|||
|
||||
const auto debugZoneInputs = DebugZoneLighting::Inputs(deferredFrameTransform, lightFrame, backgroundFrame).asVarying();
|
||||
task.addJob<DebugZoneLighting>("DrawZoneStack", debugZoneInputs);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include <gpu/Texture.h>
|
||||
#include <graphics/ShaderConstants.h>
|
||||
#include <render/ShapePipeline.h>
|
||||
#include <render/ResampleTask.h>
|
||||
|
||||
#include <render/FilterTask.h>
|
||||
|
||||
|
@ -28,7 +27,7 @@
|
|||
#include "StencilMaskPass.h"
|
||||
#include "ZoneRenderer.h"
|
||||
#include "FadeEffect.h"
|
||||
#include "ToneMappingEffect.h"
|
||||
#include "ToneMapAndResampleTask.h"
|
||||
#include "BackgroundStage.h"
|
||||
#include "FramebufferCache.h"
|
||||
#include "TextureCache.h"
|
||||
|
@ -51,7 +50,7 @@ extern void initForwardPipelines(ShapePlumber& plumber);
|
|||
|
||||
void RenderForwardTask::configure(const Config& config) {
|
||||
// Propagate resolution scale to sub jobs who need it
|
||||
auto preparePrimaryBufferConfig = config.getConfig<PreparePrimaryFramebufferMSAA>("PreparePrimaryBuffer");
|
||||
auto preparePrimaryBufferConfig = config.getConfig<PreparePrimaryFramebufferMSAA>("PreparePrimaryBufferForward");
|
||||
assert(preparePrimaryBufferConfig);
|
||||
preparePrimaryBufferConfig->setResolutionScale(config.resolutionScale);
|
||||
}
|
||||
|
@ -99,7 +98,7 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend
|
|||
|
||||
|
||||
// GPU jobs: Start preparing the main framebuffer
|
||||
const auto scaledPrimaryFramebuffer = task.addJob<PreparePrimaryFramebufferMSAA>("PreparePrimaryBuffer");
|
||||
const auto scaledPrimaryFramebuffer = task.addJob<PreparePrimaryFramebufferMSAA>("PreparePrimaryBufferForward");
|
||||
|
||||
// Prepare deferred, generate the shared Deferred Frame Transform. Only valid with the scaled frame buffer
|
||||
const auto deferredFrameTransform = task.addJob<GenerateDeferredFrameTransform>("DeferredFrameTransform");
|
||||
|
@ -141,34 +140,17 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend
|
|||
task.addJob<DebugZoneLighting>("DrawZoneStack", debugZoneInputs);
|
||||
}
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
const auto newResolvedFramebuffer = task.addJob<NewFramebuffer>("MakeResolvingFramebuffer", gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::R11G11B10));
|
||||
|
||||
// Just resolve the msaa
|
||||
const auto resolveInputs = ResolveFramebuffer::Inputs(scaledPrimaryFramebuffer, static_cast<gpu::FramebufferPointer>(nullptr)).asVarying();
|
||||
const auto resolvedFramebuffer = task.addJob<ResolveFramebuffer>("Resolve", resolveInputs);
|
||||
|
||||
const auto toneMappedBuffer = resolvedFramebuffer;
|
||||
#else
|
||||
const auto newResolvedFramebuffer = task.addJob<NewOrDefaultFramebuffer>("MakeResolvingFramebuffer");
|
||||
|
||||
|
||||
// Just resolve the msaa
|
||||
const auto resolveInputs = ResolveFramebuffer::Inputs(scaledPrimaryFramebuffer, newResolvedFramebuffer).asVarying();
|
||||
const auto resolvedFramebuffer = task.addJob<ResolveFramebuffer>("Resolve", resolveInputs);
|
||||
|
||||
// Lighting Buffer ready for tone mapping
|
||||
// Forward rendering on GLES doesn't support tonemapping to and from the same FBO, so we specify
|
||||
// the output FBO as null, which causes the tonemapping to target the blit framebuffer
|
||||
const auto toneMappingInputs = ToneMappingDeferred::Input(resolvedFramebuffer, resolvedFramebuffer).asVarying();
|
||||
const auto toneMappedBuffer = task.addJob<ToneMappingDeferred>("ToneMapping", toneMappingInputs);
|
||||
|
||||
#endif
|
||||
|
||||
// Upscale to finale resolution
|
||||
const auto primaryFramebuffer = task.addJob<render::UpsampleToBlitFramebuffer>("PrimaryBufferUpscale", toneMappedBuffer);
|
||||
const auto destFramebuffer = static_cast<gpu::FramebufferPointer>(nullptr);
|
||||
|
||||
const auto toneMappingInputs = ToneMapAndResample::Input(resolvedFramebuffer, destFramebuffer).asVarying();
|
||||
const auto toneMappedBuffer = task.addJob<ToneMapAndResample>("ToneMapping", toneMappingInputs);
|
||||
// HUD Layer
|
||||
const auto renderHUDLayerInputs = RenderHUDLayerTask::Input(primaryFramebuffer, lightingModel, hudOpaque, hudTransparent, hazeFrame).asVarying();
|
||||
const auto renderHUDLayerInputs = RenderHUDLayerTask::Input(toneMappedBuffer, lightingModel, hudOpaque, hudTransparent, hazeFrame).asVarying();
|
||||
task.addJob<RenderHUDLayerTask>("RenderHUDLayer", renderHUDLayerInputs);
|
||||
}
|
||||
|
||||
|
@ -176,8 +158,8 @@ gpu::FramebufferPointer PreparePrimaryFramebufferMSAA::createFramebuffer(const c
|
|||
gpu::FramebufferPointer framebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create(name));
|
||||
|
||||
auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR);
|
||||
|
||||
auto colorFormat = gpu::Element::COLOR_SRGBA_32;
|
||||
|
||||
auto colorFormat = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::R11G11B10);
|
||||
auto colorTexture =
|
||||
gpu::Texture::createRenderBufferMultisample(colorFormat, frameSize.x, frameSize.y, numSamples, defaultSampler);
|
||||
framebuffer->setRenderBuffer(0, colorTexture);
|
||||
|
|
|
@ -50,11 +50,13 @@ public:
|
|||
const float SCALE_RANGE_MIN = 0.1f;
|
||||
const float SCALE_RANGE_MAX = 2.0f;
|
||||
resolutionScale = std::max(SCALE_RANGE_MIN, std::min(SCALE_RANGE_MAX, scale));
|
||||
//emit dirty();
|
||||
}
|
||||
|
||||
int getNumSamples() const { return numSamples; }
|
||||
void setNumSamples(int num) {
|
||||
numSamples = std::max(1, std::min(32, num));
|
||||
emit dirty();
|
||||
}
|
||||
|
||||
signals:
|
||||
|
|
|
@ -10,45 +10,19 @@
|
|||
//
|
||||
|
||||
#include "TextRenderer3D.h"
|
||||
#include <StreamHelpers.h>
|
||||
|
||||
#include <gpu/Shader.h>
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QImage>
|
||||
#include <QStringList>
|
||||
#include <QFile>
|
||||
|
||||
#include <shaders/Shaders.h>
|
||||
#include "text/Font.h"
|
||||
|
||||
#include "GLMHelpers.h"
|
||||
#include "MatrixStack.h"
|
||||
#include "RenderUtilsLogging.h"
|
||||
|
||||
#include "GeometryCache.h"
|
||||
|
||||
const float TextRenderer3D::DEFAULT_POINT_SIZE = 12;
|
||||
|
||||
TextRenderer3D* TextRenderer3D::getInstance(const char* family, float pointSize,
|
||||
bool bold, bool italic, EffectType effect, int effectThickness) {
|
||||
return new TextRenderer3D(family, pointSize, false, italic, effect, effectThickness);
|
||||
TextRenderer3D* TextRenderer3D::getInstance(const char* family) {
|
||||
return new TextRenderer3D(family);
|
||||
}
|
||||
|
||||
TextRenderer3D::TextRenderer3D(const char* family, float pointSize, int weight, bool italic,
|
||||
EffectType effect, int effectThickness) :
|
||||
_effectType(effect),
|
||||
_effectThickness(effectThickness),
|
||||
TextRenderer3D::TextRenderer3D(const char* family) :
|
||||
_family(family),
|
||||
_font(Font::load(family)) {
|
||||
if (!_font) {
|
||||
qWarning() << "Unable to load font with family " << family;
|
||||
_font = Font::load("Courier");
|
||||
}
|
||||
if (1 != _effectThickness) {
|
||||
qWarning() << "Effect thickness not currently supported";
|
||||
}
|
||||
if (NO_EFFECT != _effectType && OUTLINE_EFFECT != _effectType) {
|
||||
qWarning() << "Effect type not currently supported";
|
||||
_font = Font::load(ROBOTO_FONT_FAMILY);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,12 +40,21 @@ float TextRenderer3D::getFontSize() const {
|
|||
return 0.0f;
|
||||
}
|
||||
|
||||
void TextRenderer3D::draw(gpu::Batch& batch, float x, float y, const QString& str, const glm::vec4& color,
|
||||
const glm::vec2& bounds, bool unlit, bool forward) {
|
||||
// The font does all the OpenGL work
|
||||
void TextRenderer3D::draw(gpu::Batch& batch, float x, float y, const glm::vec2& bounds,
|
||||
const QString& str, const glm::vec4& color, bool unlit, bool forward) {
|
||||
if (_font) {
|
||||
_color = color;
|
||||
_font->drawString(batch, _drawInfo, str, _color, _effectType, { x, y }, bounds, unlit, forward);
|
||||
_font->drawString(batch, _drawInfo, str, color, glm::vec3(0.0f), 0, TextEffect::NO_EFFECT, { x, y }, bounds, 1.0f, unlit, forward);
|
||||
}
|
||||
}
|
||||
|
||||
void TextRenderer3D::draw(gpu::Batch& batch, float x, float y, const glm::vec2& bounds, float scale,
|
||||
const QString& str, const QString& font, const glm::vec4& color, const glm::vec3& effectColor,
|
||||
float effectThickness, TextEffect effect, bool unlit, bool forward) {
|
||||
if (font != _family) {
|
||||
_family = font;
|
||||
_font = Font::load(_family);
|
||||
}
|
||||
if (_font) {
|
||||
_font->drawString(batch, _drawInfo, str, color, effectColor, effectThickness, effect, { x, y }, bounds, scale, unlit, forward);
|
||||
}
|
||||
}
|
|
@ -14,48 +14,32 @@
|
|||
|
||||
#include <memory>
|
||||
#include <glm/glm.hpp>
|
||||
#include <QColor>
|
||||
#include <gpu/Forward.h>
|
||||
|
||||
namespace gpu {
|
||||
class Batch;
|
||||
}
|
||||
class Font;
|
||||
|
||||
#include "text/Font.h"
|
||||
#include "text/EffectType.h"
|
||||
#include "text/FontFamilies.h"
|
||||
#include "TextEffect.h"
|
||||
#include "FontFamilies.h"
|
||||
|
||||
// TextRenderer3D is actually a fairly thin wrapper around a Font class
|
||||
// defined in the cpp file.
|
||||
class TextRenderer3D {
|
||||
public:
|
||||
static const float DEFAULT_POINT_SIZE;
|
||||
|
||||
static TextRenderer3D* getInstance(const char* family, float pointSize = DEFAULT_POINT_SIZE,
|
||||
bool bold = false, bool italic = false, EffectType effect = NO_EFFECT, int effectThickness = 1);
|
||||
static TextRenderer3D* getInstance(const char* family);
|
||||
|
||||
glm::vec2 computeExtent(const QString& str) const;
|
||||
float getFontSize() const; // Pixel size
|
||||
|
||||
void draw(gpu::Batch& batch, float x, float y, const QString& str, const glm::vec4& color,
|
||||
const glm::vec2& bounds, bool unlit, bool forward);
|
||||
void draw(gpu::Batch& batch, float x, float y, const glm::vec2& bounds,
|
||||
const QString& str, const glm::vec4& color, bool unlit, bool forward);
|
||||
void draw(gpu::Batch& batch, float x, float y, const glm::vec2& bounds, float scale,
|
||||
const QString& str, const QString& font, const glm::vec4& color, const glm::vec3& effectColor,
|
||||
float effectThickness, TextEffect effect, bool unlit, bool forward);
|
||||
|
||||
private:
|
||||
TextRenderer3D(const char* family, float pointSize, int weight = -1, bool italic = false,
|
||||
EffectType effect = NO_EFFECT, int effectThickness = 1);
|
||||
TextRenderer3D(const char* family);
|
||||
|
||||
// the type of effect to apply
|
||||
const EffectType _effectType;
|
||||
QString _family;
|
||||
|
||||
// the thickness of the effect
|
||||
const int _effectThickness;
|
||||
|
||||
// text color
|
||||
glm::vec4 _color;
|
||||
Font::DrawInfo _drawInfo;
|
||||
std::shared_ptr<Font> _font;
|
||||
Font::DrawInfo _drawInfo;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // hifi_TextRenderer3D_h
|
||||
|
|
110
libraries/render-utils/src/ToneMapAndResampleTask.cpp
Normal file
110
libraries/render-utils/src/ToneMapAndResampleTask.cpp
Normal file
|
@ -0,0 +1,110 @@
|
|||
//
|
||||
// ToneMapAndResampleTask.cpp
|
||||
// libraries/render-utils/src
|
||||
//
|
||||
// Created by Anna Brewer on 7/3/19.
|
||||
// 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
|
||||
//
|
||||
|
||||
#include "ToneMapAndResampleTask.h"
|
||||
|
||||
#include <gpu/Context.h>
|
||||
#include <shaders/Shaders.h>
|
||||
|
||||
#include "render-utils/ShaderConstants.h"
|
||||
#include "StencilMaskPass.h"
|
||||
#include "FramebufferCache.h"
|
||||
|
||||
using namespace render;
|
||||
using namespace shader::gpu::program;
|
||||
using namespace shader::render_utils::program;
|
||||
|
||||
gpu::PipelinePointer ToneMapAndResample::_pipeline;
|
||||
gpu::PipelinePointer ToneMapAndResample::_mirrorPipeline;
|
||||
|
||||
ToneMapAndResample::ToneMapAndResample() {
|
||||
Parameters parameters;
|
||||
_parametersBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(Parameters), (const gpu::Byte*) ¶meters));
|
||||
}
|
||||
|
||||
void ToneMapAndResample::init() {
|
||||
// shared_ptr to gpu::State
|
||||
gpu::StatePointer blitState = gpu::StatePointer(new gpu::State());
|
||||
|
||||
blitState->setDepthTest(gpu::State::DepthTest(false, false));
|
||||
blitState->setColorWriteMask(true, true, true, true);
|
||||
|
||||
_pipeline = gpu::PipelinePointer(gpu::Pipeline::create(gpu::Shader::createProgram(toneMapping), blitState));
|
||||
_mirrorPipeline = gpu::PipelinePointer(gpu::Pipeline::create(gpu::Shader::createProgram(toneMapping_mirrored), blitState));
|
||||
}
|
||||
|
||||
void ToneMapAndResample::setExposure(float exposure) {
|
||||
auto& params = _parametersBuffer.get<Parameters>();
|
||||
if (params._exposure != exposure) {
|
||||
_parametersBuffer.edit<Parameters>()._exposure = exposure;
|
||||
_parametersBuffer.edit<Parameters>()._twoPowExposure = pow(2.0, exposure);
|
||||
}
|
||||
}
|
||||
|
||||
void ToneMapAndResample::setToneCurve(ToneCurve curve) {
|
||||
auto& params = _parametersBuffer.get<Parameters>();
|
||||
if (params._toneCurve != (int)curve) {
|
||||
_parametersBuffer.edit<Parameters>()._toneCurve = (int)curve;
|
||||
}
|
||||
}
|
||||
|
||||
void ToneMapAndResample::configure(const Config& config) {
|
||||
setExposure(config.exposure);
|
||||
setToneCurve((ToneCurve)config.curve);
|
||||
}
|
||||
|
||||
void ToneMapAndResample::run(const RenderContextPointer& renderContext, const Input& input, Output& output) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->hasViewFrustum());
|
||||
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
auto lightingBuffer = input.get0()->getRenderBuffer(0);
|
||||
auto destinationFramebuffer = input.get1();
|
||||
|
||||
if (!destinationFramebuffer) {
|
||||
destinationFramebuffer = args->_blitFramebuffer;
|
||||
}
|
||||
|
||||
if (!lightingBuffer || !destinationFramebuffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_pipeline) {
|
||||
init();
|
||||
}
|
||||
|
||||
const auto bufferSize = destinationFramebuffer->getSize();
|
||||
|
||||
auto srcBufferSize = glm::ivec2(lightingBuffer->getDimensions());
|
||||
|
||||
glm::ivec4 destViewport{ 0, 0, bufferSize.x, bufferSize.y };
|
||||
|
||||
gpu::doInBatch("Resample::run", args->_context, [&](gpu::Batch& batch) {
|
||||
batch.enableStereo(false);
|
||||
batch.setFramebuffer(destinationFramebuffer);
|
||||
|
||||
batch.setViewportTransform(destViewport);
|
||||
batch.setProjectionTransform(glm::mat4());
|
||||
batch.resetViewTransform();
|
||||
batch.setPipeline(args->_renderMode == RenderArgs::MIRROR_RENDER_MODE ? _mirrorPipeline : _pipeline);
|
||||
|
||||
batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(srcBufferSize, args->_viewport));
|
||||
batch.setUniformBuffer(render_utils::slot::buffer::ToneMappingParams, _parametersBuffer);
|
||||
batch.setResourceTexture(render_utils::slot::texture::ToneMappingColor, lightingBuffer);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
});
|
||||
|
||||
// Set full final viewport
|
||||
args->_viewport = destViewport;
|
||||
|
||||
output = destinationFramebuffer;
|
||||
}
|
|
@ -1,16 +1,16 @@
|
|||
//
|
||||
// ToneMappingEffect.h
|
||||
// ToneMapAndResample.h
|
||||
// libraries/render-utils/src
|
||||
//
|
||||
// Created by Sam Gateau on 12/7/2015.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
// Created by Anna Brewer on 7/3/19.
|
||||
// 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
|
||||
//
|
||||
|
||||
#ifndef hifi_ToneMappingEffect_h
|
||||
#define hifi_ToneMappingEffect_h
|
||||
#ifndef hifi_ToneMapAndResample_h
|
||||
#define hifi_ToneMapAndResample_h
|
||||
|
||||
#include <DependencyManager.h>
|
||||
#include <NumericalConstants.h>
|
||||
|
@ -20,29 +20,66 @@
|
|||
#include <render/Forward.h>
|
||||
#include <render/DrawTask.h>
|
||||
|
||||
enum class ToneCurve {
|
||||
// Different tone curve available
|
||||
None,
|
||||
Gamma22,
|
||||
Reinhard,
|
||||
Filmic,
|
||||
};
|
||||
|
||||
class ToneMappingConfig : public render::Job::Config {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(float exposure MEMBER exposure WRITE setExposure);
|
||||
Q_PROPERTY(int curve MEMBER curve WRITE setCurve);
|
||||
|
||||
class ToneMappingEffect {
|
||||
public:
|
||||
ToneMappingEffect();
|
||||
virtual ~ToneMappingEffect() {}
|
||||
ToneMappingConfig() : render::Job::Config(true) {}
|
||||
|
||||
void render(RenderArgs* args, const gpu::TexturePointer& lightingBuffer, const gpu::FramebufferPointer& destinationBuffer);
|
||||
void setExposure(float newExposure) { exposure = newExposure; emit dirty(); }
|
||||
void setCurve(int newCurve) { curve = std::max((int)ToneCurve::None, std::min((int)ToneCurve::Filmic, newCurve)); emit dirty(); }
|
||||
|
||||
|
||||
float exposure{ 0.0f };
|
||||
int curve{ (int)ToneCurve::Gamma22 };
|
||||
|
||||
signals:
|
||||
void dirty();
|
||||
};
|
||||
|
||||
class ToneMapAndResample {
|
||||
public:
|
||||
ToneMapAndResample();
|
||||
virtual ~ToneMapAndResample() {}
|
||||
|
||||
void render(RenderArgs* args, const gpu::TexturePointer& lightingBuffer, gpu::FramebufferPointer& destinationBuffer);
|
||||
|
||||
void setExposure(float exposure);
|
||||
float getExposure() const { return _parametersBuffer.get<Parameters>()._exposure; }
|
||||
|
||||
// Different tone curve available
|
||||
enum ToneCurve {
|
||||
None = 0,
|
||||
Gamma22,
|
||||
Reinhard,
|
||||
Filmic,
|
||||
};
|
||||
void setToneCurve(ToneCurve curve);
|
||||
ToneCurve getToneCurve() const { return (ToneCurve)_parametersBuffer.get<Parameters>()._toneCurve; }
|
||||
|
||||
private:
|
||||
// Inputs: lightingFramebuffer, destinationFramebuffer
|
||||
using Input = render::VaryingSet2<gpu::FramebufferPointer, gpu::FramebufferPointer>;
|
||||
using Output = gpu::FramebufferPointer;
|
||||
using Config = ToneMappingConfig;
|
||||
using JobModel = render::Job::ModelIO<ToneMapAndResample, Input, Output, Config>;
|
||||
|
||||
void configure(const Config& config);
|
||||
void run(const render::RenderContextPointer& renderContext, const Input& input, Output& output);
|
||||
|
||||
protected:
|
||||
static gpu::PipelinePointer _pipeline;
|
||||
static gpu::PipelinePointer _mirrorPipeline;
|
||||
|
||||
gpu::FramebufferPointer _destinationFrameBuffer;
|
||||
|
||||
float _factor{ 2.0f };
|
||||
|
||||
gpu::FramebufferPointer getResampledFrameBuffer(const gpu::FramebufferPointer& sourceFramebuffer);
|
||||
|
||||
private:
|
||||
gpu::PipelinePointer _blitLightBuffer;
|
||||
|
||||
// Class describing the uniform buffer with all the parameters common to the tone mapping shaders
|
||||
|
@ -51,46 +88,16 @@ private:
|
|||
float _exposure = 0.0f;
|
||||
float _twoPowExposure = 1.0f;
|
||||
glm::vec2 spareA;
|
||||
int _toneCurve = Gamma22;
|
||||
int _toneCurve = (int)ToneCurve::Gamma22;
|
||||
glm::vec3 spareB;
|
||||
|
||||
Parameters() {}
|
||||
};
|
||||
|
||||
typedef gpu::BufferView UniformBufferView;
|
||||
gpu::BufferView _parametersBuffer;
|
||||
|
||||
void init(RenderArgs* args);
|
||||
void init();
|
||||
};
|
||||
|
||||
class ToneMappingConfig : public render::Job::Config {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(float exposure MEMBER exposure WRITE setExposure);
|
||||
Q_PROPERTY(int curve MEMBER curve WRITE setCurve);
|
||||
public:
|
||||
ToneMappingConfig() : render::Job::Config(true) {}
|
||||
|
||||
void setExposure(float newExposure) { exposure = newExposure; emit dirty(); }
|
||||
void setCurve(int newCurve) { curve = std::max((int)ToneMappingEffect::None, std::min((int)ToneMappingEffect::Filmic, newCurve)); emit dirty(); }
|
||||
|
||||
|
||||
float exposure{ 0.0f };
|
||||
int curve{ ToneMappingEffect::Gamma22 };
|
||||
signals:
|
||||
void dirty();
|
||||
};
|
||||
|
||||
class ToneMappingDeferred {
|
||||
public:
|
||||
// Inputs: lightingFramebuffer, destinationFramebuffer
|
||||
using Input = render::VaryingSet2<gpu::FramebufferPointer, gpu::FramebufferPointer>;
|
||||
using Output = gpu::FramebufferPointer;
|
||||
using Config = ToneMappingConfig;
|
||||
using JobModel = render::Job::ModelIO<ToneMappingDeferred, Input, Output, Config>;
|
||||
|
||||
void configure(const Config& config);
|
||||
void run(const render::RenderContextPointer& renderContext, const Input& input, Output& output);
|
||||
|
||||
ToneMappingEffect _toneMappingEffect;
|
||||
};
|
||||
|
||||
#endif // hifi_ToneMappingEffect_h
|
||||
#endif // hifi_ToneMapAndResample_h
|
|
@ -1,96 +0,0 @@
|
|||
//
|
||||
// ToneMappingEffect.cpp
|
||||
// libraries/render-utils/src
|
||||
//
|
||||
// Created by Sam Gateau on 12/7/2015.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "ToneMappingEffect.h"
|
||||
|
||||
#include <gpu/Context.h>
|
||||
#include <shaders/Shaders.h>
|
||||
|
||||
#include "render-utils/ShaderConstants.h"
|
||||
#include "StencilMaskPass.h"
|
||||
#include "FramebufferCache.h"
|
||||
|
||||
|
||||
ToneMappingEffect::ToneMappingEffect() {
|
||||
Parameters parameters;
|
||||
_parametersBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(Parameters), (const gpu::Byte*) ¶meters));
|
||||
}
|
||||
|
||||
void ToneMappingEffect::init(RenderArgs* args) {
|
||||
auto blitProgram = gpu::Shader::createProgram(shader::render_utils::program::toneMapping);
|
||||
|
||||
auto blitState = std::make_shared<gpu::State>();
|
||||
blitState->setColorWriteMask(true, true, true, true);
|
||||
_blitLightBuffer = gpu::PipelinePointer(gpu::Pipeline::create(blitProgram, blitState));
|
||||
}
|
||||
|
||||
void ToneMappingEffect::setExposure(float exposure) {
|
||||
auto& params = _parametersBuffer.get<Parameters>();
|
||||
if (params._exposure != exposure) {
|
||||
_parametersBuffer.edit<Parameters>()._exposure = exposure;
|
||||
_parametersBuffer.edit<Parameters>()._twoPowExposure = pow(2.0, exposure);
|
||||
}
|
||||
}
|
||||
|
||||
void ToneMappingEffect::setToneCurve(ToneCurve curve) {
|
||||
auto& params = _parametersBuffer.get<Parameters>();
|
||||
if (params._toneCurve != curve) {
|
||||
_parametersBuffer.edit<Parameters>()._toneCurve = curve;
|
||||
}
|
||||
}
|
||||
|
||||
void ToneMappingEffect::render(RenderArgs* args, const gpu::TexturePointer& lightingBuffer, const gpu::FramebufferPointer& destinationFramebuffer) {
|
||||
if (!_blitLightBuffer) {
|
||||
init(args);
|
||||
}
|
||||
|
||||
if (!lightingBuffer || !destinationFramebuffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto framebufferSize = glm::ivec2(lightingBuffer->getDimensions());
|
||||
gpu::doInBatch("ToneMappingEffect::render", args->_context, [&](gpu::Batch& batch) {
|
||||
batch.enableStereo(false);
|
||||
batch.setFramebuffer(destinationFramebuffer);
|
||||
|
||||
// FIXME: Generate the Luminosity map
|
||||
//batch.generateTextureMips(lightingBuffer);
|
||||
|
||||
batch.setViewportTransform(args->_viewport);
|
||||
batch.setProjectionTransform(glm::mat4());
|
||||
batch.resetViewTransform();
|
||||
batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, args->_viewport));
|
||||
batch.setPipeline(_blitLightBuffer);
|
||||
|
||||
batch.setUniformBuffer(render_utils::slot::buffer::ToneMappingParams, _parametersBuffer);
|
||||
batch.setResourceTexture(render_utils::slot::texture::ToneMappingColor, lightingBuffer);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void ToneMappingDeferred::configure(const Config& config) {
|
||||
_toneMappingEffect.setExposure(config.exposure);
|
||||
_toneMappingEffect.setToneCurve((ToneMappingEffect::ToneCurve)config.curve);
|
||||
}
|
||||
|
||||
void ToneMappingDeferred::run(const render::RenderContextPointer& renderContext, const Input& input, Output& output) {
|
||||
|
||||
auto lightingBuffer = input.get0()->getRenderBuffer(0);
|
||||
auto destFbo = input.get1();
|
||||
|
||||
if (!destFbo) {
|
||||
destFbo = renderContext->args->_blitFramebuffer;
|
||||
}
|
||||
|
||||
_toneMappingEffect.render(renderContext->args, lightingBuffer, destFbo);
|
||||
output = destFbo;
|
||||
}
|
|
@ -1 +1,2 @@
|
|||
VERTEX gpu::vertex::DrawViewportQuadTransformTexcoord
|
||||
DEFINES mirrored:f
|
||||
|
|
|
@ -36,29 +36,29 @@
|
|||
layout(location=RENDER_UTILS_ATTR_POSITION_ES) in vec4 _positionES;
|
||||
<@endif@>
|
||||
layout(location=RENDER_UTILS_ATTR_NORMAL_WS) in vec3 _normalWS;
|
||||
layout(location=RENDER_UTILS_ATTR_COLOR) in vec4 _color;
|
||||
layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01;
|
||||
#define _texCoord0 _texCoord01.xy
|
||||
#define _texCoord1 _texCoord01.zw
|
||||
layout(location=RENDER_UTILS_ATTR_FADE1) flat in vec4 _glyphBounds; // we're reusing the fade texcoord locations here
|
||||
|
||||
void main() {
|
||||
float alpha = evalSDFSuperSampled(_texCoord0);
|
||||
vec4 color = evalSDFSuperSampled(_texCoord0, _glyphBounds);
|
||||
|
||||
<@if HIFI_USE_TRANSLUCENT or HIFI_USE_FORWARD@>
|
||||
alpha *= _color.a;
|
||||
if (alpha <= 0.0) {
|
||||
color.a *= params.color.a;
|
||||
if (color.a <= 0.0) {
|
||||
discard;
|
||||
}
|
||||
<@endif@>
|
||||
|
||||
<@if HIFI_USE_UNLIT@>
|
||||
<@if HIFI_USE_TRANSLUCENT or HIFI_USE_FORWARD@>
|
||||
_fragColor0 = vec4(_color.rgb * isUnlitEnabled(), alpha);
|
||||
_fragColor0 = vec4(color.rgb * isUnlitEnabled(), color.a);
|
||||
<@else@>
|
||||
packDeferredFragmentUnlit(
|
||||
normalize(_normalWS),
|
||||
alpha,
|
||||
_color.rgb);
|
||||
color.a,
|
||||
color.rgb);
|
||||
<@endif@>
|
||||
<@else@>
|
||||
<@if HIFI_USE_TRANSLUCENT or HIFI_USE_FORWARD@>
|
||||
|
@ -72,12 +72,12 @@ void main() {
|
|||
DEFAULT_OCCLUSION,
|
||||
fragPosition,
|
||||
normalize(_normalWS),
|
||||
_color.rgb,
|
||||
color.rgb,
|
||||
DEFAULT_FRESNEL,
|
||||
DEFAULT_METALLIC,
|
||||
DEFAULT_EMISSIVE,
|
||||
DEFAULT_ROUGHNESS, alpha),
|
||||
alpha);
|
||||
DEFAULT_ROUGHNESS, color.a),
|
||||
color.a);
|
||||
<@else@>
|
||||
_fragColor0 = vec4(evalSkyboxGlobalColor(
|
||||
cam._viewInverse,
|
||||
|
@ -85,17 +85,17 @@ void main() {
|
|||
DEFAULT_OCCLUSION,
|
||||
fragPosition,
|
||||
normalize(_normalWS),
|
||||
_color.rgb,
|
||||
color.rgb,
|
||||
DEFAULT_FRESNEL,
|
||||
DEFAULT_METALLIC,
|
||||
DEFAULT_ROUGHNESS),
|
||||
alpha);
|
||||
color.a);
|
||||
<@endif@>
|
||||
<@else@>
|
||||
packDeferredFragment(
|
||||
normalize(_normalWS),
|
||||
alpha,
|
||||
_color.rgb,
|
||||
color.a,
|
||||
color.rgb,
|
||||
DEFAULT_ROUGHNESS,
|
||||
DEFAULT_METALLIC,
|
||||
DEFAULT_EMISSIVE,
|
||||
|
|
|
@ -14,11 +14,16 @@
|
|||
<@if not SDF_TEXT3D_SLH@>
|
||||
<@def SDF_TEXT3D_SLH@>
|
||||
|
||||
LAYOUT(binding=0) uniform sampler2D Font;
|
||||
LAYOUT(binding=0) uniform sampler2D fontTexture;
|
||||
|
||||
struct TextParams {
|
||||
vec4 color;
|
||||
vec4 outline;
|
||||
|
||||
vec3 effectColor;
|
||||
float effectThickness;
|
||||
|
||||
int effect;
|
||||
vec3 spare;
|
||||
};
|
||||
|
||||
LAYOUT(binding=0) uniform textParamsBuffer {
|
||||
|
@ -29,32 +34,57 @@ LAYOUT(binding=0) uniform textParamsBuffer {
|
|||
|
||||
#define TAA_TEXTURE_LOD_BIAS -3.0
|
||||
|
||||
const float interiorCutoff = 0.8;
|
||||
const float outlineExpansion = 0.2;
|
||||
const float interiorCutoff = 0.5;
|
||||
const float taaBias = pow(2.0, TAA_TEXTURE_LOD_BIAS);
|
||||
|
||||
float evalSDF(vec2 texCoord) {
|
||||
// retrieve signed distance
|
||||
float sdf = textureLod(Font, texCoord, TAA_TEXTURE_LOD_BIAS).g;
|
||||
sdf = mix(sdf, mix(sdf + outlineExpansion, 1.0 - sdf, float(sdf > interiorCutoff)), float(params.outline.x > 0.0));
|
||||
vec4 evalSDF(vec2 texCoord, vec4 glyphBounds) {
|
||||
vec3 color = params.color.rgb;
|
||||
float sdf = textureLod(fontTexture, texCoord, TAA_TEXTURE_LOD_BIAS).g;
|
||||
|
||||
// Rely on TAA for anti-aliasing
|
||||
return step(0.5, sdf);
|
||||
// Outline
|
||||
if (params.effect == 1 || params.effect == 2) {
|
||||
float outline = float(sdf < interiorCutoff);
|
||||
color = mix(color, params.effectColor, outline);
|
||||
|
||||
// with or without fill
|
||||
sdf = mix(sdf, 0.0, float(params.effect == 1) * (1.0 - outline));
|
||||
|
||||
const float EPSILON = 0.00001;
|
||||
sdf += mix(0.0, params.effectThickness - EPSILON, outline);
|
||||
} else if (params.effect == 3) { // Shadow
|
||||
// don't sample from outside of our glyph bounds
|
||||
sdf *= mix(1.0, 0.0, float(clamp(texCoord, glyphBounds.xy, glyphBounds.xy + glyphBounds.zw) != texCoord));
|
||||
|
||||
if (sdf < interiorCutoff) {
|
||||
color = params.effectColor;
|
||||
const float DOUBLE_MAX_OFFSET_PIXELS = 20.0; // must match value in Font.cpp
|
||||
// FIXME: TAA_TEXTURE_LOD_BIAS doesn't have any effect because we're only generating one mip, so here we need to use 0, but it should
|
||||
// really match the LOD that we use in the textureLod call below
|
||||
vec2 textureOffset = vec2(params.effectThickness * DOUBLE_MAX_OFFSET_PIXELS) / vec2(textureSize(fontTexture, 0/*int(TAA_TEXTURE_LOD_BIAS)*/));
|
||||
vec2 shadowTexCoords = texCoord - textureOffset;
|
||||
sdf = textureLod(fontTexture, shadowTexCoords, TAA_TEXTURE_LOD_BIAS).g;
|
||||
|
||||
// don't sample from outside of our glyph bounds
|
||||
sdf *= mix(1.0, 0.0, float(clamp(shadowTexCoords, glyphBounds.xy, glyphBounds.xy + glyphBounds.zw) != shadowTexCoords));
|
||||
}
|
||||
}
|
||||
|
||||
return vec4(color, sdf);
|
||||
}
|
||||
|
||||
float evalSDFSuperSampled(vec2 texCoord) {
|
||||
vec4 evalSDFSuperSampled(vec2 texCoord, vec4 glyphBounds) {
|
||||
vec2 dxTexCoord = dFdx(texCoord) * 0.5 * taaBias;
|
||||
vec2 dyTexCoord = dFdy(texCoord) * 0.5 * taaBias;
|
||||
|
||||
// Perform 4x supersampling for anisotropic filtering
|
||||
float a;
|
||||
a = evalSDF(texCoord);
|
||||
a += evalSDF(texCoord + dxTexCoord);
|
||||
a += evalSDF(texCoord + dyTexCoord);
|
||||
a += evalSDF(texCoord + dxTexCoord + dyTexCoord);
|
||||
a *= 0.25;
|
||||
vec4 color;
|
||||
color = evalSDF(texCoord, glyphBounds);
|
||||
color += evalSDF(texCoord + dxTexCoord, glyphBounds);
|
||||
color += evalSDF(texCoord + dyTexCoord, glyphBounds);
|
||||
color += evalSDF(texCoord + dxTexCoord + dyTexCoord, glyphBounds);
|
||||
color *= 0.25;
|
||||
|
||||
return a;
|
||||
return vec4(color.rgb, step(interiorCutoff, color.a));
|
||||
}
|
||||
|
||||
<@endfunc@>
|
||||
|
|
|
@ -23,19 +23,28 @@
|
|||
layout(location=RENDER_UTILS_ATTR_POSITION_ES) out vec4 _positionES;
|
||||
<@endif@>
|
||||
layout(location=RENDER_UTILS_ATTR_NORMAL_WS) out vec3 _normalWS;
|
||||
layout(location=RENDER_UTILS_ATTR_COLOR) out vec4 _color;
|
||||
layout(location=RENDER_UTILS_ATTR_TEXCOORD01) out vec4 _texCoord01;
|
||||
layout(location=RENDER_UTILS_ATTR_FADE1) flat out vec4 _glyphBounds; // we're reusing the fade texcoord locations here
|
||||
|
||||
void main() {
|
||||
_texCoord01 = vec4(inTexCoord0.st, 0.0, 0.0);
|
||||
_color = color_sRGBAToLinear(params.color);
|
||||
_glyphBounds = inTexCoord1;
|
||||
|
||||
vec4 position = inPosition;
|
||||
// if we're in shadow mode, we need to move each subsequent quad slightly forward so it doesn't z-fight
|
||||
// with the shadows of the letters before it
|
||||
if (params.effect == 3) { // Shadow
|
||||
const int VERTICES_PER_QUAD = 4; // must match value in Font.cpp
|
||||
const float EPSILON = 0.001;
|
||||
position.z += float(gl_VertexID / VERTICES_PER_QUAD) * EPSILON;
|
||||
}
|
||||
|
||||
TransformCamera cam = getTransformCamera();
|
||||
TransformObject obj = getTransformObject();
|
||||
<@if HIFI_USE_TRANSLUCENT or HIFI_USE_FORWARD@>
|
||||
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _positionES, gl_Position)$>
|
||||
<$transformModelToEyeAndClipPos(cam, obj, position, _positionES, gl_Position)$>
|
||||
<@else@>
|
||||
<$transformModelToClipPos(cam, obj, inPosition, gl_Position)$>
|
||||
<$transformModelToClipPos(cam, obj, position, gl_Position)$>
|
||||
<@endif@>
|
||||
|
||||
const vec3 normal = vec3(0, 0, 1);
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2015/07/16
|
||||
// Copyright 2013 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
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#ifndef hifi_EffectType_h
|
||||
#define hifi_EffectType_h
|
||||
|
||||
enum EffectType { NO_EFFECT, SHADOW_EFFECT, OUTLINE_EFFECT };
|
||||
|
||||
#endif
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <QFile>
|
||||
#include <QImage>
|
||||
#include <QNetworkReply>
|
||||
|
||||
#include <ColorUtils.h>
|
||||
|
||||
|
@ -13,6 +14,8 @@
|
|||
#include "FontFamilies.h"
|
||||
#include "../StencilMaskPass.h"
|
||||
|
||||
#include "NetworkAccessManager.h"
|
||||
|
||||
static std::mutex fontMutex;
|
||||
|
||||
std::map<std::tuple<bool, bool, bool>, gpu::PipelinePointer> Font::_pipelines;
|
||||
|
@ -21,67 +24,90 @@ gpu::Stream::FormatPointer Font::_format;
|
|||
struct TextureVertex {
|
||||
glm::vec2 pos;
|
||||
glm::vec2 tex;
|
||||
glm::vec4 bounds;
|
||||
TextureVertex() {}
|
||||
TextureVertex(const glm::vec2& pos, const glm::vec2& tex) : pos(pos), tex(tex) {}
|
||||
TextureVertex(const glm::vec2& pos, const glm::vec2& tex, const glm::vec4& bounds) : pos(pos), tex(tex), bounds(bounds) {}
|
||||
};
|
||||
|
||||
static const int NUMBER_OF_INDICES_PER_QUAD = 6; // 1 quad = 2 triangles
|
||||
static const int VERTICES_PER_QUAD = 4; // 1 quad = 4 vertices
|
||||
static const int NUMBER_OF_INDICES_PER_QUAD = 6; // 1 quad = 2 triangles
|
||||
static const int VERTICES_PER_QUAD = 4; // 1 quad = 4 vertices (must match value in sdf_text3D.slv)
|
||||
const float DOUBLE_MAX_OFFSET_PIXELS = 20.0f; // must match value in sdf_text3D.slh
|
||||
|
||||
struct QuadBuilder {
|
||||
TextureVertex vertices[VERTICES_PER_QUAD];
|
||||
|
||||
QuadBuilder(const glm::vec2& min, const glm::vec2& size,
|
||||
const glm::vec2& texMin, const glm::vec2& texSize) {
|
||||
QuadBuilder(const Glyph& glyph, const glm::vec2& offset, float scale, bool enlargeForShadows) {
|
||||
glm::vec2 min = offset + glm::vec2(glyph.offset.x, glyph.offset.y - glyph.size.y);
|
||||
glm::vec2 size = glyph.size;
|
||||
glm::vec2 texMin = glyph.texOffset;
|
||||
glm::vec2 texSize = glyph.texSize;
|
||||
|
||||
// We need the pre-adjustment bounds for clamping
|
||||
glm::vec4 bounds = glm::vec4(texMin, texSize);
|
||||
if (enlargeForShadows) {
|
||||
glm::vec2 imageSize = glyph.size / glyph.texSize;
|
||||
glm::vec2 sizeDelta = 0.5f * DOUBLE_MAX_OFFSET_PIXELS * scale * imageSize;
|
||||
glm::vec2 oldSize = size;
|
||||
size += sizeDelta;
|
||||
min.y -= sizeDelta.y;
|
||||
|
||||
texSize = texSize * (size / oldSize);
|
||||
}
|
||||
|
||||
// min = bottomLeft
|
||||
vertices[0] = TextureVertex(min,
|
||||
texMin + glm::vec2(0.0f, texSize.y));
|
||||
texMin + glm::vec2(0.0f, texSize.y), bounds);
|
||||
vertices[1] = TextureVertex(min + glm::vec2(size.x, 0.0f),
|
||||
texMin + texSize);
|
||||
texMin + texSize, bounds);
|
||||
vertices[2] = TextureVertex(min + glm::vec2(0.0f, size.y),
|
||||
texMin);
|
||||
texMin, bounds);
|
||||
vertices[3] = TextureVertex(min + size,
|
||||
texMin + glm::vec2(texSize.x, 0.0f));
|
||||
texMin + glm::vec2(texSize.x, 0.0f), bounds);
|
||||
}
|
||||
QuadBuilder(const Glyph& glyph, const glm::vec2& offset) :
|
||||
QuadBuilder(offset + glm::vec2(glyph.offset.x, glyph.offset.y - glyph.size.y), glyph.size,
|
||||
glyph.texOffset, glyph.texSize) {}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
static QHash<QString, Font::Pointer> LOADED_FONTS;
|
||||
|
||||
Font::Pointer Font::load(QIODevice& fontFile) {
|
||||
Pointer font = std::make_shared<Font>();
|
||||
font->read(fontFile);
|
||||
return font;
|
||||
}
|
||||
|
||||
static QHash<QString, Font::Pointer> LOADED_FONTS;
|
||||
|
||||
Font::Pointer Font::load(const QString& family) {
|
||||
std::lock_guard<std::mutex> lock(fontMutex);
|
||||
if (!LOADED_FONTS.contains(family)) {
|
||||
|
||||
static const QString SDFF_COURIER_PRIME_FILENAME{ ":/CourierPrime.sdff" };
|
||||
static const QString SDFF_INCONSOLATA_MEDIUM_FILENAME{ ":/InconsolataMedium.sdff" };
|
||||
static const QString SDFF_ROBOTO_FILENAME{ ":/Roboto.sdff" };
|
||||
static const QString SDFF_TIMELESS_FILENAME{ ":/Timeless.sdff" };
|
||||
|
||||
QString loadFilename;
|
||||
|
||||
if (family == MONO_FONT_FAMILY) {
|
||||
loadFilename = SDFF_COURIER_PRIME_FILENAME;
|
||||
if (family == ROBOTO_FONT_FAMILY) {
|
||||
loadFilename = ":/Roboto.sdff";
|
||||
} else if (family == INCONSOLATA_FONT_FAMILY) {
|
||||
loadFilename = SDFF_INCONSOLATA_MEDIUM_FILENAME;
|
||||
} else if (family == SANS_FONT_FAMILY) {
|
||||
loadFilename = SDFF_ROBOTO_FILENAME;
|
||||
loadFilename = ":/InconsolataMedium.sdff";
|
||||
} else if (family == COURIER_FONT_FAMILY) {
|
||||
loadFilename = ":/CourierPrime.sdff";
|
||||
} else if (family == TIMELESS_FONT_FAMILY) {
|
||||
loadFilename = ":/Timeless.sdff";
|
||||
} else if (family.startsWith("http")) {
|
||||
auto loadingFont = std::make_shared<Font>();
|
||||
loadingFont->setLoaded(false);
|
||||
LOADED_FONTS[family] = loadingFont;
|
||||
|
||||
auto& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
|
||||
QNetworkRequest networkRequest;
|
||||
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
networkRequest.setUrl(family);
|
||||
|
||||
auto networkReply = networkAccessManager.get(networkRequest);
|
||||
connect(networkReply, &QNetworkReply::finished, loadingFont.get(), &Font::handleFontNetworkReply);
|
||||
} else if (!LOADED_FONTS.contains(ROBOTO_FONT_FAMILY)) {
|
||||
// Unrecognized font and we haven't loaded Roboto yet
|
||||
loadFilename = ":/Roboto.sdff";
|
||||
} else {
|
||||
if (!LOADED_FONTS.contains(SERIF_FONT_FAMILY)) {
|
||||
loadFilename = SDFF_TIMELESS_FILENAME;
|
||||
} else {
|
||||
LOADED_FONTS[family] = LOADED_FONTS[SERIF_FONT_FAMILY];
|
||||
}
|
||||
// Unrecognized font but we've already loaded Roboto
|
||||
LOADED_FONTS[family] = LOADED_FONTS[ROBOTO_FONT_FAMILY];
|
||||
}
|
||||
|
||||
if (!loadFilename.isEmpty()) {
|
||||
|
@ -96,14 +122,24 @@ Font::Pointer Font::load(const QString& family) {
|
|||
return LOADED_FONTS[family];
|
||||
}
|
||||
|
||||
Font::Font() {
|
||||
static bool fontResourceInitComplete = false;
|
||||
if (!fontResourceInitComplete) {
|
||||
Q_INIT_RESOURCE(fonts);
|
||||
fontResourceInitComplete = true;
|
||||
void Font::handleFontNetworkReply() {
|
||||
auto requestReply = qobject_cast<QNetworkReply*>(sender());
|
||||
|
||||
if (requestReply->error() == QNetworkReply::NoError) {
|
||||
setLoaded(true);
|
||||
read(*requestReply);
|
||||
} else {
|
||||
qDebug() << "Error downloading " << requestReply->url() << " - " << requestReply->errorString();
|
||||
}
|
||||
}
|
||||
|
||||
Font::Font() {
|
||||
static std::once_flag once;
|
||||
std::call_once(once, []{
|
||||
Q_INIT_RESOURCE(fonts);
|
||||
});
|
||||
}
|
||||
|
||||
// NERD RAGE: why doesn't QHash have a 'const T & operator[] const' member
|
||||
const Glyph& Font::getGlyph(const QChar& c) const {
|
||||
if (!_glyphs.contains(c)) {
|
||||
|
@ -139,7 +175,7 @@ glm::vec2 Font::computeTokenExtent(const QString& token) const {
|
|||
glm::vec2 Font::computeExtent(const QString& str) const {
|
||||
glm::vec2 extent = glm::vec2(0.0f, 0.0f);
|
||||
|
||||
QStringList lines{ splitLines(str) };
|
||||
QStringList lines = splitLines(str);
|
||||
if (!lines.empty()) {
|
||||
for(const auto& line : lines) {
|
||||
glm::vec2 tokenExtent = computeTokenExtent(line);
|
||||
|
@ -154,7 +190,9 @@ void Font::read(QIODevice& in) {
|
|||
uint8_t header[4];
|
||||
readStream(in, header);
|
||||
if (memcmp(header, "SDFF", 4)) {
|
||||
qFatal("Bad SDFF file");
|
||||
qDebug() << "Bad SDFF file";
|
||||
_loaded = false;
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t version;
|
||||
|
@ -191,7 +229,9 @@ void Font::read(QIODevice& in) {
|
|||
// read image data
|
||||
QImage image;
|
||||
if (!image.loadFromData(in.readAll(), "PNG")) {
|
||||
qFatal("Failed to read SDFF image");
|
||||
qDebug() << "Failed to read SDFF image";
|
||||
_loaded = false;
|
||||
return;
|
||||
}
|
||||
|
||||
_glyphs.clear();
|
||||
|
@ -212,6 +252,9 @@ void Font::read(QIODevice& in) {
|
|||
formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA);
|
||||
formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::BGRA);
|
||||
}
|
||||
// FIXME: We're forcing this to use only one mip, and then manually doing anisotropic filtering in the shader,
|
||||
// and also calling textureLod. Shouldn't this just use anisotropic filtering and auto-generate mips?
|
||||
// We should also use smoothstep for anti-aliasing, as explained here: https://github.com/libgdx/libgdx/wiki/Distance-field-fonts
|
||||
_texture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::SINGLE_MIP,
|
||||
gpu::Sampler(gpu::Sampler::FILTER_MIN_POINT_MAG_LINEAR));
|
||||
_texture->setStoredMipFormat(formatMip);
|
||||
|
@ -244,20 +287,21 @@ void Font::setupGPU() {
|
|||
}
|
||||
|
||||
// Sanity checks
|
||||
static const int OFFSET = offsetof(TextureVertex, tex);
|
||||
assert(OFFSET == sizeof(glm::vec2));
|
||||
assert(sizeof(glm::vec2) == 2 * sizeof(float));
|
||||
assert(sizeof(TextureVertex) == 2 * sizeof(glm::vec2));
|
||||
static const int TEX_COORD_OFFSET = offsetof(TextureVertex, tex);
|
||||
static const int TEX_BOUNDS_OFFSET = offsetof(TextureVertex, bounds);
|
||||
assert(TEX_COORD_OFFSET == sizeof(glm::vec2));
|
||||
assert(sizeof(TextureVertex) == 2 * sizeof(glm::vec2) + sizeof(glm::vec4));
|
||||
assert(sizeof(QuadBuilder) == 4 * sizeof(TextureVertex));
|
||||
|
||||
// Setup rendering structures
|
||||
_format = std::make_shared<gpu::Stream::Format>();
|
||||
_format->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ), 0);
|
||||
_format->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), OFFSET);
|
||||
_format->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), TEX_COORD_OFFSET);
|
||||
_format->setAttribute(gpu::Stream::TEXCOORD1, 0, gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::XYZW), TEX_BOUNDS_OFFSET);
|
||||
}
|
||||
}
|
||||
|
||||
void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm::vec2& origin, const glm::vec2& bounds) {
|
||||
void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm::vec2& origin, const glm::vec2& bounds, float scale, bool enlargeForShadows) {
|
||||
drawInfo.verticesBuffer = std::make_shared<gpu::Buffer>();
|
||||
drawInfo.indicesBuffer = std::make_shared<gpu::Buffer>();
|
||||
drawInfo.indexCount = 0;
|
||||
|
@ -267,6 +311,8 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm
|
|||
drawInfo.bounds = bounds;
|
||||
drawInfo.origin = origin;
|
||||
|
||||
float enlargedBoundsX = bounds.x - 0.5f * DOUBLE_MAX_OFFSET_PIXELS * float(enlargeForShadows);
|
||||
|
||||
// Top left of text
|
||||
glm::vec2 advance = origin;
|
||||
foreach(const QString& token, tokenizeForWrapping(str)) {
|
||||
|
@ -274,7 +320,7 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm
|
|||
bool forceNewLine = false;
|
||||
|
||||
// Handle wrapping
|
||||
if (!isNewLine && (bounds.x != -1) && (advance.x + computeExtent(token).x > origin.x + bounds.x)) {
|
||||
if (!isNewLine && (bounds.x != -1) && (advance.x + computeExtent(token).x > origin.x + enlargedBoundsX)) {
|
||||
// We are out of the x bound, force new line
|
||||
forceNewLine = true;
|
||||
}
|
||||
|
@ -285,7 +331,7 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm
|
|||
if (isNewLine) {
|
||||
// No need to draw anything, go directly to next token
|
||||
continue;
|
||||
} else if (computeExtent(token).x > bounds.x) {
|
||||
} else if (computeExtent(token).x > enlargedBoundsX) {
|
||||
// token will never fit, stop drawing
|
||||
break;
|
||||
}
|
||||
|
@ -301,10 +347,10 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm
|
|||
auto glyph = _glyphs[c];
|
||||
quint16 verticesOffset = numVertices;
|
||||
|
||||
QuadBuilder qd(glyph, advance - glm::vec2(0.0f, _ascent));
|
||||
QuadBuilder qd(glyph, advance - glm::vec2(0.0f, _ascent), scale, enlargeForShadows);
|
||||
drawInfo.verticesBuffer->append(qd);
|
||||
numVertices += 4;
|
||||
|
||||
numVertices += VERTICES_PER_QUAD;
|
||||
|
||||
// Sam's recommended triangle slices
|
||||
// Triangle tri1 = { v0, v1, v3 };
|
||||
// Triangle tri2 = { v1, v2, v3 };
|
||||
|
@ -331,7 +377,6 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm
|
|||
indices[5] = verticesOffset + 3;
|
||||
drawInfo.indicesBuffer->append(sizeof(indices), (const gpu::Byte*)indices);
|
||||
drawInfo.indexCount += NUMBER_OF_INDICES_PER_QUAD;
|
||||
|
||||
|
||||
// Advance by glyph size
|
||||
advance.x += glyph.d;
|
||||
|
@ -344,38 +389,49 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm
|
|||
}
|
||||
|
||||
void Font::drawString(gpu::Batch& batch, Font::DrawInfo& drawInfo, const QString& str, const glm::vec4& color,
|
||||
EffectType effectType, const glm::vec2& origin, const glm::vec2& bounds, bool unlit, bool forward) {
|
||||
if (str == "") {
|
||||
const glm::vec3& effectColor, float effectThickness, TextEffect effect,
|
||||
const glm::vec2& origin, const glm::vec2& bounds, float scale, bool unlit, bool forward) {
|
||||
if (!_loaded || str == "") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (str != drawInfo.string || bounds != drawInfo.bounds || origin != drawInfo.origin) {
|
||||
buildVertices(drawInfo, str, origin, bounds);
|
||||
int textEffect = (int)effect;
|
||||
const int SHADOW_EFFECT = (int)TextEffect::SHADOW_EFFECT;
|
||||
|
||||
// If we're switching to or from shadow effect mode, we need to rebuild the vertices
|
||||
if (str != drawInfo.string || bounds != drawInfo.bounds || origin != drawInfo.origin ||
|
||||
(drawInfo.params.effect != textEffect && (textEffect == SHADOW_EFFECT || drawInfo.params.effect == SHADOW_EFFECT)) ||
|
||||
(textEffect == SHADOW_EFFECT && scale != _scale)) {
|
||||
_scale = scale;
|
||||
buildVertices(drawInfo, str, origin, bounds, scale, textEffect == SHADOW_EFFECT);
|
||||
}
|
||||
|
||||
setupGPU();
|
||||
|
||||
struct GpuDrawParams {
|
||||
glm::vec4 color;
|
||||
glm::vec4 outline;
|
||||
};
|
||||
|
||||
if (!drawInfo.paramsBuffer || drawInfo.params.color != color || drawInfo.params.effect != effectType) {
|
||||
if (!drawInfo.paramsBuffer || drawInfo.params.color != color || drawInfo.params.effectColor != effectColor ||
|
||||
drawInfo.params.effectThickness != effectThickness || drawInfo.params.effect != textEffect) {
|
||||
drawInfo.params.color = color;
|
||||
drawInfo.params.effect = effectType;
|
||||
GpuDrawParams gpuDrawParams;
|
||||
drawInfo.params.effectColor = effectColor;
|
||||
drawInfo.params.effectThickness = effectThickness;
|
||||
drawInfo.params.effect = textEffect;
|
||||
|
||||
// need the gamma corrected color here
|
||||
DrawParams gpuDrawParams;
|
||||
gpuDrawParams.color = ColorUtils::sRGBToLinearVec4(drawInfo.params.color);
|
||||
gpuDrawParams.outline.x = (drawInfo.params.effect == OUTLINE_EFFECT) ? 1 : 0;
|
||||
drawInfo.paramsBuffer = std::make_shared<gpu::Buffer>(sizeof(GpuDrawParams), nullptr);
|
||||
drawInfo.paramsBuffer->setSubData(0, sizeof(GpuDrawParams), (const gpu::Byte*)&gpuDrawParams);
|
||||
gpuDrawParams.effectColor = ColorUtils::sRGBToLinearVec3(drawInfo.params.effectColor);
|
||||
gpuDrawParams.effectThickness = drawInfo.params.effectThickness;
|
||||
gpuDrawParams.effect = drawInfo.params.effect;
|
||||
if (!drawInfo.paramsBuffer) {
|
||||
drawInfo.paramsBuffer = std::make_shared<gpu::Buffer>(sizeof(DrawParams), nullptr);
|
||||
}
|
||||
drawInfo.paramsBuffer->setSubData(0, sizeof(DrawParams), (const gpu::Byte*)&gpuDrawParams);
|
||||
}
|
||||
// need the gamma corrected color here
|
||||
|
||||
batch.setPipeline(_pipelines[std::make_tuple(color.a < 1.0f, unlit, forward)]);
|
||||
batch.setInputFormat(_format);
|
||||
batch.setInputBuffer(0, drawInfo.verticesBuffer, 0, _format->getChannels().at(0)._stride);
|
||||
batch.setResourceTexture(render_utils::slot::texture::TextFont, _texture);
|
||||
batch.setUniformBuffer(0, drawInfo.paramsBuffer, 0, sizeof(GpuDrawParams));
|
||||
batch.setUniformBuffer(0, drawInfo.paramsBuffer, 0, sizeof(DrawParams));
|
||||
batch.setIndexBuffer(gpu::UINT16, drawInfo.indicesBuffer, 0);
|
||||
batch.drawIndexed(gpu::TRIANGLES, drawInfo.indexCount, 0);
|
||||
}
|
||||
|
|
|
@ -10,12 +10,16 @@
|
|||
#ifndef hifi_Font_h
|
||||
#define hifi_Font_h
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "Glyph.h"
|
||||
#include "EffectType.h"
|
||||
#include "TextEffect.h"
|
||||
#include <gpu/Batch.h>
|
||||
#include <gpu/Pipeline.h>
|
||||
|
||||
class Font {
|
||||
class Font : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
using Pointer = std::shared_ptr<Font>;
|
||||
|
||||
|
@ -24,14 +28,23 @@ public:
|
|||
void read(QIODevice& path);
|
||||
|
||||
struct DrawParams {
|
||||
vec4 color{ -1 };
|
||||
EffectType effect;
|
||||
vec4 color { 0 };
|
||||
|
||||
vec3 effectColor { 0 };
|
||||
float effectThickness { 0 };
|
||||
|
||||
int effect { 0 };
|
||||
|
||||
#if defined(__clang__)
|
||||
__attribute__((unused))
|
||||
#endif
|
||||
vec3 _spare;
|
||||
};
|
||||
|
||||
struct DrawInfo {
|
||||
gpu::BufferPointer verticesBuffer;
|
||||
gpu::BufferPointer indicesBuffer;
|
||||
gpu::BufferPointer paramsBuffer;
|
||||
gpu::BufferPointer verticesBuffer { nullptr };
|
||||
gpu::BufferPointer indicesBuffer { nullptr };
|
||||
gpu::BufferPointer paramsBuffer { nullptr };
|
||||
uint32_t indexCount;
|
||||
|
||||
QString string;
|
||||
|
@ -44,12 +57,18 @@ public:
|
|||
float getFontSize() const { return _fontSize; }
|
||||
|
||||
// Render string to batch
|
||||
void drawString(gpu::Batch& batch, DrawInfo& drawInfo, const QString& str,
|
||||
const glm::vec4& color, EffectType effectType,
|
||||
const glm::vec2& origin, const glm::vec2& bound, bool unlit, bool forward);
|
||||
void drawString(gpu::Batch& batch, DrawInfo& drawInfo, const QString& str, const glm::vec4& color,
|
||||
const glm::vec3& effectColor, float effectThickness, TextEffect effect,
|
||||
const glm::vec2& origin, const glm::vec2& bound, float scale, bool unlit, bool forward);
|
||||
|
||||
static Pointer load(const QString& family);
|
||||
|
||||
bool isLoaded() const { return _loaded; }
|
||||
void setLoaded(bool loaded) { _loaded = loaded; }
|
||||
|
||||
public slots:
|
||||
void handleFontNetworkReply();
|
||||
|
||||
private:
|
||||
static Pointer load(QIODevice& fontFile);
|
||||
QStringList tokenizeForWrapping(const QString& str) const;
|
||||
|
@ -57,7 +76,7 @@ private:
|
|||
glm::vec2 computeTokenExtent(const QString& str) const;
|
||||
|
||||
const Glyph& getGlyph(const QChar& c) const;
|
||||
void buildVertices(DrawInfo& drawInfo, const QString& str, const glm::vec2& origin, const glm::vec2& bounds);
|
||||
void buildVertices(DrawInfo& drawInfo, const QString& str, const glm::vec2& origin, const glm::vec2& bounds, float scale, bool enlargeForShadows);
|
||||
|
||||
void setupGPU();
|
||||
|
||||
|
@ -70,11 +89,15 @@ private:
|
|||
|
||||
// Font characteristics
|
||||
QString _family;
|
||||
float _fontSize = 0.0f;
|
||||
float _leading = 0.0f;
|
||||
float _ascent = 0.0f;
|
||||
float _descent = 0.0f;
|
||||
float _spaceWidth = 0.0f;
|
||||
float _fontSize { 0.0f };
|
||||
float _leading { 0.0f };
|
||||
float _ascent { 0.0f };
|
||||
float _descent { 0.0f };
|
||||
float _spaceWidth { 0.0f };
|
||||
|
||||
float _scale { 0.0f };
|
||||
|
||||
bool _loaded { true };
|
||||
|
||||
gpu::TexturePointer _texture;
|
||||
gpu::BufferStreamPointer _stream;
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2015/07/16
|
||||
// Copyright 2013 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
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#ifndef hifi_FontFamilies_h
|
||||
#define hifi_FontFamilies_h
|
||||
|
||||
// the standard sans serif font family
|
||||
#define SANS_FONT_FAMILY "Helvetica"
|
||||
|
||||
// the standard sans serif font family
|
||||
#define SERIF_FONT_FAMILY "Timeless"
|
||||
|
||||
// the standard mono font family
|
||||
#define MONO_FONT_FAMILY "Courier"
|
||||
|
||||
// the Inconsolata font family
|
||||
#ifdef Q_OS_WIN
|
||||
#define INCONSOLATA_FONT_FAMILY "Fixedsys"
|
||||
#define INCONSOLATA_FONT_WEIGHT QFont::Normal
|
||||
#else
|
||||
#define INCONSOLATA_FONT_FAMILY "Inconsolata"
|
||||
#define INCONSOLATA_FONT_WEIGHT QFont::Bold
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -18,5 +18,6 @@ void Glyph::read(QIODevice& in) {
|
|||
readStream(in, size);
|
||||
readStream(in, offset);
|
||||
readStream(in, d);
|
||||
// texSize is divided by the image size later
|
||||
texSize = size;
|
||||
}
|
||||
|
|
|
@ -43,7 +43,11 @@ layout(location=0) in vec2 varTexCoord0;
|
|||
layout(location=0) out vec4 outFragColor;
|
||||
|
||||
void main(void) {
|
||||
<@if HIFI_USE_MIRRORED@>
|
||||
vec4 fragColorRaw = texture(colorMap, vec2(1.0 - varTexCoord0.x, varTexCoord0.y));
|
||||
<@else@>
|
||||
vec4 fragColorRaw = texture(colorMap, varTexCoord0);
|
||||
<@endif@>
|
||||
vec3 fragColor = fragColorRaw.xyz;
|
||||
|
||||
vec3 srcColor = fragColor * getTwoPowExposure();
|
||||
|
|
|
@ -30,6 +30,7 @@ ScriptAudioInjector::ScriptAudioInjector(const AudioInjectorPointer& injector) :
|
|||
_injector(injector)
|
||||
{
|
||||
QObject::connect(injector.data(), &AudioInjector::finished, this, &ScriptAudioInjector::finished);
|
||||
connect(injector.data(), &QObject::destroyed, this, &QObject::deleteLater);
|
||||
}
|
||||
|
||||
ScriptAudioInjector::~ScriptAudioInjector() {
|
||||
|
|
|
@ -137,7 +137,7 @@ signals:
|
|||
void finished();
|
||||
|
||||
private:
|
||||
AudioInjectorPointer _injector;
|
||||
QWeakPointer<AudioInjector> _injector;
|
||||
|
||||
friend QScriptValue injectorToScriptValue(QScriptEngine* engine, ScriptAudioInjector* const& in);
|
||||
};
|
||||
|
|
|
@ -401,13 +401,14 @@ void ScriptEngines::stopAllScripts(bool restart) {
|
|||
continue;
|
||||
}
|
||||
|
||||
bool isOverrideScript = it.key().toString().compare(this->_defaultScriptsOverride.toString());
|
||||
// queue user scripts if restarting
|
||||
if (restart && scriptEngine->isUserLoaded()) {
|
||||
if (restart && (scriptEngine->isUserLoaded() || isOverrideScript)) {
|
||||
_isReloading = true;
|
||||
ScriptEngine::Type type = scriptEngine->getType();
|
||||
|
||||
connect(scriptEngine.data(), &ScriptEngine::finished, this, [this, type] (QString scriptName) {
|
||||
reloadScript(scriptName, true)->setType(type);
|
||||
connect(scriptEngine.data(), &ScriptEngine::finished, this, [this, type, isOverrideScript] (QString scriptName) {
|
||||
reloadScript(scriptName, !isOverrideScript)->setType(type);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
17
libraries/shared/src/FontFamilies.h
Normal file
17
libraries/shared/src/FontFamilies.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2015/07/16
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_FontFamilies_h
|
||||
#define hifi_FontFamilies_h
|
||||
|
||||
#define ROBOTO_FONT_FAMILY "Roboto"
|
||||
#define COURIER_FONT_FAMILY "Courier"
|
||||
#define INCONSOLATA_FONT_FAMILY "Inconsolata"
|
||||
#define TIMELESS_FONT_FAMILY "Timeless"
|
||||
|
||||
#endif // hifi_FontFamilies_h
|
26
libraries/shared/src/TextEffect.cpp
Normal file
26
libraries/shared/src/TextEffect.cpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
//
|
||||
// Created by Sam Gondelman on 7/21/2019
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "TextEffect.h"
|
||||
|
||||
const char* textEffectNames[] = {
|
||||
"none",
|
||||
"outline",
|
||||
"outline fill",
|
||||
"shadow"
|
||||
};
|
||||
|
||||
static const size_t TEXT_EFFECT_NAMES = (sizeof(textEffectNames) / sizeof(textEffectNames[0]));
|
||||
|
||||
QString TextEffectHelpers::getNameForTextEffect(TextEffect effect) {
|
||||
if (((int)effect <= 0) || ((int)effect >= (int)TEXT_EFFECT_NAMES)) {
|
||||
effect = (TextEffect)0;
|
||||
}
|
||||
|
||||
return textEffectNames[(int)effect];
|
||||
}
|
42
libraries/shared/src/TextEffect.h
Normal file
42
libraries/shared/src/TextEffect.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2015/07/16
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_TextEffect_h
|
||||
#define hifi_TextEffect_h
|
||||
|
||||
#include "QString"
|
||||
|
||||
/**jsdoc
|
||||
* <p>A {@link Entities.EntityProperties-Text|Text} entity may use one of the following effects:</p>
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Value</th><th>Description</th></tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td><code>"none"</code></td><td>No effect.</td></tr>
|
||||
* <tr><td><code>"outline"</code></td><td>An outline effect.</td></tr>
|
||||
* <tr><td><code>"outlineFill"</code></td><td>An outline effect, with fill.</td></tr>
|
||||
* <tr><td><code>"shadow"</code></td><td>A shadow effect.</td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef {string} Entities.TextEffect
|
||||
*/
|
||||
|
||||
enum class TextEffect {
|
||||
NO_EFFECT = 0,
|
||||
OUTLINE_EFFECT,
|
||||
OUTLINE_WITH_FILL_EFFECT,
|
||||
SHADOW_EFFECT
|
||||
};
|
||||
|
||||
class TextEffectHelpers {
|
||||
public:
|
||||
static QString getNameForTextEffect(TextEffect effect);
|
||||
};
|
||||
|
||||
#endif // hifi_TextEffect_h
|
36
libraries/shared/src/shared/WebRTC.h
Normal file
36
libraries/shared/src/shared/WebRTC.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
//
|
||||
// WebRTC.h
|
||||
// libraries/shared/src/shared/
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
#ifndef hifi_WebRTC_h
|
||||
#define hifi_WebRTC_h
|
||||
|
||||
#if defined(Q_OS_MAC)
|
||||
# define WEBRTC_ENABLED 1
|
||||
# define WEBRTC_POSIX 1
|
||||
#elif defined(Q_OS_WIN)
|
||||
# define WEBRTC_ENABLED 1
|
||||
# define WEBRTC_WIN 1
|
||||
# define NOMINMAX 1
|
||||
# define WIN32_LEAN_AND_MEAN 1
|
||||
#elif defined(Q_OS_ANDROID)
|
||||
// I don't yet have a working libwebrtc for android
|
||||
// # define WEBRTC_ENABLED 1
|
||||
// # define WEBRTC_POSIX 1
|
||||
#elif defined(Q_OS_LINUX)
|
||||
# define WEBRTC_ENABLED 1
|
||||
# define WEBRTC_POSIX 1
|
||||
#endif
|
||||
|
||||
#if defined(WEBRTC_ENABLED)
|
||||
# include <modules/audio_processing/include/audio_processing.h>
|
||||
# include "modules/audio_processing/audio_processing_impl.h"
|
||||
#endif
|
||||
|
||||
#endif // hifi_WebRTC_h
|
|
@ -264,7 +264,19 @@ void OffscreenQmlSurface::initializeEngine(QQmlEngine* engine) {
|
|||
}
|
||||
|
||||
auto rootContext = engine->rootContext();
|
||||
rootContext->setContextProperty("GL", ::getGLContextData());
|
||||
|
||||
static QJsonObject QML_GL_INFO;
|
||||
static std::once_flag once_gl_info;
|
||||
std::call_once(once_gl_info, [] {
|
||||
const auto& contextInfo = gl::ContextInfo::get();
|
||||
QML_GL_INFO = QJsonObject {
|
||||
{ "version", contextInfo.version.c_str() },
|
||||
{ "sl_version", contextInfo.shadingLanguageVersion.c_str() },
|
||||
{ "vendor", contextInfo.vendor.c_str() },
|
||||
{ "renderer", contextInfo.renderer.c_str() },
|
||||
};
|
||||
});
|
||||
rootContext->setContextProperty("GL", QML_GL_INFO);
|
||||
rootContext->setContextProperty("urlHandler", new UrlHandler(rootContext));
|
||||
rootContext->setContextProperty("resourceDirectoryUrl", QUrl::fromLocalFile(PathUtils::resourcesPath()));
|
||||
rootContext->setContextProperty("ApplicationInterface", qApp);
|
||||
|
|
|
@ -45,7 +45,7 @@ Rectangle {
|
|||
anchors.right: parent.right
|
||||
spacing: 5
|
||||
Repeater {
|
||||
model: [ "MSAA:PrepareFramebuffer:numSamples:4:1"
|
||||
model: [ "MSAA:PreparePrimaryBufferForward:numSamples:4:1"
|
||||
]
|
||||
ConfigSlider {
|
||||
label: qsTr(modelData.split(":")[0])
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <ResourceManager.h>
|
||||
#include <ResourceRequestObserver.h>
|
||||
#include <ResourceCache.h>
|
||||
#include <material-networking/MaterialCache.h>
|
||||
#include <material-networking/TextureCache.h>
|
||||
#include <hfm/ModelFormatRegistry.h>
|
||||
#include <FBXSerializer.h>
|
||||
|
@ -42,6 +43,7 @@ Oven::Oven() {
|
|||
DependencyManager::set<ResourceRequestObserver>();
|
||||
DependencyManager::set<ResourceCacheSharedItems>();
|
||||
DependencyManager::set<TextureCache>();
|
||||
DependencyManager::set<MaterialCache>();
|
||||
|
||||
MaterialBaker::setNextOvenWorkerThreadOperator([] {
|
||||
return Oven::instance().getNextWorkerThread();
|
||||
|
|
Loading…
Reference in a new issue