diff --git a/interface/resources/avatar/animations/run_fwd.fbx b/interface/resources/avatar/animations/run_fwd.fbx index 2ea035e694..86add969e5 100644 Binary files a/interface/resources/avatar/animations/run_fwd.fbx and b/interface/resources/avatar/animations/run_fwd.fbx differ diff --git a/interface/resources/avatar/avatar-animation.json b/interface/resources/avatar/avatar-animation.json index 50fe5019f9..27e45daa7b 100644 --- a/interface/resources/avatar/avatar-animation.json +++ b/interface/resources/avatar/avatar-animation.json @@ -1016,8 +1016,8 @@ "type": "clip", "data": { "url": "qrc:///avatar/animations/run_fwd.fbx", - "startFrame": 0.0, - "endFrame": 21.0, + "startFrame": 1.0, + "endFrame": 22.0, "timeScale": 1.0, "loopFlag": true }, diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 9589c842e6..3ace6f381f 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -40,9 +40,9 @@ Rectangle { property string itemHref; property string itemAuthor; property int itemEdition: -1; - property bool hasSomethingToTradeIn: alreadyOwned && (itemEdition > 0); // i.e., don't trade in your artist's proof - property bool isTradingIn: isUpdating && hasSomethingToTradeIn; - property bool isStocking: availability === 'not for sale' && creator === Account.username; + property bool hasSomethingToTradeIn: itemEdition > 0; // i.e., don't trade in your artist's proof + property bool isTradingIn: canUpdate && hasSomethingToTradeIn; + property bool isStocking: (availability === 'not for sale') && (creator === Account.username) && !updated_item_id; property string certificateId; property double balanceAfterPurchase; property bool alreadyOwned: false; // Including proofs @@ -59,8 +59,9 @@ Rectangle { property bool canRezCertifiedItems: Entities.canRezCertified() || Entities.canRezTmpCertified(); property string referrer; property bool isInstalled; - property bool isUpdating; + property bool canUpdate; property string availability: "available"; + property string updated_item_id: ""; property string creator: ""; property string baseAppURL; property int currentUpdatesPage: 1; @@ -144,6 +145,7 @@ Rectangle { } onAvailableUpdatesResult: { + // Answers the updatable original item cert data still owned by this user that are EITHER instances of this marketplace id, or update to this marketplace id. if (result.status !== 'success') { console.log("Failed to get Available Updates", result.data.message); } else { @@ -155,7 +157,7 @@ Rectangle { if (root.itemEdition !== -1 && root.itemEdition !== parseInt(result.data.updates[i].edition_number)) { continue; } - root.isUpdating = true; + root.canUpdate = true; root.baseItemName = result.data.updates[i].base_item_title; // This CertID is the one corresponding to the base item CertID that the user already owns root.certificateId = result.data.updates[i].certificate_id; @@ -166,7 +168,7 @@ Rectangle { } } - if (result.data.updates.length === 0 || root.isUpdating) { + if (result.data.updates.length === 0 || root.canUpdate) { root.availableUpdatesReceived = true; refreshBuyUI(); } else { @@ -178,7 +180,7 @@ Rectangle { onUpdateItemResult: { if (result.status !== 'success') { - failureErrorText.text = result.message; + failureErrorText.text = result.data ? (result.data.message || "Unknown Error") : JSON.stringify(result); root.activeView = "checkoutFailure"; } else { root.itemHref = result.data.download_url; @@ -266,13 +268,6 @@ Rectangle { } } } - MouseArea { - enabled: titleBarContainer.usernameDropdownVisible; - anchors.fill: parent; - onClicked: { - titleBarContainer.usernameDropdownVisible = false; - } - } // // TITLE BAR END // @@ -481,7 +476,7 @@ Rectangle { FiraSansSemiBold { id: itemPriceText; text: isTradingIn ? "FREE\nUPDATE" : - (isStocking ? "Free for creator" : + (isStocking ? "Free for creator" : ((root.itemPrice === -1) ? "--" : ((root.itemPrice > 0) ? root.itemPrice : "FREE"))); // Text size size: isTradingIn ? 20 : 26; @@ -580,7 +575,7 @@ Rectangle { // "View in Inventory" button HifiControlsUit.Button { id: viewInMyPurchasesButton; - visible: isCertified && dataReady && (isUpdating ? !hasSomethingToTradeIn : alreadyOwned); + visible: isCertified && dataReady && (isTradingIn ? hasSomethingToTradeIn : alreadyOwned); color: hifi.buttons.blue; colorScheme: hifi.colorSchemes.light; anchors.top: buyTextContainer.visible ? buyTextContainer.bottom : checkoutActionButtonsContainer.top; @@ -588,9 +583,9 @@ Rectangle { height: 50; anchors.left: parent.left; anchors.right: parent.right; - text: root.isUpdating ? "UPDATE TO THIS ITEM FOR FREE" : "VIEW THIS ITEM IN YOUR INVENTORY"; + text: (canUpdate && !isTradingIn) ? "UPDATE TO THIS ITEM FOR FREE" : "VIEW THIS ITEM IN YOUR INVENTORY"; onClicked: { - if (root.isUpdating) { + if (root.canUpdate) { sendToScript({method: 'checkout_goToPurchases', filterText: root.baseItemName}); } else { sendToScript({method: 'checkout_goToPurchases', filterText: root.itemName}); @@ -602,7 +597,11 @@ Rectangle { HifiControlsUit.Button { id: buyButton; visible: isTradingIn || !alreadyOwned || isStocking || !(root.itemType === "avatar" || root.itemType === "app"); - enabled: (root.balanceAfterPurchase >= 0 && dataReady) || (!root.isCertified) || root.isUpdating; + property bool checkBalance: dataReady && (root.availability === "available") + enabled: (checkBalance && (balanceAfterPurchase >= 0)) || !isCertified || isTradingIn || isStocking; + text: isTradingIn ? "Confirm Update" : + (enabled ? (viewInMyPurchasesButton.visible ? "Get It Again" : (dataReady ? "Get Item" : "--")) : + (checkBalance ? "Insufficient Funds" : availability)) color: viewInMyPurchasesButton.visible ? hifi.buttons.white : hifi.buttons.blue; colorScheme: hifi.colorSchemes.light; anchors.top: viewInMyPurchasesButton.visible ? viewInMyPurchasesButton.bottom : @@ -611,13 +610,6 @@ Rectangle { height: 50; anchors.left: parent.left; anchors.right: parent.right; - text: isTradingIn ? - "CONFIRM UPDATE" : - (((root.isCertified) ? - (dataReady ? - ((viewInMyPurchasesButton.visible && !root.isUpdating) ? "Get It Again" : "Confirm") : - "--") : - "Get Item")); onClicked: { if (isTradingIn) { // If we're updating an app, the existing app needs to be uninstalled. @@ -1110,6 +1102,7 @@ Rectangle { root.itemAuthor = result.data.creator; root.itemType = result.data.item_type || "unknown"; root.availability = result.data.availability; + root.updated_item_id = result.data.updated_item_id || "" root.creator = result.data.creator; if (root.itemType === "unknown") { root.itemHref = result.data.review_url; @@ -1209,7 +1202,7 @@ Rectangle { buyText.text = ""; // If the user IS on the checkout page for the updated version of an owned item... - if (root.isUpdating) { + if (root.canUpdate) { // If the user HAS already selected a specific edition to update... if (hasSomethingToTradeIn) { buyText.text = "By pressing \"Confirm Update\", you agree to trade in your old item for the updated item that replaces it."; diff --git a/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml index ca6838efea..3004af6c15 100644 --- a/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml +++ b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml @@ -112,7 +112,7 @@ Rectangle { marketplaceItem.image_url = result.data.thumbnail_url; marketplaceItem.name = result.data.title; marketplaceItem.likes = result.data.likes; - if(result.data.has_liked !== undefined) { + if (result.data.has_liked !== undefined) { marketplaceItem.liked = result.data.has_liked; } marketplaceItem.creator = result.data.creator; @@ -122,6 +122,7 @@ Rectangle { marketplaceItem.attributions = result.data.attributions; marketplaceItem.license = result.data.license; marketplaceItem.availability = result.data.availability; + marketplaceItem.updated_item_id = result.data.updated_item_id || ""; marketplaceItem.created_at = result.data.created_at; marketplaceItemScrollView.contentHeight = marketplaceItemContent.height; itemsList.visible = false; @@ -979,7 +980,6 @@ Rectangle { xhr.open("GET", url); xhr.onreadystatechange = function() { if (xhr.readyState == XMLHttpRequest.DONE) { - console.log(xhr.responseText); licenseText.text = xhr.responseText; licenseInfo.visible = true; } diff --git a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml index 97e5c10a6b..8c9d3c31c8 100644 --- a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml +++ b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml @@ -2,7 +2,7 @@ // MarketplaceListItem.qml // qml/hifi/commerce/marketplace // -// MarketplaceListItem +// MarketplaceItem // // Created by Roxanne Skelly on 2019-01-22 // Copyright 2019 High Fidelity, Inc. @@ -34,6 +34,7 @@ Rectangle { property var categories: [] property int price: 0 property string availability: "unknown" + property string updated_item_id: "" property var attributions: [] property string description: "" property string license: "" @@ -42,7 +43,6 @@ Rectangle { property bool isLoggedIn: false; property int edition: -1; property bool supports3DHTML: false; - onCategoriesChanged: { categoriesListModel.clear(); @@ -52,7 +52,7 @@ Rectangle { } onDescriptionChanged: { - + if(root.supports3DHTML) { descriptionTextModel.clear(); descriptionTextModel.append({text: description}); @@ -264,17 +264,14 @@ Rectangle { } height: 50 - property bool isNFS: availability === "not for sale" // Note: server will say "sold out" or "invalidated" before it says NFS - property bool isMine: creator === Account.username - property bool isUpgrade: root.edition >= 0 - property int costToMe: ((isMine && isNFS) || isUpgrade) ? 0 : price - property bool isAvailable: costToMe >= 0 - - text: isUpgrade ? "UPGRADE FOR FREE" : (isAvailable ? (costToMe || "FREE") : availability) - enabled: isAvailable - buttonGlyph: isAvailable ? (costToMe ? hifi.glyphs.hfc : "") : "" + property bool isUpdate: root.edition >= 0 // Special case of updating from a specific older item + property bool isStocking: (creator === Account.username) && (availability === "not for sale") && !updated_item_id // Note: server will say "sold out" or "invalidated" before it says NFS + property bool isFreeSpecial: isStocking || isUpdate + enabled: isFreeSpecial || (availability === 'available') + buttonGlyph: (enabled && !isUpdate && (price > 0)) ? hifi.glyphs.hfc : "" + text: isUpdate ? "UPDATE FOR FREE" : (isStocking ? "FREE STOCK" : (enabled ? (price || "FREE") : availability)) color: hifi.buttons.blue - + onClicked: root.buy(); } diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index df6e216b32..2c2fed1d8f 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -380,7 +380,7 @@ Item { if (updateButton.visible && uninstallButton.visible) { item.itemButtonText = ""; item.glyphSize = 20; - } else { + } else if (item) { item.itemButtonText = "Send to Trash"; item.glyphSize = 30; } diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index f8e2c9115b..ea74549084 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -40,10 +40,6 @@ Rectangle { source: "images/wallet-bg.jpg"; } - Component.onDestruction: { - KeyboardScriptingInterface.raised = false; - } - Connections { target: Commerce; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index faa9f88ae9..709d11764f 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -910,7 +910,7 @@ void MyAvatar::simulate(float deltaTime, bool inView) { recorder->recordFrame(FRAME_TYPE, toFrame(*this)); } - locationChanged(); + locationChanged(true, false); // if a entity-child of this avatar has moved outside of its queryAACube, update the cube and tell the entity server. auto entityTreeRenderer = qApp->getEntities(); EntityTreePointer entityTree = entityTreeRenderer ? entityTreeRenderer->getTree() : nullptr; @@ -920,6 +920,7 @@ void MyAvatar::simulate(float deltaTime, bool inView) { zoneInteractionProperties = entityTreeRenderer->getZoneInteractionProperties(); EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender(); forEachDescendant([&](SpatiallyNestablePointer object) { + locationChanged(true, false); // we need to update attached queryAACubes in our own local tree so point-select always works // however we don't want to flood the update pipeline with AvatarEntity updates, so we assume // others have all info required to properly update queryAACube of AvatarEntities on their end @@ -2324,23 +2325,25 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { std::shared_ptr skeletonConnection = std::make_shared(); *skeletonConnection = QObject::connect(_skeletonModel.get(), &SkeletonModel::skeletonLoaded, [this, skeletonModelChangeCount, skeletonConnection]() { - if (skeletonModelChangeCount == _skeletonModelChangeCount) { + if (skeletonModelChangeCount == _skeletonModelChangeCount) { - if (_fullAvatarModelName.isEmpty()) { - // Store the FST file name into preferences - const auto& mapping = _skeletonModel->getGeometry()->getMapping(); - if (mapping.value("name").isValid()) { - _fullAvatarModelName = mapping.value("name").toString(); - } - } + if (_fullAvatarModelName.isEmpty()) { + // Store the FST file name into preferences + const auto& mapping = _skeletonModel->getGeometry()->getMapping(); + if (mapping.value("name").isValid()) { + _fullAvatarModelName = mapping.value("name").toString(); + } + } - initHeadBones(); - _skeletonModel->setCauterizeBoneSet(_headBoneSet); - _fstAnimGraphOverrideUrl = _skeletonModel->getGeometry()->getAnimGraphOverrideUrl(); - initAnimGraph(); - _skeletonModelLoaded = true; - } - QObject::disconnect(*skeletonConnection); + initHeadBones(); + _skeletonModel->setCauterizeBoneSet(_headBoneSet); + _fstAnimGraphOverrideUrl = _skeletonModel->getGeometry()->getAnimGraphOverrideUrl(); + initAnimGraph(); + initFlowFromFST(); + + _skeletonModelLoaded = true; + } + QObject::disconnect(*skeletonConnection); }); saveAvatarUrl(); @@ -5384,6 +5387,15 @@ void MyAvatar::useFlow(bool isActive, bool isCollidable, const QVariantMap& phys } } +void MyAvatar::initFlowFromFST() { + if (_skeletonModel->isLoaded()) { + auto &flowData = _skeletonModel->getHFMModel().flowData; + if (flowData.shouldInitFlow()) { + useFlow(true, flowData.shouldInitCollisions(), flowData._physicsConfig, flowData._collisionsConfig); + } + } +} + void MyAvatar::sendPacket(const QUuid& entityID, const EntityItemProperties& properties) const { auto treeRenderer = DependencyManager::get(); EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 2c8dedd430..46189c4a11 100755 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1751,6 +1751,7 @@ private: void updateCollisionSound(const glm::vec3& penetration, float deltaTime, float frequency); void initHeadBones(); void initAnimGraph(); + void initFlowFromFST(); // Avatar Preferences QUrl _fullAvatarURLFromPreferences; diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index dfd698f6c5..1bcb040a77 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -204,7 +204,8 @@ QString Overlays::overlayToEntityType(const QString& type) { #define RENAME_PROP(o, e) \ { \ auto iter = overlayProps.find(#o); \ - if (iter != overlayProps.end()) { \ + if (iter != overlayProps.end() && \ + !overlayProps.contains(#e)) { \ overlayProps[#e] = iter.value(); \ } \ } @@ -493,15 +494,34 @@ EntityItemProperties Overlays::convertOverlayToEntityProperties(QVariantMap& ove RENAME_PROP_CONVERT(p1, p1, [](const QVariant& v) { return vec3toVariant(glm::vec3(0.0f)); }); RENAME_PROP_CONVERT(p2, p2, [=](const QVariant& v) { glm::vec3 position; + bool hasPosition = false; + glm::quat rotation; + bool hasRotation = false; + auto iter2 = overlayProps.find("position"); if (iter2 != overlayProps.end()) { position = vec3FromVariant(iter2.value()); - } else if (!add) { - EntityPropertyFlags desiredProperties; - desiredProperties += PROP_POSITION; - position = DependencyManager::get()->getEntityProperties(id, desiredProperties).getPosition(); + hasPosition = true; } - return vec3toVariant(vec3FromVariant(v) - position); + iter2 = overlayProps.find("rotation"); + if (iter2 != overlayProps.end()) { + rotation = quatFromVariant(iter2.value()); + hasRotation = true; + } + + if (!add && !(hasPosition && hasRotation)) { + auto entity = DependencyManager::get()->getEntity(id); + if (entity) { + if (!hasPosition) { + position = entity->getWorldPosition(); + } + if (!hasRotation) { + rotation = entity->getWorldOrientation(); + } + } + } + + return vec3toVariant(glm::inverse(rotation) * (vec3FromVariant(v) - position)); }); RENAME_PROP(localStart, p1); diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index e842d98714..03c50008a0 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -928,9 +928,9 @@ void RenderableModelEntityItem::setJointTranslationsSet(const QVector& tra _needsJointSimulation = true; } -void RenderableModelEntityItem::locationChanged(bool tellPhysics) { +void RenderableModelEntityItem::locationChanged(bool tellPhysics, bool tellChildren) { DETAILED_PERFORMANCE_TIMER("locationChanged"); - EntityItem::locationChanged(tellPhysics); + EntityItem::locationChanged(tellPhysics, tellChildren); auto model = getModel(); if (model && model->isLoaded()) { model->updateRenderItems(); @@ -1032,9 +1032,7 @@ void RenderableModelEntityItem::copyAnimationJointDataToModel() { }); if (changed) { - forEachChild([&](SpatiallyNestablePointer object) { - object->locationChanged(false); - }); + locationChanged(false, true); } } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 9adff9ca01..2fd1041c5f 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -108,7 +108,7 @@ public: virtual void setJointTranslations(const QVector& translations) override; virtual void setJointTranslationsSet(const QVector& translationsSet) override; - virtual void locationChanged(bool tellPhysics = true) override; + virtual void locationChanged(bool tellPhysics = true, bool tellChildren = true) override; virtual int getJointIndex(const QString& name) const override; virtual QStringList getJointNames() const override; diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.h b/libraries/entities-renderer/src/RenderableZoneEntityItem.h index 32b5cf94a0..2fa55f1540 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.h +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.h @@ -150,7 +150,7 @@ public: virtual bool addToScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override; virtual void removeFromScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override; private: - virtual void locationChanged(bool tellPhysics = true) override { EntityItem::locationChanged(tellPhysics); notifyBoundChanged(); } + virtual void locationChanged(bool tellPhysics = true, bool tellChildren = true) override { EntityItem::locationChanged(tellPhysics, tellChildren); notifyBoundChanged(); } virtual void dimensionsChanged() override { EntityItem::dimensionsChanged(); notifyBoundChanged(); } void notifyBoundChanged(); void notifyChangedRenderItem(); diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index d9614a0918..40bba5a0df 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1873,7 +1873,7 @@ void EntityItem::setParentID(const QUuid& value) { glm::vec3 EntityItem::getScaledDimensions() const { glm::vec3 scale = getSNScale(); - return _unscaledDimensions * scale; + return getUnscaledDimensions() * scale; } void EntityItem::setScaledDimensions(const glm::vec3& value) { @@ -2593,7 +2593,7 @@ QList EntityItem::getActionsOfType(EntityDynamicType typeT return result; } -void EntityItem::locationChanged(bool tellPhysics) { +void EntityItem::locationChanged(bool tellPhysics, bool tellChildren) { requiresRecalcBoxes(); if (tellPhysics) { _flags |= Simulation::DIRTY_TRANSFORM; @@ -2602,7 +2602,7 @@ void EntityItem::locationChanged(bool tellPhysics) { tree->entityChanged(getThisPointer()); } } - SpatiallyNestable::locationChanged(tellPhysics); // tell all the children, also + SpatiallyNestable::locationChanged(tellPhysics, tellChildren); std::pair data(_spaceIndex, glm::vec4(getWorldPosition(), _boundingRadius)); emit spaceUpdate(data); somethingChangedNotification(); diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 3fbfde6b24..f8c9c3b6f7 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -518,7 +518,7 @@ public: virtual bool getMeshes(MeshProxyList& result) { return true; } - virtual void locationChanged(bool tellPhysics = true) override; + virtual void locationChanged(bool tellPhysics = true, bool tellChildren = true) override; virtual bool getScalesWithParent() const override; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 3d14206c95..80a6a90841 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -4209,7 +4209,7 @@ void EntityItemProperties::copySimulationRestrictedProperties(const EntityItemPo setAcceleration(entity->getAcceleration()); } if (!_localDimensionsChanged && !_dimensionsChanged) { - setDimensions(entity->getScaledDimensions()); + setLocalDimensions(entity->getScaledDimensions()); } } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index d4f15fa8b2..fffcd943c3 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -2039,6 +2039,8 @@ void EntityTree::fixupNeedsParentFixups() { Simulation::DIRTY_COLLISION_GROUP | Simulation::DIRTY_TRANSFORM); entityChanged(entity); + entity->locationChanged(true, false); + entity->forEachDescendant([&](SpatiallyNestablePointer object) { if (object->getNestableType() == NestableType::Entity) { EntityItemPointer descendantEntity = std::static_pointer_cast(object); @@ -2047,8 +2049,8 @@ void EntityTree::fixupNeedsParentFixups() { Simulation::DIRTY_TRANSFORM); entityChanged(descendantEntity); } + object->locationChanged(true, false); }); - entity->locationChanged(true); // Update our parent's bounding box bool success = false; @@ -3002,8 +3004,19 @@ void EntityTree::updateEntityQueryAACubeWorker(SpatiallyNestablePointer object, // if the queryBox has changed, tell the entity-server EntityItemPointer entity = std::dynamic_pointer_cast(object); if (entity) { - // NOTE: we rely on side-effects of the entity->updateQueryAACube() call in the following if() conditional: - if (entity->updateQueryAACube() || force) { + bool queryAACubeChanged = false; + if (!entity->hasChildren()) { + // updateQueryAACube will also update all ancestors' AACubes, so we only need to call this for leaf nodes + queryAACubeChanged = entity->updateQueryAACube(); + } else { + AACube oldCube = entity->getQueryAACube(); + object->forEachChild([&](SpatiallyNestablePointer descendant) { + updateEntityQueryAACubeWorker(descendant, packetSender, moveOperator, force, tellServer); + }); + queryAACubeChanged = oldCube != entity->getQueryAACube(); + } + + if (queryAACubeChanged || force) { bool success; AACube newCube = entity->getQueryAACube(success); if (success) { @@ -3027,10 +3040,6 @@ void EntityTree::updateEntityQueryAACubeWorker(SpatiallyNestablePointer object, entityChanged(entity); } } - - object->forEachDescendant([&](SpatiallyNestablePointer descendant) { - updateEntityQueryAACubeWorker(descendant, packetSender, moveOperator, force, tellServer); - }); } void EntityTree::updateEntityQueryAACube(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender, diff --git a/libraries/entities/src/LightEntityItem.cpp b/libraries/entities/src/LightEntityItem.cpp index 88aae9691c..fcfda66319 100644 --- a/libraries/entities/src/LightEntityItem.cpp +++ b/libraries/entities/src/LightEntityItem.cpp @@ -55,8 +55,8 @@ void LightEntityItem::setUnscaledDimensions(const glm::vec3& value) { } } -void LightEntityItem::locationChanged(bool tellPhysics) { - EntityItem::locationChanged(tellPhysics); +void LightEntityItem::locationChanged(bool tellPhysics, bool tellChildren) { + EntityItem::locationChanged(tellPhysics, tellChildren); withWriteLock([&] { _lightPropertiesChanged = true; }); diff --git a/libraries/entities/src/LightEntityItem.h b/libraries/entities/src/LightEntityItem.h index 26b74f02cd..cc64121cb3 100644 --- a/libraries/entities/src/LightEntityItem.h +++ b/libraries/entities/src/LightEntityItem.h @@ -74,7 +74,7 @@ public: static bool getLightsArePickable() { return _lightsArePickable; } static void setLightsArePickable(bool value) { _lightsArePickable = value; } - virtual void locationChanged(bool tellPhysics) override; + virtual void locationChanged(bool tellPhysics, bool tellChildren) override; virtual void dimensionsChanged() override; bool lightPropertiesChanged() const { return _lightPropertiesChanged; } diff --git a/libraries/hfm/src/hfm/HFM.h b/libraries/hfm/src/hfm/HFM.h index 9f3de3302c..4f44595eaa 100644 --- a/libraries/hfm/src/hfm/HFM.h +++ b/libraries/hfm/src/hfm/HFM.h @@ -273,6 +273,15 @@ public: {} }; +class FlowData { +public: + FlowData() {}; + QVariantMap _physicsConfig; + QVariantMap _collisionsConfig; + bool shouldInitFlow() const { return _physicsConfig.size() > 0; } + bool shouldInitCollisions() const { return _collisionsConfig.size() > 0; } +}; + /// The runtime model format. class Model { public: @@ -319,6 +328,7 @@ public: QList blendshapeChannelNames; QMap jointRotationOffsets; + FlowData flowData; }; }; @@ -343,6 +353,7 @@ typedef hfm::Mesh HFMMesh; typedef hfm::AnimationFrame HFMAnimationFrame; typedef hfm::Light HFMLight; typedef hfm::Model HFMModel; +typedef hfm::FlowData FlowData; Q_DECLARE_METATYPE(HFMAnimationFrame) Q_DECLARE_METATYPE(QVector) diff --git a/libraries/model-baker/src/model-baker/Baker.cpp b/libraries/model-baker/src/model-baker/Baker.cpp index c0c473315d..f55cacf0f2 100644 --- a/libraries/model-baker/src/model-baker/Baker.cpp +++ b/libraries/model-baker/src/model-baker/Baker.cpp @@ -20,6 +20,7 @@ #include "CalculateBlendshapeNormalsTask.h" #include "CalculateBlendshapeTangentsTask.h" #include "PrepareJointsTask.h" +#include "ParseFlowDataTask.h" namespace baker { @@ -101,7 +102,7 @@ namespace baker { class BuildModelTask { public: - using Input = VaryingSet5, std::vector, QMap, QHash>; + using Input = VaryingSet6, std::vector, QMap, QHash, FlowData>; using Output = hfm::Model::Pointer; using JobModel = Job::ModelIO; @@ -111,6 +112,7 @@ namespace baker { hfmModelOut->joints = QVector::fromStdVector(input.get2()); hfmModelOut->jointRotationOffsets = input.get3(); hfmModelOut->jointIndices = input.get4(); + hfmModelOut->flowData = input.get5(); output = hfmModelOut; } }; @@ -157,12 +159,15 @@ namespace baker { // Parse material mapping const auto materialMapping = model.addJob("ParseMaterialMapping", mapping); + // Parse flow data + const auto flowData = model.addJob("ParseFlowData", mapping); + // Combine the outputs into a new hfm::Model const auto buildBlendshapesInputs = BuildBlendshapesTask::Input(blendshapesPerMeshIn, normalsPerBlendshapePerMesh, tangentsPerBlendshapePerMesh).asVarying(); const auto blendshapesPerMeshOut = model.addJob("BuildBlendshapes", buildBlendshapesInputs); const auto buildMeshesInputs = BuildMeshesTask::Input(meshesIn, graphicsMeshes, normalsPerMesh, tangentsPerMesh, blendshapesPerMeshOut).asVarying(); const auto meshesOut = model.addJob("BuildMeshes", buildMeshesInputs); - const auto buildModelInputs = BuildModelTask::Input(hfmModelIn, meshesOut, jointsOut, jointRotationOffsets, jointIndices).asVarying(); + const auto buildModelInputs = BuildModelTask::Input(hfmModelIn, meshesOut, jointsOut, jointRotationOffsets, jointIndices, flowData).asVarying(); const auto hfmModelOut = model.addJob("BuildModel", buildModelInputs); output = Output(hfmModelOut, materialMapping); diff --git a/libraries/model-baker/src/model-baker/ParseFlowDataTask.cpp b/libraries/model-baker/src/model-baker/ParseFlowDataTask.cpp new file mode 100644 index 0000000000..6dff4f8c55 --- /dev/null +++ b/libraries/model-baker/src/model-baker/ParseFlowDataTask.cpp @@ -0,0 +1,33 @@ +// +// Created by Luis Cuenca on 5/3/2019 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ParseFlowDataTask.h" + +void ParseFlowDataTask::run(const baker::BakeContextPointer& context, const Input& mapping, Output& output) { + FlowData flowData; + static const QString FLOW_PHYSICS_FIELD = "flowPhysicsData"; + static const QString FLOW_COLLISIONS_FIELD = "flowCollisionsData"; + for (auto mappingIter = mapping.begin(); mappingIter != mapping.end(); mappingIter++) { + if (mappingIter.key() == FLOW_PHYSICS_FIELD || mappingIter.key() == FLOW_COLLISIONS_FIELD) { + QByteArray data = mappingIter.value().toByteArray(); + QJsonObject dataObject = QJsonDocument::fromJson(data).object(); + if (!dataObject.isEmpty() && dataObject.keys().size() == 1) { + QString key = dataObject.keys()[0]; + if (dataObject[key].isObject()) { + QVariantMap dataMap = dataObject[key].toObject().toVariantMap(); + if (mappingIter.key() == FLOW_PHYSICS_FIELD) { + flowData._physicsConfig.insert(key, dataMap); + } else { + flowData._collisionsConfig.insert(key, dataMap); + } + } + } + } + } + output = flowData; +} diff --git a/libraries/model-baker/src/model-baker/ParseFlowDataTask.h b/libraries/model-baker/src/model-baker/ParseFlowDataTask.h new file mode 100644 index 0000000000..7e1bc9fba1 --- /dev/null +++ b/libraries/model-baker/src/model-baker/ParseFlowDataTask.h @@ -0,0 +1,24 @@ +// +// Created by Luis Cuenca on 5/3/2019 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ParseFlowDataTask_h +#define hifi_ParseFlowDataTask_h + +#include +#include "Engine.h" + +class ParseFlowDataTask { +public: + using Input = QVariantHash; + using Output = FlowData; + using JobModel = baker::Job::ModelIO; + + void run(const baker::BakeContextPointer& context, const Input& input, Output& output); +}; + +#endif // hifi_ParseFlowDataTask_h diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index d202afc59f..4d97f43e6a 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -1100,10 +1100,12 @@ void SpatiallyNestable::forEachDescendantTest(const ChildLambdaTest& actor) cons } } -void SpatiallyNestable::locationChanged(bool tellPhysics) { - forEachChild([&](SpatiallyNestablePointer object) { - object->locationChanged(tellPhysics); - }); +void SpatiallyNestable::locationChanged(bool tellPhysics, bool tellChildren) { + if (tellChildren) { + forEachChild([&](SpatiallyNestablePointer object) { + object->locationChanged(tellPhysics, tellChildren); + }); + } } AACube SpatiallyNestable::getMaximumAACube(bool& success) const { diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index 6b709f0352..f52dc4bf8b 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -211,7 +211,7 @@ public: void dump(const QString& prefix = "") const; - virtual void locationChanged(bool tellPhysics = true); // called when a this object's location has changed + virtual void locationChanged(bool tellPhysics = true, bool tellChildren = true); // called when a this object's location has changed virtual void dimensionsChanged() { _queryAACubeSet = false; } // called when a this object's dimensions have changed virtual void parentDeleted() { } // called on children of a deleted parent diff --git a/scripts/developer/debugging/debugAvatarMixer.js b/scripts/developer/debugging/debugAvatarMixer.js index 90f2de13a9..fad4283f7c 100644 --- a/scripts/developer/debugging/debugAvatarMixer.js +++ b/scripts/developer/debugging/debugAvatarMixer.js @@ -19,6 +19,11 @@ Script.include("/~/system/libraries/controllers.js"); var isShowingOverlays = true; var debugOverlays = {}; +var textSizeOverlay = Overlays.addOverlay("text3d", { + position: MyAvatar.position, + lineHeight: 0.1, + visible: false +}); function removeOverlays() { // enumerate the overlays and remove them @@ -31,6 +36,8 @@ function removeOverlays() { } } + Overlays.deleteOverlay(textSizeOverlay); + debugOverlays = {}; } @@ -60,8 +67,6 @@ function updateOverlays() { var overlayPosition = avatar.getJointPosition("Head"); overlayPosition.y += 1.15; - var rows = 8; - var text = avatarID + "\n" +"--- Data from Mixer ---\n" +"All: " + AvatarManager.getAvatarDataRate(avatarID).toFixed(2) + "kbps (" + AvatarManager.getAvatarUpdateRate(avatarID).toFixed(2) + "hz)" + "\n" @@ -85,9 +90,11 @@ function updateOverlays() { //+" SM: " + AvatarManager.getAvatarSimulationRate(avatarID,"skeletonModel").toFixed(2) + "hz \n" +" JD: " + AvatarManager.getAvatarSimulationRate(avatarID,"jointData").toFixed(2) + "hz \n" + var dimensions = Overlays.textSize(textSizeOverlay, text); if (avatarID in debugOverlays) { // keep the overlay above the current position of this avatar Overlays.editOverlay(debugOverlays[avatarID][0], { + dimensions: { x: 1.1 * dimensions.width, y: 0.6 * dimensions.height }, position: overlayPosition, text: text }); @@ -95,15 +102,9 @@ function updateOverlays() { // add the overlay above this avatar var newOverlay = Overlays.addOverlay("text3d", { position: overlayPosition, - dimensions: { - x: 1.25, - y: rows * 0.13 - }, + dimensions: { x: 1.1 * dimensions.width, y: 0.6 * dimensions.height }, lineHeight: 0.1, - font:{size:0.1}, text: text, - size: 1, - scale: 0.4, color: { red: 255, green: 255, blue: 255}, alpha: 1, solid: true,