diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index e394884dc2..d2bcbf2886 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -470,7 +470,6 @@ void EntityServer::startDynamicDomainVerification() { // Delete the entity if it doesn't pass static certificate verification tree->deleteEntity(i.value(), true); } else { - QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest networkRequest; networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); @@ -490,9 +489,13 @@ void EntityServer::startDynamicDomainVerification() { if (networkReply->error() == QNetworkReply::NoError) { if (jsonObject["domain_id"].toString() != thisDomainID) { - qCDebug(entities) << "Entity's cert's domain ID" << jsonObject["domain_id"].toString() - << "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << i.value(); - tree->deleteEntity(i.value(), true); + if (entity->getAge() > (_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS/MSECS_PER_SECOND)) { + qCDebug(entities) << "Entity's cert's domain ID" << jsonObject["domain_id"].toString() + << "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << i.value(); + tree->deleteEntity(i.value(), true); + } else { + qCDebug(entities) << "Entity failed dynamic domain verification, but was created too recently to necessitate deletion:" << i.value(); + } } else { qCDebug(entities) << "Entity passed dynamic domain verification:" << i.value(); } diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index 174f9af7d7..660bc281e3 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -114,7 +114,7 @@ { "from": "Keyboard.W", "when": "!Keyboard.Control", "to": "Actions.LONGITUDINAL_FORWARD" }, { "from": "Keyboard.S", "when": "!Keyboard.Control", "to": "Actions.LONGITUDINAL_BACKWARD" }, { "from": "Keyboard.Shift", "when": ["!Keyboard.Left", "!Keyboard.Right"], "to": "Actions.SPRINT" }, - { "from": "Keyboard.Control", "to": "Actions.VERTICAL_DOWN" }, + { "from": "Keyboard.C", "to": "Actions.VERTICAL_DOWN" }, { "from": "Keyboard.Left", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" }, { "from": "Keyboard.Right", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" }, { "from": "Keyboard.Up", "when": "Application.CameraFirstPerson", "to": "Actions.LONGITUDINAL_FORWARD" }, diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 3152a1eed6..c0cf45d292 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -96,6 +96,7 @@ Rectangle { root.activeView = "checkoutFailure"; UserActivityLogger.commercePurchaseFailure(root.itemId, root.itemAuthor, root.itemPrice, !root.alreadyOwned, result.message); } else { + root.certificateId = result.data.certificate_id; root.itemHref = result.data.download_url; if (result.data.categories.indexOf("Wearables") > -1) { root.itemType = "wearable"; @@ -188,7 +189,7 @@ Rectangle { onItemHrefChanged: { if (root.itemHref.indexOf(".fst") > -1) { root.itemType = "avatar"; - } else if (root.itemHref.indexOf('.json.gz') > -1) { + } else if (root.itemHref.indexOf('.json.gz') > -1 || root.itemHref.indexOf('.content.zip') > -1) { root.itemType = "contentSet"; } else if (root.itemHref.indexOf('.app.json') > -1) { root.itemType = "app"; @@ -772,7 +773,7 @@ Rectangle { lightboxPopup.button1text = "CANCEL"; lightboxPopup.button1method = "root.visible = false;" lightboxPopup.button2text = "CONFIRM"; - lightboxPopup.button2method = "Commerce.replaceContentSet('" + root.itemHref + "');" + + lightboxPopup.button2method = "Commerce.replaceContentSet('" + root.itemHref + "', '" + root.certificateId + "');" + "root.visible = false;rezzedNotifContainer.visible = true; rezzedNotifContainerTimer.start();" + "UserActivityLogger.commerceEntityRezzed('" + root.itemId + "', 'checkout', '" + root.itemType + "');"; lightboxPopup.visible = true; diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index 4cfa61c9ed..2aac269d56 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -564,7 +564,7 @@ Item { onClicked: { Tablet.playSound(TabletEnums.ButtonClick); if (root.itemType === "contentSet") { - sendToPurchases({method: 'showReplaceContentLightbox', itemHref: root.itemHref}); + sendToPurchases({method: 'showReplaceContentLightbox', itemHref: root.itemHref, certID: root.certificateId}); } else if (root.itemType === "avatar") { sendToPurchases({method: 'showChangeAvatarLightbox', itemName: root.itemName, itemHref: root.itemHref}); } else if (root.itemType === "app") { diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index 726e6bd338..cdcfa4a13e 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -486,7 +486,7 @@ Rectangle { lightboxPopup.button1text = "CANCEL"; lightboxPopup.button1method = "root.visible = false;" lightboxPopup.button2text = "CONFIRM"; - lightboxPopup.button2method = "Commerce.replaceContentSet('" + msg.itemHref + "'); root.visible = false;"; + lightboxPopup.button2method = "Commerce.replaceContentSet('" + msg.itemHref + "', '" + msg.certID + "'); root.visible = false;"; lightboxPopup.visible = true; } else if (msg.method === "showChangeAvatarLightbox") { lightboxPopup.titleText = "Change Avatar"; @@ -792,7 +792,7 @@ Rectangle { currentItemType = "avatar"; } else if (currentCategories.indexOf("Wearables") > -1) { currentItemType = "wearable"; - } else if (currentRootFileUrl.endsWith('.json.gz')) { + } else if (currentRootFileUrl.endsWith('.json.gz') || currentRootFileUrl.endsWith('.content.zip')) { currentItemType = "contentSet"; } else if (currentRootFileUrl.endsWith('.app.json')) { currentItemType = "app"; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index bd87ec47d8..249a765d92 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -67,8 +67,8 @@ using namespace std; const float DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES = 30.0f; -const float YAW_SPEED_DEFAULT = 100.0f; // degrees/sec -const float PITCH_SPEED_DEFAULT = 75.0f; // degrees/sec +const float YAW_SPEED_DEFAULT = 75.0f; // degrees/sec +const float PITCH_SPEED_DEFAULT = 50.0f; // degrees/sec const float MAX_BOOST_SPEED = 0.5f * DEFAULT_AVATAR_MAX_WALKING_SPEED; // action motor gets additive boost below this speed const float MIN_AVATAR_SPEED = 0.05f; @@ -2227,7 +2227,7 @@ void MyAvatar::updateActionMotor(float deltaTime) { } float boomChange = getDriveKey(ZOOM); - _boomLength += 2.0f * _boomLength * boomChange + boomChange * boomChange; + _boomLength += 4.0f * _boomLength * boomChange + boomChange * boomChange; _boomLength = glm::clamp(_boomLength, ZOOM_MIN, ZOOM_MAX); } @@ -2760,6 +2760,18 @@ bool MyAvatar::isDriveKeyDisabled(DriveKeys key) const { } } +void MyAvatar::triggerVerticalRecenter() { + _follow.setForceActivateVertical(true); +} + +void MyAvatar::triggerHorizontalRecenter() { + _follow.setForceActivateHorizontal(true); +} + +void MyAvatar::triggerRotationRecenter() { + _follow.setForceActivateRotation(true); +} + // old school meat hook style glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const { glm::vec3 headPosition; @@ -2957,7 +2969,9 @@ void MyAvatar::FollowHelper::decrementTimeRemaining(float dt) { bool MyAvatar::FollowHelper::shouldActivateRotation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const { const float FOLLOW_ROTATION_THRESHOLD = cosf(PI / 6.0f); // 30 degrees glm::vec2 bodyFacing = getFacingDir2D(currentBodyMatrix); + return glm::dot(-myAvatar.getHeadControllerFacingMovingAverage(), bodyFacing) < FOLLOW_ROTATION_THRESHOLD; + } bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const { @@ -2974,6 +2988,7 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar, const float MAX_FORWARD_LEAN = 0.15f; const float MAX_BACKWARD_LEAN = 0.1f; + if (forwardLeanAmount > 0 && forwardLeanAmount > MAX_FORWARD_LEAN) { return true; } else if (forwardLeanAmount < 0 && forwardLeanAmount < -MAX_BACKWARD_LEAN) { @@ -2981,6 +2996,7 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar, } return fabs(lateralLeanAmount) > MAX_LATERAL_LEAN; + } bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const { @@ -2988,6 +3004,7 @@ bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, co const float CYLINDER_BOTTOM = -1.5f; glm::vec3 offset = extractTranslation(desiredBodyMatrix) - extractTranslation(currentBodyMatrix); + return (offset.y > CYLINDER_TOP) || (offset.y < CYLINDER_BOTTOM); } @@ -3005,6 +3022,19 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat if (!isActive(Vertical) && (shouldActivateVertical(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) { activate(Vertical); } + } else { + if (!isActive(Rotation) && getForceActivateRotation()) { + activate(Rotation); + setForceActivateRotation(false); + } + if (!isActive(Horizontal) && getForceActivateHorizontal()) { + activate(Horizontal); + setForceActivateHorizontal(false); + } + if (!isActive(Vertical) && getForceActivateVertical()) { + activate(Vertical); + setForceActivateVertical(false); + } } glm::mat4 desiredWorldMatrix = myAvatar.getSensorToWorldMatrix() * desiredBodyMatrix; @@ -3054,6 +3084,30 @@ glm::mat4 MyAvatar::FollowHelper::postPhysicsUpdate(const MyAvatar& myAvatar, co } } +bool MyAvatar::FollowHelper::getForceActivateRotation() const { + return _forceActivateRotation; +} + +void MyAvatar::FollowHelper::setForceActivateRotation(bool val) { + _forceActivateRotation = val; +} + +bool MyAvatar::FollowHelper::getForceActivateVertical() const { + return _forceActivateVertical; +} + +void MyAvatar::FollowHelper::setForceActivateVertical(bool val) { + _forceActivateVertical = val; +} + +bool MyAvatar::FollowHelper::getForceActivateHorizontal() const { + return _forceActivateHorizontal; +} + +void MyAvatar::FollowHelper::setForceActivateHorizontal(bool val) { + _forceActivateHorizontal = val; +} + float MyAvatar::getAccelerationEnergy() { glm::vec3 velocity = getWorldVelocity(); int changeInVelocity = abs(velocity.length() - priorVelocity.length()); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 4830004375..118f14e71b 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -404,6 +404,32 @@ public: Q_INVOKABLE void enableDriveKey(DriveKeys key); Q_INVOKABLE bool isDriveKeyDisabled(DriveKeys key) const; + /**jsdoc + *The triggerVerticalRecenter function activates one time the recentering + *behaviour in the vertical direction. This call is only takes effect when the property + *MyAvatar.hmdLeanRecenterEnabled is set to false. + *@function MyAvatar.triggerVerticalRecenter + * + */ + + /**jsdoc + *The triggerHorizontalRecenter function activates one time the recentering behaviour + *in the horizontal direction. This call is only takes effect when the property + *MyAvatar.hmdLeanRecenterEnabled is set to false. + *@function MyAvatar.triggerHorizontalRecenter + */ + + /**jsdoc + *The triggerRotationRecenter function activates one time the recentering behaviour + *in the rotation of the root of the avatar. This call is only takes effect when the property + *MyAvatar.hmdLeanRecenterEnabled is set to false. + *@function MyAvatar.triggerRotationRecenter + */ + + Q_INVOKABLE void triggerVerticalRecenter(); + Q_INVOKABLE void triggerHorizontalRecenter(); + Q_INVOKABLE void triggerRotationRecenter(); + eyeContactTarget getEyeContactTarget(); const MyHead* getMyHead() const; @@ -803,6 +829,15 @@ private: bool shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const; void prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& bodySensorMatrix, const glm::mat4& currentBodyMatrix, bool hasDriveInput); glm::mat4 postPhysicsUpdate(const MyAvatar& myAvatar, const glm::mat4& currentBodyMatrix); + bool getForceActivateRotation() const; + void setForceActivateRotation(bool val); + bool getForceActivateVertical() const; + void setForceActivateVertical(bool val); + bool getForceActivateHorizontal() const; + void setForceActivateHorizontal(bool val); + std::atomic _forceActivateRotation{ false }; + std::atomic _forceActivateVertical{ false }; + std::atomic _forceActivateHorizontal{ false }; }; FollowHelper _follow; @@ -839,6 +874,7 @@ private: bool _hmdLeanRecenterEnabled { true }; bool _sprint { false }; + AnimPose _prePhysicsRoomPose; std::mutex _holdActionsMutex; std::vector _holdActions; diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index 0a9e867323..cb06cdfed7 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -293,7 +293,7 @@ void Ledger::account() { // The api/failResponse is called just for the side effect of logging. void Ledger::updateLocationSuccess(QNetworkReply& reply) { apiResponse("updateLocation", reply); } void Ledger::updateLocationFailure(QNetworkReply& reply) { failResponse("updateLocation", reply); } -void Ledger::updateLocation(const QString& asset_id, const QString location, const bool controlledFailure) { +void Ledger::updateLocation(const QString& asset_id, const QString& location, const bool& alsoUpdateSiblings, const bool controlledFailure) { auto wallet = DependencyManager::get(); auto walletScriptingInterface = DependencyManager::get(); uint walletStatus = walletScriptingInterface->getWalletStatus(); @@ -308,6 +308,9 @@ void Ledger::updateLocation(const QString& asset_id, const QString location, con QJsonObject transaction; transaction["certificate_id"] = asset_id; transaction["place_name"] = location; + if (alsoUpdateSiblings) { + transaction["also_update_siblings"] = true; + } QJsonDocument transactionDoc{ transaction }; auto transactionString = transactionDoc.toJson(QJsonDocument::Compact); signedSend("transaction", transactionString, key, "location", "updateLocationSuccess", "updateLocationFailure", controlledFailure); diff --git a/interface/src/commerce/Ledger.h b/interface/src/commerce/Ledger.h index da97206bbc..1ccab9c2b1 100644 --- a/interface/src/commerce/Ledger.h +++ b/interface/src/commerce/Ledger.h @@ -31,7 +31,7 @@ public: void inventory(const QStringList& keys); void history(const QStringList& keys, const int& pageNumber); void account(); - void updateLocation(const QString& asset_id, const QString location, const bool controlledFailure = false); + void updateLocation(const QString& asset_id, const QString& location, const bool& alsoUpdateSiblings = false, const bool controlledFailure = false); void certificateInfo(const QString& certificateId); void transferHfcToNode(const QString& hfc_key, const QString& nodeID, const int& amount, const QString& optionalMessage); void transferHfcToUsername(const QString& hfc_key, const QString& username, const int& amount, const QString& optionalMessage); diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index 8e956249cc..7e91ffaa62 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -190,7 +190,9 @@ void QmlCommerce::transferHfcToUsername(const QString& username, const int& amou ledger->transferHfcToUsername(key, username, amount, optionalMessage); } -void QmlCommerce::replaceContentSet(const QString& itemHref) { +void QmlCommerce::replaceContentSet(const QString& itemHref, const QString& certificateID) { + auto ledger = DependencyManager::get(); + ledger->updateLocation(certificateID, DependencyManager::get()->getPlaceName(), true); qApp->replaceDomainContent(itemHref); QJsonObject messageProperties = { { "status", "SuccessfulRequestToReplaceContent" }, diff --git a/interface/src/commerce/QmlCommerce.h b/interface/src/commerce/QmlCommerce.h index a3a0ebfd32..f58726e1d7 100644 --- a/interface/src/commerce/QmlCommerce.h +++ b/interface/src/commerce/QmlCommerce.h @@ -84,7 +84,7 @@ protected: Q_INVOKABLE void transferHfcToNode(const QString& nodeID, const int& amount, const QString& optionalMessage); Q_INVOKABLE void transferHfcToUsername(const QString& username, const int& amount, const QString& optionalMessage); - Q_INVOKABLE void replaceContentSet(const QString& itemHref); + Q_INVOKABLE void replaceContentSet(const QString& itemHref, const QString& certificateID); Q_INVOKABLE QString getInstalledApps(); Q_INVOKABLE bool installApp(const QString& appHref); diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp index a4fe516590..bd71e47cf0 100644 --- a/interface/src/raypick/LaserPointer.cpp +++ b/interface/src/raypick/LaserPointer.cpp @@ -81,26 +81,14 @@ void LaserPointer::editRenderState(const std::string& state, const QVariant& sta }); } -void LaserPointer::updateRenderStateOverlay(const OverlayID& id, const QVariant& props) { - if (!id.isNull() && props.isValid()) { - QVariantMap propMap = props.toMap(); - propMap.remove("visible"); - qApp->getOverlays().editOverlay(id, propMap); - } -} +PickResultPointer LaserPointer::getVisualPickResult(const PickResultPointer& pickResult) { + PickResultPointer visualPickResult = pickResult; + auto rayPickResult = std::static_pointer_cast(visualPickResult); + IntersectionType type = rayPickResult ? rayPickResult->type : IntersectionType::NONE; -void LaserPointer::updateRenderState(const RenderState& renderState, const IntersectionType type, float distance, const QUuid& objectID, const PickRay& pickRay, bool defaultState) { - if (!renderState.getStartID().isNull()) { - QVariantMap startProps; - startProps.insert("position", vec3toVariant(pickRay.origin)); - startProps.insert("visible", true); - startProps.insert("ignoreRayIntersection", renderState.doesStartIgnoreRays()); - qApp->getOverlays().editOverlay(renderState.getStartID(), startProps); - } - glm::vec3 endVec; - if (((defaultState || !_lockEnd) && _lockEndObject.id.isNull()) || type == IntersectionType::HUD) { - endVec = pickRay.origin + pickRay.direction * distance; - } else { + if (type != IntersectionType::HUD) { + glm::vec3 endVec; + PickRay pickRay = rayPickResult ? PickRay(rayPickResult->pickVariant) : PickRay(); if (!_lockEndObject.id.isNull()) { glm::vec3 pos; glm::quat rot; @@ -122,17 +110,54 @@ void LaserPointer::updateRenderState(const RenderState& renderState, const Inter } const glm::vec3 DEFAULT_REGISTRATION_POINT = glm::vec3(0.5f); endVec = pos + rot * (dim * (DEFAULT_REGISTRATION_POINT - registrationPoint)); - } else { + glm::vec3 direction = endVec - pickRay.origin; + float distance = glm::distance(pickRay.origin, endVec); + glm::vec3 normalizedDirection = glm::normalize(direction); + + rayPickResult->type = _lockEndObject.isOverlay ? IntersectionType::OVERLAY : IntersectionType::ENTITY; + rayPickResult->objectID = _lockEndObject.id; + rayPickResult->intersection = endVec; + rayPickResult->distance = distance; + rayPickResult->surfaceNormal = -normalizedDirection; + rayPickResult->pickVariant["direction"] = vec3toVariant(normalizedDirection); + } else if (type != IntersectionType::NONE && _lockEnd) { if (type == IntersectionType::ENTITY) { - endVec = DependencyManager::get()->getEntityTransform(objectID)[3]; + endVec = DependencyManager::get()->getEntityTransform(rayPickResult->objectID)[3]; } else if (type == IntersectionType::OVERLAY) { - endVec = vec3FromVariant(qApp->getOverlays().getProperty(objectID, "position").value); + endVec = vec3FromVariant(qApp->getOverlays().getProperty(rayPickResult->objectID, "position").value); } else if (type == IntersectionType::AVATAR) { - endVec = DependencyManager::get()->getAvatar(objectID)->getPosition(); + endVec = DependencyManager::get()->getAvatar(rayPickResult->objectID)->getPosition(); } + glm::vec3 direction = endVec - pickRay.origin; + float distance = glm::distance(pickRay.origin, endVec); + glm::vec3 normalizedDirection = glm::normalize(direction); + rayPickResult->intersection = endVec; + rayPickResult->distance = distance; + rayPickResult->surfaceNormal = -normalizedDirection; + rayPickResult->pickVariant["direction"] = vec3toVariant(normalizedDirection); } } - + return visualPickResult; +} + +void LaserPointer::updateRenderStateOverlay(const OverlayID& id, const QVariant& props) { + if (!id.isNull() && props.isValid()) { + QVariantMap propMap = props.toMap(); + propMap.remove("visible"); + qApp->getOverlays().editOverlay(id, propMap); + } +} + +void LaserPointer::updateRenderState(const RenderState& renderState, const IntersectionType type, float distance, const QUuid& objectID, const PickRay& pickRay) { + if (!renderState.getStartID().isNull()) { + QVariantMap startProps; + startProps.insert("position", vec3toVariant(pickRay.origin)); + startProps.insert("visible", true); + startProps.insert("ignoreRayIntersection", renderState.doesStartIgnoreRays()); + qApp->getOverlays().editOverlay(renderState.getStartID(), startProps); + } + glm::vec3 endVec = pickRay.origin + pickRay.direction * distance; + QVariant end = vec3toVariant(endVec); if (!renderState.getPathID().isNull()) { QVariantMap pathProps; @@ -195,15 +220,15 @@ void LaserPointer::updateVisuals(const PickResultPointer& pickResult) { IntersectionType type = rayPickResult ? rayPickResult->type : IntersectionType::NONE; if (_enabled && !_currentRenderState.empty() && _renderStates.find(_currentRenderState) != _renderStates.end() && (type != IntersectionType::NONE || _laserLength > 0.0f || !_lockEndObject.id.isNull())) { - PickRay pickRay(rayPickResult->pickVariant); + PickRay pickRay = rayPickResult ? PickRay(rayPickResult->pickVariant): PickRay(); QUuid uid = rayPickResult->objectID; float distance = _laserLength > 0.0f ? _laserLength : rayPickResult->distance; - updateRenderState(_renderStates[_currentRenderState], type, distance, uid, pickRay, false); + updateRenderState(_renderStates[_currentRenderState], type, distance, uid, pickRay); disableRenderState(_defaultRenderStates[_currentRenderState].second); } else if (_enabled && !_currentRenderState.empty() && _defaultRenderStates.find(_currentRenderState) != _defaultRenderStates.end()) { disableRenderState(_renderStates[_currentRenderState]); PickRay pickRay = rayPickResult ? PickRay(rayPickResult->pickVariant) : PickRay(); - updateRenderState(_defaultRenderStates[_currentRenderState].second, IntersectionType::NONE, _defaultRenderStates[_currentRenderState].first, QUuid(), pickRay, true); + updateRenderState(_defaultRenderStates[_currentRenderState].second, IntersectionType::NONE, _defaultRenderStates[_currentRenderState].first, QUuid(), pickRay); } else if (!_currentRenderState.empty()) { disableRenderState(_renderStates[_currentRenderState]); disableRenderState(_defaultRenderStates[_currentRenderState].second); @@ -386,4 +411,4 @@ glm::vec2 LaserPointer::findPos2D(const PickedObject& pickedObject, const glm::v default: return glm::vec2(NAN); } -} \ No newline at end of file +} diff --git a/interface/src/raypick/LaserPointer.h b/interface/src/raypick/LaserPointer.h index efc5c02729..964881be42 100644 --- a/interface/src/raypick/LaserPointer.h +++ b/interface/src/raypick/LaserPointer.h @@ -82,6 +82,7 @@ public: protected: PointerEvent buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult, const std::string& button = "", bool hover = true) override; + PickResultPointer getVisualPickResult(const PickResultPointer& pickResult) override; PickedObject getHoveredObject(const PickResultPointer& pickResult) override; Pointer::Buttons getPressedButtons(const PickResultPointer& pickResult) override; @@ -102,7 +103,7 @@ private: LockEndObject _lockEndObject; void updateRenderStateOverlay(const OverlayID& id, const QVariant& props); - void updateRenderState(const RenderState& renderState, const IntersectionType type, float distance, const QUuid& objectID, const PickRay& pickRay, bool defaultState); + void updateRenderState(const RenderState& renderState, const IntersectionType type, float distance, const QUuid& objectID, const PickRay& pickRay); void disableRenderState(const RenderState& renderState); struct TriggerState { diff --git a/interface/src/scripting/AssetMappingsScriptingInterface.cpp b/interface/src/scripting/AssetMappingsScriptingInterface.cpp index c9bee659af..c5769ef4bb 100644 --- a/interface/src/scripting/AssetMappingsScriptingInterface.cpp +++ b/interface/src/scripting/AssetMappingsScriptingInterface.cpp @@ -80,7 +80,7 @@ void AssetMappingsScriptingInterface::uploadFile(QString path, QString mapping, auto result = offscreenUi->inputDialog(OffscreenUi::ICON_INFORMATION, "Specify Asset Path", dropEvent ? dropHelpText : helpText, mapping); - if (!result.isValid()) { + if (!result.isValid() || result.toString() == "") { completedCallback.call({ -1 }); return; } diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp index 2089b8a378..9bb02d678d 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp @@ -55,6 +55,7 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::gl::GLBackend::do_setUniformBuffer), (&::gpu::gl::GLBackend::do_setResourceBuffer), (&::gpu::gl::GLBackend::do_setResourceTexture), + (&::gpu::gl::GLBackend::do_setResourceTextureTable), (&::gpu::gl::GLBackend::do_setResourceFramebufferSwapChainTexture), (&::gpu::gl::GLBackend::do_setFramebuffer), diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h index f2e2271552..6355137a19 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h @@ -40,7 +40,6 @@ #endif - // Let these be configured by the one define picked above #ifdef GPU_STEREO_TECHNIQUE_DOUBLED_SIMPLE #define GPU_STEREO_DRAWCALL_DOUBLED @@ -102,6 +101,12 @@ public: static const int MAX_NUM_RESOURCE_TEXTURES = 16; size_t getMaxNumResourceTextures() const { return MAX_NUM_RESOURCE_TEXTURES; } + // Texture Tables offers 2 dedicated slot (taken from the ubo slots) + static const int MAX_NUM_RESOURCE_TABLE_TEXTURES = 2; + static const int RESOURCE_TABLE_TEXTURE_SLOT_OFFSET = TRANSFORM_CAMERA_SLOT + 1; + size_t getMaxNumResourceTextureTables() const { return MAX_NUM_RESOURCE_TABLE_TEXTURES; } + + // Draw Stage virtual void do_draw(const Batch& batch, size_t paramOffset) = 0; virtual void do_drawIndexed(const Batch& batch, size_t paramOffset) = 0; @@ -130,6 +135,7 @@ public: // Resource Stage virtual void do_setResourceBuffer(const Batch& batch, size_t paramOffset) final; virtual void do_setResourceTexture(const Batch& batch, size_t paramOffset) final; + virtual void do_setResourceTextureTable(const Batch& batch, size_t paramOffset); virtual void do_setResourceFramebufferSwapChainTexture(const Batch& batch, size_t paramOffset) final; // Pipeline Stage @@ -230,6 +236,10 @@ protected: void recycle() const override; + // FIXME instead of a single flag, create a features struct similar to + // https://www.khronos.org/registry/vulkan/specs/1.0/man/html/VkPhysicalDeviceFeatures.html + virtual bool supportsBindless() const { return false; } + static const size_t INVALID_OFFSET = (size_t)-1; bool _inRenderTransferPass { false }; int32_t _uboAlignment { 0 }; @@ -389,6 +399,10 @@ protected: virtual bool bindResourceBuffer(uint32_t slot, BufferPointer& buffer) = 0; virtual void releaseResourceBuffer(uint32_t slot) = 0; + // Helper function that provides common code used by do_setResourceTexture and + // do_setResourceTextureTable (in non-bindless mode) + void bindResourceTexture(uint32_t slot, const TexturePointer& texture); + // update resource cache and do the gl unbind call with the current gpu::Texture cached at slot s void releaseResourceTexture(uint32_t slot); diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackendPipeline.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendPipeline.cpp index 237b8bc1e9..58fcc51605 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackendPipeline.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendPipeline.cpp @@ -1,4 +1,4 @@ -// +// // GLBackendPipeline.cpp // libraries/gpu/src/gpu // @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "GLBackend.h" +#include + #include "GLShared.h" #include "GLPipeline.h" #include "GLShader.h" @@ -247,8 +249,11 @@ void GLBackend::do_setResourceTexture(const Batch& batch, size_t paramOffset) { return; } - TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint); + const auto& resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint); + bindResourceTexture(slot, resourceTexture); +} +void GLBackend::bindResourceTexture(uint32_t slot, const TexturePointer& resourceTexture) { if (!resourceTexture) { releaseResourceTexture(slot); return; @@ -306,6 +311,19 @@ void GLBackend::setResourceTexture(unsigned int slot, const TexturePointer& reso } } +void GLBackend::do_setResourceTextureTable(const Batch& batch, size_t paramOffset) { + const auto& textureTablePointer = batch._textureTables.get(batch._params[paramOffset]._uint); + if (!textureTablePointer) { + return; + } + + const auto& textureTable = *textureTablePointer; + const auto& textures = textureTable.getTextures(); + for (GLuint slot = 0; slot < textures.size(); ++slot) { + bindResourceTexture(slot, textures[slot]); + } +} + int GLBackend::ResourceStageState::findEmptyTextureSlot() const { // start from the end of the slots, try to find an empty one that can be used for (auto i = MAX_NUM_RESOURCE_TEXTURES - 1; i > 0; i--) { @@ -315,4 +333,3 @@ int GLBackend::ResourceStageState::findEmptyTextureSlot() const { } return -1; } - diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp index 35dc8a3fbd..0df228ddc4 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp @@ -78,6 +78,11 @@ R"SHADER( #endif }; +// TextureTable specific defines +static const std::string textureTableVersion { + "#extension GL_ARB_bindless_texture : require\n#define GPU_TEXTURE_TABLE_BINDLESS\n" +}; + // Versions specific of the shader static const std::array VERSION_DEFINES { { "", @@ -96,9 +101,9 @@ GLShader* GLBackend::compileBackendShader(const Shader& shader, const Shader::Co auto& shaderObject = shaderObjects[version]; std::string shaderDefines = getBackendShaderHeader() + "\n" + + (supportsBindless() ? textureTableVersion : "\n") + DOMAIN_DEFINES[shader.getType()] + "\n" + VERSION_DEFINES[version]; - if (handler) { bool retest = true; std::string currentSrc = shaderSource; @@ -120,7 +125,7 @@ GLShader* GLBackend::compileBackendShader(const Shader& shader, const Shader::Co compilationLogs[version].compiled = ::gl::compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, compilationLogs[version].message); } - if (!compilationLogs[version].compiled) { + if (!compilationLogs[version].compiled) { qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Shader didn't compile:\n" << compilationLogs[version].message.c_str(); shader.setCompilationLogs(compilationLogs); return nullptr; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 61c3da391b..722ea63a77 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -167,7 +167,6 @@ void GL41Texture::syncSampler() const { glTexParameteri(_target, GL_TEXTURE_WRAP_R, WRAP_MODES[sampler.getWrapModeW()]); glTexParameterfv(_target, GL_TEXTURE_BORDER_COLOR, (const float*)&sampler.getBorderColor()); - glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, (uint16)sampler.getMipOffset()); glTexParameterf(_target, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip()); glTexParameterf(_target, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip())); @@ -206,9 +205,6 @@ void GL41FixedAllocationTexture::allocateStorage() const { void GL41FixedAllocationTexture::syncSampler() const { Parent::syncSampler(); const Sampler& sampler = _gpuObject.getSampler(); - auto baseMip = std::max(sampler.getMipOffset(), sampler.getMinMip()); - - glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, baseMip); glTexParameterf(_target, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip()); glTexParameterf(_target, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.0f : sampler.getMaxMip())); } @@ -610,4 +606,3 @@ GL41ResourceTexture::GL41ResourceTexture(const std::weak_ptr& backend GL41ResourceTexture::~GL41ResourceTexture() { } - diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index b90c0b1857..c23a83eaf9 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -16,9 +16,11 @@ #include #include +#include #define INCREMENTAL_TRANSFER 0 #define GPU_SSBO_TRANSFORM_OBJECT 1 +#define GPU_BINDLESS_TEXTURES 0 namespace gpu { namespace gl45 { @@ -31,6 +33,9 @@ class GL45Backend : public GLBackend { friend class Context; public: +#if GPU_BINDLESS_TEXTURES + virtual bool supportsBindless() const override { return true; } +#endif #ifdef GPU_SSBO_TRANSFORM_OBJECT static const GLint TRANSFORM_OBJECT_SLOT { 14 }; // SSBO binding slot @@ -58,8 +63,62 @@ public: void generateMips() const override; Size copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const override; void syncSampler() const override; + +#if GPU_BINDLESS_TEXTURES + bool isBindless() const { + return _bindless.operator bool(); + } + + struct Bindless { + uint64_t handle{ 0 }; + uint32_t minMip{ 0 }; + uint32_t sampler{ 0 }; + + bool operator==(const Bindless& other) const { + return handle == other.handle && minMip == other.minMip && sampler == other.sampler; + } + + bool operator!=(const Bindless& other) const { + return !(*this == other); + } + + operator bool() const { + return handle != 0; + } + }; + + virtual const Bindless& getBindless() const; + void releaseBindless() const; + void recreateBindless() const; + private: + mutable Bindless _bindless; +#endif + + static Sampler getInvalidSampler(); + + // This stores the texture handle (64 bits) in xy, the min mip available in z, and the sampler ID in w + mutable Sampler _cachedSampler{ getInvalidSampler() }; }; +#if GPU_BINDLESS_TEXTURES + class GL45TextureTable : public GLObject { + static GLuint allocate(); + using Parent = GLObject; + public: + using BindlessArray = std::array; + + GL45TextureTable(const std::weak_ptr& backend, const TextureTable& texture); + ~GL45TextureTable(); + + void update(const BindlessArray& newHandles); + + // FIXME instead of making a buffer for each table, there should be a global buffer of all materials + // and we should store an offset into that buffer + BindlessArray _handles; + }; +#endif + + // // Textures that have fixed allocation sizes and cannot be managed at runtime // @@ -74,6 +133,7 @@ public: protected: Size size() const override { return _size; } + void allocateStorage() const; void syncSampler() const override; const Size _size { 0 }; @@ -104,7 +164,6 @@ public: friend class GL45Backend; using PromoteLambda = std::function; - protected: GL45VariableAllocationTexture(const std::weak_ptr& backend, const Texture& texture); ~GL45VariableAllocationTexture(); @@ -114,6 +173,9 @@ public: Size copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const override; void copyTextureMipsInGPUMem(GLuint srcId, GLuint destId, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) override; +#if GPU_BINDLESS_TEXTURES + virtual const Bindless& getBindless() const override; +#endif }; class GL45ResourceTexture : public GL45VariableAllocationTexture { @@ -182,6 +244,7 @@ protected: GLuint getQueryID(const QueryPointer& query) override; GLQuery* syncGPUObject(const Query& query) override; + // Draw Stage void do_draw(const Batch& batch, size_t paramOffset) override; void do_drawIndexed(const Batch& batch, size_t paramOffset) override; @@ -213,6 +276,12 @@ protected: // Texture Management Stage void initTextureManagementStage() override; + +#if GPU_BINDLESS_TEXTURES + GL45TextureTable* syncGPUObject(const TextureTablePointer& textureTable); + // Resource stage + void do_setResourceTextureTable(const Batch& batch, size_t paramOffset) override; +#endif }; } } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp index c17a4a1524..8de60a7921 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp @@ -165,6 +165,11 @@ void GL45Backend::makeProgramBindings(ShaderObject& shaderObject) { shaderObject.transformCameraSlot = gpu::TRANSFORM_CAMERA_SLOT; } + loc = glGetUniformBlockIndex(glprogram, "gpu_resourceTextureTable0"); + if (loc >= 0) { + glUniformBlockBinding(glprogram, loc, RESOURCE_TABLE_TEXTURE_SLOT_OFFSET); + } + (void)CHECK_GL_ERROR(); } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 5e64e1845e..5a5d701150 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -20,14 +20,17 @@ #include #include +#include +#include #include using namespace gpu; using namespace gpu::gl; using namespace gpu::gl45; -#define SPARSE_PAGE_SIZE_OVERHEAD_ESTIMATE 1.3f #define MAX_RESOURCE_TEXTURES_PER_FRAME 2 +#define FORCE_STRICT_TEXTURE 0 +#define ENABLE_SPARSE_TEXTURE 0 GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texturePointer) { if (!texturePointer) { @@ -51,14 +54,18 @@ GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texturePointer) { object = new GL45AttachmentTexture(shared_from_this(), texture); break; +#if FORCE_STRICT_TEXTURE + case TextureUsageType::RESOURCE: +#endif case TextureUsageType::STRICT_RESOURCE: qCDebug(gpugllogging) << "Strict texture " << texture.source().c_str(); object = new GL45StrictResourceTexture(shared_from_this(), texture); break; +#if !FORCE_STRICT_TEXTURE case TextureUsageType::RESOURCE: { if (GL45VariableAllocationTexture::_frameTexturesCreated < MAX_RESOURCE_TEXTURES_PER_FRAME) { -#if 0 +#if ENABLE_SPARSE_TEXTURE if (isTextureManagementSparseEnabled() && GL45Texture::isSparseEligible(texture)) { object = new GL45SparseResourceTexture(shared_from_this(), texture); } else { @@ -76,7 +83,7 @@ GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texturePointer) { } break; } - +#endif default: Q_UNREACHABLE(); } @@ -114,6 +121,61 @@ void GL45Backend::initTextureManagementStage() { using GL45Texture = GL45Backend::GL45Texture; + +class GLSamplerCache { +public: + GLuint getGLSampler(const Sampler& sampler) { + if (0 == _samplerCache.count(sampler)) { + GLuint result = 0; + glGenSamplers(1, &result); + const auto& fm = GLTexture::FILTER_MODES[sampler.getFilter()]; + glSamplerParameteri(result, GL_TEXTURE_MIN_FILTER, fm.minFilter); + glSamplerParameteri(result, GL_TEXTURE_MAG_FILTER, fm.magFilter); + if (sampler.doComparison()) { + glSamplerParameteri(result, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE_ARB); + glSamplerParameteri(result, GL_TEXTURE_COMPARE_FUNC, COMPARISON_TO_GL[sampler.getComparisonFunction()]); + } else { + glSamplerParameteri(result, GL_TEXTURE_COMPARE_MODE, GL_NONE); + } + + glSamplerParameteri(result, GL_TEXTURE_WRAP_S, GLTexture::WRAP_MODES[sampler.getWrapModeU()]); + glSamplerParameteri(result, GL_TEXTURE_WRAP_T, GLTexture::WRAP_MODES[sampler.getWrapModeV()]); + glSamplerParameteri(result, GL_TEXTURE_WRAP_R, GLTexture::WRAP_MODES[sampler.getWrapModeW()]); + + glSamplerParameterf(result, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy()); + glSamplerParameterfv(result, GL_TEXTURE_BORDER_COLOR, (const float*)&sampler.getBorderColor()); + + glSamplerParameterf(result, GL_TEXTURE_MIN_LOD, sampler.getMinMip()); + glSamplerParameterf(result, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip())); + _samplerCache[sampler] = result; + return result; + } + + return _samplerCache[sampler]; + } + + void releaseGLSampler(GLuint sampler) { + // NO OP + } + +private: + std::unordered_map _samplerCache; +}; + +static GLSamplerCache SAMPLER_CACHE; + + +Sampler GL45Texture::getInvalidSampler() { + static Sampler INVALID_SAMPLER; + static std::once_flag once; + std::call_once(once, [] { + Sampler::Desc invalidDesc; + invalidDesc._borderColor = vec4(-1.0f); + INVALID_SAMPLER = Sampler(invalidDesc); + }); + return INVALID_SAMPLER; +} + GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& texture) : GLTexture(backend, texture, allocate(texture)) { } @@ -121,10 +183,10 @@ GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& GLuint GL45Texture::allocate(const Texture& texture) { GLuint result; glCreateTextures(getGLTextureType(texture), 1, &result); -#ifdef DEBUG - auto source = texture.source(); - glObjectLabel(GL_TEXTURE, result, (GLsizei)source.length(), source.data()); -#endif + if (::gl::Context::enableDebugLogger()) { + auto source = texture.source(); + glObjectLabel(GL_TEXTURE, result, (GLsizei)source.length(), source.data()); + } return result; } @@ -189,8 +251,50 @@ Size GL45Texture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const return amountCopied; } +#if GPU_BINDLESS_TEXTURES +void GL45Texture::releaseBindless() const { + // Release the old handler + SAMPLER_CACHE.releaseGLSampler(_bindless.sampler); + glMakeTextureHandleNonResidentARB(_bindless.handle); + _bindless = Bindless(); +} + +void GL45Texture::recreateBindless() const { + if (isBindless()) { + releaseBindless(); + } else { + // Once a texture is about to become bindless, it's base mip level MUST be set to 0 + glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, 0); + } + + _bindless.sampler = SAMPLER_CACHE.getGLSampler(_cachedSampler); + _bindless.handle = glGetTextureSamplerHandleARB(_id, _bindless.sampler); + glMakeTextureHandleResidentARB(_bindless.handle); +} + +const GL45Texture::Bindless& GL45Texture::getBindless() const { + if (!_bindless) { + recreateBindless(); + } + return _bindless; +} +#endif + + void GL45Texture::syncSampler() const { const Sampler& sampler = _gpuObject.getSampler(); + if (_cachedSampler == sampler) { + return; + } + + _cachedSampler = sampler; + +#if GPU_BINDLESS_TEXTURES + if (isBindless()) { + recreateBindless(); + return; + } +#endif const auto& fm = FILTER_MODES[sampler.getFilter()]; glTextureParameteri(_id, GL_TEXTURE_MIN_FILTER, fm.minFilter); @@ -214,6 +318,8 @@ void GL45Texture::syncSampler() const { glTextureParameterf(_id, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip())); } +// Fixed allocation textures, used for strict resources & framebuffer attachments + using GL45FixedAllocationTexture = GL45Backend::GL45FixedAllocationTexture; GL45FixedAllocationTexture::GL45FixedAllocationTexture(const std::weak_ptr& backend, const Texture& texture) : GL45Texture(backend, texture), _size(texture.evalTotalSize()) { @@ -238,8 +344,6 @@ void GL45FixedAllocationTexture::allocateStorage() const { void GL45FixedAllocationTexture::syncSampler() const { Parent::syncSampler(); const Sampler& sampler = _gpuObject.getSampler(); - auto baseMip = std::max(sampler.getMipOffset(), sampler.getMinMip()); - glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, baseMip); glTextureParameterf(_id, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip()); glTextureParameterf(_id, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip())); } @@ -275,6 +379,9 @@ GL45StrictResourceTexture::GL45StrictResourceTexture(const std::weak_ptr& backend, const TextureTable& textureTable) + : Parent(backend, textureTable, allocate()){ + Backend::setGPUObject(textureTable, this); + // FIXME include these in overall buffer storage reporting + glNamedBufferStorage(_id, sizeof(uvec4) * TextureTable::COUNT, nullptr, GL_DYNAMIC_STORAGE_BIT); +} + +void GL45TextureTable::update(const BindlessArray& handles) { + if (_handles != handles) { + _handles = handles; + // FIXME include these in overall buffer storage reporting + // FIXME use a single shared buffer for bindless data + glNamedBufferSubData(_id, 0, sizeof(GL45Texture::Bindless) * TextureTable::COUNT, &_handles[0]); + } +} + +GL45TextureTable::~GL45TextureTable() { + if (_id) { + auto backend = _backend.lock(); + if (backend) { + // FIXME include these in overall buffer storage reporting + backend->releaseBuffer(_id, 0); + } + } +} + +GL45TextureTable* GL45Backend::syncGPUObject(const TextureTablePointer& textureTablePointer) { + const auto& textureTable = *textureTablePointer; + + // Find the target handles + auto defaultTextures = gpu::TextureTable::getDefault()->getTextures(); + auto textures = textureTable.getTextures(); + GL45TextureTable::BindlessArray handles{}; + for (size_t i = 0; i < textures.size(); ++i) { + auto texture = textures[i]; + if (!texture) { + texture = defaultTextures[i]; + } + if (!texture) { + continue; + } + // FIXME what if we have a non-transferrable texture here? + auto gltexture = (GL45Texture*)syncGPUObject(texture); + if (!gltexture) { + continue; + } + handles[i] = gltexture->getBindless(); + } + + // If the object hasn't been created, or the object definition is out of date, drop and re-create + GL45TextureTable* object = Backend::getGPUObject(textureTable); + + if (!object) { + object = new GL45TextureTable(shared_from_this(), textureTable); + } + + object->update(handles); + + return object; +} + +void GL45Backend::do_setResourceTextureTable(const Batch& batch, size_t paramOffset) { + auto textureTablePointer = batch._textureTables.get(batch._params[paramOffset]._uint); + if (!textureTablePointer) { + return; + } + + auto slot = batch._params[paramOffset + 1]._uint; + GL45TextureTable* glTextureTable = syncGPUObject(textureTablePointer); + if (glTextureTable) { + glBindBufferBase(GL_UNIFORM_BUFFER, slot + GLBackend::RESOURCE_TABLE_TEXTURE_SLOT_OFFSET, glTextureTable->_id); + } +} +#endif diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index d2d7b2bf55..08d077605d 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -27,6 +27,7 @@ using namespace gpu; using namespace gpu::gl; using namespace gpu::gl45; +using GL45Texture = GL45Backend::GL45Texture; using GL45VariableAllocationTexture = GL45Backend::GL45VariableAllocationTexture; GL45VariableAllocationTexture::GL45VariableAllocationTexture(const std::weak_ptr& backend, const Texture& texture) : GL45Texture(backend, texture) { @@ -40,6 +41,17 @@ GL45VariableAllocationTexture::~GL45VariableAllocationTexture() { Backend::textureResourcePopulatedGPUMemSize.update(_populatedSize, 0); } +#if GPU_BINDLESS_TEXTURES +const GL45Texture::Bindless& GL45VariableAllocationTexture::getBindless() const { + // The parent call may re-initialize the _bindless member, so we need to call it first + const auto& result = Parent::getBindless(); + // Make sure the referenced structure has the correct minimum available mip value + _bindless.minMip = _populatedMip - _allocatedMip; + // Now return the result + return result; +} +#endif + Size GL45VariableAllocationTexture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const { Size amountCopied = 0; amountCopied = Parent::copyMipFaceLinesFromTexture(mip, face, size, yOffset, internalFormat, format, type, sourceSize, sourcePointer); @@ -127,7 +139,13 @@ Size GL45ResourceTexture::copyMipsFromTexture() { void GL45ResourceTexture::syncSampler() const { Parent::syncSampler(); +#if GPU_BINDLESS_TEXTURES + if (!isBindless()) { + glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, _populatedMip - _allocatedMip); + } +#else glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, _populatedMip - _allocatedMip); +#endif } void GL45ResourceTexture::promote() { @@ -137,6 +155,13 @@ void GL45ResourceTexture::promote() { uint16_t targetAllocatedMip = _allocatedMip - std::min(_allocatedMip, 2); targetAllocatedMip = std::max(_minAllocatedMip, targetAllocatedMip); +#if GPU_BINDLESS_TEXTURES + bool bindless = isBindless(); + if (bindless) { + releaseBindless(); + } +#endif + GLuint oldId = _id; auto oldSize = _size; uint16_t oldAllocatedMip = _allocatedMip; @@ -150,10 +175,17 @@ void GL45ResourceTexture::promote() { // copy pre-existing mips copyTextureMipsInGPUMem(oldId, _id, oldAllocatedMip, _allocatedMip, _populatedMip); +#if GPU_BINDLESS_TEXTURES + if (bindless) { + getBindless(); + } +#endif + // destroy the old texture glDeleteTextures(1, &oldId); // Update sampler + _cachedSampler = getInvalidSampler(); syncSampler(); // update the memory usage @@ -170,6 +202,13 @@ void GL45ResourceTexture::demote() { auto oldSize = _size; auto oldPopulatedMip = _populatedMip; +#if GPU_BINDLESS_TEXTURES + bool bindless = isBindless(); + if (bindless) { + releaseBindless(); + } +#endif + // allocate new texture const_cast(_id) = allocate(_gpuObject); uint16_t oldAllocatedMip = _allocatedMip; @@ -179,10 +218,17 @@ void GL45ResourceTexture::demote() { // copy pre-existing mips copyTextureMipsInGPUMem(oldId, _id, oldAllocatedMip, _allocatedMip, _populatedMip); +#if GPU_BINDLESS_TEXTURES + if (bindless) { + getBindless(); + } +#endif + // destroy the old texture glDeleteTextures(1, &oldId); // Update sampler + _cachedSampler = getInvalidSampler(); syncSampler(); // update the memory usage diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index 31bbfdd708..db4941c163 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -75,6 +75,7 @@ Batch::Batch(const Batch& batch_) { _buffers._items.swap(batch._buffers._items); _textures._items.swap(batch._textures._items); + _textureTables._items.swap(batch._textureTables._items); _streamFormats._items.swap(batch._streamFormats._items); _transforms._items.swap(batch._transforms._items); _pipelines._items.swap(batch._pipelines._items); @@ -113,6 +114,7 @@ void Batch::clear() { _data.clear(); _buffers.clear(); _textures.clear(); + _textureTables.clear(); _streamFormats.clear(); _transforms.clear(); _pipelines.clear(); @@ -337,6 +339,12 @@ void Batch::setResourceTexture(uint32 slot, const TextureView& view) { setResourceTexture(slot, view._texture); } +void Batch::setResourceTextureTable(const TextureTablePointer& textureTable, uint32 slot) { + ADD_COMMAND(setResourceTextureTable); + _params.emplace_back(_textureTables.cache(textureTable)); + _params.emplace_back(slot); +} + void Batch::setResourceFramebufferSwapChainTexture(uint32 slot, const FramebufferSwapChainPointer& framebuffer, unsigned int swapChainIndex, unsigned int renderBufferSlot) { ADD_COMMAND(setResourceFramebufferSwapChainTexture); diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index 43fdb623e3..96b234295d 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -187,6 +187,7 @@ public: void setResourceTexture(uint32 slot, const TexturePointer& texture); void setResourceTexture(uint32 slot, const TextureView& view); // not a command, just a shortcut from a TextureView + void setResourceTextureTable(const TextureTablePointer& table, uint32 slot = 0); void setResourceFramebufferSwapChainTexture(uint32 slot, const FramebufferSwapChainPointer& framebuffer, unsigned int swpaChainIndex, unsigned int renderBufferSlot = 0U); // not a command, just a shortcut from a TextureView // Ouput Stage @@ -302,6 +303,7 @@ public: COMMAND_setUniformBuffer, COMMAND_setResourceBuffer, COMMAND_setResourceTexture, + COMMAND_setResourceTextureTable, COMMAND_setResourceFramebufferSwapChainTexture, COMMAND_setFramebuffer, @@ -409,9 +411,10 @@ public: return offset; } - Data get(uint32 offset) const { + const Data& get(uint32 offset) const { if (offset >= _items.size()) { - return Data(); + static const Data EMPTY; + return EMPTY; } return (_items.data() + offset)->_data; } @@ -424,6 +427,7 @@ public: typedef Cache::Vector BufferCaches; typedef Cache::Vector TextureCaches; + typedef Cache::Vector TextureTableCaches; typedef Cache::Vector StreamFormatCaches; typedef Cache::Vector TransformCaches; typedef Cache::Vector PipelineCaches; @@ -479,6 +483,7 @@ public: BufferCaches _buffers; TextureCaches _textures; + TextureTableCaches _textureTables; StreamFormatCaches _streamFormats; TransformCaches _transforms; PipelineCaches _pipelines; diff --git a/libraries/gpu/src/gpu/Forward.h b/libraries/gpu/src/gpu/Forward.h index 8100efafaf..987788c29b 100644 --- a/libraries/gpu/src/gpu/Forward.h +++ b/libraries/gpu/src/gpu/Forward.h @@ -91,6 +91,8 @@ namespace gpu { using Textures = std::vector; class TextureView; using TextureViews = std::vector; + class TextureTable; + using TextureTablePointer = std::shared_ptr; struct StereoState { bool isStereo() const { diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index a03b894e0d..6793948d13 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -17,8 +17,10 @@ #include #include +#include #include #include +#include #include "Forward.h" #include "Resource.h" #include "Metric.h" @@ -126,12 +128,23 @@ public: uint8 _wrapModeV = WRAP_REPEAT; uint8 _wrapModeW = WRAP_REPEAT; - uint8 _mipOffset = 0; uint8 _minMip = 0; uint8 _maxMip = MAX_MIP_LEVEL; Desc() {} Desc(const Filter filter, const WrapMode wrap = WRAP_REPEAT) : _filter(filter), _wrapModeU(wrap), _wrapModeV(wrap), _wrapModeW(wrap) {} + + bool operator==(const Desc& other) const { + return _borderColor == other._borderColor && + _maxAnisotropy == other._maxAnisotropy && + _filter == other._filter && + _comparisonFunc == other._comparisonFunc && + _wrapModeU == other._wrapModeU && + _wrapModeV == other._wrapModeV && + _wrapModeW == other._wrapModeW && + _minMip == other._minMip && + _maxMip == other._maxMip; + } }; Sampler() {} @@ -151,11 +164,17 @@ public: ComparisonFunction getComparisonFunction() const { return ComparisonFunction(_desc._comparisonFunc); } bool doComparison() const { return getComparisonFunction() != ALWAYS; } - uint8 getMipOffset() const { return _desc._mipOffset; } uint8 getMinMip() const { return _desc._minMip; } uint8 getMaxMip() const { return _desc._maxMip; } const Desc& getDesc() const { return _desc; } + + bool operator==(const Sampler& other) const { + return _desc == other._desc; + } + bool operator!=(const Sampler& other) const { + return !(*this == other); + } protected: Desc _desc; }; @@ -666,6 +685,17 @@ typedef std::shared_ptr< TextureSource > TextureSourcePointer; }; +namespace std { + template<> struct hash { + size_t operator()(const gpu::Sampler& sampler) const noexcept { + size_t result = 0; + const auto& desc = sampler.getDesc(); + hash_combine(result, desc._comparisonFunc, desc._filter, desc._maxAnisotropy, desc._maxMip, desc._minMip, desc._wrapModeU, desc._wrapModeV, desc._wrapModeW); + return result; + } + }; +} + Q_DECLARE_METATYPE(gpu::TexturePointer) #endif diff --git a/libraries/gpu/src/gpu/TextureTable.cpp b/libraries/gpu/src/gpu/TextureTable.cpp new file mode 100644 index 0000000000..0c3a43808b --- /dev/null +++ b/libraries/gpu/src/gpu/TextureTable.cpp @@ -0,0 +1,54 @@ +// +// Created by Bradley Austin Davis on 2017/01/25 +// Copyright 2013-2017 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 "TextureTable.h" +#include "Texture.h" + +#include +using namespace gpu; + + +const size_t TextureTable::COUNT{ TEXTURE_TABLE_COUNT }; + +TextureTable::TextureTable() { } + +TextureTable::TextureTable(const std::initializer_list& textures) { + auto max = std::min(COUNT, textures.size()); + auto itr = textures.begin(); + size_t index = 0; + while (itr != textures.end() && index < max) { + setTexture(index, *itr); + ++index; + } +} + +TextureTable::TextureTable(const Array& textures) : _textures(textures) { +} + +void TextureTable::setTexture(size_t index, const TexturePointer& texturePointer) { + if (index >= COUNT || _textures[index] == texturePointer) { + return; + } + { + Lock lock(_mutex); + ++_stamp; + _textures[index] = texturePointer; + } +} + +void TextureTable::setTexture(size_t index, const TextureView& textureView) { + setTexture(index, textureView._texture); +} + +TextureTable::Array TextureTable::getTextures() const { + Array result; + { + Lock lock(_mutex); + result = _textures; + } + return result; +} diff --git a/libraries/gpu/src/gpu/TextureTable.h b/libraries/gpu/src/gpu/TextureTable.h new file mode 100644 index 0000000000..794b8535d0 --- /dev/null +++ b/libraries/gpu/src/gpu/TextureTable.h @@ -0,0 +1,44 @@ +// +// Created by Bradley Austin Davis on 2017/01/25 +// Copyright 2013-2017 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_gpu_TextureTable_h +#define hifi_gpu_TextureTable_h + +#include "Forward.h" + +#include + +#define TEXTURE_TABLE_COUNT 8 + +namespace gpu { + +class TextureTable { +public: + static const size_t COUNT; + using Array = std::array; + TextureTable(); + TextureTable(const std::initializer_list& textures); + TextureTable(const Array& textures); + + // Only for gpu::Context + const GPUObjectPointer gpuObject{}; + + void setTexture(size_t index, const TexturePointer& texturePointer); + void setTexture(size_t index, const TextureView& texturePointer); + + Array getTextures() const; + Stamp getStamp() const { return _stamp; } + +private: + mutable Mutex _mutex; + Array _textures; + Stamp _stamp{ 1 }; +}; + +} + +#endif diff --git a/libraries/gpu/src/gpu/TextureTable.slh b/libraries/gpu/src/gpu/TextureTable.slh new file mode 100644 index 0000000000..f2d89e753b --- /dev/null +++ b/libraries/gpu/src/gpu/TextureTable.slh @@ -0,0 +1,38 @@ + +<@if not GPU_TEXTURE_TABLE_SLH@> +<@def GPU_TEXTURE_TABLE_SLH@> + +#ifdef GPU_TEXTURE_TABLE_BINDLESS +#define GPU_TEXTURE_TABLE_MAX_NUM_TEXTURES 8 + +struct GPUTextureTable { + uvec4 _textures[GPU_TEXTURE_TABLE_MAX_NUM_TEXTURES]; +}; + +#define TextureTable(index, name) layout (std140) uniform gpu_resourceTextureTable##index { GPUTextureTable name; } + +#define tableTex(name, slot) sampler2D(name._textures[slot].xy) +#define tableTexMinLod(name, slot) float(name._textures[slot].z) + +#define tableTexValue(name, slot, uv) \ + tableTexValueLod(tableTex(matTex, albedoMap), tableTexMinLod(matTex, albedoMap), uv) + +vec4 tableTexValueLod(sampler2D sampler, float minLod, vec2 uv) { + float queryLod = textureQueryLod(sampler, uv).x; + queryLod = max(minLod, queryLod); + return textureLod(sampler, uv, queryLod); +} + +#else + +#endif + +<@endif@> diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 754bdca445..803acbd961 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -504,6 +504,8 @@ TexturePointer Texture::build(const ktx::KTXDescriptor& descriptor) { gpuktxKeyValue._usage = Texture::Usage::Builder().withColor().withAlpha().build(); } + auto samplerDesc = gpuktxKeyValue._samplerDesc; + samplerDesc._maxMip = gpu::Sampler::MAX_MIP_LEVEL; auto texture = create(gpuktxKeyValue._usageType, type, texelFormat, @@ -513,7 +515,7 @@ TexturePointer Texture::build(const ktx::KTXDescriptor& descriptor) { 1, // num Samples header.getNumberOfSlices(), header.getNumberOfLevels(), - gpuktxKeyValue._samplerDesc); + samplerDesc); texture->setUsage(gpuktxKeyValue._usage); // Assing the mips availables diff --git a/libraries/graphics/src/graphics/Material.h b/libraries/graphics/src/graphics/Material.h index f9e83a0324..fd3f12e865 100755 --- a/libraries/graphics/src/graphics/Material.h +++ b/libraries/graphics/src/graphics/Material.h @@ -20,6 +20,7 @@ #include #include +#include class Transform; @@ -361,6 +362,8 @@ public: const std::string& getModel() const { return _model; } void setModel(const std::string& model) { _model = model; } + const gpu::TextureTablePointer& getTextureTable() const { return _textureTable; } + protected: std::string _name { "" }; @@ -368,6 +371,7 @@ private: mutable MaterialKey _key; mutable UniformBufferView _schemaBuffer; mutable UniformBufferView _texMapArrayBuffer; + mutable gpu::TextureTablePointer _textureTable{ std::make_shared() }; TextureMaps _textureMaps; diff --git a/libraries/pointers/src/Pointer.cpp b/libraries/pointers/src/Pointer.cpp index 5307e17355..031baece5f 100644 --- a/libraries/pointers/src/Pointer.cpp +++ b/libraries/pointers/src/Pointer.cpp @@ -68,8 +68,9 @@ void Pointer::update(unsigned int pointerID) { // This only needs to be a read lock because update won't change any of the properties that can be modified from scripts withReadLock([&] { auto pickResult = getPrevPickResult(); - updateVisuals(pickResult); - generatePointerEvents(pointerID, pickResult); + auto visualPickResult = getVisualPickResult(pickResult); + updateVisuals(visualPickResult); + generatePointerEvents(pointerID, visualPickResult); }); } diff --git a/libraries/pointers/src/Pointer.h b/libraries/pointers/src/Pointer.h index 3197c80cad..0c842dbd88 100644 --- a/libraries/pointers/src/Pointer.h +++ b/libraries/pointers/src/Pointer.h @@ -89,6 +89,7 @@ protected: virtual bool shouldHover(const PickResultPointer& pickResult) { return true; } virtual bool shouldTrigger(const PickResultPointer& pickResult) { return true; } + virtual PickResultPointer getVisualPickResult(const PickResultPointer& pickResult) { return pickResult; }; static const float POINTER_MOVE_DELAY; static const float TOUCH_PRESS_TO_MOVE_DEADSPOT_SQUARED; diff --git a/libraries/render-utils/src/MaterialTextures.slh b/libraries/render-utils/src/MaterialTextures.slh index 26bb85a853..26e809e4ba 100644 --- a/libraries/render-utils/src/MaterialTextures.slh +++ b/libraries/render-utils/src/MaterialTextures.slh @@ -48,6 +48,74 @@ TexMapArray getTexMapArray() { <@func declareMaterialTextures(withAlbedo, withRoughness, withNormal, withMetallic, withEmissive, withOcclusion, withScattering)@> +<@include gpu/TextureTable.slh@> + +#ifdef GPU_TEXTURE_TABLE_BINDLESS + +TextureTable(0, matTex); + + +<@if withAlbedo@> +#define albedoMap 0 +vec4 fetchAlbedoMap(vec2 uv) { + return tableTexValue(matTex, albedoMap, uv); +} +<@endif@> + +<@if withRoughness@> +#define roughnessMap 4 +float fetchRoughnessMap(vec2 uv) { + return tableTexValue(matTex, roughnessMap, uv).r; +} +<@endif@> + +<@if withNormal@> +#define normalMap 1 +vec3 fetchNormalMap(vec2 uv) { + return tableTexValue(matTex, normalMap, uv).xyz; +} +<@endif@> + +<@if withMetallic@> +#define metallicMap 2 +float fetchMetallicMap(vec2 uv) { + return tableTexValue(matTex, metallicMap, uv).r; +} +<@endif@> + +<@if withEmissive@> +#define emissiveMap 3 +vec3 fetchEmissiveMap(vec2 uv) { + return tableTexValue(matTex, emissiveMap, uv).rgb; +} +<@endif@> + +<@if withOcclusion@> +#define occlusionMap 5 +float fetchOcclusionMap(vec2 uv) { + return tableTexValue(matTex, occlusionMap, uv).r; +} +<@endif@> + +<@if withScattering@> +#define scatteringMap 6 +float fetchScatteringMap(vec2 uv) { + float scattering = texture(tableTex(matTex, scatteringMap), uv).r; // boolean scattering for now + return max(((scattering - 0.1) / 0.9), 0.0); + return tableTexValue(matTex, scatteringMap, uv).r; // boolean scattering for now +} +<@endif@> + +#else + <@if withAlbedo@> uniform sampler2D albedoMap; vec4 fetchAlbedoMap(vec2 uv) { @@ -102,6 +170,8 @@ float fetchScatteringMap(vec2 uv) { } <@endif@> +#endif + <@endfunc@> diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 5c64d4b584..72a4b7e0a4 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -152,7 +152,7 @@ void MeshPartPayload::bindMesh(gpu::Batch& batch) { batch.setInputStream(0, _drawMesh->getVertexStream()); } -void MeshPartPayload::bindTransform(gpu::Batch& batch, RenderArgs::RenderMode renderMode) const { + void MeshPartPayload::bindTransform(gpu::Batch& batch, RenderArgs::RenderMode renderMode) const { batch.setModelTransform(_drawTransform); } diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index aca8439547..ef4b0a5683 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -612,7 +612,8 @@ void initZPassPipelines(ShapePlumber& shapePlumber, gpu::StatePointer state) { #include "RenderPipelines.h" #include -void RenderPipelines::bindMaterial(graphics::MaterialPointer material, gpu::Batch& batch, bool enableTextures) { +// FIXME find a better way to setup the default textures +void RenderPipelines::bindMaterial(const graphics::MaterialPointer& material, gpu::Batch& batch, bool enableTextures) { if (!material) { return; } @@ -630,84 +631,65 @@ void RenderPipelines::bindMaterial(graphics::MaterialPointer material, gpu::Batc numUnlit++; } - if (!enableTextures) { - batch.setResourceTexture(ShapePipeline::Slot::ALBEDO, textureCache->getWhiteTexture()); - batch.setResourceTexture(ShapePipeline::Slot::MAP::ROUGHNESS, textureCache->getWhiteTexture()); - batch.setResourceTexture(ShapePipeline::Slot::MAP::NORMAL, textureCache->getBlueTexture()); - batch.setResourceTexture(ShapePipeline::Slot::MAP::METALLIC, textureCache->getBlackTexture()); - batch.setResourceTexture(ShapePipeline::Slot::MAP::OCCLUSION, textureCache->getWhiteTexture()); - batch.setResourceTexture(ShapePipeline::Slot::MAP::SCATTERING, textureCache->getWhiteTexture()); - batch.setResourceTexture(ShapePipeline::Slot::MAP::EMISSIVE_LIGHTMAP, textureCache->getBlackTexture()); - return; - } + const auto& drawMaterialTextures = material->getTextureTable(); // Albedo if (materialKey.isAlbedoMap()) { auto itr = textureMaps.find(graphics::MaterialKey::ALBEDO_MAP); - if (itr != textureMaps.end() && itr->second->isDefined()) { - batch.setResourceTexture(ShapePipeline::Slot::ALBEDO, itr->second->getTextureView()); + if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) { + drawMaterialTextures->setTexture(ShapePipeline::Slot::ALBEDO, itr->second->getTextureView()); } else { - batch.setResourceTexture(ShapePipeline::Slot::ALBEDO, textureCache->getGrayTexture()); + drawMaterialTextures->setTexture(ShapePipeline::Slot::ALBEDO, textureCache->getWhiteTexture()); } } // Roughness map if (materialKey.isRoughnessMap()) { auto itr = textureMaps.find(graphics::MaterialKey::ROUGHNESS_MAP); - if (itr != textureMaps.end() && itr->second->isDefined()) { - batch.setResourceTexture(ShapePipeline::Slot::MAP::ROUGHNESS, itr->second->getTextureView()); - - // texcoord are assumed to be the same has albedo + if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) { + drawMaterialTextures->setTexture(ShapePipeline::Slot::ROUGHNESS, itr->second->getTextureView()); } else { - batch.setResourceTexture(ShapePipeline::Slot::MAP::ROUGHNESS, textureCache->getWhiteTexture()); + drawMaterialTextures->setTexture(ShapePipeline::Slot::ROUGHNESS, textureCache->getWhiteTexture()); } } // Normal map if (materialKey.isNormalMap()) { auto itr = textureMaps.find(graphics::MaterialKey::NORMAL_MAP); - if (itr != textureMaps.end() && itr->second->isDefined()) { - batch.setResourceTexture(ShapePipeline::Slot::MAP::NORMAL, itr->second->getTextureView()); - - // texcoord are assumed to be the same has albedo + if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) { + drawMaterialTextures->setTexture(ShapePipeline::Slot::NORMAL, itr->second->getTextureView()); } else { - batch.setResourceTexture(ShapePipeline::Slot::MAP::NORMAL, textureCache->getBlueTexture()); + drawMaterialTextures->setTexture(ShapePipeline::Slot::NORMAL, textureCache->getBlueTexture()); } } // Metallic map if (materialKey.isMetallicMap()) { auto itr = textureMaps.find(graphics::MaterialKey::METALLIC_MAP); - if (itr != textureMaps.end() && itr->second->isDefined()) { - batch.setResourceTexture(ShapePipeline::Slot::MAP::METALLIC, itr->second->getTextureView()); - - // texcoord are assumed to be the same has albedo + if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) { + drawMaterialTextures->setTexture(ShapePipeline::Slot::METALLIC, itr->second->getTextureView()); } else { - batch.setResourceTexture(ShapePipeline::Slot::MAP::METALLIC, textureCache->getBlackTexture()); + drawMaterialTextures->setTexture(ShapePipeline::Slot::METALLIC, textureCache->getBlackTexture()); } } // Occlusion map if (materialKey.isOcclusionMap()) { auto itr = textureMaps.find(graphics::MaterialKey::OCCLUSION_MAP); - if (itr != textureMaps.end() && itr->second->isDefined()) { - batch.setResourceTexture(ShapePipeline::Slot::MAP::OCCLUSION, itr->second->getTextureView()); - - // texcoord are assumed to be the same has albedo + if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) { + drawMaterialTextures->setTexture(ShapePipeline::Slot::OCCLUSION, itr->second->getTextureView()); } else { - batch.setResourceTexture(ShapePipeline::Slot::MAP::OCCLUSION, textureCache->getWhiteTexture()); + drawMaterialTextures->setTexture(ShapePipeline::Slot::OCCLUSION, textureCache->getWhiteTexture()); } } // Scattering map if (materialKey.isScatteringMap()) { auto itr = textureMaps.find(graphics::MaterialKey::SCATTERING_MAP); - if (itr != textureMaps.end() && itr->second->isDefined()) { - batch.setResourceTexture(ShapePipeline::Slot::MAP::SCATTERING, itr->second->getTextureView()); - - // texcoord are assumed to be the same has albedo + if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) { + drawMaterialTextures->setTexture(ShapePipeline::Slot::SCATTERING, itr->second->getTextureView()); } else { - batch.setResourceTexture(ShapePipeline::Slot::MAP::SCATTERING, textureCache->getWhiteTexture()); + drawMaterialTextures->setTexture(ShapePipeline::Slot::SCATTERING, textureCache->getWhiteTexture()); } } @@ -715,18 +697,19 @@ void RenderPipelines::bindMaterial(graphics::MaterialPointer material, gpu::Batc if (materialKey.isLightmapMap()) { auto itr = textureMaps.find(graphics::MaterialKey::LIGHTMAP_MAP); - if (itr != textureMaps.end() && itr->second->isDefined()) { - batch.setResourceTexture(ShapePipeline::Slot::MAP::EMISSIVE_LIGHTMAP, itr->second->getTextureView()); + if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) { + drawMaterialTextures->setTexture(ShapePipeline::Slot::EMISSIVE_LIGHTMAP, itr->second->getTextureView()); } else { - batch.setResourceTexture(ShapePipeline::Slot::MAP::EMISSIVE_LIGHTMAP, textureCache->getGrayTexture()); + drawMaterialTextures->setTexture(ShapePipeline::Slot::EMISSIVE_LIGHTMAP, textureCache->getGrayTexture()); } } else if (materialKey.isEmissiveMap()) { auto itr = textureMaps.find(graphics::MaterialKey::EMISSIVE_MAP); - - if (itr != textureMaps.end() && itr->second->isDefined()) { - batch.setResourceTexture(ShapePipeline::Slot::MAP::EMISSIVE_LIGHTMAP, itr->second->getTextureView()); + if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) { + drawMaterialTextures->setTexture(ShapePipeline::Slot::EMISSIVE_LIGHTMAP, itr->second->getTextureView()); } else { - batch.setResourceTexture(ShapePipeline::Slot::MAP::EMISSIVE_LIGHTMAP, textureCache->getBlackTexture()); + drawMaterialTextures->setTexture(ShapePipeline::Slot::EMISSIVE_LIGHTMAP, textureCache->getBlackTexture()); } } + + batch.setResourceTextureTable(material->getTextureTable()); } diff --git a/libraries/render-utils/src/RenderPipelines.h b/libraries/render-utils/src/RenderPipelines.h index 9b9ad2c001..b7d22bc72d 100644 --- a/libraries/render-utils/src/RenderPipelines.h +++ b/libraries/render-utils/src/RenderPipelines.h @@ -15,7 +15,7 @@ class RenderPipelines { public: - static void bindMaterial(graphics::MaterialPointer material, gpu::Batch& batch, bool enableTextures); + static void bindMaterial(const graphics::MaterialPointer& material, gpu::Batch& batch, bool enableTextures); }; diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index 07450e54ba..53272c324d 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -14,7 +14,7 @@ PICK_MAX_DISTANCE, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, ensureDynamic, getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD, Reticle, Overlays, isPointingAtUI - Picks, makeLaserLockInfo Xform, makeLaserParams, AddressManager, getEntityParents + Picks, makeLaserLockInfo Xform, makeLaserParams, AddressManager, getEntityParents, Selection */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -467,8 +467,10 @@ Script.include("/~/system/libraries/Xform.js"); Script.clearTimeout(this.contextOverlayTimer); } this.contextOverlayTimer = false; - if (entityID !== this.entityWithContextOverlay) { + if (entityID === this.entityWithContextOverlay) { this.destroyContextOverlay(); + } else { + Selection.removeFromSelectedItemsList("contextOverlayHighlightList", "entity", entityID); } var targetEntity = this.targetObject.getTargetEntity();