From 24dd06b65e9068ce4b3b8913266a703983e88d7a Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 23 Feb 2017 09:09:54 -0500 Subject: [PATCH 01/89] * address FIXME regarding jasmine.done() * support disabling tests / reporting as pending --- .../developer/libraries/jasmine/hifi-boot.js | 12 +++++++++-- .../tests/unit_tests/avatarUnitTests.js | 11 +++++++++- .../tests/unit_tests/entityUnitTests.js | 8 +++++++ .../developer/tests/unit_tests/testRunner.js | 21 ++++++++++++++++--- 4 files changed, 46 insertions(+), 6 deletions(-) diff --git a/scripts/developer/libraries/jasmine/hifi-boot.js b/scripts/developer/libraries/jasmine/hifi-boot.js index f490a3618f..49d7fadd29 100644 --- a/scripts/developer/libraries/jasmine/hifi-boot.js +++ b/scripts/developer/libraries/jasmine/hifi-boot.js @@ -6,7 +6,7 @@ var lastSpecStartTime; function ConsoleReporter(options) { var startTime = new Date().getTime(); - var errorCount = 0; + var errorCount = 0, pending = []; this.jasmineStarted = function (obj) { print('Jasmine started with ' + obj.totalSpecsDefined + ' tests.'); }; @@ -15,11 +15,15 @@ var endTime = new Date().getTime(); print('
'); if (errorCount === 0) { - print ('All tests passed!'); + print ('All enabled tests passed!'); } else { print('Tests completed with ' + errorCount + ' ' + ERROR + '.'); } + if (pending.length) { + print ('disabled:
   '+ + pending.join('
   ')+'
'); + } print('Tests completed in ' + (endTime - startTime) + 'ms.'); }; this.suiteStarted = function(obj) { @@ -32,6 +36,10 @@ lastSpecStartTime = new Date().getTime(); }; this.specDone = function(obj) { + if (obj.status === 'pending') { + pending.push(obj.fullName); + return print('...(pending ' + obj.fullName +')'); + } var specEndTime = new Date().getTime(); var symbol = obj.status === PASSED ? '' + CHECKMARK + '' : diff --git a/scripts/developer/tests/unit_tests/avatarUnitTests.js b/scripts/developer/tests/unit_tests/avatarUnitTests.js index 7032b5f5e6..fc2801f83e 100644 --- a/scripts/developer/tests/unit_tests/avatarUnitTests.js +++ b/scripts/developer/tests/unit_tests/avatarUnitTests.js @@ -8,6 +8,15 @@ var ROT_IDENT = {x: 0, y: 0, z: 0, w: 1}; describe("MyAvatar", function () { + // backup/restore current skeletonModelURL + beforeAll(function() { + this.oldURL = MyAvatar.skeletonModelURL; + }); + + afterAll(function() { + MyAvatar.skeletonModelURL = this.oldURL; + }); + // reload the avatar from scratch before each test. beforeEach(function (done) { MyAvatar.skeletonModelURL = DEFAULT_AVATAR_URL; @@ -25,7 +34,7 @@ describe("MyAvatar", function () { }, 500); } }, 500); - }); + }, 10000 /*timeout -- allow time to download avatar*/); // makes the assumption that there is solid ground somewhat underneath the avatar. it("position and orientation getters", function () { diff --git a/scripts/developer/tests/unit_tests/entityUnitTests.js b/scripts/developer/tests/unit_tests/entityUnitTests.js index 033a484663..612bf6cdcd 100644 --- a/scripts/developer/tests/unit_tests/entityUnitTests.js +++ b/scripts/developer/tests/unit_tests/entityUnitTests.js @@ -19,6 +19,14 @@ describe('Entity', function() { }, }; + it('serversExist', function() { + expect(Entities.serversExist()).toBe(true); + }); + + it('canRezTmp', function() { + expect(Entities.canRezTmp()).toBe(true); + }); + beforeEach(function() { boxEntity = Entities.addEntity(boxProps); }); diff --git a/scripts/developer/tests/unit_tests/testRunner.js b/scripts/developer/tests/unit_tests/testRunner.js index 31d83cd986..545bb89600 100644 --- a/scripts/developer/tests/unit_tests/testRunner.js +++ b/scripts/developer/tests/unit_tests/testRunner.js @@ -3,11 +3,26 @@ Script.include('../../libraries/jasmine/jasmine.js'); Script.include('../../libraries/jasmine/hifi-boot.js') // Include unit tests -// FIXME: Figure out why jasmine done() is not working. -// Script.include('avatarUnitTests.js'); +Script.include('avatarUnitTests.js'); Script.include('bindUnitTest.js'); Script.include('entityUnitTests.js'); +describe("jasmine internal tests", function() { + it('should support async .done()', function(done) { + var start = new Date; + Script.setTimeout(function() { + expect((new Date - start)/1000).toBeCloseTo(0.5, 1); + done(); + }, 500); + }); + // jasmine pending test + xit('disabled test', function() { + expect(false).toBe(true); + }); +}); + +// invoke Script.stop (after any async tests complete) +jasmine.getEnv().addReporter({ jasmineDone: Script.stop }); + // Run the tests jasmine.getEnv().execute(); -Script.stop(); From 524806b2d99568f68b12b790d169ccee5fce05f4 Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 23 Feb 2017 11:22:13 -0500 Subject: [PATCH 02/89] Add initial eslint config comments --- scripts/developer/tests/unit_tests/avatarUnitTests.js | 6 ++++-- scripts/developer/tests/unit_tests/bindUnitTest.js | 2 ++ scripts/developer/tests/unit_tests/entityUnitTests.js | 4 +++- scripts/developer/tests/unit_tests/testRunner.js | 4 +++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/scripts/developer/tests/unit_tests/avatarUnitTests.js b/scripts/developer/tests/unit_tests/avatarUnitTests.js index fc2801f83e..4ab8556ab7 100644 --- a/scripts/developer/tests/unit_tests/avatarUnitTests.js +++ b/scripts/developer/tests/unit_tests/avatarUnitTests.js @@ -1,5 +1,7 @@ +/* eslint-env jasmine */ // Art3mis +// eslint-disable-next-line max-len var DEFAULT_AVATAR_URL = "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/e76946cc-c272-4adf-9bb6-02cde0a4b57d/8fd984ea6fe1495147a3303f87fa6e23.fst?1460131758"; var ORIGIN = {x: 0, y: 0, z: 0}; @@ -29,12 +31,12 @@ describe("MyAvatar", function () { MyAvatar.position = ORIGIN; MyAvatar.orientation = ROT_IDENT; // give the avatar 1/2 a second to settle on the ground in the idle pose. - Script.setTimeout(function () { + Script.setTimeout(function () { done(); }, 500); } }, 500); - }, 10000 /*timeout -- allow time to download avatar*/); + }, 10000 /* timeout -- allow time to download avatar*/); // makes the assumption that there is solid ground somewhat underneath the avatar. it("position and orientation getters", function () { diff --git a/scripts/developer/tests/unit_tests/bindUnitTest.js b/scripts/developer/tests/unit_tests/bindUnitTest.js index 609487a30b..3407490a04 100644 --- a/scripts/developer/tests/unit_tests/bindUnitTest.js +++ b/scripts/developer/tests/unit_tests/bindUnitTest.js @@ -1,3 +1,5 @@ +/* eslint-env jasmine */ + Script.include('../../../system/libraries/utils.js'); describe('Bind', function() { diff --git a/scripts/developer/tests/unit_tests/entityUnitTests.js b/scripts/developer/tests/unit_tests/entityUnitTests.js index 612bf6cdcd..730fceb144 100644 --- a/scripts/developer/tests/unit_tests/entityUnitTests.js +++ b/scripts/developer/tests/unit_tests/entityUnitTests.js @@ -1,3 +1,5 @@ +/* eslint-env jasmine */ + describe('Entity', function() { var center = Vec3.sum( MyAvatar.position, @@ -70,4 +72,4 @@ describe('Entity', function() { props = Entities.getEntityProperties(boxEntity); expect(props.lastEdited).toBeGreaterThan(prevLastEdited); }); -}); \ No newline at end of file +}); diff --git a/scripts/developer/tests/unit_tests/testRunner.js b/scripts/developer/tests/unit_tests/testRunner.js index 545bb89600..7ce55b1874 100644 --- a/scripts/developer/tests/unit_tests/testRunner.js +++ b/scripts/developer/tests/unit_tests/testRunner.js @@ -1,6 +1,8 @@ +/* eslint-env jasmine */ + // Include testing library Script.include('../../libraries/jasmine/jasmine.js'); -Script.include('../../libraries/jasmine/hifi-boot.js') +Script.include('../../libraries/jasmine/hifi-boot.js'); // Include unit tests Script.include('avatarUnitTests.js'); From ef27d30e85cf36c3847f30bc25cd64b18689d077 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 27 Apr 2017 12:01:37 -0700 Subject: [PATCH 03/89] Fix NetworkTexture self possibly being null when attempting request --- .../model-networking/src/model-networking/TextureCache.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 55704236e3..c2d947baab 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -394,12 +394,17 @@ void NetworkTexture::startRequestForNextMipLevel() { } if (_ktxResourceState == WAITING_FOR_MIP_REQUEST) { + auto self = _self.lock(); + if (!self) { + return; + } + _ktxResourceState = PENDING_MIP_REQUEST; init(); setLoadPriority(this, -static_cast(_originalKtxDescriptor->header.numberOfMipmapLevels) + _lowestKnownPopulatedMip); _url.setFragment(QString::number(_lowestKnownPopulatedMip - 1)); - TextureCache::attemptRequest(_self); + TextureCache::attemptRequest(self); } } From 314e2558a465ec1f5f78336950549d86c0f6baa7 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 27 Apr 2017 12:01:55 -0700 Subject: [PATCH 04/89] Fix style in NetworkTexture --- .../src/model-networking/TextureCache.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index c2d947baab..680550d9a1 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -473,19 +473,16 @@ void NetworkTexture::ktxMipRequestFinished() { texture->assignStoredMip(_ktxMipLevelRangeInFlight.first, _ktxMipRequest->getData().size(), reinterpret_cast(_ktxMipRequest->getData().data())); _lowestKnownPopulatedMip = _textureSource->getGPUTexture()->minAvailableMipLevel(); - } - else { + } else { qWarning(networking) << "Trying to update mips but texture is null"; } finishedLoading(true); _ktxResourceState = WAITING_FOR_MIP_REQUEST; - } - else { + } else { finishedLoading(false); if (handleFailedRequest(_ktxMipRequest->getResult())) { _ktxResourceState = PENDING_MIP_REQUEST; - } - else { + } else { qWarning(networking) << "Failed to load mip: " << _url; _ktxResourceState = FAILED_TO_LOAD; } @@ -497,8 +494,7 @@ void NetworkTexture::ktxMipRequestFinished() { if (_ktxResourceState == WAITING_FOR_MIP_REQUEST && _lowestRequestedMipLevel < _lowestKnownPopulatedMip) { startRequestForNextMipLevel(); } - } - else { + } else { qWarning() << "Mip request finished in an unexpected state: " << _ktxResourceState; } } From ae2dc385c325a87ea1c504ad9aac0e10283053c1 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 27 Apr 2017 13:03:04 -0700 Subject: [PATCH 05/89] Fix gpu access of ktx file not being thread-safe --- libraries/gpu/src/gpu/Texture.h | 8 ++++---- libraries/gpu/src/gpu/Texture_ktx.cpp | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 9b23b4e695..3b8ae508eb 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -324,11 +324,11 @@ public: void reset() override { } protected: - std::shared_ptr maybeOpenFile(); + std::shared_ptr maybeOpenFile() const; - std::mutex _cacheFileCreateMutex; - std::mutex _cacheFileWriteMutex; - std::weak_ptr _cacheFile; + mutable std::mutex _cacheFileCreateMutex; + mutable std::mutex _cacheFileWriteMutex; + mutable std::weak_ptr _cacheFile; std::string _filename; std::atomic _minMipLevelAvailable; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index efff6c7afe..d2f93c0036 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -128,7 +128,7 @@ KtxStorage::KtxStorage(const std::string& filename) : _filename(filename) { } } -std::shared_ptr KtxStorage::maybeOpenFile() { +std::shared_ptr KtxStorage::maybeOpenFile() const { std::shared_ptr file = _cacheFile.lock(); if (file) { return file; @@ -154,7 +154,8 @@ PixelsPointer KtxStorage::getMipFace(uint16 level, uint8 face) const { auto faceOffset = _ktxDescriptor->getMipFaceTexelsOffset(level, face); auto faceSize = _ktxDescriptor->getMipFaceTexelsSize(level, face); if (faceSize != 0 && faceOffset != 0) { - result = std::make_shared(_filename.c_str())->createView(faceSize, faceOffset)->toMemoryStorage(); + auto file = maybeOpenFile(); + result = file->createView(faceSize, faceOffset)->toMemoryStorage(); } return result; } From 16af62b15f733ace81ab111fbd94c3527a5eda3b Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 27 Apr 2017 13:03:42 -0700 Subject: [PATCH 06/89] Fix NetworkTexture not cleaning itself up on destruction --- .../src/model-networking/TextureCache.cpp | 16 ++++++++++++++++ .../src/model-networking/TextureCache.h | 1 + libraries/networking/src/ResourceCache.h | 2 +- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 680550d9a1..7aee95c758 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -330,6 +330,22 @@ private: int _maxNumPixels; }; +NetworkTexture::~NetworkTexture() { + if (_ktxHeaderRequest || _ktxMipRequest) { + if (_ktxHeaderRequest) { + _ktxHeaderRequest->disconnect(this); + _ktxHeaderRequest->deleteLater(); + _ktxHeaderRequest = nullptr; + } + if (_ktxMipRequest) { + _ktxMipRequest->disconnect(this); + _ktxMipRequest->deleteLater(); + _ktxMipRequest = nullptr; + } + ResourceCache::requestCompleted(_self); + } +} + const uint16_t NetworkTexture::NULL_MIP_LEVEL = std::numeric_limits::max(); void NetworkTexture::makeRequest() { if (!_sourceIsKTX) { diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 1e61b9ecee..c7a7799216 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -46,6 +46,7 @@ class NetworkTexture : public Resource, public Texture { public: NetworkTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels); + ~NetworkTexture() override; QString getType() const override { return "NetworkTexture"; } diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index d4c7d63ee5..51c8d8554a 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -344,7 +344,7 @@ class Resource : public QObject { public: Resource(const QUrl& url); - ~Resource(); + virtual ~Resource(); virtual QString getType() const { return "Resource"; } From 40e9f9025fb6ac2f7d2e0301ec7e7ed94d63eb80 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 27 Apr 2017 10:47:07 -0700 Subject: [PATCH 07/89] Load High Mips before Fbx after skybox --- .../src/model-networking/TextureCache.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 7aee95c758..961b3b3e7b 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -50,7 +50,8 @@ Q_LOGGING_CATEGORY(trace_resource_parse_image_ktx, "trace.resource.parse.image.k const std::string TextureCache::KTX_DIRNAME { "ktx_cache" }; const std::string TextureCache::KTX_EXT { "ktx" }; -static const int SKYBOX_LOAD_PRIORITY { 10 }; // Make sure skybox loads first +static const float SKYBOX_LOAD_PRIORITY { 10.0f }; // Make sure skybox loads first +static const float HIGH_MIPS_LOAD_PRIORITY { 9.0f }; // Make sure high mips loads after skybox but before models TextureCache::TextureCache() : _ktxCache(KTX_DIRNAME, KTX_EXT) { @@ -261,9 +262,6 @@ QSharedPointer TextureCache::createResource(const QUrl& url, const QSh auto content = textureExtra ? textureExtra->content : QByteArray(); auto maxNumPixels = textureExtra ? textureExtra->maxNumPixels : ABSOLUTE_MAX_TEXTURE_NUM_PIXELS; NetworkTexture* texture = new NetworkTexture(url, type, content, maxNumPixels); - if (type == image::TextureUsage::CUBE_TEXTURE) { - texture->setLoadPriority(this, SKYBOX_LOAD_PRIORITY); - } return QSharedPointer(texture, &Resource::deleter); } @@ -276,6 +274,12 @@ NetworkTexture::NetworkTexture(const QUrl& url, image::TextureUsage::Type type, _textureSource = std::make_shared(); _lowestRequestedMipLevel = 0; + if (type == image::TextureUsage::CUBE_TEXTURE) { + setLoadPriority(this, SKYBOX_LOAD_PRIORITY); + } else if (_sourceIsKTX) { + setLoadPriority(this, HIGH_MIPS_LOAD_PRIORITY); + } + if (!url.isValid()) { _loaded = true; } @@ -418,7 +422,8 @@ void NetworkTexture::startRequestForNextMipLevel() { _ktxResourceState = PENDING_MIP_REQUEST; init(); - setLoadPriority(this, -static_cast(_originalKtxDescriptor->header.numberOfMipmapLevels) + _lowestKnownPopulatedMip); + float priority = -(float)_originalKtxDescriptor->header.numberOfMipmapLevels + (float)_lowestKnownPopulatedMip; + setLoadPriority(this, priority); _url.setFragment(QString::number(_lowestKnownPopulatedMip - 1)); TextureCache::attemptRequest(self); } From 4bc92c025df48782659a247183df2ab50c86ef5d Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 27 Apr 2017 14:01:55 -0700 Subject: [PATCH 08/89] Fix ResourceCache warning on OSX --- .../model-networking/src/model-networking/TextureCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 961b3b3e7b..7745766177 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -346,7 +346,7 @@ NetworkTexture::~NetworkTexture() { _ktxMipRequest->deleteLater(); _ktxMipRequest = nullptr; } - ResourceCache::requestCompleted(_self); + TextureCache::requestCompleted(_self); } } From 945952d51190dd300a62960940d68dd4e007393c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 28 Apr 2017 10:32:06 -0700 Subject: [PATCH 09/89] don't save ATP requests to cache if they are range requests --- libraries/networking/src/AssetRequest.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/AssetRequest.cpp b/libraries/networking/src/AssetRequest.cpp index 341c3b45da..9c756b0060 100644 --- a/libraries/networking/src/AssetRequest.cpp +++ b/libraries/networking/src/AssetRequest.cpp @@ -113,8 +113,10 @@ void AssetRequest::start() { _data = data; _totalReceived += data.size(); emit progress(_totalReceived, data.size()); - - saveToCache(getUrl(), data); + + if (!_byteRange.isSet()) { + saveToCache(getUrl(), data); + } } } From bc89af28423cb55d11e4f9d7a6f60082e59dcbb8 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 28 Apr 2017 16:26:03 -0700 Subject: [PATCH 10/89] Fix stuck ATP downloads Occasionally ATP requests would get stuck because of a race inside AssetClient::handleAssetReply. This was more likely to happen to small resources. --- libraries/networking/src/AssetClient.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/libraries/networking/src/AssetClient.cpp b/libraries/networking/src/AssetClient.cpp index 15e0b8c9b5..054557e920 100644 --- a/libraries/networking/src/AssetClient.cpp +++ b/libraries/networking/src/AssetClient.cpp @@ -348,18 +348,19 @@ void AssetClient::handleAssetGetReply(QSharedPointer message, S // Store message in case we need to disconnect from it later. callbacks.message = message; + + auto weakNode = senderNode.toWeakRef(); + connect(message.data(), &ReceivedMessage::progress, this, [this, weakNode, messageID, length](qint64 size) { + handleProgressCallback(weakNode, messageID, size, length); + }); + connect(message.data(), &ReceivedMessage::completed, this, [this, weakNode, messageID]() { + handleCompleteCallback(weakNode, messageID); + }); + if (message->isComplete()) { + disconnect(message.data(), nullptr, this, nullptr); callbacks.completeCallback(true, error, message->readAll()); messageCallbackMap.erase(requestIt); - } else { - auto weakNode = senderNode.toWeakRef(); - - connect(message.data(), &ReceivedMessage::progress, this, [this, weakNode, messageID, length](qint64 size) { - handleProgressCallback(weakNode, messageID, size, length); - }); - connect(message.data(), &ReceivedMessage::completed, this, [this, weakNode, messageID]() { - handleCompleteCallback(weakNode, messageID); - }); } } From 46d4504f1540a5c8ccb5ac1034f09d7ffe5f8fa1 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 28 Apr 2017 16:27:44 -0700 Subject: [PATCH 11/89] Fix poorly indented if in AssetRequest --- libraries/networking/src/AssetRequest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/AssetRequest.cpp b/libraries/networking/src/AssetRequest.cpp index 9c756b0060..920c7ae036 100644 --- a/libraries/networking/src/AssetRequest.cpp +++ b/libraries/networking/src/AssetRequest.cpp @@ -77,7 +77,7 @@ void AssetRequest::start() { _assetRequestID = assetClient->getAsset(_hash, _byteRange.fromInclusive, _byteRange.toExclusive, [this, that, hash](bool responseReceived, AssetServerError serverError, const QByteArray& data) { - if (!that) { + if (!that) { qCWarning(asset_client) << "Got reply for dead asset request " << hash << "- error code" << _error; // If the request is dead, return return; From 3d6778e9cdd722ed1bef438179abb0f9014706a3 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 1 May 2017 15:02:23 -0700 Subject: [PATCH 12/89] Add tutorial Changelog.md --- tutorial/Changelog.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 tutorial/Changelog.md diff --git a/tutorial/Changelog.md b/tutorial/Changelog.md new file mode 100644 index 0000000000..bd923b6841 --- /dev/null +++ b/tutorial/Changelog.md @@ -0,0 +1,3 @@ + * home-tutorial-34 + * Update tutorial to only start if `HMD.active` + * Update builder's grid to use "Good - Sub-meshes" for collision shape type From 7f1db38390f03a2f8dfcf53c2c0e1c511285ecbb Mon Sep 17 00:00:00 2001 From: Vladyslav Stelmakhovskyi Date: Thu, 27 Apr 2017 09:30:00 +0200 Subject: [PATCH 13/89] Renamed EDIT to CREATE --- scripts/system/edit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 6fabeb2ec6..6fae4df64e 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -343,7 +343,7 @@ var toolBar = (function () { activeButton = tablet.addButton({ icon: "icons/tablet-icons/edit-i.svg", activeIcon: "icons/tablet-icons/edit-a.svg", - text: "EDIT", + text: "CREATE", sortOrder: 10 }); tablet.screenChanged.connect(function (type, url) { From b3e078eb7401df1554d5997ca3f0c22af139d5cd Mon Sep 17 00:00:00 2001 From: Vladyslav Stelmakhovskyi Date: Thu, 27 Apr 2017 16:02:47 +0200 Subject: [PATCH 14/89] Trigger rebuild --- scripts/system/edit.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 6fae4df64e..a6d2d165f7 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -2094,3 +2094,4 @@ entityListTool.webView.webEventReceived.connect(function (data) { }); }()); // END LOCAL_SCOPE + From 43f846770813312a3b165ac32265fcdbbcf379df Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sat, 29 Apr 2017 13:58:20 -0700 Subject: [PATCH 15/89] lint --- scripts/system/makeUserConnection.js | 422 ++++++++++++++------------- 1 file changed, 212 insertions(+), 210 deletions(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index 0ffea0c568..eda461f541 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -1,4 +1,6 @@ "use strict"; +/*jslint vars:true, plusplus:true, forin:true*/ +/*global Window, Script, Controller, MyAvatar, AvatarList, Entities, Messages, Audio, SoundCache, Account, UserActivityLogger, Vec3, Quat, XMLHttpRequest, location, print*/ // // makeUserConnection.js // scripts/system @@ -9,7 +11,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -(function() { // BEGIN LOCAL_SCOPE +(function () { // BEGIN LOCAL_SCOPE var LABEL = "makeUserConnection"; var MAX_AVATAR_DISTANCE = 0.2; // m @@ -27,7 +29,7 @@ var MAKING_CONNECTION_TIMEOUT = 800; // ms var CONNECTING_TIME = 1600; // ms var PARTICLE_RADIUS = 0.15; // m - var PARTICLE_ANGLE_INCREMENT = 360/45; // 1hz + var PARTICLE_ANGLE_INCREMENT = 360 / 45; // 1hz var HANDSHAKE_SOUND_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/davidkelly/production/audio/4beat_sweep.wav"; var SUCCESSFUL_HANDSHAKE_SOUND_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/davidkelly/production/audio/3rdbeat_success_bell.wav"; var PREFERRER_HAND_JOINT_POSTFIX_ORDER = ['Middle1', 'Index1', '']; @@ -39,7 +41,7 @@ var PARTICLE_EFFECT_PROPS = { "alpha": 0.8, "azimuthFinish": Math.PI, - "azimuthStart": -1*Math.PI, + "azimuthStart": -1 * Math.PI, "emitRate": 500, "emitSpeed": 0.0, "emitterShouldTrail": 1, @@ -56,10 +58,10 @@ "color": {"red": 255, "green": 255, "blue": 255}, "colorFinish": {"red": 0, "green": 164, "blue": 255}, "colorStart": {"red": 255, "green": 255, "blue": 255}, - "emitOrientation": {"w": -0.71, "x":0.0, "y":0.0, "z": 0.71}, + "emitOrientation": {"w": -0.71, "x": 0.0, "y": 0.0, "z": 0.71}, "emitAcceleration": {"x": 0.0, "y": 0.0, "z": 0.0}, "accelerationSpread": {"x": 0.0, "y": 0.0, "z": 0.0}, - "dimensions": {"x":0.05, "y": 0.05, "z": 0.05}, + "dimensions": {"x": 0.05, "y": 0.05, "z": 0.05}, "type": "ParticleEffect" }; var MAKING_CONNECTION_PARTICLE_PROPS = { @@ -68,7 +70,7 @@ "alphaSpread": 0, "alphaFinish": 0, "azimuthFinish": Math.PI, - "azimuthStart": -1*Math.PI, + "azimuthStart": -1 * Math.PI, "emitRate": 2000, "emitSpeed": 0.0, "emitterShouldTrail": 1, @@ -86,14 +88,14 @@ "color": {"red": 200, "green": 170, "blue": 255}, "colorFinish": {"red": 0, "green": 134, "blue": 255}, "colorStart": {"red": 185, "green": 222, "blue": 255}, - "emitOrientation": {"w": -0.71, "x":0.0, "y":0.0, "z": 0.71}, + "emitOrientation": {"w": -0.71, "x": 0.0, "y": 0.0, "z": 0.71}, "emitAcceleration": {"x": 0.0, "y": 0.0, "z": 0.0}, "accelerationSpread": {"x": 0.0, "y": 0.0, "z": 0.0}, - "dimensions": {"x":0.05, "y": 0.05, "z": 0.05}, + "dimensions": {"x": 0.05, "y": 0.05, "z": 0.05}, "type": "ParticleEffect" }; - var currentHand = undefined; + var currentHand; var currentHandJointIndex = -1; var state = STATES.INACTIVE; var connectingInterval; @@ -183,7 +185,8 @@ function handToString(hand) { if (hand === Controller.Standard.RightHand) { return "RightHand"; - } else if (hand === Controller.Standard.LeftHand) { + } + if (hand === Controller.Standard.LeftHand) { return "LeftHand"; } debug("handToString called without valid hand! value: ", hand); @@ -193,7 +196,8 @@ function stringToHand(hand) { if (hand === "RightHand") { return Controller.Standard.RightHand; - } else if (hand === "LeftHand") { + } + if (hand === "LeftHand") { return Controller.Standard.LeftHand; } debug("stringToHand called with bad hand string:", hand); @@ -203,7 +207,8 @@ function handToHaptic(hand) { if (hand === Controller.Standard.RightHand) { return 1; - } else if (hand === Controller.Standard.LeftHand) { + } + if (hand === Controller.Standard.LeftHand) { return 0; } debug("handToHaptic called without a valid hand!"); @@ -231,11 +236,11 @@ // This returns the ideal hand joint index for the avatar. // [hand]middle1 -> [hand]index1 -> [hand] function getIdealHandJointIndex(avatar, hand) { - debug("got hand " + hand + " for avatar " + avatar.sessionUUID); - var handString = handToString(hand); - for (var i = 0; i < PREFERRER_HAND_JOINT_POSTFIX_ORDER.length; i++) { - var jointName = handString + PREFERRER_HAND_JOINT_POSTFIX_ORDER[i]; - var jointIndex = avatar.getJointIndex(jointName); + debug("get hand " + hand + " for avatar " + avatar.sessionUUID); + var suffixIndex, jointName, jointIndex, handString = handToString(hand); + for (suffixIndex = 0; suffixIndex < PREFERRER_HAND_JOINT_POSTFIX_ORDER.length; suffixIndex++) { + jointName = handString + PREFERRER_HAND_JOINT_POSTFIX_ORDER[suffixIndex]; + jointIndex = avatar.getJointIndex(jointName); if (jointIndex !== -1) { debug('found joint ' + jointName + ' (' + jointIndex + ')'); return jointIndex; @@ -255,7 +260,7 @@ return avatar.getJointPosition(handJointIndex); } - function shakeHandsAnimation(animationProperties) { + function shakeHandsAnimation() { // all we are doing here is moving the right hand to a spot // that is in front of and a bit above the hips. Basing how // far in front as scaling with the avatar's height (say hips @@ -325,58 +330,58 @@ } switch (state) { - case STATES.WAITING: - // no visualization while waiting - deleteParticleEffect(); - deleteMakeConnectionParticleEffect(); - stopHandshakeSound(); - break; - case STATES.CONNECTING: - var particleProps = {}; - // put the position between the 2 hands, if we have a connectingId. This - // helps define the plane in which the particles move. - positionFractionallyTowards(myHandPosition, otherHand, 0.5); - // now manage the rest of the entity - if (!particleEffect) { - particleRotationAngle = 0.0; - particleEmitRate = 500; - particleProps = PARTICLE_EFFECT_PROPS; - particleProps.isEmitting = 0; - particleProps.position = calcParticlePos(myHandPosition, otherHand, otherOrientation); - particleProps.parentID = MyAvatar.sessionUUID; - particleEffect = Entities.addEntity(particleProps, true); - } else { - particleProps.position = calcParticlePos(myHandPosition, otherHand, otherOrientation); - particleProps.isEmitting = 1; - Entities.editEntity(particleEffect, particleProps); - } - if (!makingConnectionParticleEffect) { - var props = MAKING_CONNECTION_PARTICLE_PROPS; - props.parentID = MyAvatar.sessionUUID; - makingConnectionEmitRate = 2000; - props.emitRate = makingConnectionEmitRate; - props.position = myHandPosition; - makingConnectionParticleEffect = Entities.addEntity(props, true); - } else { - makingConnectionEmitRate *= 0.5; - Entities.editEntity(makingConnectionParticleEffect, { - emitRate: makingConnectionEmitRate, - position: myHandPosition, - isEmitting: true - }); - } - break; - case STATES.MAKING_CONNECTION: - particleEmitRate = Math.max(50, particleEmitRate * 0.5); - Entities.editEntity(makingConnectionParticleEffect, {emitRate: 0, isEmitting: 0, position: myHandPosition}); - Entities.editEntity(particleEffect, { - position: calcParticlePos(myHandPosition, otherHand, otherOrientation), - emitRate: particleEmitRate + case STATES.WAITING: + // no visualization while waiting + deleteParticleEffect(); + deleteMakeConnectionParticleEffect(); + stopHandshakeSound(); + break; + case STATES.CONNECTING: + var particleProps = {}; + // put the position between the 2 hands, if we have a connectingId. This + // helps define the plane in which the particles move. + positionFractionallyTowards(myHandPosition, otherHand, 0.5); + // now manage the rest of the entity + if (!particleEffect) { + particleRotationAngle = 0.0; + particleEmitRate = 500; + particleProps = PARTICLE_EFFECT_PROPS; + particleProps.isEmitting = 0; + particleProps.position = calcParticlePos(myHandPosition, otherHand, otherOrientation); + particleProps.parentID = MyAvatar.sessionUUID; + particleEffect = Entities.addEntity(particleProps, true); + } else { + particleProps.position = calcParticlePos(myHandPosition, otherHand, otherOrientation); + particleProps.isEmitting = 1; + Entities.editEntity(particleEffect, particleProps); + } + if (!makingConnectionParticleEffect) { + var props = MAKING_CONNECTION_PARTICLE_PROPS; + props.parentID = MyAvatar.sessionUUID; + makingConnectionEmitRate = 2000; + props.emitRate = makingConnectionEmitRate; + props.position = myHandPosition; + makingConnectionParticleEffect = Entities.addEntity(props, true); + } else { + makingConnectionEmitRate *= 0.5; + Entities.editEntity(makingConnectionParticleEffect, { + emitRate: makingConnectionEmitRate, + position: myHandPosition, + isEmitting: true }); - break; - default: - debug("unexpected state", state); - break; + } + break; + case STATES.MAKING_CONNECTION: + particleEmitRate = Math.max(50, particleEmitRate * 0.5); + Entities.editEntity(makingConnectionParticleEffect, {emitRate: 0, isEmitting: 0, position: myHandPosition}); + Entities.editEntity(particleEffect, { + position: calcParticlePos(myHandPosition, otherHand, otherOrientation), + emitRate: particleEmitRate + }); + break; + default: + debug("unexpected state", state); + break; } } @@ -412,8 +417,42 @@ }); return nearestAvatar; } + function messageSend(message) { + Messages.sendMessage(MESSAGE_CHANNEL, JSON.stringify(message)); + } + function lookForWaitingAvatar() { + // we started with nobody close enough, but maybe I've moved + // or they did. Note that 2 people doing this race, so stop + // as soon as you have a connectingId (which means you got their + // message before noticing they were in range in this loop) + // just in case we re-enter before stopping + stopWaiting(); + debug("started looking for waiting avatars"); + waitingInterval = Script.setInterval(function () { + if (state === STATES.WAITING && !connectingId) { + // find the closest in-range avatar, and send connection request + var nearestAvatar = findNearestWaitingAvatar(); + if (nearestAvatar.avatar) { + connectingId = nearestAvatar.avatar; + connectingHandString = handToString(nearestAvatar.hand); + debug("sending connectionRequest to", connectingId); + messageSend({ + key: "connectionRequest", + id: connectingId, + hand: handToString(currentHand) + }); + } + } else { + // something happened, stop looking for avatars to connect + stopWaiting(); + debug("stopped looking for waiting avatars"); + } + }, WAITING_INTERVAL); + } + + var pollCount = 0, requestUrl = location.metaverseServerUrl + '/api/v1/user/connection_request'; // As currently implemented, we select the closest waiting avatar (if close enough) and send // them a connectionRequest. If nobody is close enough we send a waiting message, and wait for a // connectionRequest. If the 2 people who want to connect are both somewhat out of range when they @@ -510,9 +549,8 @@ debug("updateTriggers called - gripping", handToString(hand)); if (state !== STATES.INACTIVE) { return; - } else { - startHandshake(fromKeyboard); } + startHandshake(fromKeyboard); } else { // TODO: should we end handshake even when inactive? Ponder debug("updateTriggers called -- no longer gripping", handToString(hand)); @@ -524,47 +562,12 @@ } } - function messageSend(message) { - Messages.sendMessage(MESSAGE_CHANNEL, JSON.stringify(message)); - } - - function lookForWaitingAvatar() { - // we started with nobody close enough, but maybe I've moved - // or they did. Note that 2 people doing this race, so stop - // as soon as you have a connectingId (which means you got their - // message before noticing they were in range in this loop) - - // just in case we re-enter before stopping - stopWaiting(); - debug("started looking for waiting avatars"); - waitingInterval = Script.setInterval(function () { - if (state === STATES.WAITING && !connectingId) { - // find the closest in-range avatar, and send connection request - var nearestAvatar = findNearestWaitingAvatar(); - if (nearestAvatar.avatar) { - connectingId = nearestAvatar.avatar; - connectingHandString = handToString(nearestAvatar.hand); - debug("sending connectionRequest to", connectingId); - messageSend({ - key: "connectionRequest", - id: connectingId, - hand: handToString(currentHand) - }); - } - } else { - // something happened, stop looking for avatars to connect - stopWaiting(); - debug("stopped looking for waiting avatars"); - } - }, WAITING_INTERVAL); - } - /* There is a mini-state machine after entering STATES.makingConnection. We make a request (which might immediately succeed, fail, or neither. If we immediately fail, we tell the user. Otherwise, we wait MAKING_CONNECTION_TIMEOUT. At that time, we poll until success or fail. */ - var result, requestBody, pollCount = 0, requestUrl = location.metaverseServerUrl + '/api/v1/user/connection_request'; + var result, requestBody; function connectionRequestCompleted() { // Final result is in. Do effects. if (result.status === 'success') { // set earlier if (!successfulHandshakeInjector) { @@ -580,10 +583,15 @@ handToHaptic(currentHand)); // don't change state (so animation continues while gripped) // but do send a notification, by calling the slot that emits the signal for it - Window.makeConnection(true, result.connection.new_connection ? - "You and " + result.connection.username + " are now connected!" : result.connection.username); - UserActivityLogger.makeUserConnection(connectingId, true, result.connection.new_connection ? - "new connection" : "already connected"); + Window.makeConnection(true, + result.connection.new_connection ? + "You and " + result.connection.username + " are now connected!" : + result.connection.username); + UserActivityLogger.makeUserConnection(connectingId, + true, + result.connection.new_connection ? + "new connection" : + "already connected"); return; } // failed endHandshake(); @@ -658,13 +666,16 @@ // This will immediately set response if successful (e.g., the other guy got his request in first), // or immediate failure, and will otherwise poll (using the requestBody we just set). - request({ // + request({ uri: requestUrl, method: 'POST', json: true, body: {'user_connection_request': requestBody} }, handleConnectionResponseAndMaybeRepeat); } + function getConnectingHandJointIndex() { + return AvatarList.getAvatarIdentifiers().indexOf(connectingId) !== -1 ? getIdealHandJointIndex(AvatarList.getAvatar(connectingId), stringToHand(connectingHandString)) : -1; + } // we change states, start the connectionInterval where we check // to be sure the hand is still close enough. If not, we terminate @@ -676,8 +687,7 @@ // do we need to do this? connectingId = id; connectingHandString = hand; - connectingHandJointIndex = AvatarList.getAvatarIdentifiers().indexOf(connectingId) !== -1 ? - getIdealHandJointIndex(AvatarList.getAvatar(connectingId), stringToHand(connectingHandString)) : -1; + connectingHandJointIndex = getConnectingHandJointIndex(); state = STATES.CONNECTING; // play sound @@ -714,7 +724,7 @@ key: "done" }); startHandshake(); - } else if (count > CONNECTING_TIME/CONNECTING_INTERVAL) { + } else if (count > CONNECTING_TIME / CONNECTING_INTERVAL) { debug("made connection with " + id); makeConnection(id); stopConnecting(); @@ -753,127 +763,119 @@ debug(e); } switch (message.key) { - case "waiting": - // add this guy to waiting object. Any other message from this person will - // remove it from the list - waitingList[senderID] = message.hand; - break; - case "connectionRequest": - delete waitingList[senderID]; - if (state === STATES.WAITING && message.id === MyAvatar.sessionUUID && - (!connectingId || connectingId === senderID)) { - // you were waiting for a connection request, so send the ack. Or, you and the other - // guy raced and both send connectionRequests. Handle that too + case "waiting": + // add this guy to waiting object. Any other message from this person will + // remove it from the list + waitingList[senderID] = message.hand; + break; + case "connectionRequest": + delete waitingList[senderID]; + if (state === STATES.WAITING && message.id === MyAvatar.sessionUUID && (!connectingId || connectingId === senderID)) { + // you were waiting for a connection request, so send the ack. Or, you and the other + // guy raced and both send connectionRequests. Handle that too + connectingId = senderID; + connectingHandString = message.hand; + connectingHandJointIndex = getConnectingHandJointIndex(); + messageSend({ + key: "connectionAck", + id: senderID, + hand: handToString(currentHand) + }); + } else if (state === STATES.WAITING && connectingId === senderID) { + // the person you are trying to connect sent a request to someone else. See the + // if statement above. So, don't cry, just start the handshake over again + startHandshake(); + } + break; + case "connectionAck": + delete waitingList[senderID]; + if (state === STATES.WAITING && (!connectingId || connectingId === senderID)) { + if (message.id === MyAvatar.sessionUUID) { + // start connecting... connectingId = senderID; connectingHandString = message.hand; - connectingHandJointIndex = AvatarList.getAvatarIdentifiers().indexOf(connectingId) !== -1 ? - getIdealHandJointIndex(AvatarList.getAvatar(connectingId), stringToHand(connectingHandString)) : -1; - messageSend({ - key: "connectionAck", - id: senderID, - hand: handToString(currentHand) - }); - } else if (state === STATES.WAITING && connectingId === senderID) { - // the person you are trying to connect sent a request to someone else. See the - // if statement above. So, don't cry, just start the handshake over again + connectingHandJointIndex = getConnectingHandJointIndex(); + stopWaiting(); + startConnecting(senderID, connectingHandString); + } else if (connectingId) { + // this is for someone else (we lost race in connectionRequest), + // so lets start over startHandshake(); } - break; - case "connectionAck": - delete waitingList[senderID]; - if (state === STATES.WAITING && (!connectingId || connectingId === senderID)) { - if (message.id === MyAvatar.sessionUUID) { - // start connecting... - connectingId = senderID; - connectingHandString = message.hand; - connectingHandJointIndex = AvatarList.getAvatarIdentifiers().indexOf(connectingId) !== -1 ? - getIdealHandJointIndex(AvatarList.getAvatar(connectingId), stringToHand(connectingHandString)) : -1; - stopWaiting(); - startConnecting(senderID, connectingHandString); - } else if (connectingId) { - // this is for someone else (we lost race in connectionRequest), - // so lets start over + } + // TODO: check to see if we are waiting for this but the person we are connecting sent it to + // someone else, and try again + break; + case "connecting": + delete waitingList[senderID]; + if (state === STATES.WAITING && senderID === connectingId) { + // temporary logging + if (connectingHandString !== message.hand) { + debug("connecting hand", connectingHandString, "not same as connecting hand in message", message.hand); + } + connectingHandString = message.hand; + if (message.id !== MyAvatar.sessionUUID) { + // the person we were trying to connect is connecting to someone else + // so try again + startHandshake(); + break; + } + startConnecting(senderID, message.hand); + } + break; + case "done": + delete waitingList[senderID]; + if (state === STATES.CONNECTING && connectingId === senderID) { + // if they are done, and didn't connect us, terminate our + // connecting + if (message.connectionId !== MyAvatar.sessionUUID) { + stopConnecting(); + // now just call startHandshake. Should be ok to do so without a + // value for isKeyboard, as we should not change the animation + // state anyways (if any) + startHandshake(); + } + } else { + // if waiting or inactive, lets clear the connecting id. If in makingConnection, + // do nothing + if (state !== STATES.MAKING_CONNECTION && connectingId === senderID) { + connectingId = undefined; + connectingHandString = undefined; + connectingHandJointIndex = -1; + if (state !== STATES.INACTIVE) { startHandshake(); } } - // TODO: check to see if we are waiting for this but the person we are connecting sent it to - // someone else, and try again - break; - case "connecting": - delete waitingList[senderID]; - if (state === STATES.WAITING && senderID === connectingId) { - // temporary logging - if (connectingHandString !== message.hand) { - debug("connecting hand", connectingHandString, "not same as connecting hand in message", message.hand); - } - connectingHandString = message.hand; - if (message.id !== MyAvatar.sessionUUID) { - // the person we were trying to connect is connecting to someone else - // so try again - startHandshake(); - break; - } - startConnecting(senderID, message.hand); - } - break; - case "done": - delete waitingList[senderID]; - if (state === STATES.CONNECTING && connectingId === senderID) { - // if they are done, and didn't connect us, terminate our - // connecting - if (message.connectionId !== MyAvatar.sessionUUID) { - stopConnecting(); - // now just call startHandshake. Should be ok to do so without a - // value for isKeyboard, as we should not change the animation - // state anyways (if any) - startHandshake(); - } - } else { - // if waiting or inactive, lets clear the connecting id. If in makingConnection, - // do nothing - if (state !== STATES.MAKING_CONNECTION && connectingId === senderID) { - connectingId = undefined; - connectingHandString = undefined; - connectingHandJointIndex = -1; - if (state !== STATES.INACTIVE) { - startHandshake(); - } - } - } - break; - default: - debug("unknown message", message); - break; + } + break; + default: + debug("unknown message", message); + break; } } Messages.subscribe(MESSAGE_CHANNEL); Messages.messageReceived.connect(messageHandler); - function makeGripHandler(hand, animate) { // determine if we are gripping or un-gripping if (animate) { - return function(value) { + return function (value) { updateTriggers(value, true, hand); }; - - } else { - return function (value) { - updateTriggers(value, false, hand); - }; } + return function (value) { + updateTriggers(value, false, hand); + }; } function keyPressEvent(event) { - if ((event.text === "x") && !event.isAutoRepeat && !event.isShifted && !event.isMeta && !event.isControl && - !event.isAlt) { + if ((event.text === "x") && !event.isAutoRepeat && !event.isShifted && !event.isMeta && !event.isControl && !event.isAlt) { updateTriggers(1.0, true, Controller.Standard.RightHand); } } function keyReleaseEvent(event) { - if ((event.text === "x") && !event.isAutoRepeat && !event.isShifted && !event.isMeta && !event.isControl && - !event.isAlt) { + if ((event.text === "x") && !event.isAutoRepeat && !event.isShifted && !event.isMeta && !event.isControl && !event.isAlt) { updateTriggers(0.0, true, Controller.Standard.RightHand); } } From e2dd00bdc468a536f7fdb4c540e1b63b0147b34c Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sat, 29 Apr 2017 14:29:43 -0700 Subject: [PATCH 16/89] unique message for exceeding poll limit --- scripts/system/makeUserConnection.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index eda461f541..e3785e9d77 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -610,7 +610,7 @@ debug(response, 'pollCount', pollCount); if (pollCount++ >= POLL_LIMIT) { // server will expire, but let's not wait that long. debug('POLL LIMIT REACHED; TIMEOUT: expired message generated by CLIENT'); - result = {status: 'error', connection: 'expired'}; + result = {status: 'error', connection: 'no-partner-found'}; connectionRequestCompleted(); } else { // poll Script.setTimeout(function () { @@ -640,8 +640,6 @@ } } - // this should be where we make the appropriate connection call. For now just make the - // visualization change. function makeConnection(id) { // send done to let the connection know you have made connection. messageSend({ @@ -651,8 +649,7 @@ state = STATES.MAKING_CONNECTION; - // continue the haptic background until the timeout fires. When we make calls, we will have an interval - // probably, in which we do this. + // continue the haptic background until the timeout fires. Controller.triggerHapticPulse(HAPTIC_DATA.background.strength, MAKING_CONNECTION_TIMEOUT, handToHaptic(currentHand)); requestBody = {'node_id': cleanId(MyAvatar.sessionUUID), 'proposed_node_id': cleanId(id)}; // for use when repeating From 13e10873ce0f6c7f475d660a51c0c3ca364783f5 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sat, 29 Apr 2017 14:39:35 -0700 Subject: [PATCH 17/89] simplify animation load and code --- scripts/system/makeUserConnection.js | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index e3785e9d77..57a9764e6e 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -260,7 +260,20 @@ return avatar.getJointPosition(handJointIndex); } + var animationData = {}; function shakeHandsAnimation() { + return animationData; + } + function endHandshakeAnimation() { + if (animHandlerId) { + debug("removing animation"); + animHandlerId = MyAvatar.removeAnimationStateHandler(animHandlerId); + } + } + function startHandshakeAnimation() { + endHandshakeAnimation(); // just in case order of press/unpress is broken + debug("adding animation"); + // all we are doing here is moving the right hand to a spot // that is in front of and a bit above the hips. Basing how // far in front as scaling with the avatar's height (say hips @@ -273,7 +286,8 @@ } result.rightHandPosition = Vec3.multiply(offset, {x: -0.25, y: 0.8, z: 1.3}); result.rightHandRotation = Quat.fromPitchYawRollDegrees(90, 0, 90); - return result; + + animHandlerId = MyAvatar.addAnimationStateHandler(shakeHandsAnimation, []); } function positionFractionallyTowards(posA, posB, frac) { @@ -460,12 +474,7 @@ // waiting message. Either way, they will start connecting eachother at that point. function startHandshake(fromKeyboard) { if (fromKeyboard) { - debug("adding animation"); - // just in case order of press/unpress is broken - if (animHandlerId) { - animHandlerId = MyAvatar.removeAnimationStateHandler(animHandlerId); - } - animHandlerId = MyAvatar.addAnimationStateHandler(shakeHandsAnimation, []); + startHandshakeAnimation(); } debug("starting handshake for", currentHand); pollCount = 0; @@ -525,10 +534,7 @@ key: "done" }); - if (animHandlerId) { - debug("removing animation"); - MyAvatar.removeAnimationStateHandler(animHandlerId); - } + endHandshakeAnimation(); // No-op if we were successful, but this way we ensure that failures and abandoned handshakes don't leave us // in a weird state. request({uri: requestUrl, method: 'DELETE'}, debug); From 7f120febbd6c356f71d7581fe08828aa90109768 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sat, 29 Apr 2017 15:50:04 -0700 Subject: [PATCH 18/89] fix that --- scripts/system/makeUserConnection.js | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index 57a9764e6e..c2d86e71fd 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -261,6 +261,19 @@ } var animationData = {}; + function updateAnimationData() { + // all we are doing here is moving the right hand to a spot + // that is in front of and a bit above the hips. Basing how + // far in front as scaling with the avatar's height (say hips + // to head distance) + var headIndex = MyAvatar.getJointIndex("Head"); + var offset = 0.5; // default distance of hand in front of you + if (headIndex) { + offset = 0.8 * MyAvatar.getAbsoluteJointTranslationInObjectFrame(headIndex).y; + } + animationData.rightHandPosition = Vec3.multiply(offset, {x: -0.25, y: 0.8, z: 1.3}); + animationData.rightHandRotation = Quat.fromPitchYawRollDegrees(90, 0, 90); + } function shakeHandsAnimation() { return animationData; } @@ -273,20 +286,7 @@ function startHandshakeAnimation() { endHandshakeAnimation(); // just in case order of press/unpress is broken debug("adding animation"); - - // all we are doing here is moving the right hand to a spot - // that is in front of and a bit above the hips. Basing how - // far in front as scaling with the avatar's height (say hips - // to head distance) - var headIndex = MyAvatar.getJointIndex("Head"); - var offset = 0.5; // default distance of hand in front of you - var result = {}; - if (headIndex) { - offset = 0.8 * MyAvatar.getAbsoluteJointTranslationInObjectFrame(headIndex).y; - } - result.rightHandPosition = Vec3.multiply(offset, {x: -0.25, y: 0.8, z: 1.3}); - result.rightHandRotation = Quat.fromPitchYawRollDegrees(90, 0, 90); - + updateAnimationData(); animHandlerId = MyAvatar.addAnimationStateHandler(shakeHandsAnimation, []); } From 03e929ca18676788dc016fdd261b252fc31f6c08 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sat, 29 Apr 2017 18:25:33 -0700 Subject: [PATCH 19/89] clarify names, particularly hand vs handString --- scripts/system/makeUserConnection.js | 86 ++++++++++++++-------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index c2d86e71fd..5540f0c122 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -24,6 +24,7 @@ MAKING_CONNECTION: 3 }; var STATE_STRINGS = ["inactive", "waiting", "connecting", "makingConnection"]; + var HAND_STRING_PROPERTY = 'hand'; // Used in our message protocol. IWBNI we changed it to handString, but that would break compatability. var WAITING_INTERVAL = 100; // ms var CONNECTING_INTERVAL = 100; // ms var MAKING_CONNECTION_TIMEOUT = 800; // ms @@ -234,7 +235,7 @@ } // This returns the ideal hand joint index for the avatar. - // [hand]middle1 -> [hand]index1 -> [hand] + // [handString]middle1 -> [handString]index1 -> [handString] function getIdealHandJointIndex(avatar, hand) { debug("get hand " + hand + " for avatar " + avatar.sessionUUID); var suffixIndex, jointName, jointIndex, handString = handToString(hand); @@ -313,11 +314,11 @@ } } - function calcParticlePos(myHand, otherHand, otherOrientation, reset) { + function calcParticlePos(myHandPosition, otherHandPosition, otherOrientation, reset) { if (reset) { particleRotationAngle = 0.0; } - var position = positionFractionallyTowards(myHand, otherHand, 0.5); + var position = positionFractionallyTowards(myHandPosition, otherHandPosition, 0.5); particleRotationAngle += PARTICLE_ANGLE_INCREMENT; // about 0.5 hz var radius = Math.min(PARTICLE_RADIUS, PARTICLE_RADIUS * particleRotationAngle / 360); var axis = Vec3.mix(Quat.getFront(MyAvatar.orientation), Quat.inverse(Quat.getFront(otherOrientation)), 0.5); @@ -333,13 +334,13 @@ } var myHandPosition = getHandPosition(MyAvatar, currentHandJointIndex); - var otherHand; + var otherHandPosition; var otherOrientation; if (connectingId) { var other = AvatarList.getAvatar(connectingId); if (other) { otherOrientation = other.orientation; - otherHand = getHandPosition(other, connectingHandJointIndex); + otherHandPosition = getHandPosition(other, connectingHandJointIndex); } } @@ -354,18 +355,18 @@ var particleProps = {}; // put the position between the 2 hands, if we have a connectingId. This // helps define the plane in which the particles move. - positionFractionallyTowards(myHandPosition, otherHand, 0.5); + positionFractionallyTowards(myHandPosition, otherHandPosition, 0.5); // now manage the rest of the entity if (!particleEffect) { particleRotationAngle = 0.0; particleEmitRate = 500; particleProps = PARTICLE_EFFECT_PROPS; particleProps.isEmitting = 0; - particleProps.position = calcParticlePos(myHandPosition, otherHand, otherOrientation); + particleProps.position = calcParticlePos(myHandPosition, otherHandPosition, otherOrientation); particleProps.parentID = MyAvatar.sessionUUID; particleEffect = Entities.addEntity(particleProps, true); } else { - particleProps.position = calcParticlePos(myHandPosition, otherHand, otherOrientation); + particleProps.position = calcParticlePos(myHandPosition, otherHandPosition, otherOrientation); particleProps.isEmitting = 1; Entities.editEntity(particleEffect, particleProps); } @@ -389,7 +390,7 @@ particleEmitRate = Math.max(50, particleEmitRate * 0.5); Entities.editEntity(makingConnectionParticleEffect, {emitRate: 0, isEmitting: 0, position: myHandPosition}); Entities.editEntity(particleEffect, { - position: calcParticlePos(myHandPosition, otherHand, otherOrientation), + position: calcParticlePos(myHandPosition, otherHandPosition, otherOrientation), emitRate: particleEmitRate }); break; @@ -399,14 +400,14 @@ } } - function isNearby(id, hand) { + function isNearby(id, handString) { if (currentHand) { - var handPos = getHandPosition(MyAvatar, currentHandJointIndex); + var handPosition = getHandPosition(MyAvatar, currentHandJointIndex); var avatar = AvatarList.getAvatar(id); if (avatar) { - var otherHand = stringToHand(hand); + var otherHand = stringToHand(handString); var otherHandJointIndex = getIdealHandJointIndex(avatar, otherHand); - var distance = Vec3.distance(getHandPosition(avatar, otherHandJointIndex), handPos); + var distance = Vec3.distance(getHandPosition(avatar, otherHandJointIndex), handPosition); return (distance < MAX_AVATAR_DISTANCE); } } @@ -414,7 +415,7 @@ } function findNearestWaitingAvatar() { - var handPos = getHandPosition(MyAvatar, currentHandJointIndex); + var handPosition = getHandPosition(MyAvatar, currentHandJointIndex); var minDistance = MAX_AVATAR_DISTANCE; var nearestAvatar = {}; Object.keys(waitingList).forEach(function (identifier) { @@ -422,7 +423,7 @@ if (avatar) { var hand = stringToHand(waitingList[identifier]); var handJointIndex = getIdealHandJointIndex(avatar, hand); - var distance = Vec3.distance(getHandPosition(avatar, handJointIndex), handPos); + var distance = Vec3.distance(getHandPosition(avatar, handJointIndex), handPosition); if (distance < minDistance) { minDistance = distance; nearestAvatar = {avatar: identifier, hand: hand, avatarObject: avatar}; @@ -434,6 +435,10 @@ function messageSend(message) { Messages.sendMessage(MESSAGE_CHANNEL, JSON.stringify(message)); } + function handStringMessageSend(message, handString) { + message[HAND_STRING_PROPERTY] = handString; + messageSend(message); + } function lookForWaitingAvatar() { // we started with nobody close enough, but maybe I've moved @@ -452,11 +457,10 @@ connectingId = nearestAvatar.avatar; connectingHandString = handToString(nearestAvatar.hand); debug("sending connectionRequest to", connectingId); - messageSend({ + handStringMessageSend({ key: "connectionRequest", - id: connectingId, - hand: handToString(currentHand) - }); + id: connectingId + }, handToString(currentHand)); } } else { // something happened, stop looking for avatars to connect @@ -494,18 +498,16 @@ connectingHandJointIndex = getIdealHandJointIndex(nearestAvatar.avatarObject, nearestAvatar.hand); currentHandJointIndex = getIdealHandJointIndex(MyAvatar, currentHand); debug("sending connectionRequest to", connectingId); - messageSend({ + handStringMessageSend({ key: "connectionRequest", id: connectingId, - hand: handToString(currentHand) - }); + }, handToString(currentHand)); } else { // send waiting message debug("sending waiting message"); - messageSend({ + handStringMessageSend({ key: "waiting", - hand: handToString(currentHand) - }); + }, handToString(currentHand)); lookForWaitingAvatar(); } } @@ -684,12 +686,12 @@ // to be sure the hand is still close enough. If not, we terminate // the interval, go back to the waiting state. If we make it // the entire CONNECTING_TIME, we make the connection. - function startConnecting(id, hand) { + function startConnecting(id, handString) { var count = 0; - debug("connecting", id, "hand", hand); + debug("connecting", id, "hand", handString); // do we need to do this? connectingId = id; - connectingHandString = hand; + connectingHandString = handString; connectingHandJointIndex = getConnectingHandJointIndex(); state = STATES.CONNECTING; @@ -705,11 +707,10 @@ } // send message that we are connecting with them - messageSend({ + handStringMessageSend({ key: "connecting", - id: id, - hand: handToString(currentHand) - }); + id: id + }, handToString(currentHand)); Controller.triggerHapticPulse(HAPTIC_DATA.initial.strength, HAPTIC_DATA.initial.duration, handToHaptic(currentHand)); connectingInterval = Script.setInterval(function () { @@ -719,7 +720,7 @@ if (state !== STATES.CONNECTING) { debug("stopping connecting interval, state changed"); stopConnecting(); - } else if (!isNearby(id, hand)) { + } else if (!isNearby(id, handString)) { // gotta go back to waiting debug(id, "moved, back to waiting"); stopConnecting(); @@ -769,7 +770,7 @@ case "waiting": // add this guy to waiting object. Any other message from this person will // remove it from the list - waitingList[senderID] = message.hand; + waitingList[senderID] = message[HAND_STRING_PROPERTY]; break; case "connectionRequest": delete waitingList[senderID]; @@ -777,13 +778,12 @@ // you were waiting for a connection request, so send the ack. Or, you and the other // guy raced and both send connectionRequests. Handle that too connectingId = senderID; - connectingHandString = message.hand; + connectingHandString = message[HAND_STRING_PROPERTY]; connectingHandJointIndex = getConnectingHandJointIndex(); - messageSend({ + handStringMessageSend({ key: "connectionAck", id: senderID, - hand: handToString(currentHand) - }); + }, handToString(currentHand)); } else if (state === STATES.WAITING && connectingId === senderID) { // the person you are trying to connect sent a request to someone else. See the // if statement above. So, don't cry, just start the handshake over again @@ -796,7 +796,7 @@ if (message.id === MyAvatar.sessionUUID) { // start connecting... connectingId = senderID; - connectingHandString = message.hand; + connectingHandString = message[HAND_STRING_PROPERTY]; connectingHandJointIndex = getConnectingHandJointIndex(); stopWaiting(); startConnecting(senderID, connectingHandString); @@ -813,17 +813,17 @@ delete waitingList[senderID]; if (state === STATES.WAITING && senderID === connectingId) { // temporary logging - if (connectingHandString !== message.hand) { - debug("connecting hand", connectingHandString, "not same as connecting hand in message", message.hand); + if (connectingHandString !== message[HAND_STRING_PROPERTY]) { + debug("connecting hand", connectingHandString, "not same as connecting hand in message", message[HAND_STRING_PROPERTY]); } - connectingHandString = message.hand; + connectingHandString = message[HAND_STRING_PROPERTY]; if (message.id !== MyAvatar.sessionUUID) { // the person we were trying to connect is connecting to someone else // so try again startHandshake(); break; } - startConnecting(senderID, message.hand); + startConnecting(senderID, connectingHandString); } break; case "done": From 154af421808ffb6e429ebc493a09f88681ff0c44 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sat, 29 Apr 2017 19:39:50 -0700 Subject: [PATCH 20/89] setupCandidate --- scripts/system/makeUserConnection.js | 37 +++++++++++----------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index 5540f0c122..f45efafdc8 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -439,6 +439,19 @@ message[HAND_STRING_PROPERTY] = handString; messageSend(message); } + function setupCandidate() { // find the closest in-range avatar, send connection request, an return true. (Otherwise falsey) + var nearestAvatar = findNearestWaitingAvatar(); + if (nearestAvatar.avatar) { + connectingId = nearestAvatar.avatar; + connectingHandString = handToString(nearestAvatar.hand); + debug("sending connectionRequest to", connectingId); + handStringMessageSend({ + key: "connectionRequest", + id: connectingId + }, handToString(currentHand)); + return true; + } + } function lookForWaitingAvatar() { // we started with nobody close enough, but maybe I've moved @@ -451,17 +464,7 @@ debug("started looking for waiting avatars"); waitingInterval = Script.setInterval(function () { if (state === STATES.WAITING && !connectingId) { - // find the closest in-range avatar, and send connection request - var nearestAvatar = findNearestWaitingAvatar(); - if (nearestAvatar.avatar) { - connectingId = nearestAvatar.avatar; - connectingHandString = handToString(nearestAvatar.hand); - debug("sending connectionRequest to", connectingId); - handStringMessageSend({ - key: "connectionRequest", - id: connectingId - }, handToString(currentHand)); - } + setupCandidate(); } else { // something happened, stop looking for avatars to connect stopWaiting(); @@ -490,18 +493,8 @@ stopWaiting(); stopConnecting(); stopMakingConnection(); - - var nearestAvatar = findNearestWaitingAvatar(); - if (nearestAvatar.avatar) { - connectingId = nearestAvatar.avatar; - connectingHandString = handToString(nearestAvatar.hand); - connectingHandJointIndex = getIdealHandJointIndex(nearestAvatar.avatarObject, nearestAvatar.hand); + if (setupCandidate()) { currentHandJointIndex = getIdealHandJointIndex(MyAvatar, currentHand); - debug("sending connectionRequest to", connectingId); - handStringMessageSend({ - key: "connectionRequest", - id: connectingId, - }, handToString(currentHand)); } else { // send waiting message debug("sending waiting message"); From d3c9cb9574b6454131e790f84dad7a6d8a1e4315 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sun, 30 Apr 2017 08:04:46 -0700 Subject: [PATCH 21/89] standardize data init/clear --- scripts/system/makeUserConnection.js | 35 +++++++++++++--------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index f45efafdc8..92d6791617 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -452,6 +452,11 @@ return true; } } + function clearConnecting() { + connectingId = undefined; + connectingHandString = undefined; + connectingHandJointIndex = -1; + } function lookForWaitingAvatar() { // we started with nobody close enough, but maybe I've moved @@ -486,9 +491,7 @@ debug("starting handshake for", currentHand); pollCount = 0; state = STATES.WAITING; - connectingId = undefined; - connectingHandString = undefined; - connectingHandJointIndex = -1; + clearConnecting(); // just in case stopWaiting(); stopConnecting(); @@ -517,9 +520,7 @@ // as we ignore the key release event when inactive. See updateTriggers // below. state = STATES.INACTIVE; - connectingId = undefined; - connectingHandString = undefined; - connectingHandJointIndex = -1; + clearConnecting(); stopWaiting(); stopConnecting(); stopMakingConnection(); @@ -674,6 +675,11 @@ function getConnectingHandJointIndex() { return AvatarList.getAvatarIdentifiers().indexOf(connectingId) !== -1 ? getIdealHandJointIndex(AvatarList.getAvatar(connectingId), stringToHand(connectingHandString)) : -1; } + function setupConnecting(id, handString) { + connectingId = id; + connectingHandString = handString; + connectingHandJointIndex = getConnectingHandJointIndex(); + } // we change states, start the connectionInterval where we check // to be sure the hand is still close enough. If not, we terminate @@ -683,9 +689,7 @@ var count = 0; debug("connecting", id, "hand", handString); // do we need to do this? - connectingId = id; - connectingHandString = handString; - connectingHandJointIndex = getConnectingHandJointIndex(); + setupConnecting(id, handString); state = STATES.CONNECTING; // play sound @@ -770,9 +774,7 @@ if (state === STATES.WAITING && message.id === MyAvatar.sessionUUID && (!connectingId || connectingId === senderID)) { // you were waiting for a connection request, so send the ack. Or, you and the other // guy raced and both send connectionRequests. Handle that too - connectingId = senderID; - connectingHandString = message[HAND_STRING_PROPERTY]; - connectingHandJointIndex = getConnectingHandJointIndex(); + setupConnecting(senderID, message[HAND_STRING_PROPERTY]); handStringMessageSend({ key: "connectionAck", id: senderID, @@ -787,10 +789,7 @@ delete waitingList[senderID]; if (state === STATES.WAITING && (!connectingId || connectingId === senderID)) { if (message.id === MyAvatar.sessionUUID) { - // start connecting... - connectingId = senderID; - connectingHandString = message[HAND_STRING_PROPERTY]; - connectingHandJointIndex = getConnectingHandJointIndex(); + setupConnecting(senderID, message[HAND_STRING_PROPERTY]); stopWaiting(); startConnecting(senderID, connectingHandString); } else if (connectingId) { @@ -835,9 +834,7 @@ // if waiting or inactive, lets clear the connecting id. If in makingConnection, // do nothing if (state !== STATES.MAKING_CONNECTION && connectingId === senderID) { - connectingId = undefined; - connectingHandString = undefined; - connectingHandJointIndex = -1; + clearConnecting(); if (state !== STATES.INACTIVE) { startHandshake(); } From 3f99509a1964b69ab3a661f39a5cb59266852159 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sun, 30 Apr 2017 09:44:34 -0700 Subject: [PATCH 22/89] check your own location data on failure, and notify if it is wrong --- scripts/system/makeUserConnection.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index 92d6791617..2c984833b4 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -600,6 +600,22 @@ debug("failing with result data", result); // IWBNI we also did some fail sound/visual effect. Window.makeConnection(false, result.connection); + if (Account.isLoggedIn()) { // Give extra failure info + request(location.metaverseServerUrl + '/api/v1/users/' + Account.username + '/location', function (error, response) { + var message = ''; + if (error || response.status !== 'success') { + message = 'Unable to get location.'; + } else if (!response.data || !response.data.location) { + message = "Unexpected location value: " + JSON.stringify(response); + } else if (response.data.location.node_id !== cleanId(MyAvatar.sessionUUID)) { + message = 'Session identification does not match database. Maybe you are logged in on another machine? That would prevent handshakes.' + JSON.stringify(response) + MyAvatar.sessionUUID; + } + if (message) { + Window.makeConnection(false, message); + } + debug("account location:", message || 'ok'); + }); + } UserActivityLogger.makeUserConnection(connectingId, false, result.connection); } var POLL_INTERVAL_MS = 200, POLL_LIMIT = 5; @@ -612,7 +628,7 @@ debug(response, 'pollCount', pollCount); if (pollCount++ >= POLL_LIMIT) { // server will expire, but let's not wait that long. debug('POLL LIMIT REACHED; TIMEOUT: expired message generated by CLIENT'); - result = {status: 'error', connection: 'no-partner-found'}; + result = {status: 'error', connection: 'No logged-in partner found.'}; connectionRequestCompleted(); } else { // poll Script.setTimeout(function () { From 921242fbe5c10eb5e779fa0e06a7dba334858194 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sun, 30 Apr 2017 13:32:21 -0700 Subject: [PATCH 23/89] It may be possible to switch hands without endHandshake, and thus not reset currentHandJointIndex on updateTrigger. That was filled in later by startHanshake ONLY if there was already waiting avatars so that we didn't have wait. As a result, our distance measure would be from hips instead of hand. This changes it to always compute currentHandJointIndex on updateTriggers (and not elsewhere). --- scripts/system/makeUserConnection.js | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index 2c984833b4..eabd4d5dac 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -255,7 +255,6 @@ function getHandPosition(avatar, handJointIndex) { if (handJointIndex === -1) { debug("calling getHandPosition with no hand joint index! (returning avatar position but this is a BUG)"); - debug(new Error().stack); return avatar.position; } return avatar.getJointPosition(handJointIndex); @@ -439,7 +438,7 @@ message[HAND_STRING_PROPERTY] = handString; messageSend(message); } - function setupCandidate() { // find the closest in-range avatar, send connection request, an return true. (Otherwise falsey) + function setupCandidate() { // find the closest in-range avatar, send connection request, and return true. (Otherwise falsey) var nearestAvatar = findNearestWaitingAvatar(); if (nearestAvatar.avatar) { connectingId = nearestAvatar.avatar; @@ -483,7 +482,7 @@ // them a connectionRequest. If nobody is close enough we send a waiting message, and wait for a // connectionRequest. If the 2 people who want to connect are both somewhat out of range when they // initiate the shake, they will race to see who sends the connectionRequest after noticing the - // waiting message. Either way, they will start connecting eachother at that point. + // waiting message. Either way, they will start connecting each other at that point. function startHandshake(fromKeyboard) { if (fromKeyboard) { startHandshakeAnimation(); @@ -496,9 +495,7 @@ stopWaiting(); stopConnecting(); stopMakingConnection(); - if (setupCandidate()) { - currentHandJointIndex = getIdealHandJointIndex(MyAvatar, currentHand); - } else { + if (!setupCandidate()) { // send waiting message debug("sending waiting message"); handStringMessageSend({ @@ -541,10 +538,8 @@ debug("currentHand", currentHand, "ignoring messages from", hand); return; } - if (!currentHand) { - currentHand = hand; - currentHandJointIndex = getIdealHandJointIndex(MyAvatar, currentHand); - } + currentHand = hand; + currentHandJointIndex = getIdealHandJointIndex(MyAvatar, currentHand); // Always, in case of changed skeleton. // ok now, we are either initiating or quitting... var isGripping = value > GRIP_MIN; if (isGripping) { From 6af21525d5040ebcb3360c165b94d191d137b99a Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sun, 30 Apr 2017 14:19:33 -0700 Subject: [PATCH 24/89] remove extra joint search --- scripts/system/makeUserConnection.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index eabd4d5dac..0faaf00ded 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -399,14 +399,12 @@ } } - function isNearby(id, handString) { + function isNearby() { if (currentHand) { var handPosition = getHandPosition(MyAvatar, currentHandJointIndex); - var avatar = AvatarList.getAvatar(id); + var avatar = AvatarList.getAvatar(connectingId); if (avatar) { - var otherHand = stringToHand(handString); - var otherHandJointIndex = getIdealHandJointIndex(avatar, otherHand); - var distance = Vec3.distance(getHandPosition(avatar, otherHandJointIndex), handPosition); + var distance = Vec3.distance(getHandPosition(avatar, connectingHandJointIndex), handPosition); return (distance < MAX_AVATAR_DISTANCE); } } @@ -728,7 +726,7 @@ if (state !== STATES.CONNECTING) { debug("stopping connecting interval, state changed"); stopConnecting(); - } else if (!isNearby(id, handString)) { + } else if (!isNearby()) { // gotta go back to waiting debug(id, "moved, back to waiting"); stopConnecting(); @@ -800,9 +798,8 @@ delete waitingList[senderID]; if (state === STATES.WAITING && (!connectingId || connectingId === senderID)) { if (message.id === MyAvatar.sessionUUID) { - setupConnecting(senderID, message[HAND_STRING_PROPERTY]); stopWaiting(); - startConnecting(senderID, connectingHandString); + startConnecting(senderID, message[HAND_STRING_PROPERTY]); } else if (connectingId) { // this is for someone else (we lost race in connectionRequest), // so lets start over From 4839a6e8729785e8921c128b43907011835011af Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sun, 30 Apr 2017 15:50:30 -0700 Subject: [PATCH 25/89] convert foreign handString to jointIndex once, not often --- scripts/system/makeUserConnection.js | 83 +++++++++++----------------- 1 file changed, 31 insertions(+), 52 deletions(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index 0faaf00ded..4b5f74a2aa 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -104,7 +104,6 @@ var makingConnectionTimeout; var animHandlerId; var connectingId; - var connectingHandString; var connectingHandJointIndex = -1; var waitingList = {}; var particleEffect; @@ -119,7 +118,7 @@ function debug() { var stateString = "<" + STATE_STRINGS[state] + ">"; - var connecting = "[" + connectingId + "/" + connectingHandString + "]"; + var connecting = "[" + connectingId + "/" + connectingHandJointIndex + "]"; print.apply(null, [].concat.apply([LABEL, stateString, JSON.stringify(waitingList), connecting], [].map.call(arguments, JSON.stringify))); } @@ -194,17 +193,6 @@ return ""; } - function stringToHand(hand) { - if (hand === "RightHand") { - return Controller.Standard.RightHand; - } - if (hand === "LeftHand") { - return Controller.Standard.LeftHand; - } - debug("stringToHand called with bad hand string:", hand); - return 0; - } - function handToHaptic(hand) { if (hand === Controller.Standard.RightHand) { return 1; @@ -236,10 +224,10 @@ // This returns the ideal hand joint index for the avatar. // [handString]middle1 -> [handString]index1 -> [handString] - function getIdealHandJointIndex(avatar, hand) { - debug("get hand " + hand + " for avatar " + avatar.sessionUUID); - var suffixIndex, jointName, jointIndex, handString = handToString(hand); - for (suffixIndex = 0; suffixIndex < PREFERRER_HAND_JOINT_POSTFIX_ORDER.length; suffixIndex++) { + function getIdealHandJointIndex(avatar, handString) { + debug("get hand " + handString + " for avatar " + (avatar && avatar.sessionUUID)); + var suffixIndex, jointName, jointIndex; + for (suffixIndex = 0; suffixIndex < (avatar ? PREFERRER_HAND_JOINT_POSTFIX_ORDER.length : 0); suffixIndex++) { jointName = handString + PREFERRER_HAND_JOINT_POSTFIX_ORDER[suffixIndex]; jointIndex = avatar.getJointIndex(jointName); if (jointIndex !== -1) { @@ -418,12 +406,11 @@ Object.keys(waitingList).forEach(function (identifier) { var avatar = AvatarList.getAvatar(identifier); if (avatar) { - var hand = stringToHand(waitingList[identifier]); - var handJointIndex = getIdealHandJointIndex(avatar, hand); + var handJointIndex = waitingList[identifier]; var distance = Vec3.distance(getHandPosition(avatar, handJointIndex), handPosition); if (distance < minDistance) { minDistance = distance; - nearestAvatar = {avatar: identifier, hand: hand, avatarObject: avatar}; + nearestAvatar = {avatarId: identifier, jointIndex: handJointIndex}; } } }); @@ -432,26 +419,25 @@ function messageSend(message) { Messages.sendMessage(MESSAGE_CHANNEL, JSON.stringify(message)); } - function handStringMessageSend(message, handString) { - message[HAND_STRING_PROPERTY] = handString; + function handStringMessageSend(message) { + message[HAND_STRING_PROPERTY] = handToString(currentHand); messageSend(message); } function setupCandidate() { // find the closest in-range avatar, send connection request, and return true. (Otherwise falsey) var nearestAvatar = findNearestWaitingAvatar(); - if (nearestAvatar.avatar) { - connectingId = nearestAvatar.avatar; - connectingHandString = handToString(nearestAvatar.hand); + if (nearestAvatar.avatarId) { + connectingId = nearestAvatar.avatarId; + connectingHandJointIndex = nearestAvatar.jointIndex; debug("sending connectionRequest to", connectingId); handStringMessageSend({ key: "connectionRequest", id: connectingId - }, handToString(currentHand)); + }); return true; } } function clearConnecting() { connectingId = undefined; - connectingHandString = undefined; connectingHandJointIndex = -1; } @@ -498,7 +484,7 @@ debug("sending waiting message"); handStringMessageSend({ key: "waiting", - }, handToString(currentHand)); + }); lookForWaitingAvatar(); } } @@ -537,7 +523,7 @@ return; } currentHand = hand; - currentHandJointIndex = getIdealHandJointIndex(MyAvatar, currentHand); // Always, in case of changed skeleton. + currentHandJointIndex = getIdealHandJointIndex(MyAvatar, handToString(currentHand)); // Always, in case of changed skeleton. // ok now, we are either initiating or quitting... var isGripping = value > GRIP_MIN; if (isGripping) { @@ -681,24 +667,20 @@ body: {'user_connection_request': requestBody} }, handleConnectionResponseAndMaybeRepeat); } - function getConnectingHandJointIndex() { - return AvatarList.getAvatarIdentifiers().indexOf(connectingId) !== -1 ? getIdealHandJointIndex(AvatarList.getAvatar(connectingId), stringToHand(connectingHandString)) : -1; - } - function setupConnecting(id, handString) { + function setupConnecting(id, jointIndex) { connectingId = id; - connectingHandString = handString; - connectingHandJointIndex = getConnectingHandJointIndex(); + connectingHandJointIndex = jointIndex; } // we change states, start the connectionInterval where we check // to be sure the hand is still close enough. If not, we terminate // the interval, go back to the waiting state. If we make it // the entire CONNECTING_TIME, we make the connection. - function startConnecting(id, handString) { + function startConnecting(id, jointIndex) { var count = 0; - debug("connecting", id, "hand", handString); + debug("connecting", id, "hand", jointIndex); // do we need to do this? - setupConnecting(id, handString); + setupConnecting(id, jointIndex); state = STATES.CONNECTING; // play sound @@ -716,7 +698,7 @@ handStringMessageSend({ key: "connecting", id: id - }, handToString(currentHand)); + }); Controller.triggerHapticPulse(HAPTIC_DATA.initial.strength, HAPTIC_DATA.initial.duration, handToHaptic(currentHand)); connectingInterval = Script.setInterval(function () { @@ -760,13 +742,16 @@ | ---------- (done) ---------> | */ function messageHandler(channel, messageString, senderID) { + var message = {}; + function exisitingOrSearchedJointIndex() { // If this is a new connectingId, we'll need to find the jointIndex + return connectingId ? connectingHandJointIndex : getIdealHandJointIndex(AvatarList.getAvatar(senderID), message[HAND_STRING_PROPERTY]); + } if (channel !== MESSAGE_CHANNEL) { return; } if (MyAvatar.sessionUUID === senderID) { // ignore my own return; } - var message = {}; try { message = JSON.parse(messageString); } catch (e) { @@ -774,20 +759,19 @@ } switch (message.key) { case "waiting": - // add this guy to waiting object. Any other message from this person will - // remove it from the list - waitingList[senderID] = message[HAND_STRING_PROPERTY]; + // add this guy to waiting object. Any other message from this person will remove it from the list + waitingList[senderID] = getIdealHandJointIndex(AvatarList.getAvatar(senderID), message[HAND_STRING_PROPERTY]); break; case "connectionRequest": delete waitingList[senderID]; if (state === STATES.WAITING && message.id === MyAvatar.sessionUUID && (!connectingId || connectingId === senderID)) { // you were waiting for a connection request, so send the ack. Or, you and the other // guy raced and both send connectionRequests. Handle that too - setupConnecting(senderID, message[HAND_STRING_PROPERTY]); + setupConnecting(senderID, exisitingOrSearchedJointIndex()); handStringMessageSend({ key: "connectionAck", id: senderID, - }, handToString(currentHand)); + }); } else if (state === STATES.WAITING && connectingId === senderID) { // the person you are trying to connect sent a request to someone else. See the // if statement above. So, don't cry, just start the handshake over again @@ -799,7 +783,7 @@ if (state === STATES.WAITING && (!connectingId || connectingId === senderID)) { if (message.id === MyAvatar.sessionUUID) { stopWaiting(); - startConnecting(senderID, message[HAND_STRING_PROPERTY]); + startConnecting(senderID, exisitingOrSearchedJointIndex()); } else if (connectingId) { // this is for someone else (we lost race in connectionRequest), // so lets start over @@ -812,18 +796,13 @@ case "connecting": delete waitingList[senderID]; if (state === STATES.WAITING && senderID === connectingId) { - // temporary logging - if (connectingHandString !== message[HAND_STRING_PROPERTY]) { - debug("connecting hand", connectingHandString, "not same as connecting hand in message", message[HAND_STRING_PROPERTY]); - } - connectingHandString = message[HAND_STRING_PROPERTY]; if (message.id !== MyAvatar.sessionUUID) { // the person we were trying to connect is connecting to someone else // so try again startHandshake(); break; } - startConnecting(senderID, connectingHandString); + startConnecting(senderID, connectingHandJointIndex); } break; case "done": From 7ba5ea42293f9f967ecf85b1120d060c98addc0a Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Tue, 2 May 2017 11:29:51 -0700 Subject: [PATCH 26/89] On touch, grip is a trigger, not a button, so it can have lots of callbacks with slightly different values that we ignore. In that case, don't update hand/joint. --- scripts/system/makeUserConnection.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index 4b5f74a2aa..17979e9018 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -519,11 +519,9 @@ function updateTriggers(value, fromKeyboard, hand) { if (currentHand && hand !== currentHand) { - debug("currentHand", currentHand, "ignoring messages from", hand); + debug("currentHand", currentHand, "ignoring messages from", hand); // this can be a lot of spam on Touch. Should guard that someday. return; } - currentHand = hand; - currentHandJointIndex = getIdealHandJointIndex(MyAvatar, handToString(currentHand)); // Always, in case of changed skeleton. // ok now, we are either initiating or quitting... var isGripping = value > GRIP_MIN; if (isGripping) { @@ -531,6 +529,8 @@ if (state !== STATES.INACTIVE) { return; } + currentHand = hand; + currentHandJointIndex = getIdealHandJointIndex(MyAvatar, handToString(currentHand)); // Always, in case of changed skeleton. startHandshake(fromKeyboard); } else { // TODO: should we end handshake even when inactive? Ponder From 00a4fe044af536e9543a51ffa539e40eec29e3d5 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 28 Apr 2017 14:36:38 -0700 Subject: [PATCH 27/89] fix frozen bots --- libraries/recording/src/recording/Deck.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libraries/recording/src/recording/Deck.cpp b/libraries/recording/src/recording/Deck.cpp index 186516e01c..a4f154f85b 100644 --- a/libraries/recording/src/recording/Deck.cpp +++ b/libraries/recording/src/recording/Deck.cpp @@ -166,6 +166,12 @@ void Deck::processFrames() { if (!overLimit) { auto nextFrameTime = nextClip->positionFrameTime(); nextInterval = (int)Frame::frameTimeToMilliseconds(nextFrameTime - _position); + if (nextInterval < 0) { + qCWarning(recordingLog) << " Unexected nextInterval < 0 nextFrameTime:" << nextFrameTime + << "_position:" << _position << "-- setting nextInterval to 0"; + nextInterval = 0; + } + #ifdef WANT_RECORDING_DEBUG qCDebug(recordingLog) << "Now " << _position; qCDebug(recordingLog) << "Next frame time " << nextInterval; From de134adb9537150aee968db62b5084d1a01f8cac Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 28 Apr 2017 14:46:27 -0700 Subject: [PATCH 28/89] CR review --- libraries/recording/src/recording/Deck.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/recording/src/recording/Deck.cpp b/libraries/recording/src/recording/Deck.cpp index a4f154f85b..c9ac0524ad 100644 --- a/libraries/recording/src/recording/Deck.cpp +++ b/libraries/recording/src/recording/Deck.cpp @@ -167,7 +167,7 @@ void Deck::processFrames() { auto nextFrameTime = nextClip->positionFrameTime(); nextInterval = (int)Frame::frameTimeToMilliseconds(nextFrameTime - _position); if (nextInterval < 0) { - qCWarning(recordingLog) << " Unexected nextInterval < 0 nextFrameTime:" << nextFrameTime + qCWarning(recordingLog) << "Unexpected nextInterval < 0 nextFrameTime:" << nextFrameTime << "_position:" << _position << "-- setting nextInterval to 0"; nextInterval = 0; } From 8e12c1959d352c2ae456e313f6001f52e43416ab Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 3 May 2017 09:53:32 -0700 Subject: [PATCH 29/89] Removing logspam on toggling scripts window --- interface/resources/qml/hifi/dialogs/RunningScripts.qml | 3 --- 1 file changed, 3 deletions(-) diff --git a/interface/resources/qml/hifi/dialogs/RunningScripts.qml b/interface/resources/qml/hifi/dialogs/RunningScripts.qml index d95dbc2e55..0514bfbffd 100644 --- a/interface/resources/qml/hifi/dialogs/RunningScripts.qml +++ b/interface/resources/qml/hifi/dialogs/RunningScripts.qml @@ -34,9 +34,6 @@ ScrollingWindow { property var runningScriptsModel: ListModel { } property bool isHMD: false - onVisibleChanged: console.log("Running scripts visible changed to " + visible) - onShownChanged: console.log("Running scripts visible changed to " + visible) - Settings { category: "Overlay.RunningScripts" property alias x: root.x From a0a6dee7647ac20502e218e1258c7790f511ba38 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 2 May 2017 15:36:38 -0700 Subject: [PATCH 30/89] some cleanups, delete far-grabs during transition to near-grab rather than set ttl to zero --- .../system/controllers/handControllerGrab.js | 95 +++++++------------ 1 file changed, 36 insertions(+), 59 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 6a7ed55417..add513d2b3 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -14,7 +14,7 @@ /* global getEntityCustomData, flatten, Xform, Script, Quat, Vec3, MyAvatar, Entities, Overlays, Settings, Reticle, Controller, Camera, Messages, Mat4, getControllerWorldLocation, getGrabPointSphereOffset, - setGrabCommunications, Menu, HMD, isInEditMode */ + setGrabCommunications, Menu, HMD, isInEditMode, AvatarManager */ /* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ (function() { // BEGIN LOCAL_SCOPE @@ -75,7 +75,6 @@ var WEB_TOUCH_Y_OFFSET = 0.05; // how far forward (or back with a negative numbe // // distant manipulation // -var linearTimeScale = 0; var DISTANCE_HOLDING_RADIUS_FACTOR = 3.5; // multiplied by distance between hand and object var DISTANCE_HOLDING_ACTION_TIMEFRAME = 0.1; // how quickly objects move to their new position var DISTANCE_HOLDING_UNITY_MASS = 1200; // The mass at which the distance holding action timeframe is unmodified @@ -155,7 +154,6 @@ var INCHES_TO_METERS = 1.0 / 39.3701; // these control how long an abandoned pointer line or action will hang around var ACTION_TTL = 15; // seconds -var ACTION_TTL_ZERO = 0; // seconds var ACTION_TTL_REFRESH = 5; var PICKS_PER_SECOND_PER_HAND = 60; var MSECS_PER_SEC = 1000.0; @@ -193,7 +191,6 @@ var FORBIDDEN_GRAB_TYPES = ["Unknown", "Light", "PolyLine", "Zone"]; var holdEnabled = true; var nearGrabEnabled = true; var farGrabEnabled = true; -var farToNearGrab = false; var myAvatarScalingEnabled = true; var objectScalingEnabled = true; var mostRecentSearchingHand = RIGHT_HAND; @@ -300,14 +297,13 @@ function getFingerWorldLocation(hand) { // Object assign polyfill if (typeof Object.assign != 'function') { Object.assign = function(target, varArgs) { - 'use strict'; - if (target == null) { + if (target === null) { throw new TypeError('Cannot convert undefined or null to object'); } var to = Object(target); for (var index = 1; index < arguments.length; index++) { var nextSource = arguments[index]; - if (nextSource != null) { + if (nextSource !== null) { for (var nextKey in nextSource) { if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { to[nextKey] = nextSource[nextKey]; @@ -801,7 +797,7 @@ function calculateNearestStylusTarget(stylusTargets) { } return nearestStylusTarget; -}; +} // EntityPropertiesCache is a helper class that contains a cache of entity properties. // the hope is to prevent excess calls to Entity.getEntityProperties() @@ -1229,8 +1225,9 @@ function MyController(hand) { newState !== STATE_OVERLAY_LASER_TOUCHING)) { return; } - setGrabCommunications((newState === STATE_DISTANCE_HOLDING) || (newState === STATE_DISTANCE_ROTATING) - || (newState === STATE_NEAR_GRABBING)); + setGrabCommunications((newState === STATE_DISTANCE_HOLDING) || + (newState === STATE_DISTANCE_ROTATING) || + (newState === STATE_NEAR_GRABBING)); if (WANT_DEBUG || WANT_DEBUG_STATE) { var oldStateName = stateToName(this.state); var newStateName = stateToName(newState); @@ -1428,7 +1425,7 @@ function MyController(hand) { if (PICK_WITH_HAND_RAY) { this.overlayLineOff(); } - } + }; this.otherGrabbingLineOn = function(avatarPosition, entityPosition, color) { if (this.otherGrabbingLine === null) { @@ -1641,11 +1638,6 @@ function MyController(hand) { var tipPosition = this.stylusTip.position; - var candidates = { - entities: [], - overlays: [] - }; - // build list of stylus targets, near the stylusTip var stylusTargets = []; var candidateEntities = Entities.findEntities(tipPosition, WEB_DISPLAY_STYLUS_DISTANCE); @@ -1972,9 +1964,10 @@ function MyController(hand) { var debug = (WANT_DEBUG_SEARCH_NAME && props.name === WANT_DEBUG_SEARCH_NAME); var otherHandControllerState = this.getOtherHandController().state; - var okToEquipFromOtherHand = ((otherHandControllerState === STATE_NEAR_GRABBING - || otherHandControllerState === STATE_DISTANCE_HOLDING || otherHandControllerState === STATE_DISTANCE_ROTATING) - && this.getOtherHandController().grabbedThingID === hotspot.entityID); + var okToEquipFromOtherHand = ((otherHandControllerState === STATE_NEAR_GRABBING || + otherHandControllerState === STATE_DISTANCE_HOLDING || + otherHandControllerState === STATE_DISTANCE_ROTATING) && + this.getOtherHandController().grabbedThingID === hotspot.entityID); var hasParent = true; if (props.parentID === NULL_UUID) { hasParent = false; @@ -1999,7 +1992,7 @@ function MyController(hand) { return entityProps.cloneable; } return false; - } + }; this.entityIsGrabbable = function(entityID) { var grabbableProps = entityPropertiesCache.getGrabbableProps(entityID); var props = entityPropertiesCache.getProps(entityID); @@ -2346,7 +2339,7 @@ function MyController(hand) { this.otherGrabbingLineOff(); } else if (this.otherGrabbingUUID !== null) { if (this.triggerSmoothedGrab() && !isEditing() && farGrabEnabled && farSearching) { - var avatar = AvatarList.getAvatar(this.otherGrabbingUUID); + var avatar = AvatarManager.getAvatar(this.otherGrabbingUUID); var IN_FRONT_OF_AVATAR = { x: 0, y: 0.2, z: 0.4 }; // Up from hips and in front of avatar. var startPosition = Vec3.sum(avatar.position, Vec3.multiplyQbyV(avatar.rotation, IN_FRONT_OF_AVATAR)); var finishPisition = Vec3.sum(rayPickInfo.properties.position, // Entity's centroid. @@ -2720,7 +2713,8 @@ function MyController(hand) { var distanceToObject = Vec3.length(Vec3.subtract(MyAvatar.position, this.currentObjectPosition)); - var candidateHotSpotEntities = Entities.findEntities(controllerLocation.position,MAX_FAR_TO_NEAR_EQUIP_HOTSPOT_RADIUS); + var candidateHotSpotEntities = + Entities.findEntities(controllerLocation.position,MAX_FAR_TO_NEAR_EQUIP_HOTSPOT_RADIUS); entityPropertiesCache.addEntities(candidateHotSpotEntities); var potentialEquipHotspot = this.chooseBestEquipHotspotForFarToNearEquip(candidateHotSpotEntities); @@ -2730,40 +2724,23 @@ function MyController(hand) { this.grabbedThingID = potentialEquipHotspot.entityID; this.grabbedIsOverlay = false; - var success = Entities.updateAction(this.grabbedThingID, this.actionID, { - targetPosition: newTargetPosition, - linearTimeScale: this.distanceGrabTimescale(this.mass, distanceToObject), - targetRotation: this.currentObjectRotation, - angularTimeScale: this.distanceGrabTimescale(this.mass, distanceToObject), - ttl: ACTION_TTL_ZERO - }); - - if (success) { - this.actionTimeout = now + (ACTION_TTL_ZERO * MSECS_PER_SEC); - } else { - print("continueDistanceHolding -- updateAction failed"); - } + Entities.deleteAction(this.grabbedThingID, this.actionID); + this.actionID = null; + this.setState(STATE_HOLD, "equipping '" + entityPropertiesCache.getProps(this.grabbedThingID).name + "'"); return; } } var rayPositionOnEntity = Vec3.subtract(grabbedProperties.position, this.offsetPosition); //Far to Near Grab: If object is draw by user inside FAR_TO_NEAR_GRAB_MAX_DISTANCE, grab it - if (this.entityIsFarToNearGrabbable(rayPositionOnEntity, controllerLocation.position, FAR_TO_NEAR_GRAB_MAX_DISTANCE)) { + if (this.entityIsFarToNearGrabbable(rayPositionOnEntity, + controllerLocation.position, + FAR_TO_NEAR_GRAB_MAX_DISTANCE)) { this.farToNearGrab = true; - var success = Entities.updateAction(this.grabbedThingID, this.actionID, { - targetPosition: newTargetPosition, - linearTimeScale: this.distanceGrabTimescale(this.mass, distanceToObject), - targetRotation: this.currentObjectRotation, - angularTimeScale: this.distanceGrabTimescale(this.mass, distanceToObject), - ttl: ACTION_TTL_ZERO // Overriding ACTION_TTL,Assign ACTION_TTL_ZERO so that the object is dropped down immediately after the trigger is released. - }); - if (success) { - this.actionTimeout = now + (ACTION_TTL_ZERO * MSECS_PER_SEC); - } else { - print("continueDistanceHolding -- updateAction failed"); - } + Entities.deleteAction(this.grabbedThingID, this.actionID); + this.actionID = null; + this.setState(STATE_NEAR_GRABBING , "near grab entity '" + this.grabbedThingID + "'"); return; } @@ -2844,7 +2821,7 @@ function MyController(hand) { COLORS_GRAB_DISTANCE_HOLD, this.grabbedThingID); this.previousWorldControllerRotation = worldControllerRotation; - } + }; this.setupHoldAction = function() { this.actionID = Entities.addAction("hold", this.grabbedThingID, { @@ -3043,15 +3020,14 @@ function MyController(hand) { var worldEntities = Entities.findEntities(MyAvatar.position, 50); var count = 0; worldEntities.forEach(function(item) { - var item = Entities.getEntityProperties(item, ["name"]); - if (item.name.indexOf('-clone-' + grabbedProperties.id) !== -1) { + var itemWE = Entities.getEntityProperties(itemWE, ["name"]); + if (itemWE.name.indexOf('-clone-' + grabbedProperties.id) !== -1) { count++; } - }) + }); var limit = grabInfo.cloneLimit ? grabInfo.cloneLimit : 0; if (count >= limit && limit !== 0) { - delete limit; return; } @@ -3067,7 +3043,7 @@ function MyController(hand) { delete cUserData.grabbableKey.cloneable; delete cUserData.grabbableKey.cloneDynamic; delete cUserData.grabbableKey.cloneLimit; - delete cProperties.id + delete cProperties.id; cProperties.dynamic = dynamic; cProperties.locked = false; @@ -3133,7 +3109,7 @@ function MyController(hand) { _this.currentAngularVelocity = ZERO_VEC; _this.prevDropDetected = false; - } + }; if (isClone) { // 100 ms seems to be sufficient time to force the check even occur after the object has been initialized. @@ -3149,7 +3125,6 @@ function MyController(hand) { var ttl = ACTION_TTL; if (this.farToNearGrab) { - ttl = ACTION_TTL_ZERO; // farToNearGrab - Assign ACTION_TTL_ZERO so that, the object is dropped down immediately after the trigger is released. if(!this.triggerClicked){ this.farToNearGrab = false; } @@ -3322,7 +3297,7 @@ function MyController(hand) { this.maybeScale(props); } - if (this.actionID && this.actionTimeout - now < ttl * MSECS_PER_SEC) { + if (this.actionID && this.actionTimeout - now < ACTION_TTL_REFRESH * MSECS_PER_SEC) { // if less than a 5 seconds left, refresh the actions ttl var success = Entities.updateAction(this.grabbedThingID, this.actionID, { hand: this.hand === RIGHT_HAND ? "right" : "left", @@ -3761,10 +3736,12 @@ function MyController(hand) { var TABLET_MAX_TOUCH_DISTANCE = 0.01; if (this.stylusTarget) { - if (this.stylusTarget.distance > TABLET_MIN_TOUCH_DISTANCE && this.stylusTarget.distance < TABLET_MAX_TOUCH_DISTANCE) { + if (this.stylusTarget.distance > TABLET_MIN_TOUCH_DISTANCE && + this.stylusTarget.distance < TABLET_MAX_TOUCH_DISTANCE) { var POINTER_PRESS_TO_MOVE_DELAY = 0.33; // seconds if (this.deadspotExpired || this.touchingEnterTimer > POINTER_PRESS_TO_MOVE_DELAY || - distance2D(this.stylusTarget.position2D, this.touchingEnterStylusTarget.position2D) > this.deadspotRadius) { + distance2D(this.stylusTarget.position2D, + this.touchingEnterStylusTarget.position2D) > this.deadspotRadius) { sendTouchMoveEventToStylusTarget(this.hand, this.stylusTarget); this.deadspotExpired = true; } From 79604d3529e637b1504cd2618934dc7dc2b7d987 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 2 May 2017 15:48:12 -0700 Subject: [PATCH 31/89] AvatarList not AvatarManager --- scripts/system/controllers/handControllerGrab.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index add513d2b3..61d123c0c5 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -14,7 +14,7 @@ /* global getEntityCustomData, flatten, Xform, Script, Quat, Vec3, MyAvatar, Entities, Overlays, Settings, Reticle, Controller, Camera, Messages, Mat4, getControllerWorldLocation, getGrabPointSphereOffset, - setGrabCommunications, Menu, HMD, isInEditMode, AvatarManager */ + setGrabCommunications, Menu, HMD, isInEditMode, AvatarList */ /* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ (function() { // BEGIN LOCAL_SCOPE @@ -2339,7 +2339,7 @@ function MyController(hand) { this.otherGrabbingLineOff(); } else if (this.otherGrabbingUUID !== null) { if (this.triggerSmoothedGrab() && !isEditing() && farGrabEnabled && farSearching) { - var avatar = AvatarManager.getAvatar(this.otherGrabbingUUID); + var avatar = AvatarList.getAvatar(this.otherGrabbingUUID); var IN_FRONT_OF_AVATAR = { x: 0, y: 0.2, z: 0.4 }; // Up from hips and in front of avatar. var startPosition = Vec3.sum(avatar.position, Vec3.multiplyQbyV(avatar.rotation, IN_FRONT_OF_AVATAR)); var finishPisition = Vec3.sum(rayPickInfo.properties.position, // Entity's centroid. From 20051b970a4da51d40a6c744e027688e97720294 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 2 May 2017 16:20:59 -0700 Subject: [PATCH 32/89] make clone work again --- scripts/system/controllers/handControllerGrab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 61d123c0c5..48b04192f5 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -3020,7 +3020,7 @@ function MyController(hand) { var worldEntities = Entities.findEntities(MyAvatar.position, 50); var count = 0; worldEntities.forEach(function(item) { - var itemWE = Entities.getEntityProperties(itemWE, ["name"]); + var itemWE = Entities.getEntityProperties(item, ["name"]); if (itemWE.name.indexOf('-clone-' + grabbedProperties.id) !== -1) { count++; } From 0263021c0c5ad88026e1df9e865f2abcd0905085 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 3 May 2017 10:26:51 -0700 Subject: [PATCH 33/89] Fix loaded being reset to false for KTX resources --- .../src/model-networking/TextureCache.cpp | 2 +- libraries/networking/src/ResourceCache.cpp | 12 +++++++----- libraries/networking/src/ResourceCache.h | 7 ++++++- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 7745766177..7c1ec74eb8 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -421,7 +421,7 @@ void NetworkTexture::startRequestForNextMipLevel() { _ktxResourceState = PENDING_MIP_REQUEST; - init(); + init(false); float priority = -(float)_originalKtxDescriptor->header.numberOfMipmapLevels + (float)_lowestKnownPopulatedMip; setLoadPriority(this, priority); _url.setFragment(QString::number(_lowestKnownPopulatedMip - 1)); diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 8d4edab2d5..88ea68780b 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -533,13 +533,13 @@ void Resource::ensureLoading() { } void Resource::setLoadPriority(const QPointer& owner, float priority) { - if (!(_failedToLoad || _loaded)) { + if (!(_failedToLoad)) { _loadPriorities.insert(owner, priority); } } void Resource::setLoadPriorities(const QHash, float>& priorities) { - if (_failedToLoad || _loaded) { + if (_failedToLoad) { return; } for (QHash, float>::const_iterator it = priorities.constBegin(); @@ -549,7 +549,7 @@ void Resource::setLoadPriorities(const QHash, float>& prioriti } void Resource::clearLoadPriority(const QPointer& owner) { - if (!(_failedToLoad || _loaded)) { + if (!(_failedToLoad)) { _loadPriorities.remove(owner); } } @@ -612,10 +612,12 @@ void Resource::allReferencesCleared() { } } -void Resource::init() { +void Resource::init(bool resetLoaded) { _startedLoading = false; _failedToLoad = false; - _loaded = false; + if (resetLoaded) { + _loaded = false; + } _attempts = 0; _activeUrl = _url; diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 51c8d8554a..a908e11bcc 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -425,7 +425,7 @@ protected slots: void attemptRequest(); protected: - virtual void init(); + virtual void init(bool resetLoaded = true); /// Called by ResourceCache to begin loading this Resource. /// This method can be overriden to provide custom request functionality. If this is done, @@ -454,9 +454,14 @@ protected: QUrl _url; QUrl _activeUrl; ByteRange _requestByteRange; + + // _loaded == true means we are in a loaded and usable state. It is possible that there may still be + // active requests/loading while in this state. Example: Progressive KTX downloads, where higher resolution + // mips are being download. bool _startedLoading = false; bool _failedToLoad = false; bool _loaded = false; + QHash, float> _loadPriorities; QWeakPointer _self; QPointer _cache; From 2c19c5f04526326108ad9b4e7ab42a5a604be5a6 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 3 May 2017 10:27:20 -0700 Subject: [PATCH 34/89] Fix KTX creation not handling invalid storage --- libraries/ktx/src/ktx/Reader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index b22f262e85..440e2f048c 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -174,7 +174,7 @@ namespace ktx { } std::unique_ptr KTX::create(const StoragePointer& src) { - if (!src) { + if (!src || !(*src)) { return nullptr; } From 7999ed6f605b29d5082d408be779a0a8b82122b2 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 3 May 2017 10:28:00 -0700 Subject: [PATCH 35/89] Fix Resource::refresh for NetworkTexture --- .../src/model-networking/TextureCache.cpp | 21 +++++++++++++++++++ .../src/model-networking/TextureCache.h | 2 ++ libraries/networking/src/ResourceCache.h | 2 +- 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 7c1ec74eb8..969058c240 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -682,6 +682,27 @@ void NetworkTexture::loadContent(const QByteArray& content) { QThreadPool::globalInstance()->start(new ImageReader(_self, _url, content, _maxNumPixels)); } +void NetworkTexture::refresh() { + if ((_ktxHeaderRequest || _ktxMipRequest) && !loaded && !_failedToLoad) { + return; + } + if (_ktxHeaderRequest || _ktxMipRequest) { + if (_ktxHeaderRequest) { + _ktxHeaderRequest->disconnect(this); + _ktxHeaderRequest->deleteLater(); + _ktxHeaderRequest = nullptr; + } + if (_ktxMipRequest) { + _ktxMipRequest->disconnect(this); + _ktxMipRequest->deleteLater(); + _ktxMipRequest = nullptr; + } + ResourceCache::requestCompleted(_self); + } + + Resource::refresh(); +} + ImageReader::ImageReader(const QWeakPointer& resource, const QUrl& url, const QByteArray& data, int maxNumPixels) : _resource(resource), _url(url), diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index c7a7799216..aabc7fcb85 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -58,6 +58,8 @@ public: gpu::TexturePointer getFallbackTexture() const; + void refresh() override; + signals: void networkTextureCreated(const QWeakPointer& self); diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index a908e11bcc..f94e1e26d2 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -385,7 +385,7 @@ public: float getProgress() const { return (_bytesTotal <= 0) ? 0.0f : (float)_bytesReceived / _bytesTotal; } /// Refreshes the resource. - void refresh(); + virtual void refresh(); void setSelf(const QWeakPointer& self) { _self = self; } From a886963e20699192cfa0effccb312a0840f1fc97 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 3 May 2017 11:12:11 -0700 Subject: [PATCH 36/89] Fix refresh crash with ktx textures --- .../src/model-networking/TextureCache.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 969058c240..06ff891cb9 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -472,6 +472,10 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { void NetworkTexture::ktxHeaderRequestFinished() { Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA); + if (!_ktxHeaderRequest) { + return; + } + _ktxHeaderRequestFinished = true; maybeHandleFinishedInitialLoad(); } @@ -479,6 +483,10 @@ void NetworkTexture::ktxHeaderRequestFinished() { void NetworkTexture::ktxMipRequestFinished() { Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA || _ktxResourceState == REQUESTING_MIP); + if (!_ktxMipRequest) { + return; + } + if (_ktxResourceState == LOADING_INITIAL_DATA) { _ktxHighMipRequestFinished = true; maybeHandleFinishedInitialLoad(); @@ -683,7 +691,7 @@ void NetworkTexture::loadContent(const QByteArray& content) { } void NetworkTexture::refresh() { - if ((_ktxHeaderRequest || _ktxMipRequest) && !loaded && !_failedToLoad) { + if ((_ktxHeaderRequest || _ktxMipRequest) && !_loaded && !_failedToLoad) { return; } if (_ktxHeaderRequest || _ktxMipRequest) { From 7f6a1be2c7b2d294254d130d291ab2a2d7d44773 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 2 May 2017 19:57:09 -0400 Subject: [PATCH 37/89] continue idling while minimized --- interface/src/Application.cpp | 17 +++++++++++++++++ interface/src/Application.h | 2 ++ 2 files changed, 19 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9fa66262cc..f9669702c7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2530,6 +2530,11 @@ bool Application::event(QEvent* event) { isPaintingThrottled = false; + return true; + } else if ((int)event->type() == (int)Idle) { + float nsecsElapsed = (float)_lastTimeUpdated.nsecsElapsed(); + idle(nsecsElapsed); + return true; } @@ -6499,10 +6504,22 @@ void Application::activeChanged(Qt::ApplicationState state) { } void Application::windowMinimizedChanged(bool minimized) { + // initialize the _minimizedWindowTimer + static std::once_flag once; + std::call_once(once, [&] { + connect(&_minimizedWindowTimer, &QTimer::timeout, this, [] { + QCoreApplication::postEvent(QCoreApplication::instance(), new QEvent(static_cast(Idle)), Qt::HighEventPriority); + }); + }); + + // avoid rendering to the display plugin but continue posting Idle events, + // so that physics continues to simulate and the deadlock watchdog knows we're alive if (!minimized && !getActiveDisplayPlugin()->isActive()) { + _minimizedWindowTimer.stop(); getActiveDisplayPlugin()->activate(); } else if (minimized && getActiveDisplayPlugin()->isActive()) { getActiveDisplayPlugin()->deactivate(); + _minimizedWindowTimer.start(THROTTLED_SIM_FRAME_PERIOD_MS); } } diff --git a/interface/src/Application.h b/interface/src/Application.h index 041f1f8930..86b2335e62 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -138,6 +138,7 @@ public: enum Event { Present = DisplayPlugin::Present, Paint = Present + 1, + Idle = Paint + 1, Lambda = Paint + 1 }; @@ -536,6 +537,7 @@ private: RateCounter<> _avatarSimCounter; RateCounter<> _simCounter; + QTimer _minimizedWindowTimer; QElapsedTimer _timerStart; QElapsedTimer _lastTimeUpdated; From 0f9002d724e94d0776b5c5d2dca9f9a71934b1ef Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 1 May 2017 14:13:59 -0700 Subject: [PATCH 38/89] Prevent possible crash in texture buffering thread --- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 10 +++++++++- libraries/gpu-gl/src/gpu/gl/GLTexture.h | 4 +++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index 7f075a1698..e85e74ce73 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -150,6 +150,7 @@ GLExternalTexture::~GLExternalTexture() { // Variable sized textures using MemoryPressureState = GLVariableAllocationSupport::MemoryPressureState; using WorkQueue = GLVariableAllocationSupport::WorkQueue; +using TransferJobPointer = GLVariableAllocationSupport::TransferJobPointer; std::list GLVariableAllocationSupport::_memoryManagedTextures; MemoryPressureState GLVariableAllocationSupport::_memoryPressureState { MemoryPressureState::Idle }; @@ -159,6 +160,7 @@ WorkQueue GLVariableAllocationSupport::_transferQueue; WorkQueue GLVariableAllocationSupport::_promoteQueue; WorkQueue GLVariableAllocationSupport::_demoteQueue; TexturePointer GLVariableAllocationSupport::_currentTransferTexture; +TransferJobPointer GLVariableAllocationSupport::_currentTransferJob; size_t GLVariableAllocationSupport::_frameTexturesCreated { 0 }; #define OVERSUBSCRIBED_PRESSURE_VALUE 0.95f @@ -553,9 +555,15 @@ void GLVariableAllocationSupport::executeNextTransfer(const TexturePointer& curr if (!_pendingTransfers.empty()) { // Keeping hold of a strong pointer during the transfer ensures that the transfer thread cannot try to access a destroyed texture _currentTransferTexture = currentTexture; - if (_pendingTransfers.front()->tryTransfer()) { + // Keeping hold of a strong pointer to the transfer job ensures that if the pending transfer queue is rebuilt, the transfer job + // doesn't leave scope, causing a crash in the buffering thread + _currentTransferJob = _pendingTransfers.front(); + // transfer jobs use asynchronous buffering of the texture data because it may involve disk IO, so we execute a try here to determine if the buffering + // is complete + if (_currentTransferJob->tryTransfer()) { _pendingTransfers.pop(); _currentTransferTexture.reset(); + _currentTransferJob.reset(); } } } diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.h b/libraries/gpu-gl/src/gpu/gl/GLTexture.h index e0b8a63a99..2603b36b21 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.h +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.h @@ -86,7 +86,8 @@ public: void transfer(); }; - using TransferQueue = std::queue>; + using TransferJobPointer = std::shared_ptr; + using TransferQueue = std::queue; static MemoryPressureState _memoryPressureState; public: @@ -100,6 +101,7 @@ protected: static WorkQueue _promoteQueue; static WorkQueue _demoteQueue; static TexturePointer _currentTransferTexture; + static TransferJobPointer _currentTransferJob; static const uvec3 INITIAL_MIP_TRANSFER_DIMENSIONS; static const uvec3 MAX_TRANSFER_DIMENSIONS; static const size_t MAX_TRANSFER_SIZE; From e96c3e84b42842d8ca4b305a2ed68d8e0b409514 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 3 May 2017 13:58:49 -0700 Subject: [PATCH 39/89] if a script attempts to edit a non-entity, don't send the edit packet over the wire --- libraries/entities/src/EntityScriptingInterface.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 10479e931c..7b2f8b8554 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -407,9 +407,11 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& // return QUuid(); // } + bool entityFound = false; _entityTree->withReadLock([&] { EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID); if (entity) { + entityFound = true; // make sure the properties has a type, so that the encode can know which properties to include properties.setType(entity->getType()); bool hasTerseUpdateChanges = properties.hasTerseUpdateChanges(); @@ -464,7 +466,11 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& }); } }); - queueEntityMessage(PacketType::EntityEdit, entityID, properties); + if (entityFound) { + queueEntityMessage(PacketType::EntityEdit, entityID, properties); + } else { + qDebug() << "warning: attempted edit on unknown entity: " << id; + } return id; } From cb51aaab6189b085f2422f9cf13bbaa2e04060ad Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 3 May 2017 15:40:39 -0700 Subject: [PATCH 40/89] ARG --- interface/src/Application.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.h b/interface/src/Application.h index 86b2335e62..385aa0a286 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -139,7 +139,7 @@ public: Present = DisplayPlugin::Present, Paint = Present + 1, Idle = Paint + 1, - Lambda = Paint + 1 + Lambda = Idle + 1 }; // FIXME? Empty methods, do we still need them? From 14315155b5de932c702184edd5f3e08fe2f9c43c Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 3 May 2017 15:41:00 -0700 Subject: [PATCH 41/89] Remove the +1s --- interface/src/Application.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.h b/interface/src/Application.h index 385aa0a286..367d878dd4 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -137,9 +137,9 @@ public: enum Event { Present = DisplayPlugin::Present, - Paint = Present + 1, - Idle = Paint + 1, - Lambda = Idle + 1 + Paint, + Idle, + Lambda }; // FIXME? Empty methods, do we still need them? From 0fff7678bf4836feee84e33fa791e13daf0788ac Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 3 May 2017 19:07:46 -0700 Subject: [PATCH 42/89] code review --- libraries/entities/src/EntityScriptingInterface.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 7b2f8b8554..2189970cc7 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -407,7 +407,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& // return QUuid(); // } - bool entityFound = false; + bool entityFound { false }; _entityTree->withReadLock([&] { EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID); if (entity) { @@ -469,7 +469,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& if (entityFound) { queueEntityMessage(PacketType::EntityEdit, entityID, properties); } else { - qDebug() << "warning: attempted edit on unknown entity: " << id; + qCWarning(entities) << "attempted edit on unknown entity: " << id; } return id; } From ed09dcbdc948d102918efc09a233e50051a6adc3 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 3 May 2017 21:32:06 -0700 Subject: [PATCH 43/89] don't call editEntity on overlays --- scripts/system/libraries/WebTablet.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index e71aefc51e..5355677bf7 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -274,7 +274,8 @@ WebTablet.prototype.getLocation = function() { }; WebTablet.prototype.setHomeButtonTexture = function() { - Entities.editEntity(this.tabletEntityID, {textures: JSON.stringify({"tex.close": HOME_BUTTON_TEXTURE})}); + // TODO - is this still needed? + // Entities.editEntity(this.tabletEntityID, {textures: JSON.stringify({"tex.close": HOME_BUTTON_TEXTURE})}); }; WebTablet.prototype.setURL = function (url) { @@ -337,7 +338,8 @@ WebTablet.prototype.geometryChanged = function (geometry) { // compute position, rotation & parentJointIndex of the tablet this.calculateTabletAttachmentProperties(NO_HANDS, false, tabletProperties); - Entities.editEntity(this.tabletEntityID, tabletProperties); + // TODO -- is this still needed? + // Entities.editEntity(this.tabletEntityID, tabletProperties); } }; @@ -438,7 +440,8 @@ WebTablet.prototype.onHmdChanged = function () { var tabletProperties = {}; // compute position, rotation & parentJointIndex of the tablet this.calculateTabletAttachmentProperties(NO_HANDS, false, tabletProperties); - Entities.editEntity(this.tabletEntityID, tabletProperties); + // TODO -- is this still needed? + // Entities.editEntity(this.tabletEntityID, tabletProperties); // Full scene FXAA should be disabled on the overlay when the tablet in desktop mode. // This should make the text more readable. @@ -529,7 +532,8 @@ WebTablet.prototype.cameraModeChanged = function (newMode) { var tabletProperties = {}; // compute position, rotation & parentJointIndex of the tablet self.calculateTabletAttachmentProperties(NO_HANDS, false, tabletProperties); - Entities.editEntity(self.tabletEntityID, tabletProperties); + // TODO -- is this still needed? + // Entities.editEntity(self.tabletEntityID, tabletProperties); } }; From 69944cc76dcfa1485e6c8a613b188729023766f9 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 3 May 2017 21:32:49 -0700 Subject: [PATCH 44/89] add a way to get a SpatiallyNestable's name without knowing what its type is --- interface/src/ui/overlays/Base3DOverlay.cpp | 7 +++++++ interface/src/ui/overlays/Base3DOverlay.h | 5 +++++ libraries/avatars/src/AvatarData.h | 2 ++ libraries/entities/src/EntityItem.h | 2 +- .../entities/src/EntityScriptingInterface.cpp | 14 +++++++++++++- libraries/shared/src/SpatiallyNestable.h | 2 ++ 6 files changed, 30 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp index d7057c6faa..6f1167cfc9 100644 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ b/interface/src/ui/overlays/Base3DOverlay.cpp @@ -81,6 +81,10 @@ QVariantMap convertOverlayLocationFromScriptSemantics(const QVariantMap& propert void Base3DOverlay::setProperties(const QVariantMap& originalProperties) { QVariantMap properties = originalProperties; + if (properties["name"].isValid()) { + setName(properties["name"].toString()); + } + // carry over some legacy keys if (!properties["position"].isValid() && !properties["localPosition"].isValid()) { if (properties["p1"].isValid()) { @@ -207,6 +211,9 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) { } QVariant Base3DOverlay::getProperty(const QString& property) { + if (property == "name") { + return _name; + } if (property == "position" || property == "start" || property == "p1" || property == "point") { return vec3toVariant(getPosition()); } diff --git a/interface/src/ui/overlays/Base3DOverlay.h b/interface/src/ui/overlays/Base3DOverlay.h index a1c23e5cd8..29d4c093a9 100644 --- a/interface/src/ui/overlays/Base3DOverlay.h +++ b/interface/src/ui/overlays/Base3DOverlay.h @@ -26,6 +26,9 @@ public: virtual OverlayID getOverlayID() const override { return OverlayID(getID().toString()); } void setOverlayID(OverlayID overlayID) override { setID(overlayID); } + virtual QString getName() const override { return QString("Overlay:") + _name; } + void setName(QString name) { _name = name; } + // getters virtual bool is3D() const override { return true; } @@ -74,6 +77,8 @@ protected: bool _drawInFront; bool _isAA; bool _isGrabbable { false }; + + QString _name; }; #endif // hifi_Base3DOverlay_h diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index b4c79470de..a93f37839f 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -357,6 +357,8 @@ class AvatarData : public QObject, public SpatiallyNestable { public: + virtual QString getName() const override { return QString("Avatar:") + _displayName; } + static const QString FRAME_NAME; static void fromFrame(const QByteArray& frameData, AvatarData& avatar, bool useFrameSkeleton = true); diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index ff5f12b2f7..1896893b52 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -281,7 +281,7 @@ public: float getAngularDamping() const; void setAngularDamping(float value); - QString getName() const; + virtual QString getName() const override; void setName(const QString& value); QString getDebugName(); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 2189970cc7..c499ce4acc 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -469,7 +469,19 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& if (entityFound) { queueEntityMessage(PacketType::EntityEdit, entityID, properties); } else { - qCWarning(entities) << "attempted edit on unknown entity: " << id; + QString name = "unknown"; + QSharedPointer parentFinder = DependencyManager::get(); + if (parentFinder) { + bool success; + auto nestableWP = parentFinder->find(id, success, static_cast(_entityTree.get())); + if (success) { + auto nestable = nestableWP.lock(); + if (nestable) { + name = nestable->getName(); + } + } + } + qCWarning(entities) << "attempted edit on unknown entity: " << id << name; } return id; } diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index 820c8685d7..0f89016f54 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -42,6 +42,8 @@ public: virtual const QUuid getID() const; virtual void setID(const QUuid& id); + virtual QString getName() const { return "SpatiallyNestable"; } + virtual const QUuid getParentID() const; virtual void setParentID(const QUuid& parentID); From 9171995ed32f0fbbdf1ee05a32283e533cf40610 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 3 May 2017 21:41:00 -0700 Subject: [PATCH 45/89] add a comment --- libraries/entities/src/EntityScriptingInterface.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index c499ce4acc..153d9044e9 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -469,6 +469,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& if (entityFound) { queueEntityMessage(PacketType::EntityEdit, entityID, properties); } else { + // attempt to get a name for the mystery ID QString name = "unknown"; QSharedPointer parentFinder = DependencyManager::get(); if (parentFinder) { From 99ed2556f26baeb1fea2975e63469ab9d11d90bd Mon Sep 17 00:00:00 2001 From: Vladyslav Stelmakhovskyi Date: Thu, 4 May 2017 16:11:07 +0200 Subject: [PATCH 46/89] Do not send non primary button events to Tablet window --- interface/src/ui/overlays/Web3DOverlay.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index fedead5aa5..38bf5b293b 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -421,6 +421,11 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) { return; } + //do not send non primary button events to tablet + if (event.getButton() != PointerEvent::PrimaryButton) { + return; + } + QTouchEvent::TouchPoint point; point.setId(event.getID()); point.setState(touchPointState); From 74a82f92df94b21909532cd8b563177dc6a96409 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 4 May 2017 10:05:17 -0700 Subject: [PATCH 47/89] return null uuid for failure --- libraries/entities/src/EntityScriptingInterface.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 153d9044e9..a4ec7b7636 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -483,6 +483,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& } } qCWarning(entities) << "attempted edit on unknown entity: " << id << name; + return QUuid(); // null UUID to indicate failure } return id; } From 3f977a6743155ef8a3e135a893f87ce72be58638 Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 4 May 2017 10:00:32 -0700 Subject: [PATCH 48/89] Add a simple check on the uniform buffer slot to make sure it s valid --- libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp index 1d1f92b297..5bc9cfa56e 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp @@ -149,6 +149,10 @@ void GLBackend::resetUniformStage() { void GLBackend::do_setUniformBuffer(const Batch& batch, size_t paramOffset) { GLuint slot = batch._params[paramOffset + 3]._uint; + if (slot > GLBackend::MAX_NUM_UNIFORM_BUFFERS) { + // "GLBackend::do_setUniformBuffer: Trying to set a uniform Buffer at slot #" + slot + " which doesn't exist. MaxNumUniformBuffers = " + getMaxNumUniformBuffers()); + return; + } BufferPointer uniformBuffer = batch._buffers.get(batch._params[paramOffset + 2]._uint); GLintptr rangeStart = batch._params[paramOffset + 1]._uint; GLsizeiptr rangeSize = batch._params[paramOffset + 0]._uint; From 389d9405f98aa71165eaeaf997b65dba254d2eb6 Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 4 May 2017 10:18:14 -0700 Subject: [PATCH 49/89] Showing the comments --- libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp index 5bc9cfa56e..2d71e8ed78 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp @@ -149,8 +149,8 @@ void GLBackend::resetUniformStage() { void GLBackend::do_setUniformBuffer(const Batch& batch, size_t paramOffset) { GLuint slot = batch._params[paramOffset + 3]._uint; - if (slot > GLBackend::MAX_NUM_UNIFORM_BUFFERS) { - // "GLBackend::do_setUniformBuffer: Trying to set a uniform Buffer at slot #" + slot + " which doesn't exist. MaxNumUniformBuffers = " + getMaxNumUniformBuffers()); + if (slot >(GLuint)MAX_NUM_UNIFORM_BUFFERS) { + qCDebug(gpugllogging) << "GLBackend::do_setUniformBuffer: Trying to set a uniform Buffer at slot #" << slot << " which doesn't exist. MaxNumUniformBuffers = " << getMaxNumUniformBuffers(); return; } BufferPointer uniformBuffer = batch._buffers.get(batch._params[paramOffset + 2]._uint); @@ -207,7 +207,7 @@ void GLBackend::resetResourceStage() { void GLBackend::do_setResourceBuffer(const Batch& batch, size_t paramOffset) { GLuint slot = batch._params[paramOffset + 1]._uint; if (slot >= (GLuint)MAX_NUM_RESOURCE_BUFFERS) { - // "GLBackend::do_setResourceBuffer: Trying to set a resource Buffer at slot #" + slot + " which doesn't exist. MaxNumResourceBuffers = " + getMaxNumResourceBuffers()); + qCDebug(gpugllogging) << "GLBackend::do_setResourceBuffer: Trying to set a resource Buffer at slot #" << slot << " which doesn't exist. MaxNumResourceBuffers = " << getMaxNumResourceBuffers(); return; } @@ -237,7 +237,7 @@ void GLBackend::do_setResourceBuffer(const Batch& batch, size_t paramOffset) { void GLBackend::do_setResourceTexture(const Batch& batch, size_t paramOffset) { GLuint slot = batch._params[paramOffset + 1]._uint; if (slot >= (GLuint) MAX_NUM_RESOURCE_TEXTURES) { - // "GLBackend::do_setResourceTexture: Trying to set a resource Texture at slot #" + slot + " which doesn't exist. MaxNumResourceTextures = " + getMaxNumResourceTextures()); + qCDebug(gpugllogging) << "GLBackend::do_setResourceTexture: Trying to set a resource Texture at slot #" << slot << " which doesn't exist. MaxNumResourceTextures = " << getMaxNumResourceTextures(); return; } From ea2b188b64fb89ff51bd9bdf3c958757296afb4c Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 2 May 2017 17:07:50 -0700 Subject: [PATCH 50/89] Recompute Y from X/Z for normal maps --- libraries/render-utils/src/MaterialTextures.slh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/render-utils/src/MaterialTextures.slh b/libraries/render-utils/src/MaterialTextures.slh index 7b73896cc5..a530f4faf2 100644 --- a/libraries/render-utils/src/MaterialTextures.slh +++ b/libraries/render-utils/src/MaterialTextures.slh @@ -64,7 +64,9 @@ float fetchRoughnessMap(vec2 uv) { uniform sampler2D normalMap; vec3 fetchNormalMap(vec2 uv) { // unpack normal, swizzle to get into hifi tangent space with Y axis pointing out - return normalize(texture(normalMap, uv).rbg -vec3(0.5, 0.5, 0.5)); + vec2 t = 2.0 * (texture(normalMap, uv).rg - vec2(0.5, 0.5)); + vec2 t2 = t*t; + return normalize(vec3(t.x, sqrt(1 - t2.x - t2.y), t.y)); } <@endif@> From 10289f542319dfe689a51b581305d247eb2c77f9 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 2 May 2017 19:39:07 -0700 Subject: [PATCH 51/89] Format non compressed normal maps to new format. --- libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp | 3 ++ libraries/gpu/src/gpu/Format.cpp | 2 ++ libraries/gpu/src/gpu/Format.h | 1 + libraries/gpu/src/gpu/Texture_ktx.cpp | 2 ++ libraries/image/src/image/Image.cpp | 36 ++++++++++--------- 5 files changed, 28 insertions(+), 16 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp index 7f724cce65..12bfb8e70b 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp @@ -140,6 +140,7 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { switch (dstFormat.getSemantic()) { case gpu::RGB: case gpu::RGBA: + case gpu::XY: result = GL_RG8; break; default: @@ -289,6 +290,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E switch (dstFormat.getSemantic()) { case gpu::RGB: case gpu::RGBA: + case gpu::XY: texel.internalFormat = GL_RG8; break; default: @@ -516,6 +518,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E switch (dstFormat.getSemantic()) { case gpu::RGB: case gpu::RGBA: + case gpu::XY: texel.internalFormat = GL_RG8; break; default: diff --git a/libraries/gpu/src/gpu/Format.cpp b/libraries/gpu/src/gpu/Format.cpp index 19d8855bd9..a9c7d249a8 100644 --- a/libraries/gpu/src/gpu/Format.cpp +++ b/libraries/gpu/src/gpu/Format.cpp @@ -25,6 +25,8 @@ const Element Element::COLOR_COMPRESSED_SRGBA_MASK{ VEC4, NUINT8, COMPRESSED_BC1 const Element Element::COLOR_COMPRESSED_SRGBA{ VEC4, NUINT8, COMPRESSED_BC3_SRGBA }; const Element Element::COLOR_COMPRESSED_XY{ VEC4, NUINT8, COMPRESSED_BC5_XY }; +const Element Element::VEC2_XY{ VEC2, NUINT8, XY }; + const Element Element::COLOR_R11G11B10{ SCALAR, FLOAT, R11G11B10 }; const Element Element::VEC4F_COLOR_RGBA{ VEC4, FLOAT, RGBA }; const Element Element::VEC2F_UV{ VEC2, FLOAT, UV }; diff --git a/libraries/gpu/src/gpu/Format.h b/libraries/gpu/src/gpu/Format.h index f69e8d9386..9fadcecb22 100644 --- a/libraries/gpu/src/gpu/Format.h +++ b/libraries/gpu/src/gpu/Format.h @@ -234,6 +234,7 @@ public: static const Element COLOR_COMPRESSED_SRGBA_MASK; static const Element COLOR_COMPRESSED_SRGBA; static const Element COLOR_COMPRESSED_XY; + static const Element VEC2_XY; static const Element VEC4F_COLOR_RGBA; static const Element VEC2F_UV; static const Element VEC2F_XY; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index d2f93c0036..4952e7a83b 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -423,6 +423,8 @@ bool Texture::evalKTXFormat(const Element& mipFormat, const Element& texelFormat header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RGBA, ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8, ktx::GLBaseInternalFormat::RGBA); } else if (texelFormat == Format::COLOR_R_8 && mipFormat == Format::COLOR_R_8) { header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RED, ktx::GLInternalFormat_Uncompressed::R8, ktx::GLBaseInternalFormat::RED); + } else if (texelFormat == Format::VEC2_XY && mipFormat == Format::VEC2_XY) { + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RG, ktx::GLInternalFormat_Uncompressed::RG8, ktx::GLBaseInternalFormat::RG); } else if (texelFormat == Format::COLOR_COMPRESSED_SRGB && mipFormat == Format::COLOR_COMPRESSED_SRGB) { header.setCompressed(ktx::GLInternalFormat_Compressed::COMPRESSED_SRGB_S3TC_DXT1_EXT, ktx::GLBaseInternalFormat::RGB); } else if (texelFormat == Format::COLOR_COMPRESSED_SRGBA_MASK && mipFormat == Format::COLOR_COMPRESSED_SRGBA_MASK) { diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index 707a2e4496..63c7d0d8ab 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -290,6 +290,19 @@ void generateMips(gpu::Texture* texture, QImage& image, int face = -1) { float inputGamma = 2.2f; float outputGamma = 2.2f; + nvtt::InputOptions inputOptions; + inputOptions.setTextureLayout(textureType, width, height); + inputOptions.setMipmapData(data, width, height); + + inputOptions.setFormat(inputFormat); + inputOptions.setGamma(inputGamma, outputGamma); + inputOptions.setAlphaMode(alphaMode); + inputOptions.setWrapMode(wrapMode); + inputOptions.setRoundMode(roundMode); + + inputOptions.setMipmapGeneration(true); + inputOptions.setMipmapFilter(nvtt::MipmapFilter_Box); + nvtt::CompressionOptions compressionOptions; compressionOptions.setQuality(nvtt::Quality_Production); @@ -346,26 +359,17 @@ void generateMips(gpu::Texture* texture, QImage& image, int face = -1) { compressionOptions.setFormat(nvtt::Format_RGB); compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm); compressionOptions.setPixelFormat(8, 0, 0, 0); + } else if (mipFormat == gpu::Element::VEC2_XY) { + inputOptions.setNormalMap(true); + compressionOptions.setFormat(nvtt::Format_RGBA); + compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm); + compressionOptions.setPixelFormat(8, 8, 0, 0); } else { qCWarning(imagelogging) << "Unknown mip format"; Q_UNREACHABLE(); return; } - - nvtt::InputOptions inputOptions; - inputOptions.setTextureLayout(textureType, width, height); - inputOptions.setMipmapData(data, width, height); - - inputOptions.setFormat(inputFormat); - inputOptions.setGamma(inputGamma, outputGamma); - inputOptions.setAlphaMode(alphaMode); - inputOptions.setWrapMode(wrapMode); - inputOptions.setRoundMode(roundMode); - - inputOptions.setMipmapGeneration(true); - inputOptions.setMipmapFilter(nvtt::MipmapFilter_Box); - nvtt::OutputOptions outputOptions; outputOptions.setOutputHeader(false); MyOutputHandler outputHandler(texture, face); @@ -548,8 +552,8 @@ gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(const QImag gpu::Element formatMip = gpu::Element::COLOR_COMPRESSED_XY; gpu::Element formatGPU = gpu::Element::COLOR_COMPRESSED_XY; #else - gpu::Element formatMip = gpu::Element::COLOR_RGBA_32; - gpu::Element formatGPU = gpu::Element::COLOR_RGBA_32; + gpu::Element formatMip = gpu::Element::VEC2_XY; + gpu::Element formatGPU = gpu::Element::VEC2_XY; #endif theTexture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)); From 542923839dc75a4e55297d7a4df9eddf03432e4b Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 3 May 2017 15:54:07 -0700 Subject: [PATCH 52/89] Change name to VEC2NU8_XY --- libraries/gpu/src/gpu/Format.cpp | 2 +- libraries/gpu/src/gpu/Format.h | 2 +- libraries/gpu/src/gpu/Texture_ktx.cpp | 2 +- libraries/image/src/image/Image.cpp | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libraries/gpu/src/gpu/Format.cpp b/libraries/gpu/src/gpu/Format.cpp index a9c7d249a8..43bcd35b88 100644 --- a/libraries/gpu/src/gpu/Format.cpp +++ b/libraries/gpu/src/gpu/Format.cpp @@ -25,7 +25,7 @@ const Element Element::COLOR_COMPRESSED_SRGBA_MASK{ VEC4, NUINT8, COMPRESSED_BC1 const Element Element::COLOR_COMPRESSED_SRGBA{ VEC4, NUINT8, COMPRESSED_BC3_SRGBA }; const Element Element::COLOR_COMPRESSED_XY{ VEC4, NUINT8, COMPRESSED_BC5_XY }; -const Element Element::VEC2_XY{ VEC2, NUINT8, XY }; +const Element Element::VEC2NU8_XY{ VEC2, NUINT8, XY }; const Element Element::COLOR_R11G11B10{ SCALAR, FLOAT, R11G11B10 }; const Element Element::VEC4F_COLOR_RGBA{ VEC4, FLOAT, RGBA }; diff --git a/libraries/gpu/src/gpu/Format.h b/libraries/gpu/src/gpu/Format.h index 9fadcecb22..8b7bbdcbed 100644 --- a/libraries/gpu/src/gpu/Format.h +++ b/libraries/gpu/src/gpu/Format.h @@ -234,7 +234,7 @@ public: static const Element COLOR_COMPRESSED_SRGBA_MASK; static const Element COLOR_COMPRESSED_SRGBA; static const Element COLOR_COMPRESSED_XY; - static const Element VEC2_XY; + static const Element VEC2NU8_XY; static const Element VEC4F_COLOR_RGBA; static const Element VEC2F_UV; static const Element VEC2F_XY; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 4952e7a83b..7c61540043 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -423,7 +423,7 @@ bool Texture::evalKTXFormat(const Element& mipFormat, const Element& texelFormat header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RGBA, ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8, ktx::GLBaseInternalFormat::RGBA); } else if (texelFormat == Format::COLOR_R_8 && mipFormat == Format::COLOR_R_8) { header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RED, ktx::GLInternalFormat_Uncompressed::R8, ktx::GLBaseInternalFormat::RED); - } else if (texelFormat == Format::VEC2_XY && mipFormat == Format::VEC2_XY) { + } else if (texelFormat == Format::VEC2NU8_XY && mipFormat == Format::VEC2NU8_XY) { header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RG, ktx::GLInternalFormat_Uncompressed::RG8, ktx::GLBaseInternalFormat::RG); } else if (texelFormat == Format::COLOR_COMPRESSED_SRGB && mipFormat == Format::COLOR_COMPRESSED_SRGB) { header.setCompressed(ktx::GLInternalFormat_Compressed::COMPRESSED_SRGB_S3TC_DXT1_EXT, ktx::GLBaseInternalFormat::RGB); diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index 63c7d0d8ab..825b07003b 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -359,7 +359,7 @@ void generateMips(gpu::Texture* texture, QImage& image, int face = -1) { compressionOptions.setFormat(nvtt::Format_RGB); compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm); compressionOptions.setPixelFormat(8, 0, 0, 0); - } else if (mipFormat == gpu::Element::VEC2_XY) { + } else if (mipFormat == gpu::Element::VEC2NU8_XY) { inputOptions.setNormalMap(true); compressionOptions.setFormat(nvtt::Format_RGBA); compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm); @@ -552,8 +552,8 @@ gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(const QImag gpu::Element formatMip = gpu::Element::COLOR_COMPRESSED_XY; gpu::Element formatGPU = gpu::Element::COLOR_COMPRESSED_XY; #else - gpu::Element formatMip = gpu::Element::VEC2_XY; - gpu::Element formatGPU = gpu::Element::VEC2_XY; + gpu::Element formatMip = gpu::Element::VEC2NU8_XY; + gpu::Element formatGPU = gpu::Element::VEC2NU8_XY; #endif theTexture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)); From c8a9b02c7a1fe873ab21c6190231793147d8440a Mon Sep 17 00:00:00 2001 From: Vladyslav Stelmakhovskyi Date: Thu, 4 May 2017 20:26:02 +0200 Subject: [PATCH 53/89] Block only secondary button --- interface/src/ui/overlays/Web3DOverlay.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 38bf5b293b..ecc63801fc 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -421,8 +421,10 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) { return; } - //do not send non primary button events to tablet - if (event.getButton() != PointerEvent::PrimaryButton) { + //do not send secondary button events to tablet + if (event.getButton() == PointerEvent::SecondaryButton || + //do not block composed events + event.getButtons() == PointerEvent::SecondaryButton) { return; } From f728ae795595550182cc45554f85f020a2a5caa6 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 4 May 2017 12:38:27 -0700 Subject: [PATCH 54/89] Remove unecessary normalize --- libraries/render-utils/src/MaterialTextures.slh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/MaterialTextures.slh b/libraries/render-utils/src/MaterialTextures.slh index a530f4faf2..e694935361 100644 --- a/libraries/render-utils/src/MaterialTextures.slh +++ b/libraries/render-utils/src/MaterialTextures.slh @@ -66,7 +66,7 @@ vec3 fetchNormalMap(vec2 uv) { // unpack normal, swizzle to get into hifi tangent space with Y axis pointing out vec2 t = 2.0 * (texture(normalMap, uv).rg - vec2(0.5, 0.5)); vec2 t2 = t*t; - return normalize(vec3(t.x, sqrt(1 - t2.x - t2.y), t.y)); + return vec3(t.x, sqrt(1 - t2.x - t2.y), t.y); } <@endif@> From 87a685c4f9302ba15bc9b02237eacb2d2982fe68 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 4 May 2017 13:41:20 -0700 Subject: [PATCH 55/89] Fix OSX warning in TextureCache --- .../model-networking/src/model-networking/TextureCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 06ff891cb9..9653cde7d8 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -705,7 +705,7 @@ void NetworkTexture::refresh() { _ktxMipRequest->deleteLater(); _ktxMipRequest = nullptr; } - ResourceCache::requestCompleted(_self); + TextureCache::requestCompleted(_self); } Resource::refresh(); From fe36e6a793c2a7dcffd0777d97466d5ab9ea058b Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 4 May 2017 16:45:12 -0400 Subject: [PATCH 56/89] fix JSConsole script message handlers and about: filename --- interface/src/ui/JSConsole.cpp | 13 +++++++------ interface/src/ui/JSConsole.h | 5 +++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/interface/src/ui/JSConsole.cpp b/interface/src/ui/JSConsole.cpp index 7700874d9a..2d21d1d7ec 100644 --- a/interface/src/ui/JSConsole.cpp +++ b/interface/src/ui/JSConsole.cpp @@ -33,6 +33,8 @@ const QString RESULT_ERROR_STYLE = "color: #d13b22;"; const QString GUTTER_PREVIOUS_COMMAND = "<"; const QString GUTTER_ERROR = "X"; +const QString JSConsole::_consoleFileName { "about:console" }; + JSConsole::JSConsole(QWidget* parent, ScriptEngine* scriptEngine) : QWidget(parent), _ui(new Ui::Console), @@ -84,8 +86,8 @@ void JSConsole::setScriptEngine(ScriptEngine* scriptEngine) { } // if scriptEngine is NULL then create one and keep track of it using _ownScriptEngine - _ownScriptEngine = scriptEngine == NULL; - _scriptEngine = _ownScriptEngine ? DependencyManager::get()->loadScript(QString(), false) : scriptEngine; + _ownScriptEngine = (scriptEngine == NULL); + _scriptEngine = _ownScriptEngine ? DependencyManager::get()->loadScript(_consoleFileName, false) : scriptEngine; connect(_scriptEngine, &ScriptEngine::printedMessage, this, &JSConsole::handlePrint); connect(_scriptEngine, &ScriptEngine::errorMessage, this, &JSConsole::handleError); @@ -107,11 +109,10 @@ void JSConsole::executeCommand(const QString& command) { QScriptValue JSConsole::executeCommandInWatcher(const QString& command) { QScriptValue result; - static const QString filename = "JSConcole"; QMetaObject::invokeMethod(_scriptEngine, "evaluate", Qt::ConnectionType::BlockingQueuedConnection, Q_RETURN_ARG(QScriptValue, result), Q_ARG(const QString&, command), - Q_ARG(const QString&, filename)); + Q_ARG(const QString&, _consoleFileName)); return result; } @@ -134,12 +135,12 @@ void JSConsole::commandFinished() { resetCurrentCommandHistory(); } -void JSConsole::handleError(const QString& scriptName, const QString& message) { +void JSConsole::handleError(const QString& message, const QString& scriptName) { Q_UNUSED(scriptName); appendMessage(GUTTER_ERROR, "" + message.toHtmlEscaped() + ""); } -void JSConsole::handlePrint(const QString& scriptName, const QString& message) { +void JSConsole::handlePrint(const QString& message, const QString& scriptName) { Q_UNUSED(scriptName); appendMessage("", message); } diff --git a/interface/src/ui/JSConsole.h b/interface/src/ui/JSConsole.h index d5f5aff301..938b670e78 100644 --- a/interface/src/ui/JSConsole.h +++ b/interface/src/ui/JSConsole.h @@ -47,8 +47,8 @@ protected: protected slots: void scrollToBottom(); void resizeTextInput(); - void handlePrint(const QString& scriptName, const QString& message); - void handleError(const QString& scriptName, const QString& message); + void handlePrint(const QString& message, const QString& scriptName); + void handleError(const QString& message, const QString& scriptName); void commandFinished(); private: @@ -66,6 +66,7 @@ private: bool _ownScriptEngine; QString _rootCommand; ScriptEngine* _scriptEngine; + static const QString _consoleFileName; }; From 8cea7f6a62a8d0a7c006b344f1e703aae873146a Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 4 May 2017 16:54:16 -0400 Subject: [PATCH 57/89] add test for print/Script.print behavior --- scripts/developer/tests/printTest.js | 34 ++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 scripts/developer/tests/printTest.js diff --git a/scripts/developer/tests/printTest.js b/scripts/developer/tests/printTest.js new file mode 100644 index 0000000000..b94ff0c54b --- /dev/null +++ b/scripts/developer/tests/printTest.js @@ -0,0 +1,34 @@ +/* eslint-env jasmine */ + +// this test generates sample print, Script.print, etc. output + +main(); + +function main() { + // to match with historical behavior, Script.print(message) output only triggers + // the printedMessage signal (and therefore doesn't show up in the application log) + Script.print('[Script.print] hello world'); + + // the rest of these should show up in both the application log and signaled print handlers + print('[print]', 'hello', 'world'); + + // note: these trigger the equivalent of an emit + Script.printedMessage('[Script.printedMessage] hello world', '{filename}'); + Script.errorMessage('[Script.errorMessage] hello world', '{filename}'); + + { + // FIXME: while not directly related to Script.print, these currently don't + // show up at all in the "HMD-friendly script log" or "Console..." + + Script.infoMessage('[Script.infoMessage] hello world', '{filename}'); + Script.warningMessage('[Script.warningMessage] hello world', '{filename}'); + + Vec3.print('[Vec3.print]', Vec3.HALF); + + var q = Quat.fromPitchYawRollDegrees(45, 45, 45); + Quat.print('[Quat.print]', q); + + var m = Mat4.createFromRotAndTrans(q, Vec3.HALF); + Mat4.print('[Mat4.print (row major)]', m); + } +} From ba5a371da1aeb454f9178091fdd8a25f813d3418 Mon Sep 17 00:00:00 2001 From: Vladyslav Stelmakhovskyi Date: Thu, 4 May 2017 22:54:41 +0200 Subject: [PATCH 58/89] Decrease CPU usage in tabletUI periodical updates --- interface/src/Application.cpp | 3 +- interface/src/ui/AvatarInputs.h | 2 +- scripts/system/tablet-ui/tabletUI.js | 70 ++++++++++++---------------- 3 files changed, 34 insertions(+), 41 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 64b18ec522..4a7d552ad2 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5435,7 +5435,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("Test", TestScriptingInterface::getInstance()); } - scriptEngine->registerGlobalObject("Overlays", &_overlays); scriptEngine->registerGlobalObject("Rates", new RatesScriptingInterface(this)); // hook our avatar and avatar hash map object into this script engine @@ -5534,6 +5533,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri auto entityScriptServerLog = DependencyManager::get(); scriptEngine->registerGlobalObject("EntityScriptServerLog", entityScriptServerLog.data()); + scriptEngine->registerGlobalObject("AvatarInputs", AvatarInputs::getInstance()); + qScriptRegisterMetaType(scriptEngine, OverlayIDtoScriptValue, OverlayIDfromScriptValue); diff --git a/interface/src/ui/AvatarInputs.h b/interface/src/ui/AvatarInputs.h index 34b2cbca8b..bc9955a24f 100644 --- a/interface/src/ui/AvatarInputs.h +++ b/interface/src/ui/AvatarInputs.h @@ -34,7 +34,7 @@ class AvatarInputs : public QQuickItem { public: static AvatarInputs* getInstance(); - float loudnessToAudioLevel(float loudness); + Q_INVOKABLE float loudnessToAudioLevel(float loudness); AvatarInputs(QQuickItem* parent = nullptr); void update(); bool showAudioTools() const { return _showAudioTools; } diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index dbfd3e632e..3d8e1811ba 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -13,7 +13,7 @@ // /* global Script, HMD, WebTablet, UIWebTablet, UserActivityLogger, Settings, Entities, Messages, Tablet, Overlays, - MyAvatar, Menu */ + MyAvatar, Menu, AvatarInputs */ (function() { // BEGIN LOCAL_SCOPE var tabletRezzed = false; @@ -25,6 +25,9 @@ var debugTablet = false; var tabletScalePercentage = 100.0; UIWebTablet = null; + var MSECS_PER_SEC = 1000.0; + var MUTE_MICROPHONE_MENU_ITEM = "Mute Microphone"; + var gTablet = null; Script.include("../libraries/WebTablet.js"); @@ -49,7 +52,7 @@ } function getTabletScalePercentageFromSettings() { - var toolbarMode = Tablet.getTablet("com.highfidelity.interface.tablet.system").toolbarMode; + var toolbarMode = gTablet.toolbarMode; var tabletScalePercentage = DEFAULT_TABLET_SCALE; if (!toolbarMode) { if (HMD.active) { @@ -77,6 +80,9 @@ if (debugTablet) { print("TABLET rezzing"); } + if (gTablet === null) { + gTablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + } tabletScalePercentage = getTabletScalePercentageFromSettings(); UIWebTablet = new WebTablet("qml/hifi/tablet/TabletRoot.qml", @@ -92,7 +98,7 @@ } function showTabletUI() { - Tablet.getTablet("com.highfidelity.interface.tablet.system").tabletShown = true; + gTablet.tabletShown = true; if (!tabletRezzed || !tabletIsValid()) { closeTabletUI(); @@ -114,7 +120,7 @@ } function hideTabletUI() { - Tablet.getTablet("com.highfidelity.interface.tablet.system").tabletShown = false; + gTablet.tabletShown = false; if (!UIWebTablet) { return; } @@ -130,7 +136,7 @@ } function closeTabletUI() { - Tablet.getTablet("com.highfidelity.interface.tablet.system").tabletShown = false; + gTablet.tabletShown = false; if (UIWebTablet) { if (UIWebTablet.onClose) { UIWebTablet.onClose(); @@ -149,17 +155,21 @@ print("TABLET closeTabletUI, UIWebTablet is null"); } tabletRezzed = false; + gTablet = null } function updateShowTablet() { - var MSECS_PER_SEC = 1000.0; var now = Date.now(); + if (gTablet === null) { + gTablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + } + // close the WebTablet if it we go into toolbar mode. - var tabletShown = Tablet.getTablet("com.highfidelity.interface.tablet.system").tabletShown; - var toolbarMode = Tablet.getTablet("com.highfidelity.interface.tablet.system").toolbarMode; - var landscape = Tablet.getTablet("com.highfidelity.interface.tablet.system").landscape; + var tabletShown = gTablet.tabletShown; + var toolbarMode = gTablet.toolbarMode; + var landscape = gTablet.landscape; if (tabletShown && toolbarMode) { closeTabletUI(); @@ -167,18 +177,20 @@ return; } + //TODO: move to tablet qml? if (tabletShown) { - var MUTE_MICROPHONE_MENU_ITEM = "Mute Microphone"; var currentMicEnabled = !Menu.isOptionChecked(MUTE_MICROPHONE_MENU_ITEM); var currentMicLevel = getMicLevel(); - var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - tablet.updateMicEnabled(currentMicEnabled); - tablet.updateAudioBar(currentMicLevel); + gTablet.updateMicEnabled(currentMicEnabled); + gTablet.updateAudioBar(currentMicLevel); } - updateTabletWidthFromSettings(); - if (UIWebTablet) { - UIWebTablet.setLandscape(landscape); + if (validCheckTime - now > MSECS_PER_SEC/4) { + //each 250ms should be just fine + updateTabletWidthFromSettings(); + if (UIWebTablet) { + UIWebTablet.setLandscape(landscape); + } } if (validCheckTime - now > MSECS_PER_SEC) { @@ -228,7 +240,7 @@ } if (channel === "home") { if (UIWebTablet) { - Tablet.getTablet("com.highfidelity.interface.tablet.system").landscape = false; + gTablet.landscape = false; } } } @@ -239,30 +251,10 @@ Script.setInterval(updateShowTablet, 100); - // Initialise variables used to calculate audio level - var accumulatedLevel = 0.0; - // Note: Might have to tweak the following two based on the rate we're getting the data - var AVERAGING_RATIO = 0.05; - // Calculate microphone level with the same scaling equation (log scale, exponentially averaged) in AvatarInputs and pal.js function getMicLevel() { - var LOUDNESS_FLOOR = 11.0; - var LOUDNESS_SCALE = 2.8 / 5.0; - var LOG2 = Math.log(2.0); - var micLevel = 0.0; - accumulatedLevel = AVERAGING_RATIO * accumulatedLevel + (1 - AVERAGING_RATIO) * (MyAvatar.audioLoudness); - // Convert to log base 2 - var logLevel = Math.log(accumulatedLevel + 1) / LOG2; - - if (logLevel <= LOUDNESS_FLOOR) { - micLevel = logLevel / LOUDNESS_FLOOR * LOUDNESS_SCALE; - } else { - micLevel = (logLevel - (LOUDNESS_FLOOR - 1.0)) * LOUDNESS_SCALE; - } - if (micLevel > 1.0) { - micLevel = 1.0; - } - return micLevel; + //reuse already existing C++ code + return AvatarInputs.loudnessToAudioLevel(MyAvatar.audioLoudness) } Script.scriptEnding.connect(function () { From e765d8858ce874dbce989286ea54c635138ac8f4 Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 4 May 2017 16:57:41 -0400 Subject: [PATCH 59/89] update script engine to not treat about:* as a local filename; resolve debugPrint FIXME --- libraries/script-engine/src/ScriptEngine.cpp | 13 +++++++++---- libraries/script-engine/src/ScriptEngine.h | 1 + libraries/script-engine/src/ScriptEngines.cpp | 5 +++-- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index c904062507..8bbb9a3e2c 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -105,11 +105,11 @@ static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine) { } message += context->argument(i).toString(); } - qCDebug(scriptengineScript).noquote() << "script:print()<<" << message; // noquote() so that \n is treated as newline + qCDebug(scriptengineScript).noquote() << message; // noquote() so that \n is treated as newline - // FIXME - this approach neeeds revisiting. print() comes here, which ends up calling Script.print? - engine->globalObject().property("Script").property("print") - .call(engine->nullValue(), QScriptValueList({ message })); + if (ScriptEngine *scriptEngine = qobject_cast(engine)) { + scriptEngine->print(message); + } return QScriptValue(); } @@ -472,6 +472,11 @@ void ScriptEngine::scriptInfoMessage(const QString& message) { emit infoMessage(message, getFilename()); } +void ScriptEngine::scriptPrintedMessage(const QString& message) { + qCDebug(scriptengine) << message; + emit printedMessage(message, getFilename()); +} + // Even though we never pass AnimVariantMap directly to and from javascript, the queued invokeMethod of // callAnimationStateHandler requires that the type be registered. // These two are meaningful, if we ever do want to use them... diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 5ea8d052e9..6188f24d71 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -221,6 +221,7 @@ public: void scriptErrorMessage(const QString& message); void scriptWarningMessage(const QString& message); void scriptInfoMessage(const QString& message); + void scriptPrintedMessage(const QString& message); int getNumRunningEntityScripts() const; bool getEntityScriptDetails(const EntityItemID& entityID, EntityScriptDetails &details) const; diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp index 88b0e0b7b5..2076657288 100644 --- a/libraries/script-engine/src/ScriptEngines.cpp +++ b/libraries/script-engine/src/ScriptEngines.cpp @@ -453,7 +453,8 @@ ScriptEngine* ScriptEngines::loadScript(const QUrl& scriptFilename, bool isUserL (scriptFilename.scheme() != "http" && scriptFilename.scheme() != "https" && scriptFilename.scheme() != "atp" && - scriptFilename.scheme() != "file")) { + scriptFilename.scheme() != "file" && + scriptFilename.scheme() != "about")) { // deal with a "url" like c:/something scriptUrl = normalizeScriptURL(QUrl::fromLocalFile(scriptFilename.toString())); } else { @@ -472,7 +473,7 @@ ScriptEngine* ScriptEngines::loadScript(const QUrl& scriptFilename, bool isUserL }, Qt::QueuedConnection); - if (scriptFilename.isEmpty()) { + if (scriptFilename.isEmpty() || !scriptUrl.isValid()) { launchScriptEngine(scriptEngine); } else { // connect to the appropriate signals of this script engine From 6cb95b239340f5d58eaf263598e7e3a952b1bf64 Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 4 May 2017 16:59:38 -0400 Subject: [PATCH 60/89] add and connect handlers for scriptWarningMessage and scriptInfoMessage signals --- interface/src/ui/JSConsole.cpp | 16 ++++++++++++++++ interface/src/ui/JSConsole.h | 2 ++ 2 files changed, 18 insertions(+) diff --git a/interface/src/ui/JSConsole.cpp b/interface/src/ui/JSConsole.cpp index 2d21d1d7ec..1abc5e8c2e 100644 --- a/interface/src/ui/JSConsole.cpp +++ b/interface/src/ui/JSConsole.cpp @@ -28,6 +28,8 @@ const int MAX_HISTORY_SIZE = 64; const QString COMMAND_STYLE = "color: #266a9b;"; const QString RESULT_SUCCESS_STYLE = "color: #677373;"; +const QString RESULT_INFO_STYLE = "color: #223bd1;"; +const QString RESULT_WARNING_STYLE = "color: #d1b322;"; const QString RESULT_ERROR_STYLE = "color: #d13b22;"; const QString GUTTER_PREVIOUS_COMMAND = "<"; @@ -79,6 +81,8 @@ void JSConsole::setScriptEngine(ScriptEngine* scriptEngine) { } if (_scriptEngine != NULL) { disconnect(_scriptEngine, &ScriptEngine::printedMessage, this, &JSConsole::handlePrint); + disconnect(_scriptEngine, &ScriptEngine::infoMessage, this, &JSConsole::handleInfo); + disconnect(_scriptEngine, &ScriptEngine::warningMessage, this, &JSConsole::handleWarning); disconnect(_scriptEngine, &ScriptEngine::errorMessage, this, &JSConsole::handleError); if (_ownScriptEngine) { _scriptEngine->deleteLater(); @@ -90,6 +94,8 @@ void JSConsole::setScriptEngine(ScriptEngine* scriptEngine) { _scriptEngine = _ownScriptEngine ? DependencyManager::get()->loadScript(_consoleFileName, false) : scriptEngine; connect(_scriptEngine, &ScriptEngine::printedMessage, this, &JSConsole::handlePrint); + connect(_scriptEngine, &ScriptEngine::infoMessage, this, &JSConsole::handleInfo); + connect(_scriptEngine, &ScriptEngine::warningMessage, this, &JSConsole::handleWarning); connect(_scriptEngine, &ScriptEngine::errorMessage, this, &JSConsole::handleError); } @@ -145,6 +151,16 @@ void JSConsole::handlePrint(const QString& message, const QString& scriptName) { appendMessage("", message); } +void JSConsole::handleInfo(const QString& message, const QString& scriptName) { + Q_UNUSED(scriptName); + appendMessage("", "" + message.toHtmlEscaped() + ""); +} + +void JSConsole::handleWarning(const QString& message, const QString& scriptName) { + Q_UNUSED(scriptName); + appendMessage("", "" + message.toHtmlEscaped() + ""); +} + void JSConsole::mouseReleaseEvent(QMouseEvent* event) { _ui->promptTextEdit->setFocus(); } diff --git a/interface/src/ui/JSConsole.h b/interface/src/ui/JSConsole.h index 938b670e78..864f847071 100644 --- a/interface/src/ui/JSConsole.h +++ b/interface/src/ui/JSConsole.h @@ -48,6 +48,8 @@ protected slots: void scrollToBottom(); void resizeTextInput(); void handlePrint(const QString& message, const QString& scriptName); + void handleInfo(const QString& message, const QString& scriptName); + void handleWarning(const QString& message, const QString& scriptName); void handleError(const QString& message, const QString& scriptName); void commandFinished(); From deeb9c367ad7cdf7dd991060716461a64c13659d Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 4 May 2017 17:46:28 -0400 Subject: [PATCH 61/89] update test to reflect support for scriptWarningMessage and scriptInfoMessage --- scripts/developer/tests/printTest.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/scripts/developer/tests/printTest.js b/scripts/developer/tests/printTest.js index b94ff0c54b..d1069a6a8f 100644 --- a/scripts/developer/tests/printTest.js +++ b/scripts/developer/tests/printTest.js @@ -14,15 +14,12 @@ function main() { // note: these trigger the equivalent of an emit Script.printedMessage('[Script.printedMessage] hello world', '{filename}'); + Script.infoMessage('[Script.infoMessage] hello world', '{filename}'); + Script.warningMessage('[Script.warningMessage] hello world', '{filename}'); Script.errorMessage('[Script.errorMessage] hello world', '{filename}'); { - // FIXME: while not directly related to Script.print, these currently don't - // show up at all in the "HMD-friendly script log" or "Console..." - - Script.infoMessage('[Script.infoMessage] hello world', '{filename}'); - Script.warningMessage('[Script.warningMessage] hello world', '{filename}'); - + // FIXME: these only show up in the application debug log Vec3.print('[Vec3.print]', Vec3.HALF); var q = Quat.fromPitchYawRollDegrees(45, 45, 45); From ecda3132233f36dd53046ab9ff92f730fd0adc0e Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 4 May 2017 15:07:59 -0700 Subject: [PATCH 62/89] Add runtime access to compression settings --- interface/src/ui/PreferencesDialog.cpp | 24 +++++ libraries/image/src/image/Image.cpp | 131 ++++++++++++++++++------- libraries/image/src/image/Image.h | 10 ++ 3 files changed, 131 insertions(+), 34 deletions(-) diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index e2e22fe366..767c122bb6 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -330,6 +330,30 @@ void setupPreferences() { preferences->addPreference(preference); } } + { + auto getter = []()->bool { return image::isColorTexturesCompressionEnabled(); }; + auto setter = [](bool value) { return image::setColorTexturesCompressionEnabled(value); }; + auto preference = new CheckPreference(RENDER, "Compress Color Textures", getter, setter); + preferences->addPreference(preference); + } + { + auto getter = []()->bool { return image::isNormalTexturesCompressionEnabled(); }; + auto setter = [](bool value) { return image::setNormalTexturesCompressionEnabled(value); }; + auto preference = new CheckPreference(RENDER, "Compress Normal Textures", getter, setter); + preferences->addPreference(preference); + } + { + auto getter = []()->bool { return image::isGrayscaleTexturesCompressionEnabled(); }; + auto setter = [](bool value) { return image::setGrayscaleTexturesCompressionEnabled(value); }; + auto preference = new CheckPreference(RENDER, "Compress Grayscale Textures", getter, setter); + preferences->addPreference(preference); + } + { + auto getter = []()->bool { return image::isCubeTexturesCompressionEnabled(); }; + auto setter = [](bool value) { return image::setCubeTexturesCompressionEnabled(value); }; + auto preference = new CheckPreference(RENDER, "Compress Cube Textures", getter, setter); + preferences->addPreference(preference); + } } { static const QString RENDER("Networking"); diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index 825b07003b..68add428c1 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -22,16 +22,19 @@ #include #include #include +#include #include "ImageLogging.h" using namespace gpu; #define CPU_MIPMAPS 1 -#define COMPRESS_COLOR_TEXTURES 0 -#define COMPRESS_NORMALMAP_TEXTURES 0 // Disable Normalmap compression for now -#define COMPRESS_GRAYSCALE_TEXTURES 0 -#define COMPRESS_CUBEMAP_TEXTURES 0 // Disable Cubemap compression for now + +static std::mutex settingsMutex; +static Setting::Handle compressColorTextures("hifi.graphics.compressColorTextures", false); +static Setting::Handle compressNormalTextures("hifi.graphics.compressNormalTextures", false); +static Setting::Handle compressGrayscaleTextures("hifi.graphics.compressGrayscaleTextures", false); +static Setting::Handle compressCubeTextures("hifi.graphics.compressCubeTextures", false); static const glm::uvec2 SPARSE_PAGE_SIZE(128); static const glm::uvec2 MAX_TEXTURE_SIZE(4096); @@ -144,6 +147,64 @@ gpu::TexturePointer TextureUsage::createCubeTextureFromImageWithoutIrradiance(co return processCubeTextureColorFromImage(srcImage, srcImageName, false); } + +bool isColorTexturesCompressionEnabled() { +#if CPU_MIPMAPS + std::lock_guard guard(settingsMutex); + return compressColorTextures.get(); +#else + return false; +#endif +} + +bool isNormalTexturesCompressionEnabled() { +#if CPU_MIPMAPS + std::lock_guard guard(settingsMutex); + return compressNormalTextures.get(); +#else + return false; +#endif +} + +bool isGrayscaleTexturesCompressionEnabled() { +#if CPU_MIPMAPS + std::lock_guard guard(settingsMutex); + return compressGrayscaleTextures.get(); +#else + return false; +#endif +} + +bool isCubeTexturesCompressionEnabled() { +#if CPU_MIPMAPS + std::lock_guard guard(settingsMutex); + return compressCubeTextures.get(); +#else + return false; +#endif +} + +void setColorTexturesCompressionEnabled(bool enabled) { + std::lock_guard guard(settingsMutex); + compressColorTextures.set(enabled); +} + +void setNormalTexturesCompressionEnabled(bool enabled) { + std::lock_guard guard(settingsMutex); + compressNormalTextures.set(enabled); +} + +void setGrayscaleTexturesCompressionEnabled(bool enabled) { + std::lock_guard guard(settingsMutex); + compressGrayscaleTextures.set(enabled); +} + +void setCubeTexturesCompressionEnabled(bool enabled) { + std::lock_guard guard(settingsMutex); + compressCubeTextures.set(enabled); +} + + gpu::TexturePointer processImage(const QByteArray& content, const std::string& filename, int maxNumPixels, TextureUsage::Type textureType) { // Help the QImage loader by extracting the image file format from the url filename ext. // Some tga are not created properly without it. @@ -428,18 +489,19 @@ gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(const QImage& s gpu::TexturePointer theTexture = nullptr; if ((image.width() > 0) && (image.height() > 0)) { -#if CPU_MIPMAPS && COMPRESS_COLOR_TEXTURES + gpu::Element formatMip; gpu::Element formatGPU; - if (validAlpha) { - formatGPU = alphaAsMask ? gpu::Element::COLOR_COMPRESSED_SRGBA_MASK : gpu::Element::COLOR_COMPRESSED_SRGBA; + if (isColorTexturesCompressionEnabled()) { + if (validAlpha) { + formatGPU = alphaAsMask ? gpu::Element::COLOR_COMPRESSED_SRGBA_MASK : gpu::Element::COLOR_COMPRESSED_SRGBA; + } else { + formatGPU = gpu::Element::COLOR_COMPRESSED_SRGB; + } + formatMip = formatGPU; } else { - formatGPU = gpu::Element::COLOR_COMPRESSED_SRGB; + formatMip = gpu::Element::COLOR_SBGRA_32; + formatGPU = gpu::Element::COLOR_SRGBA_32; } - gpu::Element formatMip = formatGPU; -#else - gpu::Element formatMip = gpu::Element::COLOR_SBGRA_32; - gpu::Element formatGPU = gpu::Element::COLOR_SRGBA_32; -#endif if (isStrict) { theTexture = gpu::Texture::createStrict(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)); @@ -547,14 +609,12 @@ gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(const QImag gpu::TexturePointer theTexture = nullptr; if ((image.width() > 0) && (image.height() > 0)) { - -#if CPU_MIPMAPS && COMPRESS_NORMALMAP_TEXTURES - gpu::Element formatMip = gpu::Element::COLOR_COMPRESSED_XY; - gpu::Element formatGPU = gpu::Element::COLOR_COMPRESSED_XY; -#else gpu::Element formatMip = gpu::Element::VEC2NU8_XY; gpu::Element formatGPU = gpu::Element::VEC2NU8_XY; -#endif + if (isNormalTexturesCompressionEnabled()) { + formatMip = gpu::Element::COLOR_COMPRESSED_XY; + formatGPU = gpu::Element::COLOR_COMPRESSED_XY; + } theTexture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)); theTexture->setSource(srcImageName); @@ -580,14 +640,15 @@ gpu::TexturePointer TextureUsage::process2DTextureGrayscaleFromImage(const QImag gpu::TexturePointer theTexture = nullptr; if ((image.width() > 0) && (image.height() > 0)) { - -#if CPU_MIPMAPS && COMPRESS_GRAYSCALE_TEXTURES - gpu::Element formatMip = gpu::Element::COLOR_COMPRESSED_RED; - gpu::Element formatGPU = gpu::Element::COLOR_COMPRESSED_RED; -#else - gpu::Element formatMip = gpu::Element::COLOR_R_8; - gpu::Element formatGPU = gpu::Element::COLOR_R_8; -#endif + gpu::Element formatMip; + gpu::Element formatGPU; + if (isGrayscaleTexturesCompressionEnabled()) { + formatMip = gpu::Element::COLOR_COMPRESSED_RED; + formatGPU = gpu::Element::COLOR_COMPRESSED_RED; + } else { + formatMip = gpu::Element::COLOR_R_8; + formatGPU = gpu::Element::COLOR_R_8; + } theTexture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)); theTexture->setSource(srcImageName); @@ -864,13 +925,15 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(const QImage& image = image.convertToFormat(QImage::Format_ARGB32); } -#if CPU_MIPMAPS && COMPRESS_CUBEMAP_TEXTURES - gpu::Element formatMip = gpu::Element::COLOR_COMPRESSED_SRGBA; - gpu::Element formatGPU = gpu::Element::COLOR_COMPRESSED_SRGBA; -#else - gpu::Element formatMip = gpu::Element::COLOR_SRGBA_32; - gpu::Element formatGPU = gpu::Element::COLOR_SRGBA_32; -#endif + gpu::Element formatMip; + gpu::Element formatGPU; + if (isCubeTexturesCompressionEnabled()) { + formatMip = gpu::Element::COLOR_COMPRESSED_SRGBA; + formatGPU = gpu::Element::COLOR_COMPRESSED_SRGBA; + } else { + formatMip = gpu::Element::COLOR_SRGBA_32; + formatGPU = gpu::Element::COLOR_SRGBA_32; + } // Find the layout of the cubemap in the 2D image // Use the original image size since processSourceImage may have altered the size / aspect ratio diff --git a/libraries/image/src/image/Image.h b/libraries/image/src/image/Image.h index 3e5aa868d2..d9dd1105cd 100644 --- a/libraries/image/src/image/Image.h +++ b/libraries/image/src/image/Image.h @@ -63,6 +63,16 @@ gpu::TexturePointer processCubeTextureColorFromImage(const QImage& srcImage, con } // namespace TextureUsage +bool isColorTexturesCompressionEnabled(); +bool isNormalTexturesCompressionEnabled(); +bool isGrayscaleTexturesCompressionEnabled(); +bool isCubeTexturesCompressionEnabled(); + +void setColorTexturesCompressionEnabled(bool enabled); +void setNormalTexturesCompressionEnabled(bool enabled); +void setGrayscaleTexturesCompressionEnabled(bool enabled); +void setCubeTexturesCompressionEnabled(bool enabled); + gpu::TexturePointer processImage(const QByteArray& content, const std::string& url, int maxNumPixels, TextureUsage::Type textureType); } // namespace image From f71552c648af5c10e32e764c0601555a87bc35be Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 4 May 2017 17:04:13 -0400 Subject: [PATCH 63/89] * update Vec3.print, Quat.print, Mat4.print, ScriptUUID.print to work with JSConsole and HMD-friendly script log * add test output for Quat-euler and Mat4-row/col prints --- libraries/script-engine/src/Mat4.cpp | 16 ++++++++++------ libraries/script-engine/src/Mat4.h | 5 +++-- libraries/script-engine/src/Quat.cpp | 15 +++++++++++++-- libraries/script-engine/src/Quat.h | 5 +++-- libraries/script-engine/src/ScriptUUID.cpp | 10 ++++++++-- libraries/script-engine/src/ScriptUUID.h | 5 +++-- libraries/script-engine/src/Vec3.cpp | 12 +++++++++--- libraries/script-engine/src/Vec3.h | 3 ++- scripts/developer/tests/printTest.js | 14 +++++++++++--- 9 files changed, 62 insertions(+), 23 deletions(-) diff --git a/libraries/script-engine/src/Mat4.cpp b/libraries/script-engine/src/Mat4.cpp index 6676d0cde1..2d26a66823 100644 --- a/libraries/script-engine/src/Mat4.cpp +++ b/libraries/script-engine/src/Mat4.cpp @@ -11,7 +11,9 @@ #include #include +#include #include "ScriptEngineLogging.h" +#include "ScriptEngine.h" #include "Mat4.h" glm::mat4 Mat4::multiply(const glm::mat4& m1, const glm::mat4& m2) const { @@ -66,10 +68,12 @@ glm::vec3 Mat4::getUp(const glm::mat4& m) const { return glm::vec3(m[0][1], m[1][1], m[2][1]); } -void Mat4::print(const QString& label, const glm::mat4& m) const { - qCDebug(scriptengine) << qPrintable(label) << - "row0 =" << m[0][0] << "," << m[1][0] << "," << m[2][0] << "," << m[3][0] << - "row1 =" << m[0][1] << "," << m[1][1] << "," << m[2][1] << "," << m[3][1] << - "row2 =" << m[0][2] << "," << m[1][2] << "," << m[2][2] << "," << m[3][2] << - "row3 =" << m[0][3] << "," << m[1][3] << "," << m[2][3] << "," << m[3][3]; +void Mat4::print(const QString& label, const glm::mat4& m, bool transpose) const { + glm::mat4 out = transpose ? glm::transpose(m) : m; + QString message = QString("%1 %2").arg(qPrintable(label)); + message = message.arg(glm::to_string(out).c_str()); + qCDebug(scriptengine) << message; + if (ScriptEngine* scriptEngine = qobject_cast(engine())) { + scriptEngine->print(message); + } } diff --git a/libraries/script-engine/src/Mat4.h b/libraries/script-engine/src/Mat4.h index 19bbbe178a..8b942874ee 100644 --- a/libraries/script-engine/src/Mat4.h +++ b/libraries/script-engine/src/Mat4.h @@ -16,9 +16,10 @@ #include #include +#include /// Scriptable Mat4 object. Used exclusively in the JavaScript API -class Mat4 : public QObject { +class Mat4 : public QObject, protected QScriptable { Q_OBJECT public slots: @@ -43,7 +44,7 @@ public slots: glm::vec3 getRight(const glm::mat4& m) const; glm::vec3 getUp(const glm::mat4& m) const; - void print(const QString& label, const glm::mat4& m) const; + void print(const QString& label, const glm::mat4& m, bool transpose = false) const; }; #endif // hifi_Mat4_h diff --git a/libraries/script-engine/src/Quat.cpp b/libraries/script-engine/src/Quat.cpp index 05002dcf5d..8d84b45b90 100644 --- a/libraries/script-engine/src/Quat.cpp +++ b/libraries/script-engine/src/Quat.cpp @@ -15,7 +15,9 @@ #include #include +#include #include "ScriptEngineLogging.h" +#include "ScriptEngine.h" #include "Quat.h" quat Quat::normalize(const glm::quat& q) { @@ -114,8 +116,17 @@ float Quat::dot(const glm::quat& q1, const glm::quat& q2) { return glm::dot(q1, q2); } -void Quat::print(const QString& label, const glm::quat& q) { - qCDebug(scriptengine) << qPrintable(label) << q.x << "," << q.y << "," << q.z << "," << q.w; +void Quat::print(const QString& label, const glm::quat& q, bool asDegrees) { + QString message = QString("%1 %2").arg(qPrintable(label)); + if (asDegrees) { + message = message.arg(glm::to_string(safeEulerAngles(q)).c_str()); + } else { + message = message.arg(glm::to_string(q).c_str()); + } + qCDebug(scriptengine) << message; + if (ScriptEngine* scriptEngine = qobject_cast(engine())) { + scriptEngine->print(message); + } } bool Quat::equal(const glm::quat& q1, const glm::quat& q2) { diff --git a/libraries/script-engine/src/Quat.h b/libraries/script-engine/src/Quat.h index ee3ab9aa7c..3b3a6fde7c 100644 --- a/libraries/script-engine/src/Quat.h +++ b/libraries/script-engine/src/Quat.h @@ -18,6 +18,7 @@ #include #include +#include /**jsdoc * A Quaternion @@ -30,7 +31,7 @@ */ /// Scriptable interface a Quaternion helper class object. Used exclusively in the JavaScript API -class Quat : public QObject { +class Quat : public QObject, protected QScriptable { Q_OBJECT public slots: @@ -58,7 +59,7 @@ public slots: glm::quat slerp(const glm::quat& q1, const glm::quat& q2, float alpha); glm::quat squad(const glm::quat& q1, const glm::quat& q2, const glm::quat& s1, const glm::quat& s2, float h); float dot(const glm::quat& q1, const glm::quat& q2); - void print(const QString& label, const glm::quat& q); + void print(const QString& label, const glm::quat& q, bool asDegrees = false); bool equal(const glm::quat& q1, const glm::quat& q2); glm::quat cancelOutRollAndPitch(const glm::quat& q); glm::quat cancelOutRoll(const glm::quat& q); diff --git a/libraries/script-engine/src/ScriptUUID.cpp b/libraries/script-engine/src/ScriptUUID.cpp index 6a52f4f6ca..ee15f1a760 100644 --- a/libraries/script-engine/src/ScriptUUID.cpp +++ b/libraries/script-engine/src/ScriptUUID.cpp @@ -14,6 +14,7 @@ #include #include "ScriptEngineLogging.h" +#include "ScriptEngine.h" #include "ScriptUUID.h" QUuid ScriptUUID::fromString(const QString& s) { @@ -36,6 +37,11 @@ bool ScriptUUID::isNull(const QUuid& id) { return id.isNull(); } -void ScriptUUID::print(const QString& lable, const QUuid& id) { - qCDebug(scriptengine) << qPrintable(lable) << id.toString(); +void ScriptUUID::print(const QString& label, const QUuid& id) { + QString message = QString("%1 %2").arg(qPrintable(label)); + message = message.arg(id.toString()); + qCDebug(scriptengine) << message; + if (ScriptEngine* scriptEngine = qobject_cast(engine())) { + scriptEngine->print(message); + } } diff --git a/libraries/script-engine/src/ScriptUUID.h b/libraries/script-engine/src/ScriptUUID.h index db94b5082b..221f9c46f0 100644 --- a/libraries/script-engine/src/ScriptUUID.h +++ b/libraries/script-engine/src/ScriptUUID.h @@ -15,9 +15,10 @@ #define hifi_ScriptUUID_h #include +#include /// Scriptable interface for a UUID helper class object. Used exclusively in the JavaScript API -class ScriptUUID : public QObject { +class ScriptUUID : public QObject, protected QScriptable { Q_OBJECT public slots: @@ -26,7 +27,7 @@ public slots: QUuid generate(); bool isEqual(const QUuid& idA, const QUuid& idB); bool isNull(const QUuid& id); - void print(const QString& lable, const QUuid& id); + void print(const QString& label, const QUuid& id); }; #endif // hifi_ScriptUUID_h diff --git a/libraries/script-engine/src/Vec3.cpp b/libraries/script-engine/src/Vec3.cpp index 6c8f618500..f66b99dfa1 100644 --- a/libraries/script-engine/src/Vec3.cpp +++ b/libraries/script-engine/src/Vec3.cpp @@ -14,20 +14,26 @@ #include #include +#include #include "ScriptEngineLogging.h" #include "NumericalConstants.h" #include "Vec3.h" +#include "ScriptEngine.h" float Vec3::orientedAngle(const glm::vec3& v1, const glm::vec3& v2, const glm::vec3& v3) { float radians = glm::orientedAngle(glm::normalize(v1), glm::normalize(v2), glm::normalize(v3)); return glm::degrees(radians); } - -void Vec3::print(const QString& lable, const glm::vec3& v) { - qCDebug(scriptengine) << qPrintable(lable) << v.x << "," << v.y << "," << v.z; +void Vec3::print(const QString& label, const glm::vec3& v) { + QString message = QString("%1 %2").arg(qPrintable(label)); + message = message.arg(glm::to_string(v).c_str()); + qCDebug(scriptengine) << message; + if (ScriptEngine* scriptEngine = qobject_cast(engine())) { + scriptEngine->print(message); + } } bool Vec3::withinEpsilon(const glm::vec3& v1, const glm::vec3& v2, float epsilon) { diff --git a/libraries/script-engine/src/Vec3.h b/libraries/script-engine/src/Vec3.h index b3a3dc3035..c7179a80c0 100644 --- a/libraries/script-engine/src/Vec3.h +++ b/libraries/script-engine/src/Vec3.h @@ -17,6 +17,7 @@ #include #include +#include #include "GLMHelpers.h" @@ -48,7 +49,7 @@ */ /// Scriptable interface a Vec3ernion helper class object. Used exclusively in the JavaScript API -class Vec3 : public QObject { +class Vec3 : public QObject, protected QScriptable { Q_OBJECT Q_PROPERTY(glm::vec3 UNIT_X READ UNIT_X CONSTANT) Q_PROPERTY(glm::vec3 UNIT_Y READ UNIT_Y CONSTANT) diff --git a/scripts/developer/tests/printTest.js b/scripts/developer/tests/printTest.js index d1069a6a8f..5fcd8ecb74 100644 --- a/scripts/developer/tests/printTest.js +++ b/scripts/developer/tests/printTest.js @@ -19,13 +19,21 @@ function main() { Script.errorMessage('[Script.errorMessage] hello world', '{filename}'); { - // FIXME: these only show up in the application debug log Vec3.print('[Vec3.print]', Vec3.HALF); var q = Quat.fromPitchYawRollDegrees(45, 45, 45); Quat.print('[Quat.print]', q); + Quat.print('[Quat.print (euler)]', q, true); - var m = Mat4.createFromRotAndTrans(q, Vec3.HALF); - Mat4.print('[Mat4.print (row major)]', m); + function vec4(x,y,z,w) { + return { x: x, y: y, z: z, w: w }; + } + var m = Mat4.createFromColumns( + vec4(1,2,3,4), vec4(5,6,7,8), vec4(9,10,11,12), vec4(13,14,15,16) + ); + Mat4.print('[Mat4.print (col major)]', m); + Mat4.print('[Mat4.print (row major)]', m, true); + + Uuid.print('[Uuid.print]', Uuid.toString(0)); } } From f11d6eff92e0827b91a13532ae5dbdbf29db5526 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 4 May 2017 15:43:44 -0700 Subject: [PATCH 64/89] fix typos: RenderItemsMap not RenderItems --- libraries/render-utils/src/Model.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index eddda41d5e..acc84646c5 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -573,7 +573,7 @@ bool Model::addToScene(const render::ScenePointer& scene, bool somethingAdded = false; if (_collisionGeometry) { - if (_collisionRenderItems.empty()) { + if (_collisionRenderItemsMap.empty()) { foreach (auto renderItem, _collisionRenderItems) { auto item = scene->allocateID(); auto renderPayload = std::make_shared(renderItem); @@ -583,7 +583,7 @@ bool Model::addToScene(const render::ScenePointer& scene, transaction.resetItem(item, renderPayload); _collisionRenderItemsMap.insert(item, renderPayload); } - somethingAdded = !_collisionRenderItems.empty(); + somethingAdded = !_collisionRenderItemsMap.empty(); } } else { if (_modelMeshRenderItemsMap.empty()) { @@ -632,7 +632,7 @@ void Model::removeFromScene(const render::ScenePointer& scene, render::Transacti transaction.removeItem(item); } _collisionRenderItems.clear(); - _collisionRenderItems.clear(); + _collisionRenderItemsMap.clear(); _addedToScene = false; _renderInfoVertexCount = 0; From f9d29256e0531c850ac9351ff80ad406ae0f9c6c Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 4 May 2017 18:58:30 -0400 Subject: [PATCH 65/89] use an actual Uuid value for the Uuid.print test --- scripts/developer/tests/printTest.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/developer/tests/printTest.js b/scripts/developer/tests/printTest.js index 5fcd8ecb74..c1fe6ec745 100644 --- a/scripts/developer/tests/printTest.js +++ b/scripts/developer/tests/printTest.js @@ -34,6 +34,6 @@ function main() { Mat4.print('[Mat4.print (col major)]', m); Mat4.print('[Mat4.print (row major)]', m, true); - Uuid.print('[Uuid.print]', Uuid.toString(0)); + Uuid.print('[Uuid.print]', Uuid.fromString(Uuid.toString(0))); } } From f39411656a5f515bf69782d105b37bf823e51465 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 1 May 2017 16:46:03 -0700 Subject: [PATCH 66/89] use machine GUID from registry for machine ID --- libraries/networking/src/FingerprintUtils.cpp | 129 +++--------------- 1 file changed, 20 insertions(+), 109 deletions(-) diff --git a/libraries/networking/src/FingerprintUtils.cpp b/libraries/networking/src/FingerprintUtils.cpp index 1990d356b6..89f692e77a 100644 --- a/libraries/networking/src/FingerprintUtils.cpp +++ b/libraries/networking/src/FingerprintUtils.cpp @@ -19,8 +19,8 @@ #include #ifdef Q_OS_WIN -#include -#include +#include +#include #endif //Q_OS_WIN #ifdef Q_OS_MAC @@ -47,122 +47,32 @@ QString FingerprintUtils::getMachineFingerprintString() { #endif //Q_OS_MAC #ifdef Q_OS_WIN - HRESULT hres; - IWbemLocator *pLoc = NULL; - - // initialize com. Interface already does, but other - // users of this lib don't necessarily do so. - hres = CoInitializeEx(0, COINIT_MULTITHREADED); - if (FAILED(hres)) { - qCDebug(networking) << "Failed to initialize COM library!"; - return uuidString; - } + HKEY cryptoKey; - // initialize WbemLocator - hres = CoCreateInstance( - CLSID_WbemLocator, - 0, - CLSCTX_INPROC_SERVER, - IID_IWbemLocator, (LPVOID *) &pLoc); + // try and open the key that contains the machine GUID + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Cryptography", 0, KEY_READ, &cryptoKey) == ERROR_SUCCESS) { + DWORD type; + DWORD guidSize; - if (FAILED(hres)) { - qCDebug(networking) << "Failed to initialize WbemLocator"; - return uuidString; - } - - // Connect to WMI through the IWbemLocator::ConnectServer method - IWbemServices *pSvc = NULL; + const char* MACHINE_GUID_KEY = "MachineGuid"; - // Connect to the root\cimv2 namespace with - // the current user and obtain pointer pSvc - // to make IWbemServices calls. - hres = pLoc->ConnectServer( - _bstr_t(L"ROOT\\CIMV2"), // Object path of WMI namespace - NULL, // User name. NULL = current user - NULL, // User password. NULL = current - 0, // Locale. NULL indicates current - NULL, // Security flags. - 0, // Authority (for example, Kerberos) - 0, // Context object - &pSvc // pointer to IWbemServices proxy - ); + // try and retrieve the size of the GUID value + if (RegQueryValueEx(cryptoKey, MACHINE_GUID_KEY, NULL, &type, NULL, &guidSize) == ERROR_SUCCESS) { + // make sure that the value is a string + if (type == REG_SZ) { + // retrieve the machine GUID and return that as our UUID string + std::string machineGUID(guidSize / sizeof(char), '\0'); - if (FAILED(hres)) { - pLoc->Release(); - qCDebug(networking) << "Failed to connect to WMI"; - return uuidString; - } - - // Set security levels on the proxy - hres = CoSetProxyBlanket( - pSvc, // Indicates the proxy to set - RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx - RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx - NULL, // Server principal name - RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx - RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx - NULL, // client identity - EOAC_NONE // proxy capabilities - ); - - if (FAILED(hres)) { - pSvc->Release(); - pLoc->Release(); - qCDebug(networking) << "Failed to set security on proxy blanket"; - return uuidString; - } - - // Use the IWbemServices pointer to grab the Win32_BIOS stuff - IEnumWbemClassObject* pEnumerator = NULL; - hres = pSvc->ExecQuery( - bstr_t("WQL"), - bstr_t("SELECT * FROM Win32_ComputerSystemProduct"), - WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, - NULL, - &pEnumerator); - - if (FAILED(hres)) { - pSvc->Release(); - pLoc->Release(); - qCDebug(networking) << "query to get Win32_ComputerSystemProduct info"; - return uuidString; - } - - // Get the SerialNumber from the Win32_BIOS data - IWbemClassObject *pclsObj; - ULONG uReturn = 0; - - SHORT sRetStatus = -100; - - while (pEnumerator) { - HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn); - - if(0 == uReturn){ - break; - } - - VARIANT vtProp; - - // Get the value of the Name property - hr = pclsObj->Get(L"UUID", 0, &vtProp, 0, 0); - if (!FAILED(hres)) { - switch (vtProp.vt) { - case VT_BSTR: - uuidString = QString::fromWCharArray(vtProp.bstrVal); - break; + if (RegQueryValueEx(cryptoKey, MACHINE_GUID_KEY, NULL, NULL, + reinterpret_cast(&machineGUID[0]), &guidSize) == ERROR_SUCCESS) { + uuidString = QString::fromStdString(machineGUID); + } } } - VariantClear(&vtProp); - pclsObj->Release(); + RegCloseKey(cryptoKey); } - pEnumerator->Release(); - // Cleanup - pSvc->Release(); - pLoc->Release(); - - qCDebug(networking) << "Windows BIOS UUID: " << uuidString; #endif //Q_OS_WIN return uuidString; @@ -177,6 +87,7 @@ QUuid FingerprintUtils::getMachineFingerprint() { // return QUuid() ("{00000...}"), which handles // any errors in getting the string QUuid uuid(uuidString); + if (uuid == QUuid()) { // if you cannot read a fallback key cuz we aren't saving them, just generate one for // this session and move on From 1857b297e091bf0441526c5420436e13f8bc9582 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 4 May 2017 15:52:04 -0700 Subject: [PATCH 67/89] don't re-grab the machine fingerprint every DS connection --- libraries/networking/src/FingerprintUtils.cpp | 51 +++++++++++-------- libraries/networking/src/FingerprintUtils.h | 1 + 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/libraries/networking/src/FingerprintUtils.cpp b/libraries/networking/src/FingerprintUtils.cpp index 89f692e77a..216e0f28dd 100644 --- a/libraries/networking/src/FingerprintUtils.cpp +++ b/libraries/networking/src/FingerprintUtils.cpp @@ -30,6 +30,9 @@ #endif //Q_OS_MAC static const QString FALLBACK_FINGERPRINT_KEY = "fallbackFingerprint"; + +QUuid FingerprintUtils::_machineFingerprint { QUuid() }; + QString FingerprintUtils::getMachineFingerprintString() { QString uuidString; #ifdef Q_OS_LINUX @@ -81,30 +84,36 @@ QString FingerprintUtils::getMachineFingerprintString() { QUuid FingerprintUtils::getMachineFingerprint() { - QString uuidString = getMachineFingerprintString(); + if (_machineFingerprint.isNull()) { + QString uuidString = getMachineFingerprintString(); + + // now, turn into uuid. A malformed string will + // return QUuid() ("{00000...}"), which handles + // any errors in getting the string + QUuid uuid(uuidString); - // now, turn into uuid. A malformed string will - // return QUuid() ("{00000...}"), which handles - // any errors in getting the string - QUuid uuid(uuidString); - - if (uuid == QUuid()) { - // if you cannot read a fallback key cuz we aren't saving them, just generate one for - // this session and move on - if (DependencyManager::get().isNull()) { - return QUuid::createUuid(); - } - // read fallback key (if any) - Settings settings; - uuid = QUuid(settings.value(FALLBACK_FINGERPRINT_KEY).toString()); - qCDebug(networking) << "read fallback maching fingerprint: " << uuid.toString(); if (uuid == QUuid()) { - // no fallback yet, set one - uuid = QUuid::createUuid(); - settings.setValue(FALLBACK_FINGERPRINT_KEY, uuid.toString()); - qCDebug(networking) << "no fallback machine fingerprint, setting it to: " << uuid.toString(); + // if you cannot read a fallback key cuz we aren't saving them, just generate one for + // this session and move on + if (DependencyManager::get().isNull()) { + return QUuid::createUuid(); + } + // read fallback key (if any) + Settings settings; + uuid = QUuid(settings.value(FALLBACK_FINGERPRINT_KEY).toString()); + qCDebug(networking) << "read fallback maching fingerprint: " << uuid.toString(); + + if (uuid == QUuid()) { + // no fallback yet, set one + uuid = QUuid::createUuid(); + settings.setValue(FALLBACK_FINGERPRINT_KEY, uuid.toString()); + qCDebug(networking) << "no fallback machine fingerprint, setting it to: " << uuid.toString(); + } } + + _machineFingerprint = uuid; } - return uuid; + + return _machineFingerprint; } diff --git a/libraries/networking/src/FingerprintUtils.h b/libraries/networking/src/FingerprintUtils.h index 572b150ec4..c4cb900a48 100644 --- a/libraries/networking/src/FingerprintUtils.h +++ b/libraries/networking/src/FingerprintUtils.h @@ -21,6 +21,7 @@ public: private: static QString getMachineFingerprintString(); + static QUuid _machineFingerprint; }; #endif // hifi_FingerprintUtils_h From c17d1a6d881333f196bd8272d2346cc062c98f71 Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 4 May 2017 19:21:32 -0400 Subject: [PATCH 68/89] manually promote glm values to double when stringifying (to prevent -Wdouble-promotion "error") --- libraries/script-engine/src/Mat4.cpp | 2 +- libraries/script-engine/src/Quat.cpp | 4 ++-- libraries/script-engine/src/Vec3.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/script-engine/src/Mat4.cpp b/libraries/script-engine/src/Mat4.cpp index 2d26a66823..6965f43b32 100644 --- a/libraries/script-engine/src/Mat4.cpp +++ b/libraries/script-engine/src/Mat4.cpp @@ -69,7 +69,7 @@ glm::vec3 Mat4::getUp(const glm::mat4& m) const { } void Mat4::print(const QString& label, const glm::mat4& m, bool transpose) const { - glm::mat4 out = transpose ? glm::transpose(m) : m; + glm::dmat4 out = transpose ? glm::transpose(m) : m; QString message = QString("%1 %2").arg(qPrintable(label)); message = message.arg(glm::to_string(out).c_str()); qCDebug(scriptengine) << message; diff --git a/libraries/script-engine/src/Quat.cpp b/libraries/script-engine/src/Quat.cpp index 8d84b45b90..a6f7acffc8 100644 --- a/libraries/script-engine/src/Quat.cpp +++ b/libraries/script-engine/src/Quat.cpp @@ -119,9 +119,9 @@ float Quat::dot(const glm::quat& q1, const glm::quat& q2) { void Quat::print(const QString& label, const glm::quat& q, bool asDegrees) { QString message = QString("%1 %2").arg(qPrintable(label)); if (asDegrees) { - message = message.arg(glm::to_string(safeEulerAngles(q)).c_str()); + message = message.arg(glm::to_string(glm::dvec3(safeEulerAngles(q))).c_str()); } else { - message = message.arg(glm::to_string(q).c_str()); + message = message.arg(glm::to_string(glm::dquat(q)).c_str()); } qCDebug(scriptengine) << message; if (ScriptEngine* scriptEngine = qobject_cast(engine())) { diff --git a/libraries/script-engine/src/Vec3.cpp b/libraries/script-engine/src/Vec3.cpp index f66b99dfa1..a156f56d96 100644 --- a/libraries/script-engine/src/Vec3.cpp +++ b/libraries/script-engine/src/Vec3.cpp @@ -29,7 +29,7 @@ float Vec3::orientedAngle(const glm::vec3& v1, const glm::vec3& v2, const glm::v void Vec3::print(const QString& label, const glm::vec3& v) { QString message = QString("%1 %2").arg(qPrintable(label)); - message = message.arg(glm::to_string(v).c_str()); + message = message.arg(glm::to_string(glm::dvec3(v)).c_str()); qCDebug(scriptengine) << message; if (ScriptEngine* scriptEngine = qobject_cast(engine())) { scriptEngine->print(message); From c04c0c5b13090056ea332f10d11d766578d7c678 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 4 May 2017 18:36:51 -0700 Subject: [PATCH 69/89] if model-overlay has no name, use its url --- interface/src/ui/overlays/ModelOverlay.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index e993166558..8360dc186b 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -279,3 +279,10 @@ void ModelOverlay::locationChanged(bool tellPhysics) { _model->setTranslation(getPosition()); } } + +QString ModelOverlay::getName() const { + if (_name != "") { + return QString("Overlay:") + getType() + ":" + _name; + } + return QString("Overlay:") + getType() + ":" + _url.toString(); +} From 0406cc41e0da1136c0cd384205812c6fad5becf6 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 4 May 2017 18:37:04 -0700 Subject: [PATCH 70/89] if model-overlay has no name, use its url --- interface/src/ui/overlays/ModelOverlay.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/src/ui/overlays/ModelOverlay.h b/interface/src/ui/overlays/ModelOverlay.h index 8afe9a20b6..312f88dcb5 100644 --- a/interface/src/ui/overlays/ModelOverlay.h +++ b/interface/src/ui/overlays/ModelOverlay.h @@ -22,6 +22,8 @@ public: static QString const TYPE; virtual QString getType() const override { return TYPE; } + virtual QString getName() const override; + ModelOverlay(); ModelOverlay(const ModelOverlay* modelOverlay); From d25db099b94619596d975e9024aa5484cd1bbdb7 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 4 May 2017 18:49:46 -0700 Subject: [PATCH 71/89] be more effecient about ignoring edits to unknown entities --- libraries/entities/src/EntityTree.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 3ad5cc92a5..76483d0786 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -990,6 +990,17 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c entityItemID, properties); endDecode = usecTimestampNow(); + EntityItemPointer existingEntity; + if (!isAdd) { + // search for the entity by EntityItemID + startLookup = usecTimestampNow(); + existingEntity = findEntityByEntityItemID(entityItemID); + endLookup = usecTimestampNow(); + if (!existingEntity) { + // this is not an add-entity operation, and we don't know about the identified entity. + validEditPacket = false; + } + } if (validEditPacket && !_entityScriptSourceWhitelist.isEmpty() && !properties.getScript().isEmpty()) { bool passedWhiteList = false; @@ -1036,12 +1047,6 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c // If we got a valid edit packet, then it could be a new entity or it could be an update to // an existing entity... handle appropriately if (validEditPacket) { - - // search for the entity by EntityItemID - startLookup = usecTimestampNow(); - EntityItemPointer existingEntity = findEntityByEntityItemID(entityItemID); - endLookup = usecTimestampNow(); - startFilter = usecTimestampNow(); bool wasChanged = false; // Having (un)lock rights bypasses the filter, unless it's a physics result. From 70efe4aa4f0242cf957dde3ec54e540be045d12f Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 4 May 2017 18:52:17 -0700 Subject: [PATCH 72/89] avoid calling editEntity on overlays --- scripts/system/controllers/handControllerGrab.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index db0840b078..b05f0b241b 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -3878,6 +3878,7 @@ function MyController(hand) { // we appear to be holding something and this script isn't in a state that would be holding something. // unhook it. if we previously took note of this entity's parent, put it back where it was. This // works around some problems that happen when more than one hand or avatar is passing something around. + var childType = Entities.getNestableType(childID); if (_this.previousParentID[childID]) { var previousParentID = _this.previousParentID[childID]; var previousParentJointIndex = _this.previousParentJointIndex[childID]; @@ -3895,7 +3896,7 @@ function MyController(hand) { } _this.previouslyUnhooked[childID] = now; - if (Overlays.getProperty(childID, "grabbable")) { + if (childType == "overlay" && Overlays.getProperty(childID, "grabbable")) { // only auto-unhook overlays that were flagged as grabbable. this avoids unhooking overlays // used in tutorial. Overlays.editOverlay(childID, { @@ -3903,7 +3904,12 @@ function MyController(hand) { parentJointIndex: previousParentJointIndex }); } - Entities.editEntity(childID, { parentID: previousParentID, parentJointIndex: previousParentJointIndex }); + if (childType == "entity") { + Entities.editEntity(childID, { + parentID: previousParentID, + parentJointIndex: previousParentJointIndex + }); + } } else { Entities.editEntity(childID, { parentID: NULL_UUID }); From 51cb5797739c7acd46700915865ed424eda80a3f Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 4 May 2017 19:05:01 -0700 Subject: [PATCH 73/89] function to turn nestable-type into a string --- libraries/shared/src/SpatiallyNestable.cpp | 14 ++++++++++++++ libraries/shared/src/SpatiallyNestable.h | 2 ++ 2 files changed, 16 insertions(+) diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 75574967e4..9c7e216cb6 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -1138,3 +1138,17 @@ SpatiallyNestablePointer SpatiallyNestable::findByID(QUuid id, bool& success) { } return parentWP.lock(); } + + +QString SpatiallyNestable::nestableTypeToString(NestableType nestableType) { + switch(nestableType) { + case NestableType::Entity: + return "entity"; + case NestableType::Avatar: + return "avatar"; + case NestableType::Overlay: + return "overlay"; + default: + return "unknown"; + } +} diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index 0f89016f54..e8961dba98 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -64,6 +64,8 @@ public: static glm::vec3 localToWorldAngularVelocity(const glm::vec3& angularVelocity, const QUuid& parentID, int parentJointIndex, bool& success); + static QString nestableTypeToString(NestableType nestableType); + // world frame virtual const Transform getTransform(bool& success, int depth = 0) const; virtual const Transform getTransform() const; From a7c684e007692fabbb4e684a08d2441999b0220a Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 4 May 2017 19:05:29 -0700 Subject: [PATCH 74/89] entity scripting call to get the nestable-type of an object, by id --- .../entities/src/EntityScriptingInterface.cpp | 33 +++++++++++++++---- .../entities/src/EntityScriptingInterface.h | 2 ++ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index a4ec7b7636..83bc2184a8 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -466,9 +466,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& }); } }); - if (entityFound) { - queueEntityMessage(PacketType::EntityEdit, entityID, properties); - } else { + if (!entityFound) { // attempt to get a name for the mystery ID QString name = "unknown"; QSharedPointer parentFinder = DependencyManager::get(); @@ -478,13 +476,18 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& if (success) { auto nestable = nestableWP.lock(); if (nestable) { - name = nestable->getName(); + NestableType nestableType = nestable->getNestableType(); + if (nestableType == NestableType::Overlay || nestableType == NestableType::Avatar) { + qCWarning(entities) << "attempted edit on non-entity: " << id << name; + return QUuid(); // null UUID to indicate failure + } } } } - qCWarning(entities) << "attempted edit on unknown entity: " << id << name; - return QUuid(); // null UUID to indicate failure } + // we queue edit packets even if we don't know about the entity. This is to allow AC agents + // to edit entities they know only by ID. + queueEntityMessage(PacketType::EntityEdit, entityID, properties); return id; } @@ -1535,6 +1538,24 @@ bool EntityScriptingInterface::isChildOfParent(QUuid childID, QUuid parentID) { return isChild; } +QString EntityScriptingInterface::getNestableType(QUuid id) { + QSharedPointer parentFinder = DependencyManager::get(); + if (!parentFinder) { + return "unknown"; + } + bool success; + SpatiallyNestableWeakPointer objectWP = parentFinder->find(id, success); + if (!success) { + return "unknown"; + } + SpatiallyNestablePointer object = objectWP.lock(); + if (!object) { + return "unknown"; + } + NestableType nestableType = object->getNestableType(); + return SpatiallyNestable::nestableTypeToString(nestableType); +} + QVector EntityScriptingInterface::getChildrenIDsOfJoint(const QUuid& parentID, int jointIndex) { QVector result; if (!_entityTree) { diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index b25764790e..f5656860e3 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -304,6 +304,8 @@ public slots: Q_INVOKABLE QVector getChildrenIDsOfJoint(const QUuid& parentID, int jointIndex); Q_INVOKABLE bool isChildOfParent(QUuid childID, QUuid parentID); + Q_INVOKABLE QString getNestableType(QUuid id); + Q_INVOKABLE QUuid getKeyboardFocusEntity() const; Q_INVOKABLE void setKeyboardFocusEntity(QUuid id); From 900fae30fdff36fb384ad574fb7bd47ad4368e67 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 4 May 2017 19:05:52 -0700 Subject: [PATCH 75/89] avoid calling editEntity on overlays --- scripts/system/controllers/handControllerGrab.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index b05f0b241b..adec4d7258 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -3912,9 +3912,12 @@ function MyController(hand) { } } else { - Entities.editEntity(childID, { parentID: NULL_UUID }); - if (Overlays.getProperty(childID, "grabbable")) { - Overlays.editOverlay(childID, { parentID: NULL_UUID }); + if (childType == "entity") { + Entities.editEntity(childID, { parentID: NULL_UUID }); + } else if (childType == "overlay") { + if (Overlays.getProperty(childID, "grabbable")) { + Overlays.editOverlay(childID, { parentID: NULL_UUID }); + } } } } From 7f4399b0a38369b06093aea08b3d1494f80fe8c1 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 4 May 2017 19:16:15 -0700 Subject: [PATCH 76/89] fix name in entity-edit-an-overlay warning --- libraries/entities/src/EntityScriptingInterface.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 83bc2184a8..ffb65a2dba 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -467,8 +467,8 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& } }); if (!entityFound) { - // attempt to get a name for the mystery ID - QString name = "unknown"; + // we've made an edit to an entity we don't know about, or to a non-entity. If it's a known non-entity, + // print a warning and don't send an edit packet to the entity-server. QSharedPointer parentFinder = DependencyManager::get(); if (parentFinder) { bool success; @@ -478,7 +478,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& if (nestable) { NestableType nestableType = nestable->getNestableType(); if (nestableType == NestableType::Overlay || nestableType == NestableType::Avatar) { - qCWarning(entities) << "attempted edit on non-entity: " << id << name; + qCWarning(entities) << "attempted edit on non-entity: " << id << nestable->getName(); return QUuid(); // null UUID to indicate failure } } From 25807a5258f3c6539acd6c8e739f5d50b6e354fb Mon Sep 17 00:00:00 2001 From: humbletim Date: Fri, 5 May 2017 09:48:24 -0400 Subject: [PATCH 77/89] fix warning color --- interface/src/ui/JSConsole.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/JSConsole.cpp b/interface/src/ui/JSConsole.cpp index 1abc5e8c2e..79314ce49a 100644 --- a/interface/src/ui/JSConsole.cpp +++ b/interface/src/ui/JSConsole.cpp @@ -29,7 +29,7 @@ const QString COMMAND_STYLE = "color: #266a9b;"; const QString RESULT_SUCCESS_STYLE = "color: #677373;"; const QString RESULT_INFO_STYLE = "color: #223bd1;"; -const QString RESULT_WARNING_STYLE = "color: #d1b322;"; +const QString RESULT_WARNING_STYLE = "color: #d13b22;"; const QString RESULT_ERROR_STYLE = "color: #d13b22;"; const QString GUTTER_PREVIOUS_COMMAND = "<"; From b41fb952ae18156045b3bee3c992509175d6a442 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 5 May 2017 10:29:46 -0700 Subject: [PATCH 78/89] UI changes - Thanks Alan! --- interface/resources/qml/hifi/ComboDialog.qml | 11 +++++++---- interface/resources/qml/hifi/Pal.qml | 8 ++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/interface/resources/qml/hifi/ComboDialog.qml b/interface/resources/qml/hifi/ComboDialog.qml index e328805d74..83fcad18c7 100644 --- a/interface/resources/qml/hifi/ComboDialog.qml +++ b/interface/resources/qml/hifi/ComboDialog.qml @@ -23,7 +23,8 @@ Item { property var callbackFunction; property int dialogWidth; property int dialogHeight; - property int comboOptionTextSize: 18; + property int comboOptionTextSize: 16; + property int comboBodyTextSize: 16; FontLoader { id: ralewayRegular; source: "../../fonts/Raleway-Regular.ttf"; } FontLoader { id: ralewaySemiBold; source: "../../fonts/Raleway-SemiBold.ttf"; } visible: false; @@ -63,7 +64,7 @@ Item { anchors.left: parent.left; anchors.leftMargin: 20; size: 24; - color: 'black'; + color: hifi.colors.darkGray; horizontalAlignment: Text.AlignLeft; verticalAlignment: Text.AlignTop; } @@ -141,6 +142,7 @@ Item { height: 30; size: comboOptionTextSize; wrapMode: Text.WordWrap; + color: hifi.colors.darkGray; } RalewayRegular { @@ -148,11 +150,12 @@ Item { text: bodyText; anchors.top: optionTitle.bottom; anchors.left: comboOptionSelected.right; - anchors.leftMargin: 25; + anchors.leftMargin: 10; anchors.right: parent.right; anchors.rightMargin: 10; - size: comboOptionTextSize; + size: comboBodyTextSize; wrapMode: Text.WordWrap; + color: hifi.colors.darkGray; } MouseArea { diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 60439cf63f..152e2f70a2 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -1013,10 +1013,10 @@ Rectangle { onClicked: { popupComboDialog("Set your availability:", availabilityComboBox.availabilityStrings, - ["Your username will be visible in everyone's 'Nearby' list.\nAnyone will be able to jump to your location from within the 'Nearby' list.", - "Your location will be visible in the 'Connections' list only for those with whom you are connected or friends.\nThey will be able to jump to your location if the domain allows.", - "Your location will be visible in the 'Connections' list only for those with whom you are friends.\nThey will be able to jump to your location if the domain allows.", - "Your location will not be visible in the 'Connections' list of any other users. Only domain admins will be able to see your username in the 'Nearby' list."], + ["Your username will be visible in everyone's 'Nearby' list. Anyone will be able to jump to your location from within the 'Nearby' list.", + "Your location will be visible in the 'Connections' list only for those with whom you are connected or friends. They'll be able to jump to your location if the domain allows.", + "Your location will be visible in the 'Connections' list only for those with whom you are friends. They'll be able to jump to your location if the domain allows. You will not receive 'Go To' notifications from Connections.", + "You will appear offline in the 'Connections' list, and you will not receive 'Go To' notifications from Connections."], ["all", "connections", "friends", "none"]); } onEntered: availabilityComboBox.color = hifi.colors.lightGrayText; From 2ecc71a9907e5f430bab4e8d2bf3a2228746dbeb Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 5 May 2017 10:49:09 -0700 Subject: [PATCH 79/89] Annoncement Card visual tweeks. --- interface/resources/images/Announce-Blast.svg | 21 +++++++++++++++++++ interface/resources/qml/hifi/Card.qml | 4 +--- 2 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 interface/resources/images/Announce-Blast.svg diff --git a/interface/resources/images/Announce-Blast.svg b/interface/resources/images/Announce-Blast.svg new file mode 100644 index 0000000000..56cdb10b9f --- /dev/null +++ b/interface/resources/images/Announce-Blast.svg @@ -0,0 +1,21 @@ + + + + + + + + + diff --git a/interface/resources/qml/hifi/Card.qml b/interface/resources/qml/hifi/Card.qml index 9617b41150..fd76c03e45 100644 --- a/interface/resources/qml/hifi/Card.qml +++ b/interface/resources/qml/hifi/Card.qml @@ -167,11 +167,9 @@ Item { Rectangle { id: lozenge; visible: isAnnouncement; - color: hifi.colors.redHighlight; + color: lozengeHot.containsMouse ? hifi.colors.redAccent : hifi.colors.redHighlight; anchors.fill: infoRow; radius: lozenge.height / 2.0; - border.width: lozengeHot.containsMouse ? 4 : 0; - border.color: "white"; } Row { id: infoRow; From 905f36976ff85cdffb5a1eb61c2cd2e992d9fc92 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 5 May 2017 11:28:49 -0700 Subject: [PATCH 80/89] New language --- interface/resources/qml/hifi/Pal.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 152e2f70a2..1f8cdfc81b 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -1015,8 +1015,8 @@ Rectangle { availabilityComboBox.availabilityStrings, ["Your username will be visible in everyone's 'Nearby' list. Anyone will be able to jump to your location from within the 'Nearby' list.", "Your location will be visible in the 'Connections' list only for those with whom you are connected or friends. They'll be able to jump to your location if the domain allows.", - "Your location will be visible in the 'Connections' list only for those with whom you are friends. They'll be able to jump to your location if the domain allows. You will not receive 'Go To' notifications from Connections.", - "You will appear offline in the 'Connections' list, and you will not receive 'Go To' notifications from Connections."], + "Your location will be visible in the 'Connections' list only for those with whom you are friends. They'll be able to jump to your location if the domain allows. You will not receive 'Happening Now' notifications in 'Go To'.", + "You will appear offline in the 'Connections' list, and you will not receive 'Happening Now' notifications in 'Go To'."], ["all", "connections", "friends", "none"]); } onEntered: availabilityComboBox.color = hifi.colors.lightGrayText; From a10606b9949f12d13fc7dc7052d0594471d4e819 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 5 May 2017 11:44:59 -0700 Subject: [PATCH 81/89] OMG language changed again --- interface/resources/qml/hifi/Pal.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 1f8cdfc81b..1755d2fbec 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -1015,7 +1015,7 @@ Rectangle { availabilityComboBox.availabilityStrings, ["Your username will be visible in everyone's 'Nearby' list. Anyone will be able to jump to your location from within the 'Nearby' list.", "Your location will be visible in the 'Connections' list only for those with whom you are connected or friends. They'll be able to jump to your location if the domain allows.", - "Your location will be visible in the 'Connections' list only for those with whom you are friends. They'll be able to jump to your location if the domain allows. You will not receive 'Happening Now' notifications in 'Go To'.", + "Your location will be visible in the 'Connections' list only for those with whom you are friends. They'll be able to jump to your location if the domain allows. You will only receive 'Happening Now' notifications in 'Go To' from friends.", "You will appear offline in the 'Connections' list, and you will not receive 'Happening Now' notifications in 'Go To'."], ["all", "connections", "friends", "none"]); } From e882e7b648460a67c8a75eb183f740e0478d7903 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Fri, 5 May 2017 15:36:38 -0400 Subject: [PATCH 82/89] rm extra lock in audio read --- libraries/audio-client/src/AudioClient.cpp | 10 +++++++--- libraries/audio-client/src/AudioClient.h | 4 +--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index b684aac89c..fb0220c24d 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1115,7 +1115,7 @@ void AudioClient::prepareLocalAudioInjectors() { while (samplesNeeded > 0) { // lock for every write to avoid locking out the device callback // this lock is intentional - the buffer is only lock-free in its use in the device callback - RecursiveLock lock(_localAudioMutex); + Lock lock(_localAudioMutex); samplesNeeded = bufferCapacity - _localSamplesAvailable.load(std::memory_order_relaxed); if (samplesNeeded <= 0) { @@ -1452,7 +1452,7 @@ void AudioClient::outputNotify() { bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo) { bool supportedFormat = false; - RecursiveLock lock(_localAudioMutex); + Lock lock(_localAudioMutex); _localSamplesAvailable.exchange(0, std::memory_order_release); // cleanup any previously initialized device @@ -1681,8 +1681,12 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) { int injectorSamplesPopped = 0; { - RecursiveLock lock(_audio->_localAudioMutex); bool append = networkSamplesPopped > 0; + // this does not require a lock as of the only two functions adding to _localSamplesAvailable (samples count): + // - prepareLocalAudioInjectors will only increase samples count + // - switchOutputToAudioDevice will zero samples count + // stop the device, so that readData will exhaust the existing buffer or see a zeroed samples count + // and start the device, which can only see a zeroed samples count samplesRequested = std::min(samplesRequested, _audio->_localSamplesAvailable.load(std::memory_order_acquire)); if ((injectorSamplesPopped = _localInjectorsStream.appendSamples(mixBuffer, samplesRequested, append)) > 0) { _audio->_localSamplesAvailable.fetch_sub(injectorSamplesPopped, std::memory_order_release); diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 139749e8e8..aaedee7456 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -96,8 +96,6 @@ public: using AudioPositionGetter = std::function; using AudioOrientationGetter = std::function; - using RecursiveMutex = std::recursive_mutex; - using RecursiveLock = std::unique_lock; using Mutex = std::mutex; using Lock = std::unique_lock; @@ -345,7 +343,7 @@ private: int16_t _localScratchBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_AMBISONIC]; float* _localOutputMixBuffer { NULL }; AudioInjectorsThread _localAudioThread; - RecursiveMutex _localAudioMutex; + Mutex _localAudioMutex; // for output audio (used by this thread) int _outputPeriod { 0 }; From cdf6c332a6b347eea32eeb645f5ae031edafac0c Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Fri, 5 May 2017 15:42:22 -0400 Subject: [PATCH 83/89] prevent overflow of local injectors buffer --- libraries/audio-client/src/AudioClient.cpp | 32 +++++++++++----------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index fb0220c24d..f61429812f 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1097,26 +1097,26 @@ void AudioClient::handleRecordedAudioInput(const QByteArray& audio) { } void AudioClient::prepareLocalAudioInjectors() { - if (_outputPeriod == 0) { - return; - } - - int bufferCapacity = _localInjectorsStream.getSampleCapacity(); - if (_localToOutputResampler) { - // avoid overwriting the buffer, - // instead of failing on writes because the buffer is used as a lock-free pipe - bufferCapacity -= - _localToOutputResampler->getMaxOutput(AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL) * - AudioConstants::STEREO; - bufferCapacity += 1; - } - int samplesNeeded = std::numeric_limits::max(); while (samplesNeeded > 0) { - // lock for every write to avoid locking out the device callback - // this lock is intentional - the buffer is only lock-free in its use in the device callback + // unlock between every write to allow device switching Lock lock(_localAudioMutex); + // in case of a device switch, consider bufferCapacity volatile across iterations + if (_outputPeriod == 0) { + return; + } + + int bufferCapacity = _localInjectorsStream.getSampleCapacity(); + if (_localToOutputResampler) { + // avoid overwriting the buffer, + // instead of failing on writes because the buffer is used as a lock-free pipe + bufferCapacity -= + _localToOutputResampler->getMaxOutput(AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL) * + AudioConstants::STEREO; + bufferCapacity += 1; + } + samplesNeeded = bufferCapacity - _localSamplesAvailable.load(std::memory_order_relaxed); if (samplesNeeded <= 0) { break; From 48af83a960bd231d2574a7de6c5a91dbc2f7c562 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Fri, 5 May 2017 17:05:40 -0400 Subject: [PATCH 84/89] guard against audio injector and local buffer datarace --- libraries/audio-client/src/AudioClient.cpp | 24 +++++++++++-------- libraries/audio/src/AbstractAudioInterface.h | 4 ++++ libraries/audio/src/AudioInjector.cpp | 23 ++++++++++++------ libraries/audio/src/AudioInjector.h | 2 ++ .../audio/src/AudioInjectorLocalBuffer.cpp | 3 +-- .../audio/src/AudioInjectorLocalBuffer.h | 2 +- 6 files changed, 38 insertions(+), 20 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index f61429812f..0710250c6a 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1168,16 +1168,18 @@ bool AudioClient::mixLocalAudioInjectors(float* mixBuffer) { memset(mixBuffer, 0, AudioConstants::NETWORK_FRAME_SAMPLES_STEREO * sizeof(float)); for (AudioInjector* injector : _activeLocalAudioInjectors) { - if (injector->getLocalBuffer()) { + // the lock guarantees that injectorBuffer, if found, is invariant + AudioInjectorLocalBuffer* injectorBuffer; + if ((injectorBuffer = injector->getLocalBuffer())) { static const int HRTF_DATASET_INDEX = 1; int numChannels = injector->isAmbisonic() ? AudioConstants::AMBISONIC : (injector->isStereo() ? AudioConstants::STEREO : AudioConstants::MONO); - qint64 bytesToRead = numChannels * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL; + size_t bytesToRead = numChannels * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL; // get one frame from the injector memset(_localScratchBuffer, 0, bytesToRead); - if (0 < injector->getLocalBuffer()->readData((char*)_localScratchBuffer, bytesToRead)) { + if (0 < injectorBuffer->readData((char*)_localScratchBuffer, bytesToRead)) { if (injector->isAmbisonic()) { @@ -1317,15 +1319,17 @@ void AudioClient::setIsStereoInput(bool isStereoInput) { } bool AudioClient::outputLocalInjector(AudioInjector* injector) { - Lock lock(_injectorsMutex); - if (injector->getLocalBuffer() && _audioInput ) { - // just add it to the vector of active local injectors, if - // not already there. - // Since this is invoked with invokeMethod, there _should_ be - // no reason to lock access to the vector of injectors. + AudioInjectorLocalBuffer* injectorBuffer; + if ((injectorBuffer = injector->getLocalBuffer())) { + // local injectors are on the AudioInjectorsThread, so we must guard access + Lock lock(_injectorsMutex); if (!_activeLocalAudioInjectors.contains(injector)) { qCDebug(audioclient) << "adding new injector"; _activeLocalAudioInjectors.append(injector); + + // move local buffer to the LocalAudioThread to avoid dataraces with AudioInjector (like stop()) + injectorBuffer->setParent(nullptr); + injectorBuffer->moveToThread(&_localAudioThread); } else { qCDebug(audioclient) << "injector exists in active list already"; } @@ -1333,7 +1337,7 @@ bool AudioClient::outputLocalInjector(AudioInjector* injector) { return true; } else { - // no local buffer or audio + // no local buffer return false; } } diff --git a/libraries/audio/src/AbstractAudioInterface.h b/libraries/audio/src/AbstractAudioInterface.h index d61b8d60ca..2e4611cd4e 100644 --- a/libraries/audio/src/AbstractAudioInterface.h +++ b/libraries/audio/src/AbstractAudioInterface.h @@ -33,7 +33,11 @@ public: PacketType packetType, QString codecName = QString("")); public slots: + // threadsafe + // moves injector->getLocalBuffer() to another thread (so removes its parent) + // take care to delete it when ~AudioInjector, as parenting Qt semantics will not work virtual bool outputLocalInjector(AudioInjector* injector) = 0; + virtual bool shouldLoopbackInjectors() { return false; } virtual void setIsStereoInput(bool stereo) = 0; diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 86cbc98d84..ce98225190 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -51,6 +51,10 @@ AudioInjector::AudioInjector(const QByteArray& audioData, const AudioInjectorOpt { } +AudioInjector::~AudioInjector() { + deleteLocalBuffer(); +} + bool AudioInjector::stateHas(AudioInjectorState state) const { return (_state & state) == state; } @@ -87,11 +91,7 @@ void AudioInjector::finish() { emit finished(); - if (_localBuffer) { - _localBuffer->stop(); - _localBuffer->deleteLater(); - _localBuffer = NULL; - } + deleteLocalBuffer(); if (stateHas(AudioInjectorState::PendingDelete)) { // we've been asked to delete after finishing, trigger a deleteLater here @@ -163,7 +163,7 @@ bool AudioInjector::injectLocally() { if (_localAudioInterface) { if (_audioData.size() > 0) { - _localBuffer = new AudioInjectorLocalBuffer(_audioData, this); + _localBuffer = new AudioInjectorLocalBuffer(_audioData); _localBuffer->open(QIODevice::ReadOnly); _localBuffer->setShouldLoop(_options.loop); @@ -172,7 +172,8 @@ bool AudioInjector::injectLocally() { _localBuffer->setCurrentOffset(_currentSendOffset); // call this function on the AudioClient's thread - success = QMetaObject::invokeMethod(_localAudioInterface, "outputLocalInjector", Q_ARG(AudioInjector*, this)); + // this will move the local buffer's thread to the LocalInjectorThread + success = _localAudioInterface->outputLocalInjector(this); if (!success) { qCDebug(audio) << "AudioInjector::injectLocally could not output locally via _localAudioInterface"; @@ -185,6 +186,14 @@ bool AudioInjector::injectLocally() { return success; } +void AudioInjector::deleteLocalBuffer() { + if (_localBuffer) { + _localBuffer->stop(); + _localBuffer->deleteLater(); + _localBuffer = nullptr; + } +} + const uchar MAX_INJECTOR_VOLUME = packFloatGainToByte(1.0f); static const int64_t NEXT_FRAME_DELTA_ERROR_OR_FINISHED = -1; static const int64_t NEXT_FRAME_DELTA_IMMEDIATELY = 0; diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index 7d57708738..a901c2520f 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -52,6 +52,7 @@ class AudioInjector : public QObject { public: AudioInjector(const Sound& sound, const AudioInjectorOptions& injectorOptions); AudioInjector(const QByteArray& audioData, const AudioInjectorOptions& injectorOptions); + ~AudioInjector(); bool isFinished() const { return (stateHas(AudioInjectorState::Finished)); } @@ -99,6 +100,7 @@ private: int64_t injectNextFrame(); bool inject(bool(AudioInjectorManager::*injection)(AudioInjector*)); bool injectLocally(); + void deleteLocalBuffer(); static AbstractAudioInterface* _localAudioInterface; diff --git a/libraries/audio/src/AudioInjectorLocalBuffer.cpp b/libraries/audio/src/AudioInjectorLocalBuffer.cpp index 049adb0cc6..7834644baf 100644 --- a/libraries/audio/src/AudioInjectorLocalBuffer.cpp +++ b/libraries/audio/src/AudioInjectorLocalBuffer.cpp @@ -11,8 +11,7 @@ #include "AudioInjectorLocalBuffer.h" -AudioInjectorLocalBuffer::AudioInjectorLocalBuffer(const QByteArray& rawAudioArray, QObject* parent) : - QIODevice(parent), +AudioInjectorLocalBuffer::AudioInjectorLocalBuffer(const QByteArray& rawAudioArray) : _rawAudioArray(rawAudioArray), _shouldLoop(false), _isStopped(false), diff --git a/libraries/audio/src/AudioInjectorLocalBuffer.h b/libraries/audio/src/AudioInjectorLocalBuffer.h index 07d8ae5b9f..673966ff09 100644 --- a/libraries/audio/src/AudioInjectorLocalBuffer.h +++ b/libraries/audio/src/AudioInjectorLocalBuffer.h @@ -19,7 +19,7 @@ class AudioInjectorLocalBuffer : public QIODevice { Q_OBJECT public: - AudioInjectorLocalBuffer(const QByteArray& rawAudioArray, QObject* parent); + AudioInjectorLocalBuffer(const QByteArray& rawAudioArray); void stop(); From ed3fc004a6a2a1f55026e01032fbbc2fbf7c0abc Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Fri, 5 May 2017 18:15:56 -0400 Subject: [PATCH 85/89] avoid assignment-as-test --- libraries/audio-client/src/AudioClient.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 0710250c6a..927000d614 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1169,8 +1169,8 @@ bool AudioClient::mixLocalAudioInjectors(float* mixBuffer) { for (AudioInjector* injector : _activeLocalAudioInjectors) { // the lock guarantees that injectorBuffer, if found, is invariant - AudioInjectorLocalBuffer* injectorBuffer; - if ((injectorBuffer = injector->getLocalBuffer())) { + AudioInjectorLocalBuffer* injectorBuffer = injector->getLocalBuffer(); + if (injectorBuffer) { static const int HRTF_DATASET_INDEX = 1; @@ -1319,8 +1319,8 @@ void AudioClient::setIsStereoInput(bool isStereoInput) { } bool AudioClient::outputLocalInjector(AudioInjector* injector) { - AudioInjectorLocalBuffer* injectorBuffer; - if ((injectorBuffer = injector->getLocalBuffer())) { + AudioInjectorLocalBuffer* injectorBuffer = injector->getLocalBuffer(); + if (injectorBuffer) { // local injectors are on the AudioInjectorsThread, so we must guard access Lock lock(_injectorsMutex); if (!_activeLocalAudioInjectors.contains(injector)) { From 7db404eba01c7af86a400f4384523775e54b27cc Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Fri, 5 May 2017 18:16:37 -0400 Subject: [PATCH 86/89] recast avoid overwriting buffer logic --- libraries/audio-client/src/AudioClient.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 927000d614..680e9129aa 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1108,17 +1108,16 @@ void AudioClient::prepareLocalAudioInjectors() { } int bufferCapacity = _localInjectorsStream.getSampleCapacity(); + int maxOutputSamples = AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * AudioConstants::STEREO; if (_localToOutputResampler) { - // avoid overwriting the buffer, - // instead of failing on writes because the buffer is used as a lock-free pipe - bufferCapacity -= + maxOutputSamples = _localToOutputResampler->getMaxOutput(AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL) * AudioConstants::STEREO; - bufferCapacity += 1; } samplesNeeded = bufferCapacity - _localSamplesAvailable.load(std::memory_order_relaxed); - if (samplesNeeded <= 0) { + if (samplesNeeded < maxOutputSamples) { + // avoid overwriting the buffer to prevent losing frames break; } From 4895f51dc1cf8139a0ecb03baa6630dea22ea450 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 5 May 2017 16:41:32 -0700 Subject: [PATCH 87/89] Update content set URL --- server-console/src/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server-console/src/main.js b/server-console/src/main.js index cdbb1d0a3c..4ce1ccfb02 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -42,7 +42,7 @@ const appIcon = path.join(__dirname, '../resources/console.png'); const DELETE_LOG_FILES_OLDER_THAN_X_SECONDS = 60 * 60 * 24 * 7; // 7 Days const LOG_FILE_REGEX = /(domain-server|ac-monitor|ac)-.*-std(out|err).txt/; -const HOME_CONTENT_URL = "http://cdn.highfidelity.com/content-sets/home-tutorial-28.tar.gz"; +const HOME_CONTENT_URL = "http://cdn.highfidelity.com/content-sets/home-tutorial-RC39.tar.gz"; function getBuildInfo() { var buildInfoPath = null; From b2be22f0e0dcf57f550d53baccec2a07ebfeb811 Mon Sep 17 00:00:00 2001 From: Vladyslav Stelmakhovskyi Date: Sat, 6 May 2017 10:28:57 +0200 Subject: [PATCH 88/89] Maerge broke the logic. Fixed now --- scripts/system/tablet-ui/tabletUI.js | 39 +++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index b8ec610721..bd5be142a0 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -25,9 +25,18 @@ var debugTablet = false; var tabletScalePercentage = 100.0; UIWebTablet = null; + var MSECS_PER_SEC = 1000.0; + var MUTE_MICROPHONE_MENU_ITEM = "Mute Microphone"; + var gTablet = null; Script.include("../libraries/WebTablet.js"); + function checkTablet() { + if (gTablet === null) { + gTablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + } + } + function tabletIsValid() { if (!UIWebTablet) { return false; @@ -49,6 +58,7 @@ } function getTabletScalePercentageFromSettings() { + checkTablet() var toolbarMode = gTablet.toolbarMode; var tabletScalePercentage = DEFAULT_TABLET_SCALE; if (!toolbarMode) { @@ -77,9 +87,7 @@ if (debugTablet) { print("TABLET rezzing"); } - if (gTablet === null) { - gTablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - } + checkTablet() tabletScalePercentage = getTabletScalePercentageFromSettings(); UIWebTablet = new WebTablet("qml/hifi/tablet/TabletRoot.qml", @@ -95,6 +103,7 @@ } function showTabletUI() { + checkTablet() gTablet.tabletShown = true; if (!tabletRezzed || !tabletIsValid()) { @@ -117,6 +126,7 @@ } function hideTabletUI() { + checkTablet() gTablet.tabletShown = false; if (!UIWebTablet) { return; @@ -133,6 +143,7 @@ } function closeTabletUI() { + checkTablet() gTablet.tabletShown = false; if (UIWebTablet) { if (UIWebTablet.onClose) { @@ -159,9 +170,7 @@ function updateShowTablet() { var now = Date.now(); - if (gTablet === null) { - gTablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - } + checkTablet() // close the WebTablet if it we go into toolbar mode. var tabletShown = gTablet.tabletShown; @@ -223,6 +232,23 @@ closeTabletUI(); rezTablet(); tabletShown = false; + + // also cause the stylus model to be loaded + var tmpStylusID = Overlays.addOverlay("model", { + name: "stylus", + url: Script.resourcesPath() + "meshes/tablet-stylus-fat.fbx", + loadPriority: 10.0, + position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, {x: 0, y: 0.1, z: -2})), + dimensions: { x: 0.01, y: 0.01, z: 0.2 }, + solid: true, + visible: true, + ignoreRayIntersection: true, + drawInFront: false, + lifetime: 3 + }); + Script.setTimeout(function() { + Overlays.deleteOverlay(tmpStylusID); + }, 300); } else if (!tabletShown) { hideTabletUI(); } @@ -237,6 +263,7 @@ } if (channel === "home") { if (UIWebTablet) { + checkTablet() gTablet.landscape = false; } } From b63015eaea681b8a97c594d486be8cd7e2e3f3db Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 6 May 2017 14:34:28 -0700 Subject: [PATCH 89/89] Fix sped up animations --- interface/src/avatar/MySkeletonModel.cpp | 4 +--- .../src/avatars-renderer/SkeletonModel.cpp | 6 +++--- .../avatars-renderer/src/avatars-renderer/SkeletonModel.h | 1 + libraries/render-utils/src/CauterizedModel.h | 8 ++++---- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 639c9f003f..1b9aa4dc18 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -140,9 +140,6 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { auto orientation = myAvatar->getLocalOrientation(); _rig->computeMotionAnimationState(deltaTime, position, velocity, orientation, ccState); - // evaluate AnimGraph animation and update jointStates. - Model::updateRig(deltaTime, parentTransform); - Rig::EyeParameters eyeParams; eyeParams.eyeLookAt = lookAt; eyeParams.eyeSaccade = head->getSaccade(); @@ -153,6 +150,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { _rig->updateFromEyeParameters(eyeParams); + // evaluate AnimGraph animation and update jointStates. Parent::updateRig(deltaTime, parentTransform); } diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index 8fcc859b62..e1e5dc4282 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -120,7 +120,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { } // evaluate AnimGraph animation and update jointStates. - Model::updateRig(deltaTime, parentTransform); + Parent::updateRig(deltaTime, parentTransform); } void SkeletonModel::updateAttitude() { @@ -136,7 +136,7 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { if (fullUpdate) { setBlendshapeCoefficients(_owningAvatar->getHead()->getSummedBlendshapeCoefficients()); - Model::simulate(deltaTime, fullUpdate); + Parent::simulate(deltaTime, fullUpdate); // let rig compute the model offset glm::vec3 registrationPoint; @@ -144,7 +144,7 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { setOffset(registrationPoint); } } else { - Model::simulate(deltaTime, fullUpdate); + Parent::simulate(deltaTime, fullUpdate); } if (!isActive() || !_owningAvatar->isMyAvatar()) { diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h index 23af04e964..059dd245fd 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h @@ -23,6 +23,7 @@ using SkeletonModelWeakPointer = std::weak_ptr; /// A skeleton loaded from a model. class SkeletonModel : public CauterizedModel { + using Parent = CauterizedModel; Q_OBJECT public: diff --git a/libraries/render-utils/src/CauterizedModel.h b/libraries/render-utils/src/CauterizedModel.h index 39bcedf639..dcff7bd12d 100644 --- a/libraries/render-utils/src/CauterizedModel.h +++ b/libraries/render-utils/src/CauterizedModel.h @@ -28,10 +28,10 @@ public: const std::unordered_set& getCauterizeBoneSet() const { return _cauterizeBoneSet; } void setCauterizeBoneSet(const std::unordered_set& boneSet) { _cauterizeBoneSet = boneSet; } - void deleteGeometry() override; - bool updateGeometry() override; + void deleteGeometry() override; + bool updateGeometry() override; - void createVisibleRenderItemSet() override; + void createVisibleRenderItemSet() override; void createCollisionRenderItemSet() override; virtual void updateClusterMatrices() override; @@ -41,7 +41,7 @@ public: protected: std::unordered_set _cauterizeBoneSet; - QVector _cauterizeMeshStates; + QVector _cauterizeMeshStates; bool _isCauterized { false }; bool _enableCauterization { false }; };