mirror of
https://github.com/overte-org/overte.git
synced 2025-07-23 18:24:08 +02:00
Merge branch 'master' into decals
This commit is contained in:
commit
15cdfef16a
51 changed files with 993 additions and 319 deletions
|
@ -464,32 +464,41 @@ void AssetServer::handleAssetMappingOperation(QSharedPointer<ReceivedMessage> me
|
||||||
auto replyPacket = NLPacketList::create(PacketType::AssetMappingOperationReply, QByteArray(), true, true);
|
auto replyPacket = NLPacketList::create(PacketType::AssetMappingOperationReply, QByteArray(), true, true);
|
||||||
replyPacket->writePrimitive(messageID);
|
replyPacket->writePrimitive(messageID);
|
||||||
|
|
||||||
|
bool canWriteToAssetServer = true;
|
||||||
|
if (senderNode) {
|
||||||
|
canWriteToAssetServer = senderNode->getCanWriteToAssetServer();
|
||||||
|
}
|
||||||
|
|
||||||
switch (operationType) {
|
switch (operationType) {
|
||||||
case AssetMappingOperationType::Get:
|
case AssetMappingOperationType::Get:
|
||||||
handleGetMappingOperation(*message, senderNode, *replyPacket);
|
handleGetMappingOperation(*message, *replyPacket);
|
||||||
break;
|
break;
|
||||||
case AssetMappingOperationType::GetAll:
|
case AssetMappingOperationType::GetAll:
|
||||||
handleGetAllMappingOperation(*message, senderNode, *replyPacket);
|
handleGetAllMappingOperation(*replyPacket);
|
||||||
break;
|
break;
|
||||||
case AssetMappingOperationType::Set:
|
case AssetMappingOperationType::Set:
|
||||||
handleSetMappingOperation(*message, senderNode, *replyPacket);
|
handleSetMappingOperation(*message, canWriteToAssetServer, *replyPacket);
|
||||||
break;
|
break;
|
||||||
case AssetMappingOperationType::Delete:
|
case AssetMappingOperationType::Delete:
|
||||||
handleDeleteMappingsOperation(*message, senderNode, *replyPacket);
|
handleDeleteMappingsOperation(*message, canWriteToAssetServer, *replyPacket);
|
||||||
break;
|
break;
|
||||||
case AssetMappingOperationType::Rename:
|
case AssetMappingOperationType::Rename:
|
||||||
handleRenameMappingOperation(*message, senderNode, *replyPacket);
|
handleRenameMappingOperation(*message, canWriteToAssetServer, *replyPacket);
|
||||||
break;
|
break;
|
||||||
case AssetMappingOperationType::SetBakingEnabled:
|
case AssetMappingOperationType::SetBakingEnabled:
|
||||||
handleSetBakingEnabledOperation(*message, senderNode, *replyPacket);
|
handleSetBakingEnabledOperation(*message, canWriteToAssetServer, *replyPacket);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
if (senderNode) {
|
||||||
nodeList->sendPacketList(std::move(replyPacket), *senderNode);
|
nodeList->sendPacketList(std::move(replyPacket), *senderNode);
|
||||||
|
} else {
|
||||||
|
nodeList->sendPacketList(std::move(replyPacket), message->getSenderSockAddr());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetServer::handleGetMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket) {
|
void AssetServer::handleGetMappingOperation(ReceivedMessage& message, NLPacketList& replyPacket) {
|
||||||
QString assetPath = message.readString();
|
QString assetPath = message.readString();
|
||||||
|
|
||||||
QUrl url { assetPath };
|
QUrl url { assetPath };
|
||||||
|
@ -568,7 +577,7 @@ void AssetServer::handleGetMappingOperation(ReceivedMessage& message, SharedNode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetServer::handleGetAllMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket) {
|
void AssetServer::handleGetAllMappingOperation(NLPacketList& replyPacket) {
|
||||||
replyPacket.writePrimitive(AssetUtils::AssetServerError::NoError);
|
replyPacket.writePrimitive(AssetUtils::AssetServerError::NoError);
|
||||||
|
|
||||||
uint32_t count = (uint32_t)_fileMappings.size();
|
uint32_t count = (uint32_t)_fileMappings.size();
|
||||||
|
@ -591,8 +600,8 @@ void AssetServer::handleGetAllMappingOperation(ReceivedMessage& message, SharedN
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetServer::handleSetMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket) {
|
void AssetServer::handleSetMappingOperation(ReceivedMessage& message, bool hasWriteAccess, NLPacketList& replyPacket) {
|
||||||
if (senderNode->getCanWriteToAssetServer()) {
|
if (hasWriteAccess) {
|
||||||
QString assetPath = message.readString();
|
QString assetPath = message.readString();
|
||||||
|
|
||||||
auto assetHash = message.read(AssetUtils::SHA256_HASH_LENGTH).toHex();
|
auto assetHash = message.read(AssetUtils::SHA256_HASH_LENGTH).toHex();
|
||||||
|
@ -614,8 +623,8 @@ void AssetServer::handleSetMappingOperation(ReceivedMessage& message, SharedNode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetServer::handleDeleteMappingsOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket) {
|
void AssetServer::handleDeleteMappingsOperation(ReceivedMessage& message, bool hasWriteAccess, NLPacketList& replyPacket) {
|
||||||
if (senderNode->getCanWriteToAssetServer()) {
|
if (hasWriteAccess) {
|
||||||
int numberOfDeletedMappings { 0 };
|
int numberOfDeletedMappings { 0 };
|
||||||
message.readPrimitive(&numberOfDeletedMappings);
|
message.readPrimitive(&numberOfDeletedMappings);
|
||||||
|
|
||||||
|
@ -642,8 +651,8 @@ void AssetServer::handleDeleteMappingsOperation(ReceivedMessage& message, Shared
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetServer::handleRenameMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket) {
|
void AssetServer::handleRenameMappingOperation(ReceivedMessage& message, bool hasWriteAccess, NLPacketList& replyPacket) {
|
||||||
if (senderNode->getCanWriteToAssetServer()) {
|
if (hasWriteAccess) {
|
||||||
QString oldPath = message.readString();
|
QString oldPath = message.readString();
|
||||||
QString newPath = message.readString();
|
QString newPath = message.readString();
|
||||||
|
|
||||||
|
@ -664,8 +673,8 @@ void AssetServer::handleRenameMappingOperation(ReceivedMessage& message, SharedN
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetServer::handleSetBakingEnabledOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket) {
|
void AssetServer::handleSetBakingEnabledOperation(ReceivedMessage& message, bool hasWriteAccess, NLPacketList& replyPacket) {
|
||||||
if (senderNode->getCanWriteToAssetServer()) {
|
if (hasWriteAccess) {
|
||||||
bool enabled { true };
|
bool enabled { true };
|
||||||
message.readPrimitive(&enabled);
|
message.readPrimitive(&enabled);
|
||||||
|
|
||||||
|
@ -739,9 +748,14 @@ void AssetServer::handleAssetGet(QSharedPointer<ReceivedMessage> message, Shared
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetServer::handleAssetUpload(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
void AssetServer::handleAssetUpload(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||||
|
bool canWriteToAssetServer = true;
|
||||||
|
if (senderNode) {
|
||||||
|
canWriteToAssetServer = senderNode->getCanWriteToAssetServer();
|
||||||
|
}
|
||||||
|
|
||||||
if (senderNode->getCanWriteToAssetServer()) {
|
|
||||||
qCDebug(asset_server) << "Starting an UploadAssetTask for upload from" << uuidStringWithoutCurlyBraces(senderNode->getUUID());
|
if (canWriteToAssetServer) {
|
||||||
|
qCDebug(asset_server) << "Starting an UploadAssetTask for upload from" << uuidStringWithoutCurlyBraces(message->getSourceID());
|
||||||
|
|
||||||
auto task = new UploadAssetTask(message, senderNode, _filesDirectory, _filesizeLimit);
|
auto task = new UploadAssetTask(message, senderNode, _filesDirectory, _filesizeLimit);
|
||||||
_transferTaskPool.start(task);
|
_transferTaskPool.start(task);
|
||||||
|
@ -761,7 +775,11 @@ void AssetServer::handleAssetUpload(QSharedPointer<ReceivedMessage> message, Sha
|
||||||
|
|
||||||
// send off the packet
|
// send off the packet
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
if (senderNode) {
|
||||||
nodeList->sendPacket(std::move(permissionErrorPacket), *senderNode);
|
nodeList->sendPacket(std::move(permissionErrorPacket), *senderNode);
|
||||||
|
} else {
|
||||||
|
nodeList->sendPacket(std::move(permissionErrorPacket), message->getSenderSockAddr());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,9 +24,6 @@
|
||||||
#include "RegisteredMetaTypes.h"
|
#include "RegisteredMetaTypes.h"
|
||||||
|
|
||||||
struct AssetMeta {
|
struct AssetMeta {
|
||||||
AssetMeta() {
|
|
||||||
}
|
|
||||||
|
|
||||||
int bakeVersion { 0 };
|
int bakeVersion { 0 };
|
||||||
bool failedLastBake { false };
|
bool failedLastBake { false };
|
||||||
QString lastBakeErrors;
|
QString lastBakeErrors;
|
||||||
|
@ -55,14 +52,15 @@ private slots:
|
||||||
void sendStatsPacket() override;
|
void sendStatsPacket() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using Mappings = std::unordered_map<QString, QString>;
|
void handleGetMappingOperation(ReceivedMessage& message, NLPacketList& replyPacket);
|
||||||
|
void handleGetAllMappingOperation(NLPacketList& replyPacket);
|
||||||
|
void handleSetMappingOperation(ReceivedMessage& message, bool hasWriteAccess, NLPacketList& replyPacket);
|
||||||
|
void handleDeleteMappingsOperation(ReceivedMessage& message, bool hasWriteAccess, NLPacketList& replyPacket);
|
||||||
|
void handleRenameMappingOperation(ReceivedMessage& message, bool hasWriteAccess, NLPacketList& replyPacket);
|
||||||
|
void handleSetBakingEnabledOperation(ReceivedMessage& message, bool hasWriteAccess, NLPacketList& replyPacket);
|
||||||
|
|
||||||
void handleGetMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket);
|
void handleAssetServerBackup(ReceivedMessage& message, NLPacketList& replyPacket);
|
||||||
void handleGetAllMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket);
|
void handleAssetServerRestore(ReceivedMessage& message, NLPacketList& replyPacket);
|
||||||
void handleSetMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket);
|
|
||||||
void handleDeleteMappingsOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket);
|
|
||||||
void handleRenameMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket);
|
|
||||||
void handleSetBakingEnabledOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket);
|
|
||||||
|
|
||||||
// Mapping file operations must be called from main assignment thread only
|
// Mapping file operations must be called from main assignment thread only
|
||||||
bool loadMappingsFromFile();
|
bool loadMappingsFromFile();
|
||||||
|
@ -106,7 +104,7 @@ private:
|
||||||
/// Remove baked paths when the original asset is deleteds
|
/// Remove baked paths when the original asset is deleteds
|
||||||
void removeBakedPathsForDeletedAsset(AssetUtils::AssetHash originalAssetHash);
|
void removeBakedPathsForDeletedAsset(AssetUtils::AssetHash originalAssetHash);
|
||||||
|
|
||||||
Mappings _fileMappings;
|
AssetUtils::Mappings _fileMappings;
|
||||||
|
|
||||||
QDir _resourcesDirectory;
|
QDir _resourcesDirectory;
|
||||||
QDir _filesDirectory;
|
QDir _filesDirectory;
|
||||||
|
|
|
@ -112,5 +112,9 @@ void SendAssetTask::run() {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
if (_senderNode) {
|
||||||
nodeList->sendPacketList(std::move(replyPacketList), *_senderNode);
|
nodeList->sendPacketList(std::move(replyPacketList), *_senderNode);
|
||||||
|
} else {
|
||||||
|
nodeList->sendPacketList(std::move(replyPacketList), _message->getSenderSockAddr());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,8 +42,11 @@ void UploadAssetTask::run() {
|
||||||
uint64_t fileSize;
|
uint64_t fileSize;
|
||||||
buffer.read(reinterpret_cast<char*>(&fileSize), sizeof(fileSize));
|
buffer.read(reinterpret_cast<char*>(&fileSize), sizeof(fileSize));
|
||||||
|
|
||||||
qDebug() << "UploadAssetTask reading a file of " << fileSize << "bytes from"
|
if (_senderNode) {
|
||||||
<< uuidStringWithoutCurlyBraces(_senderNode->getUUID());
|
qDebug() << "UploadAssetTask reading a file of " << fileSize << "bytes from" << uuidStringWithoutCurlyBraces(_senderNode->getUUID());
|
||||||
|
} else {
|
||||||
|
qDebug() << "UploadAssetTask reading a file of " << fileSize << "bytes from" << _receivedMessage->getSenderSockAddr();
|
||||||
|
}
|
||||||
|
|
||||||
auto replyPacket = NLPacket::create(PacketType::AssetUploadReply, -1, true);
|
auto replyPacket = NLPacket::create(PacketType::AssetUploadReply, -1, true);
|
||||||
replyPacket->writePrimitive(messageID);
|
replyPacket->writePrimitive(messageID);
|
||||||
|
@ -56,8 +59,11 @@ void UploadAssetTask::run() {
|
||||||
auto hash = AssetUtils::hashData(fileData);
|
auto hash = AssetUtils::hashData(fileData);
|
||||||
auto hexHash = hash.toHex();
|
auto hexHash = hash.toHex();
|
||||||
|
|
||||||
qDebug() << "Hash for uploaded file from" << uuidStringWithoutCurlyBraces(_senderNode->getUUID())
|
if (_senderNode) {
|
||||||
<< "is: (" << hexHash << ") ";
|
qDebug() << "Hash for uploaded file from" << uuidStringWithoutCurlyBraces(_senderNode->getUUID()) << "is: (" << hexHash << ")";
|
||||||
|
} else {
|
||||||
|
qDebug() << "Hash for uploaded file from" << _receivedMessage->getSenderSockAddr() << "is: (" << hexHash << ")";
|
||||||
|
}
|
||||||
|
|
||||||
QFile file { _resourcesDir.filePath(QString(hexHash)) };
|
QFile file { _resourcesDir.filePath(QString(hexHash)) };
|
||||||
|
|
||||||
|
@ -103,5 +109,9 @@ void UploadAssetTask::run() {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
if (_senderNode) {
|
||||||
nodeList->sendPacket(std::move(replyPacket), *_senderNode);
|
nodeList->sendPacket(std::move(replyPacket), *_senderNode);
|
||||||
|
} else {
|
||||||
|
nodeList->sendPacket(std::move(replyPacket), _receivedMessage->getSenderSockAddr());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
400
domain-server/src/BackupSupervisor.cpp
Normal file
400
domain-server/src/BackupSupervisor.cpp
Normal file
|
@ -0,0 +1,400 @@
|
||||||
|
//
|
||||||
|
// BackupSupervisor.cpp
|
||||||
|
// domain-server/src
|
||||||
|
//
|
||||||
|
// Created by Clement Brisset on 1/12/18.
|
||||||
|
// Copyright 2018 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 "BackupSupervisor.h"
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QDate>
|
||||||
|
|
||||||
|
#include <AssetClient.h>
|
||||||
|
#include <AssetRequest.h>
|
||||||
|
#include <AssetUpload.h>
|
||||||
|
#include <MappingRequest.h>
|
||||||
|
#include <PathUtils.h>
|
||||||
|
|
||||||
|
const QString BACKUPS_DIR = "backups/";
|
||||||
|
const QString ASSETS_DIR = "files/";
|
||||||
|
const QString MAPPINGS_PREFIX = "mappings-";
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
BackupSupervisor::BackupSupervisor() {
|
||||||
|
_backupsDirectory = PathUtils::getAppDataPath() + BACKUPS_DIR;
|
||||||
|
QDir backupDir { _backupsDirectory };
|
||||||
|
if (!backupDir.exists()) {
|
||||||
|
backupDir.mkpath(".");
|
||||||
|
}
|
||||||
|
|
||||||
|
_assetsDirectory = PathUtils::getAppDataPath() + BACKUPS_DIR + ASSETS_DIR;
|
||||||
|
QDir assetsDir { _assetsDirectory };
|
||||||
|
if (!assetsDir.exists()) {
|
||||||
|
assetsDir.mkpath(".");
|
||||||
|
}
|
||||||
|
|
||||||
|
loadAllBackups();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackupSupervisor::loadAllBackups() {
|
||||||
|
_backups.clear();
|
||||||
|
_assetsInBackups.clear();
|
||||||
|
_assetsOnDisk.clear();
|
||||||
|
_allBackupsLoadedSuccessfully = true;
|
||||||
|
|
||||||
|
QDir assetsDir { _assetsDirectory };
|
||||||
|
auto assetNames = assetsDir.entryList(QDir::Files);
|
||||||
|
qDebug() << "Loading" << assetNames.size() << "assets.";
|
||||||
|
|
||||||
|
// store all valid hashes
|
||||||
|
copy_if(begin(assetNames), end(assetNames),
|
||||||
|
inserter(_assetsOnDisk, begin(_assetsOnDisk)), AssetUtils::isValidHash);
|
||||||
|
|
||||||
|
QDir backupsDir { _backupsDirectory };
|
||||||
|
auto files = backupsDir.entryList({ MAPPINGS_PREFIX + "*.json" }, QDir::Files);
|
||||||
|
qDebug() << "Loading" << files.size() << "backups.";
|
||||||
|
|
||||||
|
for (const auto& fileName : files) {
|
||||||
|
auto filePath = backupsDir.filePath(fileName);
|
||||||
|
auto success = loadBackup(filePath);
|
||||||
|
if (!success) {
|
||||||
|
qCritical() << "Failed to load backup file" << filePath;
|
||||||
|
_allBackupsLoadedSuccessfully = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<AssetUtils::AssetHash> missingAssets;
|
||||||
|
set_difference(begin(_assetsInBackups), end(_assetsInBackups),
|
||||||
|
begin(_assetsOnDisk), end(_assetsOnDisk),
|
||||||
|
back_inserter(missingAssets));
|
||||||
|
if (missingAssets.size() > 0) {
|
||||||
|
qWarning() << "Found" << missingAssets.size() << "assets missing.";
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<AssetUtils::AssetHash> deprecatedAssets;
|
||||||
|
set_difference(begin(_assetsOnDisk), end(_assetsOnDisk),
|
||||||
|
begin(_assetsInBackups), end(_assetsInBackups),
|
||||||
|
back_inserter(deprecatedAssets));
|
||||||
|
|
||||||
|
if (deprecatedAssets.size() > 0) {
|
||||||
|
qDebug() << "Found" << deprecatedAssets.size() << "assets to delete.";
|
||||||
|
if (_allBackupsLoadedSuccessfully) {
|
||||||
|
for (const auto& hash : deprecatedAssets) {
|
||||||
|
QFile::remove(_assetsDirectory + hash);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qWarning() << "Some backups did not load properly, aborting deleting for safety.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BackupSupervisor::loadBackup(const QString& backupFile) {
|
||||||
|
_backups.push_back({ backupFile.toStdString(), {}, false });
|
||||||
|
auto& backup = _backups.back();
|
||||||
|
|
||||||
|
QFile file { backupFile };
|
||||||
|
if (!file.open(QFile::ReadOnly)) {
|
||||||
|
qCritical() << "Could not open backup file:" << backupFile;
|
||||||
|
backup.corruptedBackup = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
QJsonParseError error;
|
||||||
|
auto document = QJsonDocument::fromJson(file.readAll(), &error);
|
||||||
|
if (document.isNull() || !document.isObject()) {
|
||||||
|
qCritical() << "Could not parse backup file to JSON object:" << backupFile;
|
||||||
|
qCritical() << " Error:" << error.errorString();
|
||||||
|
backup.corruptedBackup = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto jsonObject = document.object();
|
||||||
|
for (auto it = begin(jsonObject); it != end(jsonObject); ++it) {
|
||||||
|
const auto& assetPath = it.key();
|
||||||
|
const auto& assetHash = it.value().toString();
|
||||||
|
|
||||||
|
if (!AssetUtils::isValidHash(assetHash)) {
|
||||||
|
qCritical() << "Corrupted mapping in backup file" << backupFile << ":" << it.key();
|
||||||
|
backup.corruptedBackup = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
backup.mappings[assetPath] = assetHash;
|
||||||
|
_assetsInBackups.insert(assetHash);
|
||||||
|
}
|
||||||
|
|
||||||
|
_backups.push_back(backup);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackupSupervisor::backupAssetServer() {
|
||||||
|
if (backupInProgress() || restoreInProgress()) {
|
||||||
|
qWarning() << "There is already a backup/restore in progress.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto assetClient = DependencyManager::get<AssetClient>();
|
||||||
|
auto request = assetClient->createGetAllMappingsRequest();
|
||||||
|
|
||||||
|
connect(request, &GetAllMappingsRequest::finished, this, [this](GetAllMappingsRequest* request) {
|
||||||
|
qDebug() << "Got" << request->getMappings().size() << "mappings!";
|
||||||
|
|
||||||
|
if (request->getError() != MappingRequest::NoError) {
|
||||||
|
qCritical() << "Could not complete backup.";
|
||||||
|
qCritical() << " Error:" << request->getErrorString();
|
||||||
|
finishBackup();
|
||||||
|
request->deleteLater();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!writeBackupFile(request->getMappings())) {
|
||||||
|
finishBackup();
|
||||||
|
request->deleteLater();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(!_backups.empty());
|
||||||
|
const auto& mappings = _backups.back().mappings;
|
||||||
|
backupMissingFiles(mappings);
|
||||||
|
|
||||||
|
request->deleteLater();
|
||||||
|
});
|
||||||
|
|
||||||
|
startBackup();
|
||||||
|
request->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackupSupervisor::backupMissingFiles(const AssetUtils::Mappings& mappings) {
|
||||||
|
_assetsLeftToRequest.reserve(mappings.size());
|
||||||
|
for (auto& mapping : mappings) {
|
||||||
|
const auto& hash = mapping.second;
|
||||||
|
if (_assetsOnDisk.find(hash) == end(_assetsOnDisk)) {
|
||||||
|
_assetsLeftToRequest.push_back(hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
backupNextMissingFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackupSupervisor::backupNextMissingFile() {
|
||||||
|
if (_assetsLeftToRequest.empty()) {
|
||||||
|
finishBackup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto hash = _assetsLeftToRequest.back();
|
||||||
|
_assetsLeftToRequest.pop_back();
|
||||||
|
|
||||||
|
auto assetClient = DependencyManager::get<AssetClient>();
|
||||||
|
auto assetRequest = assetClient->createRequest(hash);
|
||||||
|
|
||||||
|
connect(assetRequest, &AssetRequest::finished, this, [this](AssetRequest* request) {
|
||||||
|
if (request->getError() == AssetRequest::NoError) {
|
||||||
|
qDebug() << "Got" << request->getHash();
|
||||||
|
|
||||||
|
bool success = writeAssetFile(request->getHash(), request->getData());
|
||||||
|
if (!success) {
|
||||||
|
qCritical() << "Failed to write asset file" << request->getHash();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qCritical() << "Failed to backup asset" << request->getHash();
|
||||||
|
}
|
||||||
|
|
||||||
|
backupNextMissingFile();
|
||||||
|
|
||||||
|
request->deleteLater();
|
||||||
|
});
|
||||||
|
|
||||||
|
assetRequest->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BackupSupervisor::writeBackupFile(const AssetUtils::AssetMappings& mappings) {
|
||||||
|
auto filename = MAPPINGS_PREFIX + QDateTime::currentDateTimeUtc().toString(Qt::ISODate) + ".json";
|
||||||
|
QFile file { PathUtils::getAppDataPath() + BACKUPS_DIR + filename };
|
||||||
|
if (!file.open(QFile::WriteOnly)) {
|
||||||
|
qCritical() << "Could not open backup file" << file.fileName();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AssetServerBackup backup;
|
||||||
|
QJsonObject jsonObject;
|
||||||
|
for (auto& mapping : mappings) {
|
||||||
|
backup.mappings[mapping.first] = mapping.second.hash;
|
||||||
|
_assetsInBackups.insert(mapping.second.hash);
|
||||||
|
jsonObject.insert(mapping.first, mapping.second.hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonDocument document(jsonObject);
|
||||||
|
file.write(document.toJson());
|
||||||
|
|
||||||
|
backup.filePath = file.fileName().toStdString();
|
||||||
|
_backups.push_back(backup);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BackupSupervisor::writeAssetFile(const AssetUtils::AssetHash& hash, const QByteArray& data) {
|
||||||
|
QDir assetsDir { _assetsDirectory };
|
||||||
|
QFile file { assetsDir.filePath(hash) };
|
||||||
|
if (!file.open(QFile::WriteOnly)) {
|
||||||
|
qCritical() << "Could not open backup file" << file.fileName();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
file.write(data);
|
||||||
|
|
||||||
|
_assetsOnDisk.insert(hash);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackupSupervisor::restoreAssetServer(int backupIndex) {
|
||||||
|
if (backupInProgress() || restoreInProgress()) {
|
||||||
|
qWarning() << "There is already a backup/restore in progress.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto assetClient = DependencyManager::get<AssetClient>();
|
||||||
|
auto request = assetClient->createGetAllMappingsRequest();
|
||||||
|
|
||||||
|
connect(request, &GetAllMappingsRequest::finished, this, [this, backupIndex](GetAllMappingsRequest* request) {
|
||||||
|
if (request->getError() == MappingRequest::NoError) {
|
||||||
|
const auto& newMappings = _backups.at(backupIndex).mappings;
|
||||||
|
computeServerStateDifference(request->getMappings(), newMappings);
|
||||||
|
|
||||||
|
restoreAllAssets();
|
||||||
|
} else {
|
||||||
|
finishRestore();
|
||||||
|
}
|
||||||
|
|
||||||
|
request->deleteLater();
|
||||||
|
});
|
||||||
|
|
||||||
|
startRestore();
|
||||||
|
request->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackupSupervisor::computeServerStateDifference(const AssetUtils::AssetMappings& currentMappings,
|
||||||
|
const AssetUtils::Mappings& newMappings) {
|
||||||
|
_mappingsLeftToSet.reserve((int)newMappings.size());
|
||||||
|
_assetsLeftToUpload.reserve((int)newMappings.size());
|
||||||
|
_mappingsLeftToDelete.reserve((int)currentMappings.size());
|
||||||
|
|
||||||
|
set<AssetUtils::AssetHash> currentAssets;
|
||||||
|
for (const auto& currentMapping : currentMappings) {
|
||||||
|
const auto& currentPath = currentMapping.first;
|
||||||
|
const auto& currentHash = currentMapping.second.hash;
|
||||||
|
|
||||||
|
if (newMappings.find(currentPath) == end(newMappings)) {
|
||||||
|
_mappingsLeftToDelete.push_back(currentPath);
|
||||||
|
}
|
||||||
|
currentAssets.insert(currentHash);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& newMapping : newMappings) {
|
||||||
|
const auto& newPath = newMapping.first;
|
||||||
|
const auto& newHash = newMapping.second;
|
||||||
|
|
||||||
|
auto it = currentMappings.find(newPath);
|
||||||
|
if (it == end(currentMappings) || it->second.hash != newHash) {
|
||||||
|
_mappingsLeftToSet.push_back({ newPath, newHash });
|
||||||
|
}
|
||||||
|
if (currentAssets.find(newHash) == end(currentAssets)) {
|
||||||
|
_assetsLeftToUpload.push_back(newHash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "Mappings to set:" << _mappingsLeftToSet.size();
|
||||||
|
qDebug() << "Mappings to del:" << _mappingsLeftToDelete.size();
|
||||||
|
qDebug() << "Assets to upload:" << _assetsLeftToUpload.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackupSupervisor::restoreAllAssets() {
|
||||||
|
restoreNextAsset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackupSupervisor::restoreNextAsset() {
|
||||||
|
if (_assetsLeftToUpload.empty()) {
|
||||||
|
updateMappings();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto hash = _assetsLeftToUpload.back();
|
||||||
|
_assetsLeftToUpload.pop_back();
|
||||||
|
|
||||||
|
auto assetFilename = _assetsDirectory + hash;
|
||||||
|
|
||||||
|
auto assetClient = DependencyManager::get<AssetClient>();
|
||||||
|
auto request = assetClient->createUpload(assetFilename);
|
||||||
|
|
||||||
|
connect(request, &AssetUpload::finished, this, [this](AssetUpload* request) {
|
||||||
|
if (request->getError() != AssetUpload::NoError) {
|
||||||
|
qCritical() << "Failed to restore asset:" << request->getFilename();
|
||||||
|
qCritical() << " Error:" << request->getErrorString();
|
||||||
|
}
|
||||||
|
|
||||||
|
restoreNextAsset();
|
||||||
|
|
||||||
|
request->deleteLater();
|
||||||
|
});
|
||||||
|
|
||||||
|
request->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackupSupervisor::updateMappings() {
|
||||||
|
auto assetClient = DependencyManager::get<AssetClient>();
|
||||||
|
for (const auto& mapping : _mappingsLeftToSet) {
|
||||||
|
auto request = assetClient->createSetMappingRequest(mapping.first, mapping.second);
|
||||||
|
connect(request, &SetMappingRequest::finished, this, [this](SetMappingRequest* request) {
|
||||||
|
if (request->getError() != MappingRequest::NoError) {
|
||||||
|
qCritical() << "Failed to set mapping:" << request->getPath();
|
||||||
|
qCritical() << " Error:" << request->getErrorString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (--_mappingRequestsInFlight == 0) {
|
||||||
|
finishRestore();
|
||||||
|
}
|
||||||
|
|
||||||
|
request->deleteLater();
|
||||||
|
});
|
||||||
|
|
||||||
|
request->start();
|
||||||
|
++_mappingRequestsInFlight;
|
||||||
|
}
|
||||||
|
_mappingsLeftToSet.clear();
|
||||||
|
|
||||||
|
auto request = assetClient->createDeleteMappingsRequest(_mappingsLeftToDelete);
|
||||||
|
connect(request, &DeleteMappingsRequest::finished, this, [this](DeleteMappingsRequest* request) {
|
||||||
|
if (request->getError() != MappingRequest::NoError) {
|
||||||
|
qCritical() << "Failed to delete mappings";
|
||||||
|
qCritical() << " Error:" << request->getErrorString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (--_mappingRequestsInFlight == 0) {
|
||||||
|
finishRestore();
|
||||||
|
}
|
||||||
|
|
||||||
|
request->deleteLater();
|
||||||
|
});
|
||||||
|
_mappingsLeftToDelete.clear();
|
||||||
|
|
||||||
|
request->start();
|
||||||
|
++_mappingRequestsInFlight;
|
||||||
|
}
|
||||||
|
bool BackupSupervisor::deleteBackup(int backupIndex) {
|
||||||
|
if (backupInProgress() || restoreInProgress()) {
|
||||||
|
qWarning() << "There is a backup/restore in progress.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto& filePath = _backups.at(backupIndex).filePath;
|
||||||
|
auto success = QFile::remove(filePath.c_str());
|
||||||
|
|
||||||
|
loadAllBackups();
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
85
domain-server/src/BackupSupervisor.h
Normal file
85
domain-server/src/BackupSupervisor.h
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
//
|
||||||
|
// BackupSupervisor.h
|
||||||
|
// domain-server/src
|
||||||
|
//
|
||||||
|
// Created by Clement Brisset on 1/12/18.
|
||||||
|
// Copyright 2018 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_BackupSupervisor_h
|
||||||
|
#define hifi_BackupSupervisor_h
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include <AssetUtils.h>
|
||||||
|
|
||||||
|
#include <ReceivedMessage.h>
|
||||||
|
|
||||||
|
struct AssetServerBackup {
|
||||||
|
std::string filePath;
|
||||||
|
AssetUtils::Mappings mappings;
|
||||||
|
bool corruptedBackup;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BackupSupervisor : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
BackupSupervisor();
|
||||||
|
|
||||||
|
void backupAssetServer();
|
||||||
|
void restoreAssetServer(int backupIndex);
|
||||||
|
bool deleteBackup(int backupIndex);
|
||||||
|
|
||||||
|
const std::vector<AssetServerBackup>& getBackups() const { return _backups; };
|
||||||
|
|
||||||
|
bool backupInProgress() const { return _backupInProgress; }
|
||||||
|
bool restoreInProgress() const { return _restoreInProgress; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void loadAllBackups();
|
||||||
|
bool loadBackup(const QString& backupFile);
|
||||||
|
|
||||||
|
void startBackup() { _backupInProgress = true; }
|
||||||
|
void finishBackup() { _backupInProgress = false; }
|
||||||
|
void backupMissingFiles(const AssetUtils::Mappings& mappings);
|
||||||
|
void backupNextMissingFile();
|
||||||
|
bool writeBackupFile(const AssetUtils::AssetMappings& mappings);
|
||||||
|
bool writeAssetFile(const AssetUtils::AssetHash& hash, const QByteArray& data);
|
||||||
|
|
||||||
|
void startRestore() { _restoreInProgress = true; }
|
||||||
|
void finishRestore() { _restoreInProgress = false; }
|
||||||
|
void computeServerStateDifference(const AssetUtils::AssetMappings& currentMappings,
|
||||||
|
const AssetUtils::Mappings& newMappings);
|
||||||
|
void restoreAllAssets();
|
||||||
|
void restoreNextAsset();
|
||||||
|
void updateMappings();
|
||||||
|
|
||||||
|
QString _backupsDirectory;
|
||||||
|
QString _assetsDirectory;
|
||||||
|
|
||||||
|
// Internal storage for backups on disk
|
||||||
|
bool _allBackupsLoadedSuccessfully { false };
|
||||||
|
std::vector<AssetServerBackup> _backups;
|
||||||
|
std::set<AssetUtils::AssetHash> _assetsInBackups;
|
||||||
|
std::set<AssetUtils::AssetHash> _assetsOnDisk;
|
||||||
|
|
||||||
|
// Internal storage for backup in progress
|
||||||
|
bool _backupInProgress { false };
|
||||||
|
std::vector<AssetUtils::AssetHash> _assetsLeftToRequest;
|
||||||
|
|
||||||
|
// Internal storage for restore in progress
|
||||||
|
bool _restoreInProgress { false };
|
||||||
|
std::vector<AssetUtils::AssetHash> _assetsLeftToUpload;
|
||||||
|
std::vector<std::pair<AssetUtils::AssetPath, AssetUtils::AssetHash>> _mappingsLeftToSet;
|
||||||
|
AssetUtils::AssetPathList _mappingsLeftToDelete;
|
||||||
|
int _mappingRequestsInFlight { 0 };
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* hifi_BackupSupervisor_h */
|
|
@ -26,6 +26,7 @@
|
||||||
#include <QCommandLineParser>
|
#include <QCommandLineParser>
|
||||||
|
|
||||||
#include <AccountManager.h>
|
#include <AccountManager.h>
|
||||||
|
#include <AssetClient.h>
|
||||||
#include <BuildInfo.h>
|
#include <BuildInfo.h>
|
||||||
#include <DependencyManager.h>
|
#include <DependencyManager.h>
|
||||||
#include <HifiConfigVariantMap.h>
|
#include <HifiConfigVariantMap.h>
|
||||||
|
@ -343,6 +344,12 @@ void DomainServer::parseCommandLine() {
|
||||||
|
|
||||||
DomainServer::~DomainServer() {
|
DomainServer::~DomainServer() {
|
||||||
qInfo() << "Domain Server is shutting down.";
|
qInfo() << "Domain Server is shutting down.";
|
||||||
|
|
||||||
|
// cleanup the AssetClient thread
|
||||||
|
DependencyManager::destroy<AssetClient>();
|
||||||
|
_assetClientThread.quit();
|
||||||
|
_assetClientThread.wait();
|
||||||
|
|
||||||
// destroy the LimitedNodeList before the DomainServer QCoreApplication is down
|
// destroy the LimitedNodeList before the DomainServer QCoreApplication is down
|
||||||
DependencyManager::destroy<LimitedNodeList>();
|
DependencyManager::destroy<LimitedNodeList>();
|
||||||
}
|
}
|
||||||
|
@ -684,11 +691,17 @@ void DomainServer::setupNodeListAndAssignments() {
|
||||||
packetReceiver.registerListener(PacketType::ICEServerHeartbeatDenied, this, "processICEServerHeartbeatDenialPacket");
|
packetReceiver.registerListener(PacketType::ICEServerHeartbeatDenied, this, "processICEServerHeartbeatDenialPacket");
|
||||||
packetReceiver.registerListener(PacketType::ICEServerHeartbeatACK, this, "processICEServerHeartbeatACK");
|
packetReceiver.registerListener(PacketType::ICEServerHeartbeatACK, this, "processICEServerHeartbeatACK");
|
||||||
|
|
||||||
// add whatever static assignments that have been parsed to the queue
|
|
||||||
addStaticAssignmentsToQueue();
|
|
||||||
|
|
||||||
// set a custom packetVersionMatch as the verify packet operator for the udt::Socket
|
// set a custom packetVersionMatch as the verify packet operator for the udt::Socket
|
||||||
nodeList->setPacketFilterOperator(&DomainServer::isPacketVerified);
|
nodeList->setPacketFilterOperator(&DomainServer::isPacketVerified);
|
||||||
|
|
||||||
|
_assetClientThread.setObjectName("AssetClient Thread");
|
||||||
|
auto assetClient = DependencyManager::set<AssetClient>();
|
||||||
|
assetClient->moveToThread(&_assetClientThread);
|
||||||
|
QObject::connect(&_assetClientThread, &QThread::started, assetClient.data(), &AssetClient::init);
|
||||||
|
_assetClientThread.start();
|
||||||
|
|
||||||
|
// add whatever static assignments that have been parsed to the queue
|
||||||
|
addStaticAssignmentsToQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DomainServer::resetAccountManagerAccessToken() {
|
bool DomainServer::resetAccountManagerAccessToken() {
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <QtCore/QQueue>
|
#include <QtCore/QQueue>
|
||||||
#include <QtCore/QSharedPointer>
|
#include <QtCore/QSharedPointer>
|
||||||
#include <QtCore/QStringList>
|
#include <QtCore/QStringList>
|
||||||
|
#include <QtCore/QThread>
|
||||||
#include <QtCore/QUrl>
|
#include <QtCore/QUrl>
|
||||||
#include <QAbstractNativeEventFilter>
|
#include <QAbstractNativeEventFilter>
|
||||||
|
|
||||||
|
@ -25,6 +26,7 @@
|
||||||
#include <HTTPSConnection.h>
|
#include <HTTPSConnection.h>
|
||||||
#include <LimitedNodeList.h>
|
#include <LimitedNodeList.h>
|
||||||
|
|
||||||
|
#include "BackupSupervisor.h"
|
||||||
#include "DomainGatekeeper.h"
|
#include "DomainGatekeeper.h"
|
||||||
#include "DomainMetadata.h"
|
#include "DomainMetadata.h"
|
||||||
#include "DomainServerSettingsManager.h"
|
#include "DomainServerSettingsManager.h"
|
||||||
|
@ -251,6 +253,8 @@ private:
|
||||||
bool _sendICEServerAddressToMetaverseAPIRedo { false };
|
bool _sendICEServerAddressToMetaverseAPIRedo { false };
|
||||||
|
|
||||||
QHash<QUuid, QPointer<HTTPSConnection>> _pendingOAuthConnections;
|
QHash<QUuid, QPointer<HTTPSConnection>> _pendingOAuthConnections;
|
||||||
|
|
||||||
|
QThread _assetClientThread;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -88,8 +88,8 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
{ "from": "Keyboard.W", "to": "Actions.LONGITUDINAL_FORWARD" },
|
{ "from": "Keyboard.W", "when": "!Keyboard.Control", "to": "Actions.LONGITUDINAL_FORWARD" },
|
||||||
{ "from": "Keyboard.S", "to": "Actions.LONGITUDINAL_BACKWARD" },
|
{ "from": "Keyboard.S", "when": "!Keyboard.Control", "to": "Actions.LONGITUDINAL_BACKWARD" },
|
||||||
{ "from": "Keyboard.C", "to": "Actions.VERTICAL_DOWN" },
|
{ "from": "Keyboard.C", "to": "Actions.VERTICAL_DOWN" },
|
||||||
{ "from": "Keyboard.E", "to": "Actions.VERTICAL_UP" },
|
{ "from": "Keyboard.E", "to": "Actions.VERTICAL_UP" },
|
||||||
{ "from": "Keyboard.Left", "when": "Keyboard.RightMouseButton", "to": "Actions.LATERAL_LEFT" },
|
{ "from": "Keyboard.Left", "when": "Keyboard.RightMouseButton", "to": "Actions.LATERAL_LEFT" },
|
||||||
|
|
|
@ -202,8 +202,4 @@ TreeView {
|
||||||
}
|
}
|
||||||
|
|
||||||
onDoubleClicked: isExpanded(index) ? collapse(index) : expand(index)
|
onDoubleClicked: isExpanded(index) ? collapse(index) : expand(index)
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
selectionModel.setCurrentIndex(index, ItemSelectionModel.ClearAndSelect);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ Windows.ScrollingWindow {
|
||||||
property var assetProxyModel: Assets.proxyModel;
|
property var assetProxyModel: Assets.proxyModel;
|
||||||
property var assetMappingsModel: Assets.mappingModel;
|
property var assetMappingsModel: Assets.mappingModel;
|
||||||
property var currentDirectory;
|
property var currentDirectory;
|
||||||
property var selectedItems: treeView.selection.selectedIndexes.length;
|
property var selectedItemCount: treeView.selection.selectedIndexes.length;
|
||||||
|
|
||||||
Settings {
|
Settings {
|
||||||
category: "Overlay.AssetServer"
|
category: "Overlay.AssetServer"
|
||||||
|
@ -75,17 +75,17 @@ Windows.ScrollingWindow {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function doDeleteFile(path) {
|
function doDeleteFile(paths) {
|
||||||
console.log("Deleting " + path);
|
console.log("Deleting " + paths);
|
||||||
|
|
||||||
Assets.deleteMappings(path, function(err) {
|
Assets.deleteMappings(paths, function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log("Asset browser - error deleting path: ", path, err);
|
console.log("Asset browser - error deleting paths: ", paths, err);
|
||||||
|
|
||||||
box = errorMessageBox("There was an error deleting:\n" + path + "\n" + err);
|
box = errorMessageBox("There was an error deleting:\n" + paths + "\n" + err);
|
||||||
box.selected.connect(reload);
|
box.selected.connect(reload);
|
||||||
} else {
|
} else {
|
||||||
console.log("Asset browser - finished deleting path: ", path);
|
console.log("Asset browser - finished deleting paths: ", paths);
|
||||||
reload();
|
reload();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -145,7 +145,7 @@ Windows.ScrollingWindow {
|
||||||
function canAddToWorld(path) {
|
function canAddToWorld(path) {
|
||||||
var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i, /\.jpg\b/i, /\.png\b/i];
|
var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i, /\.jpg\b/i, /\.png\b/i];
|
||||||
|
|
||||||
if (selectedItems > 1) {
|
if (selectedItemCount > 1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,7 +155,7 @@ Windows.ScrollingWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
function canRename() {
|
function canRename() {
|
||||||
if (treeView.selection.hasSelection && selectedItems == 1) {
|
if (treeView.selection.hasSelection && selectedItemCount == 1) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
@ -344,29 +344,28 @@ Windows.ScrollingWindow {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function deleteFile(index) {
|
function deleteFile(index) {
|
||||||
var path = [];
|
var paths = [];
|
||||||
|
|
||||||
if (!index) {
|
if (!index) {
|
||||||
for (var i = 0; i < selectedItems; i++) {
|
for (var i = 0; i < selectedItemCount; ++i) {
|
||||||
treeView.selection.setCurrentIndex(treeView.selection.selectedIndexes[i], 0x100);
|
index = treeView.selection.selectedIndexes[i];
|
||||||
index = treeView.selection.currentIndex;
|
paths[i] = assetProxyModel.data(index, 0x100);
|
||||||
path[i] = assetProxyModel.data(index, 0x100);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!path) {
|
if (!paths) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var modalMessage = "";
|
var modalMessage = "";
|
||||||
var items = selectedItems.toString();
|
var items = selectedItemCount.toString();
|
||||||
var isFolder = assetProxyModel.data(treeView.selection.currentIndex, 0x101);
|
var isFolder = assetProxyModel.data(treeView.selection.currentIndex, 0x101);
|
||||||
var typeString = isFolder ? 'folder' : 'file';
|
var typeString = isFolder ? 'folder' : 'file';
|
||||||
|
|
||||||
if (selectedItems > 1) {
|
if (selectedItemCount > 1) {
|
||||||
modalMessage = "You are about to delete " + items + " items \nDo you want to continue?";
|
modalMessage = "You are about to delete " + items + " items \nDo you want to continue?";
|
||||||
} else {
|
} else {
|
||||||
modalMessage = "You are about to delete the following " + typeString + ":\n" + path + "\nDo you want to continue?";
|
modalMessage = "You are about to delete the following " + typeString + ":\n" + paths + "\nDo you want to continue?";
|
||||||
}
|
}
|
||||||
|
|
||||||
var object = desktop.messageBox({
|
var object = desktop.messageBox({
|
||||||
|
@ -378,7 +377,7 @@ Windows.ScrollingWindow {
|
||||||
});
|
});
|
||||||
object.selected.connect(function(button) {
|
object.selected.connect(function(button) {
|
||||||
if (button === OriginalDialogs.StandardButton.Yes) {
|
if (button === OriginalDialogs.StandardButton.Yes) {
|
||||||
doDeleteFile(path);
|
doDeleteFile(paths);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -705,7 +704,7 @@ Windows.ScrollingWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}// End_OF( itemLoader )
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: treeLabelToolTip
|
id: treeLabelToolTip
|
||||||
|
@ -742,18 +741,25 @@ Windows.ScrollingWindow {
|
||||||
showTimer.stop();
|
showTimer.stop();
|
||||||
treeLabelToolTip.visible = false;
|
treeLabelToolTip.visible = false;
|
||||||
}
|
}
|
||||||
}
|
}// End_OF( treeLabelToolTip )
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
propagateComposedEvents: true
|
propagateComposedEvents: true
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
acceptedButtons: Qt.RightButton
|
acceptedButtons: Qt.RightButton
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (!HMD.active) { // Popup only displays properly on desktop
|
if (treeView.selection.hasSelection && !HMD.active) { // Popup only displays properly on desktop
|
||||||
var index = treeView.indexAt(mouse.x, mouse.y);
|
// Only display the popup if the click triggered within
|
||||||
treeView.selection.setCurrentIndex(index, 0x0002);
|
// the selection.
|
||||||
contextMenu.currentIndex = index;
|
var clickedIndex = treeView.indexAt(mouse.x, mouse.y);
|
||||||
|
var displayContextMenu = false;
|
||||||
|
for ( var i = 0; i < selectedItemCount; ++i) {
|
||||||
|
var currentSelectedIndex = treeView.selection.selectedIndexes[i];
|
||||||
|
if (clickedIndex === currentSelectedIndex) {
|
||||||
contextMenu.popup();
|
contextMenu.popup();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -762,30 +768,32 @@ Windows.ScrollingWindow {
|
||||||
id: contextMenu
|
id: contextMenu
|
||||||
title: "Edit"
|
title: "Edit"
|
||||||
property var url: ""
|
property var url: ""
|
||||||
property var currentIndex: null
|
|
||||||
|
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: "Copy URL"
|
text: "Copy URL"
|
||||||
|
enabled: (selectedItemCount == 1)
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
copyURLToClipboard(contextMenu.currentIndex);
|
copyURLToClipboard(treeView.selection.currentIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: "Rename"
|
text: "Rename"
|
||||||
|
enabled: (selectedItemCount == 1)
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
renameFile(contextMenu.currentIndex);
|
renameFile(treeView.selection.currentIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: "Delete"
|
text: "Delete"
|
||||||
|
enabled: (selectedItemCount > 0)
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
deleteFile(contextMenu.currentIndex);
|
deleteFile();
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}// End_OF( contextMenu )
|
||||||
|
}// End_OF( treeView )
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: infoRow
|
id: infoRow
|
||||||
|
@ -798,8 +806,8 @@ Windows.ScrollingWindow {
|
||||||
|
|
||||||
function makeText() {
|
function makeText() {
|
||||||
var numPendingBakes = assetMappingsModel.numPendingBakes;
|
var numPendingBakes = assetMappingsModel.numPendingBakes;
|
||||||
if (selectedItems > 1 || numPendingBakes === 0) {
|
if (selectedItemCount > 1 || numPendingBakes === 0) {
|
||||||
return selectedItems + " items selected";
|
return selectedItemCount + " items selected";
|
||||||
} else {
|
} else {
|
||||||
return numPendingBakes + " bakes pending"
|
return numPendingBakes + " bakes pending"
|
||||||
}
|
}
|
||||||
|
@ -896,7 +904,7 @@ Windows.ScrollingWindow {
|
||||||
"Baking compresses and optimizes files for faster network transfer and display. We recommend you bake your content to reduce initial load times for your visitors.");
|
"Baking compresses and optimizes files for faster network transfer and display. We recommend you bake your content to reduce initial load times for your visitors.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}// End_OF( infoRow )
|
||||||
|
|
||||||
HifiControls.ContentSection {
|
HifiControls.ContentSection {
|
||||||
id: uploadSection
|
id: uploadSection
|
||||||
|
@ -956,7 +964,7 @@ Windows.ScrollingWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}// End_OF( uploadSection )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -391,7 +391,7 @@ Item {
|
||||||
anchors.topMargin: 15;
|
anchors.topMargin: 15;
|
||||||
width: 118;
|
width: 118;
|
||||||
height: paintedHeight;
|
height: paintedHeight;
|
||||||
wrapMode: Text.WordWrap;
|
wrapMode: Text.Wrap;
|
||||||
// Alignment
|
// Alignment
|
||||||
horizontalAlignment: Text.AlignRight;
|
horizontalAlignment: Text.AlignRight;
|
||||||
}
|
}
|
||||||
|
@ -408,7 +408,7 @@ Item {
|
||||||
height: paintedHeight;
|
height: paintedHeight;
|
||||||
color: model.status === "invalidated" ? hifi.colors.redAccent : hifi.colors.baseGrayHighlight;
|
color: model.status === "invalidated" ? hifi.colors.redAccent : hifi.colors.baseGrayHighlight;
|
||||||
linkColor: hifi.colors.blueAccent;
|
linkColor: hifi.colors.blueAccent;
|
||||||
wrapMode: Text.WordWrap;
|
wrapMode: Text.Wrap;
|
||||||
font.strikeout: model.status === "invalidated";
|
font.strikeout: model.status === "invalidated";
|
||||||
|
|
||||||
onLinkActivated: {
|
onLinkActivated: {
|
||||||
|
|
|
@ -39,7 +39,7 @@ Rectangle {
|
||||||
property var assetProxyModel: Assets.proxyModel;
|
property var assetProxyModel: Assets.proxyModel;
|
||||||
property var assetMappingsModel: Assets.mappingModel;
|
property var assetMappingsModel: Assets.mappingModel;
|
||||||
property var currentDirectory;
|
property var currentDirectory;
|
||||||
property var selectedItems: treeView.selection.selectedIndexes.length;
|
property var selectedItemCount: treeView.selection.selectedIndexes.length;
|
||||||
|
|
||||||
Settings {
|
Settings {
|
||||||
category: "Overlay.AssetServer"
|
category: "Overlay.AssetServer"
|
||||||
|
@ -76,17 +76,17 @@ Rectangle {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function doDeleteFile(path) {
|
function doDeleteFile(paths) {
|
||||||
console.log("Deleting " + path);
|
console.log("Deleting " + paths);
|
||||||
|
|
||||||
Assets.deleteMappings(path, function(err) {
|
Assets.deleteMappings(paths, function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log("Asset browser - error deleting path: ", path, err);
|
console.log("Asset browser - error deleting paths: ", paths, err);
|
||||||
|
|
||||||
box = errorMessageBox("There was an error deleting:\n" + path + "\n" + err);
|
box = errorMessageBox("There was an error deleting:\n" + paths + "\n" + err);
|
||||||
box.selected.connect(reload);
|
box.selected.connect(reload);
|
||||||
} else {
|
} else {
|
||||||
console.log("Asset browser - finished deleting path: ", path);
|
console.log("Asset browser - finished deleting paths: ", paths);
|
||||||
reload();
|
reload();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -146,7 +146,7 @@ Rectangle {
|
||||||
function canAddToWorld(path) {
|
function canAddToWorld(path) {
|
||||||
var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i];
|
var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i];
|
||||||
|
|
||||||
if (selectedItems > 1) {
|
if (selectedItemCount > 1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,7 +156,7 @@ Rectangle {
|
||||||
}
|
}
|
||||||
|
|
||||||
function canRename() {
|
function canRename() {
|
||||||
if (treeView.selection.hasSelection && selectedItems == 1) {
|
if (treeView.selection.hasSelection && selectedItemCount == 1) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
@ -345,29 +345,28 @@ Rectangle {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function deleteFile(index) {
|
function deleteFile(index) {
|
||||||
var path = [];
|
var paths = [];
|
||||||
|
|
||||||
if (!index) {
|
if (!index) {
|
||||||
for (var i = 0; i < selectedItems; i++) {
|
for (var i = 0; i < selectedItemCount; ++i) {
|
||||||
treeView.selection.setCurrentIndex(treeView.selection.selectedIndexes[i], 0x100);
|
index = treeView.selection.selectedIndexes[i];
|
||||||
index = treeView.selection.currentIndex;
|
paths[i] = assetProxyModel.data(index, 0x100);
|
||||||
path[i] = assetProxyModel.data(index, 0x100);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!path) {
|
if (!paths) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var modalMessage = "";
|
var modalMessage = "";
|
||||||
var items = selectedItems.toString();
|
var items = selectedItemCount.toString();
|
||||||
var isFolder = assetProxyModel.data(treeView.selection.currentIndex, 0x101);
|
var isFolder = assetProxyModel.data(treeView.selection.currentIndex, 0x101);
|
||||||
var typeString = isFolder ? 'folder' : 'file';
|
var typeString = isFolder ? 'folder' : 'file';
|
||||||
|
|
||||||
if (selectedItems > 1) {
|
if (selectedItemCount > 1) {
|
||||||
modalMessage = "You are about to delete " + items + " items \nDo you want to continue?";
|
modalMessage = "You are about to delete " + items + " items \nDo you want to continue?";
|
||||||
} else {
|
} else {
|
||||||
modalMessage = "You are about to delete the following " + typeString + ":\n" + path + "\nDo you want to continue?";
|
modalMessage = "You are about to delete the following " + typeString + ":\n" + paths + "\nDo you want to continue?";
|
||||||
}
|
}
|
||||||
|
|
||||||
var object = tabletRoot.messageBox({
|
var object = tabletRoot.messageBox({
|
||||||
|
@ -379,7 +378,7 @@ Rectangle {
|
||||||
});
|
});
|
||||||
object.selected.connect(function(button) {
|
object.selected.connect(function(button) {
|
||||||
if (button === OriginalDialogs.StandardButton.Yes) {
|
if (button === OriginalDialogs.StandardButton.Yes) {
|
||||||
doDeleteFile(path);
|
doDeleteFile(paths);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -704,7 +703,7 @@ Rectangle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}// End_OF( itemLoader )
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: treeLabelToolTip
|
id: treeLabelToolTip
|
||||||
|
@ -741,18 +740,25 @@ Rectangle {
|
||||||
showTimer.stop();
|
showTimer.stop();
|
||||||
treeLabelToolTip.visible = false;
|
treeLabelToolTip.visible = false;
|
||||||
}
|
}
|
||||||
}
|
}// End_OF( treeLabelToolTip )
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
propagateComposedEvents: true
|
propagateComposedEvents: true
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
acceptedButtons: Qt.RightButton
|
acceptedButtons: Qt.RightButton
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (!HMD.active) { // Popup only displays properly on desktop
|
if (treeView.selection.hasSelection && !HMD.active) { // Popup only displays properly on desktop
|
||||||
var index = treeView.indexAt(mouse.x, mouse.y);
|
// Only display the popup if the click triggered within
|
||||||
treeView.selection.setCurrentIndex(index, 0x0002);
|
// the selection.
|
||||||
contextMenu.currentIndex = index;
|
var clickedIndex = treeView.indexAt(mouse.x, mouse.y);
|
||||||
|
var displayContextMenu = false;
|
||||||
|
for ( var i = 0; i < selectedItemCount; ++i) {
|
||||||
|
var currentSelectedIndex = treeView.selection.selectedIndexes[i];
|
||||||
|
if (clickedIndex === currentSelectedIndex) {
|
||||||
contextMenu.popup();
|
contextMenu.popup();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -761,30 +767,32 @@ Rectangle {
|
||||||
id: contextMenu
|
id: contextMenu
|
||||||
title: "Edit"
|
title: "Edit"
|
||||||
property var url: ""
|
property var url: ""
|
||||||
property var currentIndex: null
|
|
||||||
|
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: "Copy URL"
|
text: "Copy URL"
|
||||||
|
enabled: (selectedItemCount == 1)
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
copyURLToClipboard(contextMenu.currentIndex);
|
copyURLToClipboard(treeView.selection.currentIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: "Rename"
|
text: "Rename"
|
||||||
|
enabled: (selectedItemCount == 1)
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
renameFile(contextMenu.currentIndex);
|
renameFile(treeView.selection.currentIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: "Delete"
|
text: "Delete"
|
||||||
|
enabled: (selectedItemCount > 0)
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
deleteFile(contextMenu.currentIndex);
|
deleteFile();
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}// End_OF( contextMenu )
|
||||||
|
}// End_OF( treeView )
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: infoRow
|
id: infoRow
|
||||||
|
@ -797,8 +805,8 @@ Rectangle {
|
||||||
|
|
||||||
function makeText() {
|
function makeText() {
|
||||||
var numPendingBakes = assetMappingsModel.numPendingBakes;
|
var numPendingBakes = assetMappingsModel.numPendingBakes;
|
||||||
if (selectedItems > 1 || numPendingBakes === 0) {
|
if (selectedItemCount > 1 || numPendingBakes === 0) {
|
||||||
return selectedItems + " items selected";
|
return selectedItemCount + " items selected";
|
||||||
} else {
|
} else {
|
||||||
return numPendingBakes + " bakes pending"
|
return numPendingBakes + " bakes pending"
|
||||||
}
|
}
|
||||||
|
@ -895,7 +903,7 @@ Rectangle {
|
||||||
"Baking compresses and optimizes files for faster network transfer and display. We recommend you bake your content to reduce initial load times for your visitors.");
|
"Baking compresses and optimizes files for faster network transfer and display. We recommend you bake your content to reduce initial load times for your visitors.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}// End_OF( infoRow )
|
||||||
|
|
||||||
HifiControls.TabletContentSection {
|
HifiControls.TabletContentSection {
|
||||||
id: uploadSection
|
id: uploadSection
|
||||||
|
@ -972,7 +980,7 @@ Rectangle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}// End_OF( uploadSection )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Binary file not shown.
|
@ -2374,6 +2374,7 @@ void Application::initializeGL() {
|
||||||
qFatal("Unable to make offscreen context current");
|
qFatal("Unable to make offscreen context current");
|
||||||
}
|
}
|
||||||
_offscreenContext->doneCurrent();
|
_offscreenContext->doneCurrent();
|
||||||
|
_offscreenContext->setThreadContext();
|
||||||
_renderEventHandler = new RenderEventHandler(_glWidget->qglContext());
|
_renderEventHandler = new RenderEventHandler(_glWidget->qglContext());
|
||||||
|
|
||||||
// The UI can't be created until the primary OpenGL
|
// The UI can't be created until the primary OpenGL
|
||||||
|
|
|
@ -1111,7 +1111,7 @@ void MyAvatar::setEnableDebugDrawIKChains(bool isEnabled) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::setEnableMeshVisible(bool isEnabled) {
|
void MyAvatar::setEnableMeshVisible(bool isEnabled) {
|
||||||
_skeletonModel->setVisibleInScene(isEnabled, qApp->getMain3DScene(), render::ItemKey::TAG_BITS_NONE);
|
_skeletonModel->setVisibleInScene(isEnabled, qApp->getMain3DScene(), render::ItemKey::TAG_BITS_NONE, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::setEnableInverseKinematics(bool isEnabled) {
|
void MyAvatar::setEnableInverseKinematics(bool isEnabled) {
|
||||||
|
@ -1460,10 +1460,23 @@ void MyAvatar::clearJointsData() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
||||||
|
_skeletonModelChangeCount++;
|
||||||
|
int skeletonModelChangeCount = _skeletonModelChangeCount;
|
||||||
Avatar::setSkeletonModelURL(skeletonModelURL);
|
Avatar::setSkeletonModelURL(skeletonModelURL);
|
||||||
_skeletonModel->setVisibleInScene(true, qApp->getMain3DScene(), render::ItemKey::TAG_BITS_NONE);
|
_skeletonModel->setVisibleInScene(true, qApp->getMain3DScene(), render::ItemKey::TAG_BITS_NONE, true);
|
||||||
_headBoneSet.clear();
|
_headBoneSet.clear();
|
||||||
_cauterizationNeedsUpdate = true;
|
_cauterizationNeedsUpdate = true;
|
||||||
|
|
||||||
|
std::shared_ptr<QMetaObject::Connection> skeletonConnection = std::make_shared<QMetaObject::Connection>();
|
||||||
|
*skeletonConnection = QObject::connect(_skeletonModel.get(), &SkeletonModel::skeletonLoaded, [this, skeletonModelChangeCount, skeletonConnection]() {
|
||||||
|
if (skeletonModelChangeCount == _skeletonModelChangeCount) {
|
||||||
|
initHeadBones();
|
||||||
|
_skeletonModel->setCauterizeBoneSet(_headBoneSet);
|
||||||
|
_fstAnimGraphOverrideUrl = _skeletonModel->getGeometry()->getAnimGraphOverrideUrl();
|
||||||
|
initAnimGraph();
|
||||||
|
}
|
||||||
|
QObject::disconnect(*skeletonConnection);
|
||||||
|
});
|
||||||
saveAvatarUrl();
|
saveAvatarUrl();
|
||||||
emit skeletonChanged();
|
emit skeletonChanged();
|
||||||
|
|
||||||
|
@ -1808,7 +1821,7 @@ void MyAvatar::attach(const QString& modelURL, const QString& jointName,
|
||||||
|
|
||||||
void MyAvatar::setVisibleInSceneIfReady(Model* model, const render::ScenePointer& scene, bool visible) {
|
void MyAvatar::setVisibleInSceneIfReady(Model* model, const render::ScenePointer& scene, bool visible) {
|
||||||
if (model->isActive() && model->isRenderable()) {
|
if (model->isActive() && model->isRenderable()) {
|
||||||
model->setVisibleInScene(visible, scene, render::ItemKey::TAG_BITS_NONE);
|
model->setVisibleInScene(visible, scene, render::ItemKey::TAG_BITS_NONE, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1872,9 +1885,7 @@ void MyAvatar::setAnimGraphUrl(const QUrl& url) {
|
||||||
|
|
||||||
_currentAnimGraphUrl.set(url);
|
_currentAnimGraphUrl.set(url);
|
||||||
_skeletonModel->getRig().initAnimGraph(url);
|
_skeletonModel->getRig().initAnimGraph(url);
|
||||||
|
connect(&(_skeletonModel->getRig()), SIGNAL(onLoadComplete()), this, SLOT(animGraphLoaded()));
|
||||||
_bodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation..
|
|
||||||
updateSensorToWorldMatrix(); // Uses updated position/orientation and _bodySensorMatrix changes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::initAnimGraph() {
|
void MyAvatar::initAnimGraph() {
|
||||||
|
@ -1889,28 +1900,24 @@ void MyAvatar::initAnimGraph() {
|
||||||
|
|
||||||
_skeletonModel->getRig().initAnimGraph(graphUrl);
|
_skeletonModel->getRig().initAnimGraph(graphUrl);
|
||||||
_currentAnimGraphUrl.set(graphUrl);
|
_currentAnimGraphUrl.set(graphUrl);
|
||||||
|
connect(&(_skeletonModel->getRig()), SIGNAL(onLoadComplete()), this, SLOT(animGraphLoaded()));
|
||||||
_bodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation..
|
|
||||||
updateSensorToWorldMatrix(); // Uses updated position/orientation and _bodySensorMatrix changes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::destroyAnimGraph() {
|
void MyAvatar::destroyAnimGraph() {
|
||||||
_skeletonModel->getRig().destroyAnimGraph();
|
_skeletonModel->getRig().destroyAnimGraph();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MyAvatar::animGraphLoaded() {
|
||||||
|
_bodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation..
|
||||||
|
updateSensorToWorldMatrix(); // Uses updated position/orientation and _bodySensorMatrix changes
|
||||||
|
_isAnimatingScale = true;
|
||||||
|
_cauterizationNeedsUpdate = true;
|
||||||
|
disconnect(&(_skeletonModel->getRig()), SIGNAL(onLoadComplete()), this, SLOT(animGraphLoaded()));
|
||||||
|
}
|
||||||
|
|
||||||
void MyAvatar::postUpdate(float deltaTime, const render::ScenePointer& scene) {
|
void MyAvatar::postUpdate(float deltaTime, const render::ScenePointer& scene) {
|
||||||
|
|
||||||
Avatar::postUpdate(deltaTime, scene);
|
Avatar::postUpdate(deltaTime, scene);
|
||||||
|
|
||||||
if (_skeletonModel->isLoaded() && !_skeletonModel->getRig().getAnimNode()) {
|
|
||||||
initHeadBones();
|
|
||||||
_skeletonModel->setCauterizeBoneSet(_headBoneSet);
|
|
||||||
_fstAnimGraphOverrideUrl = _skeletonModel->getGeometry()->getAnimGraphOverrideUrl();
|
|
||||||
initAnimGraph();
|
|
||||||
_isAnimatingScale = true;
|
|
||||||
_cauterizationNeedsUpdate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_enableDebugDrawDefaultPose || _enableDebugDrawAnimPose) {
|
if (_enableDebugDrawDefaultPose || _enableDebugDrawAnimPose) {
|
||||||
|
|
||||||
auto animSkeleton = _skeletonModel->getRig().getAnimSkeleton();
|
auto animSkeleton = _skeletonModel->getRig().getAnimSkeleton();
|
||||||
|
@ -2008,7 +2015,7 @@ void MyAvatar::preDisplaySide(RenderArgs* renderArgs) {
|
||||||
_attachmentData[i].jointName.compare("HeadTop_End", Qt::CaseInsensitive) == 0 ||
|
_attachmentData[i].jointName.compare("HeadTop_End", Qt::CaseInsensitive) == 0 ||
|
||||||
_attachmentData[i].jointName.compare("Face", Qt::CaseInsensitive) == 0) {
|
_attachmentData[i].jointName.compare("Face", Qt::CaseInsensitive) == 0) {
|
||||||
_attachmentModels[i]->setVisibleInScene(shouldDrawHead, qApp->getMain3DScene(),
|
_attachmentModels[i]->setVisibleInScene(shouldDrawHead, qApp->getMain3DScene(),
|
||||||
render::ItemKey::TAG_BITS_NONE);
|
render::ItemKey::TAG_BITS_NONE, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -569,6 +569,7 @@ public slots:
|
||||||
void increaseSize();
|
void increaseSize();
|
||||||
void decreaseSize();
|
void decreaseSize();
|
||||||
void resetSize();
|
void resetSize();
|
||||||
|
void animGraphLoaded();
|
||||||
|
|
||||||
void setGravity(float gravity);
|
void setGravity(float gravity);
|
||||||
float getGravity();
|
float getGravity();
|
||||||
|
@ -654,6 +655,7 @@ private:
|
||||||
bool isMyAvatar() const override { return true; }
|
bool isMyAvatar() const override { return true; }
|
||||||
virtual int parseDataFromBuffer(const QByteArray& buffer) override;
|
virtual int parseDataFromBuffer(const QByteArray& buffer) override;
|
||||||
virtual glm::vec3 getSkeletonPosition() const override;
|
virtual glm::vec3 getSkeletonPosition() const override;
|
||||||
|
int _skeletonModelChangeCount { 0 };
|
||||||
|
|
||||||
void saveAvatarScale();
|
void saveAvatarScale();
|
||||||
|
|
||||||
|
|
|
@ -425,10 +425,10 @@ void Circle3DOverlay::setProperties(const QVariantMap& properties) {
|
||||||
* <em>Write-only.</em>
|
* <em>Write-only.</em>
|
||||||
* @property {Color} outerColor - Sets the values of <code>outerStartColor</code> and <code>outerEndColor</code>.
|
* @property {Color} outerColor - Sets the values of <code>outerStartColor</code> and <code>outerEndColor</code>.
|
||||||
* <em>Write-only.</em>
|
* <em>Write-only.</em>
|
||||||
* @property {Color} innerStartcolor - The color at the inner start point of the overlay. <em>Write-only.</em>
|
* @property {Color} innerStartcolor - The color at the inner start point of the overlay.
|
||||||
* @property {Color} innerEndColor - The color at the inner end point of the overlay. <em>Write-only.</em>
|
* @property {Color} innerEndColor - The color at the inner end point of the overlay.
|
||||||
* @property {Color} outerStartColor - The color at the outer start point of the overlay. <em>Write-only.</em>
|
* @property {Color} outerStartColor - The color at the outer start point of the overlay.
|
||||||
* @property {Color} outerEndColor - The color at the outer end point of the overlay. <em>Write-only.</em>
|
* @property {Color} outerEndColor - The color at the outer end point of the overlay.
|
||||||
* @property {number} alpha=0.5 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>. Setting this value also sets
|
* @property {number} alpha=0.5 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>. Setting this value also sets
|
||||||
* the values of <code>innerStartAlpha</code>, <code>innerEndAlpha</code>, <code>outerStartAlpha</code>, and
|
* the values of <code>innerStartAlpha</code>, <code>innerEndAlpha</code>, <code>outerStartAlpha</code>, and
|
||||||
* <code>outerEndAlpha</code>. Synonym: <code>Alpha</code>; <em>write-only</em>.
|
* <code>outerEndAlpha</code>. Synonym: <code>Alpha</code>; <em>write-only</em>.
|
||||||
|
@ -440,10 +440,10 @@ void Circle3DOverlay::setProperties(const QVariantMap& properties) {
|
||||||
* <em>Write-only.</em>
|
* <em>Write-only.</em>
|
||||||
* @property {number} outerAlpha - Sets the values of <code>outerStartAlpha</code> and <code>outerEndAlpha</code>.
|
* @property {number} outerAlpha - Sets the values of <code>outerStartAlpha</code> and <code>outerEndAlpha</code>.
|
||||||
* <em>Write-only.</em>
|
* <em>Write-only.</em>
|
||||||
* @property {number} innerStartAlpha=0 - The alpha at the inner start point of the overlay. <em>Write-only.</em>
|
* @property {number} innerStartAlpha=0 - The alpha at the inner start point of the overlay.
|
||||||
* @property {number} innerEndAlpha=0 - The alpha at the inner end point of the overlay. <em>Write-only.</em>
|
* @property {number} innerEndAlpha=0 - The alpha at the inner end point of the overlay.
|
||||||
* @property {number} outerStartAlpha=0 - The alpha at the outer start point of the overlay. <em>Write-only.</em>
|
* @property {number} outerStartAlpha=0 - The alpha at the outer start point of the overlay.
|
||||||
* @property {number} outerEndAlpha=0 - The alpha at the outer end point of the overlay. <em>Write-only.</em>
|
* @property {number} outerEndAlpha=0 - The alpha at the outer end point of the overlay.
|
||||||
|
|
||||||
* @property {boolean} hasTickMarks=false - If <code>true</code>, tick marks are drawn.
|
* @property {boolean} hasTickMarks=false - If <code>true</code>, tick marks are drawn.
|
||||||
* @property {number} majorTickMarksAngle=0 - The angle between major tick marks, in degrees.
|
* @property {number} majorTickMarksAngle=0 - The angle between major tick marks, in degrees.
|
||||||
|
|
|
@ -88,7 +88,7 @@ void ModelOverlay::update(float deltatime) {
|
||||||
if (_visibleDirty) {
|
if (_visibleDirty) {
|
||||||
_visibleDirty = false;
|
_visibleDirty = false;
|
||||||
// don't show overlays in mirrors
|
// don't show overlays in mirrors
|
||||||
_model->setVisibleInScene(getVisible(), scene, render::ItemKey::TAG_BITS_0);
|
_model->setVisibleInScene(getVisible(), scene, render::ItemKey::TAG_BITS_0, false);
|
||||||
}
|
}
|
||||||
if (_drawInFrontDirty) {
|
if (_drawInFrontDirty) {
|
||||||
_drawInFrontDirty = false;
|
_drawInFrontDirty = false;
|
||||||
|
|
|
@ -38,6 +38,8 @@ static AnimNode::Pointer loadManipulatorNode(const QJsonObject& jsonObj, const Q
|
||||||
static AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
static AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||||
static AnimNode::Pointer loadDefaultPoseNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
static AnimNode::Pointer loadDefaultPoseNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||||
|
|
||||||
|
static const float ANIM_GRAPH_LOAD_PRIORITY = 10.0f;
|
||||||
|
|
||||||
// called after children have been loaded
|
// called after children have been loaded
|
||||||
// returns node on success, nullptr on failure.
|
// returns node on success, nullptr on failure.
|
||||||
static bool processDoNothing(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; }
|
static bool processDoNothing(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; }
|
||||||
|
@ -653,6 +655,7 @@ AnimNodeLoader::AnimNodeLoader(const QUrl& url) :
|
||||||
{
|
{
|
||||||
_resource = QSharedPointer<Resource>::create(url);
|
_resource = QSharedPointer<Resource>::create(url);
|
||||||
_resource->setSelf(_resource);
|
_resource->setSelf(_resource);
|
||||||
|
_resource->setLoadPriority(this, ANIM_GRAPH_LOAD_PRIORITY);
|
||||||
connect(_resource.data(), &Resource::loaded, this, &AnimNodeLoader::onRequestDone);
|
connect(_resource.data(), &Resource::loaded, this, &AnimNodeLoader::onRequestDone);
|
||||||
connect(_resource.data(), &Resource::failed, this, &AnimNodeLoader::onRequestError);
|
connect(_resource.data(), &Resource::failed, this, &AnimNodeLoader::onRequestError);
|
||||||
_resource->ensureLoading();
|
_resource->ensureLoading();
|
||||||
|
|
|
@ -1585,14 +1585,13 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rig::initAnimGraph(const QUrl& url) {
|
void Rig::initAnimGraph(const QUrl& url) {
|
||||||
if (_animGraphURL != url || (!_animNode && !_animLoading)) {
|
if (_animGraphURL != url || !_animNode) {
|
||||||
_animGraphURL = url;
|
_animGraphURL = url;
|
||||||
|
|
||||||
_animNode.reset();
|
_animNode.reset();
|
||||||
|
|
||||||
// load the anim graph
|
// load the anim graph
|
||||||
_animLoader.reset(new AnimNodeLoader(url));
|
_animLoader.reset(new AnimNodeLoader(url));
|
||||||
_animLoading = true;
|
|
||||||
std::weak_ptr<AnimSkeleton> weakSkeletonPtr = _animSkeleton;
|
std::weak_ptr<AnimSkeleton> weakSkeletonPtr = _animSkeleton;
|
||||||
connect(_animLoader.get(), &AnimNodeLoader::success, [this, weakSkeletonPtr](AnimNode::Pointer nodeIn) {
|
connect(_animLoader.get(), &AnimNodeLoader::success, [this, weakSkeletonPtr](AnimNode::Pointer nodeIn) {
|
||||||
_animNode = nodeIn;
|
_animNode = nodeIn;
|
||||||
|
@ -1617,7 +1616,6 @@ void Rig::initAnimGraph(const QUrl& url) {
|
||||||
auto roleState = roleAnimState.second;
|
auto roleState = roleAnimState.second;
|
||||||
overrideRoleAnimation(roleState.role, roleState.url, roleState.fps, roleState.loop, roleState.firstFrame, roleState.lastFrame);
|
overrideRoleAnimation(roleState.role, roleState.url, roleState.fps, roleState.loop, roleState.firstFrame, roleState.lastFrame);
|
||||||
}
|
}
|
||||||
_animLoading = false;
|
|
||||||
|
|
||||||
emit onLoadComplete();
|
emit onLoadComplete();
|
||||||
});
|
});
|
||||||
|
|
|
@ -283,7 +283,6 @@ protected:
|
||||||
std::shared_ptr<AnimNode> _animNode;
|
std::shared_ptr<AnimNode> _animNode;
|
||||||
std::shared_ptr<AnimSkeleton> _animSkeleton;
|
std::shared_ptr<AnimSkeleton> _animSkeleton;
|
||||||
std::unique_ptr<AnimNodeLoader> _animLoader;
|
std::unique_ptr<AnimNodeLoader> _animLoader;
|
||||||
bool _animLoading { false };
|
|
||||||
AnimVariantMap _animVars;
|
AnimVariantMap _animVars;
|
||||||
enum class RigRole {
|
enum class RigRole {
|
||||||
Idle = 0,
|
Idle = 0,
|
||||||
|
|
|
@ -50,7 +50,7 @@ const glm::vec3 HAND_TO_PALM_OFFSET(0.0f, 0.12f, 0.08f);
|
||||||
|
|
||||||
namespace render {
|
namespace render {
|
||||||
template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar) {
|
template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar) {
|
||||||
return ItemKey::Builder::opaqueShape().withTypeMeta().withTagBits(ItemKey::TAG_BITS_0 | ItemKey::TAG_BITS_1);
|
return ItemKey::Builder::opaqueShape().withTypeMeta().withTagBits(ItemKey::TAG_BITS_0 | ItemKey::TAG_BITS_1).withMetaCullGroup();
|
||||||
}
|
}
|
||||||
template <> const Item::Bound payloadGetBound(const AvatarSharedPointer& avatar) {
|
template <> const Item::Bound payloadGetBound(const AvatarSharedPointer& avatar) {
|
||||||
return static_pointer_cast<Avatar>(avatar)->getBounds();
|
return static_pointer_cast<Avatar>(avatar)->getBounds();
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include <gl/GLWidget.h>
|
#include <gl/GLWidget.h>
|
||||||
#include <gl/GLEscrow.h>
|
#include <gl/GLEscrow.h>
|
||||||
#include <gl/Context.h>
|
#include <gl/Context.h>
|
||||||
|
#include <gl/OffscreenGLCanvas.h>
|
||||||
|
|
||||||
#include <gpu/Texture.h>
|
#include <gpu/Texture.h>
|
||||||
#include <gpu/StandardShaderLib.h>
|
#include <gpu/StandardShaderLib.h>
|
||||||
|
@ -130,14 +131,14 @@ public:
|
||||||
CHECK_GL_ERROR();
|
CHECK_GL_ERROR();
|
||||||
_context->doneCurrent();
|
_context->doneCurrent();
|
||||||
while (!_shutdown) {
|
while (!_shutdown) {
|
||||||
if (_pendingMainThreadOperation) {
|
if (_pendingOtherThreadOperation) {
|
||||||
PROFILE_RANGE(render, "MainThreadOp")
|
PROFILE_RANGE(render, "MainThreadOp")
|
||||||
{
|
{
|
||||||
Lock lock(_mutex);
|
Lock lock(_mutex);
|
||||||
_context->doneCurrent();
|
_context->doneCurrent();
|
||||||
// Move the context to the main thread
|
// Move the context to the main thread
|
||||||
_context->moveToThread(qApp->thread());
|
_context->moveToThread(_targetOperationThread);
|
||||||
_pendingMainThreadOperation = false;
|
_pendingOtherThreadOperation = false;
|
||||||
// Release the main thread to do it's action
|
// Release the main thread to do it's action
|
||||||
_condition.notify_one();
|
_condition.notify_one();
|
||||||
}
|
}
|
||||||
|
@ -146,7 +147,7 @@ public:
|
||||||
{
|
{
|
||||||
// Main thread does it's thing while we wait on the lock to release
|
// Main thread does it's thing while we wait on the lock to release
|
||||||
Lock lock(_mutex);
|
Lock lock(_mutex);
|
||||||
_condition.wait(lock, [&] { return _finishedMainThreadOperation; });
|
_condition.wait(lock, [&] { return _finishedOtherThreadOperation; });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,23 +215,25 @@ public:
|
||||||
_condition.notify_one();
|
_condition.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
void withMainThreadContext(std::function<void()> f) {
|
void withOtherThreadContext(std::function<void()> f) {
|
||||||
// Signal to the thread that there is work to be done on the main thread
|
// Signal to the thread that there is work to be done on the main thread
|
||||||
Lock lock(_mutex);
|
Lock lock(_mutex);
|
||||||
_pendingMainThreadOperation = true;
|
_targetOperationThread = QThread::currentThread();
|
||||||
_finishedMainThreadOperation = false;
|
_pendingOtherThreadOperation = true;
|
||||||
_condition.wait(lock, [&] { return !_pendingMainThreadOperation; });
|
_finishedOtherThreadOperation = false;
|
||||||
|
_condition.wait(lock, [&] { return !_pendingOtherThreadOperation; });
|
||||||
|
|
||||||
_context->makeCurrent();
|
_context->makeCurrent();
|
||||||
f();
|
f();
|
||||||
_context->doneCurrent();
|
_context->doneCurrent();
|
||||||
|
|
||||||
|
_targetOperationThread = nullptr;
|
||||||
// Move the context back to the presentation thread
|
// Move the context back to the presentation thread
|
||||||
_context->moveToThread(this);
|
_context->moveToThread(this);
|
||||||
|
|
||||||
// restore control of the context to the presentation thread and signal
|
// restore control of the context to the presentation thread and signal
|
||||||
// the end of the operation
|
// the end of the operation
|
||||||
_finishedMainThreadOperation = true;
|
_finishedOtherThreadOperation = true;
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
_condition.notify_one();
|
_condition.notify_one();
|
||||||
}
|
}
|
||||||
|
@ -244,9 +247,11 @@ private:
|
||||||
Mutex _mutex;
|
Mutex _mutex;
|
||||||
// Used to allow the main thread to perform context operations
|
// Used to allow the main thread to perform context operations
|
||||||
Condition _condition;
|
Condition _condition;
|
||||||
bool _pendingMainThreadOperation { false };
|
|
||||||
bool _finishedMainThreadOperation { false };
|
|
||||||
QThread* _mainThread { nullptr };
|
QThread* _targetOperationThread { nullptr };
|
||||||
|
bool _pendingOtherThreadOperation { false };
|
||||||
|
bool _finishedOtherThreadOperation { false };
|
||||||
std::queue<OpenGLDisplayPlugin*> _newPluginQueue;
|
std::queue<OpenGLDisplayPlugin*> _newPluginQueue;
|
||||||
gl::Context* _context { nullptr };
|
gl::Context* _context { nullptr };
|
||||||
};
|
};
|
||||||
|
@ -744,10 +749,12 @@ void OpenGLDisplayPlugin::swapBuffers() {
|
||||||
context->swapBuffers();
|
context->swapBuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLDisplayPlugin::withMainThreadContext(std::function<void()> f) const {
|
void OpenGLDisplayPlugin::withOtherThreadContext(std::function<void()> f) const {
|
||||||
static auto presentThread = DependencyManager::get<PresentThread>();
|
static auto presentThread = DependencyManager::get<PresentThread>();
|
||||||
presentThread->withMainThreadContext(f);
|
presentThread->withOtherThreadContext(f);
|
||||||
_container->makeRenderingContextCurrent();
|
if (!OffscreenGLCanvas::restoreThreadContext()) {
|
||||||
|
qWarning("Unable to restore original OpenGL context");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLDisplayPlugin::setDisplayTexture(const QString& name) {
|
bool OpenGLDisplayPlugin::setDisplayTexture(const QString& name) {
|
||||||
|
@ -784,7 +791,7 @@ QImage OpenGLDisplayPlugin::getScreenshot(float aspectRatio) const {
|
||||||
}
|
}
|
||||||
auto glBackend = const_cast<OpenGLDisplayPlugin&>(*this).getGLBackend();
|
auto glBackend = const_cast<OpenGLDisplayPlugin&>(*this).getGLBackend();
|
||||||
QImage screenshot(bestSize.x, bestSize.y, QImage::Format_ARGB32);
|
QImage screenshot(bestSize.x, bestSize.y, QImage::Format_ARGB32);
|
||||||
withMainThreadContext([&] {
|
withOtherThreadContext([&] {
|
||||||
glBackend->downloadFramebuffer(_compositeFramebuffer, ivec4(corner, bestSize), screenshot);
|
glBackend->downloadFramebuffer(_compositeFramebuffer, ivec4(corner, bestSize), screenshot);
|
||||||
});
|
});
|
||||||
return screenshot.mirrored(false, true);
|
return screenshot.mirrored(false, true);
|
||||||
|
@ -797,7 +804,7 @@ QImage OpenGLDisplayPlugin::getSecondaryCameraScreenshot() const {
|
||||||
|
|
||||||
auto glBackend = const_cast<OpenGLDisplayPlugin&>(*this).getGLBackend();
|
auto glBackend = const_cast<OpenGLDisplayPlugin&>(*this).getGLBackend();
|
||||||
QImage screenshot(region.z, region.w, QImage::Format_ARGB32);
|
QImage screenshot(region.z, region.w, QImage::Format_ARGB32);
|
||||||
withMainThreadContext([&] {
|
withOtherThreadContext([&] {
|
||||||
glBackend->downloadFramebuffer(secondaryCameraFramebuffer, region, screenshot);
|
glBackend->downloadFramebuffer(secondaryCameraFramebuffer, region, screenshot);
|
||||||
});
|
});
|
||||||
return screenshot.mirrored(false, true);
|
return screenshot.mirrored(false, true);
|
||||||
|
@ -886,7 +893,7 @@ void OpenGLDisplayPlugin::updateCompositeFramebuffer() {
|
||||||
void OpenGLDisplayPlugin::copyTextureToQuickFramebuffer(NetworkTexturePointer networkTexture, QOpenGLFramebufferObject* target, GLsync* fenceSync) {
|
void OpenGLDisplayPlugin::copyTextureToQuickFramebuffer(NetworkTexturePointer networkTexture, QOpenGLFramebufferObject* target, GLsync* fenceSync) {
|
||||||
#if !defined(USE_GLES)
|
#if !defined(USE_GLES)
|
||||||
auto glBackend = const_cast<OpenGLDisplayPlugin&>(*this).getGLBackend();
|
auto glBackend = const_cast<OpenGLDisplayPlugin&>(*this).getGLBackend();
|
||||||
withMainThreadContext([&] {
|
withOtherThreadContext([&] {
|
||||||
GLuint sourceTexture = glBackend->getTextureID(networkTexture->getGPUTexture());
|
GLuint sourceTexture = glBackend->getTextureID(networkTexture->getGPUTexture());
|
||||||
GLuint targetTexture = target->texture();
|
GLuint targetTexture = target->texture();
|
||||||
GLuint fbo[2] {0, 0};
|
GLuint fbo[2] {0, 0};
|
||||||
|
|
|
@ -119,7 +119,7 @@ protected:
|
||||||
void renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer texture, glm::ivec4 viewport, const glm::ivec4 scissor);
|
void renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer texture, glm::ivec4 viewport, const glm::ivec4 scissor);
|
||||||
virtual void updateFrameData();
|
virtual void updateFrameData();
|
||||||
|
|
||||||
void withMainThreadContext(std::function<void()> f) const;
|
void withOtherThreadContext(std::function<void()> f) const;
|
||||||
|
|
||||||
void present();
|
void present();
|
||||||
virtual void swapBuffers();
|
virtual void swapBuffers();
|
||||||
|
|
|
@ -985,6 +985,7 @@ void RenderableModelEntityItem::copyAnimationJointDataToModel() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool changed { false };
|
||||||
// relay any inbound joint changes from scripts/animation/network to the model/rig
|
// relay any inbound joint changes from scripts/animation/network to the model/rig
|
||||||
_jointDataLock.withWriteLock([&] {
|
_jointDataLock.withWriteLock([&] {
|
||||||
for (int index = 0; index < _localJointData.size(); ++index) {
|
for (int index = 0; index < _localJointData.size(); ++index) {
|
||||||
|
@ -992,13 +993,21 @@ void RenderableModelEntityItem::copyAnimationJointDataToModel() {
|
||||||
if (jointData.rotationDirty) {
|
if (jointData.rotationDirty) {
|
||||||
model->setJointRotation(index, true, jointData.joint.rotation, 1.0f);
|
model->setJointRotation(index, true, jointData.joint.rotation, 1.0f);
|
||||||
jointData.rotationDirty = false;
|
jointData.rotationDirty = false;
|
||||||
|
changed = true;
|
||||||
}
|
}
|
||||||
if (jointData.translationDirty) {
|
if (jointData.translationDirty) {
|
||||||
model->setJointTranslation(index, true, jointData.joint.translation, 1.0f);
|
model->setJointTranslation(index, true, jointData.joint.translation, 1.0f);
|
||||||
jointData.translationDirty = false;
|
jointData.translationDirty = false;
|
||||||
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
forEachChild([&](SpatiallyNestablePointer object) {
|
||||||
|
object->locationChanged(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
using namespace render;
|
using namespace render;
|
||||||
|
@ -1343,7 +1352,7 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
||||||
// FIXME: this seems like it could be optimized if we tracked our last known visible state in
|
// FIXME: this seems like it could be optimized if we tracked our last known visible state in
|
||||||
// the renderable item. As it stands now the model checks it's visible/invisible state
|
// the renderable item. As it stands now the model checks it's visible/invisible state
|
||||||
// so most of the time we don't do anything in this function.
|
// so most of the time we don't do anything in this function.
|
||||||
model->setVisibleInScene(_visible, scene, viewTaskBits);
|
model->setVisibleInScene(_visible, scene, viewTaskBits, false);
|
||||||
}
|
}
|
||||||
// TODO? early exit here when not visible?
|
// TODO? early exit here when not visible?
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
#include <QtCore/QProcessEnvironment>
|
#include <QtCore/QProcessEnvironment>
|
||||||
#include <QtCore/QDebug>
|
#include <QtCore/QDebug>
|
||||||
|
#include <QtCore/QThread>
|
||||||
#include <QtGui/QOffscreenSurface>
|
#include <QtGui/QOffscreenSurface>
|
||||||
#include <QtGui/QOpenGLContext>
|
#include <QtGui/QOpenGLContext>
|
||||||
#include <QtGui/QOpenGLDebugLogger>
|
#include <QtGui/QOpenGLDebugLogger>
|
||||||
|
@ -119,3 +120,29 @@ void OffscreenGLCanvas::moveToThreadWithContext(QThread* thread) {
|
||||||
moveToThread(thread);
|
moveToThread(thread);
|
||||||
_context->moveToThread(thread);
|
_context->moveToThread(thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char* THREAD_CONTEXT_PROPERTY = "offscreenGlCanvas";
|
||||||
|
|
||||||
|
void OffscreenGLCanvas::setThreadContext() {
|
||||||
|
QThread::currentThread()->setProperty(THREAD_CONTEXT_PROPERTY, QVariant::fromValue<QObject*>(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OffscreenGLCanvas::restoreThreadContext() {
|
||||||
|
// Restore the rendering context for this thread
|
||||||
|
auto threadCanvasVariant = QThread::currentThread()->property(THREAD_CONTEXT_PROPERTY);
|
||||||
|
if (!threadCanvasVariant.isValid()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto threadCanvasObject = qvariant_cast<QObject*>(threadCanvasVariant);
|
||||||
|
auto threadCanvas = static_cast<OffscreenGLCanvas*>(threadCanvasObject);
|
||||||
|
if (!threadCanvas) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!threadCanvas->makeCurrent()) {
|
||||||
|
qFatal("Unable to restore Offscreen rendering context");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -32,6 +32,9 @@ public:
|
||||||
}
|
}
|
||||||
QObject* getContextObject();
|
QObject* getContextObject();
|
||||||
|
|
||||||
|
void setThreadContext();
|
||||||
|
static bool restoreThreadContext();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onMessageLogged(const QOpenGLDebugMessage &debugMessage);
|
void onMessageLogged(const QOpenGLDebugMessage &debugMessage);
|
||||||
|
|
||||||
|
|
|
@ -54,13 +54,11 @@ void KeyboardMouseDevice::InputDevice::focusOutEvent() {
|
||||||
|
|
||||||
void KeyboardMouseDevice::keyPressEvent(QKeyEvent* event) {
|
void KeyboardMouseDevice::keyPressEvent(QKeyEvent* event) {
|
||||||
auto input = _inputDevice->makeInput((Qt::Key) event->key());
|
auto input = _inputDevice->makeInput((Qt::Key) event->key());
|
||||||
if (!(event->modifiers() & Qt::KeyboardModifier::ControlModifier)) {
|
|
||||||
auto result = _inputDevice->_buttonPressedMap.insert(input.getChannel());
|
auto result = _inputDevice->_buttonPressedMap.insert(input.getChannel());
|
||||||
if (result.second) {
|
if (result.second) {
|
||||||
// key pressed again ? without catching the release event ?
|
// key pressed again ? without catching the release event ?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void KeyboardMouseDevice::keyReleaseEvent(QKeyEvent* event) {
|
void KeyboardMouseDevice::keyReleaseEvent(QKeyEvent* event) {
|
||||||
auto input = _inputDevice->makeInput((Qt::Key) event->key());
|
auto input = _inputDevice->makeInput((Qt::Key) event->key());
|
||||||
|
@ -237,6 +235,7 @@ controller::Input::NamedVector KeyboardMouseDevice::InputDevice::getAvailableInp
|
||||||
availableInputs.append(Input::NamedPair(makeInput(Qt::Key_PageUp), QKeySequence(Qt::Key_PageUp).toString()));
|
availableInputs.append(Input::NamedPair(makeInput(Qt::Key_PageUp), QKeySequence(Qt::Key_PageUp).toString()));
|
||||||
availableInputs.append(Input::NamedPair(makeInput(Qt::Key_PageDown), QKeySequence(Qt::Key_PageDown).toString()));
|
availableInputs.append(Input::NamedPair(makeInput(Qt::Key_PageDown), QKeySequence(Qt::Key_PageDown).toString()));
|
||||||
availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Tab), QKeySequence(Qt::Key_Tab).toString()));
|
availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Tab), QKeySequence(Qt::Key_Tab).toString()));
|
||||||
|
availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Control), "Control"));
|
||||||
|
|
||||||
availableInputs.append(Input::NamedPair(makeInput(Qt::LeftButton), "LeftMouseButton"));
|
availableInputs.append(Input::NamedPair(makeInput(Qt::LeftButton), "LeftMouseButton"));
|
||||||
availableInputs.append(Input::NamedPair(makeInput(Qt::MiddleButton), "MiddleMouseButton"));
|
availableInputs.append(Input::NamedPair(makeInput(Qt::MiddleButton), "MiddleMouseButton"));
|
||||||
|
|
|
@ -40,7 +40,7 @@ AssetClient::AssetClient() {
|
||||||
static_cast<AssetClient*>(dependency)->deleteLater();
|
static_cast<AssetClient*>(dependency)->deleteLater();
|
||||||
});
|
});
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||||
auto& packetReceiver = nodeList->getPacketReceiver();
|
auto& packetReceiver = nodeList->getPacketReceiver();
|
||||||
|
|
||||||
packetReceiver.registerListener(PacketType::AssetMappingOperationReply, this, "handleAssetMappingOperationReply");
|
packetReceiver.registerListener(PacketType::AssetMappingOperationReply, this, "handleAssetMappingOperationReply");
|
||||||
|
@ -308,7 +308,7 @@ void AssetClient::handleAssetMappingOperationReply(QSharedPointer<ReceivedMessag
|
||||||
}
|
}
|
||||||
|
|
||||||
bool haveAssetServer() {
|
bool haveAssetServer() {
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||||
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
||||||
|
|
||||||
if (!assetServer) {
|
if (!assetServer) {
|
||||||
|
@ -402,7 +402,7 @@ MessageID AssetClient::getAsset(const QString& hash, AssetUtils::DataOffset star
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||||
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
||||||
|
|
||||||
if (assetServer) {
|
if (assetServer) {
|
||||||
|
@ -435,7 +435,7 @@ MessageID AssetClient::getAsset(const QString& hash, AssetUtils::DataOffset star
|
||||||
MessageID AssetClient::getAssetInfo(const QString& hash, GetInfoCallback callback) {
|
MessageID AssetClient::getAssetInfo(const QString& hash, GetInfoCallback callback) {
|
||||||
Q_ASSERT(QThread::currentThread() == thread());
|
Q_ASSERT(QThread::currentThread() == thread());
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||||
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
||||||
|
|
||||||
if (assetServer) {
|
if (assetServer) {
|
||||||
|
@ -635,7 +635,7 @@ void AssetClient::handleCompleteCallback(const QWeakPointer<Node>& node, Message
|
||||||
MessageID AssetClient::getAssetMapping(const AssetUtils::AssetPath& path, MappingOperationCallback callback) {
|
MessageID AssetClient::getAssetMapping(const AssetUtils::AssetPath& path, MappingOperationCallback callback) {
|
||||||
Q_ASSERT(QThread::currentThread() == thread());
|
Q_ASSERT(QThread::currentThread() == thread());
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||||
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
||||||
|
|
||||||
if (assetServer) {
|
if (assetServer) {
|
||||||
|
@ -662,7 +662,7 @@ MessageID AssetClient::getAssetMapping(const AssetUtils::AssetPath& path, Mappin
|
||||||
MessageID AssetClient::getAllAssetMappings(MappingOperationCallback callback) {
|
MessageID AssetClient::getAllAssetMappings(MappingOperationCallback callback) {
|
||||||
Q_ASSERT(QThread::currentThread() == thread());
|
Q_ASSERT(QThread::currentThread() == thread());
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||||
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
||||||
|
|
||||||
if (assetServer) {
|
if (assetServer) {
|
||||||
|
@ -685,7 +685,7 @@ MessageID AssetClient::getAllAssetMappings(MappingOperationCallback callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageID AssetClient::deleteAssetMappings(const AssetUtils::AssetPathList& paths, MappingOperationCallback callback) {
|
MessageID AssetClient::deleteAssetMappings(const AssetUtils::AssetPathList& paths, MappingOperationCallback callback) {
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||||
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
||||||
|
|
||||||
if (assetServer) {
|
if (assetServer) {
|
||||||
|
@ -716,7 +716,7 @@ MessageID AssetClient::deleteAssetMappings(const AssetUtils::AssetPathList& path
|
||||||
MessageID AssetClient::setAssetMapping(const QString& path, const AssetUtils::AssetHash& hash, MappingOperationCallback callback) {
|
MessageID AssetClient::setAssetMapping(const QString& path, const AssetUtils::AssetHash& hash, MappingOperationCallback callback) {
|
||||||
Q_ASSERT(QThread::currentThread() == thread());
|
Q_ASSERT(QThread::currentThread() == thread());
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||||
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
||||||
|
|
||||||
if (assetServer) {
|
if (assetServer) {
|
||||||
|
@ -742,7 +742,7 @@ MessageID AssetClient::setAssetMapping(const QString& path, const AssetUtils::As
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageID AssetClient::renameAssetMapping(const AssetUtils::AssetPath& oldPath, const AssetUtils::AssetPath& newPath, MappingOperationCallback callback) {
|
MessageID AssetClient::renameAssetMapping(const AssetUtils::AssetPath& oldPath, const AssetUtils::AssetPath& newPath, MappingOperationCallback callback) {
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||||
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
||||||
|
|
||||||
if (assetServer) {
|
if (assetServer) {
|
||||||
|
@ -769,7 +769,7 @@ MessageID AssetClient::renameAssetMapping(const AssetUtils::AssetPath& oldPath,
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageID AssetClient::setBakingEnabled(const AssetUtils::AssetPathList& paths, bool enabled, MappingOperationCallback callback) {
|
MessageID AssetClient::setBakingEnabled(const AssetUtils::AssetPathList& paths, bool enabled, MappingOperationCallback callback) {
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||||
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
||||||
|
|
||||||
if (assetServer) {
|
if (assetServer) {
|
||||||
|
@ -859,7 +859,7 @@ bool AssetClient::cancelUploadAssetRequest(MessageID id) {
|
||||||
MessageID AssetClient::uploadAsset(const QByteArray& data, UploadResultCallback callback) {
|
MessageID AssetClient::uploadAsset(const QByteArray& data, UploadResultCallback callback) {
|
||||||
Q_ASSERT(QThread::currentThread() == thread());
|
Q_ASSERT(QThread::currentThread() == thread());
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||||
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
||||||
|
|
||||||
if (assetServer) {
|
if (assetServer) {
|
||||||
|
|
|
@ -44,7 +44,9 @@ enum AssetServerError : uint8_t {
|
||||||
AssetTooLarge,
|
AssetTooLarge,
|
||||||
PermissionDenied,
|
PermissionDenied,
|
||||||
MappingOperationFailed,
|
MappingOperationFailed,
|
||||||
FileOperationFailed
|
FileOperationFailed,
|
||||||
|
NoAssetServer,
|
||||||
|
LostConnection
|
||||||
};
|
};
|
||||||
|
|
||||||
enum AssetMappingOperationType : uint8_t {
|
enum AssetMappingOperationType : uint8_t {
|
||||||
|
@ -71,7 +73,8 @@ struct MappingInfo {
|
||||||
QString bakingErrors;
|
QString bakingErrors;
|
||||||
};
|
};
|
||||||
|
|
||||||
using AssetMapping = std::map<AssetPath, MappingInfo>;
|
using AssetMappings = std::map<AssetPath, MappingInfo>;
|
||||||
|
using Mappings = std::map<AssetPath, AssetHash>;
|
||||||
|
|
||||||
QUrl getATPUrl(const QString& input);
|
QUrl getATPUrl(const QString& input);
|
||||||
AssetHash extractAssetHash(const QString& input);
|
AssetHash extractAssetHash(const QString& input);
|
||||||
|
|
|
@ -49,7 +49,7 @@ public:
|
||||||
const QHostAddress& getIP() const { return _sockAddr.getAddress(); }
|
const QHostAddress& getIP() const { return _sockAddr.getAddress(); }
|
||||||
void setIPToLocalhost() { _sockAddr.setAddress(QHostAddress(QHostAddress::LocalHost)); }
|
void setIPToLocalhost() { _sockAddr.setAddress(QHostAddress(QHostAddress::LocalHost)); }
|
||||||
|
|
||||||
const HifiSockAddr& getSockAddr() { return _sockAddr; }
|
const HifiSockAddr& getSockAddr() const { return _sockAddr; }
|
||||||
void setSockAddr(const HifiSockAddr& sockAddr, const QString& hostname);
|
void setSockAddr(const HifiSockAddr& sockAddr, const QString& hostname);
|
||||||
|
|
||||||
unsigned short getPort() const { return _sockAddr.getPort(); }
|
unsigned short getPort() const { return _sockAddr.getPort(); }
|
||||||
|
|
|
@ -90,21 +90,16 @@ LimitedNodeList::LimitedNodeList(int socketListenPort, int dtlsListenPort) :
|
||||||
updateLocalSocket();
|
updateLocalSocket();
|
||||||
|
|
||||||
// set &PacketReceiver::handleVerifiedPacket as the verified packet callback for the udt::Socket
|
// set &PacketReceiver::handleVerifiedPacket as the verified packet callback for the udt::Socket
|
||||||
_nodeSocket.setPacketHandler(
|
_nodeSocket.setPacketHandler([this](std::unique_ptr<udt::Packet> packet) {
|
||||||
[this](std::unique_ptr<udt::Packet> packet) {
|
|
||||||
_packetReceiver->handleVerifiedPacket(std::move(packet));
|
_packetReceiver->handleVerifiedPacket(std::move(packet));
|
||||||
}
|
});
|
||||||
);
|
_nodeSocket.setMessageHandler([this](std::unique_ptr<udt::Packet> packet) {
|
||||||
_nodeSocket.setMessageHandler(
|
|
||||||
[this](std::unique_ptr<udt::Packet> packet) {
|
|
||||||
_packetReceiver->handleVerifiedMessagePacket(std::move(packet));
|
_packetReceiver->handleVerifiedMessagePacket(std::move(packet));
|
||||||
}
|
});
|
||||||
);
|
_nodeSocket.setMessageFailureHandler([this](HifiSockAddr from,
|
||||||
_nodeSocket.setMessageFailureHandler(
|
udt::Packet::MessageNumber messageNumber) {
|
||||||
[this](HifiSockAddr from, udt::Packet::MessageNumber messageNumber) {
|
|
||||||
_packetReceiver->handleMessageFailure(from, messageNumber);
|
_packetReceiver->handleMessageFailure(from, messageNumber);
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
// set our isPacketVerified method as the verify operator for the udt::Socket
|
// set our isPacketVerified method as the verify operator for the udt::Socket
|
||||||
using std::placeholders::_1;
|
using std::placeholders::_1;
|
||||||
|
@ -309,8 +304,19 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe
|
||||||
sourceNode = matchingNode.data();
|
sourceNode = matchingNode.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!sourceNode &&
|
||||||
|
sourceID == getDomainUUID() &&
|
||||||
|
packet.getSenderSockAddr() == getDomainSockAddr() &&
|
||||||
|
PacketTypeEnum::getDomainSourcedPackets().contains(headerType)) {
|
||||||
|
// This is a packet sourced by the domain server
|
||||||
|
|
||||||
|
emit dataReceived(NodeType::Unassigned, packet.getPayloadSize());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (sourceNode) {
|
if (sourceNode) {
|
||||||
if (!PacketTypeEnum::getNonVerifiedPackets().contains(headerType)) {
|
if (!PacketTypeEnum::getNonVerifiedPackets().contains(headerType) &&
|
||||||
|
!isDomainServer()) {
|
||||||
|
|
||||||
QByteArray packetHeaderHash = NLPacket::verificationHashInHeader(packet);
|
QByteArray packetHeaderHash = NLPacket::verificationHashInHeader(packet);
|
||||||
QByteArray expectedHash = NLPacket::hashForPacketAndSecret(packet, sourceNode->getConnectionSecret());
|
QByteArray expectedHash = NLPacket::hashForPacketAndSecret(packet, sourceNode->getConnectionSecret());
|
||||||
|
|
|
@ -124,6 +124,10 @@ public:
|
||||||
|
|
||||||
PacketReceiver& getPacketReceiver() { return *_packetReceiver; }
|
PacketReceiver& getPacketReceiver() { return *_packetReceiver; }
|
||||||
|
|
||||||
|
virtual bool isDomainServer() const { return true; }
|
||||||
|
virtual QUuid getDomainUUID() const { assert(false); return QUuid(); }
|
||||||
|
virtual HifiSockAddr getDomainSockAddr() const { assert(false); return HifiSockAddr(); }
|
||||||
|
|
||||||
// use sendUnreliablePacket to send an unrelaible packet (that you do not need to move)
|
// use sendUnreliablePacket to send an unrelaible packet (that you do not need to move)
|
||||||
// either to a node (via its active socket) or to a manual sockaddr
|
// either to a node (via its active socket) or to a manual sockaddr
|
||||||
qint64 sendUnreliablePacket(const NLPacket& packet, const Node& destinationNode);
|
qint64 sendUnreliablePacket(const NLPacket& packet, const Node& destinationNode);
|
||||||
|
|
|
@ -106,9 +106,6 @@ void GetMappingRequest::doStart() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
GetAllMappingsRequest::GetAllMappingsRequest() {
|
|
||||||
};
|
|
||||||
|
|
||||||
void GetAllMappingsRequest::doStart() {
|
void GetAllMappingsRequest::doStart() {
|
||||||
auto assetClient = DependencyManager::get<AssetClient>();
|
auto assetClient = DependencyManager::get<AssetClient>();
|
||||||
_mappingRequestID = assetClient->getAllAssetMappings(
|
_mappingRequestID = assetClient->getAllAssetMappings(
|
||||||
|
|
|
@ -120,9 +120,7 @@ private:
|
||||||
class GetAllMappingsRequest : public MappingRequest {
|
class GetAllMappingsRequest : public MappingRequest {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
GetAllMappingsRequest();
|
AssetUtils::AssetMappings getMappings() const { return _mappings; }
|
||||||
|
|
||||||
AssetUtils::AssetMapping getMappings() const { return _mappings; }
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void finished(GetAllMappingsRequest* thisRequest);
|
void finished(GetAllMappingsRequest* thisRequest);
|
||||||
|
@ -130,7 +128,7 @@ signals:
|
||||||
private:
|
private:
|
||||||
virtual void doStart() override;
|
virtual void doStart() override;
|
||||||
|
|
||||||
AssetUtils::AssetMapping _mappings;
|
AssetUtils::AssetMappings _mappings;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SetBakingEnabledRequest : public MappingRequest {
|
class SetBakingEnabledRequest : public MappingRequest {
|
||||||
|
|
|
@ -92,6 +92,10 @@ public:
|
||||||
|
|
||||||
void removeFromIgnoreMuteSets(const QUuid& nodeID);
|
void removeFromIgnoreMuteSets(const QUuid& nodeID);
|
||||||
|
|
||||||
|
virtual bool isDomainServer() const override { return false; }
|
||||||
|
virtual QUuid getDomainUUID() const override { return _domainHandler.getUUID(); }
|
||||||
|
virtual HifiSockAddr getDomainSockAddr() const override { return _domainHandler.getSockAddr(); }
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void reset(bool skipDomainHandlerReset = false);
|
void reset(bool skipDomainHandlerReset = false);
|
||||||
void resetFromDomainHandler() { reset(true); }
|
void resetFromDomainHandler() { reset(true); }
|
||||||
|
|
|
@ -267,10 +267,7 @@ void PacketReceiver::handleVerifiedMessage(QSharedPointer<ReceivedMessage> recei
|
||||||
|
|
||||||
QMutexLocker packetListenerLocker(&_packetListenerLock);
|
QMutexLocker packetListenerLocker(&_packetListenerLock);
|
||||||
|
|
||||||
bool listenerIsDead = false;
|
|
||||||
|
|
||||||
auto it = _messageListenerMap.find(receivedMessage->getType());
|
auto it = _messageListenerMap.find(receivedMessage->getType());
|
||||||
|
|
||||||
if (it != _messageListenerMap.end() && it->method.isValid()) {
|
if (it != _messageListenerMap.end() && it->method.isValid()) {
|
||||||
|
|
||||||
auto listener = it.value();
|
auto listener = it.value();
|
||||||
|
@ -279,22 +276,18 @@ void PacketReceiver::handleVerifiedMessage(QSharedPointer<ReceivedMessage> recei
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (listener.object) {
|
|
||||||
|
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
|
||||||
Qt::ConnectionType connectionType;
|
Qt::ConnectionType connectionType;
|
||||||
// check if this is a directly connected listener
|
// check if this is a directly connected listener
|
||||||
{
|
{
|
||||||
QMutexLocker directConnectLocker(&_directConnectSetMutex);
|
QMutexLocker directConnectLocker(&_directConnectSetMutex);
|
||||||
|
|
||||||
connectionType = _directlyConnectedObjects.contains(listener.object) ? Qt::DirectConnection : Qt::AutoConnection;
|
connectionType = _directlyConnectedObjects.contains(listener.object) ? Qt::DirectConnection : Qt::AutoConnection;
|
||||||
}
|
}
|
||||||
|
|
||||||
PacketType packetType = receivedMessage->getType();
|
|
||||||
|
|
||||||
if (matchingNode) {
|
if (matchingNode) {
|
||||||
matchingNode->recordBytesReceived(receivedMessage->getSize());
|
matchingNode->recordBytesReceived(receivedMessage->getSize());
|
||||||
|
}
|
||||||
|
|
||||||
QMetaMethod metaMethod = listener.method;
|
QMetaMethod metaMethod = listener.method;
|
||||||
|
|
||||||
|
@ -321,29 +314,6 @@ void PacketReceiver::handleVerifiedMessage(QSharedPointer<ReceivedMessage> recei
|
||||||
Q_ARG(QSharedPointer<ReceivedMessage>, receivedMessage));
|
Q_ARG(QSharedPointer<ReceivedMessage>, receivedMessage));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
listenerIsDead = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// one final check on the QPointer before we invoke
|
|
||||||
if (listener.object) {
|
|
||||||
success = listener.method.invoke(listener.object,
|
|
||||||
Q_ARG(QSharedPointer<ReceivedMessage>, receivedMessage));
|
|
||||||
} else {
|
|
||||||
listenerIsDead = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!success) {
|
|
||||||
qCDebug(networking).nospace() << "Error delivering packet " << packetType << " to listener "
|
|
||||||
<< listener.object << "::" << qPrintable(listener.method.methodSignature());
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
listenerIsDead = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (listenerIsDead) {
|
|
||||||
qCDebug(networking).nospace() << "Listener for packet " << receivedMessage->getType()
|
qCDebug(networking).nospace() << "Listener for packet " << receivedMessage->getType()
|
||||||
<< " has been destroyed. Removing from listener map.";
|
<< " has been destroyed. Removing from listener map.";
|
||||||
it = _messageListenerMap.erase(it);
|
it = _messageListenerMap.erase(it);
|
||||||
|
@ -354,6 +324,12 @@ void PacketReceiver::handleVerifiedMessage(QSharedPointer<ReceivedMessage> recei
|
||||||
_directlyConnectedObjects.remove(listener.object);
|
_directlyConnectedObjects.remove(listener.object);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
qCDebug(networking).nospace() << "Error delivering packet " << receivedMessage->getType() << " to listener "
|
||||||
|
<< listener.object << "::" << qPrintable(listener.method.methodSignature());
|
||||||
|
}
|
||||||
|
|
||||||
} else if (it == _messageListenerMap.end()) {
|
} else if (it == _messageListenerMap.end()) {
|
||||||
qCWarning(networking) << "No listener found for packet type" << receivedMessage->getType();
|
qCWarning(networking) << "No listener found for packet type" << receivedMessage->getType();
|
||||||
|
|
||||||
|
|
|
@ -177,6 +177,17 @@ public:
|
||||||
<< PacketTypeEnum::Value::ReplicatedKillAvatar << PacketTypeEnum::Value::ReplicatedBulkAvatarData;
|
<< PacketTypeEnum::Value::ReplicatedKillAvatar << PacketTypeEnum::Value::ReplicatedBulkAvatarData;
|
||||||
return NON_SOURCED_PACKETS;
|
return NON_SOURCED_PACKETS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const static QSet<PacketTypeEnum::Value> getDomainSourcedPackets() {
|
||||||
|
const static QSet<PacketTypeEnum::Value> DOMAIN_SOURCED_PACKETS = QSet<PacketTypeEnum::Value>()
|
||||||
|
<< PacketTypeEnum::Value::AssetMappingOperation
|
||||||
|
<< PacketTypeEnum::Value::AssetMappingOperationReply
|
||||||
|
<< PacketTypeEnum::Value::AssetGet
|
||||||
|
<< PacketTypeEnum::Value::AssetGetReply
|
||||||
|
<< PacketTypeEnum::Value::AssetUpload
|
||||||
|
<< PacketTypeEnum::Value::AssetUploadReply;
|
||||||
|
return DOMAIN_SOURCED_PACKETS;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
using PacketType = PacketTypeEnum::Value;
|
using PacketType = PacketTypeEnum::Value;
|
||||||
|
|
|
@ -58,6 +58,7 @@ void RenderEventHandler::onInitalize() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_canvas.setThreadContext();
|
||||||
if (!_canvas.makeCurrent()) {
|
if (!_canvas.makeCurrent()) {
|
||||||
qFatal("Unable to make QML rendering context current on render thread");
|
qFatal("Unable to make QML rendering context current on render thread");
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,7 @@ void MeshPartPayload::removeMaterial(graphics::MaterialPointer material) {
|
||||||
_drawMaterials.remove(material);
|
_drawMaterials.remove(material);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeshPartPayload::updateKey(bool isVisible, bool isLayered, uint8_t tagBits) {
|
void MeshPartPayload::updateKey(bool isVisible, bool isLayered, uint8_t tagBits, bool isGroupCulled) {
|
||||||
ItemKey::Builder builder;
|
ItemKey::Builder builder;
|
||||||
builder.withTypeShape();
|
builder.withTypeShape();
|
||||||
|
|
||||||
|
@ -89,6 +89,10 @@ void MeshPartPayload::updateKey(bool isVisible, bool isLayered, uint8_t tagBits)
|
||||||
builder.withLayered();
|
builder.withLayered();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isGroupCulled) {
|
||||||
|
builder.withSubMetaCulled();
|
||||||
|
}
|
||||||
|
|
||||||
if (_drawMaterials.top()) {
|
if (_drawMaterials.top()) {
|
||||||
auto matKey = _drawMaterials.top()->getKey();
|
auto matKey = _drawMaterials.top()->getKey();
|
||||||
if (matKey.isTranslucent()) {
|
if (matKey.isTranslucent()) {
|
||||||
|
@ -284,7 +288,7 @@ void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& render
|
||||||
_worldBound.transform(boundTransform);
|
_worldBound.transform(boundTransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelMeshPartPayload::updateKey(bool isVisible, bool isLayered, uint8_t tagBits) {
|
void ModelMeshPartPayload::updateKey(bool isVisible, bool isLayered, uint8_t tagBits, bool isGroupCulled) {
|
||||||
ItemKey::Builder builder;
|
ItemKey::Builder builder;
|
||||||
builder.withTypeShape();
|
builder.withTypeShape();
|
||||||
|
|
||||||
|
@ -298,6 +302,10 @@ void ModelMeshPartPayload::updateKey(bool isVisible, bool isLayered, uint8_t tag
|
||||||
builder.withLayered();
|
builder.withLayered();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isGroupCulled) {
|
||||||
|
builder.withSubMetaCulled();
|
||||||
|
}
|
||||||
|
|
||||||
if (_isBlendShaped || _isSkinned) {
|
if (_isBlendShaped || _isSkinned) {
|
||||||
builder.withDeformed();
|
builder.withDeformed();
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ public:
|
||||||
typedef render::Payload<MeshPartPayload> Payload;
|
typedef render::Payload<MeshPartPayload> Payload;
|
||||||
typedef Payload::DataPointer Pointer;
|
typedef Payload::DataPointer Pointer;
|
||||||
|
|
||||||
virtual void updateKey(bool isVisible, bool isLayered, uint8_t tagBits);
|
virtual void updateKey(bool isVisible, bool isLayered, uint8_t tagBits, bool isGroupCulled = false);
|
||||||
|
|
||||||
virtual void updateMeshPart(const std::shared_ptr<const graphics::Mesh>& drawMesh, int partIndex);
|
virtual void updateMeshPart(const std::shared_ptr<const graphics::Mesh>& drawMesh, int partIndex);
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ public:
|
||||||
using TransformType = glm::mat4;
|
using TransformType = glm::mat4;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void updateKey(bool isVisible, bool isLayered, uint8_t tagBits) override;
|
void updateKey(bool isVisible, bool isLayered, uint8_t tagBits, bool isGroupCulled = false) override;
|
||||||
void updateClusterBuffer(const std::vector<TransformType>& clusterTransforms);
|
void updateClusterBuffer(const std::vector<TransformType>& clusterTransforms);
|
||||||
void updateTransformForSkinnedMesh(const Transform& renderTransform, const Transform& boundTransform);
|
void updateTransformForSkinnedMesh(const Transform& renderTransform, const Transform& boundTransform);
|
||||||
|
|
||||||
|
|
|
@ -271,6 +271,7 @@ void Model::updateRenderItems() {
|
||||||
uint8_t viewTagBits = self->getViewTagBits();
|
uint8_t viewTagBits = self->getViewTagBits();
|
||||||
bool isLayeredInFront = self->isLayeredInFront();
|
bool isLayeredInFront = self->isLayeredInFront();
|
||||||
bool isLayeredInHUD = self->isLayeredInHUD();
|
bool isLayeredInHUD = self->isLayeredInHUD();
|
||||||
|
bool isGroupCulled = self->isGroupCulled();
|
||||||
|
|
||||||
render::Transaction transaction;
|
render::Transaction transaction;
|
||||||
for (int i = 0; i < (int) self->_modelMeshRenderItemIDs.size(); i++) {
|
for (int i = 0; i < (int) self->_modelMeshRenderItemIDs.size(); i++) {
|
||||||
|
@ -284,7 +285,7 @@ void Model::updateRenderItems() {
|
||||||
transaction.updateItem<ModelMeshPartPayload>(itemID, [modelTransform, clusterTransforms,
|
transaction.updateItem<ModelMeshPartPayload>(itemID, [modelTransform, clusterTransforms,
|
||||||
invalidatePayloadShapeKey, isWireframe, isVisible,
|
invalidatePayloadShapeKey, isWireframe, isVisible,
|
||||||
viewTagBits, isLayeredInFront,
|
viewTagBits, isLayeredInFront,
|
||||||
isLayeredInHUD](ModelMeshPartPayload& data) {
|
isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) {
|
||||||
data.updateClusterBuffer(clusterTransforms);
|
data.updateClusterBuffer(clusterTransforms);
|
||||||
|
|
||||||
Transform renderTransform = modelTransform;
|
Transform renderTransform = modelTransform;
|
||||||
|
@ -300,7 +301,7 @@ void Model::updateRenderItems() {
|
||||||
}
|
}
|
||||||
data.updateTransformForSkinnedMesh(renderTransform, modelTransform);
|
data.updateTransformForSkinnedMesh(renderTransform, modelTransform);
|
||||||
|
|
||||||
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits);
|
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits, isGroupCulled);
|
||||||
data.setLayer(isLayeredInFront, isLayeredInHUD);
|
data.setLayer(isLayeredInFront, isLayeredInHUD);
|
||||||
data.setShapeKey(invalidatePayloadShapeKey, isWireframe);
|
data.setShapeKey(invalidatePayloadShapeKey, isWireframe);
|
||||||
});
|
});
|
||||||
|
@ -684,10 +685,11 @@ void Model::calculateTriangleSets() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Model::setVisibleInScene(bool isVisible, const render::ScenePointer& scene, uint8_t viewTagBits) {
|
void Model::setVisibleInScene(bool isVisible, const render::ScenePointer& scene, uint8_t viewTagBits, bool isGroupCulled) {
|
||||||
if (_isVisible != isVisible || _viewTagBits != viewTagBits) {
|
if (_isVisible != isVisible || _viewTagBits != viewTagBits || _isGroupCulled != isGroupCulled) {
|
||||||
_isVisible = isVisible;
|
_isVisible = isVisible;
|
||||||
_viewTagBits = viewTagBits;
|
_viewTagBits = viewTagBits;
|
||||||
|
_isGroupCulled = isGroupCulled;
|
||||||
|
|
||||||
bool isLayeredInFront = _isLayeredInFront;
|
bool isLayeredInFront = _isLayeredInFront;
|
||||||
bool isLayeredInHUD = _isLayeredInHUD;
|
bool isLayeredInHUD = _isLayeredInHUD;
|
||||||
|
@ -695,14 +697,14 @@ void Model::setVisibleInScene(bool isVisible, const render::ScenePointer& scene,
|
||||||
render::Transaction transaction;
|
render::Transaction transaction;
|
||||||
foreach (auto item, _modelMeshRenderItemsMap.keys()) {
|
foreach (auto item, _modelMeshRenderItemsMap.keys()) {
|
||||||
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, viewTagBits, isLayeredInFront,
|
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, viewTagBits, isLayeredInFront,
|
||||||
isLayeredInHUD](ModelMeshPartPayload& data) {
|
isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) {
|
||||||
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits);
|
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits, isGroupCulled);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
foreach(auto item, _collisionRenderItemsMap.keys()) {
|
foreach(auto item, _collisionRenderItemsMap.keys()) {
|
||||||
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, viewTagBits, isLayeredInFront,
|
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, viewTagBits, isLayeredInFront,
|
||||||
isLayeredInHUD](ModelMeshPartPayload& data) {
|
isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) {
|
||||||
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits);
|
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits, isGroupCulled);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
scene->enqueueTransaction(transaction);
|
scene->enqueueTransaction(transaction);
|
||||||
|
@ -717,19 +719,20 @@ void Model::setLayeredInFront(bool isLayeredInFront, const render::ScenePointer&
|
||||||
bool isVisible = _isVisible;
|
bool isVisible = _isVisible;
|
||||||
uint8_t viewTagBits = _viewTagBits;
|
uint8_t viewTagBits = _viewTagBits;
|
||||||
bool isLayeredInHUD = _isLayeredInHUD;
|
bool isLayeredInHUD = _isLayeredInHUD;
|
||||||
|
bool isGroupCulled = _isGroupCulled;
|
||||||
|
|
||||||
render::Transaction transaction;
|
render::Transaction transaction;
|
||||||
foreach(auto item, _modelMeshRenderItemsMap.keys()) {
|
foreach(auto item, _modelMeshRenderItemsMap.keys()) {
|
||||||
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, viewTagBits, isLayeredInFront,
|
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, viewTagBits, isLayeredInFront,
|
||||||
isLayeredInHUD](ModelMeshPartPayload& data) {
|
isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) {
|
||||||
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits);
|
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits, isGroupCulled);
|
||||||
data.setLayer(isLayeredInFront, isLayeredInHUD);
|
data.setLayer(isLayeredInFront, isLayeredInHUD);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
foreach(auto item, _collisionRenderItemsMap.keys()) {
|
foreach(auto item, _collisionRenderItemsMap.keys()) {
|
||||||
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, viewTagBits, isLayeredInFront,
|
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, viewTagBits, isLayeredInFront,
|
||||||
isLayeredInHUD](ModelMeshPartPayload& data) {
|
isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) {
|
||||||
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits);
|
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits, isGroupCulled);
|
||||||
data.setLayer(isLayeredInFront, isLayeredInHUD);
|
data.setLayer(isLayeredInFront, isLayeredInHUD);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -744,19 +747,20 @@ void Model::setLayeredInHUD(bool isLayeredInHUD, const render::ScenePointer& sce
|
||||||
bool isVisible = _isVisible;
|
bool isVisible = _isVisible;
|
||||||
uint8_t viewTagBits = _viewTagBits;
|
uint8_t viewTagBits = _viewTagBits;
|
||||||
bool isLayeredInFront = _isLayeredInFront;
|
bool isLayeredInFront = _isLayeredInFront;
|
||||||
|
bool isGroupCulled = _isGroupCulled;
|
||||||
|
|
||||||
render::Transaction transaction;
|
render::Transaction transaction;
|
||||||
foreach(auto item, _modelMeshRenderItemsMap.keys()) {
|
foreach(auto item, _modelMeshRenderItemsMap.keys()) {
|
||||||
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, viewTagBits, isLayeredInFront,
|
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, viewTagBits, isLayeredInFront,
|
||||||
isLayeredInHUD](ModelMeshPartPayload& data) {
|
isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) {
|
||||||
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits);
|
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits, isGroupCulled);
|
||||||
data.setLayer(isLayeredInFront, isLayeredInHUD);
|
data.setLayer(isLayeredInFront, isLayeredInHUD);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
foreach(auto item, _collisionRenderItemsMap.keys()) {
|
foreach(auto item, _collisionRenderItemsMap.keys()) {
|
||||||
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, viewTagBits, isLayeredInFront,
|
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, viewTagBits, isLayeredInFront,
|
||||||
isLayeredInHUD](ModelMeshPartPayload& data) {
|
isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) {
|
||||||
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits);
|
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits, isGroupCulled);
|
||||||
data.setLayer(isLayeredInFront, isLayeredInHUD);
|
data.setLayer(isLayeredInFront, isLayeredInHUD);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,7 +86,7 @@ public:
|
||||||
const QUrl& getURL() const { return _url; }
|
const QUrl& getURL() const { return _url; }
|
||||||
|
|
||||||
// new Scene/Engine rendering support
|
// new Scene/Engine rendering support
|
||||||
void setVisibleInScene(bool isVisible, const render::ScenePointer& scene, uint8_t viewTagBits);
|
void setVisibleInScene(bool isVisible, const render::ScenePointer& scene, uint8_t viewTagBits, bool isGroupCulled);
|
||||||
void setLayeredInFront(bool isLayeredInFront, const render::ScenePointer& scene);
|
void setLayeredInFront(bool isLayeredInFront, const render::ScenePointer& scene);
|
||||||
void setLayeredInHUD(bool isLayeredInHUD, const render::ScenePointer& scene);
|
void setLayeredInHUD(bool isLayeredInHUD, const render::ScenePointer& scene);
|
||||||
bool needsFixupInScene() const;
|
bool needsFixupInScene() const;
|
||||||
|
@ -109,6 +109,8 @@ public:
|
||||||
bool isLayeredInFront() const { return _isLayeredInFront; }
|
bool isLayeredInFront() const { return _isLayeredInFront; }
|
||||||
bool isLayeredInHUD() const { return _isLayeredInHUD; }
|
bool isLayeredInHUD() const { return _isLayeredInHUD; }
|
||||||
|
|
||||||
|
bool isGroupCulled() const { return _isGroupCulled; }
|
||||||
|
|
||||||
virtual void updateRenderItems();
|
virtual void updateRenderItems();
|
||||||
void setRenderItemsNeedUpdate();
|
void setRenderItemsNeedUpdate();
|
||||||
bool getRenderItemsNeedUpdate() { return _renderItemsNeedUpdate; }
|
bool getRenderItemsNeedUpdate() { return _renderItemsNeedUpdate; }
|
||||||
|
@ -466,6 +468,8 @@ protected:
|
||||||
bool _isLayeredInFront { false };
|
bool _isLayeredInFront { false };
|
||||||
bool _isLayeredInHUD { false };
|
bool _isLayeredInHUD { false };
|
||||||
|
|
||||||
|
bool _isGroupCulled{ false };
|
||||||
|
|
||||||
bool shouldInvalidatePayloadShapeKey(int meshIndex);
|
bool shouldInvalidatePayloadShapeKey(int meshIndex);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -211,13 +211,14 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext,
|
||||||
outItems.clear();
|
outItems.clear();
|
||||||
outItems.reserve(inSelection.numItems());
|
outItems.reserve(inSelection.numItems());
|
||||||
|
|
||||||
const auto filter = inputs.get1();
|
const auto srcFilter = inputs.get1();
|
||||||
if (!filter.selectsNothing()) {
|
if (!srcFilter.selectsNothing()) {
|
||||||
|
auto filter = render::ItemFilter::Builder(srcFilter).withoutSubMetaCulled().build();
|
||||||
|
|
||||||
// Now get the bound, and
|
// Now get the bound, and
|
||||||
// filter individually against the _filter
|
// filter individually against the _filter
|
||||||
// visibility cull if partially selected ( octree cell contianing it was partial)
|
// visibility cull if partially selected ( octree cell contianing it was partial)
|
||||||
// distance cull if was a subcell item ( octree cell is way bigger than the item bound itself, so now need to test per item)
|
// distance cull if was a subcell item ( octree cell is way bigger than the item bound itself, so now need to test per item)
|
||||||
|
|
||||||
if (_skipCulling) {
|
if (_skipCulling) {
|
||||||
// inside & fit items: filter only, culling is disabled
|
// inside & fit items: filter only, culling is disabled
|
||||||
{
|
{
|
||||||
|
@ -227,6 +228,9 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext,
|
||||||
if (filter.test(item.getKey())) {
|
if (filter.test(item.getKey())) {
|
||||||
ItemBound itemBound(id, item.getBound());
|
ItemBound itemBound(id, item.getBound());
|
||||||
outItems.emplace_back(itemBound);
|
outItems.emplace_back(itemBound);
|
||||||
|
if (item.getKey().isMetaCullGroup()) {
|
||||||
|
item.fetchMetaSubItemBounds(outItems, (*scene));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -239,6 +243,9 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext,
|
||||||
if (filter.test(item.getKey())) {
|
if (filter.test(item.getKey())) {
|
||||||
ItemBound itemBound(id, item.getBound());
|
ItemBound itemBound(id, item.getBound());
|
||||||
outItems.emplace_back(itemBound);
|
outItems.emplace_back(itemBound);
|
||||||
|
if (item.getKey().isMetaCullGroup()) {
|
||||||
|
item.fetchMetaSubItemBounds(outItems, (*scene));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -251,6 +258,9 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext,
|
||||||
if (filter.test(item.getKey())) {
|
if (filter.test(item.getKey())) {
|
||||||
ItemBound itemBound(id, item.getBound());
|
ItemBound itemBound(id, item.getBound());
|
||||||
outItems.emplace_back(itemBound);
|
outItems.emplace_back(itemBound);
|
||||||
|
if (item.getKey().isMetaCullGroup()) {
|
||||||
|
item.fetchMetaSubItemBounds(outItems, (*scene));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -263,6 +273,9 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext,
|
||||||
if (filter.test(item.getKey())) {
|
if (filter.test(item.getKey())) {
|
||||||
ItemBound itemBound(id, item.getBound());
|
ItemBound itemBound(id, item.getBound());
|
||||||
outItems.emplace_back(itemBound);
|
outItems.emplace_back(itemBound);
|
||||||
|
if (item.getKey().isMetaCullGroup()) {
|
||||||
|
item.fetchMetaSubItemBounds(outItems, (*scene));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -277,6 +290,9 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext,
|
||||||
if (filter.test(item.getKey())) {
|
if (filter.test(item.getKey())) {
|
||||||
ItemBound itemBound(id, item.getBound());
|
ItemBound itemBound(id, item.getBound());
|
||||||
outItems.emplace_back(itemBound);
|
outItems.emplace_back(itemBound);
|
||||||
|
if (item.getKey().isMetaCullGroup()) {
|
||||||
|
item.fetchMetaSubItemBounds(outItems, (*scene));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -290,6 +306,9 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext,
|
||||||
ItemBound itemBound(id, item.getBound());
|
ItemBound itemBound(id, item.getBound());
|
||||||
if (test.solidAngleTest(itemBound.bound)) {
|
if (test.solidAngleTest(itemBound.bound)) {
|
||||||
outItems.emplace_back(itemBound);
|
outItems.emplace_back(itemBound);
|
||||||
|
if (item.getKey().isMetaCullGroup()) {
|
||||||
|
item.fetchMetaSubItemBounds(outItems, (*scene));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -304,6 +323,9 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext,
|
||||||
ItemBound itemBound(id, item.getBound());
|
ItemBound itemBound(id, item.getBound());
|
||||||
if (test.frustumTest(itemBound.bound)) {
|
if (test.frustumTest(itemBound.bound)) {
|
||||||
outItems.emplace_back(itemBound);
|
outItems.emplace_back(itemBound);
|
||||||
|
if (item.getKey().isMetaCullGroup()) {
|
||||||
|
item.fetchMetaSubItemBounds(outItems, (*scene));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -319,6 +341,9 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext,
|
||||||
if (test.frustumTest(itemBound.bound)) {
|
if (test.frustumTest(itemBound.bound)) {
|
||||||
if (test.solidAngleTest(itemBound.bound)) {
|
if (test.solidAngleTest(itemBound.bound)) {
|
||||||
outItems.emplace_back(itemBound);
|
outItems.emplace_back(itemBound);
|
||||||
|
if (item.getKey().isMetaCullGroup()) {
|
||||||
|
item.fetchMetaSubItemBounds(outItems, (*scene));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,6 +113,21 @@ const ShapeKey Item::getShapeKey() const {
|
||||||
return shapeKey;
|
return shapeKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t Item::fetchMetaSubItemBounds(ItemBounds& subItemBounds, Scene& scene) const {
|
||||||
|
ItemIDs subItems;
|
||||||
|
auto numSubs = fetchMetaSubItems(subItems);
|
||||||
|
|
||||||
|
for (auto id : subItems) {
|
||||||
|
auto& item = scene.getItem(id);
|
||||||
|
if (item.exist()) {
|
||||||
|
subItemBounds.emplace_back(id, item.getBound());
|
||||||
|
} else {
|
||||||
|
numSubs--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return numSubs;
|
||||||
|
}
|
||||||
|
|
||||||
namespace render {
|
namespace render {
|
||||||
template <> const ItemKey payloadGetKey(const PayloadProxyInterface::Pointer& payload) {
|
template <> const ItemKey payloadGetKey(const PayloadProxyInterface::Pointer& payload) {
|
||||||
if (!payload) {
|
if (!payload) {
|
||||||
|
|
|
@ -78,6 +78,8 @@ public:
|
||||||
INVISIBLE, // Visible or not in the scene?
|
INVISIBLE, // Visible or not in the scene?
|
||||||
SHADOW_CASTER, // Item cast shadows
|
SHADOW_CASTER, // Item cast shadows
|
||||||
LAYERED, // Item belongs to one of the layers different from the default layer
|
LAYERED, // Item belongs to one of the layers different from the default layer
|
||||||
|
META_CULL_GROUP, // As a meta item, the culling of my sub items is based solely on my bounding box and my visibility in the view
|
||||||
|
SUB_META_CULLED, // As a sub item of a meta render item set as cull group, need to be set to my culling to the meta render it
|
||||||
|
|
||||||
FIRST_TAG_BIT, // 8 Tags available to organize the items and filter them against
|
FIRST_TAG_BIT, // 8 Tags available to organize the items and filter them against
|
||||||
LAST_TAG_BIT = FIRST_TAG_BIT + NUM_TAGS,
|
LAST_TAG_BIT = FIRST_TAG_BIT + NUM_TAGS,
|
||||||
|
@ -122,6 +124,8 @@ public:
|
||||||
Builder& withInvisible() { _flags.set(INVISIBLE); return (*this); }
|
Builder& withInvisible() { _flags.set(INVISIBLE); return (*this); }
|
||||||
Builder& withShadowCaster() { _flags.set(SHADOW_CASTER); return (*this); }
|
Builder& withShadowCaster() { _flags.set(SHADOW_CASTER); return (*this); }
|
||||||
Builder& withLayered() { _flags.set(LAYERED); return (*this); }
|
Builder& withLayered() { _flags.set(LAYERED); return (*this); }
|
||||||
|
Builder& withMetaCullGroup() { _flags.set(META_CULL_GROUP); return (*this); }
|
||||||
|
Builder& withSubMetaCulled() { _flags.set(SUB_META_CULLED); return (*this); }
|
||||||
|
|
||||||
Builder& withTag(Tag tag) { _flags.set(FIRST_TAG_BIT + tag); return (*this); }
|
Builder& withTag(Tag tag) { _flags.set(FIRST_TAG_BIT + tag); return (*this); }
|
||||||
// Set ALL the tags in one call using the Tag bits
|
// Set ALL the tags in one call using the Tag bits
|
||||||
|
@ -159,6 +163,12 @@ public:
|
||||||
bool isLayered() const { return _flags[LAYERED]; }
|
bool isLayered() const { return _flags[LAYERED]; }
|
||||||
bool isSpatial() const { return !isLayered(); }
|
bool isSpatial() const { return !isLayered(); }
|
||||||
|
|
||||||
|
bool isMetaCullGroup() const { return _flags[META_CULL_GROUP]; }
|
||||||
|
void setMetaCullGroup(bool cullGroup) { (cullGroup ? _flags.set(META_CULL_GROUP) : _flags.reset(META_CULL_GROUP)); }
|
||||||
|
|
||||||
|
bool isSubMetaCulled() const { return _flags[SUB_META_CULLED]; }
|
||||||
|
void setSubMetaCulled(bool metaCulled) { (metaCulled ? _flags.set(SUB_META_CULLED) : _flags.reset(SUB_META_CULLED)); }
|
||||||
|
|
||||||
bool isTag(Tag tag) const { return _flags[FIRST_TAG_BIT + tag]; }
|
bool isTag(Tag tag) const { return _flags[FIRST_TAG_BIT + tag]; }
|
||||||
uint8_t getTagBits() const { return ((_flags.to_ulong() & KEY_TAG_BITS_MASK) >> FIRST_TAG_BIT); }
|
uint8_t getTagBits() const { return ((_flags.to_ulong() & KEY_TAG_BITS_MASK) >> FIRST_TAG_BIT); }
|
||||||
|
|
||||||
|
@ -193,6 +203,7 @@ public:
|
||||||
ItemKey::Flags _mask{ 0 };
|
ItemKey::Flags _mask{ 0 };
|
||||||
public:
|
public:
|
||||||
Builder() {}
|
Builder() {}
|
||||||
|
Builder(const ItemFilter& srcFilter) : _value(srcFilter._value), _mask(srcFilter._mask) {}
|
||||||
|
|
||||||
ItemFilter build() const { return ItemFilter(_value, _mask); }
|
ItemFilter build() const { return ItemFilter(_value, _mask); }
|
||||||
|
|
||||||
|
@ -221,6 +232,12 @@ public:
|
||||||
Builder& withoutLayered() { _value.reset(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); }
|
Builder& withoutLayered() { _value.reset(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); }
|
||||||
Builder& withLayered() { _value.set(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); }
|
Builder& withLayered() { _value.set(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); }
|
||||||
|
|
||||||
|
Builder& withoutMetaCullGroup() { _value.reset(ItemKey::META_CULL_GROUP); _mask.set(ItemKey::META_CULL_GROUP); return (*this); }
|
||||||
|
Builder& withMetaCullGroup() { _value.set(ItemKey::META_CULL_GROUP); _mask.set(ItemKey::META_CULL_GROUP); return (*this); }
|
||||||
|
|
||||||
|
Builder& withoutSubMetaCulled() { _value.reset(ItemKey::SUB_META_CULLED); _mask.set(ItemKey::SUB_META_CULLED); return (*this); }
|
||||||
|
Builder& withSubMetaCulled() { _value.set(ItemKey::SUB_META_CULLED); _mask.set(ItemKey::SUB_META_CULLED); return (*this); }
|
||||||
|
|
||||||
Builder& withoutTag(ItemKey::Tag tagIndex) { _value.reset(ItemKey::FIRST_TAG_BIT + tagIndex); _mask.set(ItemKey::FIRST_TAG_BIT + tagIndex); return (*this); }
|
Builder& withoutTag(ItemKey::Tag tagIndex) { _value.reset(ItemKey::FIRST_TAG_BIT + tagIndex); _mask.set(ItemKey::FIRST_TAG_BIT + tagIndex); return (*this); }
|
||||||
Builder& withTag(ItemKey::Tag tagIndex) { _value.set(ItemKey::FIRST_TAG_BIT + tagIndex); _mask.set(ItemKey::FIRST_TAG_BIT + tagIndex); return (*this); }
|
Builder& withTag(ItemKey::Tag tagIndex) { _value.set(ItemKey::FIRST_TAG_BIT + tagIndex); _mask.set(ItemKey::FIRST_TAG_BIT + tagIndex); return (*this); }
|
||||||
// Set ALL the tags in one call using the Tag bits and the Tag bits touched
|
// Set ALL the tags in one call using the Tag bits and the Tag bits touched
|
||||||
|
@ -420,6 +437,7 @@ public:
|
||||||
|
|
||||||
// Meta Type Interface
|
// Meta Type Interface
|
||||||
uint32_t fetchMetaSubItems(ItemIDs& subItems) const { return _payload->fetchMetaSubItems(subItems); }
|
uint32_t fetchMetaSubItems(ItemIDs& subItems) const { return _payload->fetchMetaSubItems(subItems); }
|
||||||
|
uint32_t fetchMetaSubItemBounds(ItemBounds& subItemBounds, Scene& scene) const;
|
||||||
|
|
||||||
// Access the status
|
// Access the status
|
||||||
const StatusPointer& getStatus() const { return _payload->getStatus(); }
|
const StatusPointer& getStatus() const { return _payload->getStatus(); }
|
||||||
|
|
|
@ -30,7 +30,7 @@ void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varyin
|
||||||
const auto culledSpatialSelection = task.addJob<CullSpatialSelection>("CullSceneSelection", cullInputs, cullFunctor, RenderDetails::ITEM);
|
const auto culledSpatialSelection = task.addJob<CullSpatialSelection>("CullSceneSelection", cullInputs, cullFunctor, RenderDetails::ITEM);
|
||||||
|
|
||||||
// Overlays are not culled
|
// Overlays are not culled
|
||||||
const ItemFilter overlayfilter = ItemFilter::Builder().withVisible().withTagBits(tagBits, tagMask);
|
const ItemFilter overlayfilter = ItemFilter::Builder().withVisible().withoutSubMetaCulled().withTagBits(tagBits, tagMask);
|
||||||
const auto nonspatialFilter = render::Varying(overlayfilter);
|
const auto nonspatialFilter = render::Varying(overlayfilter);
|
||||||
const auto nonspatialSelection = task.addJob<FetchNonspatialItems>("FetchOverlaySelection", nonspatialFilter);
|
const auto nonspatialSelection = task.addJob<FetchNonspatialItems>("FetchOverlaySelection", nonspatialFilter);
|
||||||
|
|
||||||
|
|
|
@ -207,6 +207,10 @@ public:
|
||||||
|
|
||||||
void dump(const QString& prefix = "") const;
|
void dump(const QString& prefix = "") const;
|
||||||
|
|
||||||
|
virtual void locationChanged(bool tellPhysics = true); // called when a this object's location has changed
|
||||||
|
virtual void dimensionsChanged() { _queryAACubeSet = false; } // called when a this object's dimensions have changed
|
||||||
|
virtual void parentDeleted() { } // called on children of a deleted parent
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
const NestableType _nestableType; // EntityItem or an AvatarData
|
const NestableType _nestableType; // EntityItem or an AvatarData
|
||||||
QUuid _id;
|
QUuid _id;
|
||||||
|
@ -218,10 +222,6 @@ protected:
|
||||||
mutable ReadWriteLockable _childrenLock;
|
mutable ReadWriteLockable _childrenLock;
|
||||||
mutable QHash<QUuid, SpatiallyNestableWeakPointer> _children;
|
mutable QHash<QUuid, SpatiallyNestableWeakPointer> _children;
|
||||||
|
|
||||||
virtual void locationChanged(bool tellPhysics = true); // called when a this object's location has changed
|
|
||||||
virtual void dimensionsChanged() { _queryAACubeSet = false; } // called when a this object's dimensions have changed
|
|
||||||
virtual void parentDeleted() { } // called on children of a deleted parent
|
|
||||||
|
|
||||||
// _queryAACube is used to decide where something lives in the octree
|
// _queryAACube is used to decide where something lives in the octree
|
||||||
mutable AACube _queryAACube;
|
mutable AACube _queryAACube;
|
||||||
mutable bool _queryAACubeSet { false };
|
mutable bool _queryAACubeSet { false };
|
||||||
|
|
|
@ -485,7 +485,7 @@ bool OpenVrDisplayPlugin::internalActivate() {
|
||||||
if (_threadedSubmit) {
|
if (_threadedSubmit) {
|
||||||
_submitThread = std::make_shared<OpenVrSubmitThread>(*this);
|
_submitThread = std::make_shared<OpenVrSubmitThread>(*this);
|
||||||
if (!_submitCanvas) {
|
if (!_submitCanvas) {
|
||||||
withMainThreadContext([&] {
|
withOtherThreadContext([&] {
|
||||||
_submitCanvas = std::make_shared<gl::OffscreenContext>();
|
_submitCanvas = std::make_shared<gl::OffscreenContext>();
|
||||||
_submitCanvas->create();
|
_submitCanvas->create();
|
||||||
_submitCanvas->doneCurrent();
|
_submitCanvas->doneCurrent();
|
||||||
|
|
|
@ -47,7 +47,7 @@ function calcSpawnInfo(hand, landscape) {
|
||||||
var headPos = (HMD.active && Camera.mode === "first person") ? HMD.position : Camera.position;
|
var headPos = (HMD.active && Camera.mode === "first person") ? HMD.position : Camera.position;
|
||||||
var headRot = (HMD.active && Camera.mode === "first person") ? HMD.orientation : Camera.orientation;
|
var headRot = (HMD.active && Camera.mode === "first person") ? HMD.orientation : Camera.orientation;
|
||||||
|
|
||||||
var forward = Quat.getForward(headRot);
|
var forward = Quat.getForward(Quat.cancelOutRollAndPitch(headRot));
|
||||||
var FORWARD_OFFSET = 0.5 * MyAvatar.sensorToWorldScale;
|
var FORWARD_OFFSET = 0.5 * MyAvatar.sensorToWorldScale;
|
||||||
finalPosition = Vec3.sum(headPos, Vec3.multiply(FORWARD_OFFSET, forward));
|
finalPosition = Vec3.sum(headPos, Vec3.multiply(FORWARD_OFFSET, forward));
|
||||||
var orientation = Quat.lookAt({x: 0, y: 0, z: 0}, forward, Vec3.multiplyQbyV(MyAvatar.orientation, Vec3.UNIT_Y));
|
var orientation = Quat.lookAt({x: 0, y: 0, z: 0}, forward, Vec3.multiplyQbyV(MyAvatar.orientation, Vec3.UNIT_Y));
|
||||||
|
@ -269,8 +269,9 @@ WebTablet.prototype.setLandscape = function(newLandscapeValue) {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.landscape = newLandscapeValue;
|
this.landscape = newLandscapeValue;
|
||||||
|
var cameraOrientation = Quat.cancelOutRollAndPitch(Camera.orientation);
|
||||||
Overlays.editOverlay(this.tabletEntityID,
|
Overlays.editOverlay(this.tabletEntityID,
|
||||||
{ rotation: Quat.multiply(Camera.orientation, this.landscape ? ROT_LANDSCAPE : ROT_Y_180) });
|
{ rotation: Quat.multiply(cameraOrientation, this.landscape ? ROT_LANDSCAPE : ROT_Y_180) });
|
||||||
|
|
||||||
var tabletWidth = getTabletWidthFromSettings() * MyAvatar.sensorToWorldScale;
|
var tabletWidth = getTabletWidthFromSettings() * MyAvatar.sensorToWorldScale;
|
||||||
var tabletScaleFactor = tabletWidth / TABLET_NATURAL_DIMENSIONS.x;
|
var tabletScaleFactor = tabletWidth / TABLET_NATURAL_DIMENSIONS.x;
|
||||||
|
@ -278,7 +279,7 @@ WebTablet.prototype.setLandscape = function(newLandscapeValue) {
|
||||||
var screenWidth = 0.82 * tabletWidth;
|
var screenWidth = 0.82 * tabletWidth;
|
||||||
var screenHeight = 0.81 * tabletHeight;
|
var screenHeight = 0.81 * tabletHeight;
|
||||||
Overlays.editOverlay(this.webOverlayID, {
|
Overlays.editOverlay(this.webOverlayID, {
|
||||||
rotation: Quat.multiply(Camera.orientation, ROT_LANDSCAPE_WINDOW),
|
rotation: Quat.multiply(cameraOrientation, ROT_LANDSCAPE_WINDOW),
|
||||||
dimensions: {x: this.landscape ? screenHeight : screenWidth, y: this.landscape ? screenWidth : screenHeight, z: 0.1}
|
dimensions: {x: this.landscape ? screenHeight : screenWidth, y: this.landscape ? screenWidth : screenHeight, z: 0.1}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue