3
0
Fork 0
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:
Sam Gondelman 2019-04-25 13:21:27 -07:00 committed by GitHub
commit ef64de7c04
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
102 changed files with 2632 additions and 1146 deletions
assignment-client/src/assets
cmake/externals/LibOVR
interface
libraries
plugins
scripts
tools
nitpick/AppDataHighFidelity
oven/src
unpublishedScripts/marketplace/record/assets

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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

View file

@ -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;
}
}

View file

@ -952,7 +952,7 @@ Rectangle {
text: "LOG IN"
onClicked: {
sendToScript({method: 'needsLogIn_loginClicked'});
sendToScript({method: 'marketplace_loginClicked'});
}
}

View file

@ -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'});
}
}
}

View file

@ -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) {

View file

@ -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;

View file

@ -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

View file

@ -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...";

View 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);

View file

@ -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();

View file

@ -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;

View file

@ -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);
}
}
}
}

View file

@ -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");

View file

@ -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);

View file

@ -409,6 +409,7 @@ protected:
private:
bool _settingsLoaded { false };
float _inputVolume { 1.0f };
float _inputLevel { 0.0f };
float _localInjectorGain { 0.0f }; // in dB

View file

@ -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);
});
}

View file

@ -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() {

View file

@ -97,6 +97,8 @@ private:
bool _cubemapOutputFormat;
QTimer _snapshotTimer;
qint16 _snapshotIndex;
bool _waitingOnSnapshot { false };
bool _taking360Snapshot { false };
bool _oldEnabled;
QVariant _oldAttachedEntityId;
QVariant _oldOrientation;

View file

@ -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;
}
}

View file

@ -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;

View file

@ -261,7 +261,7 @@ public:
qCDebug(animation) << " " << pair.first << "=" << pair.second.getString();
break;
default:
assert(("invalid AnimVariant::Type", false));
assert(false);
}
}
}

View file

@ -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;
}

View file

@ -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; }

View file

@ -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 };

View file

@ -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;
};

View file

@ -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();
}
}

View file

@ -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:

View file

@ -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);
}

View file

@ -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();
};

View file

@ -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;

View file

@ -48,6 +48,8 @@ public:
void pluginUpdate() override {};
virtual StencilMaskMode getStencilMaskMode() const override { return StencilMaskMode::PAINT; }
signals:
void hmdMountedChanged();
void hmdVisibleChanged(bool visible);

View file

@ -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;
}

View file

@ -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";
}

View file

@ -784,6 +784,7 @@ private:
void setHFMMaterial(HFMMaterial& fbxmat, const GLTFMaterial& material);
HFMTexture getHFMTexture(const GLTFTexture& texture);
void glTFDebugDump();
void hfmDebugDump(const HFMModel& hfmModel);
};

View file

@ -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;

View file

@ -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;

View file

@ -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());

View file

@ -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);

View file

@ -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;

View file

@ -576,6 +576,7 @@ Resource::Resource(const Resource& other) :
Resource::Resource(const QUrl& url) :
_url(url),
_effectiveBaseURL(url),
_activeUrl(url),
_requestID(++requestID) {
init();

View file

@ -28,7 +28,7 @@
static AAssetManager* ASSET_MANAGER = nullptr;
#define USE_BLIT_PRESENT 0
#define USE_BLIT_PRESENT 1
#if !USE_BLIT_PRESENT

View file

@ -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();

View file

@ -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);
}
});
}

View file

@ -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 };
};

View file

@ -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;
};
}

View file

@ -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()) {

View file

@ -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);

View file

@ -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());
}

View file

@ -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

View 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

View file

@ -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;
}

View file

@ -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)

View file

@ -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;
}

View file

@ -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 };
};

View file

@ -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;
}

View file

@ -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 };
};

View file

@ -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)

View 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()
}
}

View file

@ -1,3 +1,4 @@
TaskList 1.0 TaskList.qml
TaskViewList 1.0 TaskViewList.qml
TaskTimeFrameView 1.0 TaskTimeFrameView.qml
TaskPropView 1.0 TaskPropView.qml

View 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); }
}
}

View 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); }
}
}

View 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: {
}
}

View 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
}
}

View 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) }
}
}

View 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

View 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
}

View 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
}
}

View 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
}

View 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
}
}
}

View 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
}

View 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
}

View 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
}

View 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
}

View file

@ -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;

View file

@ -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
}
}
}
}
}

View file

@ -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,

View file

@ -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 {

View file

@ -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();
});
}());

View 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
}
}
}
}

View 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
}
}

View 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 }
}
}
}
}

View file

@ -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
}
}

View 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) }
}
}

View 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 = {};
};
}());

View 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 }
}
}
}
}
}

View 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
}
}

View 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

View 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();

View file

@ -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);

View file

@ -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();
}

View file

@ -25,7 +25,8 @@ Grid = function() {
color: gridColor,
alpha: gridAlpha,
minorGridEvery: minorGridEvery,
majorGridEvery: majorGridEvery
majorGridEvery: majorGridEvery,
ignorePickIntersection: true
});
that.visible = false;

View file

@ -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)",

View file

@ -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);

Some files were not shown because too many files have changed in this diff Show more