From c4d43e7884c3fd6fa8614a3587d5677ed98b1dce Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Mon, 8 Apr 2019 10:27:51 -0700 Subject: [PATCH 01/56] Fix out of bounds when the model url is incorrect --- libraries/hfm/src/hfm/HFM.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/hfm/src/hfm/HFM.cpp b/libraries/hfm/src/hfm/HFM.cpp index e930f30d1a..6a9a714234 100644 --- a/libraries/hfm/src/hfm/HFM.cpp +++ b/libraries/hfm/src/hfm/HFM.cpp @@ -166,7 +166,9 @@ void HFMModel::computeKdops() { glm::vec3(INV_SQRT_3, INV_SQRT_3, -INV_SQRT_3), glm::vec3(INV_SQRT_3, -INV_SQRT_3, -INV_SQRT_3) }; - + if (joints.size() != shapeVertices.size()) { + return; + } // now that all joints have been scanned compute a k-Dop bounding volume of mesh for (int i = 0; i < joints.size(); ++i) { HFMJoint& joint = joints[i]; From f08b5e3e277e34cdfb697a2d353e89a38baa2595 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 8 Apr 2019 11:03:21 -0700 Subject: [PATCH 02/56] disable jsbaker (cherry picked from commit 7cbda006a088ec5613dce484514466666ac73b1f) --- tools/oven/src/BakerCLI.cpp | 5 +++-- tools/oven/src/DomainBaker.cpp | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/oven/src/BakerCLI.cpp b/tools/oven/src/BakerCLI.cpp index f09db3ed26..64462a4e37 100644 --- a/tools/oven/src/BakerCLI.cpp +++ b/tools/oven/src/BakerCLI.cpp @@ -55,8 +55,9 @@ void BakerCLI::bakeFile(QUrl inputUrl, const QString& outputPath, const QString& } } } else if (type == SCRIPT_EXTENSION) { - _baker = std::unique_ptr { new JSBaker(inputUrl, outputPath) }; - _baker->moveToThread(Oven::instance().getNextWorkerThread()); + // FIXME: disabled for now because it breaks some scripts + //_baker = std::unique_ptr { new JSBaker(inputUrl, outputPath) }; + //_baker->moveToThread(Oven::instance().getNextWorkerThread()); } else if (type == MATERIAL_EXTENSION) { _baker = std::unique_ptr { new MaterialBaker(inputUrl.toDisplayString(), true, outputPath) }; _baker->moveToThread(Oven::instance().getNextWorkerThread()); diff --git a/tools/oven/src/DomainBaker.cpp b/tools/oven/src/DomainBaker.cpp index b92a310f5d..8a4489fcac 100644 --- a/tools/oven/src/DomainBaker.cpp +++ b/tools/oven/src/DomainBaker.cpp @@ -397,6 +397,8 @@ void DomainBaker::enumerateEntities() { } } + // FIXME: disabled for now because it breaks some scripts + /* // Scripts if (entity.contains(SCRIPT_KEY)) { addScriptBaker(SCRIPT_KEY, entity[SCRIPT_KEY].toString(), *it); @@ -404,6 +406,7 @@ void DomainBaker::enumerateEntities() { if (entity.contains(SERVER_SCRIPTS_KEY)) { // TODO: serverScripts can be multiple scripts, need to handle that } + */ // Materials if (entity.contains(MATERIAL_URL_KEY)) { From 4b7274f02fc0c2a98009e70e5a35f206e7d5f2b6 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Mon, 8 Apr 2019 11:28:10 -0700 Subject: [PATCH 03/56] Fix comparison warning --- libraries/hfm/src/hfm/HFM.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/hfm/src/hfm/HFM.cpp b/libraries/hfm/src/hfm/HFM.cpp index 6a9a714234..236445bfda 100644 --- a/libraries/hfm/src/hfm/HFM.cpp +++ b/libraries/hfm/src/hfm/HFM.cpp @@ -166,7 +166,7 @@ void HFMModel::computeKdops() { glm::vec3(INV_SQRT_3, INV_SQRT_3, -INV_SQRT_3), glm::vec3(INV_SQRT_3, -INV_SQRT_3, -INV_SQRT_3) }; - if (joints.size() != shapeVertices.size()) { + if (joints.size() != (int)shapeVertices.size()) { return; } // now that all joints have been scanned compute a k-Dop bounding volume of mesh From 8a460276e59842e5d4ac307fd878f8cd511a71b2 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 8 Apr 2019 23:45:47 -0700 Subject: [PATCH 04/56] use # instead of ? for material URL name selection (cherry picked from commit a50cca006e5d1be145e03d851663937a640652cd) --- libraries/baking/src/ModelBaker.cpp | 2 +- .../entities-renderer/src/RenderableMaterialEntityItem.cpp | 6 +++++- libraries/entities/src/EntityItemProperties.cpp | 2 +- libraries/render-utils/src/Model.cpp | 4 ++-- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/libraries/baking/src/ModelBaker.cpp b/libraries/baking/src/ModelBaker.cpp index 82af2f94e9..e58ec00afa 100644 --- a/libraries/baking/src/ModelBaker.cpp +++ b/libraries/baking/src/ModelBaker.cpp @@ -285,7 +285,7 @@ void ModelBaker::handleFinishedMaterialBaker() { QJsonArray materialMapping; for (auto material : _hfmModel->materials) { QJsonObject json; - json["mat::" + material.name] = relativeBakedMaterialURL + "?" + material.name; + json["mat::" + material.name] = relativeBakedMaterialURL + "#" + material.name; materialMapping.push_back(json); } diff --git a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp index da8baca95a..01d1098daa 100644 --- a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp @@ -121,7 +121,11 @@ void MaterialEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPo QString materialURL = entity->getMaterialURL(); if (materialURL != _materialURL) { _materialURL = materialURL; - if (_materialURL.contains("?")) { + if (_materialURL.contains("#")) { + auto split = _materialURL.split("#"); + newCurrentMaterialName = split.last().toStdString(); + } else if (_materialURL.contains("?")) { + qDebug() << "DEPRECATED: Use # instead of ? for material URLS:" << _materialURL; auto split = _materialURL.split("?"); newCurrentMaterialName = split.last().toStdString(); } diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 5958af66dd..44e317696c 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -976,7 +976,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * by setting the entityHostType parameter in {@link Entities.addEntity} to "avatar". * Material entities render as non-scalable spheres if they don't have their parent set. * @typedef {object} Entities.EntityProperties-Material - * @property {string} materialURL="" - URL to a {@link MaterialResource}. If you append ?name to the URL, the + * @property {string} materialURL="" - URL to a {@link MaterialResource}. If you append #name to the URL, the * material with that name in the {@link MaterialResource} will be applied to the entity.
* Alternatively, set the property value to "materialData" to use the materialData property * for the {@link MaterialResource} values. diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 6b33012adf..fbe0af11f2 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1561,8 +1561,8 @@ void Model::applyMaterialMapping() { { QString url = networkMaterialResource->getURL().toString(); bool foundMaterialName = false; - if (url.contains("?")) { - auto split = url.split("?"); + if (url.contains("#")) { + auto split = url.split("#"); std::string materialName = split.last().toStdString(); auto networkMaterialIter = networkMaterialResource->parsedMaterials.networkMaterials.find(materialName); if (networkMaterialIter != networkMaterialResource->parsedMaterials.networkMaterials.end()) { From 2d2fd0d2a4e54a3461061c0a71083d5ec7a8fc2c Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 10 Apr 2019 09:44:56 -0700 Subject: [PATCH 05/56] only call onMove on lastMouseEvent change --- scripts/system/libraries/entitySelectionTool.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index a0265a6ab8..60c9533e6a 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -1347,12 +1347,16 @@ SelectionDisplay = (function() { }; that.updateLastMouseEvent = function(event) { - if (activeTool && lastMouseEvent !== null) { + if (activeTool && lastMouseEvent !== null) { + var change = lastMouseEvent.isShifted !== event.isShifted || lastMouseEvent.isMeta !== event.isMeta || + lastMouseEvent.isControl !== event.isControl || lastMouseEvent.isAlt !== event.isAlt; lastMouseEvent.isShifted = event.isShifted; lastMouseEvent.isMeta = event.isMeta; lastMouseEvent.isControl = event.isControl; - lastMouseEvent.isAlt = event.isAlt; - activeTool.onMove(lastMouseEvent); + lastMouseEvent.isAlt = event.isAlt; + if (change) { + activeTool.onMove(lastMouseEvent); + } } }; From f94c78e15d111c81aa506d2ca2d50e6f7b511ca9 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 8 Apr 2019 13:29:43 -0700 Subject: [PATCH 06/56] fix avatar/shape material targets, refresh material target when parent changes --- scripts/system/edit.js | 29 +++++++++++++--------- scripts/system/html/js/entityProperties.js | 25 +++++++++++++++++-- 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 8ca6c5b14c..b98d28ea48 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -2523,18 +2523,23 @@ var PropertiesTool = function (opts) { propertyRanges: propertyRanges, }); } else if (data.type === "materialTargetRequest") { - var properties = Entities.getEntityProperties(data.entityID, ["type", "parentID"]); - var parentModel = properties.parentID !== Uuid.NULL && - Entities.getEntityProperties(properties.parentID, ["type"]).type === "Model"; - var parentModelData; - if (properties.type === "Material" && parentModel) { - parentModelData = Graphics.getModel(properties.parentID); - } - emitScriptEvent({ - type: 'materialTargetReply', - materialTargetData: parentModelData, - }); - } + var parentModelData; + var properties = Entities.getEntityProperties(data.entityID, ["type", "parentID"]); + if (properties.type === "Material" && properties.parentID !== Uuid.NULL) { + var parentType = Entities.getEntityProperties(properties.parentID, ["type"]).type; + if (parentType === "Model" || Entities.getNestableType(properties.parentID) === "avatar") { + parentModelData = Graphics.getModel(properties.parentID); + } else if (parentType === "Shape" || parentType === "Box" || parentType === "Sphere") { + parentModelData = {}; + parentModelData.numMeshes = 1; + parentModelData.materialNames = []; + } + } + emitScriptEvent({ + type: 'materialTargetReply', + materialTargetData: parentModelData, + }); + } }; HMD.displayModeChanged.connect(function() { diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index ca04f36dc9..1fe29cc579 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -55,6 +55,7 @@ const GROUPS = [ label: "Parent", type: "string", propertyID: "parentID", + onChange: parentIDChanged, }, { label: "Parent Joint Index", @@ -2007,6 +2008,9 @@ function createStringProperty(property, elProperty) { elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(property)); + if (propertyData.onChange !== undefined) { + elInput.addEventListener('change', propertyData.onChange); + } elProperty.appendChild(elInput); @@ -2622,6 +2626,17 @@ function createProperty(propertyData, propertyElementID, propertyName, propertyI } +/** + * PROPERTY-SPECIFIC CALLBACKS + */ + +function parentIDChanged() { + if (selectedEntityProperties.type === "Material") { + requestMaterialTarget(); + } +} + + /** * BUTTON CALLBACKS */ @@ -3156,6 +3171,10 @@ function setTextareaScrolling(element) { * MATERIAL TARGET FUNCTIONS */ +function requestMaterialTarget() { + EventBridge.emitWebEvent(JSON.stringify({ type: 'materialTargetRequest', entityID: selectedEntityProperties.id })); +} + function setMaterialTargetData(materialTargetData) { let elDivOptions = getPropertyInputElement("parentMaterialName"); resetDynamicMultiselectProperty(elDivOptions); @@ -3256,7 +3275,9 @@ function sendMaterialTargetProperty() { if (materialTargetList !== "") { materialTargetList = materialTargetList.substring(0, materialTargetList.length - 1); - materialTargetList = "[" + materialTargetList + "]"; + if (materialTargetList.length > 1) { + materialTargetList = "[" + materialTargetList + "]"; + } } updateProperty("parentMaterialName", materialTargetList, false); @@ -3780,7 +3801,7 @@ function loaded() { } if (hasSelectedEntityChanged && selectedEntityProperties.type === "Material") { - EventBridge.emitWebEvent(JSON.stringify({ type: 'materialTargetRequest', entityID: selectedEntityProperties.id })); + requestMaterialTarget(); } let activeElement = document.activeElement; From 6ea92a4698b6ec58e47793d6f951f8bb7fac94fa Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Wed, 3 Apr 2019 12:27:32 -0700 Subject: [PATCH 07/56] Fix TextureBaker failures and heap corruption in MaterialBaker --- libraries/baking/src/MaterialBaker.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/baking/src/MaterialBaker.cpp b/libraries/baking/src/MaterialBaker.cpp index 9fcd7d0354..7fc2573d7b 100644 --- a/libraries/baking/src/MaterialBaker.cpp +++ b/libraries/baking/src/MaterialBaker.cpp @@ -144,7 +144,12 @@ void MaterialBaker::processMaterial() { connect(textureBaker.data(), &TextureBaker::finished, this, &MaterialBaker::handleFinishedTextureBaker); _textureBakers.insert(textureKey, textureBaker); textureBaker->moveToThread(_getNextOvenWorkerThreadOperator ? _getNextOvenWorkerThreadOperator() : thread()); - QMetaObject::invokeMethod(textureBaker.data(), "bake"); + // By default, Qt will invoke this bake immediately if the TextureBaker is on the same worker thread as this MaterialBaker. + // We don't want that, because threads may be waiting for work while this thread is stuck processing a TextureBaker. + // On top of that, _textureBakers isn't fully populated. + // So, use Qt::QueuedConnection. + // TODO: Better thread utilization at the top level, not just the MaterialBaker level + QMetaObject::invokeMethod(textureBaker.data(), "bake", Qt::QueuedConnection); } _materialsNeedingRewrite.insert(textureKey, networkMaterial.second); } else { From 4954d64c9e803072b60c6d4d0e00ac5e7de4df7b Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 11 Apr 2019 14:20:52 -0700 Subject: [PATCH 08/56] Fix lack of ranges on numerical types in Create --- libraries/entities/src/EntityItemProperties.cpp | 10 +++++----- libraries/entities/src/EntityItemProperties.h | 13 +++++++++++++ libraries/entities/src/EntityItemPropertiesMacros.h | 3 ++- scripts/system/html/js/entityProperties.js | 2 ++ 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 44e317696c..2b738bc4e7 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -2630,11 +2630,11 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ENTITY_ITEM_MIN_FRICTION, ENTITY_ITEM_MAX_FRICTION); ADD_PROPERTY_TO_MAP(PROP_LIFETIME, Lifetime, lifetime, float); ADD_PROPERTY_TO_MAP(PROP_COLLISIONLESS, Collisionless, collisionless, bool); - ADD_PROPERTY_TO_MAP(PROP_COLLISIONLESS, unused, ignoreForCollisions, unused); // legacy support - ADD_PROPERTY_TO_MAP(PROP_COLLISION_MASK, unused, collisionMask, unused); - ADD_PROPERTY_TO_MAP(PROP_COLLISION_MASK, unused, collidesWith, unused); - ADD_PROPERTY_TO_MAP(PROP_DYNAMIC, unused, collisionsWillMove, unused); // legacy support - ADD_PROPERTY_TO_MAP(PROP_DYNAMIC, unused, dynamic, unused); + ADD_PROPERTY_TO_MAP(PROP_COLLISIONLESS, unused, ignoreForCollisions, bool); // legacy support + ADD_PROPERTY_TO_MAP(PROP_COLLISION_MASK, unused, collisionMask, uint16_t); + ADD_PROPERTY_TO_MAP(PROP_COLLISION_MASK, unused, collidesWith, uint16_t); + ADD_PROPERTY_TO_MAP(PROP_DYNAMIC, unused, collisionsWillMove, bool); // legacy support + ADD_PROPERTY_TO_MAP(PROP_DYNAMIC, unused, dynamic, bool); ADD_PROPERTY_TO_MAP(PROP_COLLISION_SOUND_URL, CollisionSoundURL, collisionSoundURL, QString); ADD_PROPERTY_TO_MAP(PROP_ACTION_DATA, ActionData, actionData, QByteArray); diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index d030f4f2e4..0142f42536 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -14,6 +14,9 @@ #include +#include +#include + #include #include @@ -85,6 +88,16 @@ struct EntityPropertyInfo { QVariant maximum; }; +template +EntityPropertyInfo makePropertyInfo(EntityPropertyList p, typename std::enable_if::value>::type* = 0) { + return EntityPropertyInfo(p); +} + +template +EntityPropertyInfo makePropertyInfo(EntityPropertyList p, typename std::enable_if::value>::type* = 0) { + return EntityPropertyInfo(p, std::numeric_limits::min(), std::numeric_limits::max()); +} + /// A collection of properties of an entity item used in the scripting API. Translates between the actual properties of an /// entity and a JavaScript style hash/QScriptValue storing a set of properties. Used in scripting to set/get the complete /// set of entity item properties via JavaScript hashes/QScriptValues diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h index 7064f3e62e..4c989ef74e 100644 --- a/libraries/entities/src/EntityItemPropertiesMacros.h +++ b/libraries/entities/src/EntityItemPropertiesMacros.h @@ -416,9 +416,10 @@ inline QRect QRect_convertFromScriptValue(const QScriptValue& v, bool& isValid) T _##n; \ static T _static##N; + #define ADD_PROPERTY_TO_MAP(P, N, n, T) \ { \ - EntityPropertyInfo propertyInfo = EntityPropertyInfo(P); \ + EntityPropertyInfo propertyInfo { makePropertyInfo(P) }; \ _propertyInfos[#n] = propertyInfo; \ _enumsToPropertyStrings[P] = #n; \ } diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index ca04f36dc9..3283e6c266 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -616,6 +616,8 @@ const GROUPS = [ decimals: 3, propertyID: "webAlpha", propertyName: "alpha", + min: 0, + max: 1, }, { label: "Max FPS", From e912500cc2ac5dd0892c9ab9f67ec390bb150054 Mon Sep 17 00:00:00 2001 From: Clement Date: Fri, 12 Apr 2019 09:10:12 -0700 Subject: [PATCH 09/56] Fix oven integration in the asset server --- assignment-client/src/assets/AssetServer.cpp | 134 +++++++++++------- assignment-client/src/assets/AssetServer.h | 7 +- .../src/assets/BakeAssetTask.cpp | 34 +++-- assignment-client/src/assets/BakeAssetTask.h | 2 +- 4 files changed, 111 insertions(+), 66 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index c2aec9b058..88f81f639b 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -1326,12 +1326,40 @@ void AssetServer::handleFailedBake(QString originalAssetHash, QString assetPath, } void AssetServer::handleCompletedBake(QString originalAssetHash, QString originalAssetPath, - QString bakedTempOutputDir, QVector bakedFilePaths) { + QString bakedTempOutputDir) { bool errorCompletingBake { false }; QString errorReason; qDebug() << "Completing bake for " << originalAssetHash; + + + QDir outputDir(bakedTempOutputDir); + auto directories = outputDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); + assert(directories.size() == 1); + QString bakedDirectoryPath; + for (const auto& dirName : directories) { + outputDir.cd(dirName); + if (outputDir.exists("baked") && outputDir.exists("original")) { + bakedDirectoryPath = outputDir.filePath("baked"); + } + outputDir.cdUp(); + } + + assert(!bakedDirectoryPath.isEmpty()); + + QDirIterator it(bakedDirectoryPath, QDirIterator::Subdirectories); + QVector bakedFilePaths; + while (it.hasNext()) { + it.next(); + if (it.fileInfo().isFile()) { + bakedFilePaths.push_back(it.filePath()); + } + } + + QDir bakedDirectory(bakedDirectoryPath); + QString redirectTarget; + for (auto& filePath : bakedFilePaths) { // figure out the hash for the contents of this file QFile file(filePath); @@ -1340,62 +1368,59 @@ 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"; break; } + + QCryptographicHash hasher(QCryptographicHash::Sha256); + + if (!hasher.addData(&file)) { + // stop handling this bake, couldn't hash the contents of the file + errorCompletingBake = true; + errorReason = "Failed to finalize bake"; + break; + } + + 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); + qDebug() << "Relative file path is: " << relativeFilePath; + + 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"))) { + 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 finalize bake"; + break; + } + + qDebug() << "Added" << bakeMapping << "for bake file" << bakedFileHash << "from bake of" << originalAssetHash; } for (auto& filePath : bakedFilePaths) { @@ -1411,9 +1436,12 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina auto type = assetTypeForFilename(originalAssetPath); auto currentTypeVersion = currentBakeVersionForAssetType(type); + assert(!redirectTarget.isEmpty()); + AssetMeta meta; meta.bakeVersion = currentTypeVersion; meta.failedLastBake = errorCompletingBake; + meta.redirectTarget = redirectTarget; if (errorCompletingBake) { qWarning() << "Could not complete bake for" << originalAssetHash; @@ -1435,6 +1463,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 AssetServer::readMetaFile(AssetUtils::AssetHash hash) { auto metaFilePath = AssetUtils::HIDDEN_BAKED_CONTENT_FOLDER + hash + "/" + "meta.json"; @@ -1461,6 +1490,7 @@ std::pair 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 +1500,7 @@ std::pair 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 +1519,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); diff --git a/assignment-client/src/assets/AssetServer.h b/assignment-client/src/assets/AssetServer.h index b3d0f18a8f..fe84df5141 100644 --- a/assignment-client/src/assets/AssetServer.h +++ b/assignment-client/src/assets/AssetServer.h @@ -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 bakedFilePaths); + void handleCompletedBake(QString originalAssetHash, QString assetPath, QString bakedTempOutputDir); void handleFailedBake(QString originalAssetHash, QString assetPath, QString errors); void handleAbortedBake(QString originalAssetHash, QString assetPath); diff --git a/assignment-client/src/assets/BakeAssetTask.cpp b/assignment-client/src/assets/BakeAssetTask.cpp index ecb4ede5d8..7ca87fe4e3 100644 --- a/assignment-client/src/assets/BakeAssetTask.cpp +++ b/assignment-client/src/assets/BakeAssetTask.cpp @@ -57,12 +57,19 @@ void BakeAssetTask::run() { return; } + // Make a new temporary directory for the Oven to work in QString tempOutputDir = PathUtils::generateTemporaryDir(); + + // 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; + QFile::copy(_filePath, tempAssetPath); + 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,7 +79,7 @@ void BakeAssetTask::run() { QEventLoop loop; connect(_ovenProcess.get(), static_cast(&QProcess::finished), - this, [&loop, this, tempOutputDir](int exitCode, QProcess::ExitStatus exitStatus) { + this, [&loop, this, tempOutputDir, tempAssetPath](int exitCode, QProcess::ExitStatus exitStatus) { qDebug() << "Baking process finished: " << exitCode << exitStatus; if (exitStatus == QProcess::CrashExit) { @@ -82,18 +89,20 @@ void BakeAssetTask::run() { QString errors = "Fatal error occurred while baking"; emit bakeFailed(_assetHash, _assetPath, errors); } - } else if (exitCode == OVEN_STATUS_CODE_SUCCESS) { - QDir outputDir = tempOutputDir; - auto files = outputDir.entryInfoList(QDir::Files); - QVector outputFiles; - for (auto& file : files) { - outputFiles.push_back(file.absoluteFilePath()); + if (!QDir(tempOutputDir).rmdir(".")) { + qWarning() << "Failed to remove temporary directory:" << tempOutputDir; } + } else if (exitCode == OVEN_STATUS_CODE_SUCCESS) { + // Remove temp copy of the original asset + QFile::remove(tempAssetPath); - emit bakeComplete(_assetHash, _assetPath, tempOutputDir, outputFiles); + emit bakeComplete(_assetHash, _assetPath, tempOutputDir); } else if (exitStatus == QProcess::NormalExit && exitCode == OVEN_STATUS_CODE_ABORT) { _wasAborted.store(true); emit bakeAborted(_assetHash, _assetPath); + if (!QDir(tempOutputDir).rmdir(".")) { + qWarning() << "Failed to remove temporary directory:" << tempOutputDir; + } } else { QString errors; if (exitCode == OVEN_STATUS_CODE_FAIL) { @@ -108,6 +117,9 @@ void BakeAssetTask::run() { } } emit bakeFailed(_assetHash, _assetPath, errors); + if (!QDir(tempOutputDir).rmdir(".")) { + qWarning() << "Failed to remove temporary directory:" << tempOutputDir; + } } loop.quit(); @@ -115,9 +127,13 @@ void BakeAssetTask::run() { qDebug() << "Starting oven for " << _assetPath; _ovenProcess->start(path, args, QIODevice::ReadOnly); + qDebug() << "Running:" << path << args; if (!_ovenProcess->waitForStarted(-1)) { QString errors = "Oven process failed to start"; emit bakeFailed(_assetHash, _assetPath, errors); + if (!QDir(tempOutputDir).rmdir(".")) { + qWarning() << "Failed to remove temporary directory:" << tempOutputDir; + } return; } diff --git a/assignment-client/src/assets/BakeAssetTask.h b/assignment-client/src/assets/BakeAssetTask.h index 24b070d08a..2d50a26bc1 100644 --- a/assignment-client/src/assets/BakeAssetTask.h +++ b/assignment-client/src/assets/BakeAssetTask.h @@ -37,7 +37,7 @@ public slots: void abort(); signals: - void bakeComplete(QString assetHash, QString assetPath, QString tempOutputDir, QVector outputFiles); + void bakeComplete(QString assetHash, QString assetPath, QString tempOutputDir); void bakeFailed(QString assetHash, QString assetPath, QString errors); void bakeAborted(QString assetHash, QString assetPath); From dc41fea46e66e4764d748825a8892f4f0f3d4ae0 Mon Sep 17 00:00:00 2001 From: Clement Date: Fri, 12 Apr 2019 11:26:40 -0700 Subject: [PATCH 10/56] Update Asset Server redirect and baking conditions --- assignment-client/src/assets/AssetServer.cpp | 132 +++++++++---------- 1 file changed, 60 insertions(+), 72 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 88f81f639b..4805d2fe4c 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -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,22 @@ std::pair 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 { - return { AssetUtils::Irrelevant, "" }; + QString bakedFilename = bakedFilenameForAssetType(type); + auto bakedPath = getBakeMapping(hash, bakedFilename); + if (loaded && !meta.redirectTarget.isEmpty()) { + bakedPath = meta.redirectTarget; } - auto bakedPath = AssetUtils::HIDDEN_BAKED_CONTENT_FOLDER + hash + "/" + bakedFilename; auto jt = _fileMappings.find(bakedPath); if (jt != _fileMappings.end()) { if (jt->second == hash) { @@ -168,14 +168,8 @@ std::pair 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 +221,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 +240,6 @@ bool AssetServer::needsToBeBaked(const AssetUtils::AssetPath& path, const AssetU return false; } - bool loaded; - AssetMeta meta; - std::tie(loaded, meta) = readMetaFile(assetHash); - if (type == BakedAssetType::Texture && !loaded) { return false; } @@ -633,36 +631,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 +679,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 +1285,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 << ")"; @@ -1553,10 +1533,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); From 77d8a3691412733f10fa9f0be87752abc518581a Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Thu, 11 Apr 2019 16:41:31 -0700 Subject: [PATCH 11/56] attempt to handle atp redirects from .fbx to .baked.fst --- .../src/material-networking/TextureCache.cpp | 6 +- .../src/model-networking/ModelCache.cpp | 315 ++++++++---------- .../src/model-networking/ModelCache.h | 43 ++- 3 files changed, 171 insertions(+), 193 deletions(-) diff --git a/libraries/material-networking/src/material-networking/TextureCache.cpp b/libraries/material-networking/src/material-networking/TextureCache.cpp index 6af59930fa..8ae5b12286 100644 --- a/libraries/material-networking/src/material-networking/TextureCache.cpp +++ b/libraries/material-networking/src/material-networking/TextureCache.cpp @@ -630,11 +630,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; diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 23b365dd03..fe242335e7 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -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(); - GeometryExtra extra { GeometryMappingPair(base, _mapping), _textureBaseUrl, false }; - - // Get the raw GeometryResource - _geometryResource = modelCache->getResource(url, QUrl(), &extra, std::hash()(extra)).staticCast(); - // 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, const QUrl& url, const GeometryMappingPair& mapping, @@ -300,47 +191,137 @@ 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 (_activeUrl.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 GeometryMappingPair& mapping); + 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(); + GeometryExtra extra { GeometryMappingPair(base, _mapping), _textureBaseURL, false }; + + // Get the raw GeometryResource + _geometryResource = modelCache->getResource(url, QUrl(), &extra, std::hash()(extra)).staticCast(); + // 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())); + } +} + +bool GeometryResource::handleFailedRequest(ResourceRequest::Result result) { + if (_shouldFailOnRedirect && result == ResourceRequest::Result::RedirectFail) { + auto newPath = _request->getRelativePathUrl(); + if (newPath.fileName().toLower().endsWith(".fst")) { + _activeUrl = newPath; + _shouldFailOnRedirect = false; + makeRequest(); + return true; + } + } + return Resource::handleFailedRequest(result); +} + +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(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 GeometryMappingPair& mapping) { +void GeometryResource::setGeometryDefinition(HFMModel::Pointer hfmModel, const GeometryMappingPair& mapping) { // Do processing on the model baker::Baker modelBaker(hfmModel, mapping.second, mapping.first); modelBaker.run(); @@ -353,7 +334,7 @@ void GeometryDefinitionResource::setGeometryDefinition(HFMModel::Pointer hfmMode QHash materialIDAtlas; for (const HFMMaterial& material : _hfmModel->materials) { materialIDAtlas[material.materialID] = _materials.size(); - _materials.push_back(std::make_shared(material, _textureBaseUrl)); + _materials.push_back(std::make_shared(material, _textureBaseURL)); } std::shared_ptr meshes = std::make_shared(); @@ -376,6 +357,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(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); @@ -388,26 +386,14 @@ ModelCache::ModelCache() { } QSharedPointer 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::deleter); + return QSharedPointer(new GeometryResource(url, _modelLoader), &GeometryResource::deleter); } QSharedPointer ModelCache::createResourceCopy(const QSharedPointer& resource) { - if (resource->getURL().path().toLower().endsWith(".fst")) { - return QSharedPointer(new GeometryMappingResource(*resource.staticCast()), &Resource::deleter); - } else { - return QSharedPointer(new GeometryDefinitionResource(*resource.staticCast()), &Resource::deleter); - } + return QSharedPointer(new GeometryResource(*resource.staticCast()), &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)).staticCast(); @@ -513,23 +499,6 @@ const std::shared_ptr 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(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); diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index ca1ceaff16..93a3d811b3 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -24,8 +24,6 @@ class MeshPart; -class GeometryMappingResource; - using GeometryMappingPair = std::pair; 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 _hfmModel; MaterialMapping _materialMapping; @@ -80,23 +76,30 @@ private: /// A geometry loaded from the network. class GeometryResource : public Resource, public Geometry { + Q_OBJECT public: using Pointer = QSharedPointer; - 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) { _shouldFailOnRedirect = !url.fileName().toLower().endsWith(".fst"); } + 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; + bool handleFailedRequest(ResourceRequest::Result result) 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 GeometryMappingPair& mapping); // 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 +107,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 +169,7 @@ public: const QUrl& textureBaseUrl = QUrl()); protected: - friend class GeometryMappingResource; + friend class GeometryResource; virtual QSharedPointer createResource(const QUrl& url) override; QSharedPointer createResourceCopy(const QSharedPointer& resource) override; From 67f35e70745e9ba595829b1838daf3fb13b156de Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 12 Apr 2019 14:47:48 -0700 Subject: [PATCH 12/56] fix material targets when re-opening properties to same entity --- scripts/system/html/js/entityProperties.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 3f2ee4e699..630c9396ff 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -3505,6 +3505,7 @@ function loaded() { deleteJSONMaterialEditor(); } } + lastEntityID = null; resetProperties(); showGroupsForType("None"); From 794cedaa6070c87dc7fd45d11af959384d72974d Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 12 Apr 2019 15:49:34 -0700 Subject: [PATCH 13/56] Patch interface redirect code --- .../src/model-networking/ModelCache.cpp | 15 +-------------- .../src/model-networking/ModelCache.h | 3 +-- libraries/networking/src/ResourceCache.cpp | 1 + 3 files changed, 3 insertions(+), 16 deletions(-) diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index fe242335e7..a837599b57 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -210,7 +210,7 @@ GeometryResource::GeometryResource(const GeometryResource& other) : } void GeometryResource::downloadFinished(const QByteArray& data) { - if (_activeUrl.fileName().toLower().endsWith(".fst")) { + if (_effectiveBaseURL.fileName().toLower().endsWith(".fst")) { PROFILE_ASYNC_BEGIN(resource_parse_geometry, "GeometryResource::downloadFinished", _url.toString(), { { "url", _url.toString() } }); // store parsed contents of FST file @@ -283,19 +283,6 @@ void GeometryResource::downloadFinished(const QByteArray& data) { } } -bool GeometryResource::handleFailedRequest(ResourceRequest::Result result) { - if (_shouldFailOnRedirect && result == ResourceRequest::Result::RedirectFail) { - auto newPath = _request->getRelativePathUrl(); - if (newPath.fileName().toLower().endsWith(".fst")) { - _activeUrl = newPath; - _shouldFailOnRedirect = false; - makeRequest(); - return true; - } - } - return Resource::handleFailedRequest(result); -} - void GeometryResource::onGeometryMappingLoaded(bool success) { if (success && _geometryResource) { _hfmModel = _geometryResource->_hfmModel; diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index 93a3d811b3..b14dcd7199 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -80,7 +80,7 @@ class GeometryResource : public Resource, public Geometry { public: using Pointer = QSharedPointer; - GeometryResource(const QUrl& url, const ModelLoader& modelLoader) : Resource(url), _modelLoader(modelLoader) { _shouldFailOnRedirect = !url.fileName().toLower().endsWith(".fst"); } + GeometryResource(const QUrl& url, const ModelLoader& modelLoader) : Resource(url), _modelLoader(modelLoader) {} GeometryResource(const GeometryResource& other); QString getType() const override { return "Geometry"; } @@ -88,7 +88,6 @@ public: virtual void deleter() override; virtual void downloadFinished(const QByteArray& data) override; - bool handleFailedRequest(ResourceRequest::Result result) override; void setExtra(void* extra) override; virtual bool areTexturesLoaded() const override { return isLoaded() && Geometry::areTexturesLoaded(); } diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index d5abb27a27..746c28a306 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -577,6 +577,7 @@ Resource::Resource(const Resource& other) : Resource::Resource(const QUrl& url) : _url(url), _activeUrl(url), + _effectiveBaseURL(url), _requestID(++requestID) { init(); } From d19b1c18a05c29612961b90f74474df71e95b3ae Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Wed, 27 Mar 2019 15:31:22 -0700 Subject: [PATCH 14/56] Fix MaterialBaker not including names in baked multi-materials --- libraries/graphics/src/graphics/Material.h | 1 + .../src/material-networking/MaterialCache.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/libraries/graphics/src/graphics/Material.h b/libraries/graphics/src/graphics/Material.h index d24e906f98..80b247bed0 100755 --- a/libraries/graphics/src/graphics/Material.h +++ b/libraries/graphics/src/graphics/Material.h @@ -318,6 +318,7 @@ public: void setTextureTransforms(const Transform& transform, MaterialMappingMode mode, bool repeat); const std::string& getName() const { return _name; } + void setName(const std::string& name) { _name = name; } const std::string& getModel() const { return _model; } void setModel(const std::string& model) { _model = model; } diff --git a/libraries/material-networking/src/material-networking/MaterialCache.cpp b/libraries/material-networking/src/material-networking/MaterialCache.cpp index 9eef89d5c9..5a5f4ab54b 100644 --- a/libraries/material-networking/src/material-networking/MaterialCache.cpp +++ b/libraries/material-networking/src/material-networking/MaterialCache.cpp @@ -184,6 +184,7 @@ std::pair> NetworkMaterialResource auto nameJSON = materialJSON.value(key); if (nameJSON.isString()) { name = nameJSON.toString().toStdString(); + material->setName(name); } } else if (key == "model") { auto modelJSON = materialJSON.value(key); From 8e3b76d2e35fadb75b55f28050f9c2c35357b387 Mon Sep 17 00:00:00 2001 From: danteruiz Date: Fri, 12 Apr 2019 17:11:35 -0700 Subject: [PATCH 15/56] fixing bubble --- interface/src/avatar/AvatarManager.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 575d87dfb7..6c0eb4a371 100755 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -498,8 +498,10 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar // on the creation of entities for that avatar instance and the deletion of entities for this instance avatar->removeAvatarEntitiesFromTree(); if (removalReason != KillAvatarReason::AvatarDisconnected) { - emit AvatarInputs::getInstance()->avatarEnteredIgnoreRadius(avatar->getSessionUUID()); - emit DependencyManager::get()->enteredIgnoreRadius(); + if (removalReason == KillAvatarReason::TheirAvatarEnteredYourBubble) { + emit AvatarInputs::getInstance()->avatarEnteredIgnoreRadius(avatar->getSessionUUID()); + emit DependencyManager::get()->enteredIgnoreRadius(); + } workload::Transaction workloadTransaction; workloadTransaction.remove(avatar->getSpaceIndex()); From c5b7bdc802096d9264f731cddd422a2e7f339fc1 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 12 Apr 2019 17:51:03 -0700 Subject: [PATCH 16/56] Fix unix warning --- libraries/networking/src/ResourceCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 746c28a306..44d3d1ee4d 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -576,8 +576,8 @@ Resource::Resource(const Resource& other) : Resource::Resource(const QUrl& url) : _url(url), - _activeUrl(url), _effectiveBaseURL(url), + _activeUrl(url), _requestID(++requestID) { init(); } From 0a62bf3e487e96046c424697e5395560b7337951 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Wed, 27 Mar 2019 15:31:22 -0700 Subject: [PATCH 17/56] Fix MaterialBaker not including names in baked multi-materials --- libraries/graphics/src/graphics/Material.h | 1 + .../src/material-networking/MaterialCache.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/libraries/graphics/src/graphics/Material.h b/libraries/graphics/src/graphics/Material.h index d24e906f98..80b247bed0 100755 --- a/libraries/graphics/src/graphics/Material.h +++ b/libraries/graphics/src/graphics/Material.h @@ -318,6 +318,7 @@ public: void setTextureTransforms(const Transform& transform, MaterialMappingMode mode, bool repeat); const std::string& getName() const { return _name; } + void setName(const std::string& name) { _name = name; } const std::string& getModel() const { return _model; } void setModel(const std::string& model) { _model = model; } diff --git a/libraries/material-networking/src/material-networking/MaterialCache.cpp b/libraries/material-networking/src/material-networking/MaterialCache.cpp index 9eef89d5c9..5a5f4ab54b 100644 --- a/libraries/material-networking/src/material-networking/MaterialCache.cpp +++ b/libraries/material-networking/src/material-networking/MaterialCache.cpp @@ -184,6 +184,7 @@ std::pair> NetworkMaterialResource auto nameJSON = materialJSON.value(key); if (nameJSON.isString()) { name = nameJSON.toString().toStdString(); + material->setName(name); } } else if (key == "model") { auto modelJSON = materialJSON.value(key); From 0940931068d31b165eb1114f1a9112e62654778c Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Mon, 15 Apr 2019 10:37:28 -0700 Subject: [PATCH 18/56] Disable baking of embedded material textures in material entities --- tools/oven/src/DomainBaker.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/oven/src/DomainBaker.cpp b/tools/oven/src/DomainBaker.cpp index 8a4489fcac..50a3d212c0 100644 --- a/tools/oven/src/DomainBaker.cpp +++ b/tools/oven/src/DomainBaker.cpp @@ -412,9 +412,13 @@ void DomainBaker::enumerateEntities() { if (entity.contains(MATERIAL_URL_KEY)) { addMaterialBaker(MATERIAL_URL_KEY, entity[MATERIAL_URL_KEY].toString(), true, *it); } + // FIXME: Disabled for now because relative texture URLs are not supported for embedded materials in material entities + // We need to make texture URLs absolute in this particular case only, keeping in mind that FSTBaker also uses embedded materials + /* if (entity.contains(MATERIAL_DATA_KEY)) { addMaterialBaker(MATERIAL_DATA_KEY, entity[MATERIAL_DATA_KEY].toString(), false, *it); } + */ } } From ea8bc72bbf25762ae4f274c9cfc06586df57c46e Mon Sep 17 00:00:00 2001 From: Clement Date: Mon, 15 Apr 2019 19:08:44 -0700 Subject: [PATCH 19/56] Fix Asset Server reporting bad status for textures --- assignment-client/src/assets/AssetServer.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 4805d2fe4c..297a622c25 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -146,7 +146,6 @@ std::pair AssetServer::getAssetStatus(const A } BakedAssetType type = assetTypeForFilename(path); - if (type == BakedAssetType::Undefined) { return { AssetUtils::Irrelevant, "" }; } @@ -155,6 +154,12 @@ std::pair AssetServer::getAssetStatus(const A AssetMeta meta; std::tie(loaded, meta) = readMetaFile(hash); + // 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, "" }; + } + QString bakedFilename = bakedFilenameForAssetType(type); auto bakedPath = getBakeMapping(hash, bakedFilename); if (loaded && !meta.redirectTarget.isEmpty()) { @@ -240,6 +245,8 @@ bool AssetServer::needsToBeBaked(const AssetUtils::AssetPath& path, const AssetU return false; } + // 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; } From 1b1b651b22a9d63327a139a9cc7d7f03492e0bc1 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 16 Apr 2019 15:16:26 -0700 Subject: [PATCH 20/56] fix oven crash (cherry picked from commit 2d5cfbf4f50b8a65713f23498124ee952cf61923) --- libraries/baking/src/FBXBaker.cpp | 7 ++++++- libraries/render-utils/src/Model.cpp | 8 ++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index b7eb56c921..01897ee5e9 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -104,13 +104,15 @@ void FBXBaker::rewriteAndBakeSceneModels(const QVector& meshes, const int meshIndex = 0; for (FBXNode& rootChild : _rootNode.children) { if (rootChild.name == "Objects") { - for (auto object = rootChild.children.begin(); object != rootChild.children.end(); object++) { + auto object = rootChild.children.begin(); + while (object != rootChild.children.end()) { if (object->name == "Geometry") { if (object->properties.at(2) == "Mesh") { int meshNum = meshIndexToRuntimeOrder[meshIndex]; replaceMeshNodeWithDraco(*object, dracoMeshes[meshNum], dracoMaterialLists[meshNum]); meshIndex++; } + object++; } else if (object->name == "Model") { for (FBXNode& modelChild : object->children) { if (modelChild.name == "Properties60" || modelChild.name == "Properties70") { @@ -136,9 +138,12 @@ void FBXBaker::rewriteAndBakeSceneModels(const QVector& meshes, const meshIndex++; } } + object++; } else if (object->name == "Texture" || object->name == "Video") { // this is an embedded texture, we need to remove it from the FBX object = rootChild.children.erase(object); + } else { + object++; } if (hasErrors()) { diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index fbe0af11f2..23f385ba3e 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1540,9 +1540,13 @@ void Model::applyMaterialMapping() { auto& materialMapping = getMaterialMapping(); for (auto& mapping : materialMapping) { - std::set shapeIDs = getMeshIDsFromMaterialID(QString(mapping.first.c_str())); auto networkMaterialResource = mapping.second; - if (!networkMaterialResource || shapeIDs.size() == 0) { + if (!networkMaterialResource) { + continue; + } + + std::set shapeIDs = getMeshIDsFromMaterialID(QString(mapping.first.c_str())); + if (shapeIDs.size() == 0) { continue; } From 461c70301a82e48eb9134de19629d25f9f30ee0e Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Tue, 16 Apr 2019 15:48:42 -0700 Subject: [PATCH 21/56] Fix Interface not loading some baked models correctly --- libraries/fbx/src/FBXSerializer_Mesh.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/fbx/src/FBXSerializer_Mesh.cpp b/libraries/fbx/src/FBXSerializer_Mesh.cpp index c34b4678c7..3da08ade7c 100644 --- a/libraries/fbx/src/FBXSerializer_Mesh.cpp +++ b/libraries/fbx/src/FBXSerializer_Mesh.cpp @@ -358,7 +358,7 @@ ExtractedMesh FBXSerializer::extractMesh(const FBXNode& object, unsigned int& me std::vector dracoMaterialList; for (const auto& dracoChild : child.children) { if (dracoChild.name == "FBXDracoMeshVersion") { - if (!dracoChild.children.isEmpty()) { + if (!dracoChild.properties.isEmpty()) { dracoMeshNodeVersion = dracoChild.properties[0].toUInt(); } } else if (dracoChild.name == "MaterialList") { From 03b28b3dfaabbcfe18724b4f62d980a0f9f09e13 Mon Sep 17 00:00:00 2001 From: Clement Date: Tue, 16 Apr 2019 19:40:59 -0700 Subject: [PATCH 22/56] Add error reporting + Make temp dir erase safer --- assignment-client/src/assets/AssetServer.cpp | 91 ++++++++++++------- .../src/assets/BakeAssetTask.cpp | 54 +++++------ libraries/shared/src/PathUtils.cpp | 24 +++++ libraries/shared/src/PathUtils.h | 1 + 4 files changed, 102 insertions(+), 68 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 297a622c25..502cf15aa2 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -1314,27 +1314,57 @@ void AssetServer::handleFailedBake(QString originalAssetHash, QString assetPath, void AssetServer::handleCompletedBake(QString originalAssetHash, QString originalAssetPath, 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); - assert(directories.size() == 1); 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"; - assert(!bakedDirectoryPath.isEmpty()); + // 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 bakedFilePaths; while (it.hasNext()) { @@ -1343,9 +1373,17 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina 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); - QString redirectTarget; for (auto& filePath : bakedFilePaths) { // figure out the hash for the contents of this file @@ -1359,7 +1397,7 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina 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; } @@ -1368,7 +1406,7 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina if (!hasher.addData(&file)) { // stop handling this bake, couldn't hash the contents of the file errorCompletingBake = true; - errorReason = "Failed to finalize bake"; + errorReason = "Could not hash data for " + file.fileName(); break; } @@ -1388,13 +1426,15 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina // setup the mapping for this bake file auto relativeFilePath = bakedDirectory.relativeFilePath(filePath); - qDebug() << "Relative file path is: " << relativeFilePath; 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; } @@ -1403,41 +1443,22 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina 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"; + errorReason = "Failed to set mapping for baked file " + file.fileName(); break; } qDebug() << "Added" << bakeMapping << "for bake file" << bakedFileHash << "from bake of" << originalAssetHash; } - for (auto& filePath : bakedFilePaths) { - QFile file(filePath); - if (!file.remove()) { - qWarning() << "Failed to remove temporary file:" << filePath; - } - } - if (!QDir(bakedTempOutputDir).rmdir(".")) { - qWarning() << "Failed to remove temporary directory:" << bakedTempOutputDir; + + if (redirectTarget.isEmpty()) { + errorCompletingBake = true; + errorReason = "Could not find root file for baked output"; } - auto type = assetTypeForFilename(originalAssetPath); - auto currentTypeVersion = currentBakeVersionForAssetType(type); - - assert(!redirectTarget.isEmpty()); - - 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); + // Cleanup temporary output directory + PathUtils::deleteMyTemporaryDir(outputDirName); + reportCompletion(errorCompletingBake, errorReason, redirectTarget); } void AssetServer::handleAbortedBake(QString originalAssetHash, QString assetPath) { diff --git a/assignment-client/src/assets/BakeAssetTask.cpp b/assignment-client/src/assets/BakeAssetTask.cpp index 7ca87fe4e3..99fff4f180 100644 --- a/assignment-client/src/assets/BakeAssetTask.cpp +++ b/assignment-client/src/assets/BakeAssetTask.cpp @@ -36,21 +36,6 @@ BakeAssetTask::BakeAssetTask(const AssetUtils::AssetHash& assetHash, const Asset }); } -void cleanupTempFiles(QString tempOutputDir, std::vector 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"; @@ -59,11 +44,24 @@ void BakeAssetTask::run() { // 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; - QFile::copy(_filePath, tempAssetPath); + auto sucess = QFile::copy(_filePath, tempAssetPath); + if (!sucess) { + 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"; @@ -79,30 +77,23 @@ void BakeAssetTask::run() { QEventLoop loop; connect(_ovenProcess.get(), static_cast(&QProcess::finished), - this, [&loop, this, tempOutputDir, tempAssetPath](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 { QString errors = "Fatal error occurred while baking"; emit bakeFailed(_assetHash, _assetPath, errors); } - if (!QDir(tempOutputDir).rmdir(".")) { - qWarning() << "Failed to remove temporary directory:" << tempOutputDir; - } } else if (exitCode == OVEN_STATUS_CODE_SUCCESS) { - // Remove temp copy of the original asset - QFile::remove(tempAssetPath); - 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); - if (!QDir(tempOutputDir).rmdir(".")) { - qWarning() << "Failed to remove temporary directory:" << tempOutputDir; - } } else { QString errors; if (exitCode == OVEN_STATUS_CODE_FAIL) { @@ -116,10 +107,8 @@ void BakeAssetTask::run() { errors = "Unknown error occurred while baking"; } } + PathUtils::deleteMyTemporaryDir(tempOutputDirName); emit bakeFailed(_assetHash, _assetPath, errors); - if (!QDir(tempOutputDir).rmdir(".")) { - qWarning() << "Failed to remove temporary directory:" << tempOutputDir; - } } loop.quit(); @@ -128,12 +117,11 @@ void BakeAssetTask::run() { qDebug() << "Starting oven for " << _assetPath; _ovenProcess->start(path, args, QIODevice::ReadOnly); qDebug() << "Running:" << path << args; - if (!_ovenProcess->waitForStarted(-1)) { + if (!_ovenProcess->waitForStarted()) { + PathUtils::deleteMyTemporaryDir(tempOutputDirName); + QString errors = "Oven process failed to start"; emit bakeFailed(_assetHash, _assetPath, errors); - if (!QDir(tempOutputDir).rmdir(".")) { - qWarning() << "Failed to remove temporary directory:" << tempOutputDir; - } return; } diff --git a/libraries/shared/src/PathUtils.cpp b/libraries/shared/src/PathUtils.cpp index 60b426e46d..be60533406 100644 --- a/libraries/shared/src/PathUtils.cpp +++ b/libraries/shared/src/PathUtils.cpp @@ -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) + "\\-(?\\d+)\\-(?\\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()) { diff --git a/libraries/shared/src/PathUtils.h b/libraries/shared/src/PathUtils.h index 0096cb6c90..cac5c58ca4 100644 --- a/libraries/shared/src/PathUtils.h +++ b/libraries/shared/src/PathUtils.h @@ -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); From 7f42ecaa746d781617094b53724e59717f0a55e9 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Wed, 17 Apr 2019 11:33:53 -0700 Subject: [PATCH 23/56] Fix another Interface issue loading baked models --- libraries/fbx/src/FBXSerializer_Mesh.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/fbx/src/FBXSerializer_Mesh.cpp b/libraries/fbx/src/FBXSerializer_Mesh.cpp index 3da08ade7c..22e6a0ddb2 100644 --- a/libraries/fbx/src/FBXSerializer_Mesh.cpp +++ b/libraries/fbx/src/FBXSerializer_Mesh.cpp @@ -492,7 +492,7 @@ ExtractedMesh FBXSerializer::extractMesh(const FBXNode& object, unsigned int& me // Figure out what material this part is if (dracoMeshNodeVersion >= 2) { // Define the materialID now - if (dracoMaterialList.size() - 1 <= materialID) { + if (materialID <= dracoMaterialList.size() - 1) { part.materialID = dracoMaterialList[materialID]; } } else { From f371b7b2d9a408b9bcf86c5563dfd8636d1b0f27 Mon Sep 17 00:00:00 2001 From: Clement Date: Wed, 17 Apr 2019 12:57:15 -0700 Subject: [PATCH 24/56] Fix typo --- assignment-client/src/assets/BakeAssetTask.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/assets/BakeAssetTask.cpp b/assignment-client/src/assets/BakeAssetTask.cpp index 99fff4f180..7c845f9b38 100644 --- a/assignment-client/src/assets/BakeAssetTask.cpp +++ b/assignment-client/src/assets/BakeAssetTask.cpp @@ -55,8 +55,8 @@ void BakeAssetTask::run() { // 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 sucess = QFile::copy(_filePath, tempAssetPath); - if (!sucess) { + 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); From c7f270a9ba2aec62b4a54d83c2fa3f2f75e152cb Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Wed, 17 Apr 2019 13:47:39 -0700 Subject: [PATCH 25/56] Combine PR15375 & PR15376 for RC82 branch --- libraries/networking/src/LimitedNodeList.cpp | 6 ++++-- libraries/networking/src/NetworkPeer.h | 2 +- libraries/networking/src/NodeList.cpp | 6 +++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 18a180ad79..82f3459c15 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -588,6 +588,8 @@ void LimitedNodeList::eraseAllNodes() { foreach(const SharedNodePointer& killedNode, killedNodes) { handleNodeKill(killedNode); } + + _delayedNodeAdds.clear(); } void LimitedNodeList::reset() { @@ -755,7 +757,7 @@ void LimitedNodeList::delayNodeAdd(NewNodeInfo info) { } void LimitedNodeList::removeDelayedAdd(QUuid nodeUUID) { - auto it = std::find_if(_delayedNodeAdds.begin(), _delayedNodeAdds.end(), [&](auto info) { + auto it = std::find_if(_delayedNodeAdds.begin(), _delayedNodeAdds.end(), [&](const auto& info) { return info.uuid == nodeUUID; }); if (it != _delayedNodeAdds.end()) { @@ -764,7 +766,7 @@ void LimitedNodeList::removeDelayedAdd(QUuid nodeUUID) { } bool LimitedNodeList::isDelayedNode(QUuid nodeUUID) { - auto it = std::find_if(_delayedNodeAdds.begin(), _delayedNodeAdds.end(), [&](auto info) { + auto it = std::find_if(_delayedNodeAdds.begin(), _delayedNodeAdds.end(), [&](const auto& info) { return info.uuid == nodeUUID; }); return it != _delayedNodeAdds.end(); diff --git a/libraries/networking/src/NetworkPeer.h b/libraries/networking/src/NetworkPeer.h index b75d2f8b86..43fbc753eb 100644 --- a/libraries/networking/src/NetworkPeer.h +++ b/libraries/networking/src/NetworkPeer.h @@ -26,7 +26,7 @@ const quint16 ICE_SERVER_DEFAULT_PORT = 7337; const int ICE_HEARBEAT_INTERVAL_MSECS = 2 * 1000; const int MAX_ICE_CONNECTION_ATTEMPTS = 5; -const int UDP_PUNCH_PING_INTERVAL_MS = 25; +const int UDP_PUNCH_PING_INTERVAL_MS = 250; class NetworkPeer : public QObject { Q_OBJECT diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 0021a594bc..0a4c63d712 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -752,11 +752,11 @@ void NodeList::pingPunchForInactiveNode(const SharedNodePointer& node) { flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendAudioPing); } - // every second we're trying to ping this node and we're not getting anywhere - debug that out - const int NUM_DEBUG_CONNECTION_ATTEMPTS = 1000 / (UDP_PUNCH_PING_INTERVAL_MS); + // every two seconds we're trying to ping this node and we're not getting anywhere - debug that out + const int NUM_DEBUG_CONNECTION_ATTEMPTS = 2000 / (UDP_PUNCH_PING_INTERVAL_MS); if (node->getConnectionAttempts() > 0 && node->getConnectionAttempts() % NUM_DEBUG_CONNECTION_ATTEMPTS == 0) { - qCDebug(networking) << "No response to UDP hole punch pings for node" << node->getUUID() << "in last second."; + qCDebug(networking) << "No response to UDP hole punch pings for node" << node->getUUID() << "in last 2 s."; } auto nodeID = node->getUUID(); From ca15d3d3a8ea371dab65487fff3f0b8cf90bb93b Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Wed, 17 Apr 2019 15:11:52 -0700 Subject: [PATCH 26/56] Fix baked FSTs unable to be rebaked by creating an FST in the original output folder --- libraries/baking/src/ModelBaker.cpp | 36 +++++++++++++++++++++++++++++ libraries/baking/src/ModelBaker.h | 1 + libraries/fbx/src/FSTReader.h | 1 + 3 files changed, 38 insertions(+) diff --git a/libraries/baking/src/ModelBaker.cpp b/libraries/baking/src/ModelBaker.cpp index e58ec00afa..00f7a38627 100644 --- a/libraries/baking/src/ModelBaker.cpp +++ b/libraries/baking/src/ModelBaker.cpp @@ -167,6 +167,10 @@ void ModelBaker::saveSourceModel() { connect(networkReply, &QNetworkReply::finished, this, &ModelBaker::handleModelNetworkReply); } + + if (_mappingURL.isEmpty()) { + outputUnbakedFST(); + } } void ModelBaker::handleModelNetworkReply() { @@ -313,6 +317,37 @@ void ModelBaker::handleFinishedMaterialBaker() { outputBakedFST(); } +void ModelBaker::outputUnbakedFST() { + // Output an unbaked FST file in the original output folder to make it easier for FSTBaker to rebake this model + // TODO: Consider a more robust method that does not depend on FSTBaker navigating to a hardcoded relative path + QString outputFSTFilename = _modelURL.fileName(); + auto extensionStart = outputFSTFilename.indexOf("."); + if (extensionStart != -1) { + outputFSTFilename.resize(extensionStart); + } + outputFSTFilename += FST_EXTENSION; + QString outputFSTURL = _originalOutputDir + "/" + outputFSTFilename; + + hifi::VariantHash outputMapping; + outputMapping[FST_VERSION_FIELD] = FST_VERSION; + outputMapping[FILENAME_FIELD] = _modelURL.fileName(); + outputMapping[COMMENT_FIELD] = "This FST file was generated by Oven for use during rebaking. It is not part of the original model. This file's existence is subject to change."; + hifi::ByteArray fstOut = FSTReader::writeMapping(outputMapping); + + QFile fstOutputFile { outputFSTURL }; + if (fstOutputFile.exists()) { + handleWarning("The file '" + outputFSTURL + "' already exists. Should that be baked instead of '" + _modelURL.toString() + "'?"); + return; + } + if (!fstOutputFile.open(QIODevice::WriteOnly)) { + handleWarning("Failed to open file '" + outputFSTURL + "' for writing. Rebaking may fail on the associated model."); + return; + } + if (fstOutputFile.write(fstOut) == -1) { + handleWarning("Failed to write to file '" + outputFSTURL + "'. Rebaking may fail on the associated model."); + } +} + void ModelBaker::outputBakedFST() { // Output FST file, copying over input mappings if available QString outputFSTFilename = !_mappingURL.isEmpty() ? _mappingURL.fileName() : _modelURL.fileName(); @@ -327,6 +362,7 @@ void ModelBaker::outputBakedFST() { outputMapping[FST_VERSION_FIELD] = FST_VERSION; outputMapping[FILENAME_FIELD] = _bakedModelURL.fileName(); outputMapping.remove(TEXDIR_FIELD); + outputMapping.remove(COMMENT_FIELD); hifi::ByteArray fstOut = FSTReader::writeMapping(outputMapping); QFile fstOutputFile { outputFSTURL }; diff --git a/libraries/baking/src/ModelBaker.h b/libraries/baking/src/ModelBaker.h index d612a0a43a..f280481803 100644 --- a/libraries/baking/src/ModelBaker.h +++ b/libraries/baking/src/ModelBaker.h @@ -82,6 +82,7 @@ protected slots: void handleFinishedMaterialBaker(); private: + void outputUnbakedFST(); void outputBakedFST(); bool _hasBeenBaked { false }; diff --git a/libraries/fbx/src/FSTReader.h b/libraries/fbx/src/FSTReader.h index 2b13bf3078..3945fe1d8b 100644 --- a/libraries/fbx/src/FSTReader.h +++ b/libraries/fbx/src/FSTReader.h @@ -33,6 +33,7 @@ static const QString BLENDSHAPE_FIELD = "bs"; static const QString SCRIPT_FIELD = "script"; static const QString JOINT_NAME_MAPPING_FIELD = "jointMap"; static const QString MATERIAL_MAPPING_FIELD = "materialMap"; +static const QString COMMENT_FIELD = "comment"; class FSTReader { public: From 7e445e40f8f6af3a61f016abe8e417ee7677eb5e Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 17 Apr 2019 17:25:02 -0700 Subject: [PATCH 27/56] change text to display --- interface/resources/qml/hifi/audio/Audio.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index f7e2494813..c603131b9c 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -254,7 +254,7 @@ Rectangle { switchWidth: root.switchWidth; anchors.top: parent.top anchors.left: parent.left - labelTextOn: qsTr("Warn when muted in HMD"); + labelTextOn: qsTr("HMD Mute Warning"); labelTextSize: 16; backgroundOnColor: "#E3E3E3"; checked: AudioScriptingInterface.warnWhenMuted; From ddb040eaf7a40d9afb09db6eba45bb36c16f6186 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Thu, 18 Apr 2019 07:48:59 -0700 Subject: [PATCH 28/56] adding settingsLoaded bool check --- interface/src/scripting/Audio.cpp | 5 +++-- interface/src/scripting/Audio.h | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index caae946116..0a29152c7c 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -174,7 +174,7 @@ void Audio::setPTTDesktop(bool enabled) { _pttDesktop = enabled; } }); - if (!enabled) { + if (!enabled && _settingsLoaded) { // Set to default behavior (unmuted for Desktop) on Push-To-Talk disable. setMutedDesktop(true); } else { @@ -202,7 +202,7 @@ void Audio::setPTTHMD(bool enabled) { _pttHMD = enabled; } }); - if (!enabled) { + if (!enabled && _settingsLoaded) { // Set to default behavior (unmuted for HMD) on Push-To-Talk disable. setMutedHMD(false); } else { @@ -231,6 +231,7 @@ void Audio::loadData() { auto client = DependencyManager::get().data(); QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted()), Q_ARG(bool, false)); + _settingsLoaded = true; } bool Audio::getPTTHMD() const { diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index 00da566b30..f7116ced3a 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -409,6 +409,7 @@ protected: private: + bool _settingsLoaded { false }; float _inputVolume { 1.0f }; float _inputLevel { 0.0f }; float _localInjectorGain { 0.0f }; // in dB From b89dbf834625b59c02a025a373118b2ee1e51f36 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Thu, 18 Apr 2019 11:22:37 -0700 Subject: [PATCH 29/56] fix transparent textures on baked assets --- .../src/model-networking/ModelCache.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 23b365dd03..75c63f99ca 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -498,6 +498,20 @@ bool Geometry::areTexturesLoaded() const { material->checkResetOpacityMap(); } + for (auto& materialMapping : _materialMapping) { + if (materialMapping.second) { + for (auto& materialPair : materialMapping.second->parsedMaterials.networkMaterials) { + if (materialPair.second) { + if (materialPair.second->isMissingTexture()) { + return false; + } + + materialPair.second->checkResetOpacityMap(); + } + } + } + } + _areTexturesLoaded = true; } return true; From 3bb8da3f290bf860a4a088a4125dad757e82efd0 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Thu, 18 Apr 2019 14:46:08 -0700 Subject: [PATCH 30/56] real fix --- interface/src/scripting/Audio.cpp | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index 0a29152c7c..ce340f2665 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -174,9 +174,11 @@ void Audio::setPTTDesktop(bool enabled) { _pttDesktop = enabled; } }); - if (!enabled && _settingsLoaded) { - // Set to default behavior (unmuted for Desktop) on Push-To-Talk disable. - setMutedDesktop(true); + if (!enabled) { + if (_settingsLoaded) { + // 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. setMutedDesktop(true); @@ -202,9 +204,11 @@ void Audio::setPTTHMD(bool enabled) { _pttHMD = enabled; } }); - if (!enabled && _settingsLoaded) { - // Set to default behavior (unmuted for HMD) on Push-To-Talk disable. - setMutedHMD(false); + if (!enabled) { + if (_settingsLoaded) { + // 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); @@ -358,11 +362,6 @@ void Audio::onContextChanged() { changed = true; } }); - if (isHMD) { - setMuted(getMutedHMD()); - } else { - setMuted(getMutedDesktop()); - } if (changed) { emit contextChanged(isHMD ? Audio::HMD : Audio::DESKTOP); } From 389667ae563fd3cf54a3f41f3ee02489dbc58b80 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 17 Apr 2019 20:18:17 -0700 Subject: [PATCH 31/56] adding proper logic for shield icon behavior --- interface/resources/qml/BubbleIcon.qml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/interface/resources/qml/BubbleIcon.qml b/interface/resources/qml/BubbleIcon.qml index f4e99f136c..b5a7ddb2d8 100644 --- a/interface/resources/qml/BubbleIcon.qml +++ b/interface/resources/qml/BubbleIcon.qml @@ -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; } } From 94e51b136c7f5c4890d0a74d75b22fc884bfde1e Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Thu, 18 Apr 2019 16:54:20 -0700 Subject: [PATCH 32/56] Fix baked models not preserving scale in some cases --- libraries/baking/src/FBXBaker.cpp | 21 ++++++ .../src/graphics-scripting/Forward.h | 5 +- .../GraphicsScriptingInterface.cpp | 4 + .../graphics-scripting/ScriptableModel.cpp | 4 + libraries/graphics/src/graphics/Material.h | 1 + .../src/material-networking/MaterialCache.cpp | 23 +++++- libraries/shared/src/RegisteredMetaTypes.cpp | 73 +++++++++++++++++++ libraries/shared/src/RegisteredMetaTypes.h | 4 + 8 files changed, 131 insertions(+), 4 deletions(-) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 01897ee5e9..9b80041bcf 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -142,6 +142,27 @@ void FBXBaker::rewriteAndBakeSceneModels(const QVector& meshes, const } else if (object->name == "Texture" || object->name == "Video") { // this is an embedded texture, we need to remove it from the FBX object = rootChild.children.erase(object); + } else if (object->name == "Material") { + for (FBXNode& materialChild : object->children) { + if (materialChild.name == "Properties60" || materialChild.name == "Properties70") { + // This is a properties node + // Remove the material texture scale because that is now included in the material JSON + // Texture nodes are removed, so their texture scale is effectively gone already + static const QVariant MAYA_UV_SCALE = hifi::ByteArray("Maya|uv_scale"); + static const QVariant MAYA_UV_OFFSET = hifi::ByteArray("Maya|uv_offset"); + for (int i = 0; i < materialChild.children.size(); i++) { + const auto& prop = materialChild.children[i]; + const auto& propertyName = prop.properties.at(0); + if (propertyName == MAYA_UV_SCALE || + propertyName == MAYA_UV_OFFSET) { + materialChild.children.removeAt(i); + --i; + } + } + } + } + + object++; } else { object++; } diff --git a/libraries/graphics-scripting/src/graphics-scripting/Forward.h b/libraries/graphics-scripting/src/graphics-scripting/Forward.h index d2d330167d..6d1b9d83d2 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/Forward.h +++ b/libraries/graphics-scripting/src/graphics-scripting/Forward.h @@ -59,8 +59,8 @@ namespace scriptable { * @property {string} occlusionMap * @property {string} lightmapMap * @property {string} scatteringMap - * @property {string} texCoordTransform0 - * @property {string} texCoordTransform1 + * @property {Mat4|string} texCoordTransform0 + * @property {Mat4|string} texCoordTransform1 * @property {string} lightmapParams * @property {string} materialParams * @property {boolean} defaultFallthrough @@ -93,6 +93,7 @@ namespace scriptable { QString occlusionMap; QString lightmapMap; QString scatteringMap; + std::array texCoordTransforms; bool defaultFallthrough; std::unordered_map propertyFallthroughs; // not actually exposed to script diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp index 3bd4af601c..4182b52309 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp @@ -470,9 +470,13 @@ namespace scriptable { // These need to be implemented, but set the fallthrough for now if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::TEXCOORDTRANSFORM0)) { obj.setProperty("texCoordTransform0", FALLTHROUGH); + } else { + obj.setProperty("texCoordTransform0", mat4toScriptValue(engine, material.texCoordTransforms[0])); } if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::TEXCOORDTRANSFORM1)) { obj.setProperty("texCoordTransform1", FALLTHROUGH); + } else { + obj.setProperty("texCoordTransform1", mat4toScriptValue(engine, material.texCoordTransforms[1])); } if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::LIGHTMAP_PARAMS)) { obj.setProperty("lightmapParams", FALLTHROUGH); diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp index caee4ceb2a..8825a26bfe 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp @@ -119,6 +119,10 @@ scriptable::ScriptableMaterial::ScriptableMaterial(const graphics::MaterialPoint if (map && map->getTextureSource()) { scatteringMap = map->getTextureSource()->getUrl().toString(); } + + for (int i = 0; i < graphics::Material::NUM_TEXCOORD_TRANSFORMS; i++) { + texCoordTransforms[i] = material->getTexCoordTransform(i); + } } } diff --git a/libraries/graphics/src/graphics/Material.h b/libraries/graphics/src/graphics/Material.h index 80b247bed0..330feaa61c 100755 --- a/libraries/graphics/src/graphics/Material.h +++ b/libraries/graphics/src/graphics/Material.h @@ -324,6 +324,7 @@ public: void setModel(const std::string& model) { _model = model; } glm::mat4 getTexCoordTransform(uint i) const { return _texcoordTransforms[i]; } + void setTexCoordTransform(uint i, const glm::mat4& mat4) { _texcoordTransforms[i] = mat4; } glm::vec2 getLightmapParams() const { return _lightmapParams; } glm::vec2 getMaterialParams() const { return _materialParams; } diff --git a/libraries/material-networking/src/material-networking/MaterialCache.cpp b/libraries/material-networking/src/material-networking/MaterialCache.cpp index 5a5f4ab54b..9d6fc06d01 100644 --- a/libraries/material-networking/src/material-networking/MaterialCache.cpp +++ b/libraries/material-networking/src/material-networking/MaterialCache.cpp @@ -177,6 +177,9 @@ std::pair> NetworkMaterialResource material->setModel(modelString); } + std::array hasTexcoordTransform; + std::array texcoordTransforms; + if (modelString == HIFI_PBR) { const QString FALLTHROUGH("fallthrough"); for (auto& key : materialJSON.keys()) { @@ -372,8 +375,12 @@ std::pair> NetworkMaterialResource if (valueString == FALLTHROUGH) { material->setPropertyDoesFallthrough(graphics::Material::ExtraFlagBit::TEXCOORDTRANSFORM0); } + } else if (value.isObject()) { + auto valueVariant = value.toVariant(); + glm::mat4 transform = mat4FromVariant(valueVariant); + hasTexcoordTransform[0] = true; + texcoordTransforms[0] = transform; } - // TODO: implement texCoordTransform0 } else if (key == "texCoordTransform1") { auto value = materialJSON.value(key); if (value.isString()) { @@ -381,8 +388,12 @@ std::pair> NetworkMaterialResource if (valueString == FALLTHROUGH) { material->setPropertyDoesFallthrough(graphics::Material::ExtraFlagBit::TEXCOORDTRANSFORM1); } + } else if (value.isObject()) { + auto valueVariant = value.toVariant(); + glm::mat4 transform = mat4FromVariant(valueVariant); + hasTexcoordTransform[1] = true; + texcoordTransforms[1] = transform; } - // TODO: implement texCoordTransform1 } else if (key == "lightmapParams") { auto value = materialJSON.value(key); if (value.isString()) { @@ -409,6 +420,14 @@ std::pair> NetworkMaterialResource } } } + + // Do this after the texture maps are defined, so it overrides the default transforms + for (int i = 0; i < graphics::Material::NUM_TEXCOORD_TRANSFORMS; i++) { + if (hasTexcoordTransform[i]) { + material->setTexCoordTransform(i, texcoordTransforms[i]); + } + } + return std::pair>(name, material); } diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index 597e537d8d..e453b7bff4 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -636,6 +636,79 @@ void mat4FromScriptValue(const QScriptValue& object, glm::mat4& mat4) { mat4[3][3] = object.property("r3c3").toVariant().toFloat(); } +QVariant mat4ToVariant(const glm::mat4& mat4) { + if (mat4 != mat4) { + // NaN + return QVariant(); + } + QVariantMap object; + + object["r0c0"] = mat4[0][0]; + object["r1c0"] = mat4[0][1]; + object["r2c0"] = mat4[0][2]; + object["r3c0"] = mat4[0][3]; + object["r0c1"] = mat4[1][0]; + object["r1c1"] = mat4[1][1]; + object["r2c1"] = mat4[1][2]; + object["r3c1"] = mat4[1][3]; + object["r0c2"] = mat4[2][0]; + object["r1c2"] = mat4[2][1]; + object["r2c2"] = mat4[2][2]; + object["r3c2"] = mat4[2][3]; + object["r0c3"] = mat4[3][0]; + object["r1c3"] = mat4[3][1]; + object["r2c3"] = mat4[3][2]; + object["r3c3"] = mat4[3][3]; + + return object; +} + +glm::mat4 mat4FromVariant(const QVariant& object, bool& valid) { + glm::mat4 mat4; + valid = false; + if (!object.isValid() || object.isNull()) { + return mat4; + } else { + const static auto getElement = [](const QVariantMap& map, const char * key, float& value, bool& everyConversionValid) { + auto variantValue = map[key]; + if (variantValue.canConvert()) { + value = variantValue.toFloat(); + } else { + everyConversionValid = false; + } + }; + + auto map = object.toMap(); + bool everyConversionValid = true; + + getElement(map, "r0c0", mat4[0][0], everyConversionValid); + getElement(map, "r1c0", mat4[0][1], everyConversionValid); + getElement(map, "r2c0", mat4[0][2], everyConversionValid); + getElement(map, "r3c0", mat4[0][3], everyConversionValid); + getElement(map, "r0c1", mat4[1][0], everyConversionValid); + getElement(map, "r1c1", mat4[1][1], everyConversionValid); + getElement(map, "r2c1", mat4[1][2], everyConversionValid); + getElement(map, "r3c1", mat4[1][3], everyConversionValid); + getElement(map, "r0c2", mat4[2][0], everyConversionValid); + getElement(map, "r1c2", mat4[2][1], everyConversionValid); + getElement(map, "r2c2", mat4[2][2], everyConversionValid); + getElement(map, "r3c2", mat4[2][3], everyConversionValid); + getElement(map, "r0c3", mat4[3][0], everyConversionValid); + getElement(map, "r1c3", mat4[3][1], everyConversionValid); + getElement(map, "r2c3", mat4[3][2], everyConversionValid); + getElement(map, "r3c3", mat4[3][3], everyConversionValid); + + if (everyConversionValid) { + valid = true; + } + } +} + +glm::mat4 mat4FromVariant(const QVariant& object) { + bool valid = false; + return mat4FromVariant(object, valid); +} + QScriptValue qVectorVec3ColorToScriptValue(QScriptEngine* engine, const QVector& vector) { QScriptValue array = engine->newArray(); for (int i = 0; i < vector.size(); i++) { diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index ea2c5b8354..96c64f7384 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -67,6 +67,10 @@ void registerMetaTypes(QScriptEngine* engine); QScriptValue mat4toScriptValue(QScriptEngine* engine, const glm::mat4& mat4); void mat4FromScriptValue(const QScriptValue& object, glm::mat4& mat4); +QVariant mat4ToVariant(const glm::mat4& mat4); +glm::mat4 mat4FromVariant(const QVariant& object, bool& valid); +glm::mat4 mat4FromVariant(const QVariant& object); + /**jsdoc * A 2-dimensional vector. * From b1e3a149ffb5f6232164bc738b8c28225d5c3d4d Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Thu, 18 Apr 2019 17:40:56 -0700 Subject: [PATCH 33/56] Remove unnecessary hasTexcoordTransform --- .../src/material-networking/MaterialCache.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/libraries/material-networking/src/material-networking/MaterialCache.cpp b/libraries/material-networking/src/material-networking/MaterialCache.cpp index 9d6fc06d01..745504fb3d 100644 --- a/libraries/material-networking/src/material-networking/MaterialCache.cpp +++ b/libraries/material-networking/src/material-networking/MaterialCache.cpp @@ -177,7 +177,6 @@ std::pair> NetworkMaterialResource material->setModel(modelString); } - std::array hasTexcoordTransform; std::array texcoordTransforms; if (modelString == HIFI_PBR) { @@ -378,7 +377,6 @@ std::pair> NetworkMaterialResource } else if (value.isObject()) { auto valueVariant = value.toVariant(); glm::mat4 transform = mat4FromVariant(valueVariant); - hasTexcoordTransform[0] = true; texcoordTransforms[0] = transform; } } else if (key == "texCoordTransform1") { @@ -391,7 +389,6 @@ std::pair> NetworkMaterialResource } else if (value.isObject()) { auto valueVariant = value.toVariant(); glm::mat4 transform = mat4FromVariant(valueVariant); - hasTexcoordTransform[1] = true; texcoordTransforms[1] = transform; } } else if (key == "lightmapParams") { @@ -423,8 +420,9 @@ std::pair> NetworkMaterialResource // Do this after the texture maps are defined, so it overrides the default transforms for (int i = 0; i < graphics::Material::NUM_TEXCOORD_TRANSFORMS; i++) { - if (hasTexcoordTransform[i]) { - material->setTexCoordTransform(i, texcoordTransforms[i]); + mat4 newTransform = texcoordTransforms[i]; + if (newTransform != mat4() || newTransform != material->getTexCoordTransform(i)) { + material->setTexCoordTransform(i, newTransform); } } From d711e1a9b7c8f4d69b631fd4810bcf9d6aee32b7 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Thu, 18 Apr 2019 17:43:03 -0700 Subject: [PATCH 34/56] Save some bandwidth by not including unity texture transforms in material jsons --- .../src/graphics-scripting/GraphicsScriptingInterface.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp index 4182b52309..eb4bfa197c 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp @@ -470,12 +470,12 @@ namespace scriptable { // These need to be implemented, but set the fallthrough for now if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::TEXCOORDTRANSFORM0)) { obj.setProperty("texCoordTransform0", FALLTHROUGH); - } else { + } else if (material.texCoordTransforms[0] != mat4()) { obj.setProperty("texCoordTransform0", mat4toScriptValue(engine, material.texCoordTransforms[0])); } if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::TEXCOORDTRANSFORM1)) { obj.setProperty("texCoordTransform1", FALLTHROUGH); - } else { + } else if (material.texCoordTransforms[1] != mat4()) { obj.setProperty("texCoordTransform1", mat4toScriptValue(engine, material.texCoordTransforms[1])); } if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::LIGHTMAP_PARAMS)) { From 474041d5d1524d45c9e79cf6b2f7b32a9f03391d Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Thu, 18 Apr 2019 21:00:15 -0700 Subject: [PATCH 35/56] simplify PTT logic --- interface/src/scripting/Audio.cpp | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index ce340f2665..bceafc3c42 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -174,16 +174,10 @@ void Audio::setPTTDesktop(bool enabled) { _pttDesktop = enabled; } }); - if (!enabled) { - if (_settingsLoaded) { - // 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); @@ -204,14 +198,9 @@ void Audio::setPTTHMD(bool enabled) { _pttHMD = enabled; } }); - if (!enabled) { - if (_settingsLoaded) { - // 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) { From aea9ed91db427dace99578dae1e660e9f38c2c9b Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Fri, 19 Apr 2019 09:00:28 -0700 Subject: [PATCH 36/56] Fix mat4FromVariant --- libraries/shared/src/RegisteredMetaTypes.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index e453b7bff4..98b67d3f75 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -701,6 +701,8 @@ glm::mat4 mat4FromVariant(const QVariant& object, bool& valid) { if (everyConversionValid) { valid = true; } + + return mat4; } } From 67ea9055e005c9080575fbd1a065c0a4fd6ea59a Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 19 Apr 2019 09:32:42 -0700 Subject: [PATCH 37/56] Fix domain baker not finishing (threading, material key) This addresses 2 cases where the domain baker would not finish 1. For material baking, we were storing a value in _entitiesNeedingRewrite with a different key than what we use when we later remove it from _entitiesNeedingRewrite 2. The domain baker runs on a worker thread. When starting a baker for an item in the domain, we do an invokeMethod, which will happen synchronously if the baker ends up on the same thread as the thread that the domain baker is on. This can cause issues if the baker finishes immediately. The case I saw was a local model url that immediately failed, and finished before return from the invokeMethod in the domain baker . --- tools/oven/src/DomainBaker.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/oven/src/DomainBaker.cpp b/tools/oven/src/DomainBaker.cpp index 50a3d212c0..bf8509fefe 100644 --- a/tools/oven/src/DomainBaker.cpp +++ b/tools/oven/src/DomainBaker.cpp @@ -171,7 +171,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; @@ -212,7 +212,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; @@ -247,7 +247,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; @@ -272,7 +272,7 @@ void DomainBaker::addMaterialBaker(const QString& property, const QString& data, // setup a baker for this material QSharedPointer materialBaker { - new MaterialBaker(data, isURL, _contentOutputPath), + new MaterialBaker(materialData, isURL, _contentOutputPath), &MaterialBaker::deleteLater }; @@ -284,7 +284,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; From a9ece60888e46861c6eb32ecd898b95648455ce2 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Fri, 19 Apr 2019 18:21:31 -0700 Subject: [PATCH 38/56] allowing context change to change mute state --- interface/src/scripting/Audio.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index bceafc3c42..5dd1488606 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -351,6 +351,13 @@ void Audio::onContextChanged() { changed = true; } }); + if (_settingsLoaded) { + if (isHMD) { + setMuted(getMutedHMD()); + } else { + setMuted(getMutedDesktop()); + } + } if (changed) { emit contextChanged(isHMD ? Audio::HMD : Audio::DESKTOP); } From 7574912629c98b583402b363c9c633115f436381 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 22 Apr 2019 11:34:58 -0700 Subject: [PATCH 39/56] fix version not being preserved (cherry picked from commit 98a7e4e7110996ea972d0b859af7eb9783502b24) --- tools/oven/src/DomainBaker.cpp | 14 ++++---------- tools/oven/src/DomainBaker.h | 2 ++ 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/tools/oven/src/DomainBaker.cpp b/tools/oven/src/DomainBaker.cpp index bf8509fefe..3444077922 100644 --- a/tools/oven/src/DomainBaker.cpp +++ b/tools/oven/src/DomainBaker.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include "Gzip.h" @@ -132,10 +131,10 @@ void DomainBaker::loadLocalFile() { } // read the file contents to a JSON document - auto jsonDocument = QJsonDocument::fromJson(fileContents); + _json = QJsonDocument::fromJson(fileContents); // grab the entities object from the root JSON object - _entities = jsonDocument.object()[ENTITIES_OBJECT_KEY].toArray(); + _entities = _json.object()[ENTITIES_OBJECT_KEY].toArray(); if (_entities.isEmpty()) { // add an error to our list stating that the models file was empty @@ -753,15 +752,10 @@ void DomainBaker::writeNewEntitiesFile() { // time to write out a main models.json.gz file // first setup a document with the entities array below the entities key - QJsonDocument entitiesDocument; - - QJsonObject rootObject; - rootObject[ENTITIES_OBJECT_KEY] = _entities; - - entitiesDocument.setObject(rootObject); + _json.object()[ENTITIES_OBJECT_KEY] = _entities; // turn that QJsonDocument into a byte array ready for compression - QByteArray jsonByteArray = entitiesDocument.toJson(); + QByteArray jsonByteArray = _json.toJson(); // compress the json byte array using gzip QByteArray compressedJson; diff --git a/tools/oven/src/DomainBaker.h b/tools/oven/src/DomainBaker.h index 81f5c345cd..5e3f7c3b9a 100644 --- a/tools/oven/src/DomainBaker.h +++ b/tools/oven/src/DomainBaker.h @@ -12,6 +12,7 @@ #ifndef hifi_DomainBaker_h #define hifi_DomainBaker_h +#include #include #include #include @@ -59,6 +60,7 @@ private: QString _originalOutputPath; QUrl _destinationPath; + QJsonDocument _json; QJsonArray _entities; QHash> _modelBakers; From d7b12bcc3b6fc75f1fae9c0aae70748044c08817 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Mon, 22 Apr 2019 19:08:15 -0700 Subject: [PATCH 40/56] set AudioClient mute state as catch-all --- interface/src/scripting/Audio.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index 5dd1488606..b406c097e7 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -352,11 +352,11 @@ void Audio::onContextChanged() { } }); if (_settingsLoaded) { - if (isHMD) { - setMuted(getMutedHMD()); - } else { - setMuted(getMutedDesktop()); - } + 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().data(); + QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false)); } if (changed) { emit contextChanged(isHMD ? Audio::HMD : Audio::DESKTOP); From 90e993844a26ea44468d764d8c7d9e39f1410b64 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Tue, 23 Apr 2019 16:50:54 -0700 Subject: [PATCH 41/56] changing method name to reflect recent change in code --- .../resources/qml/hifi/commerce/marketplace/Marketplace.qml | 2 +- interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml index 5f8cc2eefb..65453ba21d 100644 --- a/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml +++ b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml @@ -952,7 +952,7 @@ Rectangle { text: "LOG IN" onClicked: { - sendToScript({method: 'needsLogIn_loginClicked'}); + sendToScript({method: 'marketplace_loginClicked'}); } } diff --git a/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml b/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml index 0a1593161a..e17196b492 100644 --- a/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml +++ b/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml @@ -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'}); } } } From 5f3a37d40e6747906785580ee63ee3e0fffb411c Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 23 Apr 2019 17:34:46 -0700 Subject: [PATCH 42/56] possible fix for openUrl exploit (cherry picked from commit 31b3f0e8f1faa8f9e4a2d89c2375607deaed2fb3) --- .../qml/LoginDialog/CompleteProfileBody.qml | 2 +- .../qml/LoginDialog/LinkAccountBody.qml | 2 +- .../resources/qml/LoginDialog/SignUpBody.qml | 2 +- .../qml/LoginDialog/UsernameCollisionBody.qml | 2 +- interface/src/Application.cpp | 40 ++++++++++++------- interface/src/Application.h | 4 +- .../scripting/WindowScriptingInterface.cpp | 7 +++- .../src/scripting/WindowScriptingInterface.h | 7 ++-- interface/src/ui/LoginDialog.cpp | 4 -- interface/src/ui/LoginDialog.h | 2 - libraries/ui/src/FileDialogHelper.cpp | 10 ++++- 11 files changed, 50 insertions(+), 32 deletions(-) diff --git a/interface/resources/qml/LoginDialog/CompleteProfileBody.qml b/interface/resources/qml/LoginDialog/CompleteProfileBody.qml index 17d6a7d3b3..f90a7d8561 100644 --- a/interface/resources/qml/LoginDialog/CompleteProfileBody.qml +++ b/interface/resources/qml/LoginDialog/CompleteProfileBody.qml @@ -398,7 +398,7 @@ Item { lineHeight: 1 lineHeightMode: Text.ProportionalHeight - onLinkActivated: loginDialog.openUrl(link); + onLinkActivated: Window.openUrl(link); Component.onCompleted: { if (termsTextMetrics.width > root.bannerWidth) { diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml index 4dd05f594d..04ffe72a57 100644 --- a/interface/resources/qml/LoginDialog/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml @@ -363,7 +363,7 @@ Item { linkColor: hifi.colors.blueAccent onLinkActivated: { Tablet.playSound(TabletEnums.ButtonClick); - loginDialog.openUrl(link); + Window.openUrl(link); lightboxPopup.titleText = "Can't Access Account"; lightboxPopup.bodyText = lightboxPopup.cantAccessBodyText; lightboxPopup.button2text = "CLOSE"; diff --git a/interface/resources/qml/LoginDialog/SignUpBody.qml b/interface/resources/qml/LoginDialog/SignUpBody.qml index 69ac2f5a6c..f1e0cfe685 100644 --- a/interface/resources/qml/LoginDialog/SignUpBody.qml +++ b/interface/resources/qml/LoginDialog/SignUpBody.qml @@ -411,7 +411,7 @@ Item { lineHeight: 1 lineHeightMode: Text.ProportionalHeight - onLinkActivated: loginDialog.openUrl(link); + onLinkActivated: Window.openUrl(link); Component.onCompleted: { if (termsTextMetrics.width > root.bannerWidth) { diff --git a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml index d450b1e7bc..d2fd1dfe35 100644 --- a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml +++ b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml @@ -234,7 +234,7 @@ Item { lineHeight: 1 lineHeightMode: Text.ProportionalHeight - onLinkActivated: loginDialog.openUrl(link); + onLinkActivated: Window.openUrl(link); Component.onCompleted: { if (termsTextMetrics.width > root.bannerWidth) { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e809120a74..184c153f0e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2443,6 +2443,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo DependencyManager::get()->preloadSounds(); DependencyManager::get()->createKeyboard(); + QDesktopServices::setUrlHandler("file", this, "showUrlHandler"); + QDesktopServices::setUrlHandler("", this, "showUrlHandler"); + auto drives = QDir::drives(); + for (auto drive : drives) { + QDesktopServices::setUrlHandler(QUrl(drive.absolutePath()).scheme(), this, "showUrlHandler"); + } + _pendingIdleEvent = false; _graphicsEngine.startup(); @@ -8259,19 +8266,6 @@ void Application::packageModel() { ModelPackager::package(); } -void Application::openUrl(const QUrl& url) const { - if (!url.isEmpty()) { - if (url.scheme() == URL_SCHEME_HIFI) { - DependencyManager::get()->handleLookupString(url.toString()); - } else if (url.scheme() == URL_SCHEME_HIFIAPP) { - DependencyManager::get()->openSystemApp(url.path()); - } else { - // address manager did not handle - ask QDesktopServices to handle - QDesktopServices::openUrl(url); - } - } -} - void Application::loadDialog() { ModalDialogListener* dlg = OffscreenUi::getOpenFileNameAsync(_glWidget, tr("Open Script"), getPreviousScriptLocation(), @@ -9140,7 +9134,7 @@ void Application::readArgumentsFromLocalSocket() const { // If we received a message, try to open it as a URL if (message.length() > 0) { - qApp->openUrl(QString::fromUtf8(message)); + DependencyManager::get()->openUrl(QString::fromUtf8(message)); } } @@ -9257,6 +9251,24 @@ QString Application::getGraphicsCardType() { return GPUIdent::getInstance()->getName(); } +void Application::showUrlHandler(const QUrl& url) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "showUrlHandler", Q_ARG(const QUrl&, url)); + return; + } + + ModalDialogListener* dlg = OffscreenUi::asyncQuestion("Confirm openUrl", "Do you recognize this path or code and want to open or execute it: " + url.toDisplayString()); + QObject::connect(dlg, &ModalDialogListener::response, this, [=](QVariant answer) { + QObject::disconnect(dlg, &ModalDialogListener::response, this, nullptr); + if (QMessageBox::Yes == static_cast(answer.toInt())) { + // Unset the handler, open the URL, and the reset the handler + QDesktopServices::unsetUrlHandler(url.scheme()); + QDesktopServices::openUrl(url); + QDesktopServices::setUrlHandler(url.scheme(), this, "showUrlHandler"); + } + }); +} + #if defined(Q_OS_ANDROID) void Application::beforeEnterBackground() { auto nodeList = DependencyManager::get(); diff --git a/interface/src/Application.h b/interface/src/Application.h index 99e57f1866..96feeba39f 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -403,8 +403,6 @@ public slots: static void packageModel(); - void openUrl(const QUrl& url) const; - void resetSensors(bool andReload = false); void setActiveFaceTracker() const; @@ -471,6 +469,8 @@ public slots: QString getGraphicsCardType(); + void showUrlHandler(const QUrl& url); + private slots: void onDesktopRootItemCreated(QQuickItem* qmlContext); void onDesktopRootContextCreated(QQmlContext* qmlContext); diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 0f3d859093..2c1311924f 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -27,6 +27,7 @@ #include "MainWindow.h" #include "Menu.h" #include "OffscreenUi.h" +#include "commerce/QmlCommerce.h" static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); static const QString LAST_BROWSE_LOCATION_SETTING = "LastBrowseLocation"; @@ -134,15 +135,17 @@ void WindowScriptingInterface::disconnectedFromDomain() { void WindowScriptingInterface::openUrl(const QUrl& url) { if (!url.isEmpty()) { - if (url.scheme() == URL_SCHEME_HIFI) { + auto scheme = url.scheme(); + if (scheme == URL_SCHEME_HIFI) { DependencyManager::get()->handleLookupString(url.toString()); + } else if (scheme == URL_SCHEME_HIFIAPP) { + DependencyManager::get()->openSystemApp(url.path()); } else { #if defined(Q_OS_ANDROID) QMap args; args["url"] = url.toString(); AndroidHelper::instance().requestActivity("WebView", true, args); #else - // address manager did not handle - ask QDesktopServices to handle QDesktopServices::openUrl(url); #endif } diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index baff6444e1..77b586ec70 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -535,9 +535,10 @@ public slots: int openMessageBox(QString title, QString text, int buttons, int defaultButton); /**jsdoc - * Open a URL in the Interface window or other application, depending on the URL's scheme. If the URL starts with - * hifi:// then that URL is navigated to in Interface, otherwise the URL is opened in the application the OS - * associates with the URL's scheme (e.g., a Web browser for http://). + * Open a URL in the Interface window or other application, depending on the URL's scheme. The following schemes are supported: + * hifi (navigate to the URL in Interface), hifiapp (open a system app in Interface). Other schemes will either be handled by the OS + * (e.g. http, https, mailto) or will create a confirmation dialog asking the user to confirm that they want to try to open + * the URL. * @function Window.openUrl * @param {string} url - The URL to open. */ diff --git a/interface/src/ui/LoginDialog.cpp b/interface/src/ui/LoginDialog.cpp index b4f504822f..50acdeaa1e 100644 --- a/interface/src/ui/LoginDialog.cpp +++ b/interface/src/ui/LoginDialog.cpp @@ -279,10 +279,6 @@ void LoginDialog::createAccountFromSteam(QString username) { } } -void LoginDialog::openUrl(const QString& url) const { - QDesktopServices::openUrl(QUrl(url)); -} - void LoginDialog::linkCompleted(QNetworkReply* reply) { emit handleLinkCompleted(); } diff --git a/interface/src/ui/LoginDialog.h b/interface/src/ui/LoginDialog.h index e2fa8adc61..7c659a9320 100644 --- a/interface/src/ui/LoginDialog.h +++ b/interface/src/ui/LoginDialog.h @@ -80,8 +80,6 @@ protected slots: Q_INVOKABLE void signup(const QString& email, const QString& username, const QString& password); - Q_INVOKABLE void openUrl(const QString& url) const; - Q_INVOKABLE bool getLoginDialogPoppedUp() const; }; diff --git a/libraries/ui/src/FileDialogHelper.cpp b/libraries/ui/src/FileDialogHelper.cpp index 6d14adf1db..e6a48da01b 100644 --- a/libraries/ui/src/FileDialogHelper.cpp +++ b/libraries/ui/src/FileDialogHelper.cpp @@ -111,7 +111,15 @@ QStringList FileDialogHelper::drives() { } void FileDialogHelper::openDirectory(const QString& path) { - QDesktopServices::openUrl(path); + QString dirPath = path; + const QString FILE_SCHEME = "file://"; + if (dirPath.startsWith(FILE_SCHEME)) { + dirPath.remove(0, FILE_SCHEME.length()); + } + QFileInfo fileInfo(dirPath); + if (fileInfo.isDir()) { + QDesktopServices::openUrl(path); + } } QList FileDialogHelper::urlToList(const QUrl& url) { From b9ba764e0ff9c153ae34c1c2f8bbeadf5bddc763 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Thu, 25 Apr 2019 12:24:02 -0700 Subject: [PATCH 43/56] fix openDirectory (cherry picked from commit 609e58a0ddcff0ace1d75a85fafd73bfa4a5eacd) --- interface/src/Application.cpp | 21 +++++++++++++++++++++ interface/src/Application.h | 2 ++ libraries/ui/src/FileDialogHelper.cpp | 11 +++-------- libraries/ui/src/FileDialogHelper.h | 3 +++ 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 184c153f0e..ba28eb6068 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2443,6 +2443,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo DependencyManager::get()->preloadSounds(); DependencyManager::get()->createKeyboard(); + FileDialogHelper::setOpenDirectoryOperator([this](const QString& path) { openDirectory(path); }); QDesktopServices::setUrlHandler("file", this, "showUrlHandler"); QDesktopServices::setUrlHandler("", this, "showUrlHandler"); auto drives = QDir::drives(); @@ -9251,6 +9252,26 @@ QString Application::getGraphicsCardType() { return GPUIdent::getInstance()->getName(); } +void Application::openDirectory(const QString& path) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "openDirectory", Q_ARG(const QString&, path)); + return; + } + + QString dirPath = path; + const QString FILE_SCHEME = "file:///"; + if (dirPath.startsWith(FILE_SCHEME)) { + dirPath.remove(0, FILE_SCHEME.length()); + } + QFileInfo fileInfo(dirPath); + if (fileInfo.isDir()) { + auto scheme = QUrl(path).scheme(); + QDesktopServices::unsetUrlHandler(scheme); + QDesktopServices::openUrl(path); + QDesktopServices::setUrlHandler(scheme, this, "showUrlHandler"); + } +} + void Application::showUrlHandler(const QUrl& url) { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "showUrlHandler", Q_ARG(const QUrl&, url)); diff --git a/interface/src/Application.h b/interface/src/Application.h index 96feeba39f..d3b4ab1d44 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -344,6 +344,8 @@ public: void toggleAwayMode(); #endif + void openDirectory(const QString& path); + signals: void svoImportRequested(const QString& url); diff --git a/libraries/ui/src/FileDialogHelper.cpp b/libraries/ui/src/FileDialogHelper.cpp index e6a48da01b..4fa4e0bd54 100644 --- a/libraries/ui/src/FileDialogHelper.cpp +++ b/libraries/ui/src/FileDialogHelper.cpp @@ -17,6 +17,7 @@ #include #include +std::function FileDialogHelper::_openDiretoryOperator = nullptr; QUrl FileDialogHelper::home() { return pathToUrl(QStandardPaths::standardLocations(QStandardPaths::HomeLocation)[0]); @@ -111,14 +112,8 @@ QStringList FileDialogHelper::drives() { } void FileDialogHelper::openDirectory(const QString& path) { - QString dirPath = path; - const QString FILE_SCHEME = "file://"; - if (dirPath.startsWith(FILE_SCHEME)) { - dirPath.remove(0, FILE_SCHEME.length()); - } - QFileInfo fileInfo(dirPath); - if (fileInfo.isDir()) { - QDesktopServices::openUrl(path); + if (_openDiretoryOperator) { + _openDiretoryOperator(path); } } diff --git a/libraries/ui/src/FileDialogHelper.h b/libraries/ui/src/FileDialogHelper.h index 12fd60daac..55561a40dd 100644 --- a/libraries/ui/src/FileDialogHelper.h +++ b/libraries/ui/src/FileDialogHelper.h @@ -16,6 +16,7 @@ #include #include +#include class FileDialogHelper : public QObject { Q_OBJECT @@ -62,6 +63,7 @@ public: Q_INVOKABLE QUrl saveHelper(const QString& saveText, const QUrl& currentFolder, const QStringList& selectionFilters); Q_INVOKABLE QList urlToList(const QUrl& url); + static void setOpenDirectoryOperator(std::function openDiretoryOperator) { _openDiretoryOperator = openDiretoryOperator; } Q_INVOKABLE void openDirectory(const QString& path); Q_INVOKABLE void monitorDirectory(const QString& path); @@ -72,6 +74,7 @@ signals: private: QFileSystemWatcher _fsWatcher; QString _fsWatcherPath; + static std::function _openDiretoryOperator; }; From 0f3d755cb0f99592c8359a42ce16e19000c66887 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Thu, 25 Apr 2019 16:00:05 -0700 Subject: [PATCH 44/56] fix baking --- tools/oven/src/DomainBaker.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/oven/src/DomainBaker.cpp b/tools/oven/src/DomainBaker.cpp index 3444077922..3c06ec1096 100644 --- a/tools/oven/src/DomainBaker.cpp +++ b/tools/oven/src/DomainBaker.cpp @@ -752,10 +752,11 @@ void DomainBaker::writeNewEntitiesFile() { // time to write out a main models.json.gz file // first setup a document with the entities array below the entities key - _json.object()[ENTITIES_OBJECT_KEY] = _entities; + QJsonObject json = _json.object(); + json[ENTITIES_OBJECT_KEY] = _entities; // turn that QJsonDocument into a byte array ready for compression - QByteArray jsonByteArray = _json.toJson(); + QByteArray jsonByteArray = QJsonDocument(json).toJson(); // compress the json byte array using gzip QByteArray compressedJson; From 43d005d47059d577dda7afa3c5cae9adf57009b1 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Tue, 30 Apr 2019 16:00:35 -0700 Subject: [PATCH 45/56] Set default av-mixer connection-rate to 1e7 --- domain-server/resources/describe-settings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 352106dcf7..74c744aced 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -1307,8 +1307,8 @@ "name": "connection_rate", "label": "Connection Rate", "help": "Number of new agents that can connect to the mixer every second", - "placeholder": "50", - "default": "50", + "placeholder": "10000000", + "default": "10000000", "advanced": true }, { From 099e04f7bbcffe16efdea5e61634d9a7ce94da3c Mon Sep 17 00:00:00 2001 From: Clement Date: Tue, 30 Apr 2019 17:57:37 -0700 Subject: [PATCH 46/56] Query shape type outside lock --- libraries/entities/src/EntityItem.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 8a50c39da9..9ae9246176 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -2023,9 +2023,10 @@ void EntityItem::setCollisionMask(uint16_t value) { void EntityItem::setDynamic(bool value) { if (getDynamic() != value) { + auto shapeType = getShapeType(); withWriteLock([&] { // dynamic and STATIC_MESH are incompatible so we check for that case - if (value && getShapeType() == SHAPE_TYPE_STATIC_MESH) { + if (value && shapeType == SHAPE_TYPE_STATIC_MESH) { if (_dynamic) { _dynamic = false; _flags |= Simulation::DIRTY_MOTION_TYPE; From d0fc5c771377b30a627c2f729279325d199aeb92 Mon Sep 17 00:00:00 2001 From: Clement Date: Tue, 30 Apr 2019 18:29:00 -0700 Subject: [PATCH 47/56] Push protocol version --- libraries/networking/src/udt/PacketHeaders.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 1dafc561f6..30c102ca4f 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -269,6 +269,7 @@ enum class EntityVersion : PacketVersion { CertificateTypeProperty, DisableWebMedia, ParticleShapeType, + ParticleShapeTypeDeadlockFix, // Add new versions above here NUM_PACKET_TYPE, From 47af406440daaccdc48bf01cb3669233a940f811 Mon Sep 17 00:00:00 2001 From: Revofire Date: Sat, 30 Mar 2019 00:27:11 -0400 Subject: [PATCH 48/56] merged 0.80.0-kasen into 0.81.0-kasen, 0.81.0-kasen source ready for build. various changes, idk, added a whitelist filter to ent script filter --- cmake/externals/polyvox/CMakeLists.txt | 6 +- interface/CMakeLists.txt | 28 +++++---- interface/src/Application.cpp | 8 +-- libraries/audio-client/src/AudioClient.cpp | 2 +- .../networking/src/UserActivityLogger.cpp | 2 + libraries/script-engine/src/ScriptEngine.cpp | 63 ++++++++++++++++--- plugins/CMakeLists.txt | 12 ++-- 7 files changed, 89 insertions(+), 32 deletions(-) diff --git a/cmake/externals/polyvox/CMakeLists.txt b/cmake/externals/polyvox/CMakeLists.txt index a92c07da86..e5c3521b63 100644 --- a/cmake/externals/polyvox/CMakeLists.txt +++ b/cmake/externals/polyvox/CMakeLists.txt @@ -1,11 +1,15 @@ set(EXTERNAL_NAME polyvox) include(ExternalProject) + +message(STATUS "===== POLYVOX BUILD_TYPE ${BUILD_TYPE} ${CMAKE_BUILD_TYPE}") +set(CMAKE_BUILD_TYPE Release) + ExternalProject_Add( ${EXTERNAL_NAME} URL https://public.highfidelity.com/dependencies/polyvox-master-2015-7-15.zip URL_MD5 9ec6323b87e849ae36e562ae1c7494a9 - CMAKE_ARGS -DENABLE_EXAMPLES=OFF -DENABLE_BINDINGS=OFF -DCMAKE_INSTALL_PREFIX:PATH= + CMAKE_ARGS -DENABLE_EXAMPLES=OFF -DENABLE_BINDINGS=OFF -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build LOG_DOWNLOAD 1 LOG_CONFIGURE 1 diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index bb8ebbd2a0..bf152ba5d8 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -15,9 +15,9 @@ set(CUSTOM_INTERFACE_QRC_PATHS "") find_npm() -if (BUILD_TOOLS AND NPM_EXECUTABLE) - add_custom_qrc_path(CUSTOM_INTERFACE_QRC_PATHS "${CMAKE_SOURCE_DIR}/tools/jsdoc/out/hifiJSDoc.json" "auto-complete/hifiJSDoc.json") -endif () +#if (BUILD_TOOLS AND NPM_EXECUTABLE) +# add_custom_qrc_path(CUSTOM_INTERFACE_QRC_PATHS "${CMAKE_SOURCE_DIR}/tools/jsdoc/out/hifiJSDoc.json" "auto-complete/hifiJSDoc.json") +#endif () set(RESOURCES_QRC ${CMAKE_CURRENT_BINARY_DIR}/resources.qrc) set(RESOURCES_RCC ${CMAKE_CURRENT_SOURCE_DIR}/compiledResources/resources.rcc) @@ -156,7 +156,9 @@ elseif (WIN32) configure_file("${HF_CMAKE_DIR}/templates/VersionInfo.rc.in" ${CONFIGURE_VERSION_INFO_RC_OUTPUT}) # add an executable that also has the icon itself and the configured rc file as resources - add_executable(${TARGET_NAME} WIN32 ${INTERFACE_SRCS} ${QM} ${CONFIGURE_ICON_RC_OUTPUT} ${CONFIGURE_VERSION_INFO_RC_OUTPUT}) + #add_executable(${TARGET_NAME} WIN32 ${INTERFACE_SRCS} ${QM} ${CONFIGURE_ICON_RC_OUTPUT} ${CONFIGURE_VERSION_INFO_RC_OUTPUT}) + ##^^^^^ creates native Win32 app w/o cmd console vvvvvv forces cmd console for logging + add_executable(${TARGET_NAME} ${INTERFACE_SRCS} ${QM} ${CONFIGURE_ICON_RC_OUTPUT} ${CONFIGURE_VERSION_INFO_RC_OUTPUT}) if (NOT DEV_BUILD) add_custom_command( @@ -175,10 +177,10 @@ else () endif () -if (BUILD_TOOLS AND NPM_EXECUTABLE) +#if (BUILD_TOOLS AND NPM_EXECUTABLE) # require JSDoc to be build before interface is deployed - add_dependencies(resources jsdoc) -endif() +# add_dependencies(resources jsdoc) +#endif() add_dependencies(${TARGET_NAME} resources) @@ -318,9 +320,9 @@ if (APPLE) "${CMAKE_SOURCE_DIR}/scripts" "${RESOURCES_DEV_DIR}/scripts" # copy JSDoc files beside the executable - COMMAND "${CMAKE_COMMAND}" -E copy_directory - "${CMAKE_SOURCE_DIR}/tools/jsdoc/out" - "${RESOURCES_DEV_DIR}/jsdoc" + #COMMAND "${CMAKE_COMMAND}" -E copy_directory + # "${CMAKE_SOURCE_DIR}/tools/jsdoc/out" + # "${RESOURCES_DEV_DIR}/jsdoc" # copy the resources files beside the executable COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${RESOURCES_RCC}" @@ -373,9 +375,9 @@ else() "${PROJECT_SOURCE_DIR}/resources/serverless/redirect.json" "${RESOURCES_DEV_DIR}/serverless/redirect.json" # copy JSDoc files beside the executable - COMMAND "${CMAKE_COMMAND}" -E copy_directory - "${CMAKE_SOURCE_DIR}/tools/jsdoc/out" - "${INTERFACE_EXEC_DIR}/jsdoc" + #COMMAND "${CMAKE_COMMAND}" -E copy_directory + # "${CMAKE_SOURCE_DIR}/tools/jsdoc/out" + # "${INTERFACE_EXEC_DIR}/jsdoc" ) # link target to external libraries diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ba28eb6068..daf8d0a77c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2767,14 +2767,14 @@ Application::~Application() { avatarManager->handleProcessedPhysicsTransaction(transaction); avatarManager->deleteAllAvatars(); - + auto myCharacterController = getMyAvatar()->getCharacterController(); myCharacterController->clearDetailedMotionStates(); - + myCharacterController->buildPhysicsTransaction(transaction); _physicsEngine->processTransaction(transaction); myCharacterController->handleProcessedPhysicsTransaction(transaction); - + _physicsEngine->setCharacterController(nullptr); // the _shapeManager should have zero references @@ -6978,7 +6978,7 @@ void Application::updateWindowTitle() const { auto accountManager = DependencyManager::get(); auto isInErrorState = nodeList->getDomainHandler().isInErrorState(); - QString buildVersion = " - " + QString buildVersion = " - Kasen v0.81.1 - " + (BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable ? QString("Version") : QString("Build")) + " " + applicationVersion(); diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index c537fea646..8979b5a8a6 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1915,7 +1915,7 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo outputDeviceI int deviceChannelCount = _outputFormat.channelCount(); int frameSize = (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * deviceChannelCount * _outputFormat.sampleRate()) / _desiredOutputFormat.sampleRate(); int requestedSize = _sessionOutputBufferSizeFrames * frameSize * AudioConstants::SAMPLE_SIZE; - _audioOutput->setBufferSize(requestedSize); + _audioOutput->setBufferSize(requestedSize * 16); // initialize mix buffers on the _audioOutput thread to avoid races connect(_audioOutput, &QAudioOutput::stateChanged, [&, frameSize, requestedSize](QAudio::State state) { diff --git a/libraries/networking/src/UserActivityLogger.cpp b/libraries/networking/src/UserActivityLogger.cpp index 948acd3d78..7315824136 100644 --- a/libraries/networking/src/UserActivityLogger.cpp +++ b/libraries/networking/src/UserActivityLogger.cpp @@ -35,6 +35,8 @@ void UserActivityLogger::disable(bool disable) { } void UserActivityLogger::logAction(QString action, QJsonObject details, JSONCallbackParameters params) { + qCDebug(networking).nospace() << ">>> UserActivityLogger::logAction(" << action << "," << QJsonDocument(details).toJson(); + return; if (_disabled.get()) { return; } diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 7abb63ca1c..3d01ea5a82 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -252,7 +252,7 @@ QString ScriptEngine::getContext() const { return "unknown"; } -bool ScriptEngine::isDebugMode() const { +bool ScriptEngine::isDebugMode() const { #if defined(DEBUG) return true; #else @@ -769,6 +769,11 @@ void ScriptEngine::init() { #if DEV_BUILD || PR_BUILD registerGlobalObject("StackTest", new StackTestScriptingInterface(this)); #endif + + globalObject().setProperty("KALILA", "isWaifu"); + globalObject().setProperty("Kute", newFunction([](QScriptContext* context, QScriptEngine* engine) -> QScriptValue { + return context->argument(0).toString().toLower() == "kalila" ? true : false; + })); } void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) { @@ -2252,6 +2257,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co BaseScriptEngine sandbox; sandbox.setProcessEventsInterval(SANDBOX_TIMEOUT); QScriptValue testConstructor, exception; + if (atoi(getenv("UNSAFE_ENTITY_SCRIPTS") ? getenv("UNSAFE_ENTITY_SCRIPTS") : "0")) { QTimer timeout; timeout.setSingleShot(true); @@ -2274,13 +2280,57 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co exception = testConstructor; } } + else { + // IF YOU FUCK UP, DELETE FROM HERE TO... + QList safeURLS = { "https://animedeck.com/", "http://animedeck.com/" }; + safeURLS += qEnvironmentVariable("EXTRA_WHITELIST").split(QRegExp("\\s*,\\s*"), QString::SkipEmptyParts); + + bool isInWhitelist = false; // assume unsafe + for (const auto& str : safeURLS) { + // qDebug() << "CHECKING" << entityID.toString() << scriptOrURL << "AGAINST" << str; + qDebug() << "SCRIPTOURL STARTSWITH" << scriptOrURL << "TESTING AGAINST" << str << "RESULTS IN" + << scriptOrURL.startsWith(str); + if (scriptOrURL.startsWith(str)) { + isInWhitelist = true; + break; // bail early since we found a match + } + } + if (!isInWhitelist) { + qDebug() << "(disabled entity script)" << entityID.toString() << scriptOrURL; + exception = makeError("UNSAFE_ENTITY_SCRIPTS == 0"); + } else { + QTimer timeout; + timeout.setSingleShot(true); + timeout.start(SANDBOX_TIMEOUT); + connect(&timeout, &QTimer::timeout, [=, &sandbox] { + qCDebug(scriptengine) << "ScriptEngine::entityScriptContentAvailable timeout"; + + // Guard against infinite loops and non-performant code + sandbox.raiseException( + sandbox.makeError(QString("Timed out (entity constructors are limited to %1ms)").arg(SANDBOX_TIMEOUT))); + }); + + testConstructor = sandbox.evaluate(program); + + if (sandbox.hasUncaughtException()) { + exception = sandbox.cloneUncaughtException(QString("(preflight %1)").arg(entityID.toString())); + sandbox.clearExceptions(); + } else if (testConstructor.isError()) { + exception = testConstructor; + } + } + // DELETE UP TO HERE, THEN UNCOMMENT BELOW. + + // qDebug() << "(disabled entity script)" << entityID.toString() << scriptOrURL; + // exception = makeError("UNSAFE_ENTITY_SCRIPTS == 0"); + } if (exception.isError()) { - // create a local copy using makeError to decouple from the sandbox engine - exception = makeError(exception); - setError(formatException(exception, _enableExtendedJSExceptions.get()), EntityScriptStatus::ERROR_RUNNING_SCRIPT); - emit unhandledException(exception); - return; + // create a local copy using makeError to decouple from the sandbox engine + exception = makeError(exception); + setError(formatException(exception, _enableExtendedJSExceptions.get()), EntityScriptStatus::ERROR_RUNNING_SCRIPT); + emit unhandledException(exception); + return; } // CONSTRUCTOR VIABILITY @@ -2648,4 +2698,3 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS } } } - diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 4a0f52e272..8a0ccb07c1 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -9,23 +9,23 @@ # add the plugin directories file(GLOB PLUGIN_SUBDIRS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/*") list(REMOVE_ITEM PLUGIN_SUBDIRS "CMakeFiles") - +set(CMAKE_BUILD_TYPE "Release") # client-side plugins if (NOT SERVER_ONLY AND NOT ANDROID) - set(DIR "oculus") - add_subdirectory(${DIR}) + #set(DIR "oculus") + #add_subdirectory(${DIR}) set(DIR "hifiSdl2") add_subdirectory(${DIR}) set(DIR "openvr") add_subdirectory(${DIR}) - set(DIR "oculusLegacy") - add_subdirectory(${DIR}) + #set(DIR "oculusLegacy") + #add_subdirectory(${DIR}) if (USE_SIXENSE) set(DIR "hifiSixense") add_subdirectory(${DIR}) endif() - + set(DIR "hifiSpacemouse") add_subdirectory(${DIR}) set(DIR "hifiNeuron") From e59a29c8c084439361d63f749ab15838bf9b4eed Mon Sep 17 00:00:00 2001 From: Revofire Date: Mon, 6 May 2019 21:17:56 -0400 Subject: [PATCH 49/56] Updated version number. --- interface/src/Application.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index daf8d0a77c..cfc918af11 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2906,7 +2906,7 @@ void Application::initializeGL() { #if !defined(DISABLE_QML) QStringList chromiumFlags; - // Bug 21993: disable microphone and camera input + // Bug 21993: disable microphone and camera input chromiumFlags << "--use-fake-device-for-media-stream"; // Disable signed distance field font rendering on ATI/AMD GPUs, due to // https://highfidelity.manuscript.com/f/cases/13677/Text-showing-up-white-on-Marketplace-app @@ -6978,7 +6978,7 @@ void Application::updateWindowTitle() const { auto accountManager = DependencyManager::get(); auto isInErrorState = nodeList->getDomainHandler().isInErrorState(); - QString buildVersion = " - Kasen v0.81.1 - " + QString buildVersion = " - Kasen v0.82.1 - " + (BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable ? QString("Version") : QString("Build")) + " " + applicationVersion(); From 500e089e4499073274acce3676d72586b644d041 Mon Sep 17 00:00:00 2001 From: Kalila R Date: Thu, 20 Jun 2019 16:56:03 -0400 Subject: [PATCH 50/56] Updated version no. to 0.83.0 --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index cfc918af11..248e2ea092 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6978,7 +6978,7 @@ void Application::updateWindowTitle() const { auto accountManager = DependencyManager::get(); auto isInErrorState = nodeList->getDomainHandler().isInErrorState(); - QString buildVersion = " - Kasen v0.82.1 - " + QString buildVersion = " - Kasen v0.83.0 - " + (BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable ? QString("Version") : QString("Build")) + " " + applicationVersion(); From 36b249d4dc77ef6cc81c0fd3991e3ec80f9c1bcc Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 25 Jul 2019 18:24:53 -0700 Subject: [PATCH 51/56] BUGZ-964: fix for incorrect color curve when rendering 3D into QML --- interface/src/ui/ResourceImageItem.cpp | 70 +++++++++++++++++++++++--- interface/src/ui/ResourceImageItem.h | 11 ++-- 2 files changed, 70 insertions(+), 11 deletions(-) diff --git a/interface/src/ui/ResourceImageItem.cpp b/interface/src/ui/ResourceImageItem.cpp index f14f4ca78e..46ffffe62d 100644 --- a/interface/src/ui/ResourceImageItem.cpp +++ b/interface/src/ui/ResourceImageItem.cpp @@ -11,11 +11,52 @@ #include "ResourceImageItem.h" #include - +#include #include +#include #include + +static const char* VERTEX_SHADER = R"SHADER( +#version 450 core + +out vec2 vTexCoord; + +void main(void) { + const float depth = 0.0; + const vec4 UNIT_QUAD[4] = vec4[4]( + vec4(-1.0, -1.0, depth, 1.0), + vec4(1.0, -1.0, depth, 1.0), + vec4(-1.0, 1.0, depth, 1.0), + vec4(1.0, 1.0, depth, 1.0) + ); + vec4 pos = UNIT_QUAD[gl_VertexID]; + + gl_Position = pos; + vTexCoord = (pos.xy + 1.0) * 0.5; +} +)SHADER"; + +static const char* FRAGMENT_SHADER = R"SHADER( +#version 450 core + +uniform sampler2D sampler; + +in vec2 vTexCoord; + +out vec4 FragColor; + +vec3 color_LinearTosRGB(vec3 lrgb) { + return mix(vec3(1.055) * pow(vec3(lrgb), vec3(0.41666)) - vec3(0.055), vec3(lrgb) * vec3(12.92), vec3(lessThan(lrgb, vec3(0.0031308)))); +} + +void main() { + FragColor = vec4(color_LinearTosRGB(texture(sampler, vTexCoord).rgb), 1.0); +} +)SHADER"; + + ResourceImageItem::ResourceImageItem() : QQuickFramebufferObject() { auto textureCache = DependencyManager::get(); connect(textureCache.data(), SIGNAL(spectatorCameraFramebufferReset()), this, SLOT(update())); @@ -95,16 +136,29 @@ void ResourceImageItemRenderer::render() { } if (_ready) { _fboMutex.lock(); - _copyFbo->bind(); - QOpenGLFramebufferObject::blitFramebuffer(framebufferObject(), _copyFbo, GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT, GL_NEAREST); - // this clears the copyFbo texture - // so next frame starts fresh - helps - // when aspect ratio changes - _copyFbo->takeTexture(); + + if (!_shader) { + _shader = new QOpenGLShaderProgram(); + _shader->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, VERTEX_SHADER); + _shader->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, FRAGMENT_SHADER); + _shader->link(); + glGenVertexArrays(1, &_vao); + } + framebufferObject()->bind(); + _shader->bind(); + + auto sourceTextureId = _copyFbo->takeTexture(); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, sourceTextureId); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glBindVertexArray(_vao); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glDeleteTextures(1, &sourceTextureId); + _copyFbo->bind(); _copyFbo->release(); - _fboMutex.unlock(); } glFlush(); diff --git a/interface/src/ui/ResourceImageItem.h b/interface/src/ui/ResourceImageItem.h index d154588094..1a679ba2ee 100644 --- a/interface/src/ui/ResourceImageItem.h +++ b/interface/src/ui/ResourceImageItem.h @@ -22,6 +22,9 @@ #include +class QOpenGLFramebufferObject; +class QOpenGLShaderProgram; + class ResourceImageItemRenderer : public QObject, public QQuickFramebufferObject::Renderer { Q_OBJECT public: @@ -30,14 +33,16 @@ public: void synchronize(QQuickFramebufferObject* item) override; void render() override; private: - bool _ready; + bool _ready{ false }; QString _url; - bool _visible; + bool _visible{ false }; NetworkTexturePointer _networkTexture; - QQuickWindow* _window; + QQuickWindow* _window{ nullptr }; QMutex _fboMutex; + uint32_t _vao{ 0 }; QOpenGLFramebufferObject* _copyFbo { nullptr }; + QOpenGLShaderProgram* _shader{ nullptr }; GLsync _fenceSync { 0 }; QTimer _updateTimer; public slots: From 7d3ac62c8600e7fb5d28293e70da80cc2af5d397 Mon Sep 17 00:00:00 2001 From: milad Date: Mon, 29 Jul 2019 14:05:55 -0700 Subject: [PATCH 52/56] changed root to micBar which had the gated property --- interface/resources/qml/hifi/audio/MicBarApplication.qml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/resources/qml/hifi/audio/MicBarApplication.qml b/interface/resources/qml/hifi/audio/MicBarApplication.qml index 3070ba3bbd..76551be906 100644 --- a/interface/resources/qml/hifi/audio/MicBarApplication.qml +++ b/interface/resources/qml/hifi/audio/MicBarApplication.qml @@ -39,8 +39,8 @@ Rectangle { } Component.onCompleted: { - AudioScriptingInterface.noiseGateOpened.connect(function() { root.gated = false; }); - AudioScriptingInterface.noiseGateClosed.connect(function() { root.gated = true; }); + AudioScriptingInterface.noiseGateOpened.connect(function() { micBar.gated = false; }); + AudioScriptingInterface.noiseGateClosed.connect(function() { micBar.gated = true; }); HMD.displayModeChanged.connect(function() { muted = AudioScriptingInterface.muted; pushToTalk = AudioScriptingInterface.pushToTalk; @@ -151,7 +151,7 @@ Rectangle { readonly property string yellow: "#C0C000"; readonly property string fill: "#55000000"; readonly property string border: standalone ? "#80FFFFFF" : "#55FFFFFF"; - readonly property string icon: (muted || clipping) ? mutedColor : root.gated ? gatedColor : unmutedColor; + readonly property string icon: (muted || clipping) ? mutedColor : micBar.gated ? gatedColor : unmutedColor; } Item { @@ -169,7 +169,7 @@ Rectangle { Image { id: image; source: (pushToTalk) ? pushToTalkIcon : muted ? mutedIcon : - clipping ? clippingIcon : root.gated ? gatedIcon : unmutedIcon; + clipping ? clippingIcon : micBar.gated ? gatedIcon : unmutedIcon; width: 29; height: 32; anchors { From fd7ac223b0d0744b34a453cf843d68bdb1bf9326 Mon Sep 17 00:00:00 2001 From: Kalila R Date: Mon, 11 Nov 2019 20:08:03 -0500 Subject: [PATCH 53/56] activitylogger still disabled but does not output to console --- libraries/networking/src/UserActivityLogger.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/networking/src/UserActivityLogger.cpp b/libraries/networking/src/UserActivityLogger.cpp index 7315824136..269ff94b80 100644 --- a/libraries/networking/src/UserActivityLogger.cpp +++ b/libraries/networking/src/UserActivityLogger.cpp @@ -35,7 +35,8 @@ void UserActivityLogger::disable(bool disable) { } void UserActivityLogger::logAction(QString action, QJsonObject details, JSONCallbackParameters params) { - qCDebug(networking).nospace() << ">>> UserActivityLogger::logAction(" << action << "," << QJsonDocument(details).toJson(); +// qCDebug(networking).nospace() << ">>> UserActivityLogger::logAction(" << action << "," << QJsonDocument(details).toJson(); +// This logs what the UserActivityLogger would normally send to centralized servers. return; if (_disabled.get()) { return; From a7b2c3a32c2736ca19225a9dffe721e3880b04f4 Mon Sep 17 00:00:00 2001 From: Kalila R Date: Mon, 11 Nov 2019 20:22:19 -0500 Subject: [PATCH 54/56] Updated application name and version. --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0da167645f..732445dcb4 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -7142,7 +7142,7 @@ void Application::updateWindowTitle() const { auto accountManager = DependencyManager::get(); auto isInErrorState = nodeList->getDomainHandler().isInErrorState(); - QString buildVersion = " - Kasen v0.83.0 - " + QString buildVersion = " - Kasen Community Edition v0.85.0 - " + (BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable ? QString("Version") : QString("Build")) + " " + applicationVersion(); From 5f7486fe7ec7a791c93ffaa41c25e3a624019fd2 Mon Sep 17 00:00:00 2001 From: Kalila R Date: Mon, 11 Nov 2019 20:26:26 -0500 Subject: [PATCH 55/56] Setting DomainBaker.cpp back to main. --- tools/oven/src/DomainBaker.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/tools/oven/src/DomainBaker.cpp b/tools/oven/src/DomainBaker.cpp index a9f45767a6..0853b41872 100644 --- a/tools/oven/src/DomainBaker.cpp +++ b/tools/oven/src/DomainBaker.cpp @@ -417,13 +417,9 @@ void DomainBaker::enumerateEntities() { addMaterialBaker(MATERIAL_URL_KEY, materialURL, true, *it); } } - // FIXME: Disabled for now because relative texture URLs are not supported for embedded materials in material entities - // We need to make texture URLs absolute in this particular case only, keeping in mind that FSTBaker also uses embedded materials - /* if (entity.contains(MATERIAL_DATA_KEY)) { addMaterialBaker(MATERIAL_DATA_KEY, entity[MATERIAL_DATA_KEY].toString(), false, *it, _destinationPath); } - */ } } @@ -449,7 +445,7 @@ void DomainBaker::handleFinishedModelBaker() { // enumerate the QJsonRef values for the URL of this model from our multi hash of // entity objects needing a URL re-write - for (auto propertyEntityPair : _entitiesNeedingRewrite.values(baker->getModelURL())) { + for (auto propertyEntityPair : _entitiesNeedingRewrite.values(baker->getOriginalInputModelURL())) { QString property = propertyEntityPair.first; // convert the entity QJsonValueRef to a QJsonObject so we can modify its URL auto entity = propertyEntityPair.second.toObject(); @@ -489,10 +485,10 @@ void DomainBaker::handleFinishedModelBaker() { } // remove the baked URL from the multi hash of entities needing a re-write - _entitiesNeedingRewrite.remove(baker->getModelURL()); + _entitiesNeedingRewrite.remove(baker->getOriginalInputModelURL()); // drop our shared pointer to this baker so that it gets cleaned up - _modelBakers.remove(baker->getModelURL()); + _modelBakers.remove(baker->getOriginalInputModelURL()); // emit progress to tell listeners how many models we have baked emit bakeProgress(++_completedSubBakes, _totalNumberOfSubBakes); From 7c1b9e79aec03b06557880698ebd32efc8fdc34c Mon Sep 17 00:00:00 2001 From: Kalila R Date: Mon, 11 Nov 2019 20:28:16 -0500 Subject: [PATCH 56/56] Setting ModelBaker.cpp back to main. --- libraries/baking/src/ModelBaker.cpp | 41 ++++++----------------------- 1 file changed, 8 insertions(+), 33 deletions(-) diff --git a/libraries/baking/src/ModelBaker.cpp b/libraries/baking/src/ModelBaker.cpp index cdd07fccf8..70290fe283 100644 --- a/libraries/baking/src/ModelBaker.cpp +++ b/libraries/baking/src/ModelBaker.cpp @@ -45,6 +45,7 @@ #include ModelBaker::ModelBaker(const QUrl& inputModelURL, const QString& bakedOutputDirectory, const QString& originalOutputDirectory, bool hasBeenBaked) : + _originalInputModelURL(inputModelURL), _modelURL(inputModelURL), _bakedOutputDir(bakedOutputDirectory), _originalOutputDir(originalOutputDirectory), @@ -245,6 +246,12 @@ void ModelBaker::bakeSourceCopy() { // Begin hfm baking baker.run(); + const auto& errors = baker.getDracoErrors(); + if (std::find(errors.cbegin(), errors.cend(), true) != errors.cend()) { + handleError("Failed to finalize the baking of a draco Geometry node from model " + _modelURL.toString()); + return; + } + _hfmModel = baker.getHFMModel(); _materialMapping = baker.getMaterialMapping(); dracoMeshes = baker.getDracoMeshes(); @@ -389,37 +396,6 @@ void ModelBaker::outputUnbakedFST() { } } -void ModelBaker::outputUnbakedFST() { - // Output an unbaked FST file in the original output folder to make it easier for FSTBaker to rebake this model - // TODO: Consider a more robust method that does not depend on FSTBaker navigating to a hardcoded relative path - QString outputFSTFilename = _modelURL.fileName(); - auto extensionStart = outputFSTFilename.indexOf("."); - if (extensionStart != -1) { - outputFSTFilename.resize(extensionStart); - } - outputFSTFilename += FST_EXTENSION; - QString outputFSTURL = _originalOutputDir + "/" + outputFSTFilename; - - hifi::VariantHash outputMapping; - outputMapping[FST_VERSION_FIELD] = FST_VERSION; - outputMapping[FILENAME_FIELD] = _modelURL.fileName(); - outputMapping[COMMENT_FIELD] = "This FST file was generated by Oven for use during rebaking. It is not part of the original model. This file's existence is subject to change."; - hifi::ByteArray fstOut = FSTReader::writeMapping(outputMapping); - - QFile fstOutputFile { outputFSTURL }; - if (fstOutputFile.exists()) { - handleWarning("The file '" + outputFSTURL + "' already exists. Should that be baked instead of '" + _modelURL.toString() + "'?"); - return; - } - if (!fstOutputFile.open(QIODevice::WriteOnly)) { - handleWarning("Failed to open file '" + outputFSTURL + "' for writing. Rebaking may fail on the associated model."); - return; - } - if (fstOutputFile.write(fstOut) == -1) { - handleWarning("Failed to write to file '" + outputFSTURL + "'. Rebaking may fail on the associated model."); - } -} - void ModelBaker::outputBakedFST() { // Output FST file, copying over input mappings if available QString outputFSTFilename = !_mappingURL.isEmpty() ? _mappingURL.fileName() : _modelURL.fileName(); @@ -467,8 +443,7 @@ void ModelBaker::abort() { bool ModelBaker::buildDracoMeshNode(FBXNode& dracoMeshNode, const QByteArray& dracoMeshBytes, const std::vector& dracoMaterialList) { if (dracoMeshBytes.isEmpty()) { - handleError("Failed to finalize the baking of a draco Geometry node"); - return false; + handleWarning("Empty mesh detected in model: '" + _modelURL.toString() + "'. It will be included in the baked output."); } FBXNode dracoNode;