diff --git a/interface/resources/html/raiseAndLowerKeyboard.js b/interface/resources/html/raiseAndLowerKeyboard.js index a0aa1eb7fe..f40c0d7376 100644 --- a/interface/resources/html/raiseAndLowerKeyboard.js +++ b/interface/resources/html/raiseAndLowerKeyboard.js @@ -7,6 +7,9 @@ // // Sends messages over the EventBridge when text input is required. // + +/* global document, window, console, setTimeout, setInterval, EventBridge */ + (function () { var POLL_FREQUENCY = 500; // ms var MAX_WARNINGS = 3; @@ -37,22 +40,24 @@ } return false; } - }; + } function shouldSetNumeric() { return document.activeElement.type === "number"; - }; + } function scheduleBringToView(timeout) { - - var timer = setTimeout(function () { - clearTimeout(timer); - + setTimeout(function () { + // If the element is not visible because the keyboard has been raised over the top of it, scroll it up into view. + // If the element is not visible because the keyboard raising has moved it off screen, scroll it down into view. var elementRect = document.activeElement.getBoundingClientRect(); - var absoluteElementTop = elementRect.top + window.scrollY; - var middle = absoluteElementTop - (window.innerHeight / 2); - - window.scrollTo(0, middle); + var VISUAL_MARGIN = 3; + var delta = elementRect.y + elementRect.height + VISUAL_MARGIN - window.innerHeight; + if (delta > 0) { + window.scrollBy(0, delta); + } else if (elementRect.y < VISUAL_MARGIN) { + window.scrollBy(0, elementRect.y - VISUAL_MARGIN); + } }, timeout); } @@ -62,11 +67,13 @@ var passwordField = shouldSetPasswordField(); if (isWindowFocused && - (keyboardRaised !== window.isKeyboardRaised || numericKeyboard !== window.isNumericKeyboard || passwordField !== window.isPasswordField)) { + (keyboardRaised !== window.isKeyboardRaised || numericKeyboard !== window.isNumericKeyboard + || passwordField !== window.isPasswordField)) { if (typeof EventBridge !== "undefined" && EventBridge !== null) { EventBridge.emitWebEvent( - keyboardRaised ? ("_RAISE_KEYBOARD" + (numericKeyboard ? "_NUMERIC" : "") + (passwordField ? "_PASSWORD" : "")) : "_LOWER_KEYBOARD" + keyboardRaised ? ("_RAISE_KEYBOARD" + (numericKeyboard ? "_NUMERIC" : "") + + (passwordField ? "_PASSWORD" : "")) : "_LOWER_KEYBOARD" ); } else { if (numWarnings < MAX_WARNINGS) { @@ -77,7 +84,7 @@ if (!window.isKeyboardRaised) { scheduleBringToView(250); // Allow time for keyboard to be raised in QML. - // 2DO: should it be rather done from 'client area height changed' event? + // 2DO: should it be rather done from 'client area height changed' event? } window.isKeyboardRaised = keyboardRaised; diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index 4db98091c1..19b57354dc 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -49,6 +49,7 @@ Item { property string upgradeTitle; property bool updateAvailable: root.upgradeUrl !== "" && !root.isShowingMyItems; property bool isShowingMyItems; + property bool valid; property string originalStatusText; property string originalStatusColor; @@ -239,6 +240,7 @@ Item { width: 62; onLoaded: { + item.enabled = root.valid; item.buttonGlyphText = hifi.glyphs.gift; item.buttonText = "Gift"; item.buttonClicked = function() { @@ -463,7 +465,7 @@ Item { Item { id: statusContainer; - visible: root.purchaseStatus === "pending" || root.purchaseStatus === "invalidated" || root.numberSold > -1; + visible: root.purchaseStatus === "pending" || !root.valid || root.numberSold > -1; anchors.left: itemName.left; anchors.right: itemName.right; anchors.top: itemName.bottom; @@ -480,7 +482,7 @@ Item { text: { if (root.purchaseStatus === "pending") { "PENDING..." - } else if (root.purchaseStatus === "invalidated") { + } else if (!root.valid) { "INVALIDATED" } else if (root.numberSold > -1) { ("Sales: " + root.numberSold + "/" + (root.limitedRun === -1 ? "\u221e" : root.limitedRun)) @@ -492,7 +494,7 @@ Item { color: { if (root.purchaseStatus === "pending") { hifi.colors.blueAccent - } else if (root.purchaseStatus === "invalidated") { + } else if (!root.valid) { hifi.colors.redAccent } else { hifi.colors.baseGray @@ -506,7 +508,7 @@ Item { text: { if (root.purchaseStatus === "pending") { hifi.glyphs.question - } else if (root.purchaseStatus === "invalidated") { + } else if (!root.valid) { hifi.glyphs.question } else { "" @@ -523,7 +525,7 @@ Item { color: { if (root.purchaseStatus === "pending") { hifi.colors.blueAccent - } else if (root.purchaseStatus === "invalidated") { + } else if (!root.valid) { hifi.colors.redAccent } else { hifi.colors.baseGray @@ -538,7 +540,7 @@ Item { onClicked: { if (root.purchaseStatus === "pending") { sendToPurchases({method: 'showPendingLightbox'}); - } else if (root.purchaseStatus === "invalidated") { + } else if (!root.valid) { sendToPurchases({method: 'showInvalidatedLightbox'}); } } @@ -546,7 +548,7 @@ Item { if (root.purchaseStatus === "pending") { statusText.color = hifi.colors.blueHighlight; statusIcon.color = hifi.colors.blueHighlight; - } else if (root.purchaseStatus === "invalidated") { + } else if (!root.valid) { statusText.color = hifi.colors.redAccent; statusIcon.color = hifi.colors.redAccent; } @@ -555,7 +557,7 @@ Item { if (root.purchaseStatus === "pending") { statusText.color = hifi.colors.blueAccent; statusIcon.color = hifi.colors.blueAccent; - } else if (root.purchaseStatus === "invalidated") { + } else if (!root.valid) { statusText.color = hifi.colors.redHighlight; statusIcon.color = hifi.colors.redHighlight; } @@ -645,8 +647,8 @@ Item { width: 160; height: 40; enabled: root.hasPermissionToRezThis && - root.purchaseStatus !== "invalidated" && - MyAvatar.skeletonModelURL !== root.itemHref; + MyAvatar.skeletonModelURL !== root.itemHref && + root.valid; onHoveredChanged: { if (hovered) { diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index d79b8d09fa..8fe1ebe6c9 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -616,6 +616,7 @@ Rectangle { upgradeTitle: model.upgrade_title; itemType: model.itemType; isShowingMyItems: root.isShowingMyItems; + valid: model.valid; anchors.topMargin: 10; anchors.bottomMargin: 10; @@ -995,10 +996,6 @@ Rectangle { for (var i = 0; i < purchasesModel.count; i++) { if (purchasesModel.get(i).title.toLowerCase().indexOf(filterBar.text.toLowerCase()) !== -1) { - if (!purchasesModel.get(i).valid) { - continue; - } - if (purchasesModel.get(i).status !== "confirmed" && !root.isShowingMyItems) { tempPurchasesModel.insert(0, purchasesModel.get(i)); } else if ((root.isShowingMyItems && purchasesModel.get(i).edition_number === "0") || @@ -1055,10 +1052,6 @@ Rectangle { var currentId; for (var i = 0; i < tempPurchasesModel.count; i++) { currentId = tempPurchasesModel.get(i).id; - - if (!purchasesModel.get(i).valid) { - continue; - } filteredPurchasesModel.append(tempPurchasesModel.get(i)); filteredPurchasesModel.setProperty(i, 'cardBackVisible', false); filteredPurchasesModel.setProperty(i, 'isInstalled', ((root.installedApps).indexOf(currentId) > -1)); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 42a6cbc790..61ed5acdd2 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -735,9 +735,9 @@ extern InputPluginList getInputPlugins(); extern void saveInputPluginSettings(const InputPluginList& plugins); // Parameters used for running tests from teh command line -const QString TEST_SCRIPT_COMMAND { "--testScript" }; -const QString TEST_QUIT_WHEN_FINISHED_OPTION { "quitWhenFinished" }; -const QString TEST_SNAPSHOT_LOCATION_COMMAND { "--testSnapshotLocation" }; +const QString TEST_SCRIPT_COMMAND{ "--testScript" }; +const QString TEST_QUIT_WHEN_FINISHED_OPTION{ "quitWhenFinished" }; +const QString TEST_RESULTS_LOCATION_COMMAND{ "--testResultsLocation" }; bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { const char** constArgv = const_cast(argv); @@ -1015,22 +1015,25 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // If the URL scheme is http(s) or ftp, then use as is, else - treat it as a local file // This is done so as not break previous command line scripts - if (testScriptPath.left(URL_SCHEME_HTTP.length()) == URL_SCHEME_HTTP || testScriptPath.left(URL_SCHEME_FTP.length()) == URL_SCHEME_FTP) { + if (testScriptPath.left(URL_SCHEME_HTTP.length()) == URL_SCHEME_HTTP || + testScriptPath.left(URL_SCHEME_FTP.length()) == URL_SCHEME_FTP) { + setProperty(hifi::properties::TEST, QUrl::fromUserInput(testScriptPath)); } else if (QFileInfo(testScriptPath).exists()) { setProperty(hifi::properties::TEST, QUrl::fromLocalFile(testScriptPath)); } - // quite when finished parameter must directly follow the test script + // quite when finished parameter must directly follow the test script if ((i + 2) < args.size() && args.at(i + 2) == TEST_QUIT_WHEN_FINISHED_OPTION) { quitWhenFinished = true; } - } else if (args.at(i) == TEST_SNAPSHOT_LOCATION_COMMAND) { + } else if (args.at(i) == TEST_RESULTS_LOCATION_COMMAND) { // Set test snapshot location only if it is a writeable directory - QString pathname(args.at(i + 1)); - QFileInfo fileInfo(pathname); + QString path(args.at(i + 1)); + + QFileInfo fileInfo(path); if (fileInfo.isDir() && fileInfo.isWritable()) { - testSnapshotLocation = pathname; + TestScriptingInterface::getInstance()->setTestResultsLocation(path); } } } @@ -7588,7 +7591,9 @@ void Application::loadAvatarBrowser() const { void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio, const QString& filename) { postLambdaEvent([notify, includeAnimated, aspectRatio, filename, this] { // Get a screenshot and save it - QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio), filename, testSnapshotLocation); + QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio), filename, + TestScriptingInterface::getInstance()->getTestResultsLocation()); + // If we're not doing an animated snapshot as well... if (!includeAnimated) { // Tell the dependency manager that the capture of the still snapshot has taken place. @@ -7602,7 +7607,9 @@ void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRa void Application::takeSecondaryCameraSnapshot(const QString& filename) { postLambdaEvent([filename, this] { - QString snapshotPath = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot(), filename, testSnapshotLocation); + QString snapshotPath = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot(), filename, + TestScriptingInterface::getInstance()->getTestResultsLocation()); + emit DependencyManager::get()->stillSnapshotTaken(snapshotPath, true); }); } diff --git a/interface/src/Application.h b/interface/src/Application.h index 4946dd7ad9..17e28f0e6e 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -419,7 +419,6 @@ public slots: void updateVerboseLogging(); Q_INVOKABLE void openAndroidActivity(const QString& activityName); - private slots: void onDesktopRootItemCreated(QQuickItem* qmlContext); void onDesktopRootContextCreated(QQmlContext* qmlContext); @@ -753,7 +752,6 @@ private: std::atomic _pendingIdleEvent { true }; std::atomic _pendingRenderEvent { true }; - QString testSnapshotLocation; bool quitWhenFinished { false }; }; #endif // hifi_Application_h diff --git a/interface/src/LODManager.cpp b/interface/src/LODManager.cpp index d06ba14bcf..da1f14c450 100644 --- a/interface/src/LODManager.cpp +++ b/interface/src/LODManager.cpp @@ -70,7 +70,7 @@ void LODManager::autoAdjustLOD(float realTimeDelta) { // Note: we MUST clamp the blend to 1.0 for stability float blend = (realTimeDelta < LOD_ADJUST_RUNNING_AVG_TIMESCALE) ? realTimeDelta / LOD_ADJUST_RUNNING_AVG_TIMESCALE : 1.0f; _avgRenderTime = (1.0f - blend) * _avgRenderTime + blend * maxRenderTime; // msec - if (!_automaticLODAdjust) { + if (!_automaticLODAdjust || _avgRenderTime == 0.0f) { // early exit return; } diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 20375a71b2..be3dd705f7 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -193,7 +193,6 @@ namespace MenuOption { const QString ShowOtherLookAtVectors = "Show Other Eye Vectors"; const QString EnableLookAtSnapping = "Enable LookAt Snapping"; const QString ShowRealtimeEntityStats = "Show Realtime Entity Stats"; - const QString StandingHMDSensorMode = "Standing HMD Sensor Mode"; const QString SimulateEyeTracking = "Simulate"; const QString SMIEyeTracking = "SMI Eye Tracking"; const QString SparseTextureManagement = "Enable Sparse Texture Management"; diff --git a/interface/src/scripting/TestScriptingInterface.cpp b/interface/src/scripting/TestScriptingInterface.cpp index 9e7c0e142e..700994c517 100644 --- a/interface/src/scripting/TestScriptingInterface.cpp +++ b/interface/src/scripting/TestScriptingInterface.cpp @@ -160,3 +160,29 @@ void TestScriptingInterface::clearCaches() { qApp->reloadResourceCaches(); } +// Writes a JSON object from javascript to a file +void TestScriptingInterface::saveObject(QVariant variant, const QString& filename) { + if (_testResultsLocation.isNull()) { + return; + } + + QJsonDocument jsonDocument; + jsonDocument = QJsonDocument::fromVariant(variant); + if (jsonDocument.isNull()) { + return; + } + + QByteArray jsonData = jsonDocument.toJson(); + + // Append trailing slash if needed + if (_testResultsLocation.right(1) != "/") { + _testResultsLocation += "/"; + } + + QString filepath = QDir::cleanPath(_testResultsLocation + filename); + QFile file(filepath); + + file.open(QFile::WriteOnly); + file.write(jsonData); + file.close(); +} diff --git a/interface/src/scripting/TestScriptingInterface.h b/interface/src/scripting/TestScriptingInterface.h index 687cb41689..5666417727 100644 --- a/interface/src/scripting/TestScriptingInterface.h +++ b/interface/src/scripting/TestScriptingInterface.h @@ -18,6 +18,10 @@ class QScriptValue; class TestScriptingInterface : public QObject { Q_OBJECT +public: + void setTestResultsLocation(const QString path) { _testResultsLocation = path; } + const QString& getTestResultsLocation() { return _testResultsLocation; }; + public slots: static TestScriptingInterface* getInstance(); @@ -46,7 +50,6 @@ public slots: */ void waitIdle(); - bool waitForConnection(qint64 maxWaitMs = 10000); void wait(int milliseconds); @@ -83,8 +86,14 @@ public slots: */ void clearCaches(); + /**jsdoc + * Save a JSON object to a file in the test results location + */ + void saveObject(QVariant v, const QString& filename); + private: bool waitForCondition(qint64 maxWaitMs, std::function condition); + QString _testResultsLocation; }; -#endif // hifi_TestScriptingInterface_h +#endif // hifi_TestScriptingInterface_h diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 8adb5138f2..7e47d9e2d4 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -481,8 +481,8 @@ public slots: /**jsdoc * Gets the status of server entity script attached to an entity * @function Entities.getServerScriptStatus - * @property {Uuid} entityID - The ID of the entity to get the server entity script status for. - * @property {Entities~getServerScriptStatusCallback} callback - The function to call upon completion. + * @param {Uuid} entityID - The ID of the entity to get the server entity script status for. + * @param {Entities~getServerScriptStatusCallback} callback - The function to call upon completion. * @returns {boolean} true always. */ /**jsdoc diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 9f067e51c6..7a0ead3e0d 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -731,7 +731,9 @@ void EntityMotionState::measureBodyAcceleration() { // hence the equation for acceleration is: a = (v1 / (1 - D)^dt - v0) / dt glm::vec3 velocity = getBodyLinearVelocityGTSigma(); - _measuredAcceleration = (velocity / powf(1.0f - _body->getLinearDamping(), dt) - _lastVelocity) * invDt; + const float MIN_DAMPING_FACTOR = 0.01f; + float invDampingAttenuationFactor = 1.0f / glm::max(powf(1.0f - _body->getLinearDamping(), dt), MIN_DAMPING_FACTOR); + _measuredAcceleration = (velocity * invDampingAttenuationFactor - _lastVelocity) * invDt; _lastVelocity = velocity; if (numSubsteps > PHYSICS_ENGINE_MAX_NUM_SUBSTEPS) { // we fall in here when _lastMeasureStep is old: the body has just become active diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 69c5b3c689..fbb4bba263 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -149,9 +149,7 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con batch.setStateScissorRect(viewport); batch.setFramebuffer(fbo); - batch.clearFramebuffer( - gpu::Framebuffer::BUFFER_COLOR0 | gpu::Framebuffer::BUFFER_DEPTH, - vec4(vec3(1.0, 1.0, 1.0), 0.0), 1.0, 0, true); + batch.clearDepthFramebuffer(1.0, false); glm::mat4 projMat; Transform viewMat; @@ -232,12 +230,11 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende const auto queryResolution = setupOutput.getN(2); // Fetch and cull the items from the scene - // Enable models to not cast shadows (otherwise, models will always cast shadows) - static const auto shadowCasterFilter = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(tagBits, tagMask).withShadowCaster(); + static const auto shadowCasterReceiverFilter = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(tagBits, tagMask); - const auto fetchInput = FetchSpatialTree::Inputs(shadowCasterFilter, queryResolution).asVarying(); + const auto fetchInput = FetchSpatialTree::Inputs(shadowCasterReceiverFilter, queryResolution).asVarying(); const auto shadowSelection = task.addJob("FetchShadowTree", fetchInput); - const auto selectionInputs = FetchSpatialSelection::Inputs(shadowSelection, shadowCasterFilter).asVarying(); + const auto selectionInputs = FetchSpatialSelection::Inputs(shadowSelection, shadowCasterReceiverFilter).asVarying(); const auto shadowItems = task.addJob("FetchShadowSelection", selectionInputs); // Cull objects that are not visible in camera view. Hopefully the cull functor only performs LOD culling, not @@ -261,21 +258,22 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende char jobName[64]; sprintf(jobName, "ShadowCascadeSetup%d", i); const auto cascadeSetupOutput = task.addJob(jobName, i, _cullFunctor, tagBits, tagMask); - const auto shadowFilter = cascadeSetupOutput.getN(0); + const auto shadowRenderFilter = cascadeSetupOutput.getN(0); + const auto shadowBoundsFilter = cascadeSetupOutput.getN(1); auto antiFrustum = render::Varying(ViewFrustumPointer()); - cascadeFrustums[i] = cascadeSetupOutput.getN(1); + cascadeFrustums[i] = cascadeSetupOutput.getN(2); if (i > 1) { antiFrustum = cascadeFrustums[i - 2]; } // CPU jobs: finer grained culling - const auto cullInputs = CullShapeBounds::Inputs(sortedShapes, shadowFilter, antiFrustum).asVarying(); + const auto cullInputs = CullShapeBounds::Inputs(sortedShapes, shadowRenderFilter, shadowBoundsFilter, antiFrustum).asVarying(); const auto culledShadowItemsAndBounds = task.addJob("CullShadowCascade", cullInputs, shadowCullFunctor, RenderDetails::SHADOW); // GPU jobs: Render to shadow map sprintf(jobName, "RenderShadowMap%d", i); task.addJob(jobName, culledShadowItemsAndBounds, shapePlumber, i); - task.addJob("ShadowCascadeTeardown", shadowFilter); + task.addJob("ShadowCascadeTeardown", shadowRenderFilter); } task.addJob("ShadowTeardown", setupOutput); @@ -406,7 +404,11 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon const auto globalShadow = lightStage->getCurrentKeyShadow(); if (globalShadow && _cascadeIndexgetCascadeCount()) { - output.edit0() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(_tagBits, _tagMask).withShadowCaster(); + auto baseFilter = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(_tagBits, _tagMask); + // Second item filter is to filter items to keep in shadow frustum computation (here we need to keep shadow receivers) + output.edit1() = baseFilter; + // First item filter is to filter items to render in shadow map (so only keep casters) + output.edit0() = baseFilter.withShadowCaster(); // Set the keylight render args auto& cascade = globalShadow->getCascade(_cascadeIndex); @@ -419,10 +421,11 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon texelSize *= minTexelCount; _cullFunctor._minSquareSize = texelSize * texelSize; - output.edit1() = cascadeFrustum; + output.edit2() = cascadeFrustum; } else { output.edit0() = ItemFilter::Builder::nothing(); - output.edit1() = ViewFrustumPointer(); + output.edit1() = ItemFilter::Builder::nothing(); + output.edit2() = ViewFrustumPointer(); } } diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index 98b70c0c9f..19ffcb4234 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -118,7 +118,7 @@ private: class RenderShadowCascadeSetup { public: - using Outputs = render::VaryingSet2; + using Outputs = render::VaryingSet3; using JobModel = render::Job::ModelO; RenderShadowCascadeSetup(unsigned int cascadeIndex, RenderShadowTask::CullFunctor& cullFunctor, uint8_t tagBits = 0x00, uint8_t tagMask = 0x00) : diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index f04427540a..b5819f114f 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -368,17 +368,19 @@ void CullShapeBounds::run(const RenderContextPointer& renderContext, const Input RenderArgs* args = renderContext->args; const auto& inShapes = inputs.get0(); - const auto& filter = inputs.get1(); - const auto& antiFrustum = inputs.get2(); + const auto& cullFilter = inputs.get1(); + const auto& boundsFilter = inputs.get2(); + const auto& antiFrustum = inputs.get3(); auto& outShapes = outputs.edit0(); auto& outBounds = outputs.edit1(); outShapes.clear(); outBounds = AABox(); - if (!filter.selectsNothing()) { + if (!cullFilter.selectsNothing() || !boundsFilter.selectsNothing()) { auto& details = args->_details.edit(_detailType); Test test(_cullFunctor, args, details, antiFrustum); + auto scene = args->_scene; for (auto& inItems : inShapes) { auto key = inItems.first; @@ -393,16 +395,26 @@ void CullShapeBounds::run(const RenderContextPointer& renderContext, const Input if (antiFrustum == nullptr) { for (auto& item : inItems.second) { if (test.solidAngleTest(item.bound) && test.frustumTest(item.bound)) { - outItems->second.emplace_back(item); - outBounds += item.bound; + const auto shapeKey = scene->getItem(item.id).getKey(); + if (cullFilter.test(shapeKey)) { + outItems->second.emplace_back(item); + } + if (boundsFilter.test(shapeKey)) { + outBounds += item.bound; + } } } } else { for (auto& item : inItems.second) { if (test.solidAngleTest(item.bound) && test.frustumTest(item.bound) && test.antiFrustumTest(item.bound)) { - outItems->second.emplace_back(item); - outBounds += item.bound; - } + const auto shapeKey = scene->getItem(item.id).getKey(); + if (cullFilter.test(shapeKey)) { + outItems->second.emplace_back(item); + } + if (boundsFilter.test(shapeKey)) { + outBounds += item.bound; + } + } } } details._rendered += (int)outItems->second.size(); diff --git a/libraries/render/src/render/CullTask.h b/libraries/render/src/render/CullTask.h index 3c5a30de89..47abe8a960 100644 --- a/libraries/render/src/render/CullTask.h +++ b/libraries/render/src/render/CullTask.h @@ -110,7 +110,7 @@ namespace render { class CullShapeBounds { public: - using Inputs = render::VaryingSet3; + using Inputs = render::VaryingSet4; using Outputs = render::VaryingSet2; using JobModel = Job::ModelIO; diff --git a/libraries/script-engine/src/AssetScriptingInterface.h b/libraries/script-engine/src/AssetScriptingInterface.h index eb9a628ae3..7f7a3a68b0 100644 --- a/libraries/script-engine/src/AssetScriptingInterface.h +++ b/libraries/script-engine/src/AssetScriptingInterface.h @@ -186,36 +186,36 @@ public: /**jsdoc * @function Assets.deleteAsset - * @property {} options - * @property {} scope - * @property {} [callback = ""] + * @param {} options + * @param {} scope + * @param {} [callback = ""] */ Q_INVOKABLE void deleteAsset(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); /**jsdoc * @function Assets.resolveAsset - * @property {} options - * @property {} scope - * @property {} [callback = ""] + * @param {} options + * @param {} scope + * @param {} [callback = ""] */ Q_INVOKABLE void resolveAsset(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); /**jsdoc * @function Assets.decompressData - * @property {} options - * @property {} scope - * @property {} [callback = ""] + * @param {} options + * @param {} scope + * @param {} [callback = ""] */ Q_INVOKABLE void decompressData(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); /**jsdoc * @function Assets.compressData - * @property {} options - * @property {} scope - * @property {} [callback = ""] + * @param {} options + * @param {} scope + * @param {} [callback = ""] */ Q_INVOKABLE void compressData(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); @@ -229,7 +229,7 @@ public: /**jsdoc * @function Assets.canWriteCacheValue - * @property {string} url + * @param {string} url * @returns {boolean} */ @@ -237,8 +237,8 @@ public: /**jsdoc * @function Assets.getCacheStatus - * @property {} scope - * @property {} [callback=undefined] + * @param {} scope + * @param {} [callback=undefined] */ Q_INVOKABLE void getCacheStatus(QScriptValue scope, QScriptValue callback = QScriptValue()) { @@ -247,38 +247,38 @@ public: /**jsdoc * @function Assets.queryCacheMeta - * @property {} options - * @property {} scope - * @property {} [callback=undefined] + * @param {} options + * @param {} scope + * @param {} [callback=undefined] */ Q_INVOKABLE void queryCacheMeta(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); /**jsdoc * @function Assets.loadFromCache - * @property {} options - * @property {} scope - * @property {} [callback=undefined] + * @param {} options + * @param {} scope + * @param {} [callback=undefined] */ Q_INVOKABLE void loadFromCache(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); /**jsdoc * @function Assets.saveToCache - * @property {} options - * @property {} scope - * @property {} [callback=undefined] + * @param {} options + * @param {} scope + * @param {} [callback=undefined] */ Q_INVOKABLE void saveToCache(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); /**jsdoc * @function Assets.saveToCache - * @property {} url - * @property {} data - * @property {} metadata - * @property {} scope - * @property {} [callback=undefined] + * @param {} url + * @param {} data + * @param {} metadata + * @param {} scope + * @param {} [callback=undefined] */ Q_INVOKABLE void saveToCache(const QUrl& url, const QByteArray& data, const QVariantMap& metadata, diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index 714cb91b3f..5a7417cb49 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -36,7 +36,6 @@ Q_DECLARE_LOGGING_CATEGORY(displayplugins) -const char* StandingHMDSensorMode { "Standing HMD Sensor Mode" }; // this probably shouldn't be hardcoded here const char* OpenVrThreadedSubmit { "OpenVR Threaded Submit" }; // this probably shouldn't be hardcoded here PoseData _nextRenderPoseData; @@ -451,7 +450,6 @@ bool OpenVrDisplayPlugin::internalActivate() { qDebug() << "OpenVR Threaded submit enabled: " << _threadedSubmit; _openVrDisplayActive = true; - _container->setIsOptionChecked(StandingHMDSensorMode, true); _system->GetRecommendedRenderTargetSize(&_renderTargetSize.x, &_renderTargetSize.y); // Recommended render target size is per-eye, so double the X size for // left + right eyes @@ -507,7 +505,6 @@ void OpenVrDisplayPlugin::internalDeactivate() { Parent::internalDeactivate(); _openVrDisplayActive = false; - _container->setIsOptionChecked(StandingHMDSensorMode, false); if (_system) { // TODO: Invalidate poses. It's fine if someone else sets these shared values, but we're about to stop updating them, and // we don't want ViveControllerManager to consider old values to be valid. diff --git a/scripts/system/controllers/controllerModules/equipEntity.js b/scripts/system/controllers/controllerModules/equipEntity.js index 1fce772ec8..1f9a95c819 100644 --- a/scripts/system/controllers/controllerModules/equipEntity.js +++ b/scripts/system/controllers/controllerModules/equipEntity.js @@ -6,11 +6,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, - getControllerJointIndex, enableDispatcherModule, disableDispatcherModule, +/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, Camera, + getControllerJointIndex, enableDispatcherModule, disableDispatcherModule, entityIsFarGrabbedByOther, Messages, makeDispatcherModuleParameters, makeRunningValues, Settings, entityHasActions, Vec3, Overlays, flatten, Xform, getControllerWorldLocation, ensureDynamic, entityIsCloneable, - cloneEntity, DISPATCHER_PROPERTIES, TEAR_AWAY_DISTANCE, Uuid, unhighlightTargetEntity + cloneEntity, DISPATCHER_PROPERTIES, Uuid, unhighlightTargetEntity, isInEditMode */ Script.include("/~/system/libraries/Xform.js"); @@ -781,7 +781,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa } } }; - + var clearGrabActions = function(entityID) { var actionIDs = Entities.getActionIDs(entityID); var myGrabTag = "grab-" + MyAvatar.sessionUUID; @@ -794,7 +794,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa } } }; - + var onMousePress = function(event) { if (isInEditMode() || !event.isLeftButton) { // don't consider any left clicks on the entity while in edit return; @@ -808,7 +808,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa if (hasEquipData && entityProperties.parentID === EMPTY_PARENT_ID && !entityIsFarGrabbedByOther(entityID)) { entityProperties.id = entityID; var rightHandPosition = MyAvatar.getJointPosition("RightHand"); - var leftHandPosition = MyAvatar.getJointPosition("LeftHand"); + var leftHandPosition = MyAvatar.getJointPosition("LeftHand"); var distanceToRightHand = Vec3.distance(entityProperties.position, rightHandPosition); var distanceToLeftHand = Vec3.distance(entityProperties.position, leftHandPosition); var leftHandAvailable = leftEquipEntity.targetEntityID === null; @@ -828,7 +828,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa }; var onKeyPress = function(event) { - if (event.text === UNEQUIP_KEY) { + if (event.text.toLowerCase() === UNEQUIP_KEY) { if (rightEquipEntity.targetEntityID) { rightEquipEntity.endEquipEntity(); } diff --git a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js index a4e439fe2f..274a4264cd 100644 --- a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js @@ -10,7 +10,7 @@ propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, entityIsGrabbable, Quat, Vec3, MSECS_PER_SEC, getControllerWorldLocation, makeDispatcherModuleParameters, makeRunningValues, TRIGGER_OFF_VALUE, NEAR_GRAB_RADIUS, findGroupParent, entityIsCloneable, propsAreCloneDynamic, cloneEntity, - HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, unhighlightTargetEntity + HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, unhighlightTargetEntity, Uuid */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); diff --git a/scripts/system/controllers/controllerModules/nearGrabHyperLinkEntity.js b/scripts/system/controllers/controllerModules/nearGrabHyperLinkEntity.js index 59ce79cfd1..962ae89bb9 100644 --- a/scripts/system/controllers/controllerModules/nearGrabHyperLinkEntity.js +++ b/scripts/system/controllers/controllerModules/nearGrabHyperLinkEntity.js @@ -7,12 +7,8 @@ // 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, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, - getControllerJointIndex, getGrabbableData, enableDispatcherModule, disableDispatcherModule, - propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, entityIsGrabbable, - Quat, Vec3, MSECS_PER_SEC, getControllerWorldLocation, makeDispatcherModuleParameters, makeRunningValues, - TRIGGER_OFF_VALUE, NEAR_GRAB_RADIUS, findGroupParent, entityIsCloneable, propsAreCloneDynamic, cloneEntity, - HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, AddressManager +/* global Script, MyAvatar, RIGHT_HAND, LEFT_HAND, enableDispatcherModule, disableDispatcherModule, + makeDispatcherModuleParameters, makeRunningValues, TRIGGER_OFF_VALUE, NEAR_GRAB_RADIUS, BUMPER_ON_VALUE, AddressManager */ (function() { diff --git a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js index d454d20a02..cf3a9cf14b 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js @@ -11,8 +11,7 @@ TRIGGER_OFF_VALUE, makeDispatcherModuleParameters, entityIsGrabbable, makeRunningValues, NEAR_GRAB_RADIUS, findGroupParent, Vec3, cloneEntity, entityIsCloneable, propsAreCloneDynamic, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, findHandChildEntities, TEAR_AWAY_DISTANCE, MSECS_PER_SEC, TEAR_AWAY_CHECK_TIME, - TEAR_AWAY_COUNT, distanceBetweenPointAndEntityBoundingBox, print, Selection, DISPATCHER_HOVERING_LIST, Uuid, - highlightTargetEntity, unhighlightTargetEntity + TEAR_AWAY_COUNT, distanceBetweenPointAndEntityBoundingBox, print, Uuid, highlightTargetEntity, unhighlightTargetEntity */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -43,11 +42,6 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); [], 100); - - // XXX does handJointIndex change if the avatar changes? - this.handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); - this.controllerJointIndex = getControllerJointIndex(this.hand); - this.thisHandIsParent = function(props) { if (!props) { return false; @@ -62,8 +56,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); return true; } - var controllerJointIndex = this.controllerJointIndex; - if (props.parentJointIndex === controllerJointIndex) { + if (props.parentJointIndex === getControllerJointIndex(this.hand)) { return true; } @@ -102,7 +95,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); // } else { // handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); // } - handJointIndex = this.controllerJointIndex; + handJointIndex = getControllerJointIndex(this.hand); var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; Entities.callEntityMethod(targetProps.id, "startNearGrab", args); diff --git a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js index 0f876816b3..368d5c483b 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js @@ -9,7 +9,7 @@ /* global Script, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, getControllerJointIndex, enableDispatcherModule, disableDispatcherModule, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, makeDispatcherModuleParameters, Overlays, makeRunningValues, Vec3, resizeTablet, getTabletWidthFromSettings, - NEAR_GRAB_RADIUS + NEAR_GRAB_RADIUS, HMD, Uuid */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -37,7 +37,6 @@ Script.include("/~/system/libraries/utils.js"); // XXX does handJointIndex change if the avatar changes? this.handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); - this.controllerJointIndex = getControllerJointIndex(this.hand); this.getOtherModule = function() { return (this.hand === RIGHT_HAND) ? leftNearParentingGrabOverlay : rightNearParentingGrabOverlay; diff --git a/scripts/system/controllers/controllerModules/teleport.js b/scripts/system/controllers/controllerModules/teleport.js index 560da57b20..3bf99ca26a 100644 --- a/scripts/system/controllers/controllerModules/teleport.js +++ b/scripts/system/controllers/controllerModules/teleport.js @@ -10,7 +10,7 @@ /* jslint bitwise: true */ -/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, getControllerJointIndex, +/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, enableDispatcherModule, disableDispatcherModule, Messages, makeDispatcherModuleParameters, makeRunningValues, Vec3, HMD, Uuid, AvatarList, Picks, Pointers, PickType */ diff --git a/scripts/system/html/entityList.html b/scripts/system/html/entityList.html index d608ab63e5..7906a3c97f 100644 --- a/scripts/system/html/entityList.html +++ b/scripts/system/html/entityList.html @@ -14,7 +14,6 @@ - diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index 8647dca035..8d63261f4c 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -20,7 +20,6 @@ - diff --git a/scripts/system/html/gridControls.html b/scripts/system/html/gridControls.html index c0bd87988d..cd646fed51 100644 --- a/scripts/system/html/gridControls.html +++ b/scripts/system/html/gridControls.html @@ -16,7 +16,6 @@ - diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index 625aa26b00..88b3ccbf7c 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -444,8 +444,6 @@ function loaded() { augmentSpinButtons(); - setUpKeyboardControl(); - // Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked document.addEventListener("contextmenu", function (event) { event.preventDefault(); diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 4b6329db44..2194b539ef 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -7,7 +7,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html /* global alert, augmentSpinButtons, clearTimeout, console, document, Element, EventBridge, - HifiEntityUI, JSONEditor, openEventBridge, setUpKeyboardControl, setTimeout, window, _ $ */ + HifiEntityUI, JSONEditor, openEventBridge, setTimeout, window, _ $ */ var PI = 3.14159265358979; var DEGREES_TO_RADIANS = PI / 180.0; @@ -2157,8 +2157,6 @@ function loaded() { augmentSpinButtons(); - setUpKeyboardControl(); - // Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked document.addEventListener("contextmenu", function(event) { event.preventDefault(); diff --git a/scripts/system/html/js/gridControls.js b/scripts/system/html/js/gridControls.js index be4271788e..79a169400a 100644 --- a/scripts/system/html/js/gridControls.js +++ b/scripts/system/html/js/gridControls.js @@ -129,8 +129,6 @@ function loaded() { augmentSpinButtons(); - setUpKeyboardControl(); - EventBridge.emitWebEvent(JSON.stringify({ type: 'init' })); }); document.addEventListener("keydown", function (keyDown) { diff --git a/scripts/system/html/js/keyboardControl.js b/scripts/system/html/js/keyboardControl.js deleted file mode 100644 index 7a8a314c62..0000000000 --- a/scripts/system/html/js/keyboardControl.js +++ /dev/null @@ -1,73 +0,0 @@ -// -// keyboardControl.js -// -// Created by David Rowe on 28 Sep 2016. -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -function setUpKeyboardControl() { - - var lowerTimer = null; - var isRaised = false; - var KEYBOARD_HEIGHT = 200; - - function raiseKeyboard() { - window.isKeyboardRaised = true; - window.isNumericKeyboard = this.type === "number"; - - if (lowerTimer !== null) { - clearTimeout(lowerTimer); - lowerTimer = null; - } - - EventBridge.emitWebEvent("_RAISE_KEYBOARD" + (this.type === "number" ? "_NUMERIC" : "")); - - if (!isRaised) { - var delta = this.getBoundingClientRect().bottom + 10 - (document.body.clientHeight - KEYBOARD_HEIGHT); - if (delta > 0) { - setTimeout(function () { - document.body.scrollTop += delta; - }, 500); // Allow time for keyboard to be raised in QML. - } - } - - isRaised = true; - } - - function doLowerKeyboard() { - window.isKeyboardRaised = false; - window.isNumericKeyboard = false; - - EventBridge.emitWebEvent("_LOWER_KEYBOARD"); - lowerTimer = null; - isRaised = false; - } - - function lowerKeyboard() { - // Delay lowering keyboard a little in case immediately raise it again. - if (lowerTimer === null) { - lowerTimer = setTimeout(doLowerKeyboard, 20); - } - } - - function documentBlur() { - // Action any pending Lower keyboard event immediately upon leaving document window so that they don't interfere with - // other Entities Editor tab. - if (lowerTimer !== null) { - clearTimeout(lowerTimer); - doLowerKeyboard(); - } - } - - var inputs = document.querySelectorAll("input[type=text], input[type=password], input[type=number], textarea"); - for (var i = 0, length = inputs.length; i < length; i++) { - inputs[i].addEventListener("focus", raiseKeyboard); - inputs[i].addEventListener("blur", lowerKeyboard); - } - - window.addEventListener("blur", documentBlur); -} - diff --git a/scripts/system/libraries/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js index 71dc5e4273..04ae01bad6 100644 --- a/scripts/system/libraries/controllerDispatcherUtils.js +++ b/scripts/system/libraries/controllerDispatcherUtils.js @@ -7,7 +7,7 @@ /* global module, Camera, HMD, MyAvatar, controllerDispatcherPlugins:true, Quat, Vec3, Overlays, Xform, - Selection, + Selection, Uuid, MSECS_PER_SEC:true , LEFT_HAND:true, RIGHT_HAND:true, FORBIDDEN_GRAB_TYPES:true, HAPTIC_PULSE_STRENGTH:true, HAPTIC_PULSE_DURATION:true, ZERO_VEC:true, ONE_VEC:true, DEFAULT_REGISTRATION_POINT:true, INCHES_TO_METERS:true, @@ -34,11 +34,12 @@ getGrabbableData:true, entityIsGrabbable:true, entityIsDistanceGrabbable:true, + getControllerJointIndexCacheTime:true, + getControllerJointIndexCache:true, getControllerJointIndex:true, propsArePhysical:true, controllerDispatcherPluginsNeedSort:true, projectOntoXYPlane:true, - getChildrenProps:true, projectOntoEntityXYPlane:true, projectOntoOverlayXYPlane:true, makeLaserLockInfo:true, @@ -53,6 +54,8 @@ TEAR_AWAY_COUNT:true, TEAR_AWAY_CHECK_TIME:true, distanceBetweenPointAndEntityBoundingBox:true, + entityIsEquipped:true, + entityIsFarGrabbedByOther:true, highlightTargetEntity:true, clearHighlightedEntities:true, unhighlightTargetEntity:true @@ -265,20 +268,32 @@ entityIsDistanceGrabbable = function(props) { return true; }; -getControllerJointIndex = function (hand) { - if (HMD.isHandControllerAvailable()) { - var controllerJointIndex = -1; - if (Camera.mode === "first person") { - controllerJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ? - "_CONTROLLER_RIGHTHAND" : - "_CONTROLLER_LEFTHAND"); - } else if (Camera.mode === "third person") { - controllerJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ? - "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : - "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"); - } +getControllerJointIndexCacheTime = [0, 0]; +getControllerJointIndexCache = [-1, -1]; - return controllerJointIndex; +getControllerJointIndex = function (hand) { + var GET_CONTROLLERJOINTINDEX_CACHE_REFRESH_TIME = 3000; // msecs + + var now = Date.now(); + if (now - getControllerJointIndexCacheTime[hand] > GET_CONTROLLERJOINTINDEX_CACHE_REFRESH_TIME) { + if (HMD.isHandControllerAvailable()) { + var controllerJointIndex = -1; + if (Camera.mode === "first person") { + controllerJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ? + "_CONTROLLER_RIGHTHAND" : + "_CONTROLLER_LEFTHAND"); + } else if (Camera.mode === "third person") { + controllerJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ? + "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : + "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"); + } + + getControllerJointIndexCacheTime[hand] = now; + getControllerJointIndexCache[hand] = controllerJointIndex; + return controllerJointIndex; + } + } else { + return getControllerJointIndexCache[hand]; } return -1; diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index c3edee264f..dc4d5aa844 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -19,6 +19,7 @@ var selectionDisplay = null; // for gridTool.js to ignore Script.include("/~/system/libraries/WebTablet.js"); Script.include("/~/system/libraries/gridTool.js"); + Script.include("/~/system/libraries/connectionUtils.js"); var METAVERSE_SERVER_URL = Account.metaverseServerURL; var MARKETPLACE_URL = METAVERSE_SERVER_URL + "/marketplace"; diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 0a01007ee9..c70c2729f5 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -251,6 +251,7 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See }); } break; + case 'refresh': // old name for refreshNearby case 'refreshNearby': data = {}; ExtendedOverlay.some(function (overlay) { // capture the audio data @@ -743,10 +744,13 @@ function receiveMessage(channel, messageString, senderID) { var message = JSON.parse(messageString); switch (message.method) { case 'select': - sendToQml(message); // Accepts objects, not just strings. + if (!onPalScreen) { + tablet.loadQMLSource(PAL_QML_SOURCE); + Script.setTimeout(function () { sendToQml(message); }, 1000); + } else { + sendToQml(message); // Accepts objects, not just strings. + } break; - default: - print('Unrecognized PAL message', messageString); } } diff --git a/tools/auto-tester/src/Test.cpp b/tools/auto-tester/src/Test.cpp index 0eec03a782..078a66aa6a 100644 --- a/tools/auto-tester/src/Test.cpp +++ b/tools/auto-tester/src/Test.cpp @@ -175,40 +175,39 @@ void Test::appendTestResultsToFile(const QString& testResultsFolderPath, TestFai } void Test::startTestsEvaluation(const QString& testFolder) { - QString pathToTestResultsDirectory; - if (testFolder.isNull()) { - // Get list of JPEG images in folder, sorted by name - pathToTestResultsDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test images", ".", QFileDialog::ShowDirsOnly); - } else { - pathToTestResultsDirectory = testFolder; - } + // Get list of JPEG images in folder, sorted by name + QString previousSelection = snapshotDirectory; - if (pathToTestResultsDirectory == QString()) { + snapshotDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test images", + previousSelection, QFileDialog::ShowDirsOnly); + + // If user cancelled then restore previous selection and return + if (snapshotDirectory == "") { + snapshotDirectory = previousSelection; return; } // Quit if test results folder could not be created - if (!createTestResultsFolderPath(pathToTestResultsDirectory)) { + if (!createTestResultsFolderPath(snapshotDirectory)) { return; } // Before any processing - all images are converted to PNGs, as this is the format stored on GitHub - QStringList sortedSnapshotFilenames = createListOfAll_imagesInDirectory("jpg", pathToTestResultsDirectory); + QStringList sortedSnapshotFilenames = createListOfAll_imagesInDirectory("jpg", snapshotDirectory); foreach(QString filename, sortedSnapshotFilenames) { QStringList stringParts = filename.split("."); - copyJPGtoPNG( - pathToTestResultsDirectory + "/" + stringParts[0] + ".jpg", - pathToTestResultsDirectory + "/" + stringParts[0] + ".png" + copyJPGtoPNG(snapshotDirectory + "/" + stringParts[0] + ".jpg", + snapshotDirectory + "/" + stringParts[0] + ".png" ); - QFile::remove(pathToTestResultsDirectory + "/" + stringParts[0] + ".jpg"); + QFile::remove(snapshotDirectory + "/" + stringParts[0] + ".jpg"); } // Create two lists. The first is the test results, the second is the expected images // The expected images are represented as a URL to enable download from GitHub // Images that are in the wrong format are ignored. - QStringList sortedTestResultsFilenames = createListOfAll_imagesInDirectory("png", pathToTestResultsDirectory); + QStringList sortedTestResultsFilenames = createListOfAll_imagesInDirectory("png", snapshotDirectory); QStringList expectedImagesURLs; resultImagesFullFilenames.clear(); @@ -216,7 +215,7 @@ void Test::startTestsEvaluation(const QString& testFolder) { expectedImagesFullFilenames.clear(); foreach(QString currentFilename, sortedTestResultsFilenames) { - QString fullCurrentFilename = pathToTestResultsDirectory + "/" + currentFilename; + QString fullCurrentFilename = snapshotDirectory + "/" + currentFilename; if (isInSnapshotFilenameFormat("png", currentFilename)) { resultImagesFullFilenames << fullCurrentFilename; @@ -236,11 +235,11 @@ void Test::startTestsEvaluation(const QString& testFolder) { QString expectedImageFilename = currentFilename.replace("/", "_").replace(".", "_EI."); expectedImagesFilenames << expectedImageFilename; - expectedImagesFullFilenames << pathToTestResultsDirectory + "/" + expectedImageFilename; + expectedImagesFullFilenames << snapshotDirectory + "/" + expectedImageFilename; } } - autoTester->downloadImages(expectedImagesURLs, pathToTestResultsDirectory, expectedImagesFilenames); + autoTester->downloadImages(expectedImagesURLs, snapshotDirectory, expectedImagesFilenames); } void Test::finishTestsEvaluation(bool isRunningFromCommandline, bool interactiveMode, QProgressBar* progressBar) { @@ -303,25 +302,39 @@ void Test::importTest(QTextStream& textStream, const QString& testPathname) { // This script will run all text.js scripts in every applicable sub-folder void Test::createRecursiveScript() { // Select folder to start recursing from - QString topLevelDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder that will contain the top level test script", ".", QFileDialog::ShowDirsOnly); - if (topLevelDirectory == "") { + QString previousSelection = testDirectory; + + testDirectory = + QFileDialog::getExistingDirectory(nullptr, "Please select folder that will contain the top level test script", + previousSelection, QFileDialog::ShowDirsOnly); + + // If user cancelled then restore previous selection and return + if (testDirectory == "") { + testDirectory = previousSelection; return; } - createRecursiveScript(topLevelDirectory, true); + createRecursiveScript(testDirectory, true); } // This method creates a `testRecursive.js` script in every sub-folder. void Test::createAllRecursiveScripts() { // Select folder to start recursing from - QString topLevelDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select the root folder for the recursive scripts", ".", QFileDialog::ShowDirsOnly); - if (topLevelDirectory == "") { + QString previousSelection = testDirectory; + + testDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select the root folder for the recursive scripts", + previousSelection, + QFileDialog::ShowDirsOnly); + + // If user cancelled then restore previous selection and return + if (testDirectory == "") { + testDirectory = previousSelection; return; } - createRecursiveScript(topLevelDirectory, false); + createRecursiveScript(testDirectory, false); - QDirIterator it(topLevelDirectory.toStdString().c_str(), QDirIterator::Subdirectories); + QDirIterator it(testDirectory.toStdString().c_str(), QDirIterator::Subdirectories); while (it.hasNext()) { QString directory = it.next(); @@ -427,29 +440,42 @@ void Test::createRecursiveScript(const QString& topLevelDirectory, bool interact void Test::createTest() { // Rename files sequentially, as ExpectedResult_00000.jpeg, ExpectedResult_00001.jpg and so on // Any existing expected result images will be deleted - QString imageSourceDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test images", ".", QFileDialog::ShowDirsOnly); - if (imageSourceDirectory == "") { + QString previousSelection = snapshotDirectory; + + snapshotDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test images", + previousSelection, + QFileDialog::ShowDirsOnly); + + // If user cancelled then restore previous selection and return + if (snapshotDirectory == "") { + snapshotDirectory = previousSelection; return; } - QString imageDestinationDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder to save the test images", ".", QFileDialog::ShowDirsOnly); - if (imageDestinationDirectory == "") { + previousSelection = testDirectory; + + QString testDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder to save the test images", + previousSelection, QFileDialog::ShowDirsOnly); + + // If user cancelled then restore previous selection and return + if (testDirectory == "") { + testDirectory = previousSelection; return; } - QStringList sortedImageFilenames = createListOfAll_imagesInDirectory("jpg", imageSourceDirectory); + QStringList sortedImageFilenames = createListOfAll_imagesInDirectory("jpg", snapshotDirectory); int i = 1; const int maxImages = pow(10, NUM_DIGITS); foreach (QString currentFilename, sortedImageFilenames) { - QString fullCurrentFilename = imageSourceDirectory + "/" + currentFilename; + QString fullCurrentFilename = snapshotDirectory + "/" + currentFilename; if (isInSnapshotFilenameFormat("jpg", currentFilename)) { if (i >= maxImages) { QMessageBox::critical(0, "Error", "More than " + QString::number(maxImages) + " images not supported"); exit(-1); } QString newFilename = "ExpectedImage_" + QString::number(i - 1).rightJustified(5, '0') + ".png"; - QString fullNewFileName = imageDestinationDirectory + "/" + newFilename; + QString fullNewFileName = testDirectory + "/" + newFilename; try { copyJPGtoPNG(fullCurrentFilename, fullNewFileName); @@ -489,31 +515,6 @@ ExtractedText Test::getTestScriptLines(QString testFileName) { QString regexTestTitle(ws + functionPerformName + "\\(" + quotedString + "\\," + ws + ownPath + "\\," + ws + functionParameter + ws + "{" + ".*"); QRegularExpression lineContainingTitle = QRegularExpression(regexTestTitle); - // Assert platform checks that test is running on the correct OS - const QString functionAssertPlatform(ws + "autoTester" + ws + "\\." + ws + "assertPlatform"); - const QString regexAssertPlatform(ws + functionAssertPlatform + ws + "\\(" + ws + quotedString + ".*"); - const QRegularExpression lineAssertPlatform = QRegularExpression(regexAssertPlatform); - - // Assert display checks that test is running on the correct display - const QString functionAssertDisplay(ws + "autoTester" + ws + "\\." + ws + "assertDisplay"); - const QString regexAssertDisplay(ws + functionAssertDisplay + ws + "\\(" + ws + quotedString + ".*"); - const QRegularExpression lineAssertDisplay = QRegularExpression(regexAssertDisplay); - - // Assert CPU checks that test is running on the correct type of CPU - const QString functionAssertCPU(ws + "autoTester" + ws + "\\." + ws + "assertCPU"); - const QString regexAssertCPU(ws + functionAssertCPU + ws + "\\(" + ws + quotedString + ".*"); - const QRegularExpression lineAssertCPU = QRegularExpression(regexAssertCPU); - - // Assert GPU checks that test is running on the correct type of GPU - const QString functionAssertGPU(ws + "autoTester" + ws + "\\." + ws + "assertGPU"); - const QString regexAssertGPU(ws + functionAssertGPU + ws + "\\(" + ws + quotedString + ".*"); - const QRegularExpression lineAssertGPU = QRegularExpression(regexAssertGPU); - - // Assert the correct amount of memory - const QString functionAssertPhysicalMemoryGB(ws + "autoTester" + ws + "\\." + ws + "assertPhysicalMemoryGB"); - const QString regexAssertPhysicalMemoryGB(ws + functionAssertPhysicalMemoryGB + ws + "\\(" + ws + quotedString + ".*"); - const QRegularExpression lineAssertPhysicalMemoryGB = QRegularExpression(regexAssertPhysicalMemoryGB); - // Each step is either of the following forms: // autoTester.addStepSnapshot("Take snapshot"... @@ -523,7 +524,7 @@ ExtractedText Test::getTestScriptLines(QString testFileName) { const QRegularExpression lineStepSnapshot = QRegularExpression(regexStepSnapshot); const QString functionAddStepName(ws + "autoTester" + ws + "\\." + ws + "addStep"); - const QString regexStep(ws + functionAddStepName + ws + "\\(" + ws + quotedString + ws + "\\)" + ".*"); + const QString regexStep(ws + functionAddStepName + ws + "\\(" + ws + quotedString + ".*"); const QRegularExpression lineStep = QRegularExpression(regexStep); while (!line.isNull()) { @@ -531,7 +532,6 @@ ExtractedText Test::getTestScriptLines(QString testFileName) { if (lineContainingTitle.match(line).hasMatch()) { QStringList tokens = line.split('"'); relevantTextFromTest.title = tokens[1]; - } else if (lineStepSnapshot.match(line).hasMatch()) { QStringList tokens = line.split('"'); QString nameOfStep = tokens[1]; @@ -561,29 +561,43 @@ ExtractedText Test::getTestScriptLines(QString testFileName) { // The folder selected must contain a script named "test.js", the file produced is named "test.md" void Test::createMDFile() { // Folder selection - QString testDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test", ".", QFileDialog::ShowDirsOnly); + QString previousSelection = testDirectory; + + testDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test", previousSelection, + QFileDialog::ShowDirsOnly); + + // If user cancelled then restore previous selection and return if (testDirectory == "") { + testDirectory = previousSelection; return; } createMDFile(testDirectory); + + QMessageBox::information(0, "Success", "MD file has been created"); } void Test::createAllMDFiles() { // Select folder to start recursing from - QString topLevelDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select the root folder for the MD files", ".", QFileDialog::ShowDirsOnly); - if (topLevelDirectory == "") { + QString previousSelection = testDirectory; + + testDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select the root folder for the MD files", + previousSelection, QFileDialog::ShowDirsOnly); + + // If user cancelled then restore previous selection and return + if (testDirectory == "") { + testDirectory = previousSelection; return; } // First test if top-level folder has a test.js file - const QString testPathname{ topLevelDirectory + "/" + TEST_FILENAME }; + const QString testPathname { testDirectory + "/" + TEST_FILENAME }; QFileInfo fileInfo(testPathname); if (fileInfo.exists()) { - createMDFile(topLevelDirectory); + createMDFile(testDirectory); } - QDirIterator it(topLevelDirectory.toStdString().c_str(), QDirIterator::Subdirectories); + QDirIterator it(testDirectory.toStdString().c_str(), QDirIterator::Subdirectories); while (it.hasNext()) { QString directory = it.next(); @@ -638,31 +652,33 @@ void Test::createMDFile(const QString& testDirectory) { stream << "## Steps\n"; stream << "Press space bar to advance step by step\n\n"; - // Note that snapshots of step n are taken in step n+1 - // (this implies that if the LAST step requests a snapshot then this will not work - caveat emptor) int snapShotIndex { 0 }; for (size_t i = 0; i < testScriptLines.stepList.size(); ++i) { stream << "### Step " << QString::number(i + 1) << "\n"; stream << "- " << testScriptLines.stepList[i]->text << "\n"; - if ((i + 1 < testScriptLines.stepList.size()) && testScriptLines.stepList[i + 1]->takeSnapshot) { + if ((i + 1 < testScriptLines.stepList.size()) && testScriptLines.stepList[i]->takeSnapshot) { stream << "- ![](./ExpectedImage_" << QString::number(snapShotIndex).rightJustified(5, '0') << ".png)\n"; ++snapShotIndex; } } mdFile.close(); - - QMessageBox::information(0, "Success", "Test MD file " + mdFilename + " has been created"); } void Test::createTestsOutline() { - QString testsRootDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select the tests root folder", ".", QFileDialog::ShowDirsOnly); - if (testsRootDirectory == "") { + QString previousSelection = testDirectory; + + testDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select the tests root folder", previousSelection, + QFileDialog::ShowDirsOnly); + + // If user cancelled then restore previous selection and return + if (testDirectory == "") { + testDirectory = previousSelection; return; } const QString testsOutlineFilename { "testsOutline.md" }; - QString mdFilename(testsRootDirectory + "/" + testsOutlineFilename); + QString mdFilename(testDirectory + "/" + testsOutlineFilename); QFile mdFile(mdFilename); if (!mdFile.open(QIODevice::WriteOnly)) { QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to create file " + mdFilename); @@ -676,10 +692,10 @@ void Test::createTestsOutline() { stream << "Directories with an appended (*) have an automatic test\n\n"; // We need to know our current depth, as this isn't given by QDirIterator - int rootDepth { testsRootDirectory.count('/') }; + int rootDepth { testDirectory.count('/') }; // Each test is shown as the folder name linking to the matching GitHub URL, and the path to the associated test.md file - QDirIterator it(testsRootDirectory.toStdString().c_str(), QDirIterator::Subdirectories); + QDirIterator it(testDirectory.toStdString().c_str(), QDirIterator::Subdirectories); while (it.hasNext()) { QString directory = it.next(); diff --git a/tools/auto-tester/src/Test.h b/tools/auto-tester/src/Test.h index bc28d6ad0a..0fb957d309 100644 --- a/tools/auto-tester/src/Test.h +++ b/tools/auto-tester/src/Test.h @@ -89,6 +89,12 @@ private: const int NUM_DIGITS { 5 }; const QString EXPECTED_IMAGE_PREFIX { "ExpectedImage_" }; + // We have two directories to work with. + // The first is the directory containing the test we are working with + // The second contains the snapshots taken for test runs that need to be evaluated + QString testDirectory; + QString snapshotDirectory; + QStringList expectedImagesFilenames; QStringList expectedImagesFullFilenames; QStringList resultImagesFullFilenames;