diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 6e3db69c63..9df606c227 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -953,16 +953,15 @@ void AssetServer::removeBakedPathsForDeletedAsset(AssetHash hash) { deleteMappings(hiddenBakedFolder); } -bool AssetServer::deleteMappings(AssetPathList& paths) { +bool AssetServer::deleteMappings(const AssetPathList& paths) { // take a copy of the current mappings in case persistence of these deletes fails auto oldMappings = _fileMappings; QSet hashesToCheckForDeletion; // enumerate the paths to delete and remove them all - for (auto& path : paths) { - - path = path.trimmed(); + for (const auto& rawPath : paths) { + auto path = rawPath.trimmed(); // figure out if this path will delete a file or folder if (pathIsFolder(path)) { @@ -991,12 +990,12 @@ bool AssetServer::deleteMappings(AssetPathList& paths) { } else { auto it = _fileMappings.find(path); if (it != _fileMappings.end()) { - _fileMappings.erase(it); - // add this hash to the list we need to check for asset removal from server hashesToCheckForDeletion << it->second; qCDebug(asset_server) << "Deleted a mapping:" << path << "=>" << it->second; + + _fileMappings.erase(it); } else { qCDebug(asset_server) << "Unable to delete a mapping that was not found:" << path; } @@ -1165,10 +1164,11 @@ void AssetServer::handleFailedBake(QString originalAssetHash, QString assetPath, void AssetServer::handleCompletedBake(QString originalAssetHash, QString originalAssetPath, QVector bakedFilePaths) { bool errorCompletingBake { false }; + QString errorReason; qDebug() << "Completing bake for " << originalAssetHash; - for (auto& filePath: bakedFilePaths) { + for (auto& filePath : bakedFilePaths) { // figure out the hash for the contents of this file QFile file(filePath); @@ -1184,6 +1184,7 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina } else { // stop handling this bake, couldn't hash the contents of the file errorCompletingBake = true; + errorReason = "Failed to finalize bake"; break; } @@ -1194,6 +1195,7 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina 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; } } @@ -1220,12 +1222,14 @@ 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"; break; } } else { 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; } } @@ -1235,6 +1239,10 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina writeMetaFile(originalAssetHash); } else { qWarning() << "Could not complete bake for" << originalAssetHash; + AssetMeta meta; + meta.failedLastBake = true; + meta.lastBakeErrors = errorReason; + writeMetaFile(originalAssetHash, meta); } _pendingBakes.remove(originalAssetHash); @@ -1246,7 +1254,6 @@ void AssetServer::handleAbortedBake(QString originalAssetHash, QString assetPath } static const QString BAKE_VERSION_KEY = "bake_version"; -static const QString APP_VERSION_KEY = "app_version"; static const QString FAILED_LAST_BAKE_KEY = "failed_last_bake"; static const QString LAST_BAKE_ERRORS_KEY = "last_bake_errors"; @@ -1273,18 +1280,15 @@ std::pair AssetServer::readMetaFile(AssetHash hash) { auto root = doc.object(); auto bakeVersion = root[BAKE_VERSION_KEY].toInt(-1); - auto appVersion = root[APP_VERSION_KEY].toInt(-1); auto failedLastBake = root[FAILED_LAST_BAKE_KEY]; auto lastBakeErrors = root[LAST_BAKE_ERRORS_KEY]; if (bakeVersion != -1 - && appVersion != -1 && failedLastBake.isBool() && lastBakeErrors.isString()) { AssetMeta meta; meta.bakeVersion = bakeVersion; - meta.applicationVersion = appVersion; meta.failedLastBake = failedLastBake.toBool(); meta.lastBakeErrors = lastBakeErrors.toString(); @@ -1303,7 +1307,6 @@ bool AssetServer::writeMetaFile(AssetHash originalAssetHash, const AssetMeta& me QJsonObject metaFileObject; metaFileObject[BAKE_VERSION_KEY] = meta.bakeVersion; - metaFileObject[APP_VERSION_KEY] = meta.applicationVersion; metaFileObject[FAILED_LAST_BAKE_KEY] = meta.failedLastBake; metaFileObject[LAST_BAKE_ERRORS_KEY] = meta.lastBakeErrors; diff --git a/assignment-client/src/assets/AssetServer.h b/assignment-client/src/assets/AssetServer.h index 10ea067ee5..94be560c9b 100644 --- a/assignment-client/src/assets/AssetServer.h +++ b/assignment-client/src/assets/AssetServer.h @@ -34,7 +34,6 @@ struct AssetMeta { } int bakeVersion { 0 }; - int applicationVersion { 0 }; bool failedLastBake { false }; QString lastBakeErrors; }; @@ -79,7 +78,7 @@ private: bool setMapping(AssetPath path, AssetHash hash); /// Delete mapping `path`. Returns `true` if deletion of mappings succeeds, else `false`. - bool deleteMappings(AssetPathList& paths); + bool deleteMappings(const AssetPathList& paths); /// Rename mapping from `oldPath` to `newPath`. Returns true if successful bool renameMapping(AssetPath oldPath, AssetPath newPath); diff --git a/interface/resources/qml/AssetServer.qml b/interface/resources/qml/AssetServer.qml index 23fec03ac2..649ea49153 100644 --- a/interface/resources/qml/AssetServer.qml +++ b/interface/resources/qml/AssetServer.qml @@ -49,9 +49,14 @@ ScrollingWindow { Component.onCompleted: { ApplicationInterface.uploadRequest.connect(uploadClicked); assetMappingsModel.errorGettingMappings.connect(handleGetMappingsError); + assetMappingsModel.autoRefreshEnabled = true; reload(); } + + Component.onDestruction: { + assetMappingsModel.autoRefreshEnabled = false; + } function doDeleteFile(path) { console.log("Deleting " + path); @@ -146,7 +151,6 @@ ScrollingWindow { function reload() { Assets.mappingModel.refresh(); - treeView.selection.clear(); } function handleGetMappingsError(errorString) { @@ -502,16 +506,6 @@ ScrollingWindow { onClicked: root.deleteFile() enabled: treeView.selection.hasSelection } - - HifiControls.GlyphButton { - - glyph: hifi.glyphs.reload - color: hifi.buttons.black - colorScheme: root.colorScheme - width: hifi.dimensions.controlLineHeight - - onClicked: root.reload() - } } } @@ -751,7 +745,7 @@ ScrollingWindow { var path = assetProxyModel.data(index, 0x100); mappings.push(path); } - print("Setting baking enabled:" + mappings + checked); + print("Setting baking enabled:" + mappings + " " + checked); Assets.setBakingEnabled(mappings, checked, function() { reload(); }); diff --git a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml index 61f5a382ad..3f1fcf6bda 100644 --- a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml +++ b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml @@ -49,9 +49,15 @@ Rectangle { isHMD = HMD.active; ApplicationInterface.uploadRequest.connect(uploadClicked); assetMappingsModel.errorGettingMappings.connect(handleGetMappingsError); + assetMappingsModel.autoRefreshEnabled = true; + reload(); } + Component.onDestruction: { + assetMappingsModel.autoRefreshEnabled = false; + } + function doDeleteFile(path) { console.log("Deleting " + path); @@ -145,7 +151,6 @@ Rectangle { function reload() { Assets.mappingModel.refresh(); - treeView.selection.clear(); } function handleGetMappingsError(errorString) { @@ -502,16 +507,6 @@ Rectangle { onClicked: root.deleteFile() enabled: treeView.selection.hasSelection } - - HifiControls.GlyphButton { - - glyph: hifi.glyphs.reload - color: hifi.buttons.black - colorScheme: root.colorScheme - width: hifi.dimensions.controlLineHeight - - onClicked: root.reload() - } } } @@ -748,7 +743,7 @@ Rectangle { var path = assetProxyModel.data(index, 0x100); mappings.push(path); } - print("Setting baking enabled:" + mappings + checked); + print("Setting baking enabled:" + mappings + " " + checked); Assets.setBakingEnabled(mappings, checked, function() { reload(); }); diff --git a/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml b/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml index 83b48094e3..80c1b58444 100644 --- a/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml +++ b/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml @@ -400,6 +400,17 @@ Rectangle { colorScheme: hifi.colorSchemes.dark anchors.left: parent.left anchors.right: parent.right + + TableViewColumn { + role: "display"; + } + + onActivated: { + var path = scriptsModel.data(index, 0x100) + if (path) { + loadScript(path) + } + } } HifiControls.VerticalSpacer { @@ -426,13 +437,6 @@ Rectangle { } } } - - onActivated: { - var path = scriptsModel.data(index, 0x100) - if (path) { - loadScript(path) - } - } } Item { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index f7116a60db..e508c972c6 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -2196,6 +2196,14 @@ float MyAvatar::getDomainMaxScale() { return _domainMaximumScale; } +void MyAvatar::setGravity(float gravity) { + _characterController.setGravity(gravity); +} + +float MyAvatar::getGravity() { + return _characterController.getGravity(); +} + void MyAvatar::increaseSize() { // make sure we're starting from an allowable scale clampTargetScaleToDomainLimits(); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index c9d073cfd9..9620d61a49 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -152,7 +152,7 @@ class MyAvatar : public Avatar { Q_PROPERTY(float userHeight READ getUserHeight WRITE setUserHeight) Q_PROPERTY(float userEyeHeight READ getUserEyeHeight) - + const QString DOMINANT_LEFT_HAND = "left"; const QString DOMINANT_RIGHT_HAND = "right"; @@ -551,6 +551,9 @@ public slots: float getDomainMinScale(); float getDomainMaxScale(); + void setGravity(float gravity); + float getGravity(); + void goToLocation(const glm::vec3& newPosition, bool hasOrientation = false, const glm::quat& newOrientation = glm::quat(), bool shouldFaceLocation = false); diff --git a/interface/src/scripting/AssetMappingsScriptingInterface.cpp b/interface/src/scripting/AssetMappingsScriptingInterface.cpp index 14e0ef5b28..5031016c3f 100644 --- a/interface/src/scripting/AssetMappingsScriptingInterface.cpp +++ b/interface/src/scripting/AssetMappingsScriptingInterface.cpp @@ -19,8 +19,13 @@ #include #include #include +#include #include +static const int AUTO_REFRESH_INTERVAL = 1000; + +int assetMappingModelMetatypeId = qRegisterMetaType("AssetMappingModel*"); + AssetMappingsScriptingInterface::AssetMappingsScriptingInterface() { _proxyModel.setSourceModel(&_assetMappingModel); _proxyModel.setSortRole(Qt::DisplayRole); @@ -189,6 +194,29 @@ void AssetMappingsScriptingInterface::setBakingEnabled(QStringList paths, bool e AssetMappingModel::AssetMappingModel() { setupRoles(); + + connect(&_autoRefreshTimer, &QTimer::timeout, this, [this] { + auto nodeList = DependencyManager::get(); + auto assetServer = nodeList->soloNodeOfType(NodeType::AssetServer); + if (assetServer) { + refresh(); + } + }); + _autoRefreshTimer.setInterval(AUTO_REFRESH_INTERVAL); +} + +bool AssetMappingModel::isAutoRefreshEnabled() { + return _autoRefreshTimer.isActive(); +} + +void AssetMappingModel::setAutoRefreshEnabled(bool enabled) { + if (enabled != _autoRefreshTimer.isActive()) { + if (enabled) { + _autoRefreshTimer.start(); + } else { + _autoRefreshTimer.stop(); + } + } } bool AssetMappingModel::isKnownFolder(QString path) const { @@ -205,10 +233,7 @@ bool AssetMappingModel::isKnownFolder(QString path) const { return false; } -int assetMappingModelMetatypeId = qRegisterMetaType("AssetMappingModel*"); - void AssetMappingModel::refresh() { - qDebug() << "Refreshing asset mapping model"; auto assetClient = DependencyManager::get(); auto request = assetClient->createGetAllMappingsRequest(); diff --git a/interface/src/scripting/AssetMappingsScriptingInterface.h b/interface/src/scripting/AssetMappingsScriptingInterface.h index e4059c1ff7..04ab488838 100644 --- a/interface/src/scripting/AssetMappingsScriptingInterface.h +++ b/interface/src/scripting/AssetMappingsScriptingInterface.h @@ -25,11 +25,16 @@ class AssetMappingModel : public QStandardItemModel { Q_OBJECT + Q_PROPERTY(bool autoRefreshEnabled READ isAutoRefreshEnabled WRITE setAutoRefreshEnabled) + public: AssetMappingModel(); Q_INVOKABLE void refresh(); + bool isAutoRefreshEnabled(); + void setAutoRefreshEnabled(bool enabled); + bool isKnownMapping(QString path) const { return _pathToItemMap.contains(path); } bool isKnownFolder(QString path) const; @@ -44,6 +49,7 @@ private: void setupRoles(); QHash _pathToItemMap; + QTimer _autoRefreshTimer; }; Q_DECLARE_METATYPE(AssetMappingModel*) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 34aae37175..2366b888e7 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -15,6 +15,10 @@ #include #include +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + static const float CONTEXT_OVERLAY_TABLET_OFFSET = 30.0f; // Degrees static const float CONTEXT_OVERLAY_TABLET_ORIENTATION = 210.0f; // Degrees static const float CONTEXT_OVERLAY_TABLET_DISTANCE = 0.65F; // Meters @@ -38,11 +42,6 @@ ContextOverlayInterface::ContextOverlayInterface() { _entityPropertyFlags += PROP_DIMENSIONS; _entityPropertyFlags += PROP_REGISTRATION_POINT; - // initially, set _enabled to match the switch. Later we enable/disable via the getter/setters - // if we are in edit or pal (for instance). Note this is temporary, as we expect to enable this all - // the time after getting edge highlighting, etc... - _enabled = _settingSwitch.get(); - auto entityTreeRenderer = DependencyManager::get().data(); connect(entityTreeRenderer, SIGNAL(mousePressOnEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createOrDestroyContextOverlay(const EntityItemID&, const PointerEvent&))); connect(entityTreeRenderer, SIGNAL(hoverEnterEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(contextOverlays_hoverEnterEntity(const EntityItemID&, const PointerEvent&))); @@ -65,40 +64,36 @@ ContextOverlayInterface::ContextOverlayInterface() { connect(_selectionScriptingInterface.data(), &SelectionScriptingInterface::selectedItemsListChanged, &_selectionToSceneHandler, &SelectionToSceneHandler::selectedItemsListChanged); } +static const uint32_t MOUSE_HW_ID = 0; static const uint32_t LEFT_HAND_HW_ID = 1; static const xColor CONTEXT_OVERLAY_COLOR = { 255, 255, 255 }; static const float CONTEXT_OVERLAY_INSIDE_DISTANCE = 1.0f; // in meters -static const float CONTEXT_OVERLAY_CLOSE_DISTANCE = 1.5f; // in meters -static const float CONTEXT_OVERLAY_CLOSE_SIZE = 0.12f; // in meters, same x and y dims -static const float CONTEXT_OVERLAY_FAR_SIZE = 0.08f; // in meters, same x and y dims -static const float CONTEXT_OVERLAY_CLOSE_OFFSET_ANGLE = 20.0f; +static const float CONTEXT_OVERLAY_SIZE = 0.09f; // in meters, same x and y dims +static const float CONTEXT_OVERLAY_OFFSET_DISTANCE = 0.1f; +static const float CONTEXT_OVERLAY_OFFSET_ANGLE = 5.0f; static const float CONTEXT_OVERLAY_UNHOVERED_ALPHA = 0.85f; static const float CONTEXT_OVERLAY_HOVERED_ALPHA = 1.0f; static const float CONTEXT_OVERLAY_UNHOVERED_PULSEMIN = 0.6f; static const float CONTEXT_OVERLAY_UNHOVERED_PULSEMAX = 1.0f; static const float CONTEXT_OVERLAY_UNHOVERED_PULSEPERIOD = 1.0f; static const float CONTEXT_OVERLAY_UNHOVERED_COLORPULSE = 1.0f; -static const float CONTEXT_OVERLAY_FAR_OFFSET = 0.1f; void ContextOverlayInterface::setEnabled(bool enabled) { - // only enable/disable if the setting in 'on'. If it is 'off', - // make sure _enabled is always false. - if (_settingSwitch.get()) { - _enabled = enabled; - } else { - _enabled = false; - } + _enabled = enabled; } bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (_enabled && event.getButton() == PointerEvent::SecondaryButton) { if (contextOverlayFilterPassed(entityItemID)) { + if (event.getID() == MOUSE_HW_ID) { + enableEntityHighlight(entityItemID); + } + qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; // Add all necessary variables to the stack EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); glm::vec3 cameraPosition = qApp->getCamera().getPosition(); - float distanceFromCameraToEntity = glm::distance(entityProperties.getPosition(), cameraPosition); glm::vec3 entityDimensions = entityProperties.getDimensions(); glm::vec3 entityPosition = entityProperties.getPosition(); glm::vec3 contextOverlayPosition = entityProperties.getPosition(); @@ -131,27 +126,22 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& // If the camera is inside the box... // ...position the Context Overlay 1 meter in front of the camera. contextOverlayPosition = cameraPosition + CONTEXT_OVERLAY_INSIDE_DISTANCE * (qApp->getCamera().getOrientation() * Vectors::FRONT); - contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_CLOSE_SIZE, CONTEXT_OVERLAY_CLOSE_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); - } else if (distanceFromCameraToEntity < CONTEXT_OVERLAY_CLOSE_DISTANCE) { - // Else if the entity is too close to the camera... - // ...rotate the Context Overlay to the right of the entity. - // This makes it easy to inspect things you're holding. - float offsetAngle = -CONTEXT_OVERLAY_CLOSE_OFFSET_ANGLE; - if (event.getID() == LEFT_HAND_HW_ID) { - offsetAngle *= -1; - } - contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, offsetAngle, 0.0f))) * (entityPosition - cameraPosition)) + cameraPosition; - contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_CLOSE_SIZE, CONTEXT_OVERLAY_CLOSE_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); + contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_SIZE, CONTEXT_OVERLAY_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); } else { - // Else, place the Context Overlay some offset away from the entity's bounding - // box in the direction of the camera. + // Rotate the Context Overlay some number of degrees offset from the entity + // along the line cast from your head to the entity's bounding box. glm::vec3 direction = glm::normalize(entityPosition - cameraPosition); float distance; BoxFace face; glm::vec3 normal; boundingBox.findRayIntersection(cameraPosition, direction, distance, face, normal); - contextOverlayPosition = (cameraPosition + direction * distance) - direction * CONTEXT_OVERLAY_FAR_OFFSET; - contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_FAR_SIZE, CONTEXT_OVERLAY_FAR_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); + float offsetAngle = -CONTEXT_OVERLAY_OFFSET_ANGLE; + if (event.getID() == LEFT_HAND_HW_ID) { + offsetAngle *= -1; + } + contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, offsetAngle, 0.0f)))) * + ((cameraPosition + direction * (distance - CONTEXT_OVERLAY_OFFSET_DISTANCE))); + contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_SIZE, CONTEXT_OVERLAY_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); } // Finally, setup and draw the Context Overlay @@ -176,6 +166,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& } } else { if (!_currentEntityWithContextOverlay.isNull()) { + disableEntityHighlight(_currentEntityWithContextOverlay); return destroyContextOverlay(_currentEntityWithContextOverlay, event); } return false; @@ -237,13 +228,13 @@ void ContextOverlayInterface::contextOverlays_hoverLeaveOverlay(const OverlayID& } void ContextOverlayInterface::contextOverlays_hoverEnterEntity(const EntityItemID& entityID, const PointerEvent& event) { - if (contextOverlayFilterPassed(entityID)) { + if (contextOverlayFilterPassed(entityID) && _enabled && event.getID() != MOUSE_HW_ID) { enableEntityHighlight(entityID); } } void ContextOverlayInterface::contextOverlays_hoverLeaveEntity(const EntityItemID& entityID, const PointerEvent& event) { - if (_currentEntityWithContextOverlay != entityID) { + if (_currentEntityWithContextOverlay != entityID && _enabled && event.getID() != MOUSE_HW_ID) { disableEntityHighlight(entityID); } } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index c14262029e..fddd1fcdb5 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -76,8 +76,6 @@ private: bool _isInMarketplaceInspectionMode { false }; - Setting::Handle _settingSwitch { "inspectionMode", false }; - void openMarketplace(); void enableEntityHighlight(const EntityItemID& entityItemID); void disableEntityHighlight(const EntityItemID& entityItemID); diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index b5b6570599..3ef291af22 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -583,29 +583,17 @@ void FBXBaker::rewriteAndBakeSceneTextures() { QString fbxTextureFileName { textureChild.properties.at(0).toByteArray() }; QFileInfo textureFileInfo { fbxTextureFileName.replace("\\", "/") }; + if (textureFileInfo.suffix() == BAKED_TEXTURE_EXT.mid(1)) { + // re-baking an FBX that already references baked textures is a fail + // so we add an error and return from here + handleError("Cannot re-bake a file that references compressed textures"); + + return; + } + + // make sure this texture points to something and isn't one we've already re-mapped if (!textureFileInfo.filePath().isEmpty()) { - - if (textureFileInfo.suffix() == BAKED_TEXTURE_EXT.mid(1)) { - // re-baking an FBX that already references baked textures is a fail - // so we add an error and return from here - handleError("Cannot re-bake a file that references compressed textures"); - - return; - } - - // construct the new baked texture file name and file path - // ensuring that the baked texture will have a unique name - // even if there was another texture with the same name at a different path - auto bakedTextureFileName = createBakedTextureFileName(textureFileInfo); - QString bakedTextureFilePath { - _bakedOutputDir + "/" + bakedTextureFileName - }; - _outputFiles.push_back(bakedTextureFilePath); - - qCDebug(model_baking).noquote() << "Re-mapping" << fbxTextureFileName - << "to" << bakedTextureFileName; - // check if this was an embedded texture we have already have in-memory content for auto textureContent = _textureContent.value(fbxTextureFileName.toLocal8Bit()); @@ -613,10 +601,29 @@ void FBXBaker::rewriteAndBakeSceneTextures() { auto urlToTexture = getTextureURL(textureFileInfo, fbxTextureFileName, !textureContent.isNull()); + QString bakedTextureFileName; + if (_remappedTexturePaths.contains(urlToTexture)) { + bakedTextureFileName = _remappedTexturePaths[urlToTexture]; + } else { + // construct the new baked texture file name and file path + // ensuring that the baked texture will have a unique name + // even if there was another texture with the same name at a different path + bakedTextureFileName = createBakedTextureFileName(textureFileInfo); + _remappedTexturePaths[urlToTexture] = bakedTextureFileName; + } + + qCDebug(model_baking).noquote() << "Re-mapping" << fbxTextureFileName + << "to" << bakedTextureFileName; + + QString bakedTextureFilePath { + _bakedOutputDir + "/" + bakedTextureFileName + }; + // write the new filename into the FBX scene textureChild.properties[0] = bakedTextureFileName.toLocal8Bit(); if (!_bakingTextures.contains(urlToTexture)) { + _outputFiles.push_back(bakedTextureFilePath); // grab the ID for this texture so we can figure out the // texture type from the loaded materials @@ -624,7 +631,7 @@ void FBXBaker::rewriteAndBakeSceneTextures() { auto textureType = textureTypes[textureID]; // bake this texture asynchronously - bakeTexture(urlToTexture, textureType, _bakedOutputDir, textureContent); + bakeTexture(urlToTexture, textureType, _bakedOutputDir, bakedTextureFileName, textureContent); } } } @@ -644,10 +651,10 @@ void FBXBaker::rewriteAndBakeSceneTextures() { } void FBXBaker::bakeTexture(const QUrl& textureURL, image::TextureUsage::Type textureType, - const QDir& outputDir, const QByteArray& textureContent) { + const QDir& outputDir, const QString& bakedFilename, const QByteArray& textureContent) { // start a bake for this texture and add it to our list to keep track of QSharedPointer bakingTexture { - new TextureBaker(textureURL, textureType, outputDir, textureContent), + new TextureBaker(textureURL, textureType, outputDir, bakedFilename, textureContent), &TextureBaker::deleteLater }; diff --git a/libraries/baking/src/FBXBaker.h b/libraries/baking/src/FBXBaker.h index 26471a29b3..ad8284bfa8 100644 --- a/libraries/baking/src/FBXBaker.h +++ b/libraries/baking/src/FBXBaker.h @@ -71,7 +71,7 @@ private: QUrl getTextureURL(const QFileInfo& textureFileInfo, QString relativeFileName, bool isEmbedded = false); void bakeTexture(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDir, - const QByteArray& textureContent = QByteArray()); + const QString& bakedFilename, const QByteArray& textureContent = QByteArray()); QUrl _fbxURL; @@ -91,6 +91,7 @@ private: QMultiHash> _bakingTextures; QHash _textureNameMatchCount; + QHash _remappedTexturePaths; TextureBakerThreadGetter _textureThreadGetter; diff --git a/libraries/baking/src/TextureBaker.cpp b/libraries/baking/src/TextureBaker.cpp index febc7ea092..1a320efabc 100644 --- a/libraries/baking/src/TextureBaker.cpp +++ b/libraries/baking/src/TextureBaker.cpp @@ -26,15 +26,19 @@ const QString BAKED_TEXTURE_EXT = ".ktx"; TextureBaker::TextureBaker(const QUrl& textureURL, image::TextureUsage::Type textureType, - const QDir& outputDirectory, const QByteArray& textureContent) : + const QDir& outputDirectory, const QString& bakedFilename, + const QByteArray& textureContent) : _textureURL(textureURL), _originalTexture(textureContent), _textureType(textureType), - _outputDirectory(outputDirectory) + _outputDirectory(outputDirectory), + _bakedTextureFileName(bakedFilename) { - // figure out the baked texture filename - auto originalFilename = textureURL.fileName(); - _bakedTextureFileName = originalFilename.left(originalFilename.lastIndexOf('.')) + BAKED_TEXTURE_EXT; + if (bakedFilename.isEmpty()) { + // figure out the baked texture filename + auto originalFilename = textureURL.fileName(); + _bakedTextureFileName = originalFilename.left(originalFilename.lastIndexOf('.')) + BAKED_TEXTURE_EXT; + } } void TextureBaker::bake() { diff --git a/libraries/baking/src/TextureBaker.h b/libraries/baking/src/TextureBaker.h index e5bd41cf0d..b2e86b2b5b 100644 --- a/libraries/baking/src/TextureBaker.h +++ b/libraries/baking/src/TextureBaker.h @@ -28,7 +28,8 @@ class TextureBaker : public Baker { public: TextureBaker(const QUrl& textureURL, image::TextureUsage::Type textureType, - const QDir& outputDirectory, const QByteArray& textureContent = QByteArray()); + const QDir& outputDirectory, const QString& bakedFilename = QString(), + const QByteArray& textureContent = QByteArray()); static const QStringList getSupportedFormats(); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index a79e29f003..c29d92bae9 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -222,6 +222,16 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene _renderablesToUpdate.insert({ entityId, renderable }); } + // NOTE: Looping over all the entity renderers is likely to be a bottleneck in the future + // Currently, this is necessary because the model entity loading logic requires constant polling + // This was working fine because the entity server used to send repeated updates as your view changed, + // but with the improved entity server logic (PR 11141), updateInScene (below) would not be triggered enough + for (const auto& entry : _entitiesInScene) { + const auto& renderable = entry.second; + if (renderable) { + renderable->update(scene, transaction); + } + } if (!_renderablesToUpdate.empty()) { for (const auto& entry : _renderablesToUpdate) { const auto& renderable = entry.second; diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index 3f1e89b86c..ea514d3181 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -291,6 +291,18 @@ void EntityRenderer::updateInScene(const ScenePointer& scene, Transaction& trans }); } +void EntityRenderer::update(const ScenePointer& scene, Transaction& transaction) { + if (!isValidRenderItem()) { + return; + } + + if (!needsUpdate()) { + return; + } + + doUpdate(scene, transaction, _entity); +} + // // Internal methods // @@ -304,6 +316,11 @@ bool EntityRenderer::needsRenderUpdate() const { return needsRenderUpdateFromEntity(_entity); } +// Returns true if the item needs to have update called +bool EntityRenderer::needsUpdate() const { + return needsUpdateFromEntity(_entity); +} + // Returns true if the item in question needs to have updateInScene called because of changes in the entity bool EntityRenderer::needsRenderUpdateFromEntity(const EntityItemPointer& entity) const { bool success = false; diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 6b47ff8b1d..56cb39252f 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -49,6 +49,8 @@ public: virtual bool addToScene(const ScenePointer& scene, Transaction& transaction) final; virtual void removeFromScene(const ScenePointer& scene, Transaction& transaction); + virtual void update(const ScenePointer& scene, Transaction& transaction); + protected: virtual bool needsRenderUpdateFromEntity() const final { return needsRenderUpdateFromEntity(_entity); } virtual void onAddToScene(const EntityItemPointer& entity); @@ -71,6 +73,12 @@ protected: // Returns true if the item in question needs to have updateInScene called because of changes in the entity virtual bool needsRenderUpdateFromEntity(const EntityItemPointer& entity) const; + // Returns true if the item in question needs to have update called + virtual bool needsUpdate() const; + + // Returns true if the item in question needs to have update called because of changes in the entity + virtual bool needsUpdateFromEntity(const EntityItemPointer& entity) const { return false; } + // Will be called on the main thread from updateInScene. This can be used to fetch things like // network textures or model geometry from resource caches virtual void doRenderUpdateSynchronous(const ScenePointer& scene, Transaction& transaction, const EntityItemPointer& entity) { } @@ -80,6 +88,8 @@ protected: // data in this method if using multi-threaded rendering virtual void doRenderUpdateAsynchronous(const EntityItemPointer& entity); + virtual void doUpdate(const ScenePointer& scene, Transaction& transaction, const EntityItemPointer& entity) { } + // Called by the `render` method after `needsRenderUpdate` virtual void doRender(RenderArgs* args) = 0; @@ -148,6 +158,15 @@ protected: onRemoveFromSceneTyped(_typedEntity); } + using Parent::needsUpdateFromEntity; + // Returns true if the item in question needs to have update called because of changes in the entity + virtual bool needsUpdateFromEntity(const EntityItemPointer& entity) const override final { + if (Parent::needsUpdateFromEntity(entity)) { + return true; + } + return needsUpdateFromTypedEntity(_typedEntity); + } + using Parent::needsRenderUpdateFromEntity; // Returns true if the item in question needs to have updateInScene called because of changes in the entity virtual bool needsRenderUpdateFromEntity(const EntityItemPointer& entity) const override final { @@ -162,6 +181,11 @@ protected: doRenderUpdateSynchronousTyped(scene, transaction, _typedEntity); } + virtual void doUpdate(const ScenePointer& scene, Transaction& transaction, const EntityItemPointer& entity) override final { + Parent::doUpdate(scene, transaction, entity); + doUpdateTyped(scene, transaction, _typedEntity); + } + virtual void doRenderUpdateAsynchronous(const EntityItemPointer& entity) override final { Parent::doRenderUpdateAsynchronous(entity); doRenderUpdateAsynchronousTyped(_typedEntity); @@ -170,6 +194,8 @@ protected: virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const { return false; } virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) { } virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) { } + virtual bool needsUpdateFromTypedEntity(const TypedEntityPointer& entity) const { return false; } + virtual void doUpdateTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) { } virtual void onAddToSceneTyped(const TypedEntityPointer& entity) { } virtual void onRemoveFromSceneTyped(const TypedEntityPointer& entity) { } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 2508b598af..799a84aaee 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -904,7 +904,7 @@ using namespace render; using namespace render::entities; ItemKey ModelEntityRenderer::getKey() { - return ItemKey::Builder::opaqueShape().withTypeMeta(); + return ItemKey::Builder().withTypeMeta(); } uint32_t ModelEntityRenderer::metaFetchMetaSubItems(ItemIDs& subItems) { @@ -1026,12 +1026,16 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { entity->copyAnimationJointDataToModel(); } -bool ModelEntityRenderer::needsRenderUpdate() const { +bool ModelEntityRenderer::needsUpdate() const { ModelPointer model; withReadLock([&] { model = _model; }); + if (_modelJustLoaded) { + return true; + } + if (model) { if (_needsJointSimulation || _moving || _animating) { return true; @@ -1057,10 +1061,10 @@ bool ModelEntityRenderer::needsRenderUpdate() const { return true; } } - return Parent::needsRenderUpdate(); + return Parent::needsUpdate(); } -bool ModelEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const { +bool ModelEntityRenderer::needsUpdateFromTypedEntity(const TypedEntityPointer& entity) const { if (resultWithReadLock([&] { if (entity->hasModel() != _hasModel) { return true; @@ -1122,7 +1126,7 @@ bool ModelEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoin return false; } -void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) { +void ModelEntityRenderer::doUpdateTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) { if (_hasModel != entity->hasModel()) { _hasModel = entity->hasModel(); } @@ -1148,9 +1152,11 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce return; } + _modelJustLoaded = false; // Check for addition if (_hasModel && !(bool)_model) { model = std::make_shared(nullptr, entity.get()); + connect(model.get(), &Model::setURLFinished, this, &ModelEntityRenderer::handleModelLoaded); model->setLoadingPriority(EntityTreeRenderer::getEntityLoadingPriority(*entity)); model->init(); entity->setModel(model); @@ -1175,8 +1181,8 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce properties.setLastEdited(usecTimestampNow()); // we must set the edit time since we're editing it auto extents = model->getMeshExtents(); properties.setDimensions(extents.maximum - extents.minimum); - qCDebug(entitiesrenderer) << "Autoresizing" - << (!entity->getName().isEmpty() ? entity->getName() : entity->getModelURL()) + qCDebug(entitiesrenderer) << "Autoresizing" + << (!entity->getName().isEmpty() ? entity->getName() : entity->getModelURL()) << "from mesh extents"; QMetaObject::invokeMethod(DependencyManager::get().data(), "editEntity", @@ -1203,7 +1209,6 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce entity->updateModelBounds(); } - if (model->isVisible() != _visible) { // FIXME: this seems like it could be optimized if we tracked our last known visible state in // the renderable item. As it stands now the model checks it's visible/invisible state @@ -1234,7 +1239,6 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce }); } - if (_animating) { if (!jointsMapped()) { mapJoints(entity, model->getJointNames()); @@ -1243,6 +1247,12 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce } } +void ModelEntityRenderer::handleModelLoaded(bool success) { + if (success) { + _modelJustLoaded = true; + } +} + // NOTE: this only renders the "meta" portion of the Model, namely it renders debugging items void ModelEntityRenderer::doRender(RenderArgs* args) { PROFILE_RANGE(render_detail, "MetaModelRender"); diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index b9c751761d..ad0afeee0a 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -138,10 +138,10 @@ protected: virtual ItemKey getKey() override; virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) override; - virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override; - virtual bool needsRenderUpdate() const override; + virtual bool needsUpdateFromTypedEntity(const TypedEntityPointer& entity) const override; + virtual bool needsUpdate() const override; virtual void doRender(RenderArgs* args) override; - virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override; + virtual void doUpdateTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override; private: void animate(const TypedEntityPointer& entity); @@ -151,6 +151,7 @@ private: // Transparency is handled in ModelMeshPartPayload virtual bool isTransparent() const override { return false; } + bool _modelJustLoaded { false }; bool _hasModel { false }; ::ModelPointer _model; GeometryResource::Pointer _compoundShapeResource; @@ -178,6 +179,9 @@ private: bool _animating { false }; uint64_t _lastAnimated { 0 }; float _currentFrame { 0 }; + +private slots: + void handleModelLoaded(bool success); }; } } // namespace diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index 8757bcbb0f..f4e4c0ea8f 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -102,7 +102,7 @@ void TextEntityRenderer::doRender(RenderArgs* args) { glm::quat orientation = glm::quat(glm::vec3(0.0f, yawRotation, 0.0f)); transformToTopLeft.setRotation(orientation); } - transformToTopLeft.postTranslate(glm::vec3(-0.5f, 0.5f, 0.0f)); // Go to the top left + transformToTopLeft.postTranslate(dimensions * glm::vec3(-0.5f, 0.5f, 0.0f)); // Go to the top left transformToTopLeft.setScale(1.0f); // Use a scale of one so that the text is not deformed batch.setModelTransform(transformToTopLeft); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 8b5feb15f0..5dc75dad08 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -140,6 +140,11 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene _webSurface->resize(QSize(windowSize.x, windowSize.y)); } +void WebEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) { + Parent::doRenderUpdateAsynchronousTyped(entity); + _modelTransform.postScale(entity->getDimensions()); +} + void WebEntityRenderer::doRender(RenderArgs* args) { withWriteLock([&] { _lastRenderTime = usecTimestampNow(); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index 4b7e7e25a1..a67eb39670 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -29,6 +29,7 @@ protected: virtual bool needsRenderUpdate() const override; virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override; virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override; + virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override; virtual void doRender(RenderArgs* args) override; virtual bool isTransparent() const override; diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 468d22ce9e..74c8d06736 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -464,7 +464,7 @@ void GeometryResourceWatcher::setResource(GeometryResource::Pointer resource) { _resource = resource; if (_resource) { if (_resource->isLoaded()) { - _geometryRef = std::make_shared(*_resource); + resourceFinished(true); } else { startWatching(); } diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index d2da521768..32e764bd10 100755 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -120,7 +120,7 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) { _dynamicsWorld->addRigidBody(_rigidBody, collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR); _dynamicsWorld->addAction(this); // restore gravity settings because adding an object to the world overwrites its gravity setting - _rigidBody->setGravity(_gravity * _currentUp); + _rigidBody->setGravity(_currentGravity * _currentUp); btCollisionShape* shape = _rigidBody->getCollisionShape(); assert(shape && shape->getShapeType() == CONVEX_HULL_SHAPE_PROXYTYPE); _ghost.setCharacterShape(static_cast(shape)); @@ -302,7 +302,7 @@ void CharacterController::playerStep(btCollisionWorld* collisionWorld, btScalar // add minimum velocity to counteract gravity's displacement during one step // Note: the 0.5 factor comes from the fact that we really want the // average velocity contribution from gravity during the step - stepUpSpeed -= 0.5f * _gravity * timeToStep; // remember: _gravity is negative scalar + stepUpSpeed -= 0.5f * _currentGravity * timeToStep; // remember: _gravity is negative scalar btScalar vDotUp = velocity.dot(_currentUp); if (vDotUp < stepUpSpeed) { @@ -351,6 +351,28 @@ static const char* stateToStr(CharacterController::State state) { } #endif // #ifdef DEBUG_STATE_CHANGE +void CharacterController::updateCurrentGravity() { + int16_t collisionGroup = computeCollisionGroup(); + if (_state == State::Hover || collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS) { + _currentGravity = 0.0f; + } else { + _currentGravity = _gravity; + } + if (_rigidBody) { + _rigidBody->setGravity(_currentGravity * _currentUp); + } +} + + +void CharacterController::setGravity(float gravity) { + _gravity = gravity; + updateCurrentGravity(); +} + +float CharacterController::getGravity() { + return _gravity; +} + #ifdef DEBUG_STATE_CHANGE void CharacterController::setState(State desiredState, const char* reason) { #else @@ -365,19 +387,7 @@ void CharacterController::setState(State desiredState) { qCDebug(physics) << "CharacterController::setState" << stateToStr(desiredState) << "from" << stateToStr(_state) << "," << reason; #endif _state = desiredState; - updateGravity(); - } -} - -void CharacterController::updateGravity() { - int16_t collisionGroup = computeCollisionGroup(); - if (_state == State::Hover || collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS) { - _gravity = 0.0f; - } else { - _gravity = DEFAULT_AVATAR_GRAVITY; - } - if (_rigidBody) { - _rigidBody->setGravity(_gravity * _currentUp); + updateCurrentGravity(); } } @@ -436,14 +446,14 @@ void CharacterController::handleChangedCollisionGroup() { _dynamicsWorld->addRigidBody(_rigidBody, collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR); } _pendingFlags &= ~PENDING_FLAG_UPDATE_COLLISION_GROUP; - updateGravity(); + updateCurrentGravity(); } } void CharacterController::updateUpAxis(const glm::quat& rotation) { _currentUp = quatRotate(glmToBullet(rotation), LOCAL_UP_AXIS); if (_rigidBody) { - _rigidBody->setGravity(_gravity * _currentUp); + _rigidBody->setGravity(_currentGravity * _currentUp); } } diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index 585eb7d3ed..0f97cc7c16 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -24,6 +24,7 @@ #include "BulletUtil.h" #include "CharacterGhostObject.h" +#include "AvatarConstants.h" const uint32_t PENDING_FLAG_ADD_TO_SIMULATION = 1U << 0; const uint32_t PENDING_FLAG_REMOVE_FROM_SIMULATION = 1U << 1; @@ -42,15 +43,18 @@ const btScalar MAX_CHARACTER_MOTOR_TIMESCALE = 60.0f; // one minute const btScalar MIN_CHARACTER_MOTOR_TIMESCALE = 0.05f; class CharacterController : public btCharacterControllerInterface { + public: CharacterController(); virtual ~CharacterController(); - bool needsRemoval() const; bool needsAddition() const; virtual void setDynamicsWorld(btDynamicsWorld* world); btCollisionObject* getCollisionObject() { return _rigidBody; } + void setGravity(float gravity); + float getGravity(); + virtual void updateShapeIfNecessary() = 0; // overrides from btCharacterControllerInterface @@ -131,7 +135,7 @@ protected: #endif virtual void updateMassProperties() = 0; - void updateGravity(); + void updateCurrentGravity(); void updateUpAxis(const glm::quat& rotation); bool checkForSupport(btCollisionWorld* collisionWorld); @@ -184,7 +188,8 @@ protected: bool _stepUpEnabled { true }; bool _hasSupport; - btScalar _gravity { 0.0f }; + btScalar _currentGravity { 0.0f }; + btScalar _gravity { DEFAULT_AVATAR_GRAVITY }; btScalar _followTime; btVector3 _followLinearDisplacement; diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 517fe97dba..3f57a1779a 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -321,17 +321,25 @@ template <> void payloadRender(const ModelMeshPartPayload::Pointer& payload, Ren } -ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int _meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform) : - _meshIndex(_meshIndex), +ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform) : + _meshIndex(meshIndex), _shapeID(shapeIndex) { assert(model && model->isLoaded()); _model = model; auto& modelMesh = model->getGeometry()->getMeshes().at(_meshIndex); + const Model::MeshState& state = model->getMeshState(_meshIndex); updateMeshPart(modelMesh, partIndex); + computeAdjustedLocalBound(state.clusterMatrices); updateTransform(transform, offsetTransform); + Transform renderTransform = transform; + if (state.clusterMatrices.size() == 1) { + renderTransform = transform.worldTransform(Transform(state.clusterMatrices[0])); + } + updateTransformForSkinnedMesh(renderTransform, transform, state.clusterBuffer); + initCache(); } diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 42bb91ce94..9948a8bddd 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -209,11 +209,6 @@ void Model::updateRenderItems() { return; } - glm::vec3 scale = getScale(); - if (_collisionGeometry) { - // _collisionGeometry is already scaled - scale = glm::vec3(1.0f); - } _needsUpdateClusterMatrices = true; _renderItemsNeedUpdate = false; @@ -221,7 +216,7 @@ void Model::updateRenderItems() { // the application will ensure only the last lambda is actually invoked. void* key = (void*)this; std::weak_ptr weakSelf = shared_from_this(); - AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [weakSelf, scale]() { + AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [weakSelf]() { // do nothing, if the model has already been destroyed. auto self = weakSelf.lock(); @@ -1219,6 +1214,7 @@ const render::ItemIDs& Model::fetchRenderItemIDs() const { } void Model::createRenderItemSet() { + updateClusterMatrices(); if (_collisionGeometry) { if (_collisionRenderItems.empty()) { createCollisionRenderItemSet(); @@ -1269,7 +1265,6 @@ void Model::createVisibleRenderItemSet() { shapeID++; } } - computeMeshPartLocalBounds(); } void Model::createCollisionRenderItemSet() { diff --git a/libraries/render-utils/src/OutlineEffect.h b/libraries/render-utils/src/OutlineEffect.h index 36dc59f29e..f88092429f 100644 --- a/libraries/render-utils/src/OutlineEffect.h +++ b/libraries/render-utils/src/OutlineEffect.h @@ -81,10 +81,10 @@ public: float getColorB() const { return color.b; } glm::vec3 color{ 1.f, 0.7f, 0.2f }; - float width{ 3.f }; - float intensity{ 1.f }; - float fillOpacityUnoccluded{ 0.35f }; - float fillOpacityOccluded{ 0.1f }; + float width{ 2.0f }; + float intensity{ 0.9f }; + float fillOpacityUnoccluded{ 0.0f }; + float fillOpacityOccluded{ 0.0f }; bool glow{ false }; signals: diff --git a/libraries/render-utils/src/glowLine.slf b/libraries/render-utils/src/glowLine.slf index c0af97930a..580a49dd3e 100644 --- a/libraries/render-utils/src/glowLine.slf +++ b/libraries/render-utils/src/glowLine.slf @@ -9,7 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -layout(location = 0) in vec4 inColor; +in vec4 _color; +in float distanceFromCenter; out vec4 _fragColor; @@ -17,10 +18,10 @@ void main(void) { // The incoming value actually ranges from -1 to 1, so modify it // so that it goes from 0 -> 1 -> 0 with the solid alpha being at // the center of the line - float alpha = 1.0 - abs(inColor.a); + float alpha = 1.0 - abs(distanceFromCenter); // Convert from a linear alpha curve to a sharp peaked one - alpha = pow(alpha, 10); + alpha = _color.a * pow(alpha, 10); // Drop everything where the curve falls off to nearly nothing if (alpha <= 0.05) { @@ -28,6 +29,5 @@ void main(void) { } // Emit the color - _fragColor = vec4(inColor.rgb, alpha); - return; + _fragColor = vec4(_color.rgb, alpha); } diff --git a/libraries/render-utils/src/glowLine.slv b/libraries/render-utils/src/glowLine.slv index e856edc787..fd3a85d254 100644 --- a/libraries/render-utils/src/glowLine.slv +++ b/libraries/render-utils/src/glowLine.slv @@ -18,7 +18,9 @@ layout(std140) uniform lineData { vec4 color; }; -layout(location = 0) out vec4 _color; +out vec4 _color; +// the distance from the center in 'quad space' +out float distanceFromCenter; void main(void) { _color = color; @@ -45,11 +47,10 @@ void main(void) { // Add or subtract the orthogonal vector based on a different vertex ID // calculation if (gl_VertexID < 2) { - // Use the alpha channel to store the distance from the center in 'quad space' - _color.a = -1.0; + distanceFromCenter = -1.0; eye.xyz -= orthogonal; } else { - _color.a = 1.0; + distanceFromCenter = 1.0; eye.xyz += orthogonal; } diff --git a/libraries/render-utils/src/simple.slf b/libraries/render-utils/src/simple.slf index 228560f394..0dd10b8e1e 100644 --- a/libraries/render-utils/src/simple.slf +++ b/libraries/render-utils/src/simple.slf @@ -75,7 +75,7 @@ void main(void) { max(0, 1.0 - shininess / 128.0), DEFAULT_METALLIC, specular, - specular); + vec3(clamp(emissiveAmount, 0.0, 1.0))); } else { packDeferredFragment( normal, diff --git a/libraries/script-engine/src/RecordingScriptingInterface.cpp b/libraries/script-engine/src/RecordingScriptingInterface.cpp index b51e9cd529..55895e31a4 100644 --- a/libraries/script-engine/src/RecordingScriptingInterface.cpp +++ b/libraries/script-engine/src/RecordingScriptingInterface.cpp @@ -54,9 +54,24 @@ float RecordingScriptingInterface::playerLength() const { return _player->length(); } +void RecordingScriptingInterface::playClip(NetworkClipLoaderPointer clipLoader, const QString& url, QScriptValue callback) { + _player->queueClip(clipLoader->getClip()); + + if (callback.isFunction()) { + QScriptValueList args { true, url }; + callback.call(_scriptEngine->globalObject(), args); + } +} + void RecordingScriptingInterface::loadRecording(const QString& url, QScriptValue callback) { auto clipLoader = DependencyManager::get()->getClipLoader(url); + if (clipLoader->isLoaded()) { + qCDebug(scriptengine) << "Recording already loaded from" << url; + playClip(clipLoader, url, callback); + return; + } + // hold a strong pointer to the loading clip so that it has a chance to load _clipLoaders.insert(clipLoader); @@ -69,12 +84,7 @@ void RecordingScriptingInterface::loadRecording(const QString& url, QScriptValue if (auto clipLoader = weakClipLoader.toStrongRef()) { qCDebug(scriptengine) << "Loaded recording from" << url; - _player->queueClip(clipLoader->getClip()); - - if (callback.isFunction()) { - QScriptValueList args { true, url }; - callback.call(_scriptEngine->globalObject(), args); - } + playClip(clipLoader, url, callback); // drop our strong pointer to this clip so it is cleaned up _clipLoaders.remove(clipLoader); diff --git a/libraries/script-engine/src/RecordingScriptingInterface.h b/libraries/script-engine/src/RecordingScriptingInterface.h index bc0b019251..22e4d30830 100644 --- a/libraries/script-engine/src/RecordingScriptingInterface.h +++ b/libraries/script-engine/src/RecordingScriptingInterface.h @@ -88,6 +88,9 @@ protected: QSharedPointer _scriptEngine; QSet _clipLoaders; + +private: + void playClip(recording::NetworkClipLoaderPointer clipLoader, const QString& url, QScriptValue callback); }; #endif // hifi_RecordingScriptingInterface_h diff --git a/libraries/shared/src/PointerEvent.cpp b/libraries/shared/src/PointerEvent.cpp index 7ec5e78b9f..e35832391d 100644 --- a/libraries/shared/src/PointerEvent.cpp +++ b/libraries/shared/src/PointerEvent.cpp @@ -77,13 +77,13 @@ QScriptValue PointerEvent::toScriptValue(QScriptEngine* engine, const PointerEve normal.setProperty("x", event._normal.x); normal.setProperty("y", event._normal.y); normal.setProperty("z", event._normal.z); - obj.setProperty("pos3D", normal); + obj.setProperty("normal", normal); QScriptValue direction = engine->newObject(); direction.setProperty("x", event._direction.x); direction.setProperty("y", event._direction.y); direction.setProperty("z", event._direction.z); - obj.setProperty("pos3D", direction); + obj.setProperty("direction", direction); bool isPrimaryButton = false; bool isSecondaryButton = false; diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index faf7933d1a..071ccd46b1 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -211,12 +211,12 @@ class UrlHandler : public QObject { public: Q_INVOKABLE bool canHandleUrl(const QString& url) { static auto handler = dynamic_cast(qApp); - return handler->canAcceptURL(url); + return handler && handler->canAcceptURL(url); } Q_INVOKABLE bool handleUrl(const QString& url) { static auto handler = dynamic_cast(qApp); - return handler->acceptURL(url); + return handler && handler->acceptURL(url); } }; diff --git a/libraries/ui/src/ui/types/RequestFilters.cpp b/libraries/ui/src/ui/types/RequestFilters.cpp index 6ef3effa4c..233a9458fe 100644 --- a/libraries/ui/src/ui/types/RequestFilters.cpp +++ b/libraries/ui/src/ui/types/RequestFilters.cpp @@ -61,7 +61,7 @@ void RequestFilters::interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info) // During the period in which we have HFC commerce in the system, but not applied everywhere: const QString tokenStringCommerce{ "Chrome/48.0 (HighFidelityInterface WithHFC)" }; - static Setting::Handle _settingSwitch{ "inspectionMode", false }; + static Setting::Handle _settingSwitch{ "commerce", false }; bool isMoney = _settingSwitch.get(); const QString tokenString = !isAuthable ? tokenStringMobile : (isMoney ? tokenStringCommerce : tokenStringMetaverse); diff --git a/scripts/developer/tests/gravityScript.js b/scripts/developer/tests/gravityScript.js new file mode 100644 index 0000000000..3468de72c3 --- /dev/null +++ b/scripts/developer/tests/gravityScript.js @@ -0,0 +1,45 @@ +// +// Gravity Script 1.0 +// ************ +// +// Created by Cain Kilgore on 9/14/2017 + +// Javascript for the Gravity Modifier Implementation to test +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +function menuParameters(menuNameSelection, menuItemNameSelection) { + Menu.addMenuItem({ + menuName: menuNameSelection, + menuItemName: menuItemNameSelection, + isCheckable: false + }); +} + +function setupMenu() { + if (!Menu.menuExists("Gravity")) { + Menu.addMenu("Gravity"); + for (var i = -5; i <= 5; i++) { + menuParameters("Gravity", i); + } + } +} + +function menuItemEvent(menuItem) { + for (var i = -5; i <= 5; i++) { + if (menuItem == i) { + MyAvatar.setGravity(i); + } + } +} + +function onScriptEnding() { + Menu.removeMenu("Gravity"); +} + +setupMenu(); +Menu.menuItemEvent.connect(menuItemEvent); +Script.scriptEnding.connect(onScriptEnding); diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index 5a668a3d6e..107160154a 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -131,7 +131,7 @@ var button; var buttonName = "WALLET"; var tablet = null; - var walletEnabled = Settings.getValue("inspectionMode", false); + var walletEnabled = Settings.getValue("commerce", false); function startup() { if (walletEnabled) { tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index eb73b0f908..d2b5f92fde 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -148,7 +148,9 @@ Script.include("/~/system/libraries/controllers.js"); if (mode === "full") { var fullEndToEdit = PICK_WITH_HAND_RAY ? this.fullEnd : fullEnd; fullEndToEdit.dimensions = dim; - LaserPointers.editRenderState(laserPointerID, mode, {path: fullPath, end: fullEndToEdit}); + LaserPointers.editRenderState(laserPointerID, mode, { path: fullPath, end: fullEndToEdit }); + this.contextOverlayTimer = false; + this.destroyContextOverlay(); } else if (mode === "half") { var halfEndToEdit = PICK_WITH_HAND_RAY ? this.halfEnd : halfEnd; halfEndToEdit.dimensions = dim; diff --git a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js index e08b61dbd5..39e9371931 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js @@ -44,10 +44,6 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); return (this.hand === RIGHT_HAND) ? leftNearParentingGrabEntity : rightNearParentingGrabEntity; }; - this.otherHandIsParent = function(props) { - return this.getOtherModule().thisHandIsParent(props); - }; - this.thisHandIsParent = function(props) { if (props.parentID !== MyAvatar.sessionUUID && props.parentID !== AVATAR_SELF_ID) { return false; @@ -99,12 +95,6 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); // this should never happen, but if it does, don't set previous parent to be this hand. // this.previousParentID[targetProps.id] = NULL; // this.previousParentJointIndex[targetProps.id] = -1; - } else if (this.otherHandIsParent(targetProps)) { - // the other hand is parent. Steal the object and information - var otherModule = this.getOtherModule(); - this.previousParentID[targetProps.id] = otherModule.previousParentID[targetProps.id]; - this.previousParentJointIndex[targetProps.id] = otherModule.previousParentJointIndex[targetProps.id]; - otherModule.endNearParentingGrabEntity(); } else { this.previousParentID[targetProps.id] = targetProps.parentID; this.previousParentJointIndex[targetProps.id] = targetProps.parentJointIndex; diff --git a/scripts/system/controllers/controllerModules/overlayLaserInput.js b/scripts/system/controllers/controllerModules/overlayLaserInput.js index d67672ca7c..7dace85ec4 100644 --- a/scripts/system/controllers/controllerModules/overlayLaserInput.js +++ b/scripts/system/controllers/controllerModules/overlayLaserInput.js @@ -298,6 +298,9 @@ Script.include("/~/system/libraries/controllers.js"); var intersection = controllerData.rayPicks[this.hand]; var offOverlay = (intersection.type !== RayPick.INTERSECTED_OVERLAY); var triggerOff = (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE); + if (triggerOff) { + this.deleteContextOverlay(); + } var grabbingOverlay = this.grabModuleWantsNearbyOverlay(controllerData); return offOverlay || grabbingOverlay || triggerOff; }; @@ -308,7 +311,6 @@ Script.include("/~/system/libraries/controllers.js"); this.laserPressExit(); this.laserPressingTarget = false; } - this.deleteContextOverlay(); this.relinquishTouchFocus(); this.reset(); this.updateLaserPointer(); diff --git a/scripts/system/controllers/controllerModules/scaleAvatar.js b/scripts/system/controllers/controllerModules/scaleAvatar.js index 05804c967b..fc28f4a00f 100644 --- a/scripts/system/controllers/controllerModules/scaleAvatar.js +++ b/scripts/system/controllers/controllerModules/scaleAvatar.js @@ -1,4 +1,4 @@ -// handControllerGrab.js +// scaleAvatar.js // // Created by Dante Ruiz on 9/11/17 // @@ -80,4 +80,5 @@ dispatcherUtils.disableDispatcherModule("LeftScaleAvatar"); dispatcherUtils.disableDispatcherModule("RightScaleAvatar"); }; + Script.scriptEnding.connect(this.cleanup); })(); diff --git a/scripts/system/controllers/controllerModules/scaleEntity.js b/scripts/system/controllers/controllerModules/scaleEntity.js new file mode 100644 index 0000000000..79b1d18db9 --- /dev/null +++ b/scripts/system/controllers/controllerModules/scaleEntity.js @@ -0,0 +1,106 @@ +// scaleEntity.js +// +// Created by Dante Ruiz on 9/18/17 +// +// Grabs physically moveable entities with hydra-like controllers; it works for either near or far objects. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +/* global Script, Vec3, MyAvatar, RIGHT_HAND */ +/* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ + +(function() { + var dispatcherUtils = Script.require("/~/system/libraries/controllerDispatcherUtils.js"); + + function ScaleEntity(hand) { + this.hand = hand; + this.grabbedThingID = false; + this.scalingStartDistance = false; + this.scalingStartDimensions = false; + + this.parameters = dispatcherUtils.makeDispatcherModuleParameters( + 120, + this.hand === RIGHT_HAND ? ["rightHandTrigger"] : ["leftHandTrigger"], + [], + 100 + ); + + this.otherHand = function() { + return this.hand === dispatcherUtils.RIGHT_HAND ? dispatcherUtils.LEFT_HAND : dispatcherUtils.RIGHT_HAND; + }; + + this.otherModule = function() { + return this.hand === dispatcherUtils.RIGHT_HAND ? leftScaleEntity : rightScaleEntity; + }; + + this.bumperPressed = function(controllerData) { + return ( controllerData.secondaryValues[this.hand] > dispatcherUtils.BUMPER_ON_VALUE); + }; + + this.getTargetProps = function(controllerData) { + // nearbyEntityProperties is already sorted by length from controller + var nearbyEntityProperties = controllerData.nearbyEntityProperties[this.hand]; + var sensorScaleFactor = MyAvatar.sensorToWorldScale; + for (var i = 0; i < nearbyEntityProperties.length; i++) { + var props = nearbyEntityProperties[i]; + var handPosition = controllerData.controllerLocations[this.hand].position; + var distance = Vec3.distance(props.position, handPosition); + if (distance > dispatcherUtils.NEAR_GRAB_RADIUS * sensorScaleFactor) { + continue; + } + if ((dispatcherUtils.entityIsGrabbable(props) || + dispatcherUtils.propsArePhysical(props)) && !props.locked) { + return props; + } + } + return null; + }; + + this.isReady = function(controllerData) { + var otherModule = this.otherModule(); + if (this.bumperPressed(controllerData) && otherModule.bumperPressed(controllerData)) { + var thisHandTargetProps = this.getTargetProps(controllerData); + var otherHandTargetProps = otherModule.getTargetProps(controllerData); + if (thisHandTargetProps && otherHandTargetProps) { + if (thisHandTargetProps.id === otherHandTargetProps.id) { + this.grabbedThingID = thisHandTargetProps.id; + this.scalingStartDistance = Vec3.length(Vec3.subtract(controllerData.controllerLocations[this.hand].position, + controllerData.controllerLocations[this.otherHand()].position)); + this.scalingStartDimensions = thisHandTargetProps.dimensions; + return dispatcherUtils.makeRunningValues(true, [], []); + } + } + } + return dispatcherUtils.makeRunningValues(false, [], []); + }; + + this.run = function(controllerData) { + var otherModule = this.otherModule(); + if (this.bumperPressed(controllerData) && otherModule.bumperPressed(controllerData)) { + if (this.hand === dispatcherUtils.RIGHT_HAND) { + var scalingCurrentDistance = + Vec3.length(Vec3.subtract(controllerData.controllerLocations[this.hand].position, + controllerData.controllerLocations[this.otherHand()].position)); + var currentRescale = scalingCurrentDistance / this.scalingStartDistance; + var newDimensions = Vec3.multiply(currentRescale, this.scalingStartDimensions); + Entities.editEntity(this.grabbedThingID, { dimensions: newDimensions }); + } + return dispatcherUtils.makeRunningValues(true, [], []); + } + return dispatcherUtils.makeRunningValues(false, [], []); + }; + } + + var leftScaleEntity = new ScaleEntity(dispatcherUtils.LEFT_HAND); + var rightScaleEntity = new ScaleEntity(dispatcherUtils.RIGHT_HAND); + + dispatcherUtils.enableDispatcherModule("LeftScaleEntity", leftScaleEntity); + dispatcherUtils.enableDispatcherModule("RightScaleEntity", rightScaleEntity); + + this.cleanup = function() { + dispatcherUtils.disableDispatcherModule("LeftScaleEntity"); + dispatcherUtils.disableDispatcherModule("RightScaleEntity"); + }; + Script.scriptEnding.connect(this.cleanup); +})(); diff --git a/scripts/system/controllers/controllerScripts.js b/scripts/system/controllers/controllerScripts.js index e8b07c623d..6b140173b8 100644 --- a/scripts/system/controllers/controllerScripts.js +++ b/scripts/system/controllers/controllerScripts.js @@ -29,7 +29,8 @@ var CONTOLLER_SCRIPTS = [ "controllerModules/disableOtherModule.js", "controllerModules/farTrigger.js", "controllerModules/teleport.js", - "controllerModules/scaleAvatar.js" + "controllerModules/scaleAvatar.js", + "controllerModules/scaleEntity.js" ]; var DEBUG_MENU_ITEM = "Debug defaultScripts.js"; diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 84c26d482b..138e3a3956 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -450,7 +450,7 @@ var parsedJsonMessage = JSON.parse(message); if (parsedJsonMessage.type === "marketplaces") { - if (parsedJsonMessage.action === "inspectionModeSetting") { + if (parsedJsonMessage.action === "commerceSetting") { confirmAllPurchases = !!parsedJsonMessage.data; injectCode(); } @@ -458,7 +458,7 @@ } }); - // Request inspection mode setting + // Request commerce setting // Code is injected into the webpage after the setting comes back. EventBridge.emitWebEvent(JSON.stringify({ type: "REQUEST_SETTING" diff --git a/scripts/system/libraries/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js index 10931e4e93..f7a08f3abb 100644 --- a/scripts/system/libraries/controllerDispatcherUtils.js +++ b/scripts/system/libraries/controllerDispatcherUtils.js @@ -319,7 +319,11 @@ if (typeof module !== 'undefined') { LEFT_HAND: LEFT_HAND, RIGHT_HAND: RIGHT_HAND, BUMPER_ON_VALUE: BUMPER_ON_VALUE, + propsArePhysical: propsArePhysical, + entityIsGrabbable: entityIsGrabbable, + NEAR_GRAB_RADIUS: NEAR_GRAB_RADIUS, projectOntoOverlayXYPlane: projectOntoOverlayXYPlane, projectOntoEntityXYPlane: projectOntoEntityXYPlane + }; } diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 2eaefe7565..7ae0aa3390 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -138,8 +138,8 @@ } else if (parsedJsonMessage.type === "REQUEST_SETTING") { tablet.emitScriptEvent(JSON.stringify({ type: "marketplaces", - action: "inspectionModeSetting", - data: Settings.getValue("inspectionMode", false) + action: "commerceSetting", + data: Settings.getValue("commerce", false) })); } else if (parsedJsonMessage.type === "PURCHASES") { tablet.pushOntoStack(MARKETPLACE_PURCHASES_QML_PATH); diff --git a/tests/render-perf/CMakeLists.txt b/tests/render-perf/CMakeLists.txt index 77c8ab2177..5b83ff313b 100644 --- a/tests/render-perf/CMakeLists.txt +++ b/tests/render-perf/CMakeLists.txt @@ -12,7 +12,7 @@ setup_hifi_project(Quick Gui OpenGL) set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") # link in the shared libraries -link_hifi_libraries(shared networking model fbx ktx image octree gl gpu gpu-gl render model-networking networking render-utils entities entities-renderer animation audio avatars script-engine physics procedural midi) +link_hifi_libraries(shared networking model fbx ktx image octree gl gpu gpu-gl render model-networking networking render-utils entities entities-renderer animation audio avatars script-engine physics procedural midi ui) package_libraries_for_deployment() diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index 3be6ba2f6c..58eb4d16f9 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -42,11 +42,15 @@ #include #include +#include + #include #include #include #include +#include + #include #include #include @@ -427,6 +431,10 @@ namespace render { } } +OffscreenGLCanvas* _chromiumShareContext{ nullptr }; +Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context); + + // Create a simple OpenGL window that renders text in various ways class QTestWindow : public QWindow, public AbstractViewStateInterface { @@ -506,8 +514,6 @@ public: AbstractViewStateInterface::setInstance(this); _octree = DependencyManager::set(false, this, nullptr); _octree->init(); - // Prevent web entities from rendering - REGISTER_ENTITY_TYPE_WITH_FACTORY(Web, WebEntityItem::factory); DependencyManager::set(_octree->getTree()); auto nodeList = DependencyManager::get(); @@ -535,6 +541,23 @@ public: _renderThread.initialize(this, _initContext); _initContext.makeCurrent(); + if (nsightActive()) { + // Prevent web entities from rendering + REGISTER_ENTITY_TYPE_WITH_FACTORY(Web, WebEntityItem::factory); + } else { + _chromiumShareContext = new OffscreenGLCanvas(); + _chromiumShareContext->setObjectName("ChromiumShareContext"); + _chromiumShareContext->create(_initContext.qglContext()); + _chromiumShareContext->makeCurrent(); + qt_gl_set_global_share_context(_chromiumShareContext->getContext()); + + // Make sure all QML surfaces share the main thread GL context + OffscreenQmlSurface::setSharedContext(_initContext.qglContext()); + + _initContext.makeCurrent(); + } + + // FIXME use a wait condition QThread::msleep(1000); _renderThread.submitFrame(gpu::FramePointer()); @@ -679,6 +702,7 @@ private: _renderCount = _renderThread._presentCount.load(); update(); + _initContext.makeCurrent(); RenderArgs renderArgs(_renderThread._gpuContext, DEFAULT_OCTREE_SIZE_SCALE, 0, RenderArgs::DEFAULT_RENDER_MODE, RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE);