diff --git a/interface/resources/qml/hifi/avatarPackager/AvatarDoctorDiagnose.qml b/interface/resources/qml/hifi/avatarPackager/AvatarDoctorDiagnose.qml
new file mode 100644
index 0000000000..302930dee0
--- /dev/null
+++ b/interface/resources/qml/hifi/avatarPackager/AvatarDoctorDiagnose.qml
@@ -0,0 +1,127 @@
+import QtQuick 2.0
+
+import "../../controlsUit" 1.0 as HifiControls
+import "../../stylesUit" 1.0
+
+Item {
+ id: root
+
+ visible: false
+
+ property var avatarDoctor: null
+ property var errors: []
+
+ property int minimumDiagnoseTimeMS: 1000
+
+ signal doneDiagnosing
+
+ onVisibleChanged: {
+ if (root.avatarDoctor !== null) {
+ root.avatarDoctor.complete.disconnect(_private.avatarDoctorComplete);
+ root.avatarDoctor = null;
+ }
+ if (doneTimer.running) {
+ doneTimer.stop();
+ }
+
+ if (!root.visible) {
+ return;
+ }
+
+ root.avatarDoctor = AvatarPackagerCore.currentAvatarProject.diagnose();
+ root.avatarDoctor.complete.connect(this, _private.avatarDoctorComplete);
+ _private.startTime = Date.now();
+ root.avatarDoctor.startDiagnosing();
+ }
+
+ QtObject {
+ id: _private
+ property real startTime: 0
+
+ function avatarDoctorComplete(errors) {
+ if (!root.visible) {
+ return;
+ }
+
+ console.warn("avatarDoctor.complete " + JSON.stringify(errors));
+ root.errors = errors;
+ AvatarPackagerCore.currentAvatarProject.hasErrors = errors.length > 0;
+ AvatarPackagerCore.addCurrentProjectToRecentProjects();
+
+ let timeSpendDiagnosingMS = Date.now() - _private.startTime;
+ let timeLeftMS = root.minimumDiagnoseTimeMS - timeSpendDiagnosingMS;
+ doneTimer.interval = timeLeftMS < 0 ? 0 : timeLeftMS;
+ doneTimer.start();
+ }
+ }
+
+ Timer {
+ id: doneTimer
+ repeat: false
+ running: false
+ onTriggered: {
+ doneDiagnosing();
+ }
+ }
+
+ property var footer: Item {
+ anchors.fill: parent
+ anchors.rightMargin: 17
+ HifiControls.Button {
+ id: cancelButton
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.right: parent.right
+ height: 30
+ width: 133
+ text: qsTr("Cancel")
+ onClicked: {
+ avatarPackager.state = AvatarPackagerState.main;
+ }
+ }
+ }
+
+ LoadingCircle {
+ id: loadingCircle
+ anchors {
+ top: parent.top
+ topMargin: 46
+ horizontalCenter: parent.horizontalCenter
+ }
+ width: 163
+ height: 163
+ }
+
+ RalewayRegular {
+ id: testingPackageTitle
+
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ top: loadingCircle.bottom
+ topMargin: 5
+ }
+
+ text: "Testing package for errors"
+ size: 28
+ color: "white"
+ }
+
+ RalewayRegular {
+ id: testingPackageText
+
+ anchors {
+ top: testingPackageTitle.bottom
+ topMargin: 26
+ left: parent.left
+ leftMargin: 21
+ right: parent.right
+ rightMargin: 16
+ }
+
+ text: "We are trying to find errors in your project so you can quickly understand and resolve them."
+ size: 21
+ color: "white"
+ lineHeight: 33
+ lineHeightMode: Text.FixedHeight
+ wrapMode: Text.Wrap
+ }
+}
diff --git a/interface/resources/qml/hifi/avatarPackager/AvatarDoctorErrorReport.qml b/interface/resources/qml/hifi/avatarPackager/AvatarDoctorErrorReport.qml
new file mode 100644
index 0000000000..73c5e34d13
--- /dev/null
+++ b/interface/resources/qml/hifi/avatarPackager/AvatarDoctorErrorReport.qml
@@ -0,0 +1,104 @@
+import QtQuick 2.0
+
+import "../../controlsUit" 1.0 as HifiControls
+import "../../stylesUit" 1.0
+
+Item {
+ id: errorReport
+
+ visible: false
+
+ property alias errors: errorRepeater.model
+
+ property var footer: Item {
+ anchors.fill: parent
+ anchors.rightMargin: 17
+ HifiControls.Button {
+ id: tryAgainButton
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.right: continueButton.left
+ anchors.rightMargin: 22
+ height: 40
+ width: 134
+ text: qsTr("Try Again")
+ onClicked: {
+ avatarPackager.state = AvatarPackagerState.avatarDoctorDiagnose;
+ }
+ }
+
+ HifiControls.Button {
+ id: continueButton
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.right: parent.right
+ height: 40
+ width: 133
+ text: qsTr("Continue")
+ color: hifi.buttons.blue
+ colorScheme: root.colorScheme
+ onClicked: {
+ avatarPackager.state = AvatarPackagerState.project;
+ }
+ }
+ }
+
+ HiFiGlyphs {
+ id: errorReportIcon
+ text: hifi.glyphs.alert
+ size: 315
+ color: "#EA4C5F"
+ anchors {
+ top: parent.top
+ topMargin: -20
+ horizontalCenter: parent.horizontalCenter
+ }
+ }
+
+ Column {
+ anchors {
+ left: parent.left
+ right: parent.right
+ bottom: parent.bottom
+ top: errorReportIcon.bottom
+ topMargin: -40
+ leftMargin: 13
+ rightMargin: 13
+ }
+ spacing: 7
+
+ Repeater {
+ id: errorRepeater
+ Item {
+ height: 37
+ width: parent.width
+
+ HiFiGlyphs {
+ id: errorIcon
+ text: hifi.glyphs.alert
+ size: 56
+ color: "#EA4C5F"
+ anchors {
+ top: parent.top
+ left: parent.left
+ leftMargin: -5
+ }
+ }
+
+ RalewayRegular {
+ id: errorLink
+ anchors {
+ top: parent.top
+ topMargin: 5
+ left: errorIcon.right
+ right: parent.right
+ }
+ color: "#00B4EF"
+ linkColor: "#00B4EF"
+ size: 28
+ text: "" + modelData.message + ""
+ onLinkActivated: Qt.openUrlExternally(modelData.url)
+ elide: Text.ElideRight
+ }
+ }
+ }
+ }
+}
diff --git a/interface/resources/qml/hifi/avatarPackager/AvatarPackagerApp.qml b/interface/resources/qml/hifi/avatarPackager/AvatarPackagerApp.qml
index b4293d5eee..8afc60fd90 100644
--- a/interface/resources/qml/hifi/avatarPackager/AvatarPackagerApp.qml
+++ b/interface/resources/qml/hifi/avatarPackager/AvatarPackagerApp.qml
@@ -143,6 +143,18 @@ Item {
PropertyChanges { target: createAvatarProject; visible: true }
PropertyChanges { target: avatarPackagerFooter; content: createAvatarProject.footer }
},
+ State {
+ name: AvatarPackagerState.avatarDoctorDiagnose
+ PropertyChanges { target: avatarPackagerHeader; title: AvatarPackagerCore.currentAvatarProject.name }
+ PropertyChanges { target: avatarDoctorDiagnose; visible: true }
+ PropertyChanges { target: avatarPackagerFooter; content: avatarDoctorDiagnose.footer }
+ },
+ State {
+ name: AvatarPackagerState.avatarDoctorErrorReport
+ PropertyChanges { target: avatarPackagerHeader; title: AvatarPackagerCore.currentAvatarProject.name }
+ PropertyChanges { target: avatarDoctorErrorReport; visible: true }
+ PropertyChanges { target: avatarPackagerFooter; content: avatarDoctorErrorReport.footer }
+ },
State {
name: AvatarPackagerState.project
PropertyChanges { target: avatarPackagerHeader; title: AvatarPackagerCore.currentAvatarProject.name; canRename: true }
@@ -168,7 +180,7 @@ Item {
return status;
}
avatarProject.reset();
- avatarPackager.state = AvatarPackagerState.project;
+ avatarPackager.state = AvatarPackagerState.avatarDoctorDiagnose;
return status;
}
@@ -242,6 +254,23 @@ Item {
color: "#404040"
}
+ AvatarDoctorDiagnose {
+ id: avatarDoctorDiagnose
+ anchors.fill: parent
+ onErrorsChanged: {
+ avatarDoctorErrorReport.errors = avatarDoctorDiagnose.errors;
+ }
+ onDoneDiagnosing: {
+ avatarPackager.state = avatarDoctorDiagnose.errors.length > 0 ? AvatarPackagerState.avatarDoctorErrorReport
+ : AvatarPackagerState.project;
+ }
+ }
+
+ AvatarDoctorErrorReport {
+ id: avatarDoctorErrorReport
+ anchors.fill: parent
+ }
+
AvatarProject {
id: avatarProject
colorScheme: root.colorScheme
@@ -383,6 +412,7 @@ Item {
title: modelData.name
path: modelData.projectPath
onOpen: avatarPackager.openProject(modelData.path)
+ hasError: modelData.hadErrors
}
}
}
diff --git a/interface/resources/qml/hifi/avatarPackager/AvatarPackagerState.qml b/interface/resources/qml/hifi/avatarPackager/AvatarPackagerState.qml
index c81173a080..4a5abbb04b 100644
--- a/interface/resources/qml/hifi/avatarPackager/AvatarPackagerState.qml
+++ b/interface/resources/qml/hifi/avatarPackager/AvatarPackagerState.qml
@@ -7,4 +7,6 @@ Item {
readonly property string project: "project"
readonly property string createProject: "createProject"
readonly property string projectUpload: "projectUpload"
+ readonly property string avatarDoctorDiagnose: "avatarDoctorDiagnose"
+ readonly property string avatarDoctorErrorReport: "avatarDoctorErrorReport"
}
diff --git a/interface/resources/qml/hifi/avatarPackager/AvatarProjectCard.qml b/interface/resources/qml/hifi/avatarPackager/AvatarProjectCard.qml
index 25222c814c..21d0683fb1 100644
--- a/interface/resources/qml/hifi/avatarPackager/AvatarProjectCard.qml
+++ b/interface/resources/qml/hifi/avatarPackager/AvatarProjectCard.qml
@@ -12,6 +12,7 @@ Item {
property alias title: title.text
property alias path: path.text
+ property alias hasError: errorIcon.visible
property color textColor: "#E3E3E3"
property color hoverTextColor: "#121212"
@@ -54,7 +55,7 @@ Item {
RalewayBold {
id: title
- elide: "ElideRight"
+ elide: Text.ElideRight
anchors {
top: parent.top
topMargin: 13
@@ -76,12 +77,24 @@ Item {
right: background.right
rightMargin: 16
}
- elide: "ElideLeft"
+ elide: Text.ElideLeft
horizontalAlignment: Text.AlignRight
text: ""
size: 20
}
+ HiFiGlyphs {
+ id: errorIcon
+ visible: false
+ text: hifi.glyphs.alert
+ size: 56
+ color: "#EA4C5F"
+ anchors {
+ top: parent.top
+ right: parent.right
+ }
+ }
+
MouseArea {
id: mouseArea
anchors.fill: parent
diff --git a/interface/resources/qml/hifi/avatarPackager/CreateAvatarProject.qml b/interface/resources/qml/hifi/avatarPackager/CreateAvatarProject.qml
index c299417c27..a0149b118f 100644
--- a/interface/resources/qml/hifi/avatarPackager/CreateAvatarProject.qml
+++ b/interface/resources/qml/hifi/avatarPackager/CreateAvatarProject.qml
@@ -32,7 +32,7 @@ Item {
return;
}
avatarProject.reset();
- avatarPackager.state = AvatarPackagerState.project;
+ avatarPackager.state = AvatarPackagerState.avatarDoctorDiagnose;
}
}
}
diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml
index 2d5f77f006..9589c842e6 100644
--- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml
+++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml
@@ -33,15 +33,19 @@ Rectangle {
property bool balanceReceived: false;
property bool availableUpdatesReceived: false;
property bool itemInfoReceived: false;
+ property bool dataReady: itemInfoReceived && ownershipStatusReceived && balanceReceived && availableUpdatesReceived;
property string baseItemName: "";
property string itemName;
property string itemId;
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 string certificateId;
property double balanceAfterPurchase;
- property bool alreadyOwned: false;
+ property bool alreadyOwned: false; // Including proofs
property int itemPrice: -1;
property bool isCertified;
property string itemType: "unknown";
@@ -56,6 +60,8 @@ Rectangle {
property string referrer;
property bool isInstalled;
property bool isUpdating;
+ property string availability: "available";
+ property string creator: "";
property string baseAppURL;
property int currentUpdatesPage: 1;
// Style
@@ -434,7 +440,7 @@ Rectangle {
anchors.top: parent.top;
anchors.left: itemPreviewImage.right;
anchors.leftMargin: 12;
- anchors.right: itemPriceContainer.left;
+ anchors.right: parent.right;
anchors.rightMargin: 8;
height: 30;
// Style
@@ -449,21 +455,22 @@ Rectangle {
Item {
id: itemPriceContainer;
// Anchors
- anchors.top: parent.top;
- anchors.right: parent.right;
+ anchors.top: itemNameText.bottom;
+ anchors.topMargin: 8;
+ anchors.left: itemNameText.left;
height: 30;
- width: itemPriceTextLabel.width + itemPriceText.width + 20;
+ width: itemPriceText.width + 20;
- // "HFC" balance label
+ // "HFC" label
HiFiGlyphs {
id: itemPriceTextLabel;
- visible: !(root.isUpdating && root.itemEdition > 0) && (root.itemPrice > 0);
+ visible: !isTradingIn && (root.itemPrice > 0);
text: hifi.glyphs.hfc;
// Size
size: 30;
// Anchors
- anchors.right: itemPriceText.left;
- anchors.rightMargin: 4;
+ anchors.right: parent.right;
+ //anchors.rightMargin: 4;
anchors.top: parent.top;
anchors.topMargin: 0;
width: paintedWidth;
@@ -473,13 +480,15 @@ Rectangle {
}
FiraSansSemiBold {
id: itemPriceText;
- text: (root.isUpdating && root.itemEdition > 0) ? "FREE\nUPDATE" : ((root.itemPrice === -1) ? "--" : ((root.itemPrice > 0) ? root.itemPrice : "FREE"));
+ text: isTradingIn ? "FREE\nUPDATE" :
+ (isStocking ? "Free for creator" :
+ ((root.itemPrice === -1) ? "--" : ((root.itemPrice > 0) ? root.itemPrice : "FREE")));
// Text size
- size: (root.isUpdating && root.itemEdition > 0) ? 20 : 26;
+ size: isTradingIn ? 20 : 26;
// Anchors
anchors.top: parent.top;
- anchors.right: parent.right;
- anchors.rightMargin: 16;
+ anchors.left: itemPriceTextLabel.visible ? itemPriceTextLabel.right : parent.left;
+ anchors.leftMargin: 4;
width: paintedWidth;
height: paintedHeight;
// Style
@@ -571,7 +580,7 @@ Rectangle {
// "View in Inventory" button
HifiControlsUit.Button {
id: viewInMyPurchasesButton;
- visible: false;
+ visible: isCertified && dataReady && (isUpdating ? !hasSomethingToTradeIn : alreadyOwned);
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.light;
anchors.top: buyTextContainer.visible ? buyTextContainer.bottom : checkoutActionButtonsContainer.top;
@@ -592,8 +601,8 @@ Rectangle {
// "Buy" button
HifiControlsUit.Button {
id: buyButton;
- visible: !((root.itemType === "avatar" || root.itemType === "app") && viewInMyPurchasesButton.visible)
- enabled: (root.balanceAfterPurchase >= 0 && ownershipStatusReceived && balanceReceived && availableUpdatesReceived) || (!root.isCertified) || root.isUpdating;
+ visible: isTradingIn || !alreadyOwned || isStocking || !(root.itemType === "avatar" || root.itemType === "app");
+ enabled: (root.balanceAfterPurchase >= 0 && dataReady) || (!root.isCertified) || root.isUpdating;
color: viewInMyPurchasesButton.visible ? hifi.buttons.white : hifi.buttons.blue;
colorScheme: hifi.colorSchemes.light;
anchors.top: viewInMyPurchasesButton.visible ? viewInMyPurchasesButton.bottom :
@@ -602,10 +611,15 @@ Rectangle {
height: 50;
anchors.left: parent.left;
anchors.right: parent.right;
- text: (root.isUpdating && root.itemEdition > 0) ? "CONFIRM UPDATE" : (((root.isCertified) ? ((ownershipStatusReceived && balanceReceived && availableUpdatesReceived) ?
- ((viewInMyPurchasesButton.visible && !root.isUpdating) ? "Get It Again" : "Confirm") : "--") : "Get Item"));
+ text: isTradingIn ?
+ "CONFIRM UPDATE" :
+ (((root.isCertified) ?
+ (dataReady ?
+ ((viewInMyPurchasesButton.visible && !root.isUpdating) ? "Get It Again" : "Confirm") :
+ "--") :
+ "Get Item"));
onClicked: {
- if (root.isUpdating && root.itemEdition > 0) {
+ if (isTradingIn) {
// If we're updating an app, the existing app needs to be uninstalled.
// This call will fail/return `false` if the app isn't installed, but that's OK.
if (root.itemType === "app") {
@@ -1063,7 +1077,11 @@ Rectangle {
buyButton.color = hifi.buttons.red;
root.shouldBuyWithControlledFailure = true;
} else {
- buyButton.text = (root.isCertified ? ((ownershipStatusReceived && balanceReceived && availableUpdatesReceived) ? (root.alreadyOwned ? "Buy Another" : "Buy"): "--") : "Get Item");
+ buyButton.text = (root.isCertified ?
+ (dataReady ?
+ (root.alreadyOwned ? "Buy Another" : "Buy") :
+ "--") :
+ "Get Item");
buyButton.color = hifi.buttons.blue;
root.shouldBuyWithControlledFailure = false;
}
@@ -1091,6 +1109,8 @@ Rectangle {
root.itemPrice = result.data.cost;
root.itemAuthor = result.data.creator;
root.itemType = result.data.item_type || "unknown";
+ root.availability = result.data.availability;
+ root.creator = result.data.creator;
if (root.itemType === "unknown") {
root.itemHref = result.data.review_url;
} else {
@@ -1139,7 +1159,7 @@ Rectangle {
signal sendToScript(var message);
function canBuyAgain() {
- return (root.itemType === "entity" || root.itemType === "wearable" || root.itemType === "contentSet" || root.itemType === "unknown");
+ return root.itemType === "entity" || root.itemType === "wearable" || root.itemType === "contentSet" || root.itemType === "unknown" || isStocking;
}
function handleContentSets() {
@@ -1185,29 +1205,23 @@ Rectangle {
function refreshBuyUI() {
if (root.isCertified) {
- if (root.ownershipStatusReceived && root.balanceReceived && root.availableUpdatesReceived) {
+ if (dataReady) {
buyText.text = "";
// If the user IS on the checkout page for the updated version of an owned item...
if (root.isUpdating) {
// If the user HAS already selected a specific edition to update...
- if (root.itemEdition > 0) {
+ if (hasSomethingToTradeIn) {
buyText.text = "By pressing \"Confirm Update\", you agree to trade in your old item for the updated item that replaces it.";
buyTextContainer.color = "#FFFFFF";
buyTextContainer.border.color = "#FFFFFF";
// Else if the user HAS NOT selected a specific edition to update...
} else {
- viewInMyPurchasesButton.visible = true;
-
handleBuyAgainLogic();
}
// If the user IS NOT on the checkout page for the updated verison of an owned item...
// (i.e. they are checking out an item "normally")
} else {
- if (root.alreadyOwned) {
- viewInMyPurchasesButton.visible = true;
- }
-
handleBuyAgainLogic();
}
} else {
diff --git a/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
index cdb8368296..ca6838efea 100644
--- a/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
+++ b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
@@ -121,7 +121,7 @@ Rectangle {
marketplaceItem.description = result.data.description;
marketplaceItem.attributions = result.data.attributions;
marketplaceItem.license = result.data.license;
- marketplaceItem.available = result.data.availability === "available";
+ marketplaceItem.availability = result.data.availability;
marketplaceItem.created_at = result.data.created_at;
marketplaceItemScrollView.contentHeight = marketplaceItemContent.height;
itemsList.visible = false;
@@ -539,7 +539,7 @@ Rectangle {
creator: model.creator
category: model.primary_category
price: model.cost
- available: model.availability === "available"
+ availability: model.availability
isLoggedIn: root.isLoggedIn;
onShowItem: {
@@ -711,7 +711,7 @@ Rectangle {
topMargin: 10;
leftMargin: 15;
}
- height: visible ? childrenRect.height : 0
+ height: visible ? 36 : 0
RalewayRegular {
id: sortText
@@ -733,8 +733,9 @@ Rectangle {
top: parent.top
leftMargin: 20
}
+
width: root.isLoggedIn ? 342 : 262
- height: 36
+ height: parent.height
radius: 4
border.width: 1
diff --git a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml
index 2c7a50033c..97e5c10a6b 100644
--- a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml
+++ b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml
@@ -33,11 +33,11 @@ Rectangle {
property string creator: ""
property var categories: []
property int price: 0
+ property string availability: "unknown"
property var attributions: []
property string description: ""
property string license: ""
property string posted: ""
- property bool available: false
property string created_at: ""
property bool isLoggedIn: false;
property int edition: -1;
@@ -264,9 +264,15 @@ Rectangle {
}
height: 50
- text: root.edition >= 0 ? "UPGRADE FOR FREE" : (root.available ? (root.price ? root.price : "FREE") : "UNAVAILABLE (not for sale)")
- enabled: root.edition >= 0 || root.available
- buttonGlyph: root.available ? (root.price ? hifi.glyphs.hfc : "") : ""
+ 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 : "") : ""
color: hifi.buttons.blue
onClicked: root.buy();
diff --git a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceListItem.qml b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceListItem.qml
index 2f37637e40..587d71da28 100644
--- a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceListItem.qml
+++ b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceListItem.qml
@@ -34,7 +34,7 @@ Rectangle {
property string creator: ""
property string category: ""
property int price: 0
- property bool available: false
+ property string availability: "unknown"
property bool isLoggedIn: false;
signal buy()
@@ -299,8 +299,16 @@ Rectangle {
bottomMargin: 10
}
- text: root.price ? root.price : "FREE"
- buttonGlyph: root.price ? hifi.glyphs.hfc : ""
+ 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 : "") : ""
+
color: hifi.buttons.blue;
onClicked: root.buy();
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index b65f39ffd7..9d07c17b34 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -1911,46 +1911,34 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
}
}, Qt::QueuedConnection);
- EntityTree::setAddMaterialToEntityOperator([this](const QUuid& entityID, graphics::MaterialLayer material, const std::string& parentMaterialName) {
+ EntityTreeRenderer::setAddMaterialToEntityOperator([this](const QUuid& entityID, graphics::MaterialLayer material, const std::string& parentMaterialName) {
if (_aboutToQuit) {
return false;
}
- // try to find the renderable
auto renderable = getEntities()->renderableForEntityId(entityID);
if (renderable) {
renderable->addMaterial(material, parentMaterialName);
- }
-
- // even if we don't find it, try to find the entity
- auto entity = getEntities()->getEntity(entityID);
- if (entity) {
- entity->addMaterial(material, parentMaterialName);
return true;
}
+
return false;
});
- EntityTree::setRemoveMaterialFromEntityOperator([this](const QUuid& entityID, graphics::MaterialPointer material, const std::string& parentMaterialName) {
+ EntityTreeRenderer::setRemoveMaterialFromEntityOperator([this](const QUuid& entityID, graphics::MaterialPointer material, const std::string& parentMaterialName) {
if (_aboutToQuit) {
return false;
}
- // try to find the renderable
auto renderable = getEntities()->renderableForEntityId(entityID);
if (renderable) {
renderable->removeMaterial(material, parentMaterialName);
- }
-
- // even if we don't find it, try to find the entity
- auto entity = getEntities()->getEntity(entityID);
- if (entity) {
- entity->removeMaterial(material, parentMaterialName);
return true;
}
+
return false;
});
- EntityTree::setAddMaterialToAvatarOperator([](const QUuid& avatarID, graphics::MaterialLayer material, const std::string& parentMaterialName) {
+ EntityTreeRenderer::setAddMaterialToAvatarOperator([](const QUuid& avatarID, graphics::MaterialLayer material, const std::string& parentMaterialName) {
auto avatarManager = DependencyManager::get();
auto avatar = avatarManager->getAvatarBySessionID(avatarID);
if (avatar) {
@@ -1959,7 +1947,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
}
return false;
});
- EntityTree::setRemoveMaterialFromAvatarOperator([](const QUuid& avatarID, graphics::MaterialPointer material, const std::string& parentMaterialName) {
+ EntityTreeRenderer::setRemoveMaterialFromAvatarOperator([](const QUuid& avatarID, graphics::MaterialPointer material, const std::string& parentMaterialName) {
auto avatarManager = DependencyManager::get();
auto avatar = avatarManager->getAvatarBySessionID(avatarID);
if (avatar) {
@@ -2282,7 +2270,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// Setup the mouse ray pick and related operators
{
- auto mouseRayPick = std::make_shared(Vectors::ZERO, Vectors::UP, PickFilter(PickScriptingInterface::PICK_ENTITIES()), 0.0f, true);
+ auto mouseRayPick = std::make_shared(Vectors::ZERO, Vectors::UP, PickFilter(PickScriptingInterface::PICK_ENTITIES() | PickScriptingInterface::PICK_LOCAL_ENTITIES()), 0.0f, true);
mouseRayPick->parentTransform = std::make_shared();
mouseRayPick->setJointState(PickQuery::JOINT_STATE_MOUSE);
auto mouseRayPickID = DependencyManager::get()->addPick(PickQuery::Ray, mouseRayPick);
diff --git a/interface/src/avatar/AvatarDoctor.cpp b/interface/src/avatar/AvatarDoctor.cpp
new file mode 100644
index 0000000000..b528441be7
--- /dev/null
+++ b/interface/src/avatar/AvatarDoctor.cpp
@@ -0,0 +1,196 @@
+//
+// AvatarDoctor.cpp
+//
+//
+// Created by Thijs Wenker on 2/12/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 "AvatarDoctor.h"
+#include
+#include
+#include
+
+
+AvatarDoctor::AvatarDoctor(QUrl avatarFSTFileUrl) :
+ _avatarFSTFileUrl(avatarFSTFileUrl) {
+
+ connect(this, &AvatarDoctor::complete, this, [this](QVariantList errors) {
+ _isDiagnosing = false;
+ });
+}
+
+void AvatarDoctor::startDiagnosing() {
+ if (_isDiagnosing) {
+ // One diagnose at a time for now
+ return;
+ }
+ _isDiagnosing = true;
+
+ _errors.clear();
+
+ _externalTextureCount = 0;
+ _checkedTextureCount = 0;
+ _missingTextureCount = 0;
+ _unsupportedTextureCount = 0;
+
+ const auto resource = DependencyManager::get()->getGeometryResource(_avatarFSTFileUrl);
+ resource->refresh();
+ const QUrl DEFAULT_URL = QUrl("https://docs.highfidelity.com/create/avatars/create-avatars.html#create-your-own-avatar");
+ const auto resourceLoaded = [this, resource, DEFAULT_URL](bool success) {
+ // MODEL
+ if (!success) {
+ _errors.push_back({ "Model file cannot be opened", DEFAULT_URL });
+ emit complete(getErrors());
+ return;
+ }
+ const auto model = resource.data();
+ const auto avatarModel = resource.data()->getHFMModel();
+ if (!avatarModel.originalURL.endsWith(".fbx")) {
+ _errors.push_back({ "Unsupported avatar model format", DEFAULT_URL });
+ emit complete(getErrors());
+ return;
+ }
+
+ // RIG
+ if (avatarModel.joints.isEmpty()) {
+ _errors.push_back({ "Avatar has no rig", DEFAULT_URL });
+ } else {
+ if (avatarModel.joints.length() > 256) {
+ _errors.push_back({ "Avatar has over 256 bones", DEFAULT_URL });
+ }
+ // Avatar does not have Hips bone mapped
+ if (!avatarModel.getJointNames().contains("Hips")) {
+ _errors.push_back({ "Hips are not mapped", DEFAULT_URL });
+ }
+ if (!avatarModel.getJointNames().contains("Spine")) {
+ _errors.push_back({ "Spine is not mapped", DEFAULT_URL });
+ }
+ if (!avatarModel.getJointNames().contains("Head")) {
+ _errors.push_back({ "Head is not mapped", DEFAULT_URL });
+ }
+ }
+
+ // SCALE
+ const float RECOMMENDED_MIN_HEIGHT = DEFAULT_AVATAR_HEIGHT * 0.25f;
+ const float RECOMMENDED_MAX_HEIGHT = DEFAULT_AVATAR_HEIGHT * 1.5f;
+
+ const float avatarHeight = avatarModel.bindExtents.largestDimension();
+ if (avatarHeight < RECOMMENDED_MIN_HEIGHT) {
+ _errors.push_back({ "Avatar is possibly too small.", DEFAULT_URL });
+ } else if (avatarHeight > RECOMMENDED_MAX_HEIGHT) {
+ _errors.push_back({ "Avatar is possibly too large.", DEFAULT_URL });
+ }
+
+ // TEXTURES
+ QStringList externalTextures{};
+ QSet textureNames{};
+ auto addTextureToList = [&externalTextures](hfm::Texture texture) mutable {
+ if (!texture.filename.isEmpty() && texture.content.isEmpty() && !externalTextures.contains(texture.name)) {
+ externalTextures << texture.name;
+ }
+ };
+
+ foreach(const HFMMaterial material, avatarModel.materials) {
+ addTextureToList(material.normalTexture);
+ addTextureToList(material.albedoTexture);
+ addTextureToList(material.opacityTexture);
+ addTextureToList(material.glossTexture);
+ addTextureToList(material.roughnessTexture);
+ addTextureToList(material.specularTexture);
+ addTextureToList(material.metallicTexture);
+ addTextureToList(material.emissiveTexture);
+ addTextureToList(material.occlusionTexture);
+ addTextureToList(material.scatteringTexture);
+ addTextureToList(material.lightmapTexture);
+ }
+
+ if (!externalTextures.empty()) {
+ // Check External Textures:
+ auto modelTexturesURLs = model->getTextures();
+ _externalTextureCount = externalTextures.length();
+ foreach(const QString textureKey, externalTextures) {
+ if (!modelTexturesURLs.contains(textureKey)) {
+ _missingTextureCount++;
+ _checkedTextureCount++;
+ continue;
+ }
+
+ const QUrl textureURL = modelTexturesURLs[textureKey].toUrl();
+
+ auto textureResource = DependencyManager::get()->getTexture(textureURL);
+ auto checkTextureLoadingComplete = [this, DEFAULT_URL] () mutable {
+ qDebug() << "checkTextureLoadingComplete" << _checkedTextureCount << "/" << _externalTextureCount;
+
+ if (_checkedTextureCount == _externalTextureCount) {
+ if (_missingTextureCount > 0) {
+ _errors.push_back({ tr("Missing %n texture(s).","", _missingTextureCount), DEFAULT_URL });
+ }
+ if (_unsupportedTextureCount > 0) {
+ _errors.push_back({ tr("%n unsupported texture(s) found.", "", _unsupportedTextureCount), DEFAULT_URL });
+ }
+ emit complete(getErrors());
+ }
+ };
+
+ auto textureLoaded = [this, textureResource, checkTextureLoadingComplete] (bool success) mutable {
+ if (!success) {
+ auto normalizedURL = DependencyManager::get()->normalizeURL(textureResource->getURL());
+ if (normalizedURL.isLocalFile()) {
+ QFile textureFile(normalizedURL.toLocalFile());
+ if (textureFile.exists()) {
+ _unsupportedTextureCount++;
+ } else {
+ _missingTextureCount++;
+ }
+ } else {
+ _missingTextureCount++;
+ }
+ }
+ _checkedTextureCount++;
+ checkTextureLoadingComplete();
+ };
+
+ if (textureResource) {
+ textureResource->refresh();
+ if (textureResource->isLoaded()) {
+ textureLoaded(!textureResource->isFailed());
+ } else {
+ connect(textureResource.data(), &NetworkTexture::finished, this, textureLoaded);
+ }
+ } else {
+ _missingTextureCount++;
+ _checkedTextureCount++;
+ checkTextureLoadingComplete();
+ }
+ }
+ } else {
+ emit complete(getErrors());
+ }
+ };
+
+ if (resource) {
+ if (resource->isLoaded()) {
+ resourceLoaded(!resource->isFailed());
+ } else {
+ connect(resource.data(), &GeometryResource::finished, this, resourceLoaded);
+ }
+ } else {
+ _errors.push_back({ "Model file cannot be opened", DEFAULT_URL });
+ emit complete(getErrors());
+ }
+}
+
+QVariantList AvatarDoctor::getErrors() const {
+ QVariantList result;
+ for (const auto& error : _errors) {
+ QVariantMap errorVariant;
+ errorVariant.insert("message", error.message);
+ errorVariant.insert("url", error.url);
+ result.append(errorVariant);
+ }
+ return result;
+}
diff --git a/interface/src/avatar/AvatarDoctor.h b/interface/src/avatar/AvatarDoctor.h
new file mode 100644
index 0000000000..bebec32542
--- /dev/null
+++ b/interface/src/avatar/AvatarDoctor.h
@@ -0,0 +1,51 @@
+//
+// AvatarDoctor.h
+//
+//
+// Created by Thijs Wenker on 02/12/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
+//
+
+#pragma once
+#ifndef hifi_AvatarDoctor_h
+#define hifi_AvatarDoctor_h
+
+#include
+#include
+#include
+
+struct AvatarDiagnosticResult {
+ QString message;
+ QUrl url;
+};
+Q_DECLARE_METATYPE(AvatarDiagnosticResult)
+Q_DECLARE_METATYPE(QVector)
+
+class AvatarDoctor : public QObject {
+ Q_OBJECT
+public:
+ AvatarDoctor(QUrl avatarFSTFileUrl);
+
+ Q_INVOKABLE void startDiagnosing();
+
+ Q_INVOKABLE QVariantList getErrors() const;
+
+signals:
+ void complete(QVariantList errors);
+
+private:
+ QUrl _avatarFSTFileUrl;
+ QVector _errors;
+
+ int _externalTextureCount = 0;
+ int _checkedTextureCount = 0;
+ int _missingTextureCount = 0;
+ int _unsupportedTextureCount = 0;
+
+ bool _isDiagnosing = false;
+};
+
+#endif // hifi_AvatarDoctor_h
diff --git a/interface/src/avatar/AvatarPackager.cpp b/interface/src/avatar/AvatarPackager.cpp
index fa70eee374..90def7ad43 100644
--- a/interface/src/avatar/AvatarPackager.cpp
+++ b/interface/src/avatar/AvatarPackager.cpp
@@ -31,6 +31,7 @@ AvatarPackager::AvatarPackager() {
qmlRegisterType();
qRegisterMetaType();
qRegisterMetaType();
+ qRegisterMetaType();
qRegisterMetaType();
qmlRegisterUncreatableMetaObject(
AvatarProjectStatus::staticMetaObject,
@@ -84,7 +85,7 @@ void AvatarPackager::addCurrentProjectToRecentProjects() {
_recentProjects.removeOne(removeProject);
}
- const auto newRecentProject = RecentAvatarProject(_currentAvatarProject->getProjectName(), fstPath);
+ const auto newRecentProject = RecentAvatarProject(_currentAvatarProject->getProjectName(), fstPath, _currentAvatarProject->getHasErrors());
_recentProjects.prepend(newRecentProject);
while (_recentProjects.size() > MAX_RECENT_PROJECTS) {
@@ -101,6 +102,7 @@ QVariantList AvatarPackager::recentProjectsToVariantList(bool includeProjectPath
QVariantMap projectVariant;
projectVariant.insert("name", project.getProjectName());
projectVariant.insert("path", project.getProjectFSTPath());
+ projectVariant.insert("hadErrors", project.getHadErrors());
if (includeProjectPaths) {
projectVariant.insert("projectPath", project.getProjectPath());
}
@@ -113,7 +115,10 @@ void AvatarPackager::recentProjectsFromVariantList(QVariantList projectsVariant)
_recentProjects.clear();
for (const auto& projectVariant : projectsVariant) {
auto map = projectVariant.toMap();
- _recentProjects.append(RecentAvatarProject(map.value("name").toString(), map.value("path").toString()));
+ _recentProjects.append(RecentAvatarProject(
+ map.value("name").toString(),
+ map.value("path").toString(),
+ map.value("hadErrors", false).toBool()));
}
}
diff --git a/interface/src/avatar/AvatarPackager.h b/interface/src/avatar/AvatarPackager.h
index ec954a60d7..13f62cb471 100644
--- a/interface/src/avatar/AvatarPackager.h
+++ b/interface/src/avatar/AvatarPackager.h
@@ -26,19 +26,23 @@ public:
RecentAvatarProject() = default;
- RecentAvatarProject(QString projectName, QString projectFSTPath) {
+ RecentAvatarProject(QString projectName, QString projectFSTPath, bool hadErrors) {
_projectName = projectName;
_projectFSTPath = projectFSTPath;
+ _hadErrors = hadErrors;
}
RecentAvatarProject(const RecentAvatarProject& other) {
_projectName = other._projectName;
_projectFSTPath = other._projectFSTPath;
+ _hadErrors = other._hadErrors;
}
QString getProjectName() const { return _projectName; }
QString getProjectFSTPath() const { return _projectFSTPath; }
+ bool getHadErrors() const { return _hadErrors; }
+
QString getProjectPath() const {
return QFileInfo(_projectFSTPath).absoluteDir().absolutePath();
}
@@ -50,6 +54,7 @@ public:
private:
QString _projectName;
QString _projectFSTPath;
+ bool _hadErrors;
};
@@ -73,6 +78,8 @@ public:
return AvatarProject::isValidNewProjectName(projectPath, projectName);
}
+ Q_INVOKABLE void addCurrentProjectToRecentProjects();
+
signals:
void avatarProjectChanged();
void recentProjectsChanged();
@@ -84,8 +91,6 @@ private:
void setAvatarProject(AvatarProject* avatarProject);
- void addCurrentProjectToRecentProjects();
-
AvatarProject* _currentAvatarProject { nullptr };
QVector _recentProjects;
diff --git a/interface/src/avatar/AvatarProject.cpp b/interface/src/avatar/AvatarProject.cpp
index 728917e673..b020cdb627 100644
--- a/interface/src/avatar/AvatarProject.cpp
+++ b/interface/src/avatar/AvatarProject.cpp
@@ -243,6 +243,10 @@ MarketplaceItemUploader* AvatarProject::upload(bool updateExisting) {
return uploader;
}
+AvatarDoctor* AvatarProject::diagnose() {
+ return new AvatarDoctor(QUrl(getFSTPath()));
+}
+
void AvatarProject::openInInventory() const {
constexpr int TIME_TO_WAIT_FOR_INVENTORY_TO_OPEN_MS { 1000 };
diff --git a/interface/src/avatar/AvatarProject.h b/interface/src/avatar/AvatarProject.h
index 1710282a3e..f11547bdca 100644
--- a/interface/src/avatar/AvatarProject.h
+++ b/interface/src/avatar/AvatarProject.h
@@ -14,6 +14,7 @@
#define hifi_AvatarProject_h
#include "MarketplaceItemUploader.h"
+#include "AvatarDoctor.h"
#include "ProjectFile.h"
#include "FST.h"
@@ -53,11 +54,14 @@ class AvatarProject : public QObject {
Q_PROPERTY(QString projectFSTPath READ getFSTPath CONSTANT)
Q_PROPERTY(QString projectFBXPath READ getFBXPath CONSTANT)
Q_PROPERTY(QString name READ getProjectName WRITE setProjectName NOTIFY nameChanged)
+ Q_PROPERTY(bool hasErrors READ getHasErrors WRITE setHasErrors NOTIFY hasErrorsChanged)
public:
Q_INVOKABLE MarketplaceItemUploader* upload(bool updateExisting);
Q_INVOKABLE void openInInventory() const;
Q_INVOKABLE QStringList getProjectFiles() const;
+ Q_INVOKABLE AvatarDoctor* diagnose();
+
Q_INVOKABLE QString getProjectName() const { return _fst->getName(); }
Q_INVOKABLE void setProjectName(const QString& newProjectName) {
@@ -72,6 +76,8 @@ public:
Q_INVOKABLE QString getFBXPath() const {
return QDir::cleanPath(QDir(_projectPath).absoluteFilePath(_fst->getModelPath()));
}
+ Q_INVOKABLE bool getHasErrors() const { return _hasErrors; }
+ Q_INVOKABLE void setHasErrors(bool hasErrors) { _hasErrors = hasErrors; }
/**
* returns the AvatarProject or a nullptr on failure.
@@ -92,6 +98,7 @@ public:
signals:
void nameChanged();
void projectFilesChanged();
+ void hasErrorsChanged();
private:
AvatarProject(const QString& fstPath, const QByteArray& data);
@@ -110,6 +117,8 @@ private:
QDir _directory;
QList _projectFiles{};
QString _projectPath;
+
+ bool _hasErrors { false };
};
#endif // hifi_AvatarProject_h
diff --git a/interface/src/raypick/StylusPick.cpp b/interface/src/raypick/StylusPick.cpp
index 0e95959566..9c3ffb972f 100644
--- a/interface/src/raypick/StylusPick.cpp
+++ b/interface/src/raypick/StylusPick.cpp
@@ -137,7 +137,7 @@ PickResultPointer StylusPick::getDefaultResult(const QVariantMap& pickVariant) c
}
PickResultPointer StylusPick::getEntityIntersection(const StylusTip& pick) {
- std::vector results;
+ StylusPickResult nearestTarget(pick.toVariantMap());
for (const auto& target : getIncludeItems()) {
if (target.isNull()) {
continue;
@@ -157,28 +157,21 @@ PickResultPointer StylusPick::getEntityIntersection(const StylusTip& pick) {
glm::vec3 normal = entityRotation * Vectors::UNIT_Z;
float distance = glm::dot(pick.position - entityPosition, normal);
- glm::vec3 intersection = pick.position - (normal * distance);
-
- glm::vec2 pos2D = RayPick::projectOntoEntityXYPlane(target, intersection, false);
- if (pos2D == glm::clamp(pos2D, glm::vec2(0), glm::vec2(1))) {
- IntersectionType type = IntersectionType::ENTITY;
- if (getFilter().doesPickLocalEntities()) {
- EntityPropertyFlags desiredProperties;
- desiredProperties += PROP_ENTITY_HOST_TYPE;
- if (DependencyManager::get()->getEntityProperties(target, desiredProperties).getEntityHostType() == entity::HostType::LOCAL) {
- type = IntersectionType::LOCAL_ENTITY;
+ if (distance < nearestTarget.distance) {
+ glm::vec3 intersection = pick.position - (normal * distance);
+ glm::vec2 pos2D = RayPick::projectOntoEntityXYPlane(target, intersection, false);
+ if (pos2D == glm::clamp(pos2D, glm::vec2(0), glm::vec2(1))) {
+ IntersectionType type = IntersectionType::ENTITY;
+ if (getFilter().doesPickLocalEntities()) {
+ if (entity->getEntityHostType() == entity::HostType::LOCAL) {
+ type = IntersectionType::LOCAL_ENTITY;
+ }
}
+ nearestTarget = StylusPickResult(type, target, distance, intersection, pick, normal);
}
- results.push_back(StylusPickResult(type, target, distance, intersection, pick, normal));
}
}
- StylusPickResult nearestTarget(pick.toVariantMap());
- for (const auto& result : results) {
- if (result.distance < nearestTarget.distance) {
- nearestTarget = result;
- }
- }
return std::make_shared(nearestTarget);
}
diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp
index c382c3de43..24c0986d09 100644
--- a/interface/src/ui/overlays/ContextOverlayInterface.cpp
+++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp
@@ -70,7 +70,6 @@ ContextOverlayInterface::ContextOverlayInterface() {
}
});
connect(entityScriptingInterface, &EntityScriptingInterface::deletingEntity, this, &ContextOverlayInterface::deletingEntity);
- connect(&qApp->getOverlays(), &Overlays::mousePressOnOverlay, this, &ContextOverlayInterface::contextOverlays_mousePressOnOverlay);
connect(&qApp->getOverlays(), &Overlays::hoverEnterOverlay, this, &ContextOverlayInterface::contextOverlays_hoverEnterOverlay);
connect(&qApp->getOverlays(), &Overlays::hoverLeaveOverlay, this, &ContextOverlayInterface::contextOverlays_hoverLeaveOverlay);
@@ -103,10 +102,14 @@ void ContextOverlayInterface::setEnabled(bool enabled) {
}
}
-void ContextOverlayInterface::clickDownOnEntity(const EntityItemID& entityItemID, const PointerEvent& event) {
- if (_enabled && event.getButton() == PointerEvent::SecondaryButton && contextOverlayFilterPassed(entityItemID)) {
- _mouseDownEntity = entityItemID;
+void ContextOverlayInterface::clickDownOnEntity(const EntityItemID& id, const PointerEvent& event) {
+ if (_enabled && event.getButton() == PointerEvent::SecondaryButton && contextOverlayFilterPassed(id)) {
+ _mouseDownEntity = id;
_mouseDownEntityTimestamp = usecTimestampNow();
+ } else if (id == _contextOverlayID && event.getButton() == PointerEvent::PrimaryButton) {
+ qCDebug(context_overlay) << "Clicked Context Overlay. Entity ID:" << _currentEntityWithContextOverlay << "ID:" << id;
+ emit contextOverlayClicked(_currentEntityWithContextOverlay);
+ _contextOverlayJustClicked = true;
} else {
if (!_currentEntityWithContextOverlay.isNull()) {
disableEntityHighlight(_currentEntityWithContextOverlay);
@@ -249,14 +252,6 @@ bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt
return ContextOverlayInterface::destroyContextOverlay(entityItemID, PointerEvent());
}
-void ContextOverlayInterface::contextOverlays_mousePressOnOverlay(const QUuid& id, const PointerEvent& event) {
- if (id == _contextOverlayID && event.getButton() == PointerEvent::PrimaryButton) {
- qCDebug(context_overlay) << "Clicked Context Overlay. Entity ID:" << _currentEntityWithContextOverlay << "ID:" << id;
- emit contextOverlayClicked(_currentEntityWithContextOverlay);
- _contextOverlayJustClicked = true;
- }
-}
-
void ContextOverlayInterface::contextOverlays_hoverEnterOverlay(const QUuid& id, const PointerEvent& event) {
if (_contextOverlayID != UNKNOWN_ENTITY_ID) {
qCDebug(context_overlay) << "Started hovering over Context Overlay. ID:" << id;
diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h
index b87535acf2..57fc8ebe6e 100644
--- a/interface/src/ui/overlays/ContextOverlayInterface.h
+++ b/interface/src/ui/overlays/ContextOverlayInterface.h
@@ -65,7 +65,6 @@ public slots:
bool createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event);
bool destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event);
bool destroyContextOverlay(const EntityItemID& entityItemID);
- void contextOverlays_mousePressOnOverlay(const QUuid& id, const PointerEvent& event);
void contextOverlays_hoverEnterOverlay(const QUuid& id, const PointerEvent& event);
void contextOverlays_hoverLeaveOverlay(const QUuid& id, const PointerEvent& event);
void contextOverlays_hoverEnterEntity(const EntityItemID& entityID, const PointerEvent& event);
diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp
index 6028f29ffa..efc2d92177 100644
--- a/interface/src/ui/overlays/Overlays.cpp
+++ b/interface/src/ui/overlays/Overlays.cpp
@@ -63,13 +63,6 @@ Overlays::Overlays() {
ADD_TYPE_MAP(PolyLine, line3d);
ADD_TYPE_MAP(Grid, grid);
ADD_TYPE_MAP(Gizmo, circle3d);
-
- auto mouseRayPick = std::make_shared(Vectors::ZERO, Vectors::UP,
- PickFilter(PickFilter::getBitMask(PickFilter::FlagBit::LOCAL_ENTITIES) |
- PickFilter::getBitMask(PickFilter::FlagBit::VISIBLE)), 0.0f, true);
- mouseRayPick->parentTransform = std::make_shared();
- mouseRayPick->setJointState(PickQuery::JOINT_STATE_MOUSE);
- _mouseRayPickID = DependencyManager::get()->addPick(PickQuery::Ray, mouseRayPick);
}
void Overlays::cleanupAllOverlays() {
@@ -86,15 +79,7 @@ void Overlays::cleanupAllOverlays() {
cleanupOverlaysToDelete();
}
-void Overlays::init() {
- auto entityScriptingInterface = DependencyManager::get();
- connect(this, &Overlays::hoverEnterOverlay, entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity);
- connect(this, &Overlays::hoverOverOverlay, entityScriptingInterface.data(), &EntityScriptingInterface::hoverOverEntity);
- connect(this, &Overlays::hoverLeaveOverlay, entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity);
- connect(this, &Overlays::mousePressOnOverlay, entityScriptingInterface.data(), &EntityScriptingInterface::mousePressOnEntity);
- connect(this, &Overlays::mouseMoveOnOverlay, entityScriptingInterface.data(), &EntityScriptingInterface::mouseMoveOnEntity);
- connect(this, &Overlays::mouseReleaseOnOverlay, entityScriptingInterface.data(), &EntityScriptingInterface::mouseReleaseOnEntity);
-}
+void Overlays::init() {}
void Overlays::update(float deltatime) {
cleanupOverlaysToDelete();
@@ -386,6 +371,8 @@ EntityItemProperties Overlays::convertOverlayToEntityProperties(QVariantMap& ove
RENAME_PROP(animationSettings, animation);
} else if (type == "Image") {
RENAME_PROP(url, imageURL);
+ } else if (type == "Text") {
+ RENAME_PROP(color, textColor);
} else if (type == "Web") {
RENAME_PROP(url, sourceUrl);
RENAME_PROP_CONVERT(inputMode, inputMode, [](const QVariant& v) { return v.toString() == "Mouse" ? "mouse" : "touch"; });
@@ -686,6 +673,8 @@ QVariantMap Overlays::convertEntityToOverlayProperties(const EntityItemPropertie
RENAME_PROP(animation, animationSettings);
} else if (type == "Image") {
RENAME_PROP(imageURL, url);
+ } else if (type == "Text") {
+ RENAME_PROP(textColor, color);
} else if (type == "Web") {
RENAME_PROP(sourceUrl, url);
RENAME_PROP_CONVERT(inputMode, inputMode, [](const QVariant& v) { return v.toString() == "mouse" ? "Mouse" : "Touch"; });
@@ -1239,12 +1228,12 @@ static PointerEvent::Button toPointerButton(const QMouseEvent& event) {
}
}
-RayToOverlayIntersectionResult getPrevPickResult(unsigned int mouseRayPickID) {
+RayToOverlayIntersectionResult getPrevPickResult() {
RayToOverlayIntersectionResult overlayResult;
overlayResult.intersects = false;
- auto pickResult = DependencyManager::get()->getPrevPickResultTyped(mouseRayPickID);
+ auto pickResult = DependencyManager::get()->getPrevPickResultTyped(DependencyManager::get()->getMouseRayPickID());
if (pickResult) {
- overlayResult.intersects = pickResult->type != IntersectionType::NONE;
+ overlayResult.intersects = pickResult->type == IntersectionType::LOCAL_ENTITY;
if (overlayResult.intersects) {
overlayResult.intersection = pickResult->intersection;
overlayResult.distance = pickResult->distance;
@@ -1292,7 +1281,7 @@ std::pair Overlays::mousePressEvent(QMouseEvent* event) {
PerformanceTimer perfTimer("Overlays::mousePressEvent");
PickRay ray = qApp->computePickRay(event->x(), event->y());
- RayToOverlayIntersectionResult rayPickResult = getPrevPickResult(_mouseRayPickID);
+ RayToOverlayIntersectionResult rayPickResult = getPrevPickResult();
if (rayPickResult.intersects) {
_currentClickingOnOverlayID = rayPickResult.overlayID;
@@ -1316,7 +1305,7 @@ bool Overlays::mouseDoublePressEvent(QMouseEvent* event) {
PerformanceTimer perfTimer("Overlays::mouseDoublePressEvent");
PickRay ray = qApp->computePickRay(event->x(), event->y());
- RayToOverlayIntersectionResult rayPickResult = getPrevPickResult(_mouseRayPickID);
+ RayToOverlayIntersectionResult rayPickResult = getPrevPickResult();
if (rayPickResult.intersects) {
_currentClickingOnOverlayID = rayPickResult.overlayID;
@@ -1332,7 +1321,7 @@ bool Overlays::mouseReleaseEvent(QMouseEvent* event) {
PerformanceTimer perfTimer("Overlays::mouseReleaseEvent");
PickRay ray = qApp->computePickRay(event->x(), event->y());
- RayToOverlayIntersectionResult rayPickResult = getPrevPickResult(_mouseRayPickID);
+ RayToOverlayIntersectionResult rayPickResult = getPrevPickResult();
if (rayPickResult.intersects) {
auto pointerEvent = calculateOverlayPointerEvent(rayPickResult.overlayID, ray, rayPickResult, event, PointerEvent::Release);
mouseReleasePointerEvent(rayPickResult.overlayID, pointerEvent);
@@ -1354,7 +1343,7 @@ bool Overlays::mouseMoveEvent(QMouseEvent* event) {
PerformanceTimer perfTimer("Overlays::mouseMoveEvent");
PickRay ray = qApp->computePickRay(event->x(), event->y());
- RayToOverlayIntersectionResult rayPickResult = getPrevPickResult(_mouseRayPickID);
+ RayToOverlayIntersectionResult rayPickResult = getPrevPickResult();
if (rayPickResult.intersects) {
auto pointerEvent = calculateOverlayPointerEvent(rayPickResult.overlayID, ray, rayPickResult, event, PointerEvent::Move);
mouseMovePointerEvent(rayPickResult.overlayID, pointerEvent);
diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h
index 7612779099..838a38eb54 100644
--- a/interface/src/ui/overlays/Overlays.h
+++ b/interface/src/ui/overlays/Overlays.h
@@ -719,7 +719,6 @@ private:
PointerEvent calculateOverlayPointerEvent(const QUuid& id, const PickRay& ray, const RayToOverlayIntersectionResult& rayPickResult,
QMouseEvent* event, PointerEvent::EventType eventType);
- unsigned int _mouseRayPickID;
QUuid _currentClickingOnOverlayID;
QUuid _currentHoverOverOverlayID;
diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp
index 5f30d98ed6..c16d65506a 100644
--- a/libraries/avatars/src/AvatarHashMap.cpp
+++ b/libraries/avatars/src/AvatarHashMap.cpp
@@ -208,16 +208,16 @@ AvatarSharedPointer AvatarHashMap::addAvatar(const QUuid& sessionUUID, const QWe
avatar->setSessionUUID(sessionUUID);
avatar->setOwningAvatarMixer(mixerWeakPointer);
- // addAvatar is only called from newOrExistingAvatar, which already locks _hashLock
- _avatarHash.insert(sessionUUID, avatar);
+ {
+ QWriteLocker locker(&_hashLock);
+ _avatarHash.insert(sessionUUID, avatar);
+ }
emit avatarAddedEvent(sessionUUID);
return avatar;
}
-AvatarSharedPointer AvatarHashMap::newOrExistingAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer,
- bool& isNew) {
- QWriteLocker locker(&_hashLock);
- auto avatar = _avatarHash.value(sessionUUID);
+AvatarSharedPointer AvatarHashMap::newOrExistingAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer, bool& isNew) {
+ auto avatar = findAvatar(sessionUUID);
if (!avatar) {
avatar = addAvatar(sessionUUID, mixerWeakPointer);
isNew = true;
diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp
index 319acc750f..9d55d936a2 100644
--- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp
+++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp
@@ -86,7 +86,7 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
auto handlePointerEvent = [&](const QUuid& entityID, const PointerEvent& event) {
std::shared_ptr thisEntity;
auto entity = getEntity(entityID);
- if (entity && entity->getType() == EntityTypes::Web) {
+ if (entity && entity->isVisible() && entity->getType() == EntityTypes::Web) {
thisEntity = std::static_pointer_cast(renderableForEntityId(entityID));
}
if (thisEntity) {
@@ -99,7 +99,7 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity, this, [&](const QUuid& entityID, const PointerEvent& event) {
std::shared_ptr thisEntity;
auto entity = getEntity(entityID);
- if (entity && entity->getType() == EntityTypes::Web) {
+ if (entity && entity->isVisible() && entity->getType() == EntityTypes::Web) {
thisEntity = std::static_pointer_cast(renderableForEntityId(entityID));
}
if (thisEntity) {
@@ -110,7 +110,7 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity, this, [&](const QUuid& entityID, const PointerEvent& event) {
std::shared_ptr thisEntity;
auto entity = getEntity(entityID);
- if (entity && entity->getType() == EntityTypes::Web) {
+ if (entity && entity->isVisible() && entity->getType() == EntityTypes::Web) {
thisEntity = std::static_pointer_cast(renderableForEntityId(entityID));
}
if (thisEntity) {
@@ -1360,3 +1360,36 @@ EntityEditPacketSender* EntityTreeRenderer::getPacketSender() {
EntityEditPacketSender* packetSender = peSimulation ? peSimulation->getPacketSender() : nullptr;
return packetSender;
}
+
+std::function EntityTreeRenderer::_addMaterialToEntityOperator = nullptr;
+std::function EntityTreeRenderer::_removeMaterialFromEntityOperator = nullptr;
+std::function EntityTreeRenderer::_addMaterialToAvatarOperator = nullptr;
+std::function EntityTreeRenderer::_removeMaterialFromAvatarOperator = nullptr;
+
+bool EntityTreeRenderer::addMaterialToEntity(const QUuid& entityID, graphics::MaterialLayer material, const std::string& parentMaterialName) {
+ if (_addMaterialToEntityOperator) {
+ return _addMaterialToEntityOperator(entityID, material, parentMaterialName);
+ }
+ return false;
+}
+
+bool EntityTreeRenderer::removeMaterialFromEntity(const QUuid& entityID, graphics::MaterialPointer material, const std::string& parentMaterialName) {
+ if (_removeMaterialFromEntityOperator) {
+ return _removeMaterialFromEntityOperator(entityID, material, parentMaterialName);
+ }
+ return false;
+}
+
+bool EntityTreeRenderer::addMaterialToAvatar(const QUuid& avatarID, graphics::MaterialLayer material, const std::string& parentMaterialName) {
+ if (_addMaterialToAvatarOperator) {
+ return _addMaterialToAvatarOperator(avatarID, material, parentMaterialName);
+ }
+ return false;
+}
+
+bool EntityTreeRenderer::removeMaterialFromAvatar(const QUuid& avatarID, graphics::MaterialPointer material, const std::string& parentMaterialName) {
+ if (_removeMaterialFromAvatarOperator) {
+ return _removeMaterialFromAvatarOperator(avatarID, material, parentMaterialName);
+ }
+ return false;
+}
\ No newline at end of file
diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h
index 204dc50c45..51568ab744 100644
--- a/libraries/entities-renderer/src/EntityTreeRenderer.h
+++ b/libraries/entities-renderer/src/EntityTreeRenderer.h
@@ -73,6 +73,7 @@ public:
static void setEntityLoadingPriorityFunction(CalculateEntityLoadingPriority fn) { _calculateEntityLoadingPriorityFunc = fn; }
void setMouseRayPickID(unsigned int rayPickID) { _mouseRayPickID = rayPickID; }
+ unsigned int getMouseRayPickID() { return _mouseRayPickID; }
void setMouseRayPickResultOperator(std::function getPrevRayPickResultOperator) { _getPrevRayPickResultOperator = getPrevRayPickResultOperator; }
void setSetPrecisionPickingOperator(std::function setPrecisionPickingOperator) { _setPrecisionPickingOperator = setPrecisionPickingOperator; }
@@ -120,6 +121,16 @@ public:
EntityEditPacketSender* getPacketSender();
+ static void setAddMaterialToEntityOperator(std::function addMaterialToEntityOperator) { _addMaterialToEntityOperator = addMaterialToEntityOperator; }
+ static void setRemoveMaterialFromEntityOperator(std::function removeMaterialFromEntityOperator) { _removeMaterialFromEntityOperator = removeMaterialFromEntityOperator; }
+ static bool addMaterialToEntity(const QUuid& entityID, graphics::MaterialLayer material, const std::string& parentMaterialName);
+ static bool removeMaterialFromEntity(const QUuid& entityID, graphics::MaterialPointer material, const std::string& parentMaterialName);
+
+ static void setAddMaterialToAvatarOperator(std::function addMaterialToAvatarOperator) { _addMaterialToAvatarOperator = addMaterialToAvatarOperator; }
+ static void setRemoveMaterialFromAvatarOperator(std::function removeMaterialFromAvatarOperator) { _removeMaterialFromAvatarOperator = removeMaterialFromAvatarOperator; }
+ static bool addMaterialToAvatar(const QUuid& avatarID, graphics::MaterialLayer material, const std::string& parentMaterialName);
+ static bool removeMaterialFromAvatar(const QUuid& avatarID, graphics::MaterialPointer material, const std::string& parentMaterialName);
+
signals:
void enterEntity(const EntityItemID& entityItemID);
void leaveEntity(const EntityItemID& entityItemID);
@@ -255,6 +266,11 @@ private:
workload::SpacePointer _space{ new workload::Space() };
workload::Transaction::Updates _spaceUpdates;
+ static std::function _addMaterialToEntityOperator;
+ static std::function _removeMaterialFromEntityOperator;
+ static std::function _addMaterialToAvatarOperator;
+ static std::function _removeMaterialFromAvatarOperator;
+
};
diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp
index 83f0bdcff3..5c73b9576d 100644
--- a/libraries/entities-renderer/src/RenderableEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp
@@ -146,7 +146,6 @@ EntityRenderer::EntityRenderer(const EntityItemPointer& entity) : _created(entit
_needsRenderUpdate = true;
emit requestRenderUpdate();
});
- _materials = entity->getMaterials();
}
EntityRenderer::~EntityRenderer() { }
diff --git a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp
index 483f9ffe1c..2eb877b0e1 100644
--- a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp
@@ -14,42 +14,210 @@
using namespace render;
using namespace render::entities;
-bool MaterialEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const {
- if (entity->getMaterial() != _drawMaterial) {
- return true;
- }
- if (entity->getParentID() != _parentID) {
- return true;
- }
- if (entity->getMaterialMappingPos() != _materialMappingPos || entity->getMaterialMappingScale() != _materialMappingScale || entity->getMaterialMappingRot() != _materialMappingRot) {
+bool MaterialEntityRenderer::needsRenderUpdate() const {
+ if (_retryApply) {
return true;
}
if (!_texturesLoaded) {
return true;
}
+ return Parent::needsRenderUpdate();
+}
+
+bool MaterialEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const {
+ if (resultWithReadLock([&] {
+ if (entity->getMaterialMappingMode() != _materialMappingMode) {
+ return true;
+ }
+ if (entity->getMaterialRepeat() != _materialRepeat) {
+ return true;
+ }
+ if (entity->getMaterialMappingPos() != _materialMappingPos || entity->getMaterialMappingScale() != _materialMappingScale || entity->getMaterialMappingRot() != _materialMappingRot) {
+ return true;
+ }
+ if (entity->getTransform() != _transform) {
+ return true;
+ }
+ if (entity->getUnscaledDimensions() != _dimensions) {
+ return true;
+ }
+
+ if (entity->getMaterialURL() != _materialURL) {
+ return true;
+ }
+ if (entity->getMaterialData() != _materialData) {
+ return true;
+ }
+ if (entity->getParentMaterialName() != _parentMaterialName) {
+ return true;
+ }
+ if (entity->getParentID() != _parentID) {
+ return true;
+ }
+ if (entity->getPriority() != _priority) {
+ return true;
+ }
+
+ return false;
+ })) {
+ return true;
+ }
return false;
}
-void MaterialEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
+void MaterialEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
withWriteLock([&] {
- if (_drawMaterial != entity->getMaterial()) {
- _texturesLoaded = false;
- _drawMaterial = entity->getMaterial();
+ bool deleteNeeded = false;
+ bool addNeeded = _retryApply;
+ bool transformChanged = false;
+ {
+ MaterialMappingMode mode = entity->getMaterialMappingMode();
+ if (mode != _materialMappingMode) {
+ _materialMappingMode = mode;
+ transformChanged = true;
+ }
}
- _parentID = entity->getParentID();
- _materialMappingPos = entity->getMaterialMappingPos();
- _materialMappingScale = entity->getMaterialMappingScale();
- _materialMappingRot = entity->getMaterialMappingRot();
+ {
+ bool repeat = entity->getMaterialRepeat();
+ if (repeat != _materialRepeat) {
+ _materialRepeat = repeat;
+ transformChanged = true;
+ }
+ }
+ {
+ glm::vec2 mappingPos = entity->getMaterialMappingPos();
+ glm::vec2 mappingScale = entity->getMaterialMappingScale();
+ float mappingRot = entity->getMaterialMappingRot();
+ if (mappingPos != _materialMappingPos || mappingScale != _materialMappingScale || mappingRot != _materialMappingRot) {
+ _materialMappingPos = mappingPos;
+ _materialMappingScale = mappingScale;
+ _materialMappingRot = mappingRot;
+ transformChanged |= _materialMappingMode == MaterialMappingMode::UV;
+ }
+ }
+ {
+ Transform transform = entity->getTransform();
+ glm::vec3 dimensions = entity->getUnscaledDimensions();
+ if (transform != _transform || dimensions != _dimensions) {
+ _transform = transform;
+ _dimensions = dimensions;
+ transformChanged |= _materialMappingMode == MaterialMappingMode::PROJECTED;
+ }
+ }
+
+ {
+ auto material = getMaterial();
+ // Update the old material regardless of if it's going to change
+ if (transformChanged && material && !_parentID.isNull()) {
+ deleteNeeded = true;
+ addNeeded = true;
+ applyTextureTransform(material);
+ }
+ }
+
+ bool urlChanged = false;
+ std::string newCurrentMaterialName = _currentMaterialName;
+ {
+ QString materialURL = entity->getMaterialURL();
+ if (materialURL != _materialURL) {
+ _materialURL = materialURL;
+ if (_materialURL.contains("?")) {
+ auto split = _materialURL.split("?");
+ newCurrentMaterialName = split.last().toStdString();
+ }
+ urlChanged = true;
+ }
+ }
+
+ bool usingMaterialData = _materialURL.startsWith("materialData");
+ bool materialDataChanged = false;
+ QUuid oldParentID = _parentID;
+ QString oldParentMaterialName = _parentMaterialName;
+ {
+ QString materialData = entity->getMaterialData();
+ if (materialData != _materialData) {
+ _materialData = materialData;
+ if (usingMaterialData) {
+ materialDataChanged = true;
+ }
+ }
+ }
+ {
+ QString parentMaterialName = entity->getParentMaterialName();
+ if (parentMaterialName != _parentMaterialName) {
+ _parentMaterialName = parentMaterialName;
+ deleteNeeded = true;
+ addNeeded = true;
+ }
+ }
+ {
+ QUuid parentID = entity->getParentID();
+ if (parentID != _parentID) {
+ _parentID = parentID;
+ deleteNeeded = true;
+ addNeeded = true;
+ }
+ }
+ {
+ quint16 priority = entity->getPriority();
+ if (priority != _priority) {
+ _priority = priority;
+ deleteNeeded = true;
+ addNeeded = true;
+ }
+ }
+
+ if (urlChanged && !usingMaterialData) {
+ _networkMaterial = MaterialCache::instance().getMaterial(_materialURL);
+ auto onMaterialRequestFinished = [&, oldParentID, oldParentMaterialName, newCurrentMaterialName](bool success) {
+ if (success) {
+ deleteMaterial(oldParentID, oldParentMaterialName);
+ _texturesLoaded = false;
+ _parsedMaterials = _networkMaterial->parsedMaterials;
+ setCurrentMaterialName(newCurrentMaterialName);
+ applyMaterial();
+ } else {
+ deleteMaterial(oldParentID, oldParentMaterialName);
+ _retryApply = false;
+ _texturesLoaded = true;
+ }
+ };
+ if (_networkMaterial) {
+ if (_networkMaterial->isLoaded()) {
+ onMaterialRequestFinished(!_networkMaterial->isFailed());
+ } else {
+ connect(_networkMaterial.data(), &Resource::finished, this, onMaterialRequestFinished);
+ }
+ }
+ } else if (materialDataChanged && usingMaterialData) {
+ deleteMaterial(oldParentID, oldParentMaterialName);
+ _texturesLoaded = false;
+ _parsedMaterials = NetworkMaterialResource::parseJSONMaterials(QJsonDocument::fromJson(_materialData.toUtf8()), _materialURL);
+ // Since our material changed, the current name might not be valid anymore, so we need to update
+ setCurrentMaterialName(newCurrentMaterialName);
+ applyMaterial();
+ } else {
+ if (deleteNeeded) {
+ deleteMaterial(oldParentID, oldParentMaterialName);
+ }
+ if (addNeeded) {
+ applyMaterial();
+ }
+ }
+
+ {
+ auto material = getMaterial();
+ bool newTexturesLoaded = material ? !material->isMissingTexture() : false;
+ if (!_texturesLoaded && newTexturesLoaded) {
+ material->checkResetOpacityMap();
+ }
+ _texturesLoaded = newTexturesLoaded;
+ }
+
_renderTransform = getModelTransform();
const float MATERIAL_ENTITY_SCALE = 0.5f;
_renderTransform.postScale(MATERIAL_ENTITY_SCALE);
_renderTransform.postScale(ENTITY_ITEM_DEFAULT_DIMENSIONS);
-
- bool newTexturesLoaded = _drawMaterial ? !_drawMaterial->isMissingTexture() : false;
- if (!_texturesLoaded && newTexturesLoaded) {
- _drawMaterial->checkResetOpacityMap();
- }
- _texturesLoaded = newTexturesLoaded;
});
}
@@ -61,8 +229,9 @@ ItemKey MaterialEntityRenderer::getKey() {
builder.withInvisible();
}
- if (_drawMaterial) {
- auto matKey = _drawMaterial->getKey();
+ const auto drawMaterial = getMaterial();
+ if (drawMaterial) {
+ auto matKey = drawMaterial->getKey();
if (matKey.isTranslucent()) {
builder.withTransparent();
}
@@ -73,8 +242,9 @@ ItemKey MaterialEntityRenderer::getKey() {
ShapeKey MaterialEntityRenderer::getShapeKey() {
graphics::MaterialKey drawMaterialKey;
- if (_drawMaterial) {
- drawMaterialKey = _drawMaterial->getKey();
+ const auto drawMaterial = getMaterial();
+ if (drawMaterial) {
+ drawMaterialKey = drawMaterial->getKey();
}
bool isTranslucent = drawMaterialKey.isTranslucent();
@@ -112,18 +282,24 @@ void MaterialEntityRenderer::doRender(RenderArgs* args) {
// Don't render if our parent is set or our material is null
QUuid parentID;
+ withReadLock([&] {
+ parentID = _parentID;
+ });
+ if (!parentID.isNull()) {
+ return;
+ }
+
Transform renderTransform;
graphics::MaterialPointer drawMaterial;
Transform textureTransform;
withReadLock([&] {
- parentID = _parentID;
renderTransform = _renderTransform;
- drawMaterial = _drawMaterial;
+ drawMaterial = getMaterial();
textureTransform.setTranslation(glm::vec3(_materialMappingPos, 0));
textureTransform.setRotation(glm::vec3(0, 0, glm::radians(_materialMappingRot)));
textureTransform.setScale(glm::vec3(_materialMappingScale, 1));
});
- if (!parentID.isNull() || !drawMaterial) {
+ if (!drawMaterial) {
return;
}
@@ -142,3 +318,86 @@ void MaterialEntityRenderer::doRender(RenderArgs* args) {
args->_details._trianglesRendered += (int)DependencyManager::get()->getSphereTriangleCount();
}
+
+void MaterialEntityRenderer::setCurrentMaterialName(const std::string& currentMaterialName) {
+ if (_parsedMaterials.networkMaterials.find(currentMaterialName) != _parsedMaterials.networkMaterials.end()) {
+ _currentMaterialName = currentMaterialName;
+ } else if (_parsedMaterials.names.size() > 0) {
+ _currentMaterialName = _parsedMaterials.names[0];
+ }
+}
+
+std::shared_ptr MaterialEntityRenderer::getMaterial() const {
+ auto material = _parsedMaterials.networkMaterials.find(_currentMaterialName);
+ if (material != _parsedMaterials.networkMaterials.end()) {
+ return material->second;
+ } else {
+ return nullptr;
+ }
+}
+
+void MaterialEntityRenderer::deleteMaterial(const QUuid& oldParentID, const QString& oldParentMaterialName) {
+ std::shared_ptr material = _appliedMaterial;
+ if (!material || oldParentID.isNull()) {
+ return;
+ }
+
+ // Our parent could be an entity or an avatar
+ std::string oldParentMaterialNameStd = oldParentMaterialName.toStdString();
+ if (EntityTreeRenderer::removeMaterialFromEntity(oldParentID, material, oldParentMaterialNameStd)) {
+ _appliedMaterial = nullptr;
+ return;
+ }
+
+ if (EntityTreeRenderer::removeMaterialFromAvatar(oldParentID, material, oldParentMaterialNameStd)) {
+ _appliedMaterial = nullptr;
+ return;
+ }
+
+ // if a remove fails, our parent is gone, so we don't need to retry
+}
+
+void MaterialEntityRenderer::applyTextureTransform(std::shared_ptr& material) {
+ Transform textureTransform;
+ if (_materialMappingMode == MaterialMappingMode::UV) {
+ textureTransform.setTranslation(glm::vec3(_materialMappingPos, 0.0f));
+ textureTransform.setRotation(glm::vec3(0.0f, 0.0f, glm::radians(_materialMappingRot)));
+ textureTransform.setScale(glm::vec3(_materialMappingScale, 1.0f));
+ } else if (_materialMappingMode == MaterialMappingMode::PROJECTED) {
+ textureTransform = _transform;
+ textureTransform.postScale(_dimensions);
+ // Pass the inverse transform here so we don't need to compute it in the shaders
+ textureTransform.evalFromRawMatrix(textureTransform.getInverseMatrix());
+ }
+ material->setTextureTransforms(textureTransform, _materialMappingMode, _materialRepeat);
+}
+
+void MaterialEntityRenderer::applyMaterial() {
+ _retryApply = false;
+
+ std::shared_ptr material = getMaterial();
+ QUuid parentID = _parentID;
+ if (!material || parentID.isNull()) {
+ _appliedMaterial = nullptr;
+ return;
+ }
+
+ applyTextureTransform(material);
+
+ graphics::MaterialLayer materialLayer = graphics::MaterialLayer(material, _priority);
+
+ // Our parent could be an entity or an avatar
+ std::string parentMaterialName = _parentMaterialName.toStdString();
+ if (EntityTreeRenderer::addMaterialToEntity(parentID, materialLayer, parentMaterialName)) {
+ _appliedMaterial = material;
+ return;
+ }
+
+ if (EntityTreeRenderer::addMaterialToAvatar(parentID, materialLayer, parentMaterialName)) {
+ _appliedMaterial = material;
+ return;
+ }
+
+ // if we've reached this point, we couldn't find our parent, so we need to try again later
+ _retryApply = true;
+}
\ No newline at end of file
diff --git a/libraries/entities-renderer/src/RenderableMaterialEntityItem.h b/libraries/entities-renderer/src/RenderableMaterialEntityItem.h
index c90048ecf5..d714727c7a 100644
--- a/libraries/entities-renderer/src/RenderableMaterialEntityItem.h
+++ b/libraries/entities-renderer/src/RenderableMaterialEntityItem.h
@@ -13,6 +13,8 @@
#include
+#include
+
class NetworkMaterial;
namespace render { namespace entities {
@@ -22,22 +24,46 @@ class MaterialEntityRenderer : public TypedEntityRenderer {
using Pointer = std::shared_ptr;
public:
MaterialEntityRenderer(const EntityItemPointer& entity) : Parent(entity) {}
+ ~MaterialEntityRenderer() { deleteMaterial(_parentID, _parentMaterialName); }
private:
+ virtual bool needsRenderUpdate() const override;
virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override;
- virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;
+ virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override;
virtual void doRender(RenderArgs* args) override;
ItemKey getKey() override;
ShapeKey getShapeKey() override;
+ QString _materialURL;
+ QString _materialData;
+ QString _parentMaterialName;
+ quint16 _priority;
QUuid _parentID;
+
+ MaterialMappingMode _materialMappingMode;
+ bool _materialRepeat;
glm::vec2 _materialMappingPos;
glm::vec2 _materialMappingScale;
float _materialMappingRot;
- bool _texturesLoaded { false };
+ Transform _transform;
+ glm::vec3 _dimensions;
+
+ bool _texturesLoaded { false };
+ bool _retryApply { false };
+
+ std::shared_ptr getMaterial() const;
+ void setCurrentMaterialName(const std::string& currentMaterialName);
+
+ void applyTextureTransform(std::shared_ptr& material);
+ void applyMaterial();
+ void deleteMaterial(const QUuid& oldParentID, const QString& oldParentMaterialName);
+
+ NetworkMaterialResourcePointer _networkMaterial;
+ NetworkMaterialResource::ParsedMaterials _parsedMaterials;
+ std::shared_ptr _appliedMaterial;
+ std::string _currentMaterialName;
- std::shared_ptr _drawMaterial;
};
} }
diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp
index 2c6d679b46..c0b12c4d1f 100644
--- a/libraries/entities/src/EntityItem.cpp
+++ b/libraries/entities/src/EntityItem.cpp
@@ -3250,25 +3250,6 @@ void EntityItem::setSpaceIndex(int32_t index) {
void EntityItem::preDelete() {
}
-void EntityItem::addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) {
- std::lock_guard lock(_materialsLock);
- _materials[parentMaterialName].push(material);
-}
-
-void EntityItem::removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) {
- std::lock_guard lock(_materialsLock);
- _materials[parentMaterialName].remove(material);
-}
-
-std::unordered_map EntityItem::getMaterials() {
- std::unordered_map toReturn;
- {
- std::lock_guard lock(_materialsLock);
- toReturn = _materials;
- }
- return toReturn;
-}
-
bool EntityItem::getCloneable() const {
bool result;
withReadLock([&] {
diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h
index 5d6627d461..824261c022 100644
--- a/libraries/entities/src/EntityItem.h
+++ b/libraries/entities/src/EntityItem.h
@@ -37,8 +37,6 @@
#include "EntityDynamicInterface.h"
#include "GrabPropertyGroup.h"
-#include "graphics/Material.h"
-
class EntitySimulation;
class EntityTreeElement;
class EntityTreeElementExtraEncodeData;
@@ -542,10 +540,6 @@ public:
virtual void preDelete();
virtual void postParentFixup() {}
- void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName);
- void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName);
- std::unordered_map getMaterials();
-
void setSimulationOwnershipExpiry(uint64_t expiry) { _simulationOwnershipExpiry = expiry; }
uint64_t getSimulationOwnershipExpiry() const { return _simulationOwnershipExpiry; }
@@ -754,11 +748,7 @@ protected:
QHash _grabActions;
private:
- std::unordered_map _materials;
- std::mutex _materialsLock;
-
static std::function _getBillboardRotationOperator;
-
};
#endif // hifi_EntityItem_h
diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp
index cfae8e250b..53b4fb4fe4 100644
--- a/libraries/entities/src/EntityTree.cpp
+++ b/libraries/entities/src/EntityTree.cpp
@@ -2953,41 +2953,9 @@ QStringList EntityTree::getJointNames(const QUuid& entityID) const {
return entity->getJointNames();
}
-std::function EntityTree::_addMaterialToEntityOperator = nullptr;
-std::function EntityTree::_removeMaterialFromEntityOperator = nullptr;
-std::function EntityTree::_addMaterialToAvatarOperator = nullptr;
-std::function EntityTree::_removeMaterialFromAvatarOperator = nullptr;
std::function EntityTree::_getEntityObjectOperator = nullptr;
std::function EntityTree::_textSizeOperator = nullptr;
-bool EntityTree::addMaterialToEntity(const QUuid& entityID, graphics::MaterialLayer material, const std::string& parentMaterialName) {
- if (_addMaterialToEntityOperator) {
- return _addMaterialToEntityOperator(entityID, material, parentMaterialName);
- }
- return false;
-}
-
-bool EntityTree::removeMaterialFromEntity(const QUuid& entityID, graphics::MaterialPointer material, const std::string& parentMaterialName) {
- if (_removeMaterialFromEntityOperator) {
- return _removeMaterialFromEntityOperator(entityID, material, parentMaterialName);
- }
- return false;
-}
-
-bool EntityTree::addMaterialToAvatar(const QUuid& avatarID, graphics::MaterialLayer material, const std::string& parentMaterialName) {
- if (_addMaterialToAvatarOperator) {
- return _addMaterialToAvatarOperator(avatarID, material, parentMaterialName);
- }
- return false;
-}
-
-bool EntityTree::removeMaterialFromAvatar(const QUuid& avatarID, graphics::MaterialPointer material, const std::string& parentMaterialName) {
- if (_removeMaterialFromAvatarOperator) {
- return _removeMaterialFromAvatarOperator(avatarID, material, parentMaterialName);
- }
- return false;
-}
-
QObject* EntityTree::getEntityObject(const QUuid& id) {
if (_getEntityObjectOperator) {
return _getEntityObjectOperator(id);
diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h
index 9df01267ea..dcce0e4b99 100644
--- a/libraries/entities/src/EntityTree.h
+++ b/libraries/entities/src/EntityTree.h
@@ -262,16 +262,6 @@ public:
void setIsServerlessMode(bool value) { _serverlessDomain = value; }
bool isServerlessMode() const { return _serverlessDomain; }
- static void setAddMaterialToEntityOperator(std::function addMaterialToEntityOperator) { _addMaterialToEntityOperator = addMaterialToEntityOperator; }
- static void setRemoveMaterialFromEntityOperator(std::function removeMaterialFromEntityOperator) { _removeMaterialFromEntityOperator = removeMaterialFromEntityOperator; }
- static bool addMaterialToEntity(const QUuid& entityID, graphics::MaterialLayer material, const std::string& parentMaterialName);
- static bool removeMaterialFromEntity(const QUuid& entityID, graphics::MaterialPointer material, const std::string& parentMaterialName);
-
- static void setAddMaterialToAvatarOperator(std::function addMaterialToAvatarOperator) { _addMaterialToAvatarOperator = addMaterialToAvatarOperator; }
- static void setRemoveMaterialFromAvatarOperator(std::function removeMaterialFromAvatarOperator) { _removeMaterialFromAvatarOperator = removeMaterialFromAvatarOperator; }
- static bool addMaterialToAvatar(const QUuid& avatarID, graphics::MaterialLayer material, const std::string& parentMaterialName);
- static bool removeMaterialFromAvatar(const QUuid& avatarID, graphics::MaterialPointer material, const std::string& parentMaterialName);
-
static void setGetEntityObjectOperator(std::function getEntityObjectOperator) { _getEntityObjectOperator = getEntityObjectOperator; }
static QObject* getEntityObject(const QUuid& id);
@@ -386,10 +376,6 @@ private:
std::shared_ptr _myAvatar{ nullptr };
- static std::function _addMaterialToEntityOperator;
- static std::function _removeMaterialFromEntityOperator;
- static std::function _addMaterialToAvatarOperator;
- static std::function _removeMaterialFromAvatarOperator;
static std::function _getEntityObjectOperator;
static std::function _textSizeOperator;
diff --git a/libraries/entities/src/MaterialEntityItem.cpp b/libraries/entities/src/MaterialEntityItem.cpp
index 1baa0b213a..3a363f2e83 100644
--- a/libraries/entities/src/MaterialEntityItem.cpp
+++ b/libraries/entities/src/MaterialEntityItem.cpp
@@ -16,9 +16,6 @@
EntityItemPointer MaterialEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
Pointer entity(new MaterialEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); });
entity->setProperties(properties);
- // When you reload content, setProperties doesn't have any of the propertiesChanged flags set, so it won't trigger a material add
- entity->removeMaterial();
- entity->applyMaterial();
return entity;
}
@@ -27,10 +24,6 @@ MaterialEntityItem::MaterialEntityItem(const EntityItemID& entityItemID) : Entit
_type = EntityTypes::Material;
}
-MaterialEntityItem::~MaterialEntityItem() {
- removeMaterial();
-}
-
EntityItemProperties MaterialEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const {
EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class
COPY_ENTITY_PROPERTY_TO_PROPERTIES(materialURL, getMaterialURL);
@@ -131,7 +124,6 @@ void MaterialEntityItem::debugDump() const {
qCDebug(entities) << " MATERIAL EntityItem id:" << getEntityItemID() << "---------------------------------------------";
qCDebug(entities) << " name:" << _name;
qCDebug(entities) << " material url:" << _materialURL;
- qCDebug(entities) << " current material name:" << _currentMaterialName.c_str();
qCDebug(entities) << " material mapping mode:" << _materialMappingMode;
qCDebug(entities) << " material repeat:" << _materialRepeat;
qCDebug(entities) << " priority:" << _priority;
@@ -154,208 +146,101 @@ void MaterialEntityItem::setUnscaledDimensions(const glm::vec3& value) {
}
}
-std::shared_ptr MaterialEntityItem::getMaterial() const {
- auto material = _parsedMaterials.networkMaterials.find(_currentMaterialName);
- if (material != _parsedMaterials.networkMaterials.end()) {
- return material->second;
- } else {
- return nullptr;
- }
+QString MaterialEntityItem::getMaterialURL() const {
+ return resultWithReadLock([&] {
+ return _materialURL;
+ });
}
-void MaterialEntityItem::setMaterialURL(const QString& materialURLString, bool materialDataChanged) {
- bool usingMaterialData = materialDataChanged || materialURLString.startsWith("materialData");
- if (_materialURL != materialURLString || (usingMaterialData && materialDataChanged)) {
- removeMaterial();
- _materialURL = materialURLString;
-
- if (materialURLString.contains("?")) {
- auto split = materialURLString.split("?");
- _currentMaterialName = split.last().toStdString();
- }
-
- if (usingMaterialData) {
- _parsedMaterials = NetworkMaterialResource::parseJSONMaterials(QJsonDocument::fromJson(getMaterialData().toUtf8()), materialURLString);
-
- // Since our material changed, the current name might not be valid anymore, so we need to update
- setCurrentMaterialName(_currentMaterialName);
- applyMaterial();
- } else {
- _networkMaterial = MaterialCache::instance().getMaterial(materialURLString);
- auto onMaterialRequestFinished = [&](bool success) {
- if (success) {
- _parsedMaterials = _networkMaterial->parsedMaterials;
-
- setCurrentMaterialName(_currentMaterialName);
- applyMaterial();
- }
- };
- if (_networkMaterial) {
- if (_networkMaterial->isLoaded()) {
- onMaterialRequestFinished(!_networkMaterial->isFailed());
- } else {
- connect(_networkMaterial.data(), &Resource::finished, this, onMaterialRequestFinished);
- }
- }
- }
- }
+void MaterialEntityItem::setMaterialURL(const QString& materialURL) {
+ withWriteLock([&] {
+ _materialURL = materialURL;
+ });
}
-void MaterialEntityItem::setCurrentMaterialName(const std::string& currentMaterialName) {
- if (_parsedMaterials.networkMaterials.find(currentMaterialName) != _parsedMaterials.networkMaterials.end()) {
- _currentMaterialName = currentMaterialName;
- } else if (_parsedMaterials.names.size() > 0) {
- _currentMaterialName = _parsedMaterials.names[0];
- }
+QString MaterialEntityItem::getMaterialData() const {
+ return resultWithReadLock([&] {
+ return _materialData;
+ });
}
void MaterialEntityItem::setMaterialData(const QString& materialData) {
- if (_materialData != materialData) {
+ withWriteLock([&] {
_materialData = materialData;
- if (_materialURL.startsWith("materialData")) {
- // Trigger material update when material data changes
- setMaterialURL(_materialURL, true);
- }
- }
+ });
+}
+
+MaterialMappingMode MaterialEntityItem::getMaterialMappingMode() const {
+ return resultWithReadLock([&] {
+ return _materialMappingMode;
+ });
}
void MaterialEntityItem::setMaterialMappingMode(MaterialMappingMode mode) {
- if (_materialMappingMode != mode) {
- removeMaterial();
+ withWriteLock([&] {
_materialMappingMode = mode;
- setUnscaledDimensions(_desiredDimensions);
- applyMaterial();
- }
+ });
+ setUnscaledDimensions(_desiredDimensions);
}
-void MaterialEntityItem::setMaterialRepeat(bool repeat) {
- if (_materialRepeat != repeat) {
- removeMaterial();
- _materialRepeat = repeat;
- applyMaterial();
- }
-}
-
-void MaterialEntityItem::setMaterialMappingPos(const glm::vec2& materialMappingPos) {
- if (_materialMappingPos != materialMappingPos) {
- removeMaterial();
- _materialMappingPos = materialMappingPos;
- applyMaterial();
- }
-}
-
-void MaterialEntityItem::setMaterialMappingScale(const glm::vec2& materialMappingScale) {
- if (_materialMappingScale != materialMappingScale) {
- removeMaterial();
- _materialMappingScale = materialMappingScale;
- applyMaterial();
- }
-}
-
-void MaterialEntityItem::setMaterialMappingRot(const float& materialMappingRot) {
- if (_materialMappingRot != materialMappingRot) {
- removeMaterial();
- _materialMappingRot = materialMappingRot;
- applyMaterial();
- }
+quint16 MaterialEntityItem::getPriority() const {
+ return resultWithReadLock([&] {
+ return _priority;
+ });
}
void MaterialEntityItem::setPriority(quint16 priority) {
- if (_priority != priority) {
- removeMaterial();
+ withWriteLock([&] {
_priority = priority;
- applyMaterial();
- }
+ });
+}
+
+QString MaterialEntityItem::getParentMaterialName() const {
+ return resultWithReadLock([&] {
+ return _parentMaterialName;
+ });
}
void MaterialEntityItem::setParentMaterialName(const QString& parentMaterialName) {
- if (_parentMaterialName != parentMaterialName) {
- removeMaterial();
+ withWriteLock([&] {
_parentMaterialName = parentMaterialName;
- applyMaterial();
- }
+ });
}
-void MaterialEntityItem::setParentID(const QUuid& parentID) {
- if (getParentID() != parentID) {
- removeMaterial();
- EntityItem::setParentID(parentID);
- applyMaterial();
- }
+glm::vec2 MaterialEntityItem::getMaterialMappingPos() const {
+ return resultWithReadLock([&] {
+ return _materialMappingPos;
+ });
}
-void MaterialEntityItem::locationChanged(bool tellPhysics) {
- EntityItem::locationChanged();
- if (_materialMappingMode == MaterialMappingMode::PROJECTED) {
- removeMaterial();
- applyMaterial();
- }
+void MaterialEntityItem::setMaterialMappingPos(const glm::vec2& materialMappingPos) {
+ withWriteLock([&] {
+ _materialMappingPos = materialMappingPos;
+ });
}
-void MaterialEntityItem::dimensionsChanged() {
- EntityItem::dimensionsChanged();
- if (_materialMappingMode == MaterialMappingMode::PROJECTED) {
- removeMaterial();
- applyMaterial();
- }
+glm::vec2 MaterialEntityItem::getMaterialMappingScale() const {
+ return resultWithReadLock([&] {
+ return _materialMappingScale;
+ });
}
-void MaterialEntityItem::removeMaterial() {
- graphics::MaterialPointer material = getMaterial();
- if (!material) {
- return;
- }
- QUuid parentID = getParentID();
- if (parentID.isNull()) {
- return;
- }
-
- // Our parent could be an entity or an avatar
- if (EntityTree::removeMaterialFromEntity(parentID, material, getParentMaterialName().toStdString())) {
- return;
- }
-
- if (EntityTree::removeMaterialFromAvatar(parentID, material, getParentMaterialName().toStdString())) {
- return;
- }
-
- // if a remove fails, our parent is gone, so we don't need to retry
+void MaterialEntityItem::setMaterialMappingScale(const glm::vec2& materialMappingScale) {
+ withWriteLock([&] {
+ _materialMappingScale = materialMappingScale;
+ });
}
-void MaterialEntityItem::applyMaterial() {
- _retryApply = false;
- graphics::MaterialPointer material = getMaterial();
- QUuid parentID = getParentID();
- if (!material || parentID.isNull()) {
- return;
- }
+float MaterialEntityItem::getMaterialMappingRot() const {
+ return resultWithReadLock([&] {
+ return _materialMappingRot;
+ });
+}
- Transform textureTransform;
- if (_materialMappingMode == MaterialMappingMode::UV) {
- textureTransform.setTranslation(glm::vec3(_materialMappingPos, 0.0f));
- textureTransform.setRotation(glm::vec3(0.0f, 0.0f, glm::radians(_materialMappingRot)));
- textureTransform.setScale(glm::vec3(_materialMappingScale, 1.0f));
- } else if (_materialMappingMode == MaterialMappingMode::PROJECTED) {
- textureTransform = getTransform();
- textureTransform.postScale(getUnscaledDimensions());
- // Pass the inverse transform here so we don't need to compute it in the shaders
- textureTransform.evalFromRawMatrix(textureTransform.getInverseMatrix());
- }
- material->setTextureTransforms(textureTransform, _materialMappingMode, _materialRepeat);
-
- graphics::MaterialLayer materialLayer = graphics::MaterialLayer(material, getPriority());
-
- // Our parent could be an entity or an avatar
- if (EntityTree::addMaterialToEntity(parentID, materialLayer, getParentMaterialName().toStdString())) {
- return;
- }
-
- if (EntityTree::addMaterialToAvatar(parentID, materialLayer, getParentMaterialName().toStdString())) {
- return;
- }
-
- // if we've reached this point, we couldn't find our parent, so we need to try again later
- _retryApply = true;
+void MaterialEntityItem::setMaterialMappingRot(float materialMappingRot) {
+ withWriteLock([&] {
+ _materialMappingRot = materialMappingRot;
+ });
}
AACube MaterialEntityItem::calculateInitialQueryAACube(bool& success) {
@@ -372,18 +257,3 @@ AACube MaterialEntityItem::calculateInitialQueryAACube(bool& success) {
}
return aaCube;
}
-
-void MaterialEntityItem::postParentFixup() {
- removeMaterial();
- _queryAACubeSet = false; // force an update so we contain our parent
- updateQueryAACube();
- applyMaterial();
-}
-
-void MaterialEntityItem::update(const quint64& now) {
- if (_retryApply) {
- applyMaterial();
- }
-
- EntityItem::update(now);
-}
diff --git a/libraries/entities/src/MaterialEntityItem.h b/libraries/entities/src/MaterialEntityItem.h
index 069c71c1d6..b9e83a7fe5 100644
--- a/libraries/entities/src/MaterialEntityItem.h
+++ b/libraries/entities/src/MaterialEntityItem.h
@@ -12,8 +12,6 @@
#include "EntityItem.h"
#include "MaterialMappingMode.h"
-#include
-#include
class MaterialEntityItem : public EntityItem {
using Pointer = std::shared_ptr;
@@ -21,13 +19,9 @@ public:
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
MaterialEntityItem(const EntityItemID& entityItemID);
- ~MaterialEntityItem();
ALLOW_INSTANTIATION // This class can be instantiated
- void update(const quint64& now) override;
- bool needsToCallUpdate() const override { return true; }
-
// methods for getting/setting all properties of an entity
virtual EntityItemProperties getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const override;
virtual bool setProperties(const EntityItemProperties& properties) override;
@@ -52,44 +46,30 @@ public:
virtual void setUnscaledDimensions(const glm::vec3& value) override;
- QString getMaterialURL() const { return _materialURL; }
- void setMaterialURL(const QString& materialURLString, bool materialDataChanged = false);
+ QString getMaterialURL() const;
+ void setMaterialURL(const QString& materialURL);
- void setCurrentMaterialName(const std::string& currentMaterialName);
+ QString getMaterialData() const;
+ void setMaterialData(const QString& materialData);
- MaterialMappingMode getMaterialMappingMode() const { return _materialMappingMode; }
+ MaterialMappingMode getMaterialMappingMode() const;
void setMaterialMappingMode(MaterialMappingMode mode);
bool getMaterialRepeat() const { return _materialRepeat; }
- void setMaterialRepeat(bool repeat);
+ void setMaterialRepeat(bool repeat) { _materialRepeat = repeat; }
- quint16 getPriority() const { return _priority; }
+ quint16 getPriority() const;
void setPriority(quint16 priority);
- QString getParentMaterialName() const { return _parentMaterialName; }
+ QString getParentMaterialName() const;
void setParentMaterialName(const QString& parentMaterialName);
- glm::vec2 getMaterialMappingPos() const { return _materialMappingPos; }
+ glm::vec2 getMaterialMappingPos() const;
void setMaterialMappingPos(const glm::vec2& materialMappingPos);
- glm::vec2 getMaterialMappingScale() const { return _materialMappingScale; }
+ glm::vec2 getMaterialMappingScale() const;
void setMaterialMappingScale(const glm::vec2& materialMappingScale);
- float getMaterialMappingRot() const { return _materialMappingRot; }
- void setMaterialMappingRot(const float& materialMappingRot);
-
- QString getMaterialData() const { return _materialData; }
- void setMaterialData(const QString& materialData);
-
- std::shared_ptr getMaterial() const;
-
- void setParentID(const QUuid& parentID) override;
-
- void locationChanged(bool tellPhysics) override;
- void dimensionsChanged() override;
-
- void applyMaterial();
- void removeMaterial();
-
- void postParentFixup() override;
+ float getMaterialMappingRot() const;
+ void setMaterialMappingRot(float materialMappingRot);
AACube calculateInitialQueryAACube(bool& success) override;
@@ -128,12 +108,6 @@ private:
float _materialMappingRot { 0 };
QString _materialData;
- NetworkMaterialResourcePointer _networkMaterial;
- NetworkMaterialResource::ParsedMaterials _parsedMaterials;
- std::string _currentMaterialName;
-
- bool _retryApply { false };
-
};
#endif // hifi_MaterialEntityItem_h
diff --git a/libraries/material-networking/src/material-networking/TextureCache.cpp b/libraries/material-networking/src/material-networking/TextureCache.cpp
index ee3c88f02c..9a9720c87d 100644
--- a/libraries/material-networking/src/material-networking/TextureCache.cpp
+++ b/libraries/material-networking/src/material-networking/TextureCache.cpp
@@ -368,16 +368,17 @@ static bool isLocalUrl(const QUrl& url) {
return (scheme == HIFI_URL_SCHEME_FILE || scheme == URL_SCHEME_QRC || scheme == RESOURCE_SCHEME);
}
-void NetworkTexture::setExtra(void* extra) {
+void NetworkTexture::setExtra(void* extra, bool isNewExtra) {
const TextureExtra* textureExtra = static_cast(extra);
_type = textureExtra ? textureExtra->type : image::TextureUsage::DEFAULT_TEXTURE;
_maxNumPixels = textureExtra ? textureExtra->maxNumPixels : ABSOLUTE_MAX_TEXTURE_NUM_PIXELS;
_sourceChannel = textureExtra ? textureExtra->sourceChannel : image::ColorChannel::NONE;
- if (_textureSource) {
- _textureSource->setUrl(_url);
- _textureSource->setType((int)_type);
- } else {
+ if (isNewExtra && !_loaded) {
+ _startedLoading = false;
+ }
+
+ if (!_textureSource || isNewExtra) {
_textureSource = std::make_shared(_url, (int)_type);
}
_lowestRequestedMipLevel = 0;
diff --git a/libraries/material-networking/src/material-networking/TextureCache.h b/libraries/material-networking/src/material-networking/TextureCache.h
index acca916acc..a8b152c40e 100644
--- a/libraries/material-networking/src/material-networking/TextureCache.h
+++ b/libraries/material-networking/src/material-networking/TextureCache.h
@@ -64,7 +64,7 @@ public:
Q_INVOKABLE void setOriginalDescriptor(ktx::KTXDescriptor* descriptor) { _originalKtxDescriptor.reset(descriptor); }
- void setExtra(void* extra) override;
+ void setExtra(void* extra, bool isNewExtra) override;
signals:
void networkTextureCreated(const QWeakPointer& self);
diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp
index 581196b2cc..b2645d20c8 100644
--- a/libraries/model-networking/src/model-networking/ModelCache.cpp
+++ b/libraries/model-networking/src/model-networking/ModelCache.cpp
@@ -309,7 +309,7 @@ public:
virtual void downloadFinished(const QByteArray& data) override;
- void setExtra(void* extra) override;
+ void setExtra(void* extra, bool isNewExtra) override;
protected:
Q_INVOKABLE void setGeometryDefinition(HFMModel::Pointer hfmModel, QVariantHash mapping);
@@ -320,7 +320,7 @@ private:
bool _combineParts;
};
-void GeometryDefinitionResource::setExtra(void* extra) {
+void GeometryDefinitionResource::setExtra(void* extra, bool isNewExtra) {
const GeometryExtra* geometryExtra = static_cast(extra);
_mapping = geometryExtra ? geometryExtra->mapping : QVariantHash();
_textureBaseUrl = geometryExtra ? resolveTextureBaseUrl(_url, geometryExtra->textureBaseUrl) : QUrl();
diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp
index 7345081380..8ad1b41020 100644
--- a/libraries/networking/src/ResourceCache.cpp
+++ b/libraries/networking/src/ResourceCache.cpp
@@ -355,7 +355,7 @@ QSharedPointer ResourceCache::getResource(const QUrl& url, const QUrl&
} else if (resourcesWithExtraHash.size() > 0.0f) {
// We haven't seen this extra info before, but we've already downloaded the resource. We need a new copy of this object (with any old hash).
resource = createResourceCopy(resourcesWithExtraHash.begin().value().lock());
- resource->setExtra(extra);
+ resource->setExtra(extra, true);
resource->setExtraHash(extraHash);
resource->setSelf(resource);
resource->setCache(this);
@@ -375,7 +375,7 @@ QSharedPointer ResourceCache::getResource(const QUrl& url, const QUrl&
if (!resource) {
resource = createResource(url);
- resource->setExtra(extra);
+ resource->setExtra(extra, false);
resource->setExtraHash(extraHash);
resource->setSelf(resource);
resource->setCache(this);
diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h
index 2096213273..62800a6ac2 100644
--- a/libraries/networking/src/ResourceCache.h
+++ b/libraries/networking/src/ResourceCache.h
@@ -417,7 +417,7 @@ public:
unsigned int getDownloadAttempts() { return _attempts; }
unsigned int getDownloadAttemptsRemaining() { return _attemptsRemaining; }
- virtual void setExtra(void* extra) {};
+ virtual void setExtra(void* extra, bool isNewExtra) {};
void setExtraHash(size_t extraHash) { _extraHash = extraHash; }
size_t getExtraHash() const { return _extraHash; }
diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp
index b1ca24de1f..ee53347158 100644
--- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp
+++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp
@@ -205,7 +205,7 @@ gpu::TexturePointer AmbientOcclusionFramebuffer::getNormalTexture() {
}
AmbientOcclusionEffectConfig::AmbientOcclusionEffectConfig() :
- render::GPUJobConfig::Persistent(QStringList() << "Render" << "Engine" << "Ambient Occlusion", false),
+ render::GPUJobConfig::Persistent(QStringList() << "Render" << "Engine" << "Ambient Occlusion"),
perspectiveScale{ 1.0f },
edgeSharpness{ 1.0f },
blurRadius{ 4 },
diff --git a/libraries/render-utils/src/LightingModel.h b/libraries/render-utils/src/LightingModel.h
index 571eadb60b..f6bd6dcd46 100644
--- a/libraries/render-utils/src/LightingModel.h
+++ b/libraries/render-utils/src/LightingModel.h
@@ -118,7 +118,7 @@ protected:
float enableSkinning{ 1.0f };
float enableBlendshape{ 1.0f };
- float enableAmbientOcclusion{ 0.0f };
+ float enableAmbientOcclusion{ 0.0f }; // false by default
float enableShadow{ 1.0f };
float spare1{ 1.0f };
float spare2{ 1.0f };
@@ -196,15 +196,13 @@ public:
bool enableSkinning{ true };
bool enableBlendshape{ true };
- bool enableAmbientOcclusion{ true };
+ bool enableAmbientOcclusion{ false }; // false by default
bool enableShadow{ true };
void setAmbientOcclusion(bool enable) { enableAmbientOcclusion = enable; emit dirty();}
bool isAmbientOcclusionEnabled() const { return enableAmbientOcclusion; }
- void setShadow(bool enable) {
- enableShadow = enable; emit dirty();
- }
+ void setShadow(bool enable) { enableShadow = enable; emit dirty(); }
bool isShadowEnabled() const { return enableShadow; }
signals:
diff --git a/scripts/developer/utilities/render/deferredLighting.qml b/scripts/developer/utilities/render/deferredLighting.qml
index 6d98e96780..f5c0b8c5da 100644
--- a/scripts/developer/utilities/render/deferredLighting.qml
+++ b/scripts/developer/utilities/render/deferredLighting.qml
@@ -47,7 +47,7 @@ Rectangle {
"Lightmap:LightingModel:enableLightmap",
"Background:LightingModel:enableBackground",
"Haze:LightingModel:enableHaze",
- "ssao:LightingModel:enableAmbientOcclusion",
+ "AO:LightingModel:enableAmbientOcclusion",
"Textures:LightingModel:enableMaterialTexturing"
]
HifiControls.CheckBox {
diff --git a/scripts/system/away.js b/scripts/system/away.js
index a2e73ae63c..45b6f43b73 100644
--- a/scripts/system/away.js
+++ b/scripts/system/away.js
@@ -45,7 +45,8 @@ var OVERLAY_DATA_HMD = {
emissive: true,
drawInFront: true,
parentID: MyAvatar.SELF_ID,
- parentJointIndex: CAMERA_MATRIX
+ parentJointIndex: CAMERA_MATRIX,
+ ignorePickIntersection: true
};
var AWAY_INTRO = {
diff --git a/scripts/system/controllers/controllerModules/teleport.js b/scripts/system/controllers/controllerModules/teleport.js
index 8770ae8dde..23457cdd85 100644
--- a/scripts/system/controllers/controllerModules/teleport.js
+++ b/scripts/system/controllers/controllerModules/teleport.js
@@ -196,14 +196,14 @@ Script.include("/~/system/libraries/controllers.js");
var playAreaOverlayProperties = {
dimensions:
- Vec3.multiply(this.teleportScaleFactor * avatarScale, {
- x: this.playArea.width,
- y: this.PLAY_AREA_OVERLAY_MODEL_DIMENSIONS.y,
- z: this.playArea.height
+ Vec3.multiply(_this.teleportScaleFactor * avatarScale, {
+ x: _this.playArea.width,
+ y: _this.PLAY_AREA_OVERLAY_MODEL_DIMENSIONS.y,
+ z: _this.playArea.height
})
};
- if (this.teleportScaleFactor < 1) {
+ if (_this.teleportScaleFactor < 1) {
// Adjust position of playAreOverlay so that its base is at correct height.
// Always parenting to teleport target is good enough for this.
var sensorToWorldMatrix = MyAvatar.sensorToWorldMatrix;
@@ -212,37 +212,37 @@ Script.include("/~/system/libraries/controllers.js");
var avatarSensorPosition = Mat4.transformPoint(worldToSensorMatrix, MyAvatar.position);
avatarSensorPosition.y = 0;
- var targetRotation = Overlays.getProperty(this.targetOverlayID, "rotation");
+ var targetRotation = Overlays.getProperty(_this.targetOverlayID, "rotation");
var relativePlayAreaCenterOffset =
- Vec3.sum(this.playAreaCenterOffset, { x: 0, y: -TARGET_MODEL_DIMENSIONS.y / 2, z: 0 });
+ Vec3.sum(_this.playAreaCenterOffset, { x: 0, y: -TARGET_MODEL_DIMENSIONS.y / 2, z: 0 });
var localPosition = Vec3.multiplyQbyV(Quat.inverse(targetRotation),
Vec3.multiplyQbyV(sensorToWorldRotation,
Vec3.multiply(avatarScale, Vec3.subtract(relativePlayAreaCenterOffset, avatarSensorPosition))));
- localPosition.y = this.teleportScaleFactor * localPosition.y;
+ localPosition.y = _this.teleportScaleFactor * localPosition.y;
- playAreaOverlayProperties.parentID = this.targetOverlayID;
+ playAreaOverlayProperties.parentID = _this.targetOverlayID;
playAreaOverlayProperties.localPosition = localPosition;
}
- Overlays.editOverlay(this.playAreaOverlay, playAreaOverlayProperties);
+ Overlays.editOverlay(_this.playAreaOverlay, playAreaOverlayProperties);
- for (var i = 0; i < this.playAreaSensorPositionOverlays.length; i++) {
- localPosition = this.playAreaSensorPositions[i];
+ for (var i = 0; i < _this.playAreaSensorPositionOverlays.length; i++) {
+ localPosition = _this.playAreaSensorPositions[i];
localPosition = Vec3.multiply(avatarScale, localPosition);
// Position relative to the play area.
- localPosition.y = avatarScale * (this.PLAY_AREA_SENSOR_OVERLAY_DIMENSIONS.y / 2
- - this.PLAY_AREA_OVERLAY_MODEL_DIMENSIONS.y / 2);
- Overlays.editOverlay(this.playAreaSensorPositionOverlays[i], {
- dimensions: Vec3.multiply(this.teleportScaleFactor * avatarScale, this.PLAY_AREA_SENSOR_OVERLAY_DIMENSIONS),
- parentID: this.playAreaOverlay,
+ localPosition.y = avatarScale * (_this.PLAY_AREA_SENSOR_OVERLAY_DIMENSIONS.y / 2
+ - _this.PLAY_AREA_OVERLAY_MODEL_DIMENSIONS.y / 2);
+ Overlays.editOverlay(_this.playAreaSensorPositionOverlays[i], {
+ dimensions: Vec3.multiply(_this.teleportScaleFactor * avatarScale, _this.PLAY_AREA_SENSOR_OVERLAY_DIMENSIONS),
+ parentID: _this.playAreaOverlay,
localPosition: localPosition
});
}
};
this.updatePlayAreaScale = function () {
- if (this.isPlayAreaAvailable) {
- this.setPlayAreaDimensions();
+ if (_this.isPlayAreaAvailable) {
+ _this.setPlayAreaDimensions();
}
};
@@ -265,7 +265,7 @@ Script.include("/~/system/libraries/controllers.js");
for (var i = 0, length = teleportRenderStates.length; i < length; i++) {
var state = properties.renderStates[teleportRenderStates[i].name];
if (state && state.end) {
- Selection.addToSelectedItemsList(this.teleporterSelectionName, "overlay", state.end);
+ Selection.addToSelectedItemsList(_this.teleporterSelectionName, "overlay", state.end);
}
}
};
@@ -448,34 +448,34 @@ Script.include("/~/system/libraries/controllers.js");
this.translateZAction = Controller.findAction("TranslateZ");
this.setPlayAreaVisible = function (visible, targetOverlayID, fade) {
- if (!this.isPlayAreaAvailable || this.isPlayAreaVisible === visible) {
+ if (!_this.isPlayAreaAvailable || _this.isPlayAreaVisible === visible) {
return;
}
- this.wasPlayAreaVisible = this.isPlayAreaVisible;
- this.isPlayAreaVisible = visible;
- this.targetOverlayID = targetOverlayID;
+ _this.wasPlayAreaVisible = _this.isPlayAreaVisible;
+ _this.isPlayAreaVisible = visible;
+ _this.targetOverlayID = targetOverlayID;
- if (this.teleportedFadeTimer !== null) {
- Script.clearTimeout(this.teleportedFadeTimer);
- this.teleportedFadeTimer = null;
+ if (_this.teleportedFadeTimer !== null) {
+ Script.clearTimeout(_this.teleportedFadeTimer);
+ _this.teleportedFadeTimer = null;
}
if (visible || !fade) {
// Immediately make visible or invisible.
- this.isPlayAreaVisible = visible;
- Overlays.editOverlay(this.playAreaOverlay, {
+ _this.isPlayAreaVisible = visible;
+ Overlays.editOverlay(_this.playAreaOverlay, {
dimensions: Vec3.ZERO,
- alpha: this.PLAY_AREA_BOX_ALPHA,
+ alpha: _this.PLAY_AREA_BOX_ALPHA,
visible: visible
});
- for (var i = 0; i < this.playAreaSensorPositionOverlays.length; i++) {
- Overlays.editOverlay(this.playAreaSensorPositionOverlays[i], {
+ for (var i = 0; i < _this.playAreaSensorPositionOverlays.length; i++) {
+ Overlays.editOverlay(_this.playAreaSensorPositionOverlays[i], {
dimensions: Vec3.ZERO,
- alpha: this.PLAY_AREA_SENSOR_ALPHA,
+ alpha: _this.PLAY_AREA_SENSOR_ALPHA,
visible: visible
});
}
- Overlays.editOverlay(this.teleportedTargetOverlay, { visible: false });
+ Overlays.editOverlay(_this.teleportedTargetOverlay, { visible: false });
} else {
// Fading out of overlays is initiated in setTeleportVisible().
}
@@ -494,22 +494,22 @@ Script.include("/~/system/libraries/controllers.js");
var MIN_PARENTING_DISTANCE = 0.2; // Parenting under this distance results in the play area's rotation jittering.
if (Vec3.distance(targetXZPosition, avatarXZPosition) < MIN_PARENTING_DISTANCE) {
// Set play area position and rotation in world coordinates with no parenting.
- Overlays.editOverlay(this.playAreaOverlay, {
+ Overlays.editOverlay(_this.playAreaOverlay, {
parentID: Uuid.NULL,
position: Vec3.sum(position,
Vec3.multiplyQbyV(sensorToWorldRotation,
Vec3.multiply(MyAvatar.sensorToWorldScale,
- Vec3.subtract(this.playAreaCenterOffset, avatarSensorPosition)))),
+ Vec3.subtract(_this.playAreaCenterOffset, avatarSensorPosition)))),
rotation: sensorToWorldRotation
});
} else {
// Set play area position and rotation in local coordinates with parenting.
- var targetRotation = Overlays.getProperty(this.targetOverlayID, "rotation");
+ var targetRotation = Overlays.getProperty(_this.targetOverlayID, "rotation");
var sensorToTargetRotation = Quat.multiply(Quat.inverse(targetRotation), sensorToWorldRotation);
var relativePlayAreaCenterOffset =
- Vec3.sum(this.playAreaCenterOffset, { x: 0, y: -TARGET_MODEL_DIMENSIONS.y / 2, z: 0 });
- Overlays.editOverlay(this.playAreaOverlay, {
- parentID: this.targetOverlayID,
+ Vec3.sum(_this.playAreaCenterOffset, { x: 0, y: -TARGET_MODEL_DIMENSIONS.y / 2, z: 0 });
+ Overlays.editOverlay(_this.playAreaOverlay, {
+ parentID: _this.targetOverlayID,
localPosition: Vec3.multiplyQbyV(Quat.inverse(targetRotation),
Vec3.multiplyQbyV(sensorToWorldRotation,
Vec3.multiply(MyAvatar.sensorToWorldScale,
@@ -578,33 +578,33 @@ Script.include("/~/system/libraries/controllers.js");
}
}
_this.teleportedFadeTimer = null;
- Selection.disableListHighlight(this.teleporterSelectionName);
+ Selection.disableListHighlight(_this.teleporterSelectionName);
}
};
this.cancelFade = function () {
// Other hand may call this to immediately hide fading overlays.
var i, length;
- if (this.teleportedFadeTimer) {
- Overlays.editOverlay(this.teleportedTargetOverlay, { visible: false });
- if (this.wasPlayAreaVisible) {
- Overlays.editOverlay(this.playAreaOverlay, { visible: false });
- for (i = 0, length = this.playAreaSensorPositionOverlays.length; i < length; i++) {
- Overlays.editOverlay(this.playAreaSensorPositionOverlays[i], { visible: false });
+ if (_this.teleportedFadeTimer) {
+ Overlays.editOverlay(_this.teleportedTargetOverlay, { visible: false });
+ if (_this.wasPlayAreaVisible) {
+ Overlays.editOverlay(_this.playAreaOverlay, { visible: false });
+ for (i = 0, length = _this.playAreaSensorPositionOverlays.length; i < length; i++) {
+ Overlays.editOverlay(_this.playAreaSensorPositionOverlays[i], { visible: false });
}
}
- this.teleportedFadeTimer = null;
+ _this.teleportedFadeTimer = null;
}
};
this.setTeleportVisible = function (visible, mode, fade) {
// Scales in teleport target and play area when start displaying them.
- if (visible === this.isTeleportVisible) {
+ if (visible === _this.isTeleportVisible) {
return;
}
if (visible) {
- this.teleportScaleMode = mode;
+ _this.teleportScaleMode = mode;
Pointers.editRenderState(
mode === "head" ? _this.teleportParabolaHeadVisuals : _this.teleportParabolaHandVisuals,
"teleport",
@@ -613,42 +613,42 @@ Script.include("/~/system/libraries/controllers.js");
end: { dimensions: Vec3.ZERO }
}
);
- this.getOtherModule().cancelFade();
- this.teleportScaleStart = Date.now();
- this.teleportScaleFactor = 0;
- this.scaleInTeleport();
- Selection.enableListHighlight(this.teleporterSelectionName, this.TELEPORTER_SELECTION_STYLE);
+ _this.getOtherModule().cancelFade();
+ _this.teleportScaleStart = Date.now();
+ _this.teleportScaleFactor = 0;
+ _this.scaleInTeleport();
+ Selection.enableListHighlight(_this.teleporterSelectionName, _this.TELEPORTER_SELECTION_STYLE);
} else {
- if (this.teleportScaleTimer !== null) {
- Script.clearTimeout(this.teleportScaleTimer);
- this.teleportScaleTimer = null;
+ if (_this.teleportScaleTimer !== null) {
+ Script.clearTimeout(_this.teleportScaleTimer);
+ _this.teleportScaleTimer = null;
}
if (fade) {
// Copy of target at teleported position for fading.
var avatarScale = MyAvatar.sensorToWorldScale;
- Overlays.editOverlay(this.teleportedTargetOverlay, {
- position: Vec3.sum(this.teleportedPosition, {
+ Overlays.editOverlay(_this.teleportedTargetOverlay, {
+ position: Vec3.sum(_this.teleportedPosition, {
x: 0,
y: -getAvatarFootOffset() + avatarScale * TARGET_MODEL_DIMENSIONS.y / 2,
z: 0
}),
- rotation: Quat.multiply(this.TELEPORTED_TARGET_ROTATION, MyAvatar.orientation),
+ rotation: Quat.multiply(_this.TELEPORTED_TARGET_ROTATION, MyAvatar.orientation),
dimensions: Vec3.multiply(avatarScale, TARGET_MODEL_DIMENSIONS),
- alpha: this.TELEPORTED_TARGET_ALPHA,
+ alpha: _this.TELEPORTED_TARGET_ALPHA,
visible: true
});
// Fade out over time.
- this.teleportedFadeDelayFactor = 1.0;
- this.teleportedFadeFactor = 1.0;
- this.teleportedFadeTimer = Script.setTimeout(this.fadeOutTeleport, this.TELEPORTED_FADE_DELAY);
+ _this.teleportedFadeDelayFactor = 1.0;
+ _this.teleportedFadeFactor = 1.0;
+ _this.teleportedFadeTimer = Script.setTimeout(_this.fadeOutTeleport, _this.TELEPORTED_FADE_DELAY);
} else {
- Selection.disableListHighlight(this.teleporterSelectionName);
+ Selection.disableListHighlight(_this.teleporterSelectionName);
}
}
- this.isTeleportVisible = visible;
+ _this.isTeleportVisible = visible;
};
@@ -697,7 +697,7 @@ Script.include("/~/system/libraries/controllers.js");
100);
this.enterTeleport = function() {
- this.state = TELEPORTER_STATES.TARGETTING;
+ _this.state = TELEPORTER_STATES.TARGETTING;
};
this.isReady = function(controllerData, deltaTime) {
@@ -761,23 +761,23 @@ Script.include("/~/system/libraries/controllers.js");
if (teleportLocationType === TARGET.NONE) {
// Use the cancel default state
- this.setTeleportState(mode, "cancel", "");
+ _this.setTeleportState(mode, "cancel", "");
} else if (teleportLocationType === TARGET.INVALID) {
- this.setTeleportState(mode, "", "cancel");
+ _this.setTeleportState(mode, "", "cancel");
} else if (teleportLocationType === TARGET.COLLIDES) {
- this.setTeleportState(mode, "cancel", "collision");
+ _this.setTeleportState(mode, "cancel", "collision");
} else if (teleportLocationType === TARGET.SURFACE || teleportLocationType === TARGET.DISCREPANCY) {
- this.setTeleportState(mode, "teleport", "collision");
- this.updatePlayArea(result.intersection);
+ _this.setTeleportState(mode, "teleport", "collision");
+ _this.updatePlayArea(result.intersection);
} else if (teleportLocationType === TARGET.SEAT) {
- this.setTeleportState(mode, "collision", "seat");
+ _this.setTeleportState(mode, "collision", "seat");
}
- return this.teleport(result, teleportLocationType);
+ return _this.teleport(result, teleportLocationType);
};
this.teleport = function(newResult, target) {
var result = newResult;
- this.teleportedPosition = newResult.intersection;
+ _this.teleportedPosition = newResult.intersection;
if (_this.buttonValue !== 0) {
return makeRunningValues(true, [], []);
}
@@ -795,14 +795,14 @@ Script.include("/~/system/libraries/controllers.js");
MyAvatar.centerBody();
}
- this.disableLasers();
- this.active = false;
+ _this.disableLasers();
+ _this.active = false;
return makeRunningValues(false, [], []);
};
this.disableLasers = function() {
- this.setPlayAreaVisible(false, null, true);
- this.setTeleportVisible(false, null, true);
+ _this.setPlayAreaVisible(false, null, true);
+ _this.setTeleportVisible(false, null, true);
Pointers.disablePointer(_this.teleportParabolaHandVisuals);
Pointers.disablePointer(_this.teleportParabolaHandCollisions);
Pointers.disablePointer(_this.teleportParabolaHeadVisuals);
@@ -815,10 +815,10 @@ Script.include("/~/system/libraries/controllers.js");
this.setTeleportState = function (mode, visibleState, invisibleState) {
var teleportState = mode + visibleState + invisibleState;
- if (teleportState === this.teleportState) {
+ if (teleportState === _this.teleportState) {
return;
}
- this.teleportState = teleportState;
+ _this.teleportState = teleportState;
var pointerID;
if (mode === 'head') {
@@ -831,16 +831,16 @@ Script.include("/~/system/libraries/controllers.js");
pointerID = _this.teleportParabolaHandVisuals;
}
var visible = visibleState === "teleport";
- this.setPlayAreaVisible(visible && MyAvatar.showPlayArea,
+ _this.setPlayAreaVisible(visible && MyAvatar.showPlayArea,
Pointers.getPointerProperties(pointerID).renderStates.teleport.end, false);
- this.setTeleportVisible(visible, mode, false);
+ _this.setTeleportVisible(visible, mode, false);
};
this.setIgnoreEntities = function(entitiesToIgnore) {
- Pointers.setIgnoreItems(this.teleportParabolaHandVisuals, entitiesToIgnore);
- Pointers.setIgnoreItems(this.teleportParabolaHandCollisions, entitiesToIgnore);
- Pointers.setIgnoreItems(this.teleportParabolaHeadVisuals, entitiesToIgnore);
- Pointers.setIgnoreItems(this.teleportParabolaHeadCollisions, entitiesToIgnore);
+ Pointers.setIgnoreItems(_this.teleportParabolaHandVisuals, entitiesToIgnore);
+ Pointers.setIgnoreItems(_this.teleportParabolaHandCollisions, entitiesToIgnore);
+ Pointers.setIgnoreItems(_this.teleportParabolaHeadVisuals, entitiesToIgnore);
+ Pointers.setIgnoreItems(_this.teleportParabolaHeadCollisions, entitiesToIgnore);
Picks.setIgnoreItems(_this.teleportHeadCollisionPick, entitiesToIgnore);
Picks.setIgnoreItems(_this.teleportHandCollisionPick, entitiesToIgnore);
};
diff --git a/scripts/system/pal.js b/scripts/system/pal.js
index 141ea03330..0c4338b31d 100644
--- a/scripts/system/pal.js
+++ b/scripts/system/pal.js
@@ -161,7 +161,8 @@ ExtendedOverlay.unHover = function () { // calls hover(false) on lastHoveringId
// hit(overlay) on the one overlay intersected by pickRay, if any.
// noHit() if no ExtendedOverlay was intersected (helps with hover)
ExtendedOverlay.applyPickRay = function (pickRay, hit, noHit) {
- var pickedOverlay = Overlays.findRayIntersection(pickRay); // Depends on nearer coverOverlays to extend closer to us than farther ones.
+ // TODO: this could just include the necessary overlays for better performance
+ var pickedOverlay = Overlays.findRayIntersection(pickRay, true); // Depends on nearer coverOverlays to extend closer to us than farther ones.
if (!pickedOverlay.intersects) {
if (noHit) {
return noHit();
diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js
index f9e9165f2e..60848224bb 100644
--- a/scripts/system/tablet-ui/tabletUI.js
+++ b/scripts/system/tablet-ui/tabletUI.js
@@ -137,11 +137,11 @@
UIWebTablet.calculateTabletAttachmentProperties(activeHand, true, tabletProperties);
}
tabletProperties.visible = true;
+ tabletProperties.ignorePickIntersection = false;
Overlays.editOverlay(HMD.tabletID, tabletProperties);
- Overlays.editOverlay(HMD.homeButtonID, { visible: true });
- Overlays.editOverlay(HMD.homeButtonHighlightID, { visible: true });
- Overlays.editOverlay(HMD.tabletScreenID, { visible: true });
- Overlays.editOverlay(HMD.tabletScreenID, { maxFPS: 90 });
+ Overlays.editOverlay(HMD.homeButtonID, { visible: true, ignorePickIntersection: false });
+ Overlays.editOverlay(HMD.homeButtonHighlightID, { visible: true, ignorePickIntersection: false });
+ Overlays.editOverlay(HMD.tabletScreenID, { visible: true, ignorePickIntersection: false, maxFPS: 90 });
updateTabletWidthFromSettings(true);
}
gTablet.tabletShown = true;
@@ -158,11 +158,10 @@
print("TABLET hide");
}
- Overlays.editOverlay(HMD.tabletID, { visible: false });
- Overlays.editOverlay(HMD.homeButtonID, { visible: false });
- Overlays.editOverlay(HMD.homeButtonHighlightID, { visible: false });
- Overlays.editOverlay(HMD.tabletScreenID, { visible: false });
- Overlays.editOverlay(HMD.tabletScreenID, { maxFPS: 1 });
+ Overlays.editOverlay(HMD.tabletID, { visible: false, ignorePickIntersection: true });
+ Overlays.editOverlay(HMD.homeButtonID, { visible: false, ignorePickIntersection: true });
+ Overlays.editOverlay(HMD.homeButtonHighlightID, { visible: false, ignorePickIntersection: true });
+ Overlays.editOverlay(HMD.tabletScreenID, { visible: false, ignorePickIntersection: true, maxFPS: 1 });
}
function closeTabletUI() {