mirror of
https://github.com/lubosz/overte.git
synced 2025-04-26 16:55:31 +02:00
Merge branch 'master' into openurl
This commit is contained in:
commit
ef64de7c04
102 changed files with 2632 additions and 1146 deletions
assignment-client/src/assets
cmake/externals/LibOVR
interface
resources/qml
src
libraries
animation/src
baking/src
display-plugins/src/display-plugins
entities/src
fbx/src
gpu/src/gpu
material-networking/src/material-networking
model-baker/src/model-baker
model-networking/src/model-networking
networking/src
oculusMobile/src/ovr
plugins/src/plugins
render-utils/src
render/src/render
shared/src
plugins
oculus
openvr/src
scripts
developer/utilities
lib
jet
prop
render
system
tools
unpublishedScripts/marketplace/record/assets
|
@ -107,6 +107,10 @@ BakeVersion currentBakeVersionForAssetType(BakedAssetType type) {
|
|||
}
|
||||
}
|
||||
|
||||
QString getBakeMapping(const AssetUtils::AssetHash& hash, const QString& relativeFilePath) {
|
||||
return AssetUtils::HIDDEN_BAKED_CONTENT_FOLDER + hash + "/" + relativeFilePath;
|
||||
}
|
||||
|
||||
const QString ASSET_SERVER_LOGGING_TARGET_NAME = "asset-server";
|
||||
|
||||
void AssetServer::bakeAsset(const AssetUtils::AssetHash& assetHash, const AssetUtils::AssetPath& assetPath, const QString& filePath) {
|
||||
|
@ -141,26 +145,27 @@ std::pair<AssetUtils::BakingStatus, QString> AssetServer::getAssetStatus(const A
|
|||
return { AssetUtils::Baked, "" };
|
||||
}
|
||||
|
||||
auto dotIndex = path.lastIndexOf(".");
|
||||
if (dotIndex == -1) {
|
||||
BakedAssetType type = assetTypeForFilename(path);
|
||||
if (type == BakedAssetType::Undefined) {
|
||||
return { AssetUtils::Irrelevant, "" };
|
||||
}
|
||||
|
||||
auto extension = path.mid(dotIndex + 1);
|
||||
bool loaded;
|
||||
AssetMeta meta;
|
||||
std::tie(loaded, meta) = readMetaFile(hash);
|
||||
|
||||
QString bakedFilename;
|
||||
|
||||
if (BAKEABLE_MODEL_EXTENSIONS.contains(extension)) {
|
||||
bakedFilename = BAKED_MODEL_SIMPLE_NAME;
|
||||
} else if (BAKEABLE_TEXTURE_EXTENSIONS.contains(extension.toLocal8Bit()) && hasMetaFile(hash)) {
|
||||
bakedFilename = BAKED_TEXTURE_SIMPLE_NAME;
|
||||
} else if (BAKEABLE_SCRIPT_EXTENSIONS.contains(extension)) {
|
||||
bakedFilename = BAKED_SCRIPT_SIMPLE_NAME;
|
||||
} else {
|
||||
// We create a meta file for Skyboxes at runtime when they get requested
|
||||
// Otherwise, textures don't get baked by themselves.
|
||||
if (type == BakedAssetType::Texture && !loaded) {
|
||||
return { AssetUtils::Irrelevant, "" };
|
||||
}
|
||||
|
||||
auto bakedPath = AssetUtils::HIDDEN_BAKED_CONTENT_FOLDER + hash + "/" + bakedFilename;
|
||||
QString bakedFilename = bakedFilenameForAssetType(type);
|
||||
auto bakedPath = getBakeMapping(hash, bakedFilename);
|
||||
if (loaded && !meta.redirectTarget.isEmpty()) {
|
||||
bakedPath = meta.redirectTarget;
|
||||
}
|
||||
|
||||
auto jt = _fileMappings.find(bakedPath);
|
||||
if (jt != _fileMappings.end()) {
|
||||
if (jt->second == hash) {
|
||||
|
@ -168,14 +173,8 @@ std::pair<AssetUtils::BakingStatus, QString> AssetServer::getAssetStatus(const A
|
|||
} else {
|
||||
return { AssetUtils::Baked, "" };
|
||||
}
|
||||
} else {
|
||||
bool loaded;
|
||||
AssetMeta meta;
|
||||
|
||||
std::tie(loaded, meta) = readMetaFile(hash);
|
||||
if (loaded && meta.failedLastBake) {
|
||||
return { AssetUtils::Error, meta.lastBakeErrors };
|
||||
}
|
||||
} else if (loaded && meta.failedLastBake) {
|
||||
return { AssetUtils::Error, meta.lastBakeErrors };
|
||||
}
|
||||
|
||||
return { AssetUtils::Pending, "" };
|
||||
|
@ -227,8 +226,16 @@ bool AssetServer::needsToBeBaked(const AssetUtils::AssetPath& path, const AssetU
|
|||
return false;
|
||||
}
|
||||
|
||||
bool loaded;
|
||||
AssetMeta meta;
|
||||
std::tie(loaded, meta) = readMetaFile(assetHash);
|
||||
|
||||
QString bakedFilename = bakedFilenameForAssetType(type);
|
||||
auto bakedPath = AssetUtils::HIDDEN_BAKED_CONTENT_FOLDER + assetHash + "/" + bakedFilename;
|
||||
auto bakedPath = getBakeMapping(assetHash, bakedFilename);
|
||||
if (loaded && !meta.redirectTarget.isEmpty()) {
|
||||
bakedPath = meta.redirectTarget;
|
||||
}
|
||||
|
||||
auto mappingIt = _fileMappings.find(bakedPath);
|
||||
bool bakedMappingExists = mappingIt != _fileMappings.end();
|
||||
|
||||
|
@ -238,10 +245,8 @@ bool AssetServer::needsToBeBaked(const AssetUtils::AssetPath& path, const AssetU
|
|||
return false;
|
||||
}
|
||||
|
||||
bool loaded;
|
||||
AssetMeta meta;
|
||||
std::tie(loaded, meta) = readMetaFile(assetHash);
|
||||
|
||||
// We create a meta file for Skyboxes at runtime when they get requested
|
||||
// Otherwise, textures don't get baked by themselves.
|
||||
if (type == BakedAssetType::Texture && !loaded) {
|
||||
return false;
|
||||
}
|
||||
|
@ -633,36 +638,33 @@ void AssetServer::handleGetMappingOperation(ReceivedMessage& message, NLPacketLi
|
|||
if (it != _fileMappings.end()) {
|
||||
|
||||
// check if we should re-direct to a baked asset
|
||||
|
||||
// first, figure out from the mapping extension what type of file this is
|
||||
auto assetPathExtension = assetPath.mid(assetPath.lastIndexOf('.') + 1).toLower();
|
||||
|
||||
auto type = assetTypeForFilename(assetPath);
|
||||
QString bakedRootFile = bakedFilenameForAssetType(type);
|
||||
|
||||
auto originalAssetHash = it->second;
|
||||
QString redirectedAssetHash;
|
||||
QString bakedAssetPath;
|
||||
quint8 wasRedirected = false;
|
||||
bool bakingDisabled = false;
|
||||
|
||||
if (!bakedRootFile.isEmpty()) {
|
||||
// we ran into an asset for which we could have a baked version, let's check if it's ready
|
||||
bakedAssetPath = AssetUtils::HIDDEN_BAKED_CONTENT_FOLDER + originalAssetHash + "/" + bakedRootFile;
|
||||
auto bakedIt = _fileMappings.find(bakedAssetPath);
|
||||
bool loaded;
|
||||
AssetMeta meta;
|
||||
std::tie(loaded, meta) = readMetaFile(originalAssetHash);
|
||||
|
||||
if (bakedIt != _fileMappings.end()) {
|
||||
if (bakedIt->second != originalAssetHash) {
|
||||
qDebug() << "Did find baked version for: " << originalAssetHash << assetPath;
|
||||
// we found a baked version of the requested asset to serve, redirect to that
|
||||
redirectedAssetHash = bakedIt->second;
|
||||
wasRedirected = true;
|
||||
} else {
|
||||
qDebug() << "Did not find baked version for: " << originalAssetHash << assetPath << " (disabled)";
|
||||
bakingDisabled = true;
|
||||
}
|
||||
auto type = assetTypeForFilename(assetPath);
|
||||
QString bakedRootFile = bakedFilenameForAssetType(type);
|
||||
QString bakedAssetPath = getBakeMapping(originalAssetHash, bakedRootFile);
|
||||
|
||||
if (loaded && !meta.redirectTarget.isEmpty()) {
|
||||
bakedAssetPath = meta.redirectTarget;
|
||||
}
|
||||
|
||||
auto bakedIt = _fileMappings.find(bakedAssetPath);
|
||||
if (bakedIt != _fileMappings.end()) {
|
||||
if (bakedIt->second != originalAssetHash) {
|
||||
qDebug() << "Did find baked version for: " << originalAssetHash << assetPath;
|
||||
// we found a baked version of the requested asset to serve, redirect to that
|
||||
redirectedAssetHash = bakedIt->second;
|
||||
wasRedirected = true;
|
||||
} else {
|
||||
qDebug() << "Did not find baked version for: " << originalAssetHash << assetPath;
|
||||
qDebug() << "Did not find baked version for: " << originalAssetHash << assetPath << " (disabled)";
|
||||
bakingDisabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -684,20 +686,13 @@ void AssetServer::handleGetMappingOperation(ReceivedMessage& message, NLPacketLi
|
|||
|
||||
auto query = QUrlQuery(url.query());
|
||||
bool isSkybox = query.hasQueryItem("skybox");
|
||||
if (isSkybox) {
|
||||
bool loaded;
|
||||
AssetMeta meta;
|
||||
std::tie(loaded, meta) = readMetaFile(originalAssetHash);
|
||||
|
||||
if (!loaded) {
|
||||
AssetMeta needsBakingMeta;
|
||||
needsBakingMeta.bakeVersion = NEEDS_BAKING_BAKE_VERSION;
|
||||
|
||||
writeMetaFile(originalAssetHash, needsBakingMeta);
|
||||
if (!bakingDisabled) {
|
||||
maybeBake(assetPath, originalAssetHash);
|
||||
}
|
||||
if (isSkybox && !loaded) {
|
||||
AssetMeta needsBakingMeta;
|
||||
needsBakingMeta.bakeVersion = NEEDS_BAKING_BAKE_VERSION;
|
||||
|
||||
writeMetaFile(originalAssetHash, needsBakingMeta);
|
||||
if (!bakingDisabled) {
|
||||
maybeBake(assetPath, originalAssetHash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1297,14 +1292,6 @@ bool AssetServer::renameMapping(AssetUtils::AssetPath oldPath, AssetUtils::Asset
|
|||
}
|
||||
}
|
||||
|
||||
static const QString BAKED_ASSET_SIMPLE_FBX_NAME = "asset.fbx";
|
||||
static const QString BAKED_ASSET_SIMPLE_TEXTURE_NAME = "texture.ktx";
|
||||
static const QString BAKED_ASSET_SIMPLE_JS_NAME = "asset.js";
|
||||
|
||||
QString getBakeMapping(const AssetUtils::AssetHash& hash, const QString& relativeFilePath) {
|
||||
return AssetUtils::HIDDEN_BAKED_CONTENT_FOLDER + hash + "/" + relativeFilePath;
|
||||
}
|
||||
|
||||
void AssetServer::handleFailedBake(QString originalAssetHash, QString assetPath, QString errors) {
|
||||
qDebug() << "Failed to bake: " << originalAssetHash << assetPath << "(" << errors << ")";
|
||||
|
||||
|
@ -1326,12 +1313,78 @@ void AssetServer::handleFailedBake(QString originalAssetHash, QString assetPath,
|
|||
}
|
||||
|
||||
void AssetServer::handleCompletedBake(QString originalAssetHash, QString originalAssetPath,
|
||||
QString bakedTempOutputDir, QVector<QString> bakedFilePaths) {
|
||||
QString bakedTempOutputDir) {
|
||||
auto reportCompletion = [this, originalAssetPath, originalAssetHash](bool errorCompletingBake,
|
||||
QString errorReason,
|
||||
QString redirectTarget) {
|
||||
auto type = assetTypeForFilename(originalAssetPath);
|
||||
auto currentTypeVersion = currentBakeVersionForAssetType(type);
|
||||
|
||||
AssetMeta meta;
|
||||
meta.bakeVersion = currentTypeVersion;
|
||||
meta.failedLastBake = errorCompletingBake;
|
||||
meta.redirectTarget = redirectTarget;
|
||||
|
||||
if (errorCompletingBake) {
|
||||
qWarning() << "Could not complete bake for" << originalAssetHash;
|
||||
meta.lastBakeErrors = errorReason;
|
||||
}
|
||||
|
||||
writeMetaFile(originalAssetHash, meta);
|
||||
|
||||
_pendingBakes.remove(originalAssetHash);
|
||||
};
|
||||
|
||||
bool errorCompletingBake { false };
|
||||
QString errorReason;
|
||||
QString redirectTarget;
|
||||
|
||||
qDebug() << "Completing bake for " << originalAssetHash;
|
||||
|
||||
// Find the directory containing the baked content
|
||||
QDir outputDir(bakedTempOutputDir);
|
||||
QString outputDirName = outputDir.dirName();
|
||||
auto directories = outputDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
QString bakedDirectoryPath;
|
||||
for (const auto& dirName : directories) {
|
||||
outputDir.cd(dirName);
|
||||
if (outputDir.exists("baked") && outputDir.exists("original")) {
|
||||
bakedDirectoryPath = outputDir.filePath("baked");
|
||||
break;
|
||||
}
|
||||
outputDir.cdUp();
|
||||
}
|
||||
if (bakedDirectoryPath.isEmpty()) {
|
||||
errorCompletingBake = true;
|
||||
errorReason = "Failed to find baking output";
|
||||
|
||||
// Cleanup temporary output directory
|
||||
PathUtils::deleteMyTemporaryDir(outputDirName);
|
||||
reportCompletion(errorCompletingBake, errorReason, redirectTarget);
|
||||
return;
|
||||
}
|
||||
|
||||
// Compile list of all the baked files
|
||||
QDirIterator it(bakedDirectoryPath, QDirIterator::Subdirectories);
|
||||
QVector<QString> bakedFilePaths;
|
||||
while (it.hasNext()) {
|
||||
it.next();
|
||||
if (it.fileInfo().isFile()) {
|
||||
bakedFilePaths.push_back(it.filePath());
|
||||
}
|
||||
}
|
||||
if (bakedFilePaths.isEmpty()) {
|
||||
errorCompletingBake = true;
|
||||
errorReason = "Baking output has no files";
|
||||
|
||||
// Cleanup temporary output directory
|
||||
PathUtils::deleteMyTemporaryDir(outputDirName);
|
||||
reportCompletion(errorCompletingBake, errorReason, redirectTarget);
|
||||
return;
|
||||
}
|
||||
|
||||
QDir bakedDirectory(bakedDirectoryPath);
|
||||
|
||||
for (auto& filePath : bakedFilePaths) {
|
||||
// figure out the hash for the contents of this file
|
||||
QFile file(filePath);
|
||||
|
@ -1340,89 +1393,72 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina
|
|||
|
||||
AssetUtils::AssetHash bakedFileHash;
|
||||
|
||||
if (file.open(QIODevice::ReadOnly)) {
|
||||
QCryptographicHash hasher(QCryptographicHash::Sha256);
|
||||
|
||||
if (hasher.addData(&file)) {
|
||||
bakedFileHash = hasher.result().toHex();
|
||||
} else {
|
||||
// stop handling this bake, couldn't hash the contents of the file
|
||||
errorCompletingBake = true;
|
||||
errorReason = "Failed to finalize bake";
|
||||
break;
|
||||
}
|
||||
|
||||
// first check that we don't already have this bake file in our list
|
||||
auto bakeFileDestination = _filesDirectory.absoluteFilePath(bakedFileHash);
|
||||
if (!QFile::exists(bakeFileDestination)) {
|
||||
// copy each to our files folder (with the hash as their filename)
|
||||
if (!file.copy(_filesDirectory.absoluteFilePath(bakedFileHash))) {
|
||||
// stop handling this bake, couldn't copy the bake file into our files directory
|
||||
errorCompletingBake = true;
|
||||
errorReason = "Failed to copy baked assets to asset server";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// setup the mapping for this bake file
|
||||
auto relativeFilePath = QUrl(filePath).fileName();
|
||||
qDebug() << "Relative file path is: " << relativeFilePath;
|
||||
if (relativeFilePath.endsWith(".fbx", Qt::CaseInsensitive)) {
|
||||
// for an FBX file, we replace the filename with the simple name
|
||||
// (to handle the case where two mapped assets have the same hash but different names)
|
||||
relativeFilePath = BAKED_ASSET_SIMPLE_FBX_NAME;
|
||||
} else if (relativeFilePath.endsWith(".js", Qt::CaseInsensitive)) {
|
||||
relativeFilePath = BAKED_ASSET_SIMPLE_JS_NAME;
|
||||
} else if (!originalAssetPath.endsWith(".fbx", Qt::CaseInsensitive)) {
|
||||
relativeFilePath = BAKED_ASSET_SIMPLE_TEXTURE_NAME;
|
||||
}
|
||||
|
||||
QString bakeMapping = getBakeMapping(originalAssetHash, relativeFilePath);
|
||||
|
||||
// add a mapping (under the hidden baked folder) for this file resulting from the bake
|
||||
if (setMapping(bakeMapping, bakedFileHash)) {
|
||||
qDebug() << "Added" << bakeMapping << "for bake file" << bakedFileHash << "from bake of" << originalAssetHash;
|
||||
} else {
|
||||
qDebug() << "Failed to set mapping";
|
||||
// stop handling this bake, couldn't add a mapping for this bake file
|
||||
errorCompletingBake = true;
|
||||
errorReason = "Failed to finalize bake";
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
qDebug() << "Failed to open baked file: " << filePath;
|
||||
// stop handling this bake, we couldn't open one of the files for reading
|
||||
errorCompletingBake = true;
|
||||
errorReason = "Failed to finalize bake";
|
||||
errorReason = "Could not open baked file " + file.fileName();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& filePath : bakedFilePaths) {
|
||||
QFile file(filePath);
|
||||
if (!file.remove()) {
|
||||
qWarning() << "Failed to remove temporary file:" << filePath;
|
||||
QCryptographicHash hasher(QCryptographicHash::Sha256);
|
||||
|
||||
if (!hasher.addData(&file)) {
|
||||
// stop handling this bake, couldn't hash the contents of the file
|
||||
errorCompletingBake = true;
|
||||
errorReason = "Could not hash data for " + file.fileName();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!QDir(bakedTempOutputDir).rmdir(".")) {
|
||||
qWarning() << "Failed to remove temporary directory:" << bakedTempOutputDir;
|
||||
|
||||
bakedFileHash = hasher.result().toHex();
|
||||
|
||||
// first check that we don't already have this bake file in our list
|
||||
auto bakeFileDestination = _filesDirectory.absoluteFilePath(bakedFileHash);
|
||||
if (!QFile::exists(bakeFileDestination)) {
|
||||
// copy each to our files folder (with the hash as their filename)
|
||||
if (!file.copy(_filesDirectory.absoluteFilePath(bakedFileHash))) {
|
||||
// stop handling this bake, couldn't copy the bake file into our files directory
|
||||
errorCompletingBake = true;
|
||||
errorReason = "Failed to copy baked assets to asset server";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// setup the mapping for this bake file
|
||||
auto relativeFilePath = bakedDirectory.relativeFilePath(filePath);
|
||||
|
||||
QString bakeMapping = getBakeMapping(originalAssetHash, relativeFilePath);
|
||||
|
||||
// Check if this is the file we should redirect to when someone asks for the original asset
|
||||
if ((relativeFilePath.endsWith(".baked.fst", Qt::CaseInsensitive) && originalAssetPath.endsWith(".fbx")) ||
|
||||
(relativeFilePath.endsWith(".texmeta.json", Qt::CaseInsensitive) && !originalAssetPath.endsWith(".fbx"))) {
|
||||
if (!redirectTarget.isEmpty()) {
|
||||
qWarning() << "Found multiple baked redirect target for" << originalAssetPath;
|
||||
}
|
||||
redirectTarget = bakeMapping;
|
||||
}
|
||||
|
||||
// add a mapping (under the hidden baked folder) for this file resulting from the bake
|
||||
if (!setMapping(bakeMapping, bakedFileHash)) {
|
||||
qDebug() << "Failed to set mapping";
|
||||
// stop handling this bake, couldn't add a mapping for this bake file
|
||||
errorCompletingBake = true;
|
||||
errorReason = "Failed to set mapping for baked file " + file.fileName();
|
||||
break;
|
||||
}
|
||||
|
||||
qDebug() << "Added" << bakeMapping << "for bake file" << bakedFileHash << "from bake of" << originalAssetHash;
|
||||
}
|
||||
|
||||
auto type = assetTypeForFilename(originalAssetPath);
|
||||
auto currentTypeVersion = currentBakeVersionForAssetType(type);
|
||||
|
||||
AssetMeta meta;
|
||||
meta.bakeVersion = currentTypeVersion;
|
||||
meta.failedLastBake = errorCompletingBake;
|
||||
|
||||
if (errorCompletingBake) {
|
||||
qWarning() << "Could not complete bake for" << originalAssetHash;
|
||||
meta.lastBakeErrors = errorReason;
|
||||
if (redirectTarget.isEmpty()) {
|
||||
errorCompletingBake = true;
|
||||
errorReason = "Could not find root file for baked output";
|
||||
}
|
||||
|
||||
writeMetaFile(originalAssetHash, meta);
|
||||
|
||||
_pendingBakes.remove(originalAssetHash);
|
||||
// Cleanup temporary output directory
|
||||
PathUtils::deleteMyTemporaryDir(outputDirName);
|
||||
reportCompletion(errorCompletingBake, errorReason, redirectTarget);
|
||||
}
|
||||
|
||||
void AssetServer::handleAbortedBake(QString originalAssetHash, QString assetPath) {
|
||||
|
@ -1435,6 +1471,7 @@ void AssetServer::handleAbortedBake(QString originalAssetHash, QString assetPath
|
|||
static const QString BAKE_VERSION_KEY = "bake_version";
|
||||
static const QString FAILED_LAST_BAKE_KEY = "failed_last_bake";
|
||||
static const QString LAST_BAKE_ERRORS_KEY = "last_bake_errors";
|
||||
static const QString REDIRECT_TARGET_KEY = "redirect_target";
|
||||
|
||||
std::pair<bool, AssetMeta> AssetServer::readMetaFile(AssetUtils::AssetHash hash) {
|
||||
auto metaFilePath = AssetUtils::HIDDEN_BAKED_CONTENT_FOLDER + hash + "/" + "meta.json";
|
||||
|
@ -1461,6 +1498,7 @@ std::pair<bool, AssetMeta> AssetServer::readMetaFile(AssetUtils::AssetHash hash)
|
|||
auto bakeVersion = root[BAKE_VERSION_KEY];
|
||||
auto failedLastBake = root[FAILED_LAST_BAKE_KEY];
|
||||
auto lastBakeErrors = root[LAST_BAKE_ERRORS_KEY];
|
||||
auto redirectTarget = root[REDIRECT_TARGET_KEY];
|
||||
|
||||
if (bakeVersion.isDouble()
|
||||
&& failedLastBake.isBool()
|
||||
|
@ -1470,6 +1508,7 @@ std::pair<bool, AssetMeta> AssetServer::readMetaFile(AssetUtils::AssetHash hash)
|
|||
meta.bakeVersion = bakeVersion.toInt();
|
||||
meta.failedLastBake = failedLastBake.toBool();
|
||||
meta.lastBakeErrors = lastBakeErrors.toString();
|
||||
meta.redirectTarget = redirectTarget.toString();
|
||||
|
||||
return { true, meta };
|
||||
} else {
|
||||
|
@ -1488,6 +1527,7 @@ bool AssetServer::writeMetaFile(AssetUtils::AssetHash originalAssetHash, const A
|
|||
metaFileObject[BAKE_VERSION_KEY] = (int)meta.bakeVersion;
|
||||
metaFileObject[FAILED_LAST_BAKE_KEY] = meta.failedLastBake;
|
||||
metaFileObject[LAST_BAKE_ERRORS_KEY] = meta.lastBakeErrors;
|
||||
metaFileObject[REDIRECT_TARGET_KEY] = meta.redirectTarget;
|
||||
|
||||
QJsonDocument metaFileDoc;
|
||||
metaFileDoc.setObject(metaFileObject);
|
||||
|
@ -1521,10 +1561,18 @@ bool AssetServer::setBakingEnabled(const AssetUtils::AssetPathList& paths, bool
|
|||
if (type == BakedAssetType::Undefined) {
|
||||
continue;
|
||||
}
|
||||
QString bakedFilename = bakedFilenameForAssetType(type);
|
||||
|
||||
auto hash = it->second;
|
||||
|
||||
bool loaded;
|
||||
AssetMeta meta;
|
||||
std::tie(loaded, meta) = readMetaFile(hash);
|
||||
|
||||
QString bakedFilename = bakedFilenameForAssetType(type);
|
||||
auto bakedMapping = getBakeMapping(hash, bakedFilename);
|
||||
if (loaded && !meta.redirectTarget.isEmpty()) {
|
||||
bakedMapping = meta.redirectTarget;
|
||||
}
|
||||
|
||||
auto it = _fileMappings.find(bakedMapping);
|
||||
bool currentlyDisabled = (it != _fileMappings.end() && it->second == hash);
|
||||
|
|
|
@ -62,12 +62,10 @@ enum class ScriptBakeVersion : BakeVersion {
|
|||
};
|
||||
|
||||
struct AssetMeta {
|
||||
AssetMeta() {
|
||||
}
|
||||
|
||||
BakeVersion bakeVersion { INITIAL_BAKE_VERSION };
|
||||
bool failedLastBake { false };
|
||||
QString lastBakeErrors;
|
||||
QString redirectTarget;
|
||||
};
|
||||
|
||||
class BakeAssetTask;
|
||||
|
@ -139,8 +137,7 @@ private:
|
|||
void bakeAsset(const AssetUtils::AssetHash& assetHash, const AssetUtils::AssetPath& assetPath, const QString& filePath);
|
||||
|
||||
/// Move baked content for asset to baked directory and update baked status
|
||||
void handleCompletedBake(QString originalAssetHash, QString assetPath, QString bakedTempOutputDir,
|
||||
QVector<QString> bakedFilePaths);
|
||||
void handleCompletedBake(QString originalAssetHash, QString assetPath, QString bakedTempOutputDir);
|
||||
void handleFailedBake(QString originalAssetHash, QString assetPath, QString errors);
|
||||
void handleAbortedBake(QString originalAssetHash, QString assetPath);
|
||||
|
||||
|
|
|
@ -36,33 +36,38 @@ BakeAssetTask::BakeAssetTask(const AssetUtils::AssetHash& assetHash, const Asset
|
|||
});
|
||||
}
|
||||
|
||||
void cleanupTempFiles(QString tempOutputDir, std::vector<QString> files) {
|
||||
for (const auto& filename : files) {
|
||||
QFile f { filename };
|
||||
if (!f.remove()) {
|
||||
qDebug() << "Failed to remove:" << filename;
|
||||
}
|
||||
}
|
||||
if (!tempOutputDir.isEmpty()) {
|
||||
QDir dir { tempOutputDir };
|
||||
if (!dir.rmdir(".")) {
|
||||
qDebug() << "Failed to remove temporary directory:" << tempOutputDir;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void BakeAssetTask::run() {
|
||||
if (_isBaking.exchange(true)) {
|
||||
qWarning() << "Tried to start bake asset task while already baking";
|
||||
return;
|
||||
}
|
||||
|
||||
// Make a new temporary directory for the Oven to work in
|
||||
QString tempOutputDir = PathUtils::generateTemporaryDir();
|
||||
QString tempOutputDirName = QDir(tempOutputDir).dirName();
|
||||
if (tempOutputDir.isEmpty()) {
|
||||
QString errors = "Could not create temporary working directory";
|
||||
emit bakeFailed(_assetHash, _assetPath, errors);
|
||||
PathUtils::deleteMyTemporaryDir(tempOutputDirName);
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy file to bake the temporary dir and give a name the oven can work with
|
||||
auto assetName = _assetPath.split("/").last();
|
||||
auto tempAssetPath = tempOutputDir + "/" + assetName;
|
||||
auto success = QFile::copy(_filePath, tempAssetPath);
|
||||
if (!success) {
|
||||
QString errors = "Couldn't copy file to bake to temporary directory";
|
||||
emit bakeFailed(_assetHash, _assetPath, errors);
|
||||
PathUtils::deleteMyTemporaryDir(tempOutputDirName);
|
||||
return;
|
||||
}
|
||||
|
||||
auto base = QFileInfo(QCoreApplication::applicationFilePath()).absoluteDir();
|
||||
QString path = base.absolutePath() + "/oven";
|
||||
QString extension = _assetPath.mid(_assetPath.lastIndexOf('.') + 1);
|
||||
QStringList args {
|
||||
"-i", _filePath,
|
||||
"-i", tempAssetPath,
|
||||
"-o", tempOutputDir,
|
||||
"-t", extension,
|
||||
};
|
||||
|
@ -72,10 +77,11 @@ void BakeAssetTask::run() {
|
|||
QEventLoop loop;
|
||||
|
||||
connect(_ovenProcess.get(), static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
|
||||
this, [&loop, this, tempOutputDir](int exitCode, QProcess::ExitStatus exitStatus) {
|
||||
this, [&loop, this, tempOutputDir, tempAssetPath, tempOutputDirName](int exitCode, QProcess::ExitStatus exitStatus) {
|
||||
qDebug() << "Baking process finished: " << exitCode << exitStatus;
|
||||
|
||||
if (exitStatus == QProcess::CrashExit) {
|
||||
PathUtils::deleteMyTemporaryDir(tempOutputDirName);
|
||||
if (_wasAborted) {
|
||||
emit bakeAborted(_assetHash, _assetPath);
|
||||
} else {
|
||||
|
@ -83,16 +89,10 @@ void BakeAssetTask::run() {
|
|||
emit bakeFailed(_assetHash, _assetPath, errors);
|
||||
}
|
||||
} else if (exitCode == OVEN_STATUS_CODE_SUCCESS) {
|
||||
QDir outputDir = tempOutputDir;
|
||||
auto files = outputDir.entryInfoList(QDir::Files);
|
||||
QVector<QString> outputFiles;
|
||||
for (auto& file : files) {
|
||||
outputFiles.push_back(file.absoluteFilePath());
|
||||
}
|
||||
|
||||
emit bakeComplete(_assetHash, _assetPath, tempOutputDir, outputFiles);
|
||||
emit bakeComplete(_assetHash, _assetPath, tempOutputDir);
|
||||
} else if (exitStatus == QProcess::NormalExit && exitCode == OVEN_STATUS_CODE_ABORT) {
|
||||
_wasAborted.store(true);
|
||||
PathUtils::deleteMyTemporaryDir(tempOutputDirName);
|
||||
emit bakeAborted(_assetHash, _assetPath);
|
||||
} else {
|
||||
QString errors;
|
||||
|
@ -107,6 +107,7 @@ void BakeAssetTask::run() {
|
|||
errors = "Unknown error occurred while baking";
|
||||
}
|
||||
}
|
||||
PathUtils::deleteMyTemporaryDir(tempOutputDirName);
|
||||
emit bakeFailed(_assetHash, _assetPath, errors);
|
||||
}
|
||||
|
||||
|
@ -115,7 +116,10 @@ void BakeAssetTask::run() {
|
|||
|
||||
qDebug() << "Starting oven for " << _assetPath;
|
||||
_ovenProcess->start(path, args, QIODevice::ReadOnly);
|
||||
if (!_ovenProcess->waitForStarted(-1)) {
|
||||
qDebug() << "Running:" << path << args;
|
||||
if (!_ovenProcess->waitForStarted()) {
|
||||
PathUtils::deleteMyTemporaryDir(tempOutputDirName);
|
||||
|
||||
QString errors = "Oven process failed to start";
|
||||
emit bakeFailed(_assetHash, _assetPath, errors);
|
||||
return;
|
||||
|
|
|
@ -37,7 +37,7 @@ public slots:
|
|||
void abort();
|
||||
|
||||
signals:
|
||||
void bakeComplete(QString assetHash, QString assetPath, QString tempOutputDir, QVector<QString> outputFiles);
|
||||
void bakeComplete(QString assetHash, QString assetPath, QString tempOutputDir);
|
||||
void bakeFailed(QString assetHash, QString assetPath, QString errors);
|
||||
void bakeAborted(QString assetHash, QString assetPath);
|
||||
|
||||
|
|
4
cmake/externals/LibOVR/CMakeLists.txt
vendored
4
cmake/externals/LibOVR/CMakeLists.txt
vendored
|
@ -17,8 +17,8 @@ if (WIN32)
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://public.highfidelity.com/dependencies/ovr_sdk_win_1.26.0_public.zip
|
||||
URL_MD5 06804ff9727b910dcd04a37c800053b5
|
||||
URL https://hifi-public.s3.amazonaws.com/dependencies/ovr_sdk_win_1.35.0.zip
|
||||
URL_MD5 1e3e8b2101387af07ff9c841d0ea285e
|
||||
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
|
||||
PATCH_COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/LibOVRCMakeLists.txt" <SOURCE_DIR>/CMakeLists.txt
|
||||
LOG_DOWNLOAD 1
|
||||
|
|
|
@ -23,15 +23,15 @@ Rectangle {
|
|||
property bool ignoreRadiusEnabled: AvatarInputs.ignoreRadiusEnabled;
|
||||
|
||||
function updateOpacity() {
|
||||
if (ignoreRadiusEnabled) {
|
||||
bubbleRect.opacity = 1.0;
|
||||
} else {
|
||||
bubbleRect.opacity = 0.7;
|
||||
}
|
||||
var rectOpacity = ignoreRadiusEnabled ? 1.0 : (mouseArea.containsMouse ? 1.0 : 0.7);
|
||||
bubbleRect.opacity = rectOpacity;
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
updateOpacity();
|
||||
AvatarInputs.ignoreRadiusEnabledChanged.connect(function() {
|
||||
ignoreRadiusEnabled = AvatarInputs.ignoreRadiusEnabled;
|
||||
});
|
||||
}
|
||||
|
||||
onIgnoreRadiusEnabledChanged: {
|
||||
|
@ -74,10 +74,10 @@ Rectangle {
|
|||
}
|
||||
drag.target: dragTarget;
|
||||
onContainsMouseChanged: {
|
||||
var rectOpacity = (ignoreRadiusEnabled && containsMouse) ? 1.0 : (containsMouse ? 1.0 : 0.7);
|
||||
if (containsMouse) {
|
||||
Tablet.playSound(TabletEnums.ButtonHover);
|
||||
}
|
||||
var rectOpacity = ignoreRadiusEnabled ? 1.0 : (mouseArea.containsMouse ? 1.0 : 0.7);
|
||||
bubbleRect.opacity = rectOpacity;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -952,7 +952,7 @@ Rectangle {
|
|||
text: "LOG IN"
|
||||
|
||||
onClicked: {
|
||||
sendToScript({method: 'needsLogIn_loginClicked'});
|
||||
sendToScript({method: 'marketplace_loginClicked'});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -137,7 +137,7 @@ Item {
|
|||
width: parent.width/2 - anchors.leftMargin*2;
|
||||
text: "Cancel"
|
||||
onClicked: {
|
||||
sendToScript({method: 'needsLogIn_cancelClicked'});
|
||||
sendToScript({method: 'passphrasePopup_cancelClicked'});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -155,7 +155,7 @@ Item {
|
|||
width: parent.width/2 - anchors.rightMargin*2;
|
||||
text: "Log In"
|
||||
onClicked: {
|
||||
sendToScript({method: 'needsLogIn_loginClicked'});
|
||||
sendToScript({method: 'marketplace_loginClicked'});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1861,12 +1861,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson)) {
|
||||
getMyAvatar()->setBoomLength(MyAvatar::ZOOM_MIN); // So that camera doesn't auto-switch to third person.
|
||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::IndependentMode)) {
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, true);
|
||||
cameraMenuChanged();
|
||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::CameraEntityMode)) {
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, true);
|
||||
cameraMenuChanged();
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -2337,7 +2331,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
DependencyManager::get<PickManager>()->setPrecisionPicking(rayPickID, value);
|
||||
});
|
||||
|
||||
EntityItem::setBillboardRotationOperator([this](const glm::vec3& position, const glm::quat& rotation, BillboardMode billboardMode, const glm::vec3& frustumPos) {
|
||||
EntityItem::setBillboardRotationOperator([](const glm::vec3& position, const glm::quat& rotation, BillboardMode billboardMode, const glm::vec3& frustumPos) {
|
||||
if (billboardMode == BillboardMode::YAW) {
|
||||
//rotate about vertical to face the camera
|
||||
glm::vec3 dPosition = frustumPos - position;
|
||||
|
@ -2365,7 +2359,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
|
||||
DependencyManager::get<UsersScriptingInterface>()->setKickConfirmationOperator([this] (const QUuid& nodeID) { userKickConfirmation(nodeID); });
|
||||
|
||||
render::entities::WebEntityRenderer::setAcquireWebSurfaceOperator([this](const QString& url, bool htmlContent, QSharedPointer<OffscreenQmlSurface>& webSurface, bool& cachedWebSurface) {
|
||||
render::entities::WebEntityRenderer::setAcquireWebSurfaceOperator([=](const QString& url, bool htmlContent, QSharedPointer<OffscreenQmlSurface>& webSurface, bool& cachedWebSurface) {
|
||||
bool isTablet = url == TabletScriptingInterface::QML;
|
||||
if (htmlContent) {
|
||||
webSurface = DependencyManager::get<OffscreenQmlSurfaceCache>()->acquire(render::entities::WebEntityRenderer::QML);
|
||||
|
@ -2405,7 +2399,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
const uint8_t TABLET_FPS = 90;
|
||||
webSurface->setMaxFps(isTablet ? TABLET_FPS : DEFAULT_MAX_FPS);
|
||||
});
|
||||
render::entities::WebEntityRenderer::setReleaseWebSurfaceOperator([this](QSharedPointer<OffscreenQmlSurface>& webSurface, bool& cachedWebSurface, std::vector<QMetaObject::Connection>& connections) {
|
||||
render::entities::WebEntityRenderer::setReleaseWebSurfaceOperator([=](QSharedPointer<OffscreenQmlSurface>& webSurface, bool& cachedWebSurface, std::vector<QMetaObject::Connection>& connections) {
|
||||
QQuickItem* rootItem = webSurface->getRootItem();
|
||||
|
||||
// Fix for crash in QtWebEngineCore when rapidly switching domains
|
||||
|
@ -5765,9 +5759,6 @@ void Application::cycleCamera() {
|
|||
menu->setIsOptionChecked(MenuOption::ThirdPerson, false);
|
||||
menu->setIsOptionChecked(MenuOption::FullscreenMirror, true);
|
||||
|
||||
} else if (menu->isOptionChecked(MenuOption::IndependentMode) || menu->isOptionChecked(MenuOption::CameraEntityMode)) {
|
||||
// do nothing if in independent or camera entity modes
|
||||
return;
|
||||
}
|
||||
cameraMenuChanged(); // handle the menu change
|
||||
}
|
||||
|
@ -5783,12 +5774,6 @@ void Application::cameraModeChanged() {
|
|||
case CAMERA_MODE_MIRROR:
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, true);
|
||||
break;
|
||||
case CAMERA_MODE_INDEPENDENT:
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::IndependentMode, true);
|
||||
break;
|
||||
case CAMERA_MODE_ENTITY:
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::CameraEntityMode, true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -5832,14 +5817,6 @@ void Application::cameraMenuChanged() {
|
|||
getMyAvatar()->setBoomLength(MyAvatar::ZOOM_DEFAULT);
|
||||
}
|
||||
}
|
||||
} else if (menu->isOptionChecked(MenuOption::IndependentMode)) {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_INDEPENDENT) {
|
||||
_myCamera.setMode(CAMERA_MODE_INDEPENDENT);
|
||||
}
|
||||
} else if (menu->isOptionChecked(MenuOption::CameraEntityMode)) {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_ENTITY) {
|
||||
_myCamera.setMode(CAMERA_MODE_ENTITY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6754,6 +6731,11 @@ void Application::updateRenderArgs(float deltaTime) {
|
|||
}
|
||||
}
|
||||
|
||||
appRenderArgs._renderArgs._stencilMaskMode = getActiveDisplayPlugin()->getStencilMaskMode();
|
||||
if (appRenderArgs._renderArgs._stencilMaskMode == StencilMaskMode::MESH) {
|
||||
appRenderArgs._renderArgs._stencilMaskOperator = getActiveDisplayPlugin()->getStencilMaskMeshOperator();
|
||||
}
|
||||
|
||||
{
|
||||
QMutexLocker viewLocker(&_viewMutex);
|
||||
_myCamera.loadViewFrustum(_displayViewFrustum);
|
||||
|
@ -8428,11 +8410,23 @@ void Application::loadAvatarBrowser() const {
|
|||
DependencyManager::get<HMDScriptingInterface>()->openTablet();
|
||||
}
|
||||
|
||||
void Application::addSnapshotOperator(const SnapshotOperator& snapshotOperator) {
|
||||
std::lock_guard<std::mutex> lock(_snapshotMutex);
|
||||
_snapshotOperators.push(snapshotOperator);
|
||||
_hasPrimarySnapshot = _hasPrimarySnapshot || std::get<2>(snapshotOperator);
|
||||
}
|
||||
|
||||
bool Application::takeSnapshotOperators(std::queue<SnapshotOperator>& snapshotOperators) {
|
||||
std::lock_guard<std::mutex> lock(_snapshotMutex);
|
||||
bool hasPrimarySnapshot = _hasPrimarySnapshot;
|
||||
_hasPrimarySnapshot = false;
|
||||
_snapshotOperators.swap(snapshotOperators);
|
||||
return hasPrimarySnapshot;
|
||||
}
|
||||
|
||||
void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio, const QString& filename) {
|
||||
postLambdaEvent([notify, includeAnimated, aspectRatio, filename, this] {
|
||||
// Get a screenshot and save it
|
||||
QString path = DependencyManager::get<Snapshot>()->saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio), filename,
|
||||
TestScriptingInterface::getInstance()->getTestResultsLocation());
|
||||
addSnapshotOperator(std::make_tuple([notify, includeAnimated, aspectRatio, filename](const QImage& snapshot) {
|
||||
QString path = DependencyManager::get<Snapshot>()->saveSnapshot(snapshot, filename, TestScriptingInterface::getInstance()->getTestResultsLocation());
|
||||
|
||||
// If we're not doing an animated snapshot as well...
|
||||
if (!includeAnimated) {
|
||||
|
@ -8441,19 +8435,20 @@ void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRa
|
|||
emit DependencyManager::get<WindowScriptingInterface>()->stillSnapshotTaken(path, notify);
|
||||
}
|
||||
} else if (!SnapshotAnimated::isAlreadyTakingSnapshotAnimated()) {
|
||||
// Get an animated GIF snapshot and save it
|
||||
SnapshotAnimated::saveSnapshotAnimated(path, aspectRatio, qApp, DependencyManager::get<WindowScriptingInterface>());
|
||||
qApp->postLambdaEvent([path, aspectRatio] {
|
||||
// Get an animated GIF snapshot and save it
|
||||
SnapshotAnimated::saveSnapshotAnimated(path, aspectRatio, DependencyManager::get<WindowScriptingInterface>());
|
||||
});
|
||||
}
|
||||
});
|
||||
}, aspectRatio, true));
|
||||
}
|
||||
|
||||
void Application::takeSecondaryCameraSnapshot(const bool& notify, const QString& filename) {
|
||||
postLambdaEvent([notify, filename, this] {
|
||||
QString snapshotPath = DependencyManager::get<Snapshot>()->saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot(), filename,
|
||||
TestScriptingInterface::getInstance()->getTestResultsLocation());
|
||||
addSnapshotOperator(std::make_tuple([notify, filename](const QImage& snapshot) {
|
||||
QString snapshotPath = DependencyManager::get<Snapshot>()->saveSnapshot(snapshot, filename, TestScriptingInterface::getInstance()->getTestResultsLocation());
|
||||
|
||||
emit DependencyManager::get<WindowScriptingInterface>()->stillSnapshotTaken(snapshotPath, notify);
|
||||
});
|
||||
}, 0.0f, false));
|
||||
}
|
||||
|
||||
void Application::takeSecondaryCamera360Snapshot(const glm::vec3& cameraPosition, const bool& cubemapOutputFormat, const bool& notify, const QString& filename) {
|
||||
|
|
|
@ -345,6 +345,10 @@ public:
|
|||
void toggleAwayMode();
|
||||
#endif
|
||||
|
||||
using SnapshotOperator = std::tuple<std::function<void(const QImage&)>, float, bool>;
|
||||
void addSnapshotOperator(const SnapshotOperator& snapshotOperator);
|
||||
bool takeSnapshotOperators(std::queue<SnapshotOperator>& snapshotOperators);
|
||||
|
||||
void openDirectory(const QString& path);
|
||||
|
||||
signals:
|
||||
|
@ -791,6 +795,9 @@ private:
|
|||
AudioInjectorPointer _snapshotSoundInjector;
|
||||
SharedSoundPointer _snapshotSound;
|
||||
SharedSoundPointer _sampleSound;
|
||||
std::mutex _snapshotMutex;
|
||||
std::queue<SnapshotOperator> _snapshotOperators;
|
||||
bool _hasPrimarySnapshot { false };
|
||||
|
||||
DisplayPluginPointer _autoSwitchDisplayModeSupportedHMDPlugin;
|
||||
QString _autoSwitchDisplayModeSupportedHMDPluginName;
|
||||
|
|
|
@ -194,20 +194,6 @@ Menu::Menu() {
|
|||
|
||||
viewMirrorAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup));
|
||||
|
||||
// View > Independent
|
||||
auto viewIndependentAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||
MenuOption::IndependentMode, 0,
|
||||
false, qApp, SLOT(cameraMenuChanged())));
|
||||
|
||||
viewIndependentAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup));
|
||||
|
||||
// View > Entity Camera
|
||||
auto viewEntityCameraAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||
MenuOption::CameraEntityMode, 0,
|
||||
false, qApp, SLOT(cameraMenuChanged())));
|
||||
|
||||
viewEntityCameraAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup));
|
||||
|
||||
viewMenu->addSeparator();
|
||||
|
||||
// View > Center Player In View
|
||||
|
|
|
@ -53,7 +53,6 @@ namespace MenuOption {
|
|||
const QString BookmarkAvatarEntities = "Bookmark Avatar Entities";
|
||||
const QString BookmarkLocation = "Bookmark Location";
|
||||
const QString CalibrateCamera = "Calibrate Camera";
|
||||
const QString CameraEntityMode = "Entity Mode";
|
||||
const QString CenterPlayerInView = "Center Player In View";
|
||||
const QString Chat = "Chat...";
|
||||
const QString ClearDiskCache = "Clear Disk Cache";
|
||||
|
@ -120,7 +119,6 @@ namespace MenuOption {
|
|||
const QString Help = "Help...";
|
||||
const QString HomeLocation = "Home ";
|
||||
const QString IncreaseAvatarSize = "Increase Avatar Size";
|
||||
const QString IndependentMode = "Independent Mode";
|
||||
const QString ActionMotorControl = "Enable Default Motor Control";
|
||||
const QString LastLocation = "Last Location";
|
||||
const QString LoadScript = "Open and Run Script File...";
|
||||
|
|
|
@ -18,9 +18,6 @@
|
|||
#include <QPushButton>
|
||||
#include <QStandardPaths>
|
||||
|
||||
static const QString AVATAR_HEAD_AND_BODY_STRING = "Avatar Body with Head";
|
||||
static const QString ENTITY_MODEL_STRING = "Entity Model";
|
||||
|
||||
ModelSelector::ModelSelector() {
|
||||
QFormLayout* form = new QFormLayout(this);
|
||||
|
||||
|
|
|
@ -152,10 +152,12 @@ public:
|
|||
_cachedArgsPointer->_viewport = args->_viewport;
|
||||
_cachedArgsPointer->_displayMode = args->_displayMode;
|
||||
_cachedArgsPointer->_renderMode = args->_renderMode;
|
||||
_cachedArgsPointer->_stencilMaskMode = args->_stencilMaskMode;
|
||||
args->_blitFramebuffer = destFramebuffer;
|
||||
args->_viewport = glm::ivec4(0, 0, destFramebuffer->getWidth(), destFramebuffer->getHeight());
|
||||
args->_displayMode = RenderArgs::MONO;
|
||||
args->_renderMode = RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE;
|
||||
args->_stencilMaskMode = StencilMaskMode::NONE;
|
||||
|
||||
gpu::doInBatch("SecondaryCameraJob::run", args->_context, [&](gpu::Batch& batch) {
|
||||
batch.disableContextStereo();
|
||||
|
@ -255,10 +257,11 @@ public:
|
|||
void run(const render::RenderContextPointer& renderContext, const RenderArgsPointer& cachedArgs) {
|
||||
auto args = renderContext->args;
|
||||
if (cachedArgs) {
|
||||
args->_blitFramebuffer = cachedArgs->_blitFramebuffer;
|
||||
args->_viewport = cachedArgs->_viewport;
|
||||
args->_displayMode = cachedArgs->_displayMode;
|
||||
args->_renderMode = cachedArgs->_renderMode;
|
||||
args->_blitFramebuffer = cachedArgs->_blitFramebuffer;
|
||||
args->_viewport = cachedArgs->_viewport;
|
||||
args->_displayMode = cachedArgs->_displayMode;
|
||||
args->_renderMode = cachedArgs->_renderMode;
|
||||
args->_stencilMaskMode = cachedArgs->_stencilMaskMode;
|
||||
}
|
||||
args->popViewFrustum();
|
||||
|
||||
|
|
|
@ -727,7 +727,7 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic
|
|||
boxHit._distance = FLT_MAX;
|
||||
|
||||
for (size_t i = 0; i < hit._boundJoints.size(); i++) {
|
||||
assert(hit._boundJoints[i] < multiSpheres.size());
|
||||
assert(hit._boundJoints[i] < (int)multiSpheres.size());
|
||||
auto &mSphere = multiSpheres[hit._boundJoints[i]];
|
||||
if (mSphere.isValid()) {
|
||||
float boundDistance = FLT_MAX;
|
||||
|
|
|
@ -1630,7 +1630,9 @@ void MyAvatar::handleChangedAvatarEntityData() {
|
|||
if (!skip) {
|
||||
sanitizeAvatarEntityProperties(properties);
|
||||
entityTree->withWriteLock([&] {
|
||||
entityTree->updateEntity(id, properties);
|
||||
if (entityTree->updateEntity(id, properties)) {
|
||||
packetSender->queueEditAvatarEntityMessage(entityTree, id);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -5811,12 +5813,19 @@ void MyAvatar::releaseGrab(const QUuid& grabID) {
|
|||
}
|
||||
|
||||
void MyAvatar::addAvatarHandsToFlow(const std::shared_ptr<Avatar>& otherAvatar) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "addAvatarHandsToFlow",
|
||||
Q_ARG(const std::shared_ptr<Avatar>&, otherAvatar));
|
||||
return;
|
||||
}
|
||||
auto &flow = _skeletonModel->getRig().getFlow();
|
||||
for (auto &handJointName : HAND_COLLISION_JOINTS) {
|
||||
int jointIndex = otherAvatar->getJointIndex(handJointName);
|
||||
if (jointIndex != -1) {
|
||||
glm::vec3 position = otherAvatar->getJointPosition(jointIndex);
|
||||
flow.setOthersCollision(otherAvatar->getID(), jointIndex, position);
|
||||
if (otherAvatar != nullptr && flow.getActive()) {
|
||||
for (auto &handJointName : HAND_COLLISION_JOINTS) {
|
||||
int jointIndex = otherAvatar->getJointIndex(handJointName);
|
||||
if (jointIndex != -1) {
|
||||
glm::vec3 position = otherAvatar->getJointPosition(jointIndex);
|
||||
flow.setOthersCollision(otherAvatar->getID(), jointIndex, position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -244,6 +244,7 @@ void GraphicsEngine::render_performFrame() {
|
|||
finalFramebuffer = framebufferCache->getFramebuffer();
|
||||
}
|
||||
|
||||
std::queue<Application::SnapshotOperator> snapshotOperators;
|
||||
if (!_programsCompiled.load()) {
|
||||
gpu::doInBatch("splashFrame", _gpuContext, [&](gpu::Batch& batch) {
|
||||
batch.setFramebuffer(finalFramebuffer);
|
||||
|
@ -271,6 +272,7 @@ void GraphicsEngine::render_performFrame() {
|
|||
PROFILE_RANGE(render, "/runRenderFrame");
|
||||
renderArgs._hudOperator = displayPlugin->getHUDOperator();
|
||||
renderArgs._hudTexture = qApp->getApplicationOverlay().getOverlayTexture();
|
||||
renderArgs._takingSnapshot = qApp->takeSnapshotOperators(snapshotOperators);
|
||||
renderArgs._blitFramebuffer = finalFramebuffer;
|
||||
render_runRenderFrame(&renderArgs);
|
||||
}
|
||||
|
@ -285,6 +287,7 @@ void GraphicsEngine::render_performFrame() {
|
|||
frameBufferCache->releaseFramebuffer(framebuffer);
|
||||
}
|
||||
};
|
||||
frame->snapshotOperators = snapshotOperators;
|
||||
// deliver final scene rendering commands to the display plugin
|
||||
{
|
||||
PROFILE_RANGE(render, "/pluginOutput");
|
||||
|
|
|
@ -174,14 +174,10 @@ void Audio::setPTTDesktop(bool enabled) {
|
|||
_pttDesktop = enabled;
|
||||
}
|
||||
});
|
||||
if (!enabled) {
|
||||
// Set to default behavior (unmuted for Desktop) on Push-To-Talk disable.
|
||||
setMutedDesktop(true);
|
||||
} else {
|
||||
// Should be muted when not pushing to talk while PTT is enabled.
|
||||
if (enabled || _settingsLoaded) {
|
||||
// Set to default behavior (muted for Desktop) on Push-To-Talk disable or when enabled. Settings also need to be loaded.
|
||||
setMutedDesktop(true);
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
emit pushToTalkChanged(enabled);
|
||||
emit pushToTalkDesktopChanged(enabled);
|
||||
|
@ -202,12 +198,9 @@ void Audio::setPTTHMD(bool enabled) {
|
|||
_pttHMD = enabled;
|
||||
}
|
||||
});
|
||||
if (!enabled) {
|
||||
// Set to default behavior (unmuted for HMD) on Push-To-Talk disable.
|
||||
setMutedHMD(false);
|
||||
} else {
|
||||
// Should be muted when not pushing to talk while PTT is enabled.
|
||||
setMutedHMD(true);
|
||||
if (enabled || _settingsLoaded) {
|
||||
// Set to default behavior (unmuted for HMD) on Push-To-Talk disable or muted for when PTT is enabled.
|
||||
setMutedHMD(enabled);
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
|
@ -231,6 +224,7 @@ void Audio::loadData() {
|
|||
|
||||
auto client = DependencyManager::get<AudioClient>().data();
|
||||
QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted()), Q_ARG(bool, false));
|
||||
_settingsLoaded = true;
|
||||
}
|
||||
|
||||
bool Audio::getPTTHMD() const {
|
||||
|
@ -357,10 +351,12 @@ void Audio::onContextChanged() {
|
|||
changed = true;
|
||||
}
|
||||
});
|
||||
if (isHMD) {
|
||||
setMuted(getMutedHMD());
|
||||
} else {
|
||||
setMuted(getMutedDesktop());
|
||||
if (_settingsLoaded) {
|
||||
bool isMuted = isHMD ? getMutedHMD() : getMutedDesktop();
|
||||
setMuted(isMuted);
|
||||
// always set audio client muted state on context changed - sometimes setMuted does not catch it.
|
||||
auto client = DependencyManager::get<AudioClient>().data();
|
||||
QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false));
|
||||
}
|
||||
if (changed) {
|
||||
emit contextChanged(isHMD ? Audio::HMD : Audio::DESKTOP);
|
||||
|
|
|
@ -409,6 +409,7 @@ protected:
|
|||
|
||||
private:
|
||||
|
||||
bool _settingsLoaded { false };
|
||||
float _inputVolume { 1.0f };
|
||||
float _inputLevel { 0.0f };
|
||||
float _localInjectorGain { 0.0f }; // in dB
|
||||
|
|
|
@ -138,7 +138,7 @@ void LoginDialog::login(const QString& username, const QString& password) const
|
|||
void LoginDialog::loginThroughOculus() {
|
||||
qDebug() << "Attempting to login through Oculus";
|
||||
if (auto oculusPlatformPlugin = PluginManager::getInstance()->getOculusPlatformPlugin()) {
|
||||
oculusPlatformPlugin->requestNonceAndUserID([this] (QString nonce, QString oculusID) {
|
||||
oculusPlatformPlugin->requestNonceAndUserID([] (QString nonce, QString oculusID) {
|
||||
DependencyManager::get<AccountManager>()->requestAccessTokenWithOculus(nonce, oculusID);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -159,47 +159,57 @@ void Snapshot::save360Snapshot(const glm::vec3& cameraPosition,
|
|||
secondaryCameraRenderConfig->setOrientation(CAMERA_ORIENTATION_DOWN);
|
||||
|
||||
_snapshotIndex = 0;
|
||||
_taking360Snapshot = true;
|
||||
|
||||
_snapshotTimer.start(SNAPSHOT_360_TIMER_INTERVAL);
|
||||
}
|
||||
|
||||
void Snapshot::takeNextSnapshot() {
|
||||
SecondaryCameraJobConfig* config =
|
||||
static_cast<SecondaryCameraJobConfig*>(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCamera"));
|
||||
if (_taking360Snapshot) {
|
||||
if (!_waitingOnSnapshot) {
|
||||
_waitingOnSnapshot = true;
|
||||
qApp->addSnapshotOperator(std::make_tuple([this](const QImage& snapshot) {
|
||||
// Order is:
|
||||
// 0. Down
|
||||
// 1. Front
|
||||
// 2. Left
|
||||
// 3. Back
|
||||
// 4. Right
|
||||
// 5. Up
|
||||
if (_snapshotIndex < 6) {
|
||||
_imageArray[_snapshotIndex] = snapshot;
|
||||
}
|
||||
|
||||
// Order is:
|
||||
// 0. Down
|
||||
// 1. Front
|
||||
// 2. Left
|
||||
// 3. Back
|
||||
// 4. Right
|
||||
// 5. Up
|
||||
if (_snapshotIndex < 6) {
|
||||
_imageArray[_snapshotIndex] = qApp->getActiveDisplayPlugin()->getSecondaryCameraScreenshot();
|
||||
}
|
||||
SecondaryCameraJobConfig* config = static_cast<SecondaryCameraJobConfig*>(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCamera"));
|
||||
if (_snapshotIndex == 0) {
|
||||
// Setup for Front Image capture
|
||||
config->setOrientation(CAMERA_ORIENTATION_FRONT);
|
||||
} else if (_snapshotIndex == 1) {
|
||||
// Setup for Left Image capture
|
||||
config->setOrientation(CAMERA_ORIENTATION_LEFT);
|
||||
} else if (_snapshotIndex == 2) {
|
||||
// Setup for Back Image capture
|
||||
config->setOrientation(CAMERA_ORIENTATION_BACK);
|
||||
} else if (_snapshotIndex == 3) {
|
||||
// Setup for Right Image capture
|
||||
config->setOrientation(CAMERA_ORIENTATION_RIGHT);
|
||||
} else if (_snapshotIndex == 4) {
|
||||
// Setup for Up Image capture
|
||||
config->setOrientation(CAMERA_ORIENTATION_UP);
|
||||
} else if (_snapshotIndex == 5) {
|
||||
_taking360Snapshot = false;
|
||||
}
|
||||
|
||||
if (_snapshotIndex == 0) {
|
||||
// Setup for Front Image capture
|
||||
config->setOrientation(CAMERA_ORIENTATION_FRONT);
|
||||
} else if (_snapshotIndex == 1) {
|
||||
// Setup for Left Image capture
|
||||
config->setOrientation(CAMERA_ORIENTATION_LEFT);
|
||||
} else if (_snapshotIndex == 2) {
|
||||
// Setup for Back Image capture
|
||||
config->setOrientation(CAMERA_ORIENTATION_BACK);
|
||||
} else if (_snapshotIndex == 3) {
|
||||
// Setup for Right Image capture
|
||||
config->setOrientation(CAMERA_ORIENTATION_RIGHT);
|
||||
} else if (_snapshotIndex == 4) {
|
||||
// Setup for Up Image capture
|
||||
config->setOrientation(CAMERA_ORIENTATION_UP);
|
||||
} else if (_snapshotIndex > 5) {
|
||||
_waitingOnSnapshot = false;
|
||||
_snapshotIndex++;
|
||||
}, 0.0f, false));
|
||||
}
|
||||
} else {
|
||||
_snapshotTimer.stop();
|
||||
|
||||
// Reset secondary camera render config
|
||||
static_cast<ToneMappingConfig*>(
|
||||
qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCameraJob.ToneMapping"))
|
||||
->setCurve(1);
|
||||
SecondaryCameraJobConfig* config = static_cast<SecondaryCameraJobConfig*>(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCamera"));
|
||||
static_cast<ToneMappingConfig*>(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCameraJob.ToneMapping"))->setCurve(1);
|
||||
config->resetSizeSpectatorCamera(qApp->getWindow()->geometry().width(), qApp->getWindow()->geometry().height());
|
||||
config->setProperty("attachedEntityId", _oldAttachedEntityId);
|
||||
config->setProperty("vFoV", _oldvFoV);
|
||||
|
@ -217,8 +227,6 @@ void Snapshot::takeNextSnapshot() {
|
|||
QtConcurrent::run([this]() { convertToEquirectangular(); });
|
||||
}
|
||||
}
|
||||
|
||||
_snapshotIndex++;
|
||||
}
|
||||
|
||||
void Snapshot::convertToCubemap() {
|
||||
|
|
|
@ -97,6 +97,8 @@ private:
|
|||
bool _cubemapOutputFormat;
|
||||
QTimer _snapshotTimer;
|
||||
qint16 _snapshotIndex;
|
||||
bool _waitingOnSnapshot { false };
|
||||
bool _taking360Snapshot { false };
|
||||
bool _oldEnabled;
|
||||
QVariant _oldAttachedEntityId;
|
||||
QVariant _oldOrientation;
|
||||
|
|
|
@ -27,7 +27,6 @@ QString SnapshotAnimated::snapshotAnimatedPath;
|
|||
QString SnapshotAnimated::snapshotStillPath;
|
||||
QVector<QImage> SnapshotAnimated::snapshotAnimatedFrameVector;
|
||||
QVector<qint64> SnapshotAnimated::snapshotAnimatedFrameDelayVector;
|
||||
Application* SnapshotAnimated::app;
|
||||
float SnapshotAnimated::aspectRatio;
|
||||
QSharedPointer<WindowScriptingInterface> SnapshotAnimated::snapshotAnimatedDM;
|
||||
GifWriter SnapshotAnimated::snapshotAnimatedGifWriter;
|
||||
|
@ -36,12 +35,11 @@ GifWriter SnapshotAnimated::snapshotAnimatedGifWriter;
|
|||
Setting::Handle<bool> SnapshotAnimated::alsoTakeAnimatedSnapshot("alsoTakeAnimatedSnapshot", true);
|
||||
Setting::Handle<float> SnapshotAnimated::snapshotAnimatedDuration("snapshotAnimatedDuration", SNAPSNOT_ANIMATED_DURATION_SECS);
|
||||
|
||||
void SnapshotAnimated::saveSnapshotAnimated(QString pathStill, float aspectRatio, Application* app, QSharedPointer<WindowScriptingInterface> dm) {
|
||||
void SnapshotAnimated::saveSnapshotAnimated(QString pathStill, float aspectRatio, QSharedPointer<WindowScriptingInterface> dm) {
|
||||
// If we're not in the middle of capturing an animated snapshot...
|
||||
if (SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp == 0) {
|
||||
SnapshotAnimated::snapshotAnimatedTimer = new QTimer();
|
||||
SnapshotAnimated::aspectRatio = aspectRatio;
|
||||
SnapshotAnimated::app = app;
|
||||
SnapshotAnimated::snapshotAnimatedDM = dm;
|
||||
// Define the output location of the still and animated snapshots.
|
||||
SnapshotAnimated::snapshotStillPath = pathStill;
|
||||
|
@ -62,44 +60,45 @@ void SnapshotAnimated::saveSnapshotAnimated(QString pathStill, float aspectRatio
|
|||
|
||||
void SnapshotAnimated::captureFrames() {
|
||||
if (SnapshotAnimated::snapshotAnimatedTimerRunning) {
|
||||
// Get a screenshot from the display, then scale the screenshot down,
|
||||
// then convert it to the image format the GIF library needs,
|
||||
// then save all that to the QImage named "frame"
|
||||
QImage frame(SnapshotAnimated::app->getActiveDisplayPlugin()->getScreenshot(SnapshotAnimated::aspectRatio));
|
||||
frame = frame.scaledToWidth(SNAPSNOT_ANIMATED_WIDTH);
|
||||
SnapshotAnimated::snapshotAnimatedFrameVector.append(frame);
|
||||
qApp->addSnapshotOperator(std::make_tuple([](const QImage& snapshot) {
|
||||
// Get a screenshot from the display, then scale the screenshot down,
|
||||
// then convert it to the image format the GIF library needs,
|
||||
// then save all that to the QImage named "frame"
|
||||
QImage frame = snapshot.scaledToWidth(SNAPSNOT_ANIMATED_WIDTH);
|
||||
SnapshotAnimated::snapshotAnimatedFrameVector.append(frame);
|
||||
|
||||
// If that was the first frame...
|
||||
if (SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp == 0) {
|
||||
// Record the current frame timestamp
|
||||
SnapshotAnimated::snapshotAnimatedTimestamp = QDateTime::currentMSecsSinceEpoch();
|
||||
// Record the first frame timestamp
|
||||
SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp = SnapshotAnimated::snapshotAnimatedTimestamp;
|
||||
SnapshotAnimated::snapshotAnimatedFrameDelayVector.append(SNAPSNOT_ANIMATED_FRAME_DELAY_MSEC / 10);
|
||||
// If this is an intermediate or the final frame...
|
||||
} else {
|
||||
// Push the current frame delay onto the vector
|
||||
SnapshotAnimated::snapshotAnimatedFrameDelayVector.append(round(((float)(QDateTime::currentMSecsSinceEpoch() - SnapshotAnimated::snapshotAnimatedTimestamp)) / 10));
|
||||
// Record the current frame timestamp
|
||||
SnapshotAnimated::snapshotAnimatedTimestamp = QDateTime::currentMSecsSinceEpoch();
|
||||
// If that was the first frame...
|
||||
if (SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp == 0) {
|
||||
// Record the current frame timestamp
|
||||
SnapshotAnimated::snapshotAnimatedTimestamp = QDateTime::currentMSecsSinceEpoch();
|
||||
// Record the first frame timestamp
|
||||
SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp = SnapshotAnimated::snapshotAnimatedTimestamp;
|
||||
SnapshotAnimated::snapshotAnimatedFrameDelayVector.append(SNAPSNOT_ANIMATED_FRAME_DELAY_MSEC / 10);
|
||||
// If this is an intermediate or the final frame...
|
||||
} else {
|
||||
// Push the current frame delay onto the vector
|
||||
SnapshotAnimated::snapshotAnimatedFrameDelayVector.append(round(((float)(QDateTime::currentMSecsSinceEpoch() - SnapshotAnimated::snapshotAnimatedTimestamp)) / 10));
|
||||
// Record the current frame timestamp
|
||||
SnapshotAnimated::snapshotAnimatedTimestamp = QDateTime::currentMSecsSinceEpoch();
|
||||
|
||||
// If that was the last frame...
|
||||
if ((SnapshotAnimated::snapshotAnimatedTimestamp - SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp) >= (SnapshotAnimated::snapshotAnimatedDuration.get() * MSECS_PER_SECOND)) {
|
||||
SnapshotAnimated::snapshotAnimatedTimerRunning = false;
|
||||
|
||||
// Notify the user that we're processing the snapshot
|
||||
// This also pops up the "Share" dialog. The unprocessed GIF will be visualized as a loading icon until processingGifCompleted() is called.
|
||||
emit SnapshotAnimated::snapshotAnimatedDM->processingGifStarted(SnapshotAnimated::snapshotStillPath);
|
||||
|
||||
// Kick off the thread that'll pack the frames into the GIF
|
||||
QtConcurrent::run(processFrames);
|
||||
// Stop the snapshot QTimer. This action by itself DOES NOT GUARANTEE
|
||||
// that the slot will not be called again in the future.
|
||||
// See: http://lists.qt-project.org/pipermail/qt-interest-old/2009-October/013926.html
|
||||
SnapshotAnimated::snapshotAnimatedTimer->stop();
|
||||
delete SnapshotAnimated::snapshotAnimatedTimer;
|
||||
// If that was the last frame...
|
||||
if ((SnapshotAnimated::snapshotAnimatedTimestamp - SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp) >= (SnapshotAnimated::snapshotAnimatedDuration.get() * MSECS_PER_SECOND)) {
|
||||
SnapshotAnimated::snapshotAnimatedTimerRunning = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}, SnapshotAnimated::aspectRatio, true));
|
||||
} else {
|
||||
// Notify the user that we're processing the snapshot
|
||||
// This also pops up the "Share" dialog. The unprocessed GIF will be visualized as a loading icon until processingGifCompleted() is called.
|
||||
emit SnapshotAnimated::snapshotAnimatedDM->processingGifStarted(SnapshotAnimated::snapshotStillPath);
|
||||
|
||||
// Kick off the thread that'll pack the frames into the GIF
|
||||
QtConcurrent::run(processFrames);
|
||||
// Stop the snapshot QTimer. This action by itself DOES NOT GUARANTEE
|
||||
// that the slot will not be called again in the future.
|
||||
// See: http://lists.qt-project.org/pipermail/qt-interest-old/2009-October/013926.html
|
||||
SnapshotAnimated::snapshotAnimatedTimer->stop();
|
||||
delete SnapshotAnimated::snapshotAnimatedTimer;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,6 @@ private:
|
|||
static QVector<QImage> snapshotAnimatedFrameVector;
|
||||
static QVector<qint64> snapshotAnimatedFrameDelayVector;
|
||||
static QSharedPointer<WindowScriptingInterface> snapshotAnimatedDM;
|
||||
static Application* app;
|
||||
static float aspectRatio;
|
||||
|
||||
static GifWriter snapshotAnimatedGifWriter;
|
||||
|
@ -51,7 +50,7 @@ private:
|
|||
static void processFrames();
|
||||
static void clearTempVariables();
|
||||
public:
|
||||
static void saveSnapshotAnimated(QString pathStill, float aspectRatio, Application* app, QSharedPointer<WindowScriptingInterface> dm);
|
||||
static void saveSnapshotAnimated(QString pathStill, float aspectRatio, QSharedPointer<WindowScriptingInterface> dm);
|
||||
static bool isAlreadyTakingSnapshotAnimated() { return snapshotAnimatedFirstFrameTimestamp != 0; };
|
||||
static Setting::Handle<bool> alsoTakeAnimatedSnapshot;
|
||||
static Setting::Handle<float> snapshotAnimatedDuration;
|
||||
|
|
|
@ -261,7 +261,7 @@ public:
|
|||
qCDebug(animation) << " " << pair.first << "=" << pair.second.getString();
|
||||
break;
|
||||
default:
|
||||
assert(("invalid AnimVariant::Type", false));
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -276,4 +276,8 @@ void MaterialBaker::setMaterials(const QHash<QString, hfm::Material>& materials,
|
|||
addTexture(material.name, image::TextureUsage::SCATTERING_TEXTURE, material.scatteringTexture);
|
||||
addTexture(material.name, image::TextureUsage::LIGHTMAP_TEXTURE, material.lightmapTexture);
|
||||
}
|
||||
}
|
||||
|
||||
void MaterialBaker::setMaterials(const NetworkMaterialResourcePointer& materialResource) {
|
||||
_materialResource = materialResource;
|
||||
}
|
|
@ -31,6 +31,9 @@ public:
|
|||
QString getBakedMaterialData() const { return _bakedMaterialData; }
|
||||
|
||||
void setMaterials(const QHash<QString, hfm::Material>& materials, const QString& baseURL);
|
||||
void setMaterials(const NetworkMaterialResourcePointer& materialResource);
|
||||
|
||||
NetworkMaterialResourcePointer getNetworkMaterialResource() const { return _materialResource; }
|
||||
|
||||
static void setNextOvenWorkerThreadOperator(std::function<QThread*()> getNextOvenWorkerThreadOperator) { _getNextOvenWorkerThreadOperator = getNextOvenWorkerThreadOperator; }
|
||||
|
||||
|
|
|
@ -241,14 +241,12 @@ void ModelBaker::bakeSourceCopy() {
|
|||
config->getJobConfig("BuildDracoMesh")->setEnabled(true);
|
||||
// Do not permit potentially lossy modification of joint data meant for runtime
|
||||
((PrepareJointsConfig*)config->getJobConfig("PrepareJoints"))->passthrough = true;
|
||||
// The resources parsed from this job will not be used for now
|
||||
// TODO: Proper full baking of all materials for a model
|
||||
config->getJobConfig("ParseMaterialMapping")->setEnabled(false);
|
||||
|
||||
// Begin hfm baking
|
||||
baker.run();
|
||||
|
||||
_hfmModel = baker.getHFMModel();
|
||||
_materialMapping = baker.getMaterialMapping();
|
||||
dracoMeshes = baker.getDracoMeshes();
|
||||
dracoMaterialLists = baker.getDracoMaterialLists();
|
||||
}
|
||||
|
@ -260,7 +258,7 @@ void ModelBaker::bakeSourceCopy() {
|
|||
return;
|
||||
}
|
||||
|
||||
if (_hfmModel->materials.size() > 0) {
|
||||
if (!_hfmModel->materials.isEmpty()) {
|
||||
_materialBaker = QSharedPointer<MaterialBaker>(
|
||||
new MaterialBaker(_modelURL.fileName(), true, _bakedOutputDir),
|
||||
&MaterialBaker::deleteLater
|
||||
|
@ -269,7 +267,7 @@ void ModelBaker::bakeSourceCopy() {
|
|||
connect(_materialBaker.data(), &MaterialBaker::finished, this, &ModelBaker::handleFinishedMaterialBaker);
|
||||
_materialBaker->bake();
|
||||
} else {
|
||||
outputBakedFST();
|
||||
bakeMaterialMap();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -285,26 +283,14 @@ void ModelBaker::handleFinishedMaterialBaker() {
|
|||
auto baseName = relativeBakedMaterialURL.left(relativeBakedMaterialURL.lastIndexOf('.'));
|
||||
relativeBakedMaterialURL = baseName + BAKED_MATERIAL_EXTENSION;
|
||||
|
||||
// First we add the materials in the model
|
||||
QJsonArray materialMapping;
|
||||
for (auto material : _hfmModel->materials) {
|
||||
QJsonObject json;
|
||||
json["mat::" + material.name] = relativeBakedMaterialURL + "#" + material.name;
|
||||
materialMapping.push_back(json);
|
||||
}
|
||||
|
||||
// The we add any existing mappings from the mapping
|
||||
if (_mapping.contains(MATERIAL_MAPPING_FIELD)) {
|
||||
QByteArray materialMapValue = _mapping[MATERIAL_MAPPING_FIELD].toByteArray();
|
||||
QJsonObject oldMaterialMapping = QJsonDocument::fromJson(materialMapValue).object();
|
||||
for (auto key : oldMaterialMapping.keys()) {
|
||||
auto materialResource = baker->getNetworkMaterialResource();
|
||||
if (materialResource) {
|
||||
for (auto materialName : materialResource->parsedMaterials.names) {
|
||||
QJsonObject json;
|
||||
json[key] = oldMaterialMapping[key];
|
||||
materialMapping.push_back(json);
|
||||
json[QString("mat::" + QString(materialName.c_str()))] = relativeBakedMaterialURL + "#" + materialName.c_str();
|
||||
_materialMappingJSON.push_back(json);
|
||||
}
|
||||
}
|
||||
|
||||
_mapping[MATERIAL_MAPPING_FIELD] = QJsonDocument(materialMapping).toJson(QJsonDocument::Compact);
|
||||
} else {
|
||||
// this material failed to bake - this doesn't fail the entire bake but we need to add the errors from
|
||||
// the material to our warnings
|
||||
|
@ -314,7 +300,62 @@ void ModelBaker::handleFinishedMaterialBaker() {
|
|||
handleWarning("Failed to bake the materials for model with URL " + _modelURL.toString());
|
||||
}
|
||||
|
||||
outputBakedFST();
|
||||
bakeMaterialMap();
|
||||
}
|
||||
|
||||
void ModelBaker::bakeMaterialMap() {
|
||||
if (!_materialMapping.empty()) {
|
||||
// TODO: The existing material map must be baked in order, so we do it all on this thread to preserve the order.
|
||||
// It could be spread over multiple threads if we had a good way of preserving the order once all of the bakers are done
|
||||
_materialBaker = QSharedPointer<MaterialBaker>(
|
||||
new MaterialBaker("materialMap" + QString::number(_materialMapIndex++), true, _bakedOutputDir),
|
||||
&MaterialBaker::deleteLater
|
||||
);
|
||||
_materialBaker->setMaterials(_materialMapping.front().second);
|
||||
connect(_materialBaker.data(), &MaterialBaker::finished, this, &ModelBaker::handleFinishedMaterialMapBaker);
|
||||
_materialBaker->bake();
|
||||
} else {
|
||||
outputBakedFST();
|
||||
}
|
||||
}
|
||||
|
||||
void ModelBaker::handleFinishedMaterialMapBaker() {
|
||||
auto baker = qobject_cast<MaterialBaker*>(sender());
|
||||
|
||||
if (baker) {
|
||||
if (!baker->hasErrors()) {
|
||||
// this MaterialBaker is done and everything went according to plan
|
||||
qCDebug(model_baking) << "Adding baked material to FST mapping " << baker->getBakedMaterialData();
|
||||
|
||||
QString materialName;
|
||||
{
|
||||
auto materialResource = baker->getNetworkMaterialResource();
|
||||
if (materialResource) {
|
||||
auto url = materialResource->getURL();
|
||||
if (!url.isEmpty()) {
|
||||
QString urlString = url.toDisplayString();
|
||||
auto index = urlString.lastIndexOf("#");
|
||||
if (index != -1) {
|
||||
materialName = urlString.right(urlString.length() - index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QJsonObject json;
|
||||
json[QString(_materialMapping.front().first.c_str())] = baker->getMaterialData() + BAKED_MATERIAL_EXTENSION + materialName;
|
||||
_materialMappingJSON.push_back(json);
|
||||
} else {
|
||||
// this material failed to bake - this doesn't fail the entire bake but we need to add the errors from
|
||||
// the material to our warnings
|
||||
_warningList << baker->getWarnings();
|
||||
}
|
||||
} else {
|
||||
handleWarning("Failed to bake the materialMap for model with URL " + _modelURL.toString() + " and mapping target " + _materialMapping.front().first.c_str());
|
||||
}
|
||||
|
||||
_materialMapping.erase(_materialMapping.begin());
|
||||
bakeMaterialMap();
|
||||
}
|
||||
|
||||
void ModelBaker::outputUnbakedFST() {
|
||||
|
@ -363,6 +404,9 @@ void ModelBaker::outputBakedFST() {
|
|||
outputMapping[FILENAME_FIELD] = _bakedModelURL.fileName();
|
||||
outputMapping.remove(TEXDIR_FIELD);
|
||||
outputMapping.remove(COMMENT_FIELD);
|
||||
if (!_materialMappingJSON.isEmpty()) {
|
||||
outputMapping[MATERIAL_MAPPING_FIELD] = QJsonDocument(_materialMappingJSON).toJson(QJsonDocument::Compact);
|
||||
}
|
||||
hifi::ByteArray fstOut = FSTReader::writeMapping(outputMapping);
|
||||
|
||||
QFile fstOutputFile { outputFSTURL };
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <QtCore/QDir>
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
#include <QJsonArray>
|
||||
|
||||
#include "Baker.h"
|
||||
#include "MaterialBaker.h"
|
||||
|
@ -80,14 +81,19 @@ protected slots:
|
|||
void handleModelNetworkReply();
|
||||
virtual void bakeSourceCopy();
|
||||
void handleFinishedMaterialBaker();
|
||||
void handleFinishedMaterialMapBaker();
|
||||
|
||||
private:
|
||||
void outputUnbakedFST();
|
||||
void outputBakedFST();
|
||||
void bakeMaterialMap();
|
||||
|
||||
bool _hasBeenBaked { false };
|
||||
|
||||
hfm::Model::Pointer _hfmModel;
|
||||
MaterialMapping _materialMapping;
|
||||
int _materialMapIndex { 0 };
|
||||
QJsonArray _materialMappingJSON;
|
||||
QSharedPointer<MaterialBaker> _materialBaker;
|
||||
};
|
||||
|
||||
|
|
|
@ -25,12 +25,4 @@ void NullDisplayPlugin::submitFrame(const gpu::FramePointer& frame) {
|
|||
if (frame) {
|
||||
_gpuContext->consumeFrameUpdates(frame);
|
||||
}
|
||||
}
|
||||
|
||||
QImage NullDisplayPlugin::getScreenshot(float aspectRatio) const {
|
||||
return QImage();
|
||||
}
|
||||
|
||||
QImage NullDisplayPlugin::getSecondaryCameraScreenshot() const {
|
||||
return QImage();
|
||||
}
|
||||
}
|
|
@ -17,8 +17,6 @@ public:
|
|||
|
||||
glm::uvec2 getRecommendedRenderSize() const override;
|
||||
void submitFrame(const gpu::FramePointer& newFrame) override;
|
||||
QImage getScreenshot(float aspectRatio = 0.0f) const override;
|
||||
QImage getSecondaryCameraScreenshot() const override;
|
||||
void copyTextureToQuickFramebuffer(NetworkTexturePointer source, QOpenGLFramebufferObject* target, GLsync* fenceSync) override {};
|
||||
void pluginUpdate() override {};
|
||||
private:
|
||||
|
|
|
@ -721,6 +721,19 @@ void OpenGLDisplayPlugin::present() {
|
|||
compositeLayers();
|
||||
}
|
||||
|
||||
{ // If we have any snapshots this frame, handle them
|
||||
PROFILE_RANGE_EX(render, "snapshotOperators", 0xffff00ff, frameId)
|
||||
while (!_currentFrame->snapshotOperators.empty()) {
|
||||
auto& snapshotOperator = _currentFrame->snapshotOperators.front();
|
||||
if (std::get<2>(snapshotOperator)) {
|
||||
std::get<0>(snapshotOperator)(getScreenshot(std::get<1>(snapshotOperator)));
|
||||
} else {
|
||||
std::get<0>(snapshotOperator)(getSecondaryCameraScreenshot());
|
||||
}
|
||||
_currentFrame->snapshotOperators.pop();
|
||||
}
|
||||
}
|
||||
|
||||
// Take the composite framebuffer and send it to the output device
|
||||
{
|
||||
PROFILE_RANGE_EX(render, "internalPresent", 0xff00ffff, frameId)
|
||||
|
@ -785,7 +798,7 @@ bool OpenGLDisplayPlugin::setDisplayTexture(const QString& name) {
|
|||
return !!_displayTexture;
|
||||
}
|
||||
|
||||
QImage OpenGLDisplayPlugin::getScreenshot(float aspectRatio) const {
|
||||
QImage OpenGLDisplayPlugin::getScreenshot(float aspectRatio) {
|
||||
auto size = _compositeFramebuffer->getSize();
|
||||
if (isHmd()) {
|
||||
size.x /= 2;
|
||||
|
@ -801,24 +814,18 @@ QImage OpenGLDisplayPlugin::getScreenshot(float aspectRatio) const {
|
|||
corner.x = round((size.x - bestSize.x) / 2.0f);
|
||||
corner.y = round((size.y - bestSize.y) / 2.0f);
|
||||
}
|
||||
auto glBackend = const_cast<OpenGLDisplayPlugin&>(*this).getGLBackend();
|
||||
QImage screenshot(bestSize.x, bestSize.y, QImage::Format_ARGB32);
|
||||
withOtherThreadContext([&] {
|
||||
glBackend->downloadFramebuffer(_compositeFramebuffer, ivec4(corner, bestSize), screenshot);
|
||||
});
|
||||
getGLBackend()->downloadFramebuffer(_compositeFramebuffer, ivec4(corner, bestSize), screenshot);
|
||||
return screenshot.mirrored(false, true);
|
||||
}
|
||||
|
||||
QImage OpenGLDisplayPlugin::getSecondaryCameraScreenshot() const {
|
||||
QImage OpenGLDisplayPlugin::getSecondaryCameraScreenshot() {
|
||||
auto textureCache = DependencyManager::get<TextureCache>();
|
||||
auto secondaryCameraFramebuffer = textureCache->getSpectatorCameraFramebuffer();
|
||||
gpu::Vec4i region(0, 0, secondaryCameraFramebuffer->getWidth(), secondaryCameraFramebuffer->getHeight());
|
||||
|
||||
auto glBackend = const_cast<OpenGLDisplayPlugin&>(*this).getGLBackend();
|
||||
QImage screenshot(region.z, region.w, QImage::Format_ARGB32);
|
||||
withOtherThreadContext([&] {
|
||||
glBackend->downloadFramebuffer(secondaryCameraFramebuffer, region, screenshot);
|
||||
});
|
||||
getGLBackend()->downloadFramebuffer(secondaryCameraFramebuffer, region, screenshot);
|
||||
return screenshot.mirrored(false, true);
|
||||
}
|
||||
|
||||
|
|
|
@ -60,8 +60,6 @@ public:
|
|||
|
||||
virtual bool setDisplayTexture(const QString& name) override;
|
||||
virtual bool onDisplayTextureReset() { return false; };
|
||||
QImage getScreenshot(float aspectRatio = 0.0f) const override;
|
||||
QImage getSecondaryCameraScreenshot() const override;
|
||||
|
||||
float presentRate() const override;
|
||||
|
||||
|
@ -185,5 +183,8 @@ protected:
|
|||
// be serialized through this mutex
|
||||
mutable Mutex _presentMutex;
|
||||
float _hudAlpha{ 1.0f };
|
||||
|
||||
QImage getScreenshot(float aspectRatio);
|
||||
QImage getSecondaryCameraScreenshot();
|
||||
};
|
||||
|
||||
|
|
|
@ -199,7 +199,7 @@ void HmdDisplayPlugin::internalPresent() {
|
|||
float newWidth = sourceSize.x - shiftLeftBy;
|
||||
|
||||
// Experimentally adjusted the region presented in preview to avoid seeing the masked pixels and recenter the center...
|
||||
static float SCALE_WIDTH = 0.9f;
|
||||
static float SCALE_WIDTH = 0.8f;
|
||||
static float SCALE_OFFSET = 2.0f;
|
||||
newWidth *= SCALE_WIDTH;
|
||||
shiftLeftBy *= SCALE_OFFSET;
|
||||
|
|
|
@ -48,6 +48,8 @@ public:
|
|||
|
||||
void pluginUpdate() override {};
|
||||
|
||||
virtual StencilMaskMode getStencilMaskMode() const override { return StencilMaskMode::PAINT; }
|
||||
|
||||
signals:
|
||||
void hmdMountedChanged();
|
||||
void hmdVisibleChanged(bool visible);
|
||||
|
|
|
@ -791,7 +791,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
bool otherOverwrites = overwriteLocalData && !weOwnSimulation;
|
||||
// calculate hasGrab once outside the lambda rather than calling it every time inside
|
||||
bool hasGrab = stillHasGrabAction();
|
||||
auto shouldUpdate = [this, lastEdited, otherOverwrites, filterRejection, hasGrab](quint64 updatedTimestamp, bool valueChanged) {
|
||||
auto shouldUpdate = [lastEdited, otherOverwrites, filterRejection, hasGrab](quint64 updatedTimestamp, bool valueChanged) {
|
||||
if (hasGrab) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -909,7 +909,6 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
|
|||
auto& node = _file.nodes[nodeIndex];
|
||||
|
||||
if (node.defined["mesh"]) {
|
||||
qCDebug(modelformat) << "node_transforms" << node.transforms;
|
||||
foreach(auto &primitive, _file.meshes[node.mesh].primitives) {
|
||||
hfmModel.meshes.append(HFMMesh());
|
||||
HFMMesh& mesh = hfmModel.meshes[hfmModel.meshes.size() - 1];
|
||||
|
@ -1276,7 +1275,6 @@ HFMTexture GLTFSerializer::getHFMTexture(const GLTFTexture& texture) {
|
|||
|
||||
QString fname = hifi::URL(url).fileName();
|
||||
hifi::URL textureUrl = _url.resolved(url);
|
||||
qCDebug(modelformat) << "fname: " << fname;
|
||||
fbxtex.name = fname;
|
||||
fbxtex.filename = textureUrl.toEncoded();
|
||||
|
||||
|
@ -1370,10 +1368,7 @@ bool GLTFSerializer::readArray(const hifi::ByteArray& bin, int byteOffset, int c
|
|||
blobstream.setByteOrder(QDataStream::LittleEndian);
|
||||
blobstream.setVersion(QDataStream::Qt_5_9);
|
||||
blobstream.setFloatingPointPrecision(QDataStream::FloatingPointPrecision::SinglePrecision);
|
||||
|
||||
qCDebug(modelformat) << "size1: " << count;
|
||||
int dataskipped = blobstream.skipRawData(byteOffset);
|
||||
qCDebug(modelformat) << "dataskipped: " << dataskipped;
|
||||
blobstream.skipRawData(byteOffset);
|
||||
|
||||
int bufferCount = 0;
|
||||
switch (accessorType) {
|
||||
|
@ -1467,6 +1462,38 @@ void GLTFSerializer::retriangulate(const QVector<int>& inIndices, const QVector<
|
|||
}
|
||||
}
|
||||
|
||||
void GLTFSerializer::glTFDebugDump() {
|
||||
qCDebug(modelformat) << "---------------- Nodes ----------------";
|
||||
for (GLTFNode node : _file.nodes) {
|
||||
if (node.defined["mesh"]) {
|
||||
qCDebug(modelformat) << "\n";
|
||||
qCDebug(modelformat) << " node_transforms" << node.transforms;
|
||||
qCDebug(modelformat) << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
qCDebug(modelformat) << "---------------- Accessors ----------------";
|
||||
for (GLTFAccessor accessor : _file.accessors) {
|
||||
qCDebug(modelformat) << "\n";
|
||||
qCDebug(modelformat) << "count: " << accessor.count;
|
||||
qCDebug(modelformat) << "byteOffset: " << accessor.byteOffset;
|
||||
qCDebug(modelformat) << "\n";
|
||||
}
|
||||
|
||||
qCDebug(modelformat) << "---------------- Textures ----------------";
|
||||
for (GLTFTexture texture : _file.textures) {
|
||||
if (texture.defined["source"]) {
|
||||
qCDebug(modelformat) << "\n";
|
||||
QString url = _file.images[texture.source].uri;
|
||||
QString fname = hifi::URL(url).fileName();
|
||||
qCDebug(modelformat) << "fname: " << fname;
|
||||
qCDebug(modelformat) << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
qCDebug(modelformat) << "\n";
|
||||
}
|
||||
|
||||
void GLTFSerializer::hfmDebugDump(const HFMModel& hfmModel) {
|
||||
qCDebug(modelformat) << "---------------- hfmModel ----------------";
|
||||
qCDebug(modelformat) << " hasSkeletonJoints =" << hfmModel.hasSkeletonJoints;
|
||||
|
@ -1607,5 +1634,8 @@ void GLTFSerializer::hfmDebugDump(const HFMModel& hfmModel) {
|
|||
qCDebug(modelformat) << "\n";
|
||||
}
|
||||
|
||||
qCDebug(modelformat) << "---------------- GLTF Model ----------------";
|
||||
glTFDebugDump();
|
||||
|
||||
qCDebug(modelformat) << "\n";
|
||||
}
|
||||
|
|
|
@ -784,6 +784,7 @@ private:
|
|||
|
||||
void setHFMMaterial(HFMMaterial& fbxmat, const GLTFMaterial& material);
|
||||
HFMTexture getHFMTexture(const GLTFTexture& texture);
|
||||
void glTFDebugDump();
|
||||
void hfmDebugDump(const HFMModel& hfmModel);
|
||||
};
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#define hifi_gpu_Frame_h
|
||||
|
||||
#include <functional>
|
||||
#include <queue>
|
||||
|
||||
#include "Forward.h"
|
||||
#include "Batch.h"
|
||||
|
@ -41,6 +42,8 @@ namespace gpu {
|
|||
/// How to process the framebuffer when the frame dies. MUST BE THREAD SAFE
|
||||
FramebufferRecycler framebufferRecycler;
|
||||
|
||||
std::queue<std::tuple<std::function<void(const QImage&)>, float, bool>> snapshotOperators;
|
||||
|
||||
protected:
|
||||
friend class Deserializer;
|
||||
|
||||
|
|
|
@ -635,11 +635,9 @@ void NetworkTexture::makeLocalRequest() {
|
|||
}
|
||||
|
||||
bool NetworkTexture::handleFailedRequest(ResourceRequest::Result result) {
|
||||
if (_currentlyLoadingResourceType != ResourceType::KTX
|
||||
&& result == ResourceRequest::Result::RedirectFail) {
|
||||
|
||||
if (_shouldFailOnRedirect && result == ResourceRequest::Result::RedirectFail) {
|
||||
auto newPath = _request->getRelativePathUrl();
|
||||
if (newPath.fileName().endsWith(".ktx")) {
|
||||
if (newPath.fileName().toLower().endsWith(".ktx")) {
|
||||
_currentlyLoadingResourceType = ResourceType::KTX;
|
||||
_activeUrl = newPath;
|
||||
_shouldFailOnRedirect = false;
|
||||
|
|
|
@ -52,7 +52,7 @@ std::vector<hifi::ByteArray> createMaterialList(const hfm::Mesh& mesh) {
|
|||
}
|
||||
|
||||
std::unique_ptr<draco::Mesh> createDracoMesh(const hfm::Mesh& mesh, const std::vector<glm::vec3>& normals, const std::vector<glm::vec3>& tangents, const std::vector<hifi::ByteArray>& materialList) {
|
||||
Q_ASSERT(normals.size() == 0 || normals.size() == mesh.vertices.size());
|
||||
Q_ASSERT(normals.size() == 0 || (int)normals.size() == mesh.vertices.size());
|
||||
Q_ASSERT(mesh.colors.size() == 0 || mesh.colors.size() == mesh.vertices.size());
|
||||
Q_ASSERT(mesh.texCoords.size() == 0 || mesh.texCoords.size() == mesh.vertices.size());
|
||||
|
||||
|
|
|
@ -31,8 +31,6 @@
|
|||
|
||||
Q_LOGGING_CATEGORY(trace_resource_parse_geometry, "trace.resource.parse.geometry")
|
||||
|
||||
class GeometryReader;
|
||||
|
||||
class GeometryExtra {
|
||||
public:
|
||||
const GeometryMappingPair& mapping;
|
||||
|
@ -87,113 +85,6 @@ namespace std {
|
|||
};
|
||||
}
|
||||
|
||||
QUrl resolveTextureBaseUrl(const QUrl& url, const QUrl& textureBaseUrl) {
|
||||
return textureBaseUrl.isValid() ? textureBaseUrl : url;
|
||||
}
|
||||
|
||||
class GeometryMappingResource : public GeometryResource {
|
||||
Q_OBJECT
|
||||
public:
|
||||
GeometryMappingResource(const QUrl& url) : GeometryResource(url) {};
|
||||
|
||||
QString getType() const override { return "GeometryMapping"; }
|
||||
|
||||
virtual void downloadFinished(const QByteArray& data) override;
|
||||
|
||||
private slots:
|
||||
void onGeometryMappingLoaded(bool success);
|
||||
|
||||
private:
|
||||
GeometryResource::Pointer _geometryResource;
|
||||
QMetaObject::Connection _connection;
|
||||
};
|
||||
|
||||
void GeometryMappingResource::downloadFinished(const QByteArray& data) {
|
||||
PROFILE_ASYNC_BEGIN(resource_parse_geometry, "GeometryMappingResource::downloadFinished", _url.toString(),
|
||||
{ { "url", _url.toString() } });
|
||||
|
||||
// store parsed contents of FST file
|
||||
_mapping = FSTReader::readMapping(data);
|
||||
|
||||
QString filename = _mapping.value("filename").toString();
|
||||
|
||||
if (filename.isNull()) {
|
||||
finishedLoading(false);
|
||||
} else {
|
||||
const QString baseURL = _mapping.value("baseURL").toString();
|
||||
const QUrl base = _effectiveBaseURL.resolved(baseURL);
|
||||
QUrl url = base.resolved(filename);
|
||||
|
||||
QString texdir = _mapping.value(TEXDIR_FIELD).toString();
|
||||
if (!texdir.isNull()) {
|
||||
if (!texdir.endsWith('/')) {
|
||||
texdir += '/';
|
||||
}
|
||||
_textureBaseUrl = resolveTextureBaseUrl(url, base.resolved(texdir));
|
||||
} else {
|
||||
_textureBaseUrl = url.resolved(QUrl("."));
|
||||
}
|
||||
|
||||
auto scripts = FSTReader::getScripts(base, _mapping);
|
||||
if (scripts.size() > 0) {
|
||||
_mapping.remove(SCRIPT_FIELD);
|
||||
for (auto &scriptPath : scripts) {
|
||||
_mapping.insertMulti(SCRIPT_FIELD, scriptPath);
|
||||
}
|
||||
}
|
||||
|
||||
auto animGraphVariant = _mapping.value("animGraphUrl");
|
||||
|
||||
if (animGraphVariant.isValid()) {
|
||||
QUrl fstUrl(animGraphVariant.toString());
|
||||
if (fstUrl.isValid()) {
|
||||
_animGraphOverrideUrl = base.resolved(fstUrl);
|
||||
} else {
|
||||
_animGraphOverrideUrl = QUrl();
|
||||
}
|
||||
} else {
|
||||
_animGraphOverrideUrl = QUrl();
|
||||
}
|
||||
|
||||
auto modelCache = DependencyManager::get<ModelCache>();
|
||||
GeometryExtra extra { GeometryMappingPair(base, _mapping), _textureBaseUrl, false };
|
||||
|
||||
// Get the raw GeometryResource
|
||||
_geometryResource = modelCache->getResource(url, QUrl(), &extra, std::hash<GeometryExtra>()(extra)).staticCast<GeometryResource>();
|
||||
// Avoid caching nested resources - their references will be held by the parent
|
||||
_geometryResource->_isCacheable = false;
|
||||
|
||||
if (_geometryResource->isLoaded()) {
|
||||
onGeometryMappingLoaded(!_geometryResource->getURL().isEmpty());
|
||||
} else {
|
||||
if (_connection) {
|
||||
disconnect(_connection);
|
||||
}
|
||||
|
||||
_connection = connect(_geometryResource.data(), &Resource::finished,
|
||||
this, &GeometryMappingResource::onGeometryMappingLoaded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GeometryMappingResource::onGeometryMappingLoaded(bool success) {
|
||||
if (success && _geometryResource) {
|
||||
_hfmModel = _geometryResource->_hfmModel;
|
||||
_materialMapping = _geometryResource->_materialMapping;
|
||||
_meshParts = _geometryResource->_meshParts;
|
||||
_meshes = _geometryResource->_meshes;
|
||||
_materials = _geometryResource->_materials;
|
||||
|
||||
// Avoid holding onto extra references
|
||||
_geometryResource.reset();
|
||||
// Make sure connection will not trigger again
|
||||
disconnect(_connection); // FIXME Should not have to do this
|
||||
}
|
||||
|
||||
PROFILE_ASYNC_END(resource_parse_geometry, "GeometryMappingResource::downloadFinished", _url.toString());
|
||||
finishedLoading(success);
|
||||
}
|
||||
|
||||
class GeometryReader : public QRunnable {
|
||||
public:
|
||||
GeometryReader(const ModelLoader& modelLoader, QWeakPointer<Resource>& resource, const QUrl& url, const GeometryMappingPair& mapping,
|
||||
|
@ -308,47 +199,124 @@ void GeometryReader::run() {
|
|||
}
|
||||
}
|
||||
|
||||
class GeometryDefinitionResource : public GeometryResource {
|
||||
Q_OBJECT
|
||||
public:
|
||||
GeometryDefinitionResource(const ModelLoader& modelLoader, const QUrl& url) : GeometryResource(url), _modelLoader(modelLoader) {}
|
||||
GeometryDefinitionResource(const GeometryDefinitionResource& other) :
|
||||
GeometryResource(other),
|
||||
_modelLoader(other._modelLoader),
|
||||
_mapping(other._mapping),
|
||||
_combineParts(other._combineParts) {}
|
||||
QUrl resolveTextureBaseUrl(const QUrl& url, const QUrl& textureBaseUrl) {
|
||||
return textureBaseUrl.isValid() ? textureBaseUrl : url;
|
||||
}
|
||||
|
||||
QString getType() const override { return "GeometryDefinition"; }
|
||||
GeometryResource::GeometryResource(const GeometryResource& other) :
|
||||
Resource(other),
|
||||
Geometry(other),
|
||||
_modelLoader(other._modelLoader),
|
||||
_mappingPair(other._mappingPair),
|
||||
_textureBaseURL(other._textureBaseURL),
|
||||
_combineParts(other._combineParts),
|
||||
_isCacheable(other._isCacheable)
|
||||
{
|
||||
if (other._geometryResource) {
|
||||
_startedLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void downloadFinished(const QByteArray& data) override;
|
||||
void GeometryResource::downloadFinished(const QByteArray& data) {
|
||||
if (_effectiveBaseURL.fileName().toLower().endsWith(".fst")) {
|
||||
PROFILE_ASYNC_BEGIN(resource_parse_geometry, "GeometryResource::downloadFinished", _url.toString(), { { "url", _url.toString() } });
|
||||
|
||||
void setExtra(void* extra) override;
|
||||
// store parsed contents of FST file
|
||||
_mapping = FSTReader::readMapping(data);
|
||||
|
||||
protected:
|
||||
Q_INVOKABLE void setGeometryDefinition(HFMModel::Pointer hfmModel, const MaterialMapping& materialMapping);
|
||||
QString filename = _mapping.value("filename").toString();
|
||||
|
||||
private:
|
||||
ModelLoader _modelLoader;
|
||||
GeometryMappingPair _mapping;
|
||||
bool _combineParts;
|
||||
};
|
||||
if (filename.isNull()) {
|
||||
finishedLoading(false);
|
||||
} else {
|
||||
const QString baseURL = _mapping.value("baseURL").toString();
|
||||
const QUrl base = _effectiveBaseURL.resolved(baseURL);
|
||||
QUrl url = base.resolved(filename);
|
||||
|
||||
void GeometryDefinitionResource::setExtra(void* extra) {
|
||||
QString texdir = _mapping.value(TEXDIR_FIELD).toString();
|
||||
if (!texdir.isNull()) {
|
||||
if (!texdir.endsWith('/')) {
|
||||
texdir += '/';
|
||||
}
|
||||
_textureBaseURL = resolveTextureBaseUrl(url, base.resolved(texdir));
|
||||
} else {
|
||||
_textureBaseURL = url.resolved(QUrl("."));
|
||||
}
|
||||
|
||||
auto scripts = FSTReader::getScripts(base, _mapping);
|
||||
if (scripts.size() > 0) {
|
||||
_mapping.remove(SCRIPT_FIELD);
|
||||
for (auto &scriptPath : scripts) {
|
||||
_mapping.insertMulti(SCRIPT_FIELD, scriptPath);
|
||||
}
|
||||
}
|
||||
|
||||
auto animGraphVariant = _mapping.value("animGraphUrl");
|
||||
|
||||
if (animGraphVariant.isValid()) {
|
||||
QUrl fstUrl(animGraphVariant.toString());
|
||||
if (fstUrl.isValid()) {
|
||||
_animGraphOverrideUrl = base.resolved(fstUrl);
|
||||
} else {
|
||||
_animGraphOverrideUrl = QUrl();
|
||||
}
|
||||
} else {
|
||||
_animGraphOverrideUrl = QUrl();
|
||||
}
|
||||
|
||||
auto modelCache = DependencyManager::get<ModelCache>();
|
||||
GeometryExtra extra { GeometryMappingPair(base, _mapping), _textureBaseURL, false };
|
||||
|
||||
// Get the raw GeometryResource
|
||||
_geometryResource = modelCache->getResource(url, QUrl(), &extra, std::hash<GeometryExtra>()(extra)).staticCast<GeometryResource>();
|
||||
// Avoid caching nested resources - their references will be held by the parent
|
||||
_geometryResource->_isCacheable = false;
|
||||
|
||||
if (_geometryResource->isLoaded()) {
|
||||
onGeometryMappingLoaded(!_geometryResource->getURL().isEmpty());
|
||||
} else {
|
||||
if (_connection) {
|
||||
disconnect(_connection);
|
||||
}
|
||||
|
||||
_connection = connect(_geometryResource.data(), &Resource::finished, this, &GeometryResource::onGeometryMappingLoaded);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (_url != _effectiveBaseURL) {
|
||||
_url = _effectiveBaseURL;
|
||||
_textureBaseURL = _effectiveBaseURL;
|
||||
}
|
||||
QThreadPool::globalInstance()->start(new GeometryReader(_modelLoader, _self, _effectiveBaseURL, _mappingPair, data, _combineParts, _request->getWebMediaType()));
|
||||
}
|
||||
}
|
||||
|
||||
void GeometryResource::onGeometryMappingLoaded(bool success) {
|
||||
if (success && _geometryResource) {
|
||||
_hfmModel = _geometryResource->_hfmModel;
|
||||
_materialMapping = _geometryResource->_materialMapping;
|
||||
_meshParts = _geometryResource->_meshParts;
|
||||
_meshes = _geometryResource->_meshes;
|
||||
_materials = _geometryResource->_materials;
|
||||
|
||||
// Avoid holding onto extra references
|
||||
_geometryResource.reset();
|
||||
// Make sure connection will not trigger again
|
||||
disconnect(_connection); // FIXME Should not have to do this
|
||||
}
|
||||
|
||||
PROFILE_ASYNC_END(resource_parse_geometry, "GeometryResource::downloadFinished", _url.toString());
|
||||
finishedLoading(success);
|
||||
}
|
||||
|
||||
void GeometryResource::setExtra(void* extra) {
|
||||
const GeometryExtra* geometryExtra = static_cast<const GeometryExtra*>(extra);
|
||||
_mapping = geometryExtra ? geometryExtra->mapping : GeometryMappingPair(QUrl(), QVariantHash());
|
||||
_textureBaseUrl = geometryExtra ? resolveTextureBaseUrl(_url, geometryExtra->textureBaseUrl) : QUrl();
|
||||
_mappingPair = geometryExtra ? geometryExtra->mapping : GeometryMappingPair(QUrl(), QVariantHash());
|
||||
_textureBaseURL = geometryExtra ? resolveTextureBaseUrl(_url, geometryExtra->textureBaseUrl) : QUrl();
|
||||
_combineParts = geometryExtra ? geometryExtra->combineParts : true;
|
||||
}
|
||||
|
||||
void GeometryDefinitionResource::downloadFinished(const QByteArray& data) {
|
||||
if (_url != _effectiveBaseURL) {
|
||||
_url = _effectiveBaseURL;
|
||||
_textureBaseUrl = _effectiveBaseURL;
|
||||
}
|
||||
QThreadPool::globalInstance()->start(new GeometryReader(_modelLoader, _self, _effectiveBaseURL, _mapping, data, _combineParts, _request->getWebMediaType()));
|
||||
}
|
||||
|
||||
void GeometryDefinitionResource::setGeometryDefinition(HFMModel::Pointer hfmModel, const MaterialMapping& materialMapping) {
|
||||
void GeometryResource::setGeometryDefinition(HFMModel::Pointer hfmModel, const MaterialMapping& materialMapping) {
|
||||
// Assume ownership of the processed HFMModel
|
||||
_hfmModel = hfmModel;
|
||||
_materialMapping = materialMapping;
|
||||
|
@ -357,7 +325,7 @@ void GeometryDefinitionResource::setGeometryDefinition(HFMModel::Pointer hfmMode
|
|||
QHash<QString, size_t> materialIDAtlas;
|
||||
for (const HFMMaterial& material : _hfmModel->materials) {
|
||||
materialIDAtlas[material.materialID] = _materials.size();
|
||||
_materials.push_back(std::make_shared<NetworkMaterial>(material, _textureBaseUrl));
|
||||
_materials.push_back(std::make_shared<NetworkMaterial>(material, _textureBaseURL));
|
||||
}
|
||||
|
||||
std::shared_ptr<GeometryMeshes> meshes = std::make_shared<GeometryMeshes>();
|
||||
|
@ -380,6 +348,23 @@ void GeometryDefinitionResource::setGeometryDefinition(HFMModel::Pointer hfmMode
|
|||
finishedLoading(true);
|
||||
}
|
||||
|
||||
void GeometryResource::deleter() {
|
||||
resetTextures();
|
||||
Resource::deleter();
|
||||
}
|
||||
|
||||
void GeometryResource::setTextures() {
|
||||
if (_hfmModel) {
|
||||
for (const HFMMaterial& material : _hfmModel->materials) {
|
||||
_materials.push_back(std::make_shared<NetworkMaterial>(material, _textureBaseURL));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GeometryResource::resetTextures() {
|
||||
_materials.clear();
|
||||
}
|
||||
|
||||
ModelCache::ModelCache() {
|
||||
const qint64 GEOMETRY_DEFAULT_UNUSED_MAX_SIZE = DEFAULT_UNUSED_MAX_SIZE;
|
||||
setUnusedResourceCacheSize(GEOMETRY_DEFAULT_UNUSED_MAX_SIZE);
|
||||
|
@ -392,26 +377,14 @@ ModelCache::ModelCache() {
|
|||
}
|
||||
|
||||
QSharedPointer<Resource> ModelCache::createResource(const QUrl& url) {
|
||||
Resource* resource = nullptr;
|
||||
if (url.path().toLower().endsWith(".fst")) {
|
||||
resource = new GeometryMappingResource(url);
|
||||
} else {
|
||||
resource = new GeometryDefinitionResource(_modelLoader, url);
|
||||
}
|
||||
|
||||
return QSharedPointer<Resource>(resource, &Resource::deleter);
|
||||
return QSharedPointer<Resource>(new GeometryResource(url, _modelLoader), &GeometryResource::deleter);
|
||||
}
|
||||
|
||||
QSharedPointer<Resource> ModelCache::createResourceCopy(const QSharedPointer<Resource>& resource) {
|
||||
if (resource->getURL().path().toLower().endsWith(".fst")) {
|
||||
return QSharedPointer<Resource>(new GeometryMappingResource(*resource.staticCast<GeometryMappingResource>()), &Resource::deleter);
|
||||
} else {
|
||||
return QSharedPointer<Resource>(new GeometryDefinitionResource(*resource.staticCast<GeometryDefinitionResource>()), &Resource::deleter);
|
||||
}
|
||||
return QSharedPointer<Resource>(new GeometryResource(*resource.staticCast<GeometryResource>()), &GeometryResource::deleter);
|
||||
}
|
||||
|
||||
GeometryResource::Pointer ModelCache::getGeometryResource(const QUrl& url,
|
||||
const GeometryMappingPair& mapping, const QUrl& textureBaseUrl) {
|
||||
GeometryResource::Pointer ModelCache::getGeometryResource(const QUrl& url, const GeometryMappingPair& mapping, const QUrl& textureBaseUrl) {
|
||||
bool combineParts = true;
|
||||
GeometryExtra geometryExtra = { mapping, textureBaseUrl, combineParts };
|
||||
GeometryResource::Pointer resource = getResource(url, QUrl(), &geometryExtra, std::hash<GeometryExtra>()(geometryExtra)).staticCast<GeometryResource>();
|
||||
|
@ -531,23 +504,6 @@ const std::shared_ptr<NetworkMaterial> Geometry::getShapeMaterial(int partID) co
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void GeometryResource::deleter() {
|
||||
resetTextures();
|
||||
Resource::deleter();
|
||||
}
|
||||
|
||||
void GeometryResource::setTextures() {
|
||||
if (_hfmModel) {
|
||||
for (const HFMMaterial& material : _hfmModel->materials) {
|
||||
_materials.push_back(std::make_shared<NetworkMaterial>(material, _textureBaseUrl));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GeometryResource::resetTextures() {
|
||||
_materials.clear();
|
||||
}
|
||||
|
||||
void GeometryResourceWatcher::startWatching() {
|
||||
connect(_resource.data(), &Resource::finished, this, &GeometryResourceWatcher::resourceFinished);
|
||||
connect(_resource.data(), &Resource::onRefresh, this, &GeometryResourceWatcher::resourceRefreshed);
|
||||
|
|
|
@ -24,8 +24,6 @@
|
|||
|
||||
class MeshPart;
|
||||
|
||||
class GeometryMappingResource;
|
||||
|
||||
using GeometryMappingPair = std::pair<QUrl, QVariantHash>;
|
||||
Q_DECLARE_METATYPE(GeometryMappingPair)
|
||||
|
||||
|
@ -60,8 +58,6 @@ public:
|
|||
const QVariantHash& getMapping() const { return _mapping; }
|
||||
|
||||
protected:
|
||||
friend class GeometryMappingResource;
|
||||
|
||||
// Shared across all geometries, constant throughout lifetime
|
||||
std::shared_ptr<const HFMModel> _hfmModel;
|
||||
MaterialMapping _materialMapping;
|
||||
|
@ -80,23 +76,29 @@ private:
|
|||
|
||||
/// A geometry loaded from the network.
|
||||
class GeometryResource : public Resource, public Geometry {
|
||||
Q_OBJECT
|
||||
public:
|
||||
using Pointer = QSharedPointer<GeometryResource>;
|
||||
|
||||
GeometryResource(const QUrl& url) : Resource(url) {}
|
||||
GeometryResource(const GeometryResource& other) :
|
||||
Resource(other),
|
||||
Geometry(other),
|
||||
_textureBaseUrl(other._textureBaseUrl),
|
||||
_isCacheable(other._isCacheable) {}
|
||||
GeometryResource(const QUrl& url, const ModelLoader& modelLoader) : Resource(url), _modelLoader(modelLoader) {}
|
||||
GeometryResource(const GeometryResource& other);
|
||||
|
||||
virtual bool areTexturesLoaded() const override { return isLoaded() && Geometry::areTexturesLoaded(); }
|
||||
QString getType() const override { return "Geometry"; }
|
||||
|
||||
virtual void deleter() override;
|
||||
|
||||
virtual void downloadFinished(const QByteArray& data) override;
|
||||
void setExtra(void* extra) override;
|
||||
|
||||
virtual bool areTexturesLoaded() const override { return isLoaded() && Geometry::areTexturesLoaded(); }
|
||||
|
||||
private slots:
|
||||
void onGeometryMappingLoaded(bool success);
|
||||
|
||||
protected:
|
||||
friend class ModelCache;
|
||||
friend class GeometryMappingResource;
|
||||
|
||||
Q_INVOKABLE void setGeometryDefinition(HFMModel::Pointer hfmModel, const MaterialMapping& materialMapping);
|
||||
|
||||
// Geometries may not hold onto textures while cached - that is for the texture cache
|
||||
// Instead, these methods clear and reset textures from the geometry when caching/loading
|
||||
|
@ -104,10 +106,18 @@ protected:
|
|||
void setTextures();
|
||||
void resetTextures();
|
||||
|
||||
QUrl _textureBaseUrl;
|
||||
|
||||
virtual bool isCacheable() const override { return _loaded && _isCacheable; }
|
||||
bool _isCacheable { true };
|
||||
|
||||
private:
|
||||
ModelLoader _modelLoader;
|
||||
GeometryMappingPair _mappingPair;
|
||||
QUrl _textureBaseURL;
|
||||
bool _combineParts;
|
||||
|
||||
GeometryResource::Pointer _geometryResource;
|
||||
QMetaObject::Connection _connection;
|
||||
|
||||
bool _isCacheable{ true };
|
||||
};
|
||||
|
||||
class GeometryResourceWatcher : public QObject {
|
||||
|
@ -158,7 +168,7 @@ public:
|
|||
const QUrl& textureBaseUrl = QUrl());
|
||||
|
||||
protected:
|
||||
friend class GeometryMappingResource;
|
||||
friend class GeometryResource;
|
||||
|
||||
virtual QSharedPointer<Resource> createResource(const QUrl& url) override;
|
||||
QSharedPointer<Resource> createResourceCopy(const QSharedPointer<Resource>& resource) override;
|
||||
|
|
|
@ -576,6 +576,7 @@ Resource::Resource(const Resource& other) :
|
|||
|
||||
Resource::Resource(const QUrl& url) :
|
||||
_url(url),
|
||||
_effectiveBaseURL(url),
|
||||
_activeUrl(url),
|
||||
_requestID(++requestID) {
|
||||
init();
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
|
||||
static AAssetManager* ASSET_MANAGER = nullptr;
|
||||
|
||||
#define USE_BLIT_PRESENT 0
|
||||
#define USE_BLIT_PRESENT 1
|
||||
|
||||
#if !USE_BLIT_PRESENT
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <SimpleMovingAverage.h>
|
||||
#include <gpu/Forward.h>
|
||||
#include "Plugin.h"
|
||||
#include "StencilMaskMode.h"
|
||||
|
||||
class QOpenGLFramebufferObject;
|
||||
|
||||
|
@ -172,10 +173,6 @@ public:
|
|||
return QRect(0, 0, recommendedSize.x, recommendedSize.y);
|
||||
}
|
||||
|
||||
// Fetch the most recently displayed image as a QImage
|
||||
virtual QImage getScreenshot(float aspectRatio = 0.0f) const = 0;
|
||||
virtual QImage getSecondaryCameraScreenshot() const = 0;
|
||||
|
||||
// will query the underlying hmd api to compute the most recent head pose
|
||||
virtual bool beginFrameRender(uint32_t frameIndex) { return true; }
|
||||
|
||||
|
@ -221,6 +218,10 @@ public:
|
|||
// for updating plugin-related commands. Mimics the input plugin.
|
||||
virtual void pluginUpdate() = 0;
|
||||
|
||||
virtual StencilMaskMode getStencilMaskMode() const { return StencilMaskMode::NONE; }
|
||||
using StencilMaskMeshOperator = std::function<void(gpu::Batch&)>;
|
||||
virtual StencilMaskMeshOperator getStencilMaskMeshOperator() { return nullptr; }
|
||||
|
||||
signals:
|
||||
void recommendedFramebufferSizeChanged(const QSize& size);
|
||||
void resetSensorsRequested();
|
||||
|
|
|
@ -19,7 +19,6 @@ using namespace render;
|
|||
|
||||
void PrepareStencil::configure(const Config& config) {
|
||||
_maskMode = config.maskMode;
|
||||
_forceDraw = config.forceDraw;
|
||||
}
|
||||
|
||||
graphics::MeshPointer PrepareStencil::getMesh() {
|
||||
|
@ -43,6 +42,7 @@ gpu::PipelinePointer PrepareStencil::getMeshStencilPipeline() {
|
|||
auto state = std::make_shared<gpu::State>();
|
||||
drawMask(*state);
|
||||
state->setColorWriteMask(gpu::State::WRITE_NONE);
|
||||
state->setCullMode(gpu::State::CullMode::CULL_NONE);
|
||||
|
||||
_meshStencilPipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
|
@ -64,8 +64,28 @@ gpu::PipelinePointer PrepareStencil::getPaintStencilPipeline() {
|
|||
void PrepareStencil::run(const RenderContextPointer& renderContext, const gpu::FramebufferPointer& srcFramebuffer) {
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
// Only draw the stencil mask if in HMD mode or not forced.
|
||||
if (!_forceDraw && (args->_displayMode != RenderArgs::STEREO_HMD)) {
|
||||
if (args->_takingSnapshot) {
|
||||
return;
|
||||
}
|
||||
|
||||
StencilMaskMode maskMode = _maskMode;
|
||||
std::function<void(gpu::Batch&)> maskOperator = [this](gpu::Batch& batch) {
|
||||
auto mesh = getMesh();
|
||||
batch.setIndexBuffer(mesh->getIndexBuffer());
|
||||
batch.setInputFormat((mesh->getVertexFormat()));
|
||||
batch.setInputStream(0, mesh->getVertexStream());
|
||||
|
||||
// Draw
|
||||
auto part = mesh->getPartBuffer().get<graphics::Mesh::Part>(0);
|
||||
batch.drawIndexed(gpu::TRIANGLES, part._numIndices, part._startIndex);
|
||||
};
|
||||
|
||||
if (maskMode == StencilMaskMode::NONE) {
|
||||
maskMode = args->_stencilMaskMode;
|
||||
maskOperator = args->_stencilMaskOperator;
|
||||
}
|
||||
|
||||
if (maskMode == StencilMaskMode::NONE || (maskMode == StencilMaskMode::MESH && !maskOperator)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -74,20 +94,12 @@ void PrepareStencil::run(const RenderContextPointer& renderContext, const gpu::F
|
|||
|
||||
batch.setViewportTransform(args->_viewport);
|
||||
|
||||
if (_maskMode < 0) {
|
||||
batch.setPipeline(getMeshStencilPipeline());
|
||||
|
||||
auto mesh = getMesh();
|
||||
batch.setIndexBuffer(mesh->getIndexBuffer());
|
||||
batch.setInputFormat((mesh->getVertexFormat()));
|
||||
batch.setInputStream(0, mesh->getVertexStream());
|
||||
|
||||
// Draw
|
||||
auto part = mesh->getPartBuffer().get<graphics::Mesh::Part>(0);
|
||||
batch.drawIndexed(gpu::TRIANGLES, part._numIndices, part._startIndex);
|
||||
} else {
|
||||
if (maskMode == StencilMaskMode::PAINT) {
|
||||
batch.setPipeline(getPaintStencilPipeline());
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
} else if (maskMode == StencilMaskMode::MESH) {
|
||||
batch.setPipeline(getMeshStencilPipeline());
|
||||
maskOperator(batch);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -15,17 +15,19 @@
|
|||
#include <render/Engine.h>
|
||||
#include <gpu/Pipeline.h>
|
||||
#include <graphics/Geometry.h>
|
||||
#include <StencilMaskMode.h>
|
||||
|
||||
class PrepareStencilConfig : public render::Job::Config {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int maskMode MEMBER maskMode NOTIFY dirty)
|
||||
Q_PROPERTY(bool forceDraw MEMBER forceDraw NOTIFY dirty)
|
||||
Q_PROPERTY(StencilMaskMode maskMode MEMBER maskMode NOTIFY dirty)
|
||||
|
||||
public:
|
||||
PrepareStencilConfig(bool enabled = true) : JobConfig(enabled) {}
|
||||
|
||||
int maskMode { 0 };
|
||||
bool forceDraw { false };
|
||||
// -1 -> don't force drawing (fallback to render args mode)
|
||||
// 0 -> force draw without mesh
|
||||
// 1 -> force draw with mesh
|
||||
StencilMaskMode maskMode { StencilMaskMode::NONE };
|
||||
|
||||
signals:
|
||||
void dirty();
|
||||
|
@ -66,8 +68,7 @@ private:
|
|||
graphics::MeshPointer _mesh;
|
||||
graphics::MeshPointer getMesh();
|
||||
|
||||
int _maskMode { 0 };
|
||||
bool _forceDraw { false };
|
||||
StencilMaskMode _maskMode { StencilMaskMode::NONE };
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include <GLMHelpers.h>
|
||||
#include <ViewFrustum.h>
|
||||
#include <StencilMaskMode.h>
|
||||
|
||||
#include <gpu/Forward.h>
|
||||
#include "Forward.h"
|
||||
|
@ -133,6 +134,10 @@ namespace render {
|
|||
|
||||
std::function<void(gpu::Batch&, const gpu::TexturePointer&, bool mirror)> _hudOperator;
|
||||
gpu::TexturePointer _hudTexture;
|
||||
|
||||
bool _takingSnapshot { false };
|
||||
StencilMaskMode _stencilMaskMode { StencilMaskMode::NONE };
|
||||
std::function<void(gpu::Batch&)> _stencilMaskOperator;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -185,6 +185,30 @@ QString PathUtils::generateTemporaryDir() {
|
|||
return "";
|
||||
}
|
||||
|
||||
bool PathUtils::deleteMyTemporaryDir(QString dirName) {
|
||||
QDir rootTempDir = QDir::tempPath();
|
||||
|
||||
QString appName = qApp->applicationName();
|
||||
QRegularExpression re { "^" + QRegularExpression::escape(appName) + "\\-(?<pid>\\d+)\\-(?<timestamp>\\d+)$" };
|
||||
|
||||
auto match = re.match(dirName);
|
||||
auto pid = match.capturedRef("pid").toLongLong();
|
||||
|
||||
if (match.hasMatch() && rootTempDir.exists(dirName) && pid == qApp->applicationPid()) {
|
||||
auto absoluteDirPath = QDir(rootTempDir.absoluteFilePath(dirName));
|
||||
|
||||
bool success = absoluteDirPath.removeRecursively();
|
||||
if (success) {
|
||||
qDebug() << " Removing temporary directory: " << absoluteDirPath.absolutePath();
|
||||
} else {
|
||||
qDebug() << " Failed to remove temporary directory: " << absoluteDirPath.absolutePath();
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Delete all temporary directories for an application
|
||||
int PathUtils::removeTemporaryApplicationDirs(QString appName) {
|
||||
if (appName.isNull()) {
|
||||
|
|
|
@ -56,6 +56,7 @@ public:
|
|||
static QString getAppLocalDataFilePath(const QString& filename);
|
||||
|
||||
static QString generateTemporaryDir();
|
||||
static bool deleteMyTemporaryDir(QString dirName);
|
||||
|
||||
static int removeTemporaryApplicationDirs(QString appName = QString::null);
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ int qMapURLStringMetaTypeId = qRegisterMetaType<QMap<QUrl,QString>>();
|
|||
int socketErrorMetaTypeId = qRegisterMetaType<QAbstractSocket::SocketError>();
|
||||
int voidLambdaType = qRegisterMetaType<std::function<void()>>();
|
||||
int variantLambdaType = qRegisterMetaType<std::function<QVariant()>>();
|
||||
int stencilModeMetaTypeId = qRegisterMetaType<StencilMaskMode>();
|
||||
|
||||
void registerMetaTypes(QScriptEngine* engine) {
|
||||
qScriptRegisterMetaType(engine, vec2ToScriptValue, vec2FromScriptValue);
|
||||
|
@ -64,6 +65,8 @@ void registerMetaTypes(QScriptEngine* engine) {
|
|||
qScriptRegisterMetaType(engine, collisionToScriptValue, collisionFromScriptValue);
|
||||
qScriptRegisterMetaType(engine, quuidToScriptValue, quuidFromScriptValue);
|
||||
qScriptRegisterMetaType(engine, aaCubeToScriptValue, aaCubeFromScriptValue);
|
||||
|
||||
qScriptRegisterMetaType(engine, stencilMaskModeToScriptValue, stencilMaskModeFromScriptValue);
|
||||
}
|
||||
|
||||
QScriptValue vec2ToScriptValue(QScriptEngine* engine, const glm::vec2& vec2) {
|
||||
|
@ -1358,4 +1361,12 @@ QVariantMap parseTexturesToMap(QString newTextures, const QVariantMap& defaultTe
|
|||
}
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
QScriptValue stencilMaskModeToScriptValue(QScriptEngine* engine, const StencilMaskMode& stencilMode) {
|
||||
return engine->newVariant((int)stencilMode);
|
||||
}
|
||||
|
||||
void stencilMaskModeFromScriptValue(const QScriptValue& object, StencilMaskMode& stencilMode) {
|
||||
stencilMode = StencilMaskMode(object.toVariant().toInt());
|
||||
}
|
|
@ -25,6 +25,7 @@
|
|||
#include "shared/Bilateral.h"
|
||||
#include "Transform.h"
|
||||
#include "PhysicsCollisionGroups.h"
|
||||
#include "StencilMaskMode.h"
|
||||
|
||||
class QColor;
|
||||
class QUrl;
|
||||
|
@ -733,5 +734,8 @@ void qVectorMeshFaceFromScriptValue(const QScriptValue& array, QVector<MeshFace>
|
|||
|
||||
QVariantMap parseTexturesToMap(QString textures, const QVariantMap& defaultTextures);
|
||||
|
||||
Q_DECLARE_METATYPE(StencilMaskMode)
|
||||
QScriptValue stencilMaskModeToScriptValue(QScriptEngine* engine, const StencilMaskMode& stencilMode);
|
||||
void stencilMaskModeFromScriptValue(const QScriptValue& object, StencilMaskMode& stencilMode);
|
||||
|
||||
#endif // hifi_RegisteredMetaTypes_h
|
||||
|
|
18
libraries/shared/src/StencilMaskMode.h
Normal file
18
libraries/shared/src/StencilMaskMode.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
//
|
||||
// Created by Sam Gondelman on 3/26/19.
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_StencilMaskMode_h
|
||||
#define hifi_StencilMaskMode_h
|
||||
|
||||
enum class StencilMaskMode {
|
||||
NONE = -1, // for legacy reasons, this is -1
|
||||
PAINT = 0,
|
||||
MESH = 1
|
||||
};
|
||||
|
||||
#endif // hifi_StencilMaskMode_h
|
|
@ -44,7 +44,7 @@ QScriptValue variantToScriptValue(QVariant& qValue, QScriptEngine& scriptEngine)
|
|||
if (qValue.canConvert<float>()) {
|
||||
return qValue.toFloat();
|
||||
}
|
||||
qCDebug(shared) << "unhandled QScript type" << qValue.type();
|
||||
//qCDebug(shared) << "unhandled QScript type" << qValue.type();
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ if (WIN32 AND (NOT USE_GLES))
|
|||
link_hifi_libraries(
|
||||
shared task gl shaders gpu ${PLATFORM_GL_BACKEND} controllers ui qml
|
||||
plugins ui-plugins display-plugins input-plugins
|
||||
audio-client networking render-utils
|
||||
audio-client networking render-utils graphics
|
||||
${PLATFORM_GL_BACKEND}
|
||||
)
|
||||
include_hifi_library_headers(octree)
|
||||
|
|
|
@ -227,3 +227,66 @@ QVector<glm::vec3> OculusBaseDisplayPlugin::getSensorPositions() {
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
DisplayPlugin::StencilMaskMeshOperator OculusBaseDisplayPlugin::getStencilMaskMeshOperator() {
|
||||
if (_session) {
|
||||
if (!_stencilMeshesInitialized) {
|
||||
_stencilMeshesInitialized = true;
|
||||
ovr::for_each_eye([&](ovrEyeType eye) {
|
||||
ovrFovStencilDesc stencilDesc = {
|
||||
ovrFovStencil_HiddenArea, 0, eye,
|
||||
_eyeRenderDescs[eye].Fov, _eyeRenderDescs[eye].HmdToEyePose.Orientation
|
||||
};
|
||||
// First we get the size of the buffer we need
|
||||
ovrFovStencilMeshBuffer buffer = { 0, 0, nullptr, 0, 0, nullptr };
|
||||
ovrResult result = ovr_GetFovStencil(_session, &stencilDesc, &buffer);
|
||||
if (!OVR_SUCCESS(result)) {
|
||||
_stencilMeshesInitialized = false;
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<ovrVector2f> ovrVertices(buffer.UsedVertexCount);
|
||||
std::vector<uint16_t> ovrIndices(buffer.UsedIndexCount);
|
||||
|
||||
// Now we populate the actual buffer
|
||||
buffer = { (int)ovrVertices.size(), 0, ovrVertices.data(), (int)ovrIndices.size(), 0, ovrIndices.data() };
|
||||
result = ovr_GetFovStencil(_session, &stencilDesc, &buffer);
|
||||
|
||||
if (!OVR_SUCCESS(result)) {
|
||||
_stencilMeshesInitialized = false;
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<glm::vec3> vertices;
|
||||
vertices.reserve(ovrVertices.size());
|
||||
for (auto& ovrVertex : ovrVertices) {
|
||||
// We need the vertices in clip space
|
||||
vertices.emplace_back(ovrVertex.x - (1.0f - (float)eye), 2.0f * ovrVertex.y - 1.0f, 0.0f);
|
||||
}
|
||||
|
||||
std::vector<uint32_t> indices;
|
||||
indices.reserve(ovrIndices.size());
|
||||
for (auto& ovrIndex : ovrIndices) {
|
||||
indices.push_back(ovrIndex);
|
||||
}
|
||||
|
||||
_stencilMeshes[eye] = graphics::Mesh::createIndexedTriangles_P3F((uint32_t)vertices.size(), (uint32_t)indices.size(), vertices.data(), indices.data());
|
||||
});
|
||||
}
|
||||
|
||||
if (_stencilMeshesInitialized) {
|
||||
return [&](gpu::Batch& batch) {
|
||||
for (auto& mesh : _stencilMeshes) {
|
||||
batch.setIndexBuffer(mesh->getIndexBuffer());
|
||||
batch.setInputFormat((mesh->getVertexFormat()));
|
||||
batch.setInputStream(0, mesh->getVertexStream());
|
||||
|
||||
// Draw
|
||||
auto part = mesh->getPartBuffer().get<graphics::Mesh::Part>(0);
|
||||
batch.drawIndexed(gpu::TRIANGLES, part._numIndices, part._startIndex);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
|
@ -16,6 +16,8 @@
|
|||
#define OVRPL_DISABLED
|
||||
#include <OVR_Platform.h>
|
||||
|
||||
#include <graphics/Geometry.h>
|
||||
|
||||
class OculusBaseDisplayPlugin : public HmdDisplayPlugin {
|
||||
using Parent = HmdDisplayPlugin;
|
||||
public:
|
||||
|
@ -34,6 +36,9 @@ public:
|
|||
QRectF getPlayAreaRect() override;
|
||||
QVector<glm::vec3> getSensorPositions() override;
|
||||
|
||||
virtual StencilMaskMode getStencilMaskMode() const override { return StencilMaskMode::MESH; }
|
||||
virtual StencilMaskMeshOperator getStencilMaskMeshOperator() override;
|
||||
|
||||
protected:
|
||||
void customizeContext() override;
|
||||
void uncustomizeContext() override;
|
||||
|
@ -52,4 +57,7 @@ protected:
|
|||
// ovrLayerEyeFovDepth _depthLayer;
|
||||
bool _hmdMounted { false };
|
||||
bool _visible { true };
|
||||
|
||||
std::array<graphics::MeshPointer, 2> _stencilMeshes;
|
||||
bool _stencilMeshesInitialized { false };
|
||||
};
|
||||
|
|
|
@ -784,3 +784,48 @@ QRectF OpenVrDisplayPlugin::getPlayAreaRect() {
|
|||
|
||||
return QRectF(center.x, center.y, dimensions.x, dimensions.y);
|
||||
}
|
||||
|
||||
DisplayPlugin::StencilMaskMeshOperator OpenVrDisplayPlugin::getStencilMaskMeshOperator() {
|
||||
if (_system) {
|
||||
if (!_stencilMeshesInitialized) {
|
||||
_stencilMeshesInitialized = true;
|
||||
for (auto eye : VR_EYES) {
|
||||
vr::HiddenAreaMesh_t stencilMesh = _system->GetHiddenAreaMesh(eye);
|
||||
if (stencilMesh.pVertexData && stencilMesh.unTriangleCount > 0) {
|
||||
std::vector<glm::vec3> vertices;
|
||||
std::vector<uint32_t> indices;
|
||||
|
||||
const int NUM_INDICES_PER_TRIANGLE = 3;
|
||||
int numIndices = stencilMesh.unTriangleCount * NUM_INDICES_PER_TRIANGLE;
|
||||
vertices.reserve(numIndices);
|
||||
indices.reserve(numIndices);
|
||||
for (int i = 0; i < numIndices; i++) {
|
||||
vr::HmdVector2_t vertex2D = stencilMesh.pVertexData[i];
|
||||
// We need the vertices in clip space
|
||||
vertices.emplace_back(vertex2D.v[0] - (1.0f - (float)eye), 2.0f * vertex2D.v[1] - 1.0f, 0.0f);
|
||||
indices.push_back(i);
|
||||
}
|
||||
|
||||
_stencilMeshes[eye] = graphics::Mesh::createIndexedTriangles_P3F((uint32_t)vertices.size(), (uint32_t)indices.size(), vertices.data(), indices.data());
|
||||
} else {
|
||||
_stencilMeshesInitialized = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_stencilMeshesInitialized) {
|
||||
return [&](gpu::Batch& batch) {
|
||||
for (auto& mesh : _stencilMeshes) {
|
||||
batch.setIndexBuffer(mesh->getIndexBuffer());
|
||||
batch.setInputFormat((mesh->getVertexFormat()));
|
||||
batch.setInputStream(0, mesh->getVertexStream());
|
||||
|
||||
// Draw
|
||||
auto part = mesh->getPartBuffer().get<graphics::Mesh::Part>(0);
|
||||
batch.drawIndexed(gpu::TRIANGLES, part._numIndices, part._startIndex);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
#include <display-plugins/hmd/HmdDisplayPlugin.h>
|
||||
|
||||
#include <graphics/Geometry.h>
|
||||
|
||||
const float TARGET_RATE_OpenVr = 90.0f; // FIXME: get from sdk tracked device property? This number is vive-only.
|
||||
|
||||
namespace gl {
|
||||
|
@ -67,6 +69,9 @@ public:
|
|||
|
||||
QRectF getPlayAreaRect() override;
|
||||
|
||||
virtual StencilMaskMode getStencilMaskMode() const override { return StencilMaskMode::MESH; }
|
||||
virtual StencilMaskMeshOperator getStencilMaskMeshOperator() override;
|
||||
|
||||
protected:
|
||||
bool internalActivate() override;
|
||||
void internalDeactivate() override;
|
||||
|
@ -94,4 +99,7 @@ private:
|
|||
bool _asyncReprojectionActive { false };
|
||||
|
||||
bool _hmdMounted { false };
|
||||
|
||||
std::array<graphics::MeshPointer, 2> _stencilMeshes;
|
||||
bool _stencilMeshesInitialized { false };
|
||||
};
|
||||
|
|
|
@ -10,7 +10,12 @@
|
|||
//
|
||||
"use strict";
|
||||
|
||||
// traverse task tree
|
||||
// traverse task tree recursively
|
||||
//
|
||||
// @param root: the root job config from where to traverse
|
||||
// @param functor: the functor function() which is applied on every subjobs of root traversed
|
||||
// if return true, then 'task_tree' is called recursively on that subjob
|
||||
// @param depth: the depth of the recurse loop since the initial call.
|
||||
function task_traverse(root, functor, depth) {
|
||||
if (root.isTask()) {
|
||||
depth++;
|
||||
|
@ -22,6 +27,9 @@ function task_traverse(root, functor, depth) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// same function as 'task_traverse' with the depth being 0
|
||||
// and visisting the root job first.
|
||||
function task_traverseTree(root, functor) {
|
||||
if (functor(root, 0, 0)) {
|
||||
task_traverse(root, functor, 0)
|
||||
|
|
123
scripts/developer/utilities/lib/jet/qml/TaskPropView.qml
Normal file
123
scripts/developer/utilities/lib/jet/qml/TaskPropView.qml
Normal file
|
@ -0,0 +1,123 @@
|
|||
//
|
||||
// jet/TaskPropView.qml
|
||||
//
|
||||
// Created by Sam Gateau, 2018/05/09
|
||||
// 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
|
||||
//
|
||||
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 1.4 as Original
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
|
||||
import stylesUit 1.0
|
||||
import controlsUit 1.0 as HifiControls
|
||||
|
||||
import "../../prop" as Prop
|
||||
|
||||
import "../jet.js" as Jet
|
||||
|
||||
Prop.PropGroup {
|
||||
|
||||
id: root;
|
||||
|
||||
property var rootConfig : Render
|
||||
property var jobPath: ""
|
||||
property alias label: root.label
|
||||
property alias indentDepth: root.indentDepth
|
||||
|
||||
property var showProps: true
|
||||
property var showSubs: true
|
||||
property var jobEnabled: rootConfig.getConfig(jobPath).enabled
|
||||
property var jobCpuTime: pullCpuTime()
|
||||
|
||||
function pullCpuTime() {
|
||||
if (jobEnabled) {
|
||||
return rootConfig.getConfig(jobPath).cpuRunTime.toPrecision(3);
|
||||
} else {
|
||||
return '.'
|
||||
}
|
||||
}
|
||||
|
||||
property var toggleJobActivation: function() {
|
||||
console.log("the button has been pressed and jobEnabled is " + jobEnabled )
|
||||
jobEnabled = !jobEnabled;
|
||||
rootConfig.getConfig(jobPath).enabled = jobEnabled;
|
||||
}
|
||||
|
||||
// Panel Header Data Component
|
||||
panelHeaderData: Component {
|
||||
Item {
|
||||
id: header
|
||||
Prop.PropLabel {
|
||||
text: root.label
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
anchors.left: parent.left
|
||||
anchors.right: cpuTime.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
Prop.PropLabel {
|
||||
id: cpuTime
|
||||
visible: false // root.jobEnabled
|
||||
width: 50
|
||||
text: jobCpuTime
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
anchors.rightMargin: 5
|
||||
anchors.right:enabledIcon.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
Prop.PropCanvasIcon {
|
||||
id: enabledIcon
|
||||
anchors.right:parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
filled: root.jobEnabled
|
||||
fillColor: (root.jobEnabled ? global.colorGreenHighlight : global.colorOrangeAccent)
|
||||
icon: 5
|
||||
iconMouseArea.onClicked: { toggleJobActivation() }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function populatePropItems() {
|
||||
var propsModel = []
|
||||
var props = Jet.job_propKeys(rootConfig.getConfig(jobPath));
|
||||
// console.log(JSON.stringify(props));
|
||||
if (showProps) {
|
||||
for (var p in props) {
|
||||
propsModel.push({"object": rootConfig.getConfig(jobPath), "property":props[p] })
|
||||
}
|
||||
root.updatePropItems(root.propItemsPanel, propsModel);
|
||||
}
|
||||
|
||||
if (showSubs) {
|
||||
Jet.task_traverse(rootConfig.getConfig(jobPath),
|
||||
function(job, depth, index) {
|
||||
var component = Qt.createComponent("./TaskPropView.qml");
|
||||
component.createObject(root.propItemsPanel, {
|
||||
"label": job.objectName,
|
||||
"rootConfig": root.rootConfig,
|
||||
"jobPath": root.jobPath + '.' + job.objectName,
|
||||
"showProps": root.showProps,
|
||||
"showSubs": root.showSubs,
|
||||
"indentDepth": root.indentDepth + 1,
|
||||
})
|
||||
/* var component = Qt.createComponent("../../prop/PropItem.qml");
|
||||
component.createObject(root.propItemsPanel, {
|
||||
"label": root.jobPath + '.' + job.objectName + ' num=' + index,
|
||||
})*/
|
||||
// propsModel.push({"type": "printLabel", "label": root.jobPath + '.' + job.objectName + ' num=' + index })
|
||||
|
||||
return (depth < 1);
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
populatePropItems()
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
TaskList 1.0 TaskList.qml
|
||||
TaskViewList 1.0 TaskViewList.qml
|
||||
TaskTimeFrameView 1.0 TaskTimeFrameView.qml
|
||||
TaskPropView 1.0 TaskPropView.qml
|
32
scripts/developer/utilities/lib/prop/PropBool.qml
Normal file
32
scripts/developer/utilities/lib/prop/PropBool.qml
Normal file
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// PropBool.qml
|
||||
//
|
||||
// Created by Sam Gateau on 3/2/2019
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.7
|
||||
|
||||
PropItem {
|
||||
Global { id: global }
|
||||
id: root
|
||||
|
||||
property alias valueVar : checkboxControl.checked
|
||||
|
||||
Component.onCompleted: {
|
||||
valueVar = root.valueVarGetter();
|
||||
}
|
||||
|
||||
PropCheckBox {
|
||||
id: checkboxControl
|
||||
|
||||
anchors.left: root.splitter.right
|
||||
anchors.verticalCenter: root.verticalCenter
|
||||
width: root.width * global.valueAreaWidthScale
|
||||
|
||||
onCheckedChanged: { root.valueVarSetter(checked); }
|
||||
}
|
||||
}
|
0
scripts/developer/utilities/lib/prop/PropColor.qml
Normal file
0
scripts/developer/utilities/lib/prop/PropColor.qml
Normal file
38
scripts/developer/utilities/lib/prop/PropEnum.qml
Normal file
38
scripts/developer/utilities/lib/prop/PropEnum.qml
Normal file
|
@ -0,0 +1,38 @@
|
|||
//
|
||||
// PropEnum.qml
|
||||
//
|
||||
// Created by Sam Gateau on 3/2/2019
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.2
|
||||
|
||||
PropItem {
|
||||
Global { id: global }
|
||||
id: root
|
||||
|
||||
property alias valueVar : valueCombo.currentIndex
|
||||
property alias enums : valueCombo.model
|
||||
|
||||
Component.onCompleted: {
|
||||
// valueVar = root.valueVarGetter();
|
||||
}
|
||||
|
||||
PropComboBox {
|
||||
id: valueCombo
|
||||
|
||||
flat: true
|
||||
|
||||
anchors.left: root.splitter.right
|
||||
anchors.right: root.right
|
||||
anchors.verticalCenter: root.verticalCenter
|
||||
height: global.slimHeight
|
||||
|
||||
currentIndex: root.valueVarGetter()
|
||||
onCurrentIndexChanged: { root.valueVarSetter(currentIndex); }
|
||||
}
|
||||
}
|
102
scripts/developer/utilities/lib/prop/PropGroup.qml
Normal file
102
scripts/developer/utilities/lib/prop/PropGroup.qml
Normal file
|
@ -0,0 +1,102 @@
|
|||
//
|
||||
// PropGroup.qml
|
||||
//
|
||||
// Created by Sam Gateau on 3/2/2019
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.7
|
||||
|
||||
// PropGroup is mostly reusing the look and feel of the PropFolderPanel
|
||||
// It is populated by calling "updatePropItems"
|
||||
// or adding manually new Items to the "propItemsPanel"
|
||||
PropFolderPanel {
|
||||
Global { id: global }
|
||||
id: root
|
||||
|
||||
property alias propItemsPanel: root.panelFrameContent
|
||||
|
||||
// Prop Group is designed to author an array of ProItems, they are defined with an array of the tuplets describing each individual item:
|
||||
// [ ..., PropItemInfo, ...]
|
||||
// PropItemInfo {
|
||||
// type: "PropXXXX", object: JSobject, property: "propName"
|
||||
// }
|
||||
//
|
||||
function updatePropItems(propItemsContainer, propItemsModel) {
|
||||
root.hasContent = false
|
||||
for (var i = 0; i < propItemsModel.length; i++) {
|
||||
var proItem = propItemsModel[i];
|
||||
// valid object
|
||||
if (proItem['object'] !== undefined && proItem['object'] !== null ) {
|
||||
// valid property
|
||||
if (proItem['property'] !== undefined && proItem.object[proItem.property] !== undefined) {
|
||||
// check type
|
||||
if (proItem['type'] === undefined) {
|
||||
proItem['type'] = typeof(proItem.object[proItem.property])
|
||||
}
|
||||
switch(proItem.type) {
|
||||
case 'boolean':
|
||||
case 'PropBool': {
|
||||
var component = Qt.createComponent("PropBool.qml");
|
||||
component.createObject(propItemsContainer, {
|
||||
"label": proItem.property,
|
||||
"object": proItem.object,
|
||||
"property": proItem.property
|
||||
})
|
||||
} break;
|
||||
case 'number':
|
||||
case 'PropScalar': {
|
||||
var component = Qt.createComponent("PropScalar.qml");
|
||||
component.createObject(propItemsContainer, {
|
||||
"label": proItem.property,
|
||||
"object": proItem.object,
|
||||
"property": proItem.property,
|
||||
"min": (proItem["min"] !== undefined ? proItem.min : 0.0),
|
||||
"max": (proItem["max"] !== undefined ? proItem.max : 1.0),
|
||||
"integer": (proItem["integral"] !== undefined ? proItem.integral : false),
|
||||
})
|
||||
} break;
|
||||
case 'PropEnum': {
|
||||
var component = Qt.createComponent("PropEnum.qml");
|
||||
component.createObject(propItemsContainer, {
|
||||
"label": proItem.property,
|
||||
"object": proItem.object,
|
||||
"property": proItem.property,
|
||||
"enums": (proItem["enums"] !== undefined ? proItem.enums : ["Undefined Enums !!!"]),
|
||||
})
|
||||
} break;
|
||||
case 'object': {
|
||||
var component = Qt.createComponent("PropItem.qml");
|
||||
component.createObject(propItemsContainer, {
|
||||
"label": proItem.property,
|
||||
"object": proItem.object,
|
||||
"property": proItem.property,
|
||||
})
|
||||
} break;
|
||||
case 'printLabel': {
|
||||
var component = Qt.createComponent("PropItem.qml");
|
||||
component.createObject(propItemsContainer, {
|
||||
"label": proItem.property
|
||||
})
|
||||
} break;
|
||||
}
|
||||
root.hasContent = true
|
||||
} else {
|
||||
console.log('Invalid property: ' + JSON.stringify(proItem));
|
||||
}
|
||||
} else if (proItem['type'] === 'printLabel') {
|
||||
var component = Qt.createComponent("PropItem.qml");
|
||||
component.createObject(propItemsContainer, {
|
||||
"label": proItem.label
|
||||
})
|
||||
} else {
|
||||
console.log('Invalid object: ' + JSON.stringify(proItem));
|
||||
}
|
||||
}
|
||||
}
|
||||
Component.onCompleted: {
|
||||
}
|
||||
}
|
63
scripts/developer/utilities/lib/prop/PropItem.qml
Normal file
63
scripts/developer/utilities/lib/prop/PropItem.qml
Normal file
|
@ -0,0 +1,63 @@
|
|||
//
|
||||
// PropItem.qml
|
||||
//
|
||||
// Created by Sam Gateau on 3/2/2019
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.7
|
||||
|
||||
Item {
|
||||
Global { id: global }
|
||||
|
||||
id: root
|
||||
|
||||
// Prop item is designed to author an object[property]:
|
||||
property var object: {}
|
||||
property string property: ""
|
||||
|
||||
// value is accessed through the "valueVarSetter" and "valueVarGetter"
|
||||
// By default, these just go get or set the value from the object[property]
|
||||
//
|
||||
function defaultGet() { return root.object[root.property]; }
|
||||
function defaultSet(value) { root.object[root.property] = value; }
|
||||
property var valueVarSetter: defaultSet
|
||||
property var valueVarGetter: defaultGet
|
||||
|
||||
// PropItem is stretching horizontally accross its parent
|
||||
// Fixed height
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: global.lineHeight
|
||||
anchors.leftMargin: global.horizontalMargin
|
||||
anchors.rightMargin: global.horizontalMargin
|
||||
|
||||
// LabelControl And SplitterControl are on the left side of the PropItem
|
||||
property bool showLabel: true
|
||||
property alias labelControl: labelControl
|
||||
property alias label: labelControl.text
|
||||
|
||||
property var labelAreaWidth: root.width * global.splitterRightWidthScale - global.splitterWidth
|
||||
|
||||
PropText {
|
||||
id: labelControl
|
||||
text: root.label
|
||||
enabled: root.showLabel
|
||||
|
||||
anchors.left: root.left
|
||||
anchors.verticalCenter: root.verticalCenter
|
||||
width: labelAreaWidth
|
||||
}
|
||||
|
||||
property alias splitter: splitterControl
|
||||
PropSplitter {
|
||||
id: splitterControl
|
||||
|
||||
anchors.left: labelControl.right
|
||||
size: global.splitterWidth
|
||||
}
|
||||
|
||||
}
|
70
scripts/developer/utilities/lib/prop/PropScalar.qml
Normal file
70
scripts/developer/utilities/lib/prop/PropScalar.qml
Normal file
|
@ -0,0 +1,70 @@
|
|||
//
|
||||
// PropItem.qml
|
||||
//
|
||||
// Created by Sam Gateau on 3/2/2019
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.7
|
||||
|
||||
import controlsUit 1.0 as HifiControls
|
||||
|
||||
PropItem {
|
||||
Global { id: global }
|
||||
id: root
|
||||
|
||||
// Scalar Prop
|
||||
property bool integral: false
|
||||
property var numDigits: 2
|
||||
|
||||
|
||||
property alias valueVar : sliderControl.value
|
||||
property alias min: sliderControl.minimumValue
|
||||
property alias max: sliderControl.maximumValue
|
||||
|
||||
|
||||
|
||||
property bool showValue: true
|
||||
|
||||
|
||||
signal valueChanged(real value)
|
||||
|
||||
Component.onCompleted: {
|
||||
valueVar = root.valueVarGetter();
|
||||
}
|
||||
|
||||
PropLabel {
|
||||
id: valueLabel
|
||||
enabled: root.showValue
|
||||
|
||||
anchors.left: root.splitter.right
|
||||
anchors.verticalCenter: root.verticalCenter
|
||||
width: root.width * global.valueAreaWidthScale
|
||||
horizontalAlignment: global.valueTextAlign
|
||||
height: global.slimHeight
|
||||
|
||||
text: sliderControl.value.toFixed(root.integral ? 0 : root.numDigits)
|
||||
|
||||
background: Rectangle {
|
||||
color: global.color
|
||||
border.color: global.colorBorderLight
|
||||
border.width: global.valueBorderWidth
|
||||
radius: global.valueBorderRadius
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.Slider {
|
||||
id: sliderControl
|
||||
stepSize: root.integral ? 1.0 : 0.0
|
||||
anchors.left: valueLabel.right
|
||||
anchors.right: root.right
|
||||
anchors.verticalCenter: root.verticalCenter
|
||||
|
||||
onValueChanged: { root.valueVarSetter(value) }
|
||||
}
|
||||
|
||||
|
||||
}
|
14
scripts/developer/utilities/lib/prop/qmldir
Normal file
14
scripts/developer/utilities/lib/prop/qmldir
Normal file
|
@ -0,0 +1,14 @@
|
|||
Module Prop
|
||||
Global 1.0 style/Global.qml
|
||||
PropText 1.0 style/PiText.qml
|
||||
PropLabel 1.0 style/PiLabel.qml
|
||||
PropSplitter 1.0 style/PiSplitter.qml
|
||||
PropComboBox 1.0 style/PiComboBox.qml
|
||||
PropCanvasIcon 1.0 style/PiCanvasIcon.qml
|
||||
PropCheckBox 1.0 style/PiCheckBox.qml
|
||||
PropFolderPanel 1.0 style/PiFolderPanel.qml
|
||||
|
||||
PropItem 1.0 PropItem.qml
|
||||
PropScalar 1.0 PropScalar.qml
|
||||
PropEnum 1.0 PropEnum.qml
|
||||
PropColor 1.0 PropColor.qml
|
54
scripts/developer/utilities/lib/prop/style/Global.qml
Normal file
54
scripts/developer/utilities/lib/prop/style/Global.qml
Normal file
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// Prop/style/Global.qml
|
||||
//
|
||||
// Created by Sam Gateau on 3/2/2019
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.7
|
||||
|
||||
import stylesUit 1.0
|
||||
import controlsUit 1.0 as HifiControls
|
||||
|
||||
|
||||
Item {
|
||||
HifiConstants { id: hifi }
|
||||
id: root
|
||||
|
||||
readonly property real lineHeight: 32
|
||||
readonly property real slimHeight: 24
|
||||
|
||||
readonly property real horizontalMargin: 4
|
||||
|
||||
readonly property color color: hifi.colors.baseGray
|
||||
readonly property color colorBackShadow: hifi.colors.baseGrayShadow
|
||||
readonly property color colorBackHighlight: hifi.colors.baseGrayHighlight
|
||||
readonly property color colorBorderLight: hifi.colors.lightGray
|
||||
readonly property color colorBorderHighight: hifi.colors.blueHighlight
|
||||
|
||||
readonly property color colorOrangeAccent: "#FF6309"
|
||||
readonly property color colorRedAccent: "#C62147"
|
||||
readonly property color colorGreenHighlight: "#1ac567"
|
||||
|
||||
readonly property real fontSize: 12
|
||||
readonly property var fontFamily: "Raleway"
|
||||
readonly property var fontWeight: Font.DemiBold
|
||||
readonly property color fontColor: hifi.colors.faintGray
|
||||
|
||||
readonly property var splitterRightWidthScale: 0.45
|
||||
readonly property real splitterWidth: 8
|
||||
|
||||
readonly property real iconWidth: fontSize
|
||||
readonly property real iconHeight: fontSize
|
||||
|
||||
readonly property var labelTextAlign: Text.AlignRight
|
||||
readonly property var labelTextElide: Text.ElideMiddle
|
||||
|
||||
readonly property var valueAreaWidthScale: 0.3 * (1.0 - splitterRightWidthScale)
|
||||
readonly property var valueTextAlign: Text.AlignHCenter
|
||||
readonly property real valueBorderWidth: 1
|
||||
readonly property real valueBorderRadius: 2
|
||||
}
|
99
scripts/developer/utilities/lib/prop/style/PiCanvasIcon.qml
Normal file
99
scripts/developer/utilities/lib/prop/style/PiCanvasIcon.qml
Normal file
|
@ -0,0 +1,99 @@
|
|||
//
|
||||
// Prop/style/PiFoldCanvas.qml
|
||||
//
|
||||
// Created by Sam Gateau on 3/9/2019
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.2
|
||||
|
||||
Canvas {
|
||||
Global { id: global }
|
||||
|
||||
width: global.iconWidth
|
||||
height: global.iconHeight
|
||||
|
||||
property var icon: 0
|
||||
|
||||
onIconChanged: function () { //console.log("Icon changed to: " + icon );
|
||||
requestPaint()
|
||||
}
|
||||
|
||||
property var filled: true
|
||||
onFilledChanged: function () { //console.log("Filled changed to: " + filled );
|
||||
requestPaint()
|
||||
}
|
||||
|
||||
property var fillColor: global.colorBorderHighight
|
||||
onFillColorChanged: function () { //console.log("fillColor changed to: " + filled );
|
||||
requestPaint()
|
||||
}
|
||||
|
||||
property alias iconMouseArea: mousearea
|
||||
|
||||
contextType: "2d"
|
||||
onPaint: {
|
||||
context.reset();
|
||||
switch (icon) {
|
||||
case false:
|
||||
case 0:{ // Right Arrow
|
||||
context.moveTo(width * 0.25, 0);
|
||||
context.lineTo(width * 0.25, height);
|
||||
context.lineTo(width, height / 2);
|
||||
context.closePath();
|
||||
} break;
|
||||
case true:
|
||||
case 1:{ // Up Arrow
|
||||
context.moveTo(0, height * 0.25);
|
||||
context.lineTo(width, height * 0.25);
|
||||
context.lineTo(width / 2, height);
|
||||
context.closePath();
|
||||
} break;
|
||||
case 2:{ // Down Arrow
|
||||
context.moveTo(0, height * 0.75);
|
||||
context.lineTo(width, height* 0.75);
|
||||
context.lineTo(width / 2, 0);
|
||||
context.closePath();
|
||||
} break;
|
||||
case 3:{ // Left Arrow
|
||||
context.moveTo(width * 0.75, 0);
|
||||
context.lineTo(width * 0.75, height);
|
||||
context.lineTo(0, height / 2);
|
||||
context.closePath();
|
||||
} break;
|
||||
case 4:{ // Square
|
||||
context.moveTo(0,0);
|
||||
context.lineTo(0, height);
|
||||
context.lineTo(width, height);
|
||||
context.lineTo(width, 0);
|
||||
context.closePath();
|
||||
} break;
|
||||
case 5:{ // Small Square
|
||||
context.moveTo(width * 0.25, height * 0.25);
|
||||
context.lineTo(width * 0.25, height * 0.75);
|
||||
context.lineTo(width * 0.75, height * 0.75);
|
||||
context.lineTo(width * 0.75, height * 0.25);
|
||||
context.closePath();
|
||||
} break;
|
||||
default: {
|
||||
|
||||
}
|
||||
}
|
||||
if (filled) {
|
||||
context.fillStyle = fillColor;
|
||||
context.fill();
|
||||
} else {
|
||||
context.strokeStyle = fillColor;
|
||||
context.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea{
|
||||
id: mousearea
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
23
scripts/developer/utilities/lib/prop/style/PiCheckBox.qml
Normal file
23
scripts/developer/utilities/lib/prop/style/PiCheckBox.qml
Normal file
|
@ -0,0 +1,23 @@
|
|||
//
|
||||
// Prop/style/PiCheckBox.qml
|
||||
//
|
||||
// Created by Sam Gateau on 3/2/2019
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.7
|
||||
import controlsUit 1.0 as HifiControls
|
||||
|
||||
HifiControls.CheckBox {
|
||||
Global { id: global }
|
||||
|
||||
color: global.fontColor
|
||||
|
||||
//anchors.left: root.splitter.right
|
||||
//anchors.verticalCenter: root.verticalCenter
|
||||
//width: root.width * global.valueAreaWidthScale
|
||||
height: global.slimHeight
|
||||
}
|
86
scripts/developer/utilities/lib/prop/style/PiComboBox.qml
Normal file
86
scripts/developer/utilities/lib/prop/style/PiComboBox.qml
Normal file
|
@ -0,0 +1,86 @@
|
|||
//
|
||||
// Prop/style/PiComboBox.qml
|
||||
//
|
||||
// Created by Sam Gateau on 3/2/2019
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.2
|
||||
|
||||
|
||||
ComboBox {
|
||||
id: valueCombo
|
||||
|
||||
height: global.slimHeight
|
||||
|
||||
|
||||
// look
|
||||
flat: true
|
||||
delegate: ItemDelegate {
|
||||
width: valueCombo.width
|
||||
height: valueCombo.height
|
||||
contentItem: PiText {
|
||||
text: modelData
|
||||
horizontalAlignment: global.valueTextAlign
|
||||
}
|
||||
background: Rectangle {
|
||||
color:highlighted?global.colorBackHighlight:global.color;
|
||||
}
|
||||
highlighted: valueCombo.highlightedIndex === index
|
||||
}
|
||||
|
||||
indicator: PiCanvasIcon {
|
||||
id: canvas
|
||||
x: valueCombo.width - width - valueCombo.rightPadding
|
||||
y: valueCombo.topPadding + (valueCombo.availableHeight - height) / 2
|
||||
|
||||
icon: 1
|
||||
/*Connections {
|
||||
target: valueCombo
|
||||
onPressedChanged: { canvas.icon = control.down + 1 }
|
||||
}*/
|
||||
}
|
||||
|
||||
contentItem: PiText {
|
||||
leftPadding: 0
|
||||
rightPadding: valueCombo.indicator.width + valueCombo.spacing
|
||||
|
||||
text: valueCombo.displayText
|
||||
horizontalAlignment: global.valueTextAlign
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
implicitWidth: 120
|
||||
implicitHeight: 40
|
||||
color: global.color
|
||||
border.color: valueCombo.popup.visible ? global.colorBorderHighight : global.colorBorderLight
|
||||
border.width: global.valueBorderWidth
|
||||
radius: global.valueBorderRadius
|
||||
}
|
||||
|
||||
popup: Popup {
|
||||
y: valueCombo.height - 1
|
||||
width: valueCombo.width
|
||||
implicitHeight: contentItem.implicitHeight + 2
|
||||
padding: 1
|
||||
|
||||
contentItem: ListView {
|
||||
clip: true
|
||||
implicitHeight: contentHeight
|
||||
model: valueCombo.popup.visible ? valueCombo.delegateModel : null
|
||||
currentIndex: valueCombo.highlightedIndex
|
||||
|
||||
ScrollIndicator.vertical: ScrollIndicator { }
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: global.color
|
||||
border.color: global.colorBorderHighight
|
||||
radius: global.valueBorderRadius
|
||||
}
|
||||
}
|
||||
}
|
123
scripts/developer/utilities/lib/prop/style/PiFolderPanel.qml
Normal file
123
scripts/developer/utilities/lib/prop/style/PiFolderPanel.qml
Normal file
|
@ -0,0 +1,123 @@
|
|||
//
|
||||
// Prop/style/PiFoldedPanel.qml
|
||||
//
|
||||
// Created by Sam Gateau on 4/17/2019
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.7
|
||||
|
||||
Item {
|
||||
Global { id: global }
|
||||
id: root
|
||||
|
||||
property var label: "panel"
|
||||
|
||||
property alias isUnfold: headerFolderIcon.icon
|
||||
property alias hasContent: headerFolderIcon.filled
|
||||
property var indentDepth: 0
|
||||
|
||||
// Panel Header Data Component
|
||||
property Component panelHeaderData: defaultPanelHeaderData
|
||||
Component { // default is a Label
|
||||
id: defaultPanelHeaderData
|
||||
PiLabel {
|
||||
text: root.label
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
|
||||
// Panel Frame Data Component
|
||||
property Component panelFrameData: defaultPanelFrameData
|
||||
Component { // default is a column
|
||||
id: defaultPanelFrameData
|
||||
Column {
|
||||
}
|
||||
}
|
||||
|
||||
property alias panelFrameContent: _panelFrameData.item
|
||||
|
||||
// Header Item
|
||||
Rectangle {
|
||||
id: header
|
||||
height: global.slimHeight
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: 0
|
||||
|
||||
color: global.colorBackShadow // header of group is darker
|
||||
|
||||
// First in the header, some indentation spacer
|
||||
Item {
|
||||
id: indentSpacer
|
||||
width: (headerFolderIcon.width * root.indentDepth) + global.horizontalMargin // Must be non-zero
|
||||
height: parent.height
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
// Second, the folder button / indicator
|
||||
Item {
|
||||
id: headerFolder
|
||||
anchors.left: indentSpacer.right
|
||||
width: headerFolderIcon.width * 2
|
||||
anchors.verticalCenter: header.verticalCenter
|
||||
height: parent.height
|
||||
|
||||
PiCanvasIcon {
|
||||
id: headerFolderIcon
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
fillColor: global.colorOrangeAccent
|
||||
iconMouseArea.onClicked: { root.isUnfold = !root.isUnfold }
|
||||
}
|
||||
}
|
||||
|
||||
// Next the header container
|
||||
// by default containing a Label showing the root.label
|
||||
Loader {
|
||||
sourceComponent: panelHeaderData
|
||||
anchors.left: headerFolder.right
|
||||
anchors.right: header.right
|
||||
anchors.rightMargin: global.horizontalMargin * 2
|
||||
anchors.verticalCenter: header.verticalCenter
|
||||
height: parent.height
|
||||
}
|
||||
}
|
||||
|
||||
// The Panel container
|
||||
Rectangle {
|
||||
id: frame
|
||||
visible: root.isUnfold
|
||||
|
||||
color: "transparent"
|
||||
border.color: global.colorBorderLight
|
||||
border.width: global.valueBorderWidth
|
||||
radius: global.valueBorderRadius
|
||||
|
||||
anchors.margins: 0
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: header.bottom
|
||||
anchors.bottom: root.bottom
|
||||
|
||||
// Next the panel frame data
|
||||
Loader {
|
||||
id: _panelFrameData
|
||||
sourceComponent: panelFrameData
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.leftMargin: 0
|
||||
anchors.rightMargin: 0
|
||||
clip: true
|
||||
}
|
||||
}
|
||||
|
||||
height: header.height + isUnfold * _panelFrameData.item.height
|
||||
anchors.margins: 0
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
}
|
25
scripts/developer/utilities/lib/prop/style/PiLabel.qml
Normal file
25
scripts/developer/utilities/lib/prop/style/PiLabel.qml
Normal file
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// Prop/style/PsLabel.qml
|
||||
//
|
||||
// Created by Sam Gateau on 3/2/2019
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.2
|
||||
|
||||
Label {
|
||||
Global { id: global }
|
||||
|
||||
color: global.fontColor
|
||||
font.pixelSize: global.fontSize
|
||||
font.family: global.fontFamily
|
||||
font.weight: global.fontWeight
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: global.labelTextAlign
|
||||
elide: global.labelTextElide
|
||||
}
|
||||
|
21
scripts/developer/utilities/lib/prop/style/PiSplitter.qml
Normal file
21
scripts/developer/utilities/lib/prop/style/PiSplitter.qml
Normal file
|
@ -0,0 +1,21 @@
|
|||
//
|
||||
// Prop/style/Splitter.qml
|
||||
//
|
||||
// Created by Sam Gateau on 3/2/2019
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.7
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property real size
|
||||
|
||||
width: size // Must be non-zero
|
||||
height: size
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
24
scripts/developer/utilities/lib/prop/style/PiText.qml
Normal file
24
scripts/developer/utilities/lib/prop/style/PiText.qml
Normal file
|
@ -0,0 +1,24 @@
|
|||
//
|
||||
// Prop/style/PsText.qml
|
||||
//
|
||||
// Created by Sam Gateau on 3/2/2019
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.7
|
||||
|
||||
Text {
|
||||
Global { id: global }
|
||||
|
||||
color: global.fontColor
|
||||
font.pixelSize: global.fontSize
|
||||
font.family: global.fontFamily
|
||||
font.weight: global.fontWeight
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: global.labelTextAlign
|
||||
elide: global.labelTextElide
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
(function() {
|
||||
var TABLET_BUTTON_NAME = "TAA";
|
||||
var QMLAPP_URL = Script.resolvePath("./antialiasing.qml");
|
||||
var QMLAPP_URL = Script.resolvePath("./luci/Antialiasing.qml");
|
||||
|
||||
|
||||
var onLuciScreen = false;
|
||||
|
|
|
@ -1,182 +0,0 @@
|
|||
//
|
||||
// Antialiasing.qml
|
||||
//
|
||||
// Created by Sam Gateau on 8/14/2017
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Layouts 1.3
|
||||
|
||||
import stylesUit 1.0
|
||||
import controlsUit 1.0 as HifiControls
|
||||
|
||||
import "configSlider"
|
||||
import "../lib/plotperf"
|
||||
|
||||
Item {
|
||||
Rectangle {
|
||||
id: root;
|
||||
|
||||
HifiConstants { id: hifi; }
|
||||
color: hifi.colors.baseGray;
|
||||
|
||||
Column {
|
||||
id: antialiasing
|
||||
spacing: 20
|
||||
padding: 10
|
||||
|
||||
Column{
|
||||
spacing: 10
|
||||
|
||||
Row {
|
||||
spacing: 10
|
||||
id: fxaaOnOff
|
||||
property bool debugFXAA: false
|
||||
HifiControls.Button {
|
||||
function getTheText() {
|
||||
if (Render.getConfig("RenderMainView.Antialiasing").fxaaOnOff) {
|
||||
return "FXAA"
|
||||
} else {
|
||||
return "TAA"
|
||||
}
|
||||
}
|
||||
text: getTheText()
|
||||
onClicked: {
|
||||
var onOff = !Render.getConfig("RenderMainView.Antialiasing").fxaaOnOff;
|
||||
if (onOff) {
|
||||
Render.getConfig("RenderMainView.JitterCam").none();
|
||||
Render.getConfig("RenderMainView.Antialiasing").fxaaOnOff = true;
|
||||
} else {
|
||||
Render.getConfig("RenderMainView.JitterCam").play();
|
||||
Render.getConfig("RenderMainView.Antialiasing").fxaaOnOff = false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
Separator {}
|
||||
Row {
|
||||
spacing: 10
|
||||
|
||||
HifiControls.Button {
|
||||
text: {
|
||||
var state = 2 - (Render.getConfig("RenderMainView.JitterCam").freeze * 1 - Render.getConfig("RenderMainView.JitterCam").stop * 2);
|
||||
if (state === 2) {
|
||||
return "Jitter"
|
||||
} else if (state === 1) {
|
||||
return "Paused at " + Render.getConfig("RenderMainView.JitterCam").index + ""
|
||||
} else {
|
||||
return "No Jitter"
|
||||
}
|
||||
}
|
||||
onClicked: { Render.getConfig("RenderMainView.JitterCam").cycleStopPauseRun(); }
|
||||
}
|
||||
HifiControls.Button {
|
||||
text: "<"
|
||||
onClicked: { Render.getConfig("RenderMainView.JitterCam").prev(); }
|
||||
}
|
||||
HifiControls.Button {
|
||||
text: ">"
|
||||
onClicked: { Render.getConfig("RenderMainView.JitterCam").next(); }
|
||||
}
|
||||
}
|
||||
Separator {}
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: "Constrain color"
|
||||
checked: Render.getConfig("RenderMainView.Antialiasing")["constrainColor"]
|
||||
onCheckedChanged: { Render.getConfig("RenderMainView.Antialiasing")["constrainColor"] = checked }
|
||||
}
|
||||
ConfigSlider {
|
||||
label: qsTr("Covariance gamma")
|
||||
integral: false
|
||||
config: Render.getConfig("RenderMainView.Antialiasing")
|
||||
property: "covarianceGamma"
|
||||
max: 1.5
|
||||
min: 0.5
|
||||
height: 38
|
||||
}
|
||||
Separator {}
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: "Feedback history color"
|
||||
checked: Render.getConfig("RenderMainView.Antialiasing")["feedbackColor"]
|
||||
onCheckedChanged: { Render.getConfig("RenderMainView.Antialiasing")["feedbackColor"] = checked }
|
||||
}
|
||||
|
||||
ConfigSlider {
|
||||
label: qsTr("Source blend")
|
||||
integral: false
|
||||
config: Render.getConfig("RenderMainView.Antialiasing")
|
||||
property: "blend"
|
||||
max: 1.0
|
||||
min: 0.0
|
||||
height: 38
|
||||
}
|
||||
|
||||
ConfigSlider {
|
||||
label: qsTr("Post sharpen")
|
||||
integral: false
|
||||
config: Render.getConfig("RenderMainView.Antialiasing")
|
||||
property: "sharpen"
|
||||
max: 1.0
|
||||
min: 0.0
|
||||
}
|
||||
Separator {}
|
||||
Row {
|
||||
|
||||
spacing: 10
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: "Debug"
|
||||
checked: Render.getConfig("RenderMainView.Antialiasing")["debug"]
|
||||
onCheckedChanged: { Render.getConfig("RenderMainView.Antialiasing")["debug"] = checked }
|
||||
}
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: "Show Debug Cursor"
|
||||
checked: Render.getConfig("RenderMainView.Antialiasing")["showCursorPixel"]
|
||||
onCheckedChanged: { Render.getConfig("RenderMainView.Antialiasing")["showCursorPixel"] = checked }
|
||||
}
|
||||
}
|
||||
ConfigSlider {
|
||||
label: qsTr("Debug Region <")
|
||||
integral: false
|
||||
config: Render.getConfig("RenderMainView.Antialiasing")
|
||||
property: "debugX"
|
||||
max: 1.0
|
||||
min: 0.0
|
||||
}
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: "Closest Fragment"
|
||||
checked: Render.getConfig("RenderMainView.Antialiasing")["showClosestFragment"]
|
||||
onCheckedChanged: { Render.getConfig("RenderMainView.Antialiasing")["showClosestFragment"] = checked }
|
||||
}
|
||||
ConfigSlider {
|
||||
label: qsTr("Debug Velocity Threshold [pix]")
|
||||
integral: false
|
||||
config: Render.getConfig("RenderMainView.Antialiasing")
|
||||
property: "debugShowVelocityThreshold"
|
||||
max: 50
|
||||
min: 0.0
|
||||
height: 38
|
||||
}
|
||||
ConfigSlider {
|
||||
label: qsTr("Debug Orb Zoom")
|
||||
integral: false
|
||||
config: Render.getConfig("RenderMainView.Antialiasing")
|
||||
property: "debugOrbZoom"
|
||||
max: 32.0
|
||||
min: 1.0
|
||||
height: 38
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@
|
|||
//
|
||||
|
||||
// Set up the qml ui
|
||||
var qml = Script.resolvePath('culling.qml');
|
||||
var qml = Script.resolvePath('luci/Culling.qml');
|
||||
var window = new OverlayWindow({
|
||||
title: 'Render Draws',
|
||||
source: qml,
|
||||
|
|
|
@ -14,6 +14,7 @@ import QtQuick.Layouts 1.3
|
|||
import stylesUit 1.0
|
||||
import controlsUit 1.0 as HifiControls
|
||||
import "configSlider"
|
||||
import "luci"
|
||||
|
||||
Rectangle {
|
||||
HifiConstants { id: hifi;}
|
||||
|
@ -28,125 +29,16 @@ Rectangle {
|
|||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: hifi.dimensions.contentMargin.x
|
||||
//padding: hifi.dimensions.contentMargin.x
|
||||
|
||||
|
||||
HifiControls.Label {
|
||||
text: "Shading"
|
||||
}
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
spacing: 5
|
||||
Column {
|
||||
spacing: 5
|
||||
// padding: 10
|
||||
Repeater {
|
||||
model: [
|
||||
"Unlit:LightingModel:enableUnlit",
|
||||
"Emissive:LightingModel:enableEmissive",
|
||||
"Lightmap:LightingModel:enableLightmap",
|
||||
"Background:LightingModel:enableBackground",
|
||||
"Haze:LightingModel:enableHaze",
|
||||
"AO:LightingModel:enableAmbientOcclusion",
|
||||
"Textures:LightingModel:enableMaterialTexturing"
|
||||
]
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: modelData.split(":")[0]
|
||||
checked: render.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]]
|
||||
onCheckedChanged: { render.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked }
|
||||
}
|
||||
}
|
||||
}
|
||||
ShadingModel {}
|
||||
|
||||
Separator {}
|
||||
ToneMapping {}
|
||||
|
||||
Column {
|
||||
spacing: 5
|
||||
Repeater {
|
||||
model: [
|
||||
"Obscurance:LightingModel:enableObscurance",
|
||||
"Scattering:LightingModel:enableScattering",
|
||||
"Diffuse:LightingModel:enableDiffuse",
|
||||
"Specular:LightingModel:enableSpecular",
|
||||
"Albedo:LightingModel:enableAlbedo",
|
||||
"Wireframe:LightingModel:enableWireframe",
|
||||
"Skinning:LightingModel:enableSkinning",
|
||||
"Blendshape:LightingModel:enableBlendshape"
|
||||
]
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: modelData.split(":")[0]
|
||||
checked: render.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]]
|
||||
onCheckedChanged: { render.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
spacing: 5
|
||||
Repeater {
|
||||
model: [
|
||||
"Ambient:LightingModel:enableAmbientLight",
|
||||
"Directional:LightingModel:enableDirectionalLight",
|
||||
"Point:LightingModel:enablePointLight",
|
||||
"Spot:LightingModel:enableSpotLight",
|
||||
"Light Contour:LightingModel:showLightContour",
|
||||
"Zone Stack:DrawZoneStack:enabled",
|
||||
"Shadow:LightingModel:enableShadow"
|
||||
]
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: modelData.split(":")[0]
|
||||
checked: render.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]]
|
||||
onCheckedChanged: { render.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Separator {}
|
||||
Column {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
spacing: 5
|
||||
Repeater {
|
||||
model: [ "Tone Mapping Exposure:ToneMapping:exposure:5.0:-5.0"
|
||||
]
|
||||
ConfigSlider {
|
||||
label: qsTr(modelData.split(":")[0])
|
||||
integral: false
|
||||
config: render.mainViewTask.getConfig(modelData.split(":")[1])
|
||||
property: modelData.split(":")[2]
|
||||
max: modelData.split(":")[3]
|
||||
min: modelData.split(":")[4]
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
}
|
||||
}
|
||||
Item {
|
||||
height: childrenRect.height
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
HifiControls.Label {
|
||||
text: "Tone Mapping Curve"
|
||||
anchors.left: parent.left
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
anchors.right: parent.right
|
||||
currentIndex: 1
|
||||
model: [
|
||||
"RGB",
|
||||
"SRGB",
|
||||
"Reinhard",
|
||||
"Filmic",
|
||||
]
|
||||
width: 200
|
||||
onCurrentIndexChanged: { render.mainViewTask.getConfig("ToneMapping")["curve"] = currentIndex; }
|
||||
}
|
||||
}
|
||||
}
|
||||
Separator {}
|
||||
Column {
|
||||
anchors.left: parent.left
|
||||
|
@ -169,133 +61,11 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
Separator {}
|
||||
Framebuffer {}
|
||||
|
||||
Item {
|
||||
height: childrenRect.height
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
id: framebuffer
|
||||
|
||||
HifiControls.Label {
|
||||
text: "Debug Framebuffer"
|
||||
anchors.left: parent.left
|
||||
}
|
||||
|
||||
property var config: render.mainViewTask.getConfig("DebugDeferredBuffer")
|
||||
|
||||
function setDebugMode(mode) {
|
||||
framebuffer.config.enabled = (mode != 0);
|
||||
framebuffer.config.mode = mode;
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
anchors.right: parent.right
|
||||
currentIndex: 0
|
||||
model: ListModel {
|
||||
id: cbItemsFramebuffer
|
||||
ListElement { text: "Off"; color: "Yellow" }
|
||||
ListElement { text: "Depth"; color: "Green" }
|
||||
ListElement { text: "Albedo"; color: "Yellow" }
|
||||
ListElement { text: "Normal"; color: "White" }
|
||||
ListElement { text: "Roughness"; color: "White" }
|
||||
ListElement { text: "Metallic"; color: "White" }
|
||||
ListElement { text: "Emissive"; color: "White" }
|
||||
ListElement { text: "Unlit"; color: "White" }
|
||||
ListElement { text: "Occlusion"; color: "White" }
|
||||
ListElement { text: "Lightmap"; color: "White" }
|
||||
ListElement { text: "Scattering"; color: "White" }
|
||||
ListElement { text: "Lighting"; color: "White" }
|
||||
ListElement { text: "Shadow Cascade 0"; color: "White" }
|
||||
ListElement { text: "Shadow Cascade 1"; color: "White" }
|
||||
ListElement { text: "Shadow Cascade 2"; color: "White" }
|
||||
ListElement { text: "Shadow Cascade 3"; color: "White" }
|
||||
ListElement { text: "Shadow Cascade Indices"; color: "White" }
|
||||
ListElement { text: "Linear Depth"; color: "White" }
|
||||
ListElement { text: "Half Linear Depth"; color: "White" }
|
||||
ListElement { text: "Half Normal"; color: "White" }
|
||||
ListElement { text: "Mid Curvature"; color: "White" }
|
||||
ListElement { text: "Mid Normal"; color: "White" }
|
||||
ListElement { text: "Low Curvature"; color: "White" }
|
||||
ListElement { text: "Low Normal"; color: "White" }
|
||||
ListElement { text: "Curvature Occlusion"; color: "White" }
|
||||
ListElement { text: "Debug Scattering"; color: "White" }
|
||||
ListElement { text: "Ambient Occlusion"; color: "White" }
|
||||
ListElement { text: "Ambient Occlusion Blurred"; color: "White" }
|
||||
ListElement { text: "Ambient Occlusion Normal"; color: "White" }
|
||||
ListElement { text: "Velocity"; color: "White" }
|
||||
ListElement { text: "Custom"; color: "White" }
|
||||
}
|
||||
width: 200
|
||||
onCurrentIndexChanged: { framebuffer.setDebugMode(currentIndex) }
|
||||
}
|
||||
}
|
||||
|
||||
Separator {}
|
||||
Row {
|
||||
spacing: 5
|
||||
Column {
|
||||
spacing: 5
|
||||
BoundingBoxes {
|
||||
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: "Opaques"
|
||||
checked: render.mainViewTask.getConfig("DrawOpaqueBounds")["enabled"]
|
||||
onCheckedChanged: { render.mainViewTask.getConfig("DrawOpaqueBounds")["enabled"] = checked }
|
||||
}
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: "Transparents"
|
||||
checked: render.mainViewTask.getConfig("DrawTransparentBounds")["enabled"]
|
||||
onCheckedChanged: { render.mainViewTask.getConfig("DrawTransparentBounds")["enabled"] = checked }
|
||||
}
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: "Opaques in Front"
|
||||
checked: render.mainViewTask.getConfig("DrawOverlayInFrontOpaqueBounds")["enabled"]
|
||||
onCheckedChanged: { render.mainViewTask.getConfig("DrawOverlayInFrontOpaqueBounds")["enabled"] = checked }
|
||||
}
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: "Transparents in Front"
|
||||
checked: render.mainViewTask.getConfig("DrawOverlayInFrontTransparentBounds")["enabled"]
|
||||
onCheckedChanged: { render.mainViewTask.getConfig("DrawOverlayInFrontTransparentBounds")["enabled"] = checked }
|
||||
}
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: "Opaques in HUD"
|
||||
checked: render.mainViewTask.getConfig("DrawOverlayHUDOpaqueBounds")["enabled"]
|
||||
onCheckedChanged: { render.mainViewTask.getConfig("DrawOverlayHUDOpaqueBounds")["enabled"] = checked }
|
||||
}
|
||||
|
||||
}
|
||||
Column {
|
||||
spacing: 5
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: "Metas"
|
||||
checked: render.mainViewTask.getConfig("DrawMetaBounds")["enabled"]
|
||||
onCheckedChanged: { render.mainViewTask.getConfig("DrawMetaBounds")["enabled"] = checked }
|
||||
}
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: "Lights"
|
||||
checked: render.mainViewTask.getConfig("DrawLightBounds")["enabled"]
|
||||
onCheckedChanged: { render.mainViewTask.getConfig("DrawLightBounds")["enabled"] = checked; }
|
||||
}
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: "Zones"
|
||||
checked: render.mainViewTask.getConfig("DrawZones")["enabled"]
|
||||
onCheckedChanged: { render.mainViewTask.getConfig("ZoneRenderer")["enabled"] = checked; render.mainViewTask.getConfig("DrawZones")["enabled"] = checked; }
|
||||
}
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: "Transparents in HUD"
|
||||
checked: render.mainViewTask.getConfig("DrawOverlayHUDTransparentBounds")["enabled"]
|
||||
onCheckedChanged: { render.mainViewTask.getConfig("DrawOverlayHUDTransparentBounds")["enabled"] = checked }
|
||||
}
|
||||
}
|
||||
}
|
||||
Separator {}
|
||||
Row {
|
||||
|
|
|
@ -10,10 +10,13 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
|
||||
(function() {
|
||||
var AppUi = Script.require('appUi');
|
||||
|
||||
var MaterialInspector = Script.require('./materialInspector.js');
|
||||
var Page = Script.require('./luci/Page.js');
|
||||
|
||||
var moveDebugCursor = false;
|
||||
var onMousePressEvent = function (e) {
|
||||
|
@ -43,83 +46,12 @@
|
|||
Render.getConfig("RenderMainView").getConfig("DebugDeferredBuffer").size = { x: nx, y: ny, z: 1.0, w: 1.0 };
|
||||
}
|
||||
|
||||
function Page(title, qmlurl, width, height, handleWindowFunc) {
|
||||
this.title = title;
|
||||
this.qml = qmlurl;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.handleWindowFunc = handleWindowFunc;
|
||||
|
||||
this.window;
|
||||
|
||||
print("Page: New Page:" + JSON.stringify(this));
|
||||
}
|
||||
|
||||
Page.prototype.killView = function () {
|
||||
print("Page: Kill window for page:" + JSON.stringify(this));
|
||||
if (this.window) {
|
||||
print("Page: Kill window for page:" + this.title);
|
||||
//this.window.closed.disconnect(function () {
|
||||
// this.killView();
|
||||
//});
|
||||
this.window.close();
|
||||
this.window = false;
|
||||
}
|
||||
};
|
||||
|
||||
Page.prototype.createView = function () {
|
||||
var that = this;
|
||||
if (!this.window) {
|
||||
print("Page: New window for page:" + this.title);
|
||||
this.window = Desktop.createWindow(Script.resolvePath(this.qml), {
|
||||
title: this.title,
|
||||
presentationMode: Desktop.PresentationMode.NATIVE,
|
||||
size: {x: this.width, y: this.height}
|
||||
});
|
||||
this.handleWindowFunc(this.window);
|
||||
this.window.closed.connect(function () {
|
||||
that.killView();
|
||||
this.handleWindowFunc(undefined);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var Pages = function () {
|
||||
this._pages = {};
|
||||
};
|
||||
|
||||
Pages.prototype.addPage = function (command, title, qmlurl, width, height, handleWindowFunc) {
|
||||
if (handleWindowFunc === undefined) {
|
||||
// Workaround for bad linter
|
||||
handleWindowFunc = function(window){};
|
||||
}
|
||||
this._pages[command] = new Page(title, qmlurl, width, height, handleWindowFunc);
|
||||
};
|
||||
|
||||
Pages.prototype.open = function (command) {
|
||||
print("Pages: command = " + command);
|
||||
if (!this._pages[command]) {
|
||||
print("Pages: unknown command = " + command);
|
||||
return;
|
||||
}
|
||||
this._pages[command].createView();
|
||||
};
|
||||
|
||||
Pages.prototype.clear = function () {
|
||||
for (var p in this._pages) {
|
||||
print("Pages: kill page: " + p);
|
||||
this._pages[p].killView();
|
||||
delete this._pages[p];
|
||||
}
|
||||
this._pages = {};
|
||||
};
|
||||
var pages = new Pages();
|
||||
|
||||
pages.addPage('openEngineView', 'Render Engine', 'engineInspector.qml', 300, 400);
|
||||
pages.addPage('openEngineLODView', 'Render LOD', 'lod.qml', 300, 400);
|
||||
pages.addPage('openCullInspectorView', 'Cull Inspector', 'culling.qml', 300, 400);
|
||||
pages.addPage('openMaterialInspectorView', 'Material Inspector', 'materialInspector.qml', 300, 400, MaterialInspector.setWindow);
|
||||
pages.addPage('openEngineLODView', 'Render LOD', '../lod.qml', 300, 400);
|
||||
pages.addPage('openCullInspectorView', 'Cull Inspector', '../luci/Culling.qml', 300, 400);
|
||||
pages.addPage('openMaterialInspectorView', 'Material Inspector', '../materialInspector.qml', 300, 400, MaterialInspector.setWindow);
|
||||
|
||||
function fromQml(message) {
|
||||
if (pages.open(message.method)) {
|
||||
|
@ -132,7 +64,7 @@
|
|||
ui = new AppUi({
|
||||
buttonName: "LUCI",
|
||||
home: Script.resolvePath("deferredLighting.qml"),
|
||||
additionalAppScreens: Script.resolvePath("engineInspector.qml"),
|
||||
additionalAppScreens : Script.resolvePath("engineInspector.qml"),
|
||||
onMessage: fromQml,
|
||||
normalButton: Script.resolvePath("../../../system/assets/images/luci-i.svg"),
|
||||
activeButton: Script.resolvePath("../../../system/assets/images/luci-a.svg")
|
||||
|
@ -144,8 +76,5 @@
|
|||
Controller.mouseReleaseEvent.disconnect(onMouseReleaseEvent);
|
||||
Controller.mouseMoveEvent.disconnect(onMouseMoveEvent);
|
||||
pages.clear();
|
||||
// killEngineInspectorView();
|
||||
// killCullInspectorView();
|
||||
// killEngineLODWindow();
|
||||
});
|
||||
}());
|
||||
|
|
100
scripts/developer/utilities/render/luci.qml
Normal file
100
scripts/developer/utilities/render/luci.qml
Normal file
|
@ -0,0 +1,100 @@
|
|||
//
|
||||
// luci.qml
|
||||
//
|
||||
// Created by Sam Gateau on 3/2/2019
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.3
|
||||
|
||||
import controlsUit 1.0 as HifiControls
|
||||
|
||||
import "../lib/prop" as Prop
|
||||
import "../lib/jet/qml" as Jet
|
||||
import "luci"
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
id: render;
|
||||
property var mainViewTask: Render.getConfig("RenderMainView")
|
||||
|
||||
Prop.Global { id: global;}
|
||||
color: global.color
|
||||
|
||||
ScrollView {
|
||||
id: control
|
||||
anchors.fill: parent
|
||||
clip: true
|
||||
|
||||
Column {
|
||||
width: render.width
|
||||
Prop.PropFolderPanel {
|
||||
label: "Shading Model"
|
||||
panelFrameData: Component {
|
||||
ShadingModel {}
|
||||
}
|
||||
}
|
||||
Prop.PropFolderPanel {
|
||||
label: "Bounding Boxes"
|
||||
panelFrameData: Component {
|
||||
BoundingBoxes {}
|
||||
}
|
||||
}
|
||||
Prop.PropFolderPanel {
|
||||
label: "Framebuffer"
|
||||
panelFrameData: Component {
|
||||
Framebuffer {}
|
||||
}
|
||||
}
|
||||
Prop.PropFolderPanel {
|
||||
label: "Tone Mapping"
|
||||
panelFrameData: Component {
|
||||
ToneMapping {}
|
||||
}
|
||||
}
|
||||
Prop.PropFolderPanel {
|
||||
label: "Antialiasing"
|
||||
panelFrameData: Component {
|
||||
Antialiasing {}
|
||||
}
|
||||
}
|
||||
Prop.PropFolderPanel {
|
||||
label: "Culling"
|
||||
panelFrameData: Component {
|
||||
Culling {}
|
||||
}
|
||||
}
|
||||
Prop.PropFolderPanel {
|
||||
label: "Tools"
|
||||
panelFrameData: Component {
|
||||
Row {
|
||||
HifiControls.Button {
|
||||
text: "LOD"
|
||||
onClicked: {
|
||||
sendToScript({method: "openEngineLODView"});
|
||||
}
|
||||
}
|
||||
HifiControls.Button {
|
||||
text: "Material"
|
||||
onClicked: {
|
||||
sendToScript({method: "openMaterialInspectorView"});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Jet.TaskPropView {
|
||||
id: "le"
|
||||
jobPath: ""
|
||||
label: "Le Render Engine"
|
||||
|
||||
// anchors.left: parent.left
|
||||
// anchors.right: parent.right
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
176
scripts/developer/utilities/render/luci/Antialiasing.qml
Normal file
176
scripts/developer/utilities/render/luci/Antialiasing.qml
Normal file
|
@ -0,0 +1,176 @@
|
|||
//
|
||||
// Antialiasing.qml
|
||||
//
|
||||
// Created by Sam Gateau on 8/14/2017
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Layouts 1.3
|
||||
|
||||
import stylesUit 1.0
|
||||
import controlsUit 1.0 as HifiControls
|
||||
|
||||
import "../configSlider"
|
||||
import "../../lib/plotperf"
|
||||
|
||||
|
||||
Column{
|
||||
HifiConstants { id: hifi; }
|
||||
|
||||
id: antialiasing
|
||||
padding: 10
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
spacing: 10
|
||||
|
||||
Row {
|
||||
spacing: 10
|
||||
id: fxaaOnOff
|
||||
property bool debugFXAA: false
|
||||
HifiControls.Button {
|
||||
function getTheText() {
|
||||
if (Render.getConfig("RenderMainView.Antialiasing").fxaaOnOff) {
|
||||
return "FXAA"
|
||||
} else {
|
||||
return "TAA"
|
||||
}
|
||||
}
|
||||
text: getTheText()
|
||||
onClicked: {
|
||||
var onOff = !Render.getConfig("RenderMainView.Antialiasing").fxaaOnOff;
|
||||
if (onOff) {
|
||||
Render.getConfig("RenderMainView.JitterCam").none();
|
||||
Render.getConfig("RenderMainView.Antialiasing").fxaaOnOff = true;
|
||||
} else {
|
||||
Render.getConfig("RenderMainView.JitterCam").play();
|
||||
Render.getConfig("RenderMainView.Antialiasing").fxaaOnOff = false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
Separator {}
|
||||
Row {
|
||||
spacing: 10
|
||||
|
||||
HifiControls.Button {
|
||||
text: {
|
||||
var state = 2 - (Render.getConfig("RenderMainView.JitterCam").freeze * 1 - Render.getConfig("RenderMainView.JitterCam").stop * 2);
|
||||
if (state === 2) {
|
||||
return "Jitter"
|
||||
} else if (state === 1) {
|
||||
return "Paused at " + Render.getConfig("RenderMainView.JitterCam").index + ""
|
||||
} else {
|
||||
return "No Jitter"
|
||||
}
|
||||
}
|
||||
onClicked: { Render.getConfig("RenderMainView.JitterCam").cycleStopPauseRun(); }
|
||||
}
|
||||
HifiControls.Button {
|
||||
text: "<"
|
||||
onClicked: { Render.getConfig("RenderMainView.JitterCam").prev(); }
|
||||
}
|
||||
HifiControls.Button {
|
||||
text: ">"
|
||||
onClicked: { Render.getConfig("RenderMainView.JitterCam").next(); }
|
||||
}
|
||||
}
|
||||
Separator {}
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: "Constrain color"
|
||||
checked: Render.getConfig("RenderMainView.Antialiasing")["constrainColor"]
|
||||
onCheckedChanged: { Render.getConfig("RenderMainView.Antialiasing")["constrainColor"] = checked }
|
||||
}
|
||||
ConfigSlider {
|
||||
label: qsTr("Covariance gamma")
|
||||
integral: false
|
||||
config: Render.getConfig("RenderMainView.Antialiasing")
|
||||
property: "covarianceGamma"
|
||||
max: 1.5
|
||||
min: 0.5
|
||||
height: 38
|
||||
}
|
||||
Separator {}
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: "Feedback history color"
|
||||
checked: Render.getConfig("RenderMainView.Antialiasing")["feedbackColor"]
|
||||
onCheckedChanged: { Render.getConfig("RenderMainView.Antialiasing")["feedbackColor"] = checked }
|
||||
}
|
||||
|
||||
ConfigSlider {
|
||||
label: qsTr("Source blend")
|
||||
integral: false
|
||||
config: Render.getConfig("RenderMainView.Antialiasing")
|
||||
property: "blend"
|
||||
max: 1.0
|
||||
min: 0.0
|
||||
height: 38
|
||||
}
|
||||
|
||||
ConfigSlider {
|
||||
label: qsTr("Post sharpen")
|
||||
integral: false
|
||||
config: Render.getConfig("RenderMainView.Antialiasing")
|
||||
property: "sharpen"
|
||||
max: 1.0
|
||||
min: 0.0
|
||||
}
|
||||
Separator {}
|
||||
Row {
|
||||
|
||||
spacing: 10
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: "Debug"
|
||||
checked: Render.getConfig("RenderMainView.Antialiasing")["debug"]
|
||||
onCheckedChanged: { Render.getConfig("RenderMainView.Antialiasing")["debug"] = checked }
|
||||
}
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: "Show Debug Cursor"
|
||||
checked: Render.getConfig("RenderMainView.Antialiasing")["showCursorPixel"]
|
||||
onCheckedChanged: { Render.getConfig("RenderMainView.Antialiasing")["showCursorPixel"] = checked }
|
||||
}
|
||||
}
|
||||
ConfigSlider {
|
||||
label: qsTr("Debug Region <")
|
||||
integral: false
|
||||
config: Render.getConfig("RenderMainView.Antialiasing")
|
||||
property: "debugX"
|
||||
max: 1.0
|
||||
min: 0.0
|
||||
}
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: "Closest Fragment"
|
||||
checked: Render.getConfig("RenderMainView.Antialiasing")["showClosestFragment"]
|
||||
onCheckedChanged: { Render.getConfig("RenderMainView.Antialiasing")["showClosestFragment"] = checked }
|
||||
}
|
||||
ConfigSlider {
|
||||
label: qsTr("Debug Velocity Threshold [pix]")
|
||||
integral: false
|
||||
config: Render.getConfig("RenderMainView.Antialiasing")
|
||||
property: "debugShowVelocityThreshold"
|
||||
max: 50
|
||||
min: 0.0
|
||||
height: 38
|
||||
}
|
||||
ConfigSlider {
|
||||
label: qsTr("Debug Orb Zoom")
|
||||
integral: false
|
||||
config: Render.getConfig("RenderMainView.Antialiasing")
|
||||
property: "debugOrbZoom"
|
||||
max: 32.0
|
||||
min: 1.0
|
||||
height: 38
|
||||
}
|
||||
}
|
||||
|
84
scripts/developer/utilities/render/luci/BoundingBoxes.qml
Normal file
84
scripts/developer/utilities/render/luci/BoundingBoxes.qml
Normal file
|
@ -0,0 +1,84 @@
|
|||
//
|
||||
// BoundingBoxes.qml
|
||||
//
|
||||
// Created by Sam Gateau on 4/18/2019
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.7
|
||||
|
||||
import "../../lib/prop" as Prop
|
||||
|
||||
Column {
|
||||
|
||||
id: root;
|
||||
|
||||
property var mainViewTask: Render.getConfig("RenderMainView")
|
||||
|
||||
spacing: 5
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: hifi.dimensions.contentMargin.x
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
spacing: 5
|
||||
Column {
|
||||
spacing: 5
|
||||
|
||||
Prop.PropCheckBox {
|
||||
text: "Opaques"
|
||||
checked: root.mainViewTask.getConfig("DrawOpaqueBounds")["enabled"]
|
||||
onCheckedChanged: { root.mainViewTask.getConfig("DrawOpaqueBounds")["enabled"] = checked }
|
||||
}
|
||||
Prop.PropCheckBox {
|
||||
text: "Transparents"
|
||||
checked: root.mainViewTask.getConfig("DrawTransparentBounds")["enabled"]
|
||||
onCheckedChanged: { root.mainViewTask.getConfig("DrawTransparentBounds")["enabled"] = checked }
|
||||
}
|
||||
Prop.PropCheckBox {
|
||||
text: "Metas"
|
||||
checked: root.mainViewTask.getConfig("DrawMetaBounds")["enabled"]
|
||||
onCheckedChanged: { root.mainViewTask.getConfig("DrawMetaBounds")["enabled"] = checked }
|
||||
}
|
||||
Prop.PropCheckBox {
|
||||
text: "Lights"
|
||||
checked: root.mainViewTask.getConfig("DrawLightBounds")["enabled"]
|
||||
onCheckedChanged: { root.mainViewTask.getConfig("DrawLightBounds")["enabled"] = checked; }
|
||||
}
|
||||
Prop.PropCheckBox {
|
||||
text: "Zones"
|
||||
checked: root.mainViewTask.getConfig("DrawZones")["enabled"]
|
||||
onCheckedChanged: { root.mainViewTask.getConfig("ZoneRenderer")["enabled"] = checked; root.mainViewTask.getConfig("DrawZones")["enabled"] = checked; }
|
||||
}
|
||||
}
|
||||
Column {
|
||||
spacing: 5
|
||||
Prop.PropCheckBox {
|
||||
text: "Opaques in Front"
|
||||
checked: root.mainViewTask.getConfig("DrawOverlayInFrontOpaqueBounds")["enabled"]
|
||||
onCheckedChanged: { root.mainViewTask.getConfig("DrawOverlayInFrontOpaqueBounds")["enabled"] = checked }
|
||||
}
|
||||
Prop.PropCheckBox {
|
||||
text: "Transparents in Front"
|
||||
checked: root.mainViewTask.getConfig("DrawOverlayInFrontTransparentBounds")["enabled"]
|
||||
onCheckedChanged: { root.mainViewTask.getConfig("DrawOverlayInFrontTransparentBounds")["enabled"] = checked }
|
||||
}
|
||||
Prop.PropCheckBox {
|
||||
text: "Opaques in HUD"
|
||||
checked: root.mainViewTask.getConfig("DrawOverlayHUDOpaqueBounds")["enabled"]
|
||||
onCheckedChanged: { root.mainViewTask.getConfig("DrawOverlayHUDOpaqueBounds")["enabled"] = checked }
|
||||
}
|
||||
Prop.PropCheckBox {
|
||||
text: "Transparents in HUD"
|
||||
checked: root.mainViewTask.getConfig("DrawOverlayHUDTransparentBounds")["enabled"]
|
||||
onCheckedChanged: { root.mainViewTask.getConfig("DrawOverlayHUDTransparentBounds")["enabled"] = checked }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,11 +9,16 @@
|
|||
//
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import "configSlider"
|
||||
|
||||
import "../../lib/prop" as Prop
|
||||
|
||||
Column {
|
||||
id: root
|
||||
spacing: 8
|
||||
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
|
||||
property var sceneOctree: Render.getConfig("RenderMainView.DrawSceneOctree");
|
||||
property var itemSelection: Render.getConfig("RenderMainView.DrawItemSelection");
|
||||
|
||||
|
@ -36,6 +41,10 @@ Column {
|
|||
|
||||
GroupBox {
|
||||
title: "Culling"
|
||||
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
|
||||
Row {
|
||||
spacing: 8
|
||||
Column {
|
||||
|
@ -91,6 +100,7 @@ Column {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
GroupBox {
|
||||
|
@ -103,13 +113,14 @@ Column {
|
|||
anchors.right: parent.right;
|
||||
Repeater {
|
||||
model: [ "Opaque:RenderMainView.DrawOpaqueDeferred", "Transparent:RenderMainView.DrawTransparentDeferred", "Light:RenderMainView.DrawLight",
|
||||
"Opaque Overlays:RenderMainView.DrawOverlay3DOpaque", "Transparent Overlays:RenderMainView.DrawOverlay3DTransparent" ]
|
||||
ConfigSlider {
|
||||
"Opaque InFront:RenderMainView.DrawInFrontOpaque", "Transparent InFront:RenderMainView.DrawInFrontTransparent",
|
||||
"Opaque HUD:RenderMainView.DrawHUDOpaque", "Transparent HUD:RenderMainView.DrawHUDTransparent" ]
|
||||
Prop.PropScalar {
|
||||
label: qsTr(modelData.split(":")[0])
|
||||
integral: true
|
||||
config: Render.getConfig(modelData.split(":")[1])
|
||||
object: Render.getConfig(modelData.split(":")[1])
|
||||
property: "maxDrawn"
|
||||
max: config.numDrawn
|
||||
max: object.numDrawn
|
||||
min: -1
|
||||
}
|
||||
}
|
69
scripts/developer/utilities/render/luci/Framebuffer.qml
Normal file
69
scripts/developer/utilities/render/luci/Framebuffer.qml
Normal file
|
@ -0,0 +1,69 @@
|
|||
//
|
||||
// Framebuffer.qml
|
||||
//
|
||||
// Created by Sam Gateau on 4/18/2019
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.7
|
||||
|
||||
import "../../lib/prop" as Prop
|
||||
|
||||
Column {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
id: framebuffer
|
||||
|
||||
property var config: Render.getConfig("RenderMainView.DebugDeferredBuffer")
|
||||
|
||||
function setDebugMode(mode) {
|
||||
framebuffer.config.enabled = (mode != 0);
|
||||
framebuffer.config.mode = mode;
|
||||
}
|
||||
|
||||
Prop.PropEnum {
|
||||
label: "Debug Buffer"
|
||||
object: config
|
||||
property: "mode"
|
||||
valueVar: 0
|
||||
enums: [
|
||||
"Off",
|
||||
"Depth",
|
||||
"Albedo",
|
||||
"Normal",
|
||||
"Roughness",
|
||||
"Metallic",
|
||||
"Emissive",
|
||||
"Unlit",
|
||||
"Occlusion",
|
||||
"Lightmap",
|
||||
"Scattering",
|
||||
"Lighting",
|
||||
"Shadow Cascade 0",
|
||||
"Shadow Cascade 1",
|
||||
"Shadow Cascade 2",
|
||||
"Shadow Cascade 3",
|
||||
"Shadow Cascade Indices",
|
||||
"Linear Depth",
|
||||
"Half Linear Depth",
|
||||
"Half Normal",
|
||||
"Mid Curvature",
|
||||
"Mid Normal",
|
||||
"Low Curvature",
|
||||
"Low Normal",
|
||||
"Curvature Occlusion",
|
||||
"Debug Scattering",
|
||||
"Ambient Occlusion",
|
||||
"Ambient Occlusion Blurred",
|
||||
"Ambient Occlusion Normal",
|
||||
"Velocity",
|
||||
"Custom",
|
||||
]
|
||||
|
||||
valueVarSetter: function (mode) { framebuffer.setDebugMode(mode) }
|
||||
}
|
||||
}
|
90
scripts/developer/utilities/render/luci/Page.js
Normal file
90
scripts/developer/utilities/render/luci/Page.js
Normal file
|
@ -0,0 +1,90 @@
|
|||
//
|
||||
// Page.js
|
||||
//
|
||||
// Sam Gateau, created on 4/19/2019
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
"use strict";
|
||||
|
||||
(function() {
|
||||
function Page(title, qmlurl, width, height, onViewCreated, onViewClosed) {
|
||||
this.title = title;
|
||||
this.qml = qmlurl;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.onViewCreated = onViewCreated;
|
||||
this.onViewClosed = onViewClosed;
|
||||
|
||||
this.window;
|
||||
|
||||
print("Page: New Page:" + JSON.stringify(this));
|
||||
}
|
||||
|
||||
Page.prototype.killView = function () {
|
||||
print("Page: Kill window for page:" + JSON.stringify(this));
|
||||
if (this.window) {
|
||||
print("Page: Kill window for page:" + this.title);
|
||||
//this.window.closed.disconnect(function () {
|
||||
// this.killView();
|
||||
//});
|
||||
this.window.close();
|
||||
this.window = false;
|
||||
}
|
||||
};
|
||||
|
||||
Page.prototype.createView = function () {
|
||||
var that = this;
|
||||
if (!this.window) {
|
||||
print("Page: New window for page:" + this.title);
|
||||
this.window = Desktop.createWindow(Script.resolvePath(this.qml), {
|
||||
title: this.title,
|
||||
presentationMode: Desktop.PresentationMode.NATIVE,
|
||||
size: {x: this.width, y: this.height}
|
||||
});
|
||||
this.onViewCreated(this.window);
|
||||
this.window.closed.connect(function () {
|
||||
that.killView();
|
||||
that.onViewClosed();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Pages = function () {
|
||||
this._pages = {};
|
||||
};
|
||||
|
||||
Pages.prototype.addPage = function (command, title, qmlurl, width, height, onViewCreated, onViewClosed) {
|
||||
if (onViewCreated === undefined) {
|
||||
// Workaround for bad linter
|
||||
onViewCreated = function(window) {};
|
||||
}
|
||||
if (onViewClosed === undefined) {
|
||||
// Workaround for bad linter
|
||||
onViewClosed = function() {};
|
||||
}
|
||||
this._pages[command] = new Page(title, qmlurl, width, height, onViewCreated, onViewClosed);
|
||||
};
|
||||
|
||||
Pages.prototype.open = function (command) {
|
||||
print("Pages: command = " + command);
|
||||
if (!this._pages[command]) {
|
||||
print("Pages: unknown command = " + command);
|
||||
return;
|
||||
}
|
||||
this._pages[command].createView();
|
||||
};
|
||||
|
||||
Pages.prototype.clear = function () {
|
||||
for (var p in this._pages) {
|
||||
print("Pages: kill page: " + p);
|
||||
this._pages[p].killView();
|
||||
delete this._pages[p];
|
||||
}
|
||||
this._pages = {};
|
||||
};
|
||||
|
||||
}());
|
92
scripts/developer/utilities/render/luci/ShadingModel.qml
Normal file
92
scripts/developer/utilities/render/luci/ShadingModel.qml
Normal file
|
@ -0,0 +1,92 @@
|
|||
//
|
||||
// ShadingModel.qml
|
||||
//
|
||||
// Created by Sam Gateau on 4/17/2019
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.7
|
||||
|
||||
import "../../lib/prop" as Prop
|
||||
|
||||
Column {
|
||||
|
||||
id: shadingModel;
|
||||
|
||||
property var mainViewTask: Render.getConfig("RenderMainView")
|
||||
|
||||
spacing: 5
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: hifi.dimensions.contentMargin.x
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
spacing: 5
|
||||
Column {
|
||||
spacing: 5
|
||||
Repeater {
|
||||
model: [
|
||||
"Unlit:LightingModel:enableUnlit",
|
||||
"Emissive:LightingModel:enableEmissive",
|
||||
"Lightmap:LightingModel:enableLightmap",
|
||||
"Background:LightingModel:enableBackground",
|
||||
"Haze:LightingModel:enableHaze",
|
||||
"AO:LightingModel:enableAmbientOcclusion",
|
||||
"Textures:LightingModel:enableMaterialTexturing"
|
||||
]
|
||||
Prop.PropCheckBox {
|
||||
text: modelData.split(":")[0]
|
||||
checked: shadingModel.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]]
|
||||
onCheckedChanged: { shadingModel.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Column {
|
||||
spacing: 5
|
||||
Repeater {
|
||||
model: [
|
||||
"Obscurance:LightingModel:enableObscurance",
|
||||
"Scattering:LightingModel:enableScattering",
|
||||
"Diffuse:LightingModel:enableDiffuse",
|
||||
"Specular:LightingModel:enableSpecular",
|
||||
"Albedo:LightingModel:enableAlbedo",
|
||||
"Wireframe:LightingModel:enableWireframe",
|
||||
"Skinning:LightingModel:enableSkinning",
|
||||
"Blendshape:LightingModel:enableBlendshape"
|
||||
]
|
||||
Prop.PropCheckBox {
|
||||
text: modelData.split(":")[0]
|
||||
checked: shadingModel.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]]
|
||||
onCheckedChanged: { shadingModel.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
spacing: 5
|
||||
Repeater {
|
||||
model: [
|
||||
"Ambient:LightingModel:enableAmbientLight",
|
||||
"Directional:LightingModel:enableDirectionalLight",
|
||||
"Point:LightingModel:enablePointLight",
|
||||
"Spot:LightingModel:enableSpotLight",
|
||||
"Light Contour:LightingModel:showLightContour",
|
||||
"Zone Stack:DrawZoneStack:enabled",
|
||||
"Shadow:LightingModel:enableShadow"
|
||||
]
|
||||
Prop.PropCheckBox {
|
||||
text: modelData.split(":")[0]
|
||||
checked: shadingModel.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]]
|
||||
onCheckedChanged: { shadingModel.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
40
scripts/developer/utilities/render/luci/ToneMapping.qml
Normal file
40
scripts/developer/utilities/render/luci/ToneMapping.qml
Normal file
|
@ -0,0 +1,40 @@
|
|||
//
|
||||
// ToneMapping.qml
|
||||
//
|
||||
// Created by Sam Gateau on 4/17/2019
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.7
|
||||
|
||||
import "../../lib/prop" as Prop
|
||||
|
||||
Column {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
Prop.PropScalar {
|
||||
label: "Exposure"
|
||||
object: Render.getConfig("RenderMainView.ToneMapping")
|
||||
property: "exposure"
|
||||
min: -4
|
||||
max: 4
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
}
|
||||
Prop.PropEnum {
|
||||
label: "Tone Curve"
|
||||
object: Render.getConfig("RenderMainView.ToneMapping")
|
||||
property: "curve"
|
||||
enums: [
|
||||
"RGB",
|
||||
"SRGB",
|
||||
"Reinhard",
|
||||
"Filmic",
|
||||
]
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
}
|
||||
}
|
7
scripts/developer/utilities/render/luci/qmldir
Normal file
7
scripts/developer/utilities/render/luci/qmldir
Normal file
|
@ -0,0 +1,7 @@
|
|||
|
||||
ShadingModel 1.0 ShadingModel.qml
|
||||
ToneMapping 1.0 ToneMapping.qml
|
||||
BoundingBoxes 1.0 BoundingBoxes.qml
|
||||
Framebuffer 1.0 Framebuffer.qml
|
||||
Antialiasing 1.0 Antialiasing.qml
|
||||
Culling 1.0 Culling.qml
|
83
scripts/developer/utilities/render/luci2.js
Normal file
83
scripts/developer/utilities/render/luci2.js
Normal file
|
@ -0,0 +1,83 @@
|
|||
|
||||
|
||||
var MaterialInspector = Script.require('./materialInspector.js');
|
||||
var Page = Script.require('./luci/Page.js');
|
||||
|
||||
|
||||
function openView() {
|
||||
//window.closed.connect(function() { Script.stop(); });
|
||||
|
||||
|
||||
var pages = new Pages();
|
||||
function fromQml(message) {
|
||||
if (pages.open(message.method)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var luciWindow
|
||||
function openLuciWindow(window) {
|
||||
if (luciWindow !== undefined) {
|
||||
activeWindow.fromQml.disconnect(fromQml);
|
||||
}
|
||||
if (window !== undefined) {
|
||||
window.fromQml.connect(fromQml);
|
||||
}
|
||||
luciWindow = window;
|
||||
|
||||
|
||||
var moveDebugCursor = false;
|
||||
var onMousePressEvent = function (e) {
|
||||
if (e.isMiddleButton) {
|
||||
moveDebugCursor = true;
|
||||
setDebugCursor(e.x, e.y);
|
||||
}
|
||||
};
|
||||
Controller.mousePressEvent.connect(onMousePressEvent);
|
||||
|
||||
var onMouseReleaseEvent = function () {
|
||||
moveDebugCursor = false;
|
||||
};
|
||||
Controller.mouseReleaseEvent.connect(onMouseReleaseEvent);
|
||||
|
||||
var onMouseMoveEvent = function (e) {
|
||||
if (moveDebugCursor) {
|
||||
setDebugCursor(e.x, e.y);
|
||||
}
|
||||
};
|
||||
Controller.mouseMoveEvent.connect(onMouseMoveEvent);
|
||||
|
||||
function setDebugCursor(x, y) {
|
||||
var nx = 2.0 * (x / Window.innerWidth) - 1.0;
|
||||
var ny = 1.0 - 2.0 * ((y) / (Window.innerHeight));
|
||||
|
||||
Render.getConfig("RenderMainView").getConfig("DebugDeferredBuffer").size = { x: nx, y: ny, z: 1.0, w: 1.0 };
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function closeLuciWindow() {
|
||||
if (luciWindow !== undefined) {
|
||||
activeWindow.fromQml.disconnect(fromQml);
|
||||
}
|
||||
luciWindow = {};
|
||||
|
||||
Controller.mousePressEvent.disconnect(onMousePressEvent);
|
||||
Controller.mouseReleaseEvent.disconnect(onMouseReleaseEvent);
|
||||
Controller.mouseMoveEvent.disconnect(onMouseMoveEvent);
|
||||
pages.clear();
|
||||
}
|
||||
|
||||
pages.addPage('Luci', 'Luci', '../luci.qml', 300, 420, openLuciWindow, closeLuciWindow);
|
||||
pages.addPage('openEngineLODView', 'Render LOD', '../lod.qml', 300, 400);
|
||||
pages.addPage('openMaterialInspectorView', 'Material Inspector', '../materialInspector.qml', 300, 400, MaterialInspector.setWindow, MaterialInspector.setWindow);
|
||||
|
||||
pages.open('Luci');
|
||||
|
||||
|
||||
return pages;
|
||||
}
|
||||
|
||||
|
||||
openView();
|
||||
|
|
@ -109,7 +109,9 @@ function mouseReleaseEvent(event) {
|
|||
}
|
||||
|
||||
function killWindow() {
|
||||
setWindow(undefined);
|
||||
activeWindow = undefined;
|
||||
|
||||
// setWindow(undefined);
|
||||
}
|
||||
|
||||
function toQml(message) {
|
||||
|
@ -138,14 +140,14 @@ function setSelectedObject(id, type) {
|
|||
function setWindow(window) {
|
||||
if (activeWindow !== undefined) {
|
||||
setSelectedObject(Uuid.NULL, "");
|
||||
activeWindow.closed.disconnect(killWindow);
|
||||
// activeWindow.closed.disconnect(killWindow);
|
||||
activeWindow.fromQml.disconnect(fromQml);
|
||||
Controller.mousePressEvent.disconnect(mousePressEvent);
|
||||
Controller.mouseReleaseEvent.disconnect(mouseReleaseEvent);
|
||||
activeWindow.close();
|
||||
}
|
||||
if (window !== undefined) {
|
||||
window.closed.connect(killWindow);
|
||||
// window.closed.connect(killWindow);
|
||||
window.fromQml.connect(fromQml);
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
|
||||
|
|
|
@ -40,9 +40,6 @@ function updateControllerDisplay() {
|
|||
var button;
|
||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
|
||||
// Independent and Entity mode make people sick; disable them in hmd.
|
||||
var desktopOnlyViews = ['Independent Mode', 'Entity Mode'];
|
||||
|
||||
var switchToVR = "ENTER VR";
|
||||
var switchToDesktop = "EXIT VR";
|
||||
|
||||
|
@ -59,9 +56,6 @@ function onHmdChanged(isHmd) {
|
|||
text: switchToVR
|
||||
});
|
||||
}
|
||||
desktopOnlyViews.forEach(function (view) {
|
||||
Menu.setMenuEnabled("View>" + view, !isHmd);
|
||||
});
|
||||
updateControllerDisplay();
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,8 @@ Grid = function() {
|
|||
color: gridColor,
|
||||
alpha: gridAlpha,
|
||||
minorGridEvery: minorGridEvery,
|
||||
majorGridEvery: majorGridEvery
|
||||
majorGridEvery: majorGridEvery,
|
||||
ignorePickIntersection: true
|
||||
});
|
||||
|
||||
that.visible = false;
|
||||
|
|
|
@ -253,9 +253,7 @@
|
|||
"UserActivityLoggerDisabled": false,
|
||||
"View/Center Player In View": true,
|
||||
"View/Enter First Person Mode in HMD": true,
|
||||
"View/Entity Mode": false,
|
||||
"View/First Person": true,
|
||||
"View/Independent Mode": false,
|
||||
"View/Mirror": false,
|
||||
"View/Third Person": false,
|
||||
"WindowGeometry": "@Rect(0 0 1920 1080)",
|
||||
|
|
|
@ -170,7 +170,7 @@ void DomainBaker::addModelBaker(const QString& property, const QString& url, con
|
|||
// move the baker to the baker thread
|
||||
// and kickoff the bake
|
||||
baker->moveToThread(Oven::instance().getNextWorkerThread());
|
||||
QMetaObject::invokeMethod(baker.data(), "bake");
|
||||
QMetaObject::invokeMethod(baker.data(), "bake", Qt::QueuedConnection);
|
||||
|
||||
// keep track of the total number of baking entities
|
||||
++_totalNumberOfSubBakes;
|
||||
|
@ -211,7 +211,7 @@ void DomainBaker::addTextureBaker(const QString& property, const QString& url, i
|
|||
|
||||
// move the baker to a worker thread and kickoff the bake
|
||||
textureBaker->moveToThread(Oven::instance().getNextWorkerThread());
|
||||
QMetaObject::invokeMethod(textureBaker.data(), "bake");
|
||||
QMetaObject::invokeMethod(textureBaker.data(), "bake", Qt::QueuedConnection);
|
||||
|
||||
// keep track of the total number of baking entities
|
||||
++_totalNumberOfSubBakes;
|
||||
|
@ -246,7 +246,7 @@ void DomainBaker::addScriptBaker(const QString& property, const QString& url, co
|
|||
|
||||
// move the baker to a worker thread and kickoff the bake
|
||||
scriptBaker->moveToThread(Oven::instance().getNextWorkerThread());
|
||||
QMetaObject::invokeMethod(scriptBaker.data(), "bake");
|
||||
QMetaObject::invokeMethod(scriptBaker.data(), "bake", Qt::QueuedConnection);
|
||||
|
||||
// keep track of the total number of baking entities
|
||||
++_totalNumberOfSubBakes;
|
||||
|
@ -271,7 +271,7 @@ void DomainBaker::addMaterialBaker(const QString& property, const QString& data,
|
|||
|
||||
// setup a baker for this material
|
||||
QSharedPointer<MaterialBaker> materialBaker {
|
||||
new MaterialBaker(data, isURL, _contentOutputPath, destinationPath),
|
||||
new MaterialBaker(materialData, isURL, _contentOutputPath, destinationPath),
|
||||
&MaterialBaker::deleteLater
|
||||
};
|
||||
|
||||
|
@ -283,7 +283,7 @@ void DomainBaker::addMaterialBaker(const QString& property, const QString& data,
|
|||
|
||||
// move the baker to a worker thread and kickoff the bake
|
||||
materialBaker->moveToThread(Oven::instance().getNextWorkerThread());
|
||||
QMetaObject::invokeMethod(materialBaker.data(), "bake");
|
||||
QMetaObject::invokeMethod(materialBaker.data(), "bake", Qt::QueuedConnection);
|
||||
|
||||
// keep track of the total number of baking entities
|
||||
++_totalNumberOfSubBakes;
|
||||
|
@ -409,7 +409,10 @@ void DomainBaker::enumerateEntities() {
|
|||
|
||||
// Materials
|
||||
if (entity.contains(MATERIAL_URL_KEY)) {
|
||||
addMaterialBaker(MATERIAL_URL_KEY, entity[MATERIAL_URL_KEY].toString(), true, *it);
|
||||
QString materialURL = entity[MATERIAL_URL_KEY].toString();
|
||||
if (!materialURL.startsWith("materialData")) {
|
||||
addMaterialBaker(MATERIAL_URL_KEY, materialURL, true, *it);
|
||||
}
|
||||
}
|
||||
if (entity.contains(MATERIAL_DATA_KEY)) {
|
||||
addMaterialBaker(MATERIAL_DATA_KEY, entity[MATERIAL_DATA_KEY].toString(), false, *it, _destinationPath);
|
||||
|
|
Before (image error) Size: 5.4 KiB After (image error) Size: 5.4 KiB |
Before (image error) Size: 2.6 KiB After (image error) Size: 2.6 KiB |
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue