From 5e6c0870e7d20cfd43a1a78531bd5dd2676d9ee6 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 17 Oct 2017 12:36:15 -0700 Subject: [PATCH 01/19] handle camera-relative controller joints for other avatars --- libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 87d4a2d343..f674a2206e 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -994,10 +994,12 @@ glm::quat Avatar::getAbsoluteJointRotationInObjectFrame(int index) const { glm::mat4 finalMat = glm::inverse(avatarMatrix) * sensorToWorldMatrix; return glmExtractRotation(finalMat); } + case CAMERA_RELATIVE_CONTROLLER_LEFTHAND_INDEX: case CONTROLLER_LEFTHAND_INDEX: { Transform controllerLeftHandTransform = Transform(getControllerLeftHandMatrix()); return controllerLeftHandTransform.getRotation(); } + case CAMERA_RELATIVE_CONTROLLER_RIGHTHAND_INDEX: case CONTROLLER_RIGHTHAND_INDEX: { Transform controllerRightHandTransform = Transform(getControllerRightHandMatrix()); return controllerRightHandTransform.getRotation(); @@ -1032,10 +1034,12 @@ glm::vec3 Avatar::getAbsoluteJointTranslationInObjectFrame(int index) const { glm::mat4 finalMat = glm::inverse(avatarMatrix) * sensorToWorldMatrix; return extractTranslation(finalMat); } + case CAMERA_RELATIVE_CONTROLLER_LEFTHAND_INDEX: case CONTROLLER_LEFTHAND_INDEX: { Transform controllerLeftHandTransform = Transform(getControllerLeftHandMatrix()); return controllerLeftHandTransform.getTranslation(); } + case CAMERA_RELATIVE_CONTROLLER_RIGHTHAND_INDEX: case CONTROLLER_RIGHTHAND_INDEX: { Transform controllerRightHandTransform = Transform(getControllerRightHandMatrix()); return controllerRightHandTransform.getTranslation(); From f1abb0cd5510bf563f69670a869776377aae2405 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 17 Oct 2017 18:59:12 -0700 Subject: [PATCH 02/19] don't send avatar-entity updates for queryCube-changes, because avatar-entities don't use them --- interface/src/avatar/MyAvatar.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 5d82405aee..cccc13711d 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -594,7 +594,9 @@ void MyAvatar::simulate(float deltaTime) { if (success) { moveOperator.addEntityToMoveList(entity, newCube); } - if (packetSender) { + // send an edit packet to update the entity-server about the queryAABox. If it's an + // avatar-entity, don't. + if (packetSender && !entity->getClientOnly()) { EntityItemProperties properties = entity->getProperties(); properties.setQueryAACubeDirty(); properties.setLastEdited(now); From 949da1704606238df551e4c93543db069c1e0e6f Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 18 Oct 2017 13:51:58 -0700 Subject: [PATCH 03/19] Wear It button for items with category 'Wearables' --- interface/resources/qml/hifi/commerce/checkout/Checkout.qml | 4 +++- .../resources/qml/hifi/commerce/purchases/PurchasedItem.qml | 5 +++-- .../resources/qml/hifi/commerce/purchases/Purchases.qml | 1 + scripts/system/marketplaces/marketplaces.js | 1 + 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 8d94e284ed..b76deb41fc 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -40,6 +40,7 @@ Rectangle { property bool shouldBuyWithControlledFailure: false; property bool debugCheckoutSuccess: false; property bool canRezCertifiedItems: Entities.canRezCertified || Entities.canRezTmpCertified; + property bool isWearable; // Style color: hifi.colors.white; Hifi.QmlCommerce { @@ -573,7 +574,7 @@ Rectangle { height: 50; anchors.left: parent.left; anchors.right: parent.right; - text: "Rez It" + text: root.isWearable ? "Wear It" : "Rez It" onClicked: { if (urlHandler.canHandleUrl(root.itemHref)) { urlHandler.handleUrl(root.itemHref); @@ -832,6 +833,7 @@ Rectangle { itemName = message.params.itemName; root.itemPrice = message.params.itemPrice; itemHref = message.params.itemHref; + root.isWearable = message.params.categories.indexOf("Wearables") > -1; setBuyText(); break; default: diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index e7e16668fe..fb42865ba4 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -39,6 +39,7 @@ Item { property int itemEdition; property int numberSold; property int limitedRun; + property bool isWearable; property string originalStatusText; property string originalStatusColor; @@ -342,7 +343,7 @@ Item { anchors.bottom: parent.bottom; anchors.right: parent.right; width: height; - enabled: root.canRezCertifiedItems && root.purchaseStatus !== "invalidated"; + enabled: (root.canRezCertifiedItems || root.isWearable) && root.purchaseStatus !== "invalidated"; onClicked: { if (urlHandler.canHandleUrl(root.itemHref)) { @@ -415,7 +416,7 @@ Item { size: 16; verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter - text: "Rez It" + text: root.isWearable ? "Wear It" : "Rez It" } } } diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index b5697f687d..6b0dc961ea 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -434,6 +434,7 @@ Rectangle { numberSold: model.number_sold; limitedRun: model.limited_run; displayedItemCount: model.displayedItemCount; + isWearable: model.categories.indexOf("Wearables") > -1; anchors.topMargin: 12; anchors.bottomMargin: 12; diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 6880d10c18..22d9b6025b 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -74,6 +74,7 @@ itemName: 'Test Flaregun', itemPrice: (debugError ? 10 : 17), itemHref: 'http://mpassets.highfidelity.com/0d90d21c-ce7a-4990-ad18-e9d2cf991027-v1/flaregun.json', + categories: ["Wearables", "Miscellaneous"] }, canRezCertifiedItems: Entities.canRezCertified || Entities.canRezTmpCertified }); From de50eef9c77399d23f4307149df027f80a5b829b Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 17 Oct 2017 13:18:29 -0700 Subject: [PATCH 04/19] Fix rez certified... --- .../resources/qml/hifi/commerce/checkout/Checkout.qml | 2 +- .../resources/qml/hifi/commerce/purchases/Purchases.qml | 2 +- scripts/system/marketplaces/marketplaces.js | 7 ++----- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index b76deb41fc..d136caccfe 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -39,7 +39,7 @@ Rectangle { property bool itemIsJson: true; property bool shouldBuyWithControlledFailure: false; property bool debugCheckoutSuccess: false; - property bool canRezCertifiedItems: Entities.canRezCertified || Entities.canRezTmpCertified; + property bool canRezCertifiedItems: Entities.canRezCertified() || Entities.canRezTmpCertified(); property bool isWearable; // Style color: hifi.colors.white; diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index 6b0dc961ea..c98f212c53 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -32,7 +32,7 @@ Rectangle { property bool securityImageResultReceived: false; property bool purchasesReceived: false; property bool punctuationMode: false; - property bool canRezCertifiedItems: Entities.canRezCertified || Entities.canRezTmpCertified; + property bool canRezCertifiedItems: Entities.canRezCertified() || Entities.canRezTmpCertified(); property bool pendingInventoryReply: true; property bool isShowingMyItems: false; property bool isDebuggingFirstUseTutorial: false; diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 22d9b6025b..5ffc35e6e5 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -75,8 +75,7 @@ itemPrice: (debugError ? 10 : 17), itemHref: 'http://mpassets.highfidelity.com/0d90d21c-ce7a-4990-ad18-e9d2cf991027-v1/flaregun.json', categories: ["Wearables", "Miscellaneous"] - }, - canRezCertifiedItems: Entities.canRezCertified || Entities.canRezTmpCertified + } }); } } @@ -116,7 +115,6 @@ if (url === MARKETPLACE_PURCHASES_QML_PATH) { tablet.sendToQml({ method: 'updatePurchases', - canRezCertifiedItems: Entities.canRezCertified || Entities.canRezTmpCertified, referrerURL: referrerURL, filterText: filterText }); @@ -204,8 +202,7 @@ tablet.pushOntoStack(MARKETPLACE_CHECKOUT_QML_PATH); tablet.sendToQml({ method: 'updateCheckoutQML', - params: parsedJsonMessage, - canRezCertifiedItems: Entities.canRezCertified || Entities.canRezTmpCertified + params: parsedJsonMessage }); } else if (parsedJsonMessage.type === "REQUEST_SETTING") { sendCommerceSettings(); From 876001146a1fe0d7e15f4f07fbc9f7a3cd52af9f Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 18 Oct 2017 14:05:47 -0700 Subject: [PATCH 05/19] Get categories from backend instead of site --- interface/resources/qml/hifi/commerce/checkout/Checkout.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index d136caccfe..b997cc9671 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -81,6 +81,7 @@ Rectangle { root.activeView = "checkoutFailure"; } else { root.itemHref = result.data.download_url; + root.isWearable = result.data.categories.indexOf("Wearables") > -1; root.activeView = "checkoutSuccess"; } } @@ -833,7 +834,6 @@ Rectangle { itemName = message.params.itemName; root.itemPrice = message.params.itemPrice; itemHref = message.params.itemHref; - root.isWearable = message.params.categories.indexOf("Wearables") > -1; setBuyText(); break; default: From 322bac035ce08f7756ddf7eb12be013045a9ea86 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 18 Oct 2017 16:19:54 -0700 Subject: [PATCH 06/19] Don't show 'no permission' text on wearables --- interface/resources/qml/hifi/commerce/checkout/Checkout.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index b997cc9671..767164d38c 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -586,7 +586,7 @@ Rectangle { } RalewaySemiBold { id: noPermissionText; - visible: !root.canRezCertifiedItems; + visible: !root.canRezCertifiedItems && !root.isWearable; text: 'You do not have Certified Rez permissions in this domain.' // Text size size: 16; From f75e59c0a6f8146f207e0fba48a424e82463652c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 12 Oct 2017 11:33:42 -0700 Subject: [PATCH 07/19] keep grabbed and worn entities from spamming entity-server --- libraries/entities/src/EntityItem.cpp | 149 +++++++++++--------- libraries/entities/src/EntityItem.h | 2 + libraries/physics/src/EntityMotionState.cpp | 6 +- libraries/shared/src/SpatiallyNestable.cpp | 14 +- libraries/shared/src/SpatiallyNestable.h | 2 +- 5 files changed, 98 insertions(+), 75 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 58b8dd22bf..cdba87ff69 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -43,7 +43,7 @@ int EntityItem::_maxActionsDataSize = 800; quint64 EntityItem::_rememberDeletedActionTime = 20 * USECS_PER_SECOND; EntityItem::EntityItem(const EntityItemID& entityItemID) : - SpatiallyNestable(NestableType::Entity, entityItemID) + SpatiallyNestable(NestableType::Entity, entityItemID) { setLocalVelocity(ENTITY_ITEM_DEFAULT_VELOCITY); setLocalAngularVelocity(ENTITY_ITEM_DEFAULT_ANGULAR_VELOCITY); @@ -719,7 +719,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef } { // When we own the simulation we don't accept updates to the entity's transform/velocities // we also want to ignore any duplicate packets that have the same "recently updated" values - // as a packet we've already recieved. This is because we want multiple edits of the same + // as a packet we've already recieved. This is because we want multiple edits of the same // information to be idempotent, but if we applied new physics properties we'd resimulation // with small differences in results. @@ -727,7 +727,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef // made these lambdas that can access other details about the previous updates to suppress // any duplicates. - // Note: duplicate packets are expected and not wrong. They may be sent for any number of + // Note: duplicate packets are expected and not wrong. They may be sent for any number of // reasons and the contract is that the client handles them in an idempotent manner. auto lastEdited = lastEditedFromBufferAdjusted; bool otherOverwrites = overwriteLocalData && !weOwnSimulation; @@ -1659,7 +1659,7 @@ bool EntityItem::verifyStaticCertificateProperties() { const auto hash = getStaticCertificateHash(); const auto text = reinterpret_cast(hash.constData()); const unsigned int textLength = hash.length(); - + // After DEBUG_CERT ends, we will get/cache this once from the marketplace when needed, and it likely won't be RSA. const char publicKey[] = "-----BEGIN PUBLIC KEY-----\n\ MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALCoBiDAZOClO26tC5pd7JikBL61WIgp\n\ @@ -2016,9 +2016,7 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask // if this entity is a descendant of MyAvatar, don't collide with MyAvatar. This avoids the // "bootstrapping" problem where you can shoot yourself across the room by grabbing something // and holding it against your own avatar. - QUuid ancestorID = findAncestorOfType(NestableType::Avatar); - if (!ancestorID.isNull() && - (ancestorID == Physics::getSessionUUID() || ancestorID == AVATAR_SELF_ID)) { + if (isChildOfMyAvatar()) { iAmHoldingThis = true; } // also, don't bootstrap our own avatar with a hold action @@ -2425,6 +2423,7 @@ QVariantMap EntityItem::getActionArguments(const QUuid& actionID) const { } bool EntityItem::shouldSuppressLocationEdits() const { + // if any of the actions indicate they'd like suppression, suppress QHash::const_iterator i = _objectActions.begin(); while (i != _objectActions.end()) { if (i.value()->shouldSuppressLocationEdits()) { @@ -2433,6 +2432,11 @@ bool EntityItem::shouldSuppressLocationEdits() const { i++; } + // if any of the ancestors are MyAvatar, suppress + if (isChildOfMyAvatar()) { + return true; + } + return false; } @@ -2495,16 +2499,16 @@ void EntityItem::globalizeProperties(EntityItemProperties& properties, const QSt bool EntityItem::matchesJSONFilters(const QJsonObject& jsonFilters) const { - + // The intention for the query JSON filter and this method is to be flexible to handle a variety of filters for // ALL entity properties. Some work will need to be done to the property system so that it can be more flexible // (to grab the value and default value of a property given the string representation of that property, for example) - + // currently the only property filter we handle is '+' for serverScripts // which means that we only handle a filtered query asking for entities where the serverScripts property is non-default - + static const QString SERVER_SCRIPTS_PROPERTY = "serverScripts"; - + foreach(const auto& property, jsonFilters.keys()) { if (property == SERVER_SCRIPTS_PROPERTY && jsonFilters[property] == EntityQueryFilterSymbol::NonDefault) { // check if this entity has a non-default value for serverScripts @@ -2515,7 +2519,7 @@ bool EntityItem::matchesJSONFilters(const QJsonObject& jsonFilters) const { } } } - + // the json filter syntax did not match what we expected, return a match return true; } @@ -2528,7 +2532,7 @@ quint64 EntityItem::getLastSimulated() const { return result; } -void EntityItem::setLastSimulated(quint64 now) { +void EntityItem::setLastSimulated(quint64 now) { withWriteLock([&] { _lastSimulated = now; }); @@ -2549,7 +2553,7 @@ void EntityItem::setLastEdited(quint64 lastEdited) { }); } -quint64 EntityItem::getLastBroadcast() const { +quint64 EntityItem::getLastBroadcast() const { quint64 result; withReadLock([&] { result = _lastBroadcast; @@ -2557,19 +2561,19 @@ quint64 EntityItem::getLastBroadcast() const { return result; } -void EntityItem::setLastBroadcast(quint64 lastBroadcast) { +void EntityItem::setLastBroadcast(quint64 lastBroadcast) { withWriteLock([&] { _lastBroadcast = lastBroadcast; }); } -void EntityItem::markAsChangedOnServer() { +void EntityItem::markAsChangedOnServer() { withWriteLock([&] { _changedOnServer = usecTimestampNow(); }); } -quint64 EntityItem::getLastChangedOnServer() const { +quint64 EntityItem::getLastChangedOnServer() const { quint64 result; withReadLock([&] { result = _changedOnServer; @@ -2577,13 +2581,13 @@ quint64 EntityItem::getLastChangedOnServer() const { return result; } -void EntityItem::update(const quint64& now) { +void EntityItem::update(const quint64& now) { withWriteLock([&] { - _lastUpdated = now; + _lastUpdated = now; }); } -quint64 EntityItem::getLastUpdated() const { +quint64 EntityItem::getLastUpdated() const { quint64 result; withReadLock([&] { result = _lastUpdated; @@ -2591,10 +2595,10 @@ quint64 EntityItem::getLastUpdated() const { return result; } -void EntityItem::requiresRecalcBoxes() { +void EntityItem::requiresRecalcBoxes() { withWriteLock([&] { - _recalcAABox = true; - _recalcMinAACube = true; + _recalcAABox = true; + _recalcMinAACube = true; _recalcMaxAACube = true; }); } @@ -2607,7 +2611,7 @@ QString EntityItem::getHref() const { return result; } -QString EntityItem::getDescription() const { +QString EntityItem::getDescription() const { QString result; withReadLock([&] { result = _description; @@ -2629,54 +2633,54 @@ float EntityItem::getLocalRenderAlpha() const { return result; } -void EntityItem::setLocalRenderAlpha(float localRenderAlpha) { +void EntityItem::setLocalRenderAlpha(float localRenderAlpha) { withWriteLock([&] { _localRenderAlpha = localRenderAlpha; }); } -glm::vec3 EntityItem::getGravity() const { +glm::vec3 EntityItem::getGravity() const { glm::vec3 result; withReadLock([&] { result = _gravity; }); return result; -} +} -void EntityItem::setGravity(const glm::vec3& value) { +void EntityItem::setGravity(const glm::vec3& value) { withWriteLock([&] { _gravity = value; }); } -glm::vec3 EntityItem::getAcceleration() const { +glm::vec3 EntityItem::getAcceleration() const { glm::vec3 result; withReadLock([&] { result = _acceleration; }); return result; -} +} -void EntityItem::setAcceleration(const glm::vec3& value) { +void EntityItem::setAcceleration(const glm::vec3& value) { withWriteLock([&] { _acceleration = value; }); } -float EntityItem::getDamping() const { +float EntityItem::getDamping() const { float result; withReadLock([&] { result = _damping; }); return result; } -void EntityItem::setDamping(float value) { +void EntityItem::setDamping(float value) { withWriteLock([&] { _damping = value; }); } -float EntityItem::getRestitution() const { +float EntityItem::getRestitution() const { float result; withReadLock([&] { result = _restitution; @@ -2684,7 +2688,7 @@ float EntityItem::getRestitution() const { return result; } -float EntityItem::getFriction() const { +float EntityItem::getFriction() const { float result; withReadLock([&] { result = _friction; @@ -2693,35 +2697,35 @@ float EntityItem::getFriction() const { } // lifetime related properties. -float EntityItem::getLifetime() const { +float EntityItem::getLifetime() const { float result; withReadLock([&] { result = _lifetime; }); return result; -} +} -void EntityItem::setLifetime(float value) { +void EntityItem::setLifetime(float value) { withWriteLock([&] { _lifetime = value; }); } -quint64 EntityItem::getCreated() const { +quint64 EntityItem::getCreated() const { quint64 result; withReadLock([&] { result = _created; }); return result; -} +} -void EntityItem::setCreated(quint64 value) { +void EntityItem::setCreated(quint64 value) { withWriteLock([&] { _created = value; }); } -QString EntityItem::getScript() const { +QString EntityItem::getScript() const { QString result; withReadLock([&] { result = _script; @@ -2729,13 +2733,13 @@ QString EntityItem::getScript() const { return result; } -void EntityItem::setScript(const QString& value) { +void EntityItem::setScript(const QString& value) { withWriteLock([&] { _script = value; }); } -quint64 EntityItem::getScriptTimestamp() const { +quint64 EntityItem::getScriptTimestamp() const { quint64 result; withReadLock([&] { result = _scriptTimestamp; @@ -2743,13 +2747,13 @@ quint64 EntityItem::getScriptTimestamp() const { return result; } -void EntityItem::setScriptTimestamp(const quint64 value) { +void EntityItem::setScriptTimestamp(const quint64 value) { withWriteLock([&] { _scriptTimestamp = value; }); } -QString EntityItem::getServerScripts() const { +QString EntityItem::getServerScripts() const { QString result; withReadLock([&] { result = _serverScripts; @@ -2759,12 +2763,12 @@ QString EntityItem::getServerScripts() const { void EntityItem::setServerScripts(const QString& serverScripts) { withWriteLock([&] { - _serverScripts = serverScripts; + _serverScripts = serverScripts; _serverScriptsChangedTimestamp = usecTimestampNow(); }); } -QString EntityItem::getCollisionSoundURL() const { +QString EntityItem::getCollisionSoundURL() const { QString result; withReadLock([&] { result = _collisionSoundURL; @@ -2772,22 +2776,22 @@ QString EntityItem::getCollisionSoundURL() const { return result; } -glm::vec3 EntityItem::getRegistrationPoint() const { +glm::vec3 EntityItem::getRegistrationPoint() const { glm::vec3 result; withReadLock([&] { result = _registrationPoint; }); return result; -} +} void EntityItem::setRegistrationPoint(const glm::vec3& value) { withWriteLock([&] { - _registrationPoint = glm::clamp(value, 0.0f, 1.0f); + _registrationPoint = glm::clamp(value, 0.0f, 1.0f); }); dimensionsChanged(); // Registration Point affects the bounding box } -float EntityItem::getAngularDamping() const { +float EntityItem::getAngularDamping() const { float result; withReadLock([&] { result = _angularDamping; @@ -2795,13 +2799,13 @@ float EntityItem::getAngularDamping() const { return result; } -void EntityItem::setAngularDamping(float value) { +void EntityItem::setAngularDamping(float value) { withWriteLock([&] { _angularDamping = value; }); } -QString EntityItem::getName() const { +QString EntityItem::getName() const { QString result; withReadLock([&] { result = _name; @@ -2809,13 +2813,13 @@ QString EntityItem::getName() const { return result; } -void EntityItem::setName(const QString& value) { +void EntityItem::setName(const QString& value) { withWriteLock([&] { _name = value; }); } -QString EntityItem::getDebugName() { +QString EntityItem::getDebugName() { QString result = getName(); if (result.isEmpty()) { result = getID().toString(); @@ -2823,7 +2827,7 @@ QString EntityItem::getDebugName() { return result; } -bool EntityItem::getVisible() const { +bool EntityItem::getVisible() const { bool result; withReadLock([&] { result = _visible; @@ -2831,13 +2835,18 @@ bool EntityItem::getVisible() const { return result; } -void EntityItem::setVisible(bool value) { +void EntityItem::setVisible(bool value) { withWriteLock([&] { _visible = value; }); } -bool EntityItem::getCollisionless() const { +bool EntityItem::isChildOfMyAvatar() const { + QUuid ancestorID = findAncestorOfType(NestableType::Avatar); + return !ancestorID.isNull() && (ancestorID == Physics::getSessionUUID() || ancestorID == AVATAR_SELF_ID); +} + +bool EntityItem::getCollisionless() const { bool result; withReadLock([&] { result = _collisionless; @@ -2845,13 +2854,13 @@ bool EntityItem::getCollisionless() const { return result; } -void EntityItem::setCollisionless(bool value) { +void EntityItem::setCollisionless(bool value) { withWriteLock([&] { _collisionless = value; }); } -uint8_t EntityItem::getCollisionMask() const { +uint8_t EntityItem::getCollisionMask() const { uint8_t result; withReadLock([&] { result = _collisionMask; @@ -2859,13 +2868,13 @@ uint8_t EntityItem::getCollisionMask() const { return result; } -void EntityItem::setCollisionMask(uint8_t value) { +void EntityItem::setCollisionMask(uint8_t value) { withWriteLock([&] { _collisionMask = value; }); } -bool EntityItem::getDynamic() const { +bool EntityItem::getDynamic() const { if (SHAPE_TYPE_STATIC_MESH == getShapeType()) { return false; } @@ -2876,13 +2885,13 @@ bool EntityItem::getDynamic() const { return result; } -void EntityItem::setDynamic(bool value) { +void EntityItem::setDynamic(bool value) { withWriteLock([&] { _dynamic = value; }); } -bool EntityItem::getLocked() const { +bool EntityItem::getLocked() const { bool result; withReadLock([&] { result = _locked; @@ -2890,7 +2899,7 @@ bool EntityItem::getLocked() const { return result; } -void EntityItem::setLocked(bool value) { +void EntityItem::setLocked(bool value) { withWriteLock([&] { _locked = value; }); @@ -2913,7 +2922,7 @@ void EntityItem::updateLocked(bool value) { } } -QString EntityItem::getUserData() const { +QString EntityItem::getUserData() const { QString result; withReadLock([&] { result = _userData; @@ -2921,7 +2930,7 @@ QString EntityItem::getUserData() const { return result; } -void EntityItem::setUserData(const QString& value) { +void EntityItem::setUserData(const QString& value) { withWriteLock([&] { _userData = value; }); @@ -2955,7 +2964,7 @@ DEFINE_PROPERTY_ACCESSOR(quint32, EditionNumber, editionNumber) DEFINE_PROPERTY_ACCESSOR(quint32, EntityInstanceNumber, entityInstanceNumber) DEFINE_PROPERTY_ACCESSOR(QString, CertificateID, certificateID) -uint32_t EntityItem::getDirtyFlags() const { +uint32_t EntityItem::getDirtyFlags() const { uint32_t result; withReadLock([&] { result = _dirtyFlags; @@ -2970,7 +2979,7 @@ void EntityItem::markDirtyFlags(uint32_t mask) { }); } -void EntityItem::clearDirtyFlags(uint32_t mask) { +void EntityItem::clearDirtyFlags(uint32_t mask) { withWriteLock([&] { _dirtyFlags &= ~mask; }); diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index c26f1694a9..d1f34217fa 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -273,6 +273,8 @@ public: inline bool isVisible() const { return getVisible(); } inline bool isInvisible() const { return !getVisible(); } + bool isChildOfMyAvatar() const; + bool getCollisionless() const; void setCollisionless(bool value); diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 7c84017758..df46f7ada7 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -491,6 +491,10 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) { return true; } + if (_entity->shouldSuppressLocationEdits()) { + return false; + } + if (!isLocallyOwned()) { // we don't own the simulation @@ -577,7 +581,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ } if (properties.transformChanged()) { - if (_entity->checkAndMaybeUpdateQueryAACube()) { + if (_entity->checkAndMaybeUpdateQueryAACube(true)) { // due to parenting, the server may not know where something is in world-space, so include the bounding cube. properties.setQueryAACube(_entity->getQueryAACube()); } diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 8c43632456..e76e3dfe27 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -963,19 +963,21 @@ AACube SpatiallyNestable::getMaximumAACube(bool& success) const { const float PARENTED_EXPANSION_FACTOR = 3.0f; -bool SpatiallyNestable::checkAndMaybeUpdateQueryAACube() { +bool SpatiallyNestable::checkAndMaybeUpdateQueryAACube(bool forcePuffed) { + bool updated = false; bool success = false; AACube maxAACube = getMaximumAACube(success); if (success) { // maybe update _queryAACube if (!_queryAACubeSet || (_parentID.isNull() && _children.size() == 0) || !_queryAACube.contains(maxAACube)) { - if (_parentJointIndex != INVALID_JOINT_INDEX || _children.size() > 0 ) { + if (forcePuffed || _parentJointIndex != INVALID_JOINT_INDEX || _children.size() > 0 ) { // make an expanded AACube centered on the object float scale = PARENTED_EXPANSION_FACTOR * maxAACube.getScale(); _queryAACube = AACube(maxAACube.calcCenter() - glm::vec3(0.5f * scale), scale); } else { _queryAACube = maxAACube; } + updated = true; forEachDescendant([&](const SpatiallyNestablePointer& descendant) { bool childSuccess; @@ -991,7 +993,7 @@ bool SpatiallyNestable::checkAndMaybeUpdateQueryAACube() { _queryAACubeSet = true; } } - return success; + return updated; } void SpatiallyNestable::setQueryAACube(const AACube& queryAACube) { @@ -1008,6 +1010,12 @@ bool SpatiallyNestable::queryAACubeNeedsUpdate() const { return true; } + bool success; + AACube maxAACube = getMaximumAACube(success); + if (success && !_queryAACube.contains(maxAACube)) { + return true; + } + // make sure children are still in their boxes, also. bool childNeedsUpdate = false; forEachDescendantTest([&](const SpatiallyNestablePointer& descendant) { diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index b6be4dc056..9be6dc14ef 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -106,7 +106,7 @@ public: virtual glm::vec3 getParentAngularVelocity(bool& success) const; virtual AACube getMaximumAACube(bool& success) const; - bool checkAndMaybeUpdateQueryAACube(); + bool checkAndMaybeUpdateQueryAACube(bool forcePuffed = false); void updateQueryAACube(); virtual void setQueryAACube(const AACube& queryAACube); From ae70f091c3b681c215a7dfb36e9d414123badb76 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 12 Oct 2017 14:04:50 -0700 Subject: [PATCH 08/19] don't accept updates to queryAACube if this interface is simulation owner --- libraries/entities/src/EntityItem.cpp | 36 ++++++++++++++++++++------- libraries/entities/src/EntityItem.h | 3 +++ 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index cdba87ff69..9e48876cfb 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -717,6 +717,14 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef weOwnSimulation = _simulationOwner.matchesValidID(myNodeID); } } + + auto lastEdited = lastEditedFromBufferAdjusted; + bool otherOverwrites = overwriteLocalData && !weOwnSimulation; + auto shouldUpdate = [lastEdited, otherOverwrites, filterRejection](quint64 updatedTimestamp, bool valueChanged) { + bool simulationChanged = lastEdited > updatedTimestamp; + return otherOverwrites && simulationChanged && (valueChanged || filterRejection); + }; + { // When we own the simulation we don't accept updates to the entity's transform/velocities // we also want to ignore any duplicate packets that have the same "recently updated" values // as a packet we've already recieved. This is because we want multiple edits of the same @@ -729,12 +737,6 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef // Note: duplicate packets are expected and not wrong. They may be sent for any number of // reasons and the contract is that the client handles them in an idempotent manner. - auto lastEdited = lastEditedFromBufferAdjusted; - bool otherOverwrites = overwriteLocalData && !weOwnSimulation; - auto shouldUpdate = [lastEdited, otherOverwrites, filterRejection](quint64 updatedTimestamp, bool valueChanged) { - bool simulationChanged = lastEdited > updatedTimestamp; - return otherOverwrites && simulationChanged && (valueChanged || filterRejection); - }; auto customUpdatePositionFromNetwork = [this, shouldUpdate, lastEdited](glm::vec3 value){ if (shouldUpdate(_lastUpdatedPositionTimestamp, value != _lastUpdatedPositionValue)) { updatePositionFromNetwork(value); @@ -780,8 +782,6 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, customUpdateVelocityFromNetwork); READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, customUpdateAngularVelocityFromNetwork); READ_ENTITY_PROPERTY(PROP_ACCELERATION, glm::vec3, customSetAcceleration); - - } READ_ENTITY_PROPERTY(PROP_DIMENSIONS, glm::vec3, updateDimensions); @@ -846,7 +846,18 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef overwriteLocalData = oldOverwrite; } - READ_ENTITY_PROPERTY(PROP_QUERY_AA_CUBE, AACube, setQueryAACube); + + { + auto customUpdateQueryAACubeFromNetwork = [this, shouldUpdate, lastEdited](AACube value){ + if (shouldUpdate(_lastUpdatedQueryAACubeTimestamp, value != _lastUpdatedQueryAACubeValue)) { + updateQueryAACubeFromNetwork(value); + _lastUpdatedQueryAACubeTimestamp = lastEdited; + _lastUpdatedQueryAACubeValue = value; + } + }; + READ_ENTITY_PROPERTY(PROP_QUERY_AA_CUBE, AACube, customUpdateQueryAACubeFromNetwork); + } + READ_ENTITY_PROPERTY(PROP_LAST_EDITED_BY, QUuid, setLastEditedBy); bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, @@ -1846,6 +1857,13 @@ void EntityItem::updateVelocityFromNetwork(const glm::vec3& value) { updateVelocity(value); } +void EntityItem::updateQueryAACubeFromNetwork(const AACube& value) { + if (shouldSuppressLocationEdits()) { + return; + } + setQueryAACube(value); +} + void EntityItem::updateDamping(float value) { auto clampedDamping = glm::clamp(value, 0.0f, 1.0f); if (_damping != clampedDamping) { diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index d1f34217fa..01a4f67b2c 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -364,6 +364,7 @@ public: void updateMass(float value); void updateVelocity(const glm::vec3& value); void updateVelocityFromNetwork(const glm::vec3& value); + void updateQueryAACubeFromNetwork(const AACube& value); void updateDamping(float value); void updateRestitution(float value); void updateFriction(float value); @@ -631,12 +632,14 @@ protected: glm::vec3 _lastUpdatedVelocityValue; glm::vec3 _lastUpdatedAngularVelocityValue; glm::vec3 _lastUpdatedAccelerationValue; + AACube _lastUpdatedQueryAACubeValue; quint64 _lastUpdatedPositionTimestamp { 0 }; quint64 _lastUpdatedRotationTimestamp { 0 }; quint64 _lastUpdatedVelocityTimestamp { 0 }; quint64 _lastUpdatedAngularVelocityTimestamp { 0 }; quint64 _lastUpdatedAccelerationTimestamp { 0 }; + quint64 _lastUpdatedQueryAACubeTimestamp { 0 }; }; From cf960dc44b925a4370fac17ee98b101d11fc1112 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 13 Oct 2017 09:29:50 -0700 Subject: [PATCH 09/19] not a parent or child doesn't mean always recompute queryAACube --- libraries/shared/src/SpatiallyNestable.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index e76e3dfe27..58456f7e9a 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -969,7 +969,7 @@ bool SpatiallyNestable::checkAndMaybeUpdateQueryAACube(bool forcePuffed) { AACube maxAACube = getMaximumAACube(success); if (success) { // maybe update _queryAACube - if (!_queryAACubeSet || (_parentID.isNull() && _children.size() == 0) || !_queryAACube.contains(maxAACube)) { + if (!_queryAACubeSet || !_queryAACube.contains(maxAACube)) { if (forcePuffed || _parentJointIndex != INVALID_JOINT_INDEX || _children.size() > 0 ) { // make an expanded AACube centered on the object float scale = PARENTED_EXPANSION_FACTOR * maxAACube.getScale(); From f2cb5d4aff1c36847f483c8ade1ce65974d1749c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 13 Oct 2017 11:26:58 -0700 Subject: [PATCH 10/19] clean-up concept of puffed queryAACube --- interface/src/avatar/MyAvatar.cpp | 2 +- .../RenderableParticleEffectEntityItem.cpp | 4 +- libraries/entities/src/EntityItem.cpp | 6 +- libraries/entities/src/EntityItem.h | 1 + .../entities/src/EntityScriptingInterface.cpp | 4 +- libraries/entities/src/EntityTree.cpp | 2 +- .../entities/src/SimpleEntitySimulation.cpp | 2 +- libraries/physics/src/EntityMotionState.cpp | 4 +- libraries/shared/src/SpatiallyNestable.cpp | 85 +++++++------------ libraries/shared/src/SpatiallyNestable.h | 5 +- 10 files changed, 50 insertions(+), 65 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index cccc13711d..3189ad3c77 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -587,7 +587,7 @@ void MyAvatar::simulate(float deltaTime) { MovingEntitiesOperator moveOperator; forEachDescendant([&](SpatiallyNestablePointer object) { // if the queryBox has changed, tell the entity-server - if (object->getNestableType() == NestableType::Entity && object->checkAndMaybeUpdateQueryAACube()) { + if (object->getNestableType() == NestableType::Entity && object->updateQueryAACube()) { EntityItemPointer entity = std::static_pointer_cast(object); bool success; AACube newCube = entity->getQueryAACube(success); diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 3328076911..047217b6aa 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -69,8 +69,8 @@ ParticleEffectEntityRenderer::ParticleEffectEntityRenderer(const EntityItemPoint } bool ParticleEffectEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const { - entity->checkAndMaybeUpdateQueryAACube(); - + entity->updateQueryAACube(); + if (_emitting != entity->getIsEmitting()) { return true; } diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 9e48876cfb..421efac687 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1392,8 +1392,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(lastEditedBy, setLastEditedBy); - AACube saveQueryAACube = _queryAACube; - if (checkAndMaybeUpdateQueryAACube() && saveQueryAACube != _queryAACube) { + if (updateQueryAACube()) { somethingChanged = true; } @@ -1557,6 +1556,9 @@ AACube EntityItem::getQueryAACube(bool& success) const { return result; } +bool EntityItem::shouldPuffQueryAACube() const { + return hasActions() || isChildOfMyAvatar(); +} // NOTE: This should only be used in cases of old bitstreams which only contain radius data // 0,0,0 --> maxDimension,maxDimension,maxDimension diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 01a4f67b2c..764ff34f99 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -240,6 +240,7 @@ public: using SpatiallyNestable::getQueryAACube; virtual AACube getQueryAACube(bool& success) const override; + virtual bool shouldPuffQueryAACube() const override; QString getScript() const; void setScript(const QString& value); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index f5117dddc0..66bb24825f 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -457,7 +457,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& // if they've changed. entity->forEachDescendant([&](SpatiallyNestablePointer descendant) { if (descendant->getNestableType() == NestableType::Entity) { - if (descendant->checkAndMaybeUpdateQueryAACube()) { + if (descendant->updateQueryAACube()) { EntityItemPointer entityDescendant = std::static_pointer_cast(descendant); EntityItemProperties newQueryCubeProperties; newQueryCubeProperties.setQueryAACube(descendant->getQueryAACube()); @@ -1792,4 +1792,4 @@ QString EntityScriptingInterface::computeCertificateID(const QUuid& entityID) { } return result; } -#endif \ No newline at end of file +#endif diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index d16aeaa6e1..e4de39a799 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1804,7 +1804,7 @@ QVector EntityTree::sendEntities(EntityEditPacketSender* packetSen addToNeedsParentFixupList(entity); } entity->forceQueryAACubeUpdate(); - entity->checkAndMaybeUpdateQueryAACube(); + entity->updateQueryAACube(); moveOperator.addEntityToMoveList(entity, entity->getQueryAACube()); i++; } else { diff --git a/libraries/entities/src/SimpleEntitySimulation.cpp b/libraries/entities/src/SimpleEntitySimulation.cpp index d41b22137e..3203c2968c 100644 --- a/libraries/entities/src/SimpleEntitySimulation.cpp +++ b/libraries/entities/src/SimpleEntitySimulation.cpp @@ -136,7 +136,7 @@ void SimpleEntitySimulation::sortEntitiesThatMoved() { SetOfEntities::iterator itemItr = _entitiesToSort.begin(); while (itemItr != _entitiesToSort.end()) { EntityItemPointer entity = *itemItr; - entity->checkAndMaybeUpdateQueryAACube(); + entity->updateQueryAACube(); ++itemItr; } EntitySimulation::sortEntitiesThatMoved(); diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index df46f7ada7..6884482074 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -581,7 +581,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ } if (properties.transformChanged()) { - if (_entity->checkAndMaybeUpdateQueryAACube(true)) { + if (_entity->updateQueryAACube()) { // due to parenting, the server may not know where something is in world-space, so include the bounding cube. properties.setQueryAACube(_entity->getQueryAACube()); } @@ -648,7 +648,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ _entity->forEachDescendant([&](SpatiallyNestablePointer descendant) { if (descendant->getNestableType() == NestableType::Entity) { EntityItemPointer entityDescendant = std::static_pointer_cast(descendant); - if (descendant->checkAndMaybeUpdateQueryAACube()) { + if (descendant->updateQueryAACube()) { EntityItemProperties newQueryCubeProperties; newQueryCubeProperties.setQueryAACube(descendant->getQueryAACube()); newQueryCubeProperties.setLastEdited(properties.getLastEdited()); diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 58456f7e9a..baa289095d 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -963,37 +963,39 @@ AACube SpatiallyNestable::getMaximumAACube(bool& success) const { const float PARENTED_EXPANSION_FACTOR = 3.0f; -bool SpatiallyNestable::checkAndMaybeUpdateQueryAACube(bool forcePuffed) { - bool updated = false; - bool success = false; +bool SpatiallyNestable::updateQueryAACube() { + if (!queryAACubeNeedsUpdate()) { + return false; + } + + bool success; AACube maxAACube = getMaximumAACube(success); if (success) { - // maybe update _queryAACube - if (!_queryAACubeSet || !_queryAACube.contains(maxAACube)) { - if (forcePuffed || _parentJointIndex != INVALID_JOINT_INDEX || _children.size() > 0 ) { - // make an expanded AACube centered on the object - float scale = PARENTED_EXPANSION_FACTOR * maxAACube.getScale(); - _queryAACube = AACube(maxAACube.calcCenter() - glm::vec3(0.5f * scale), scale); - } else { - _queryAACube = maxAACube; - } - updated = true; - - forEachDescendant([&](const SpatiallyNestablePointer& descendant) { - bool childSuccess; - AACube descendantAACube = descendant->getQueryAACube(childSuccess); - if (childSuccess) { - if (_queryAACube.contains(descendantAACube)) { - return ; - } - _queryAACube += descendantAACube.getMinimumPoint(); - _queryAACube += descendantAACube.getMaximumPoint(); - } - }); - _queryAACubeSet = true; + if (shouldPuffQueryAACube()) { + // make an expanded AACube centered on the object + float scale = PARENTED_EXPANSION_FACTOR * maxAACube.getScale(); + _queryAACube = AACube(maxAACube.calcCenter() - glm::vec3(0.5f * scale), scale); + _queryAACubeIsPuffed = true; + } else { + _queryAACube = maxAACube; + _queryAACubeIsPuffed = false; } + + forEachDescendant([&](const SpatiallyNestablePointer& descendant) { + bool childSuccess; + AACube descendantAACube = descendant->getQueryAACube(childSuccess); + if (childSuccess) { + if (_queryAACube.contains(descendantAACube)) { + return; // from lambda + } + _queryAACube += descendantAACube.getMinimumPoint(); + _queryAACube += descendantAACube.getMaximumPoint(); + } + }); + + _queryAACubeSet = true; } - return updated; + return true; } void SpatiallyNestable::setQueryAACube(const AACube& queryAACube) { @@ -1016,6 +1018,10 @@ bool SpatiallyNestable::queryAACubeNeedsUpdate() const { return true; } + if (shouldPuffQueryAACube() != _queryAACubeIsPuffed) { + return true; + } + // make sure children are still in their boxes, also. bool childNeedsUpdate = false; forEachDescendantTest([&](const SpatiallyNestablePointer& descendant) { @@ -1029,31 +1035,6 @@ bool SpatiallyNestable::queryAACubeNeedsUpdate() const { return childNeedsUpdate; } -void SpatiallyNestable::updateQueryAACube() { - bool success; - AACube maxAACube = getMaximumAACube(success); - if (_parentJointIndex != INVALID_JOINT_INDEX || _children.size() > 0 ) { - // make an expanded AACube centered on the object - float scale = PARENTED_EXPANSION_FACTOR * maxAACube.getScale(); - _queryAACube = AACube(maxAACube.calcCenter() - glm::vec3(0.5f * scale), scale); - } else { - _queryAACube = maxAACube; - } - - forEachDescendant([&](const SpatiallyNestablePointer& descendant) { - bool success; - AACube descendantAACube = descendant->getQueryAACube(success); - if (success) { - if (_queryAACube.contains(descendantAACube)) { - return; - } - _queryAACube += descendantAACube.getMinimumPoint(); - _queryAACube += descendantAACube.getMaximumPoint(); - } - }); - _queryAACubeSet = true; -} - AACube SpatiallyNestable::getQueryAACube(bool& success) const { if (_queryAACubeSet) { success = true; diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index 9be6dc14ef..37f6cfdfd9 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -106,11 +106,11 @@ public: virtual glm::vec3 getParentAngularVelocity(bool& success) const; virtual AACube getMaximumAACube(bool& success) const; - bool checkAndMaybeUpdateQueryAACube(bool forcePuffed = false); - void updateQueryAACube(); virtual void setQueryAACube(const AACube& queryAACube); virtual bool queryAACubeNeedsUpdate() const; + virtual bool shouldPuffQueryAACube() const { return false; } + bool updateQueryAACube(); void forceQueryAACubeUpdate() { _queryAACubeSet = false; } virtual AACube getQueryAACube(bool& success) const; virtual AACube getQueryAACube() const; @@ -234,6 +234,7 @@ private: glm::vec3 _angularVelocity; mutable bool _parentKnowsMe { false }; bool _isDead { false }; + bool _queryAACubeIsPuffed { false }; }; From 633f63df54041b6f53e2a0c47fa7fcdcd2ad7189 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 13 Oct 2017 13:11:18 -0700 Subject: [PATCH 11/19] fix updateQueryAACube return-value --- libraries/shared/src/SpatiallyNestable.cpp | 50 +++++++++++----------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index baa289095d..8dbd2dd5e0 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -970,31 +970,33 @@ bool SpatiallyNestable::updateQueryAACube() { bool success; AACube maxAACube = getMaximumAACube(success); - if (success) { - if (shouldPuffQueryAACube()) { - // make an expanded AACube centered on the object - float scale = PARENTED_EXPANSION_FACTOR * maxAACube.getScale(); - _queryAACube = AACube(maxAACube.calcCenter() - glm::vec3(0.5f * scale), scale); - _queryAACubeIsPuffed = true; - } else { - _queryAACube = maxAACube; - _queryAACubeIsPuffed = false; - } - - forEachDescendant([&](const SpatiallyNestablePointer& descendant) { - bool childSuccess; - AACube descendantAACube = descendant->getQueryAACube(childSuccess); - if (childSuccess) { - if (_queryAACube.contains(descendantAACube)) { - return; // from lambda - } - _queryAACube += descendantAACube.getMinimumPoint(); - _queryAACube += descendantAACube.getMaximumPoint(); - } - }); - - _queryAACubeSet = true; + if (!success) { + return false; } + + if (shouldPuffQueryAACube()) { + // make an expanded AACube centered on the object + float scale = PARENTED_EXPANSION_FACTOR * maxAACube.getScale(); + _queryAACube = AACube(maxAACube.calcCenter() - glm::vec3(0.5f * scale), scale); + _queryAACubeIsPuffed = true; + } else { + _queryAACube = maxAACube; + _queryAACubeIsPuffed = false; + } + + forEachDescendant([&](const SpatiallyNestablePointer& descendant) { + bool childSuccess; + AACube descendantAACube = descendant->getQueryAACube(childSuccess); + if (childSuccess) { + if (_queryAACube.contains(descendantAACube)) { + return; // from lambda + } + _queryAACube += descendantAACube.getMinimumPoint(); + _queryAACube += descendantAACube.getMaximumPoint(); + } + }); + + _queryAACubeSet = true; return true; } From c1e8d5144c14dce5912bce3e0f7ee21d04047316 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 13 Oct 2017 14:53:36 -0700 Subject: [PATCH 12/19] remove code which is no longer needed because of custom physics setters --- libraries/entities/src/EntityItem.cpp | 15 --------------- libraries/entities/src/EntityTree.cpp | 3 ++- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 421efac687..705867c8d9 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1767,9 +1767,6 @@ void EntityItem::updateParentID(const QUuid& value) { } void EntityItem::updatePositionFromNetwork(const glm::vec3& value) { - if (shouldSuppressLocationEdits()) { - return; - } updatePosition(value); } @@ -1796,9 +1793,6 @@ void EntityItem::updateRotation(const glm::quat& rotation) { } void EntityItem::updateRotationFromNetwork(const glm::quat& rotation) { - if (shouldSuppressLocationEdits()) { - return; - } updateRotation(rotation); } @@ -1853,16 +1847,10 @@ void EntityItem::updateVelocity(const glm::vec3& value) { } void EntityItem::updateVelocityFromNetwork(const glm::vec3& value) { - if (shouldSuppressLocationEdits()) { - return; - } updateVelocity(value); } void EntityItem::updateQueryAACubeFromNetwork(const AACube& value) { - if (shouldSuppressLocationEdits()) { - return; - } setQueryAACube(value); } @@ -1918,9 +1906,6 @@ void EntityItem::updateAngularVelocity(const glm::vec3& value) { } void EntityItem::updateAngularVelocityFromNetwork(const glm::vec3& value) { - if (shouldSuppressLocationEdits()) { - return; - } updateAngularVelocity(value); } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index e4de39a799..b58d10ce24 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1028,7 +1028,8 @@ void EntityTree::fixupTerseEditLogging(EntityItemProperties& properties, QList Date: Fri, 13 Oct 2017 15:40:08 -0700 Subject: [PATCH 13/19] cleanups + call updateQueryAACube when parent changes or action is deleted --- libraries/entities/src/EntityItem.cpp | 32 ++++++--------------------- libraries/entities/src/EntityItem.h | 5 ----- 2 files changed, 7 insertions(+), 30 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 705867c8d9..79e78f9dc7 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -739,7 +739,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef // reasons and the contract is that the client handles them in an idempotent manner. auto customUpdatePositionFromNetwork = [this, shouldUpdate, lastEdited](glm::vec3 value){ if (shouldUpdate(_lastUpdatedPositionTimestamp, value != _lastUpdatedPositionValue)) { - updatePositionFromNetwork(value); + updatePosition(value); _lastUpdatedPositionTimestamp = lastEdited; _lastUpdatedPositionValue = value; } @@ -747,7 +747,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef auto customUpdateRotationFromNetwork = [this, shouldUpdate, lastEdited](glm::quat value){ if (shouldUpdate(_lastUpdatedRotationTimestamp, value != _lastUpdatedRotationValue)) { - updateRotationFromNetwork(value); + updateRotation(value); _lastUpdatedRotationTimestamp = lastEdited; _lastUpdatedRotationValue = value; } @@ -755,7 +755,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef auto customUpdateVelocityFromNetwork = [this, shouldUpdate, lastEdited](glm::vec3 value){ if (shouldUpdate(_lastUpdatedVelocityTimestamp, value != _lastUpdatedVelocityValue)) { - updateVelocityFromNetwork(value); + updateVelocity(value); _lastUpdatedVelocityTimestamp = lastEdited; _lastUpdatedVelocityValue = value; } @@ -763,7 +763,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef auto customUpdateAngularVelocityFromNetwork = [this, shouldUpdate, lastEdited](glm::vec3 value){ if (shouldUpdate(_lastUpdatedAngularVelocityTimestamp, value != _lastUpdatedAngularVelocityValue)) { - updateAngularVelocityFromNetwork(value); + updateAngularVelocity(value); _lastUpdatedAngularVelocityTimestamp = lastEdited; _lastUpdatedAngularVelocityValue = value; } @@ -850,7 +850,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef { auto customUpdateQueryAACubeFromNetwork = [this, shouldUpdate, lastEdited](AACube value){ if (shouldUpdate(_lastUpdatedQueryAACubeTimestamp, value != _lastUpdatedQueryAACubeValue)) { - updateQueryAACubeFromNetwork(value); + setQueryAACube(value); _lastUpdatedQueryAACubeTimestamp = lastEdited; _lastUpdatedQueryAACubeValue = value; } @@ -1763,13 +1763,10 @@ void EntityItem::updateParentID(const QUuid& value) { if (tree) { tree->addToNeedsParentFixupList(getThisPointer()); } + updateQueryAACube(); } } -void EntityItem::updatePositionFromNetwork(const glm::vec3& value) { - updatePosition(value); -} - void EntityItem::updateDimensions(const glm::vec3& value) { if (getDimensions() != value) { setDimensions(value); @@ -1792,10 +1789,6 @@ void EntityItem::updateRotation(const glm::quat& rotation) { } } -void EntityItem::updateRotationFromNetwork(const glm::quat& rotation) { - updateRotation(rotation); -} - void EntityItem::updateMass(float mass) { // Setting the mass actually changes the _density (at fixed volume), however // we must protect the density range to help maintain stability of physics simulation @@ -1846,14 +1839,6 @@ void EntityItem::updateVelocity(const glm::vec3& value) { } } -void EntityItem::updateVelocityFromNetwork(const glm::vec3& value) { - updateVelocity(value); -} - -void EntityItem::updateQueryAACubeFromNetwork(const AACube& value) { - setQueryAACube(value); -} - void EntityItem::updateDamping(float value) { auto clampedDamping = glm::clamp(value, 0.0f, 1.0f); if (_damping != clampedDamping) { @@ -1905,10 +1890,6 @@ void EntityItem::updateAngularVelocity(const glm::vec3& value) { } } -void EntityItem::updateAngularVelocityFromNetwork(const glm::vec3& value) { - updateAngularVelocity(value); -} - void EntityItem::updateAngularDamping(float value) { auto clampedDamping = glm::clamp(value, 0.0f, 1.0f); if (_angularDamping != clampedDamping) { @@ -2213,6 +2194,7 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi _dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION; _dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar setDynamicDataNeedsTransmit(true); + updateQueryAACube(); return success; } return false; diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 764ff34f99..ce4bf13896 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -357,21 +357,16 @@ public: virtual void updateRegistrationPoint(const glm::vec3& value); void updatePosition(const glm::vec3& value); void updateParentID(const QUuid& value); - void updatePositionFromNetwork(const glm::vec3& value); void updateDimensions(const glm::vec3& value); void updateRotation(const glm::quat& rotation); - void updateRotationFromNetwork(const glm::quat& rotation); void updateDensity(float value); void updateMass(float value); void updateVelocity(const glm::vec3& value); - void updateVelocityFromNetwork(const glm::vec3& value); - void updateQueryAACubeFromNetwork(const AACube& value); void updateDamping(float value); void updateRestitution(float value); void updateFriction(float value); void updateGravity(const glm::vec3& value); void updateAngularVelocity(const glm::vec3& value); - void updateAngularVelocityFromNetwork(const glm::vec3& value); void updateAngularDamping(float value); void updateCollisionless(bool value); void updateCollisionMask(uint8_t value); From 2c8c5e214cec55ba95f5ae52f192d88d1a24e78a Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 13 Oct 2017 16:10:58 -0700 Subject: [PATCH 14/19] puff querybox if entity is in motion --- libraries/entities/src/EntityItem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 79e78f9dc7..9af39254ed 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1557,7 +1557,7 @@ AACube EntityItem::getQueryAACube(bool& success) const { } bool EntityItem::shouldPuffQueryAACube() const { - return hasActions() || isChildOfMyAvatar(); + return hasActions() || isChildOfMyAvatar() || isMovingRelativeToParent(); } // NOTE: This should only be used in cases of old bitstreams which only contain radius data From ae1b2bd9312a34f5f0476b5e7f8b7b10837c8580 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 20 Oct 2017 11:11:23 -0700 Subject: [PATCH 15/19] 'Tis a silly hack --- scripts/system/marketplaces/marketplaces.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 5ffc35e6e5..3bf6435a25 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -135,9 +135,10 @@ function setCertificateInfo(currentEntityWithContextOverlay, itemCertificateId) { wireEventBridge(true); + var certificateId = itemCertificateId || (Entities.getEntityProperties(currentEntityWithContextOverlay, ['certificateID']).certificateID + "\n"); tablet.sendToQml({ method: 'inspectionCertificate_setCertificateId', - certificateId: itemCertificateId || Entities.getEntityProperties(currentEntityWithContextOverlay, ['certificateID']).certificateID + certificateId: certificateId }); } From c217992866261b898b82699662a29ad7baab1709 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 20 Oct 2017 11:42:03 -0700 Subject: [PATCH 16/19] Order of operations lol --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 8cbb214857..e39a4edc11 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -140,8 +140,8 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& if (event.getID() == LEFT_HAND_HW_ID) { offsetAngle *= -1.0f; } - contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, offsetAngle, 0.0f)))) * - ((cameraPosition + direction * (distance - CONTEXT_OVERLAY_OFFSET_DISTANCE))); + contextOverlayPosition = cameraPosition + + (glm::quat(glm::radians(glm::vec3(0.0f, offsetAngle, 0.0f)))) * (direction * (distance - CONTEXT_OVERLAY_OFFSET_DISTANCE)); contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_SIZE, CONTEXT_OVERLAY_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); } From 2b0285adc2fdf50fa9e70827e61bc7dead647958 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 20 Oct 2017 15:05:21 -0700 Subject: [PATCH 17/19] fix action (grab) deadlock --- libraries/entities/src/EntityItem.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 9af39254ed..2d3aeeba38 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -2109,6 +2109,7 @@ bool EntityItem::addAction(EntitySimulationPointer simulation, EntityDynamicPoin removeActionInternal(action->getID()); } }); + updateQueryAACube(); return result; } @@ -2167,6 +2168,8 @@ bool EntityItem::removeAction(EntitySimulationPointer simulation, const QUuid& a checkWaitingToRemove(simulation); success = removeActionInternal(actionID); }); + updateQueryAACube(); + return success; } @@ -2194,7 +2197,6 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi _dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION; _dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar setDynamicDataNeedsTransmit(true); - updateQueryAACube(); return success; } return false; From 5c8451a6266200726e5c848f6e34663ac8dffd0e Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 23 Oct 2017 10:36:23 -0700 Subject: [PATCH 18/19] Auto-refresh Wallet Home & Purchases on 4s timer --- .../qml/hifi/commerce/purchases/Purchases.qml | 4 +++- .../qml/hifi/commerce/wallet/WalletHome.qml | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index c98f212c53..f41bdf55ba 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -583,9 +583,11 @@ Rectangle { Timer { id: inventoryTimer; - interval: 90000; + interval: 4000; // Change this back to 90000 after demo + //interval: 90000; onTriggered: { if (root.activeView === "purchasesMain" && !root.pendingInventoryReply) { + console.log("Refreshing Purchases..."); root.pendingInventoryReply = true; commerce.inventory(); } diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml index 50891deb60..9323d97fe4 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml @@ -43,6 +43,7 @@ Item { calculatePendingAndInvalidated(); } + refreshTimer.start(); } } @@ -117,6 +118,8 @@ Item { historyReceived = false; commerce.balance(); commerce.history(); + } else { + refreshTimer.stop(); } } } @@ -138,6 +141,17 @@ Item { } } + Timer { + id: refreshTimer; + interval: 4000; // Remove this after demo? + onTriggered: { + console.log("Refreshing Wallet Home..."); + historyReceived = false; + commerce.balance(); + commerce.history(); + } + } + // Recent Activity Rectangle { id: recentActivityContainer; From 30bffdea3234239d25fe806fa30de7f551059f8b Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 23 Oct 2017 11:45:51 -0700 Subject: [PATCH 19/19] Quick bugfix --- interface/resources/qml/hifi/commerce/purchases/Purchases.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index f41bdf55ba..27737aa0a9 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -663,6 +663,8 @@ Rectangle { currentPurchasesModelStatus !== previousPurchasesModelStatus) { purchasesModel.setProperty(i, "statusChanged", true); + } else { + purchasesModel.setProperty(i, "statusChanged", false); } } }