Merge branch 'master' of github.com:highfidelity/hifi into textToSpeech

This commit is contained in:
Zach Fox 2018-11-06 10:29:05 -08:00
commit 58dd690c0a
137 changed files with 3035 additions and 3441 deletions

View file

@ -69,10 +69,10 @@ void ScriptableAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
AvatarData::setSkeletonModelURL(skeletonModelURL);
}
static AnimPose composeAnimPose(const FBXJoint& fbxJoint, const glm::quat rotation, const glm::vec3 translation) {
static AnimPose composeAnimPose(const HFMJoint& joint, const glm::quat rotation, const glm::vec3 translation) {
glm::mat4 translationMat = glm::translate(translation);
glm::mat4 rotationMat = glm::mat4_cast(fbxJoint.preRotation * rotation * fbxJoint.postRotation);
glm::mat4 finalMat = translationMat * fbxJoint.preTransform * rotationMat * fbxJoint.postTransform;
glm::mat4 rotationMat = glm::mat4_cast(joint.preRotation * rotation * joint.postRotation);
glm::mat4 finalMat = translationMat * joint.preTransform * rotationMat * joint.postTransform;
return AnimPose(finalMat);
}
@ -84,7 +84,7 @@ void ScriptableAvatar::update(float deltatime) {
// Run animation
if (_animation && _animation->isLoaded() && _animation->getFrames().size() > 0 && !_bind.isNull() && _bind->isLoaded()) {
if (!_animSkeleton) {
_animSkeleton = std::make_shared<AnimSkeleton>(_bind->getGeometry());
_animSkeleton = std::make_shared<AnimSkeleton>(_bind->getHFMModel());
}
float currentFrame = _animationDetails.currentFrame + deltatime * _animationDetails.fps;
if (_animationDetails.loop || currentFrame < _animationDetails.lastFrame) {
@ -93,7 +93,7 @@ void ScriptableAvatar::update(float deltatime) {
}
_animationDetails.currentFrame = currentFrame;
const QVector<FBXJoint>& modelJoints = _bind->getGeometry().joints;
const QVector<HFMJoint>& modelJoints = _bind->getHFMModel().joints;
QStringList animationJointNames = _animation->getJointNames();
const int nJoints = modelJoints.size();
@ -102,8 +102,8 @@ void ScriptableAvatar::update(float deltatime) {
}
const int frameCount = _animation->getFrames().size();
const FBXAnimationFrame& floorFrame = _animation->getFrames().at((int)glm::floor(currentFrame) % frameCount);
const FBXAnimationFrame& ceilFrame = _animation->getFrames().at((int)glm::ceil(currentFrame) % frameCount);
const HFMAnimationFrame& floorFrame = _animation->getFrames().at((int)glm::floor(currentFrame) % frameCount);
const HFMAnimationFrame& ceilFrame = _animation->getFrames().at((int)glm::ceil(currentFrame) % frameCount);
const float frameFraction = glm::fract(currentFrame);
std::vector<AnimPose> poses = _animSkeleton->getRelativeDefaultPoses();
@ -113,7 +113,7 @@ void ScriptableAvatar::update(float deltatime) {
const QString& name = animationJointNames[i];
// As long as we need the model preRotations anyway, let's get the jointIndex from the bind skeleton rather than
// trusting the .fst (which is sometimes not updated to match changes to .fbx).
int mapping = _bind->getGeometry().getJointIndex(name);
int mapping = _bind->getHFMModel().getJointIndex(name);
if (mapping != -1 && !_maskedJoints.contains(name)) {
AnimPose floorPose = composeAnimPose(modelJoints[mapping], floorFrame.rotations[i], floorFrame.translations[i] * UNIT_SCALE);

View file

@ -0,0 +1,4 @@
<svg width="22" height="26" fill="none" version="1.1" viewBox="0 0 22 26" xmlns="http://www.w3.org/2000/svg">
<path d="M1 7L11 1L21 7M1 7L11 13M1 7V19L11 25M11 13L21 7M11 13V25M21 7V19L11 25" stroke="#000" stroke-linejoin="round" stroke-width="2"/>
<circle class="st1" cx="19.407" cy="2.5881" r="2.5846" fill="#ef3b4e" stroke-width=".24043"/>
</svg>

After

Width:  |  Height:  |  Size: 356 B

View file

@ -0,0 +1,3 @@
<svg width="22" height="26" viewBox="0 0 22 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 7L11 1L21 7M1 7L11 13M1 7V19L11 25M11 13L21 7M11 13V25M21 7V19L11 25" stroke="black" stroke-width="2" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 243 B

View file

@ -0,0 +1,4 @@
<svg width="22" height="26" fill="none" version="1.1" viewBox="0 0 22 26" xmlns="http://www.w3.org/2000/svg">
<path d="M1 7L11 1L21 7M1 7L11 13M1 7V19L11 25M11 13L21 7M11 13V25M21 7V19L11 25" stroke="#fff" stroke-linejoin="round" stroke-width="2"/>
<circle class="st1" cx="19.41" cy="2.5828" r="2.5846" fill="#ef3b4e" stroke-width=".24043"/>
</svg>

After

Width:  |  Height:  |  Size: 355 B

View file

@ -0,0 +1,3 @@
<svg width="22" height="26" viewBox="0 0 22 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 7L11 1L21 7M1 7L11 13M1 7V19L11 25M11 13L21 7M11 13V25M21 7V19L11 25" stroke="white" stroke-width="2" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 243 B

View file

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 96 96" style="enable-background:new 0 0 96 96;" xml:space="preserve">
<style type="text/css">
.st1{fill:#EF3B4E;}
</style>
<circle class="st1" cx="84.6" cy="11.5" r="10.75"/>
<g><path d="M2.4,70.5c0,6.1,4.9,11,11,11H76c6.1,0,11-4.9,11-11V59.6c3.7-0.7,6.6-3.9,6.6-7.9v-7.5c0-3.9-2.8-7.2-6.6-7.9V25.5 c0-6.1-4.9-11-11-11H13.4c-6.1,0-11,4.9-11,11V70.5z M87.6,51.8c0,1.1-0.9,2-2,2H72.2c-2.8,0-5-2.2-5-5v-1.5c0-2.8,2.2-5,5-5h13.3 c1.1,0,2,0.9,2,2V51.8z M8.4,25.5c0-2.8,2.2-5,5-5H76c2.8,0,5,2.2,5,5v10.7h-8.7c-6.1,0-11,4.9-11,11v1.5c0,6.1,4.9,11,11,11H81 v10.7c0,2.8-2.2,5-5,5H13.4c-2.8,0-5-2.2-5-5V25.5z"></path></g></svg>

Before

Width:  |  Height:  |  Size: 755 B

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 96 96" style="enable-background:new 0 0 96 96;" xml:space="preserve"><g><path d="M2.4,70.5c0,6.1,4.9,11,11,11H76c6.1,0,11-4.9,11-11V59.6c3.7-0.7,6.6-3.9,6.6-7.9v-7.5c0-3.9-2.8-7.2-6.6-7.9V25.5 c0-6.1-4.9-11-11-11H13.4c-6.1,0-11,4.9-11,11V70.5z M87.6,51.8c0,1.1-0.9,2-2,2H72.2c-2.8,0-5-2.2-5-5v-1.5c0-2.8,2.2-5,5-5h13.3 c1.1,0,2,0.9,2,2V51.8z M8.4,25.5c0-2.8,2.2-5,5-5H76c2.8,0,5,2.2,5,5v10.7h-8.7c-6.1,0-11,4.9-11,11v1.5c0,6.1,4.9,11,11,11H81 v10.7c0,2.8-2.2,5-5,5H13.4c-2.8,0-5-2.2-5-5V25.5z"></path></g></svg>

Before

Width:  |  Height:  |  Size: 643 B

View file

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 96 96" style="enable-background:new 0 0 96 96;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
.st1{fill:#EF3B4E;}
</style>
<circle class="st1" cx="84.6" cy="11.5" r="10.75"/>
<g>
<path class="st0" d="M2.4,70.5c0,6.1,4.9,11,11,11H76c6.1,0,11-4.9,11-11V59.6c3.7-0.7,6.6-3.9,6.6-7.9v-7.5c0-3.9-2.8-7.2-6.6-7.9
V25.5c0-6.1-4.9-11-11-11H13.4c-6.1,0-11,4.9-11,11C2.4,25.5,2.4,70.5,2.4,70.5z M87.6,51.8c0,1.1-0.9,2-2,2H72.2c-2.8,0-5-2.2-5-5
v-1.5c0-2.8,2.2-5,5-5h13.3c1.1,0,2,0.9,2,2L87.6,51.8L87.6,51.8z M8.4,25.5c0-2.8,2.2-5,5-5H76c2.8,0,5,2.2,5,5v10.7h-8.7
c-6.1,0-11,4.9-11,11v1.5c0,6.1,4.9,11,11,11H81v10.7c0,2.8-2.2,5-5,5H13.4c-2.8,0-5-2.2-5-5V25.5z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 984 B

View file

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 96 96" style="enable-background:new 0 0 96 96;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g>
<path class="st0" d="M2.4,70.5c0,6.1,4.9,11,11,11H76c6.1,0,11-4.9,11-11V59.6c3.7-0.7,6.6-3.9,6.6-7.9v-7.5c0-3.9-2.8-7.2-6.6-7.9
V25.5c0-6.1-4.9-11-11-11H13.4c-6.1,0-11,4.9-11,11C2.4,25.5,2.4,70.5,2.4,70.5z M87.6,51.8c0,1.1-0.9,2-2,2H72.2c-2.8,0-5-2.2-5-5
v-1.5c0-2.8,2.2-5,5-5h13.3c1.1,0,2,0.9,2,2L87.6,51.8L87.6,51.8z M8.4,25.5c0-2.8,2.2-5,5-5H76c2.8,0,5,2.2,5,5v10.7h-8.7
c-6.1,0-11,4.9-11,11v1.5c0,6.1,4.9,11,11,11H81v10.7c0,2.8-2.2,5-5,5H13.4c-2.8,0-5-2.2-5-5V25.5z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 909 B

View file

@ -96,7 +96,7 @@ Item {
topMargin: hifi.dimensions.contentSpacing.y
}
text: qsTr("Sign in to High Fidelity to make friends, get HFC, and buy interesting things on the Marketplace!")
text: qsTr("Sign in to High Fidelity to make friends, get HFC, and get interesting things on the Marketplace!")
width: parent.width
wrapMode: Text.WordWrap
lineHeight: 1
@ -136,7 +136,7 @@ Item {
TextField {
id: usernameField
text: Settings.getValue("wallet/savedUsername", "");
text: Settings.getValue("keepMeLoggedIn/savedUsername", "");
width: parent.width
focus: true
placeholderText: "Username or Email"
@ -165,7 +165,7 @@ Item {
root.text = "";
}
Component.onCompleted: {
var savedUsername = Settings.getValue("wallet/savedUsername", "");
var savedUsername = Settings.getValue("keepMeLoggedIn/savedUsername", "");
usernameField.text = savedUsername === "Unknown user" ? "" : savedUsername;
}
}
@ -239,7 +239,10 @@ Item {
}
Keys.onReturnPressed: linkAccountBody.login()
Keys.onReturnPressed: {
Settings.setValue("keepMeLoggedIn/savedUsername", usernameField.text);
linkAccountBody.login();
}
}
InfoItem {
@ -263,21 +266,21 @@ Item {
CheckBox {
id: autoLogoutCheckbox
checked: !Settings.getValue("wallet/autoLogout", true)
text: "Keep me signed in"
checked: Settings.getValue("keepMeLoggedIn", false)
text: "Keep me logged in"
boxSize: 20;
labelFontSize: 15
color: hifi.colors.black
onCheckedChanged: {
Settings.setValue("wallet/autoLogout", !checked);
Settings.setValue("keepMeLoggedIn", checked);
if (checked) {
Settings.setValue("wallet/savedUsername", Account.username);
Settings.setValue("keepMeLoggedIn/savedUsername", usernameField.text);
} else {
Settings.setValue("wallet/savedUsername", "");
Settings.setValue("keepMeLoggedIn/savedUsername", "");
}
}
Component.onDestruction: {
Settings.setValue("wallet/autoLogout", !checked);
Settings.setValue("keepMeLoggedIn", checked);
}
}
@ -289,7 +292,10 @@ Item {
text: qsTr(loginDialog.isSteamRunning() ? "Link Account" : "Log in")
color: hifi.buttons.blue
onClicked: linkAccountBody.login()
onClicked: {
Settings.setValue("keepMeLoggedIn/savedUsername", usernameField.text);
linkAccountBody.login();
}
}
}
@ -403,6 +409,7 @@ Item {
case Qt.Key_Enter:
case Qt.Key_Return:
event.accepted = true
Settings.setValue("keepMeLoggedIn/savedUsername", usernameField.text);
linkAccountBody.login()
break
}

View file

@ -260,16 +260,21 @@ Item {
interactive: false;
anchors.fill: parent;
model: filterBarModel;
delegate: Rectangle {
delegate: Item {
width: parent.width;
height: 50;
Rectangle {
id: dropDownButton;
color: hifi.colors.white;
width: parent.width;
height: 50;
visible: true;
RalewaySemiBold {
id: dropDownButtonText;
text: model.displayName;
anchors.fill: parent;
anchors.topMargin: 2;
anchors.leftMargin: 12;
color: hifi.colors.baseGray;
horizontalAlignment: Text.AlignLeft;
@ -294,6 +299,13 @@ Item {
}
}
}
Rectangle {
height: 2;
width: parent.width;
color: hifi.colors.lightGray;
visible: model.separator
}
}
}
}

View file

@ -63,8 +63,8 @@ MessageBox {
popup.dialogButtons.yesButton.fontCapitalization = Font.MixedCase;
popup.button1text = 'CANCEL'
popup.titleText = 'Get Wearables'
popup.bodyText = 'Buy wearables from <b><a href="app://marketplace">Marketplace.</a></b>' + '<br/>' +
'Wear wearable from <b><a href="app://purchases">My Purchases.</a></b>' + '<br/>' + '<br/>' +
popup.bodyText = 'Get wearables from <b><a href="app://marketplace">Marketplace.</a></b>' + '<br/>' +
'Wear wearable from <b><a href="app://purchases">Inventory.</a></b>' + '<br/>' + '<br/>' +
'Visit “AvatarIsland” to get wearables'
popup.imageSource = getWearablesUrl;
@ -89,7 +89,7 @@ MessageBox {
function showDeleteFavorite(favoriteName, callback) {
popup.titleText = 'Delete Favorite: {AvatarName}'.replace('{AvatarName}', favoriteName)
popup.bodyText = 'This will delete your favorite. You will retain access to the wearables and avatar that made up the favorite from My Purchases.'
popup.bodyText = 'This will delete your favorite. You will retain access to the wearables and avatar that made up the favorite from Inventory.'
popup.imageSource = null;
popup.button1text = 'CANCEL'
popup.button2text = 'DELETE'
@ -128,8 +128,8 @@ MessageBox {
popup.button1text = 'CANCEL'
popup.titleText = 'Get Avatars'
popup.bodyText = 'Buy avatars from <b><a href="app://marketplace">Marketplace.</a></b>' + '<br/>' +
'Wear avatars in <b><a href="app://purchases">My Purchases.</a></b>' + '<br/>' + '<br/>' +
popup.bodyText = 'Get avatars from <b><a href="app://marketplace">Marketplace.</a></b>' + '<br/>' +
'Wear avatars in <b><a href="app://purchases">Inventory.</a></b>' + '<br/>' + '<br/>' +
'Visit “BodyMart” to get free avatars.'
popup.imageSource = getAvatarsUrl;

View file

@ -240,11 +240,6 @@ Rectangle {
lightboxPopup.button1method = function() {
lightboxPopup.visible = false;
}
lightboxPopup.button2text = "GO TO WALLET";
lightboxPopup.button2method = function() {
lightboxPopup.visible = false;
sendToScript({method: 'checkout_openWallet'});
};
lightboxPopup.visible = true;
} else {
sendToScript(msg);
@ -383,7 +378,7 @@ Rectangle {
anchors.leftMargin: 16;
width: paintedWidth;
height: paintedHeight;
text: "Review Purchase:";
text: "Review:";
color: hifi.colors.black;
size: 28;
}
@ -448,7 +443,7 @@ Rectangle {
// "HFC" balance label
HiFiGlyphs {
id: itemPriceTextLabel;
visible: !(root.isUpdating && root.itemEdition > 0);
visible: !(root.isUpdating && root.itemEdition > 0) && (root.itemPrice > 0);
text: hifi.glyphs.hfc;
// Size
size: 30;
@ -464,7 +459,7 @@ Rectangle {
}
FiraSansSemiBold {
id: itemPriceText;
text: (root.isUpdating && root.itemEdition > 0) ? "FREE\nUPDATE" : ((root.itemPrice === -1) ? "--" : root.itemPrice);
text: (root.isUpdating && root.itemEdition > 0) ? "FREE\nUPDATE" : ((root.itemPrice === -1) ? "--" : ((root.itemPrice > 0) ? root.itemPrice : "FREE"));
// Text size
size: (root.isUpdating && root.itemEdition > 0) ? 20 : 26;
// Anchors
@ -559,7 +554,7 @@ Rectangle {
}
}
// "View in My Purchases" button
// "View in Inventory" button
HifiControlsUit.Button {
id: viewInMyPurchasesButton;
visible: false;
@ -570,7 +565,7 @@ Rectangle {
height: 50;
anchors.left: parent.left;
anchors.right: parent.right;
text: root.isUpdating ? "UPDATE TO THIS ITEM FOR FREE" : "VIEW THIS ITEM IN MY PURCHASES";
text: root.isUpdating ? "UPDATE TO THIS ITEM FOR FREE" : "VIEW THIS ITEM IN YOUR INVENTORY";
onClicked: {
if (root.isUpdating) {
sendToScript({method: 'checkout_goToPurchases', filterText: root.baseItemName});
@ -594,7 +589,7 @@ Rectangle {
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) ? "Buy It Again" : "Confirm Purchase") : "--") : "Get Item"));
((viewInMyPurchasesButton.visible && !root.isUpdating) ? "Get It Again" : "Confirm") : "--") : "Get Item"));
onClicked: {
if (root.isUpdating && root.itemEdition > 0) {
// If we're updating an app, the existing app needs to be uninstalled.
@ -608,9 +603,9 @@ Rectangle {
} else if (root.isCertified) {
if (!root.shouldBuyWithControlledFailure) {
if (root.itemType === "contentSet" && !Entities.canReplaceContent()) {
lightboxPopup.titleText = "Purchase Content Set";
lightboxPopup.titleText = "Get Content Set";
lightboxPopup.bodyText = "You will not be able to replace this domain's content with <b>" + root.itemName +
" </b>until the server owner gives you 'Replace Content' permissions.<br><br>Are you sure you want to purchase this content set?";
" </b>until the server owner gives you 'Replace Content' permissions.<br><br>Are you sure you want to get this content set?";
lightboxPopup.button1text = "CANCEL";
lightboxPopup.button1method = function() {
lightboxPopup.visible = false;
@ -694,7 +689,7 @@ Rectangle {
id: completeText2;
text: "The " + (root.itemTypesText)[itemTypesArray.indexOf(root.itemType)] +
' <font color="' + hifi.colors.blueAccent + '"><a href="#">' + root.itemName + '</a></font>' +
" has been added to your Purchases and a receipt will appear in your Wallet's transaction history.";
" has been added to your Inventory.";
// Text size
size: 18;
// Anchors
@ -833,7 +828,7 @@ Rectangle {
}
lightboxPopup.button2text = "OPEN GOTO";
lightboxPopup.button2method = function() {
sendToScript({method: 'purchases_openGoTo'});
sendToScript({method: 'checkout_openGoTo'});
lightboxPopup.visible = false;
};
lightboxPopup.visible = true;
@ -864,7 +859,7 @@ Rectangle {
RalewaySemiBold {
id: myPurchasesLink;
text: '<font color="' + hifi.colors.primaryHighlight + '"><a href="#">View this item in My Purchases</a></font>';
text: '<font color="' + hifi.colors.primaryHighlight + '"><a href="#">View this item in your Inventory</a></font>';
// Text size
size: 18;
// Anchors
@ -886,7 +881,8 @@ Rectangle {
RalewaySemiBold {
id: walletLink;
text: '<font color="' + hifi.colors.primaryHighlight + '"><a href="#">View receipt in Wallet</a></font>';
visible: !WalletScriptingInterface.limitedCommerce;
text: '<font color="' + hifi.colors.primaryHighlight + '"><a href="#">View receipt in Recent Activity</a></font>';
// Text size
size: 18;
// Anchors
@ -902,18 +898,18 @@ Rectangle {
horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignVCenter;
onLinkActivated: {
sendToScript({method: 'purchases_openWallet'});
sendToScript({method: 'checkout_openRecentActivity'});
}
}
RalewayRegular {
id: pendingText;
text: 'Your item is marked "pending" while your purchase is being confirmed. ' +
text: 'Your item is marked "pending" while the transfer is being confirmed. ' +
'<b><font color="' + hifi.colors.primaryHighlight + '"><a href="#">Learn More</a></font></b>';
// Text size
size: 18;
// Anchors
anchors.top: walletLink.bottom;
anchors.top: walletLink.visible ? walletLink.bottom : myPurchasesLink.bottom;
anchors.topMargin: 32;
height: paintedHeight;
anchors.left: parent.left;
@ -925,8 +921,8 @@ Rectangle {
horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignVCenter;
onLinkActivated: {
lightboxPopup.titleText = "Purchase Confirmations";
lightboxPopup.bodyText = 'Your item is marked "pending" while your purchase is being confirmed.<br><br>' +
lightboxPopup.titleText = "Confirmations";
lightboxPopup.bodyText = 'Your item is marked "pending" while the transfer is being confirmed.<br><br>' +
'Confirmations usually take about 90 seconds.';
lightboxPopup.button1text = "CLOSE";
lightboxPopup.button1method = function() {
@ -936,9 +932,9 @@ Rectangle {
}
}
// "Continue Shopping" button
// "Continue" button
HifiControlsUit.Button {
id: continueShoppingButton;
id: continueButton;
color: hifi.buttons.noneBorderlessGray;
colorScheme: hifi.colorSchemes.light;
anchors.bottom: parent.bottom;
@ -946,9 +942,9 @@ Rectangle {
anchors.right: parent.right;
width: 193;
height: 44;
text: "Continue Shopping";
text: "Continue";
onClicked: {
sendToScript({method: 'checkout_continueShopping', itemId: itemId});
sendToScript({method: 'checkout_continue', itemId: itemId});
}
}
}
@ -971,7 +967,7 @@ Rectangle {
RalewayRegular {
id: failureHeaderText;
text: "<b>Purchase Failed.</b><br>Your Purchases and HFC balance haven't changed.";
text: "<b>Purchase Failed.</b><br>Your Inventory and HFC balance haven't changed.";
// Text size
size: 24;
// Anchors
@ -1037,7 +1033,7 @@ Rectangle {
width: parent.width/2 - anchors.leftMargin*2;
text: "Back to Marketplace";
onClicked: {
sendToScript({method: 'checkout_continueShopping', itemId: itemId});
sendToScript({method: 'checkout_continue', itemId: itemId});
}
}
}
@ -1122,10 +1118,10 @@ Rectangle {
if (root.balanceAfterPurchase < 0) {
// If you already own the item...
if (!root.alreadyOwned) {
buyText.text = "<b>Your Wallet does not have sufficient funds to purchase this item.</b>";
buyText.text = "<b>You do not have sufficient funds to purchase this item.</b>";
// Else if you don't already own the item...
} else if (canBuyAgain()) {
buyText.text = "<b>Your Wallet does not have sufficient funds to purchase this item again.</b>";
buyText.text = "<b>You do not have sufficient funds to purchase this item again.</b>";
} else {
buyText.text = "<b>While you do not have sufficient funds to buy this, you already have this item.</b>"
}
@ -1171,7 +1167,7 @@ Rectangle {
buyText.text = "";
}
} else {
buyText.text = '<i>This type of item cannot currently be certified, so it will not show up in "My Purchases". You can access it again for free from the Marketplace.</i>';
buyText.text = '<i>This type of item cannot currently be certified, so it will not show up in "Inventory". You can access it again for free from the Marketplace.</i>';
buyTextContainer.color = hifi.colors.white;
buyTextContainer.border.color = hifi.colors.white;
buyGlyph.text = "";

View file

@ -14,9 +14,9 @@
import Hifi 1.0 as Hifi
import QtQuick 2.5
import QtGraphicalEffects 1.0
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
import "qrc:////qml//styles-uit"
import "qrc:////qml//controls-uit" as HifiControlsUit
import "qrc:////qml//controls" as HifiControls
// references XXX from root context
@ -33,7 +33,7 @@ Rectangle {
property string buttonLayout: "leftright";
readonly property string securityPicBodyText: "When you see your Security Pic, your actions and data are securely making use of your " +
"Wallet's private keys.<br><br>You can change your Security Pic in your Wallet.";
"private keys.<br><br>You can change your Security Pic via Settings > Security...";
id: root;
visible: false;
@ -41,6 +41,8 @@ Rectangle {
color: Qt.rgba(0, 0, 0, 0.5);
z: 999;
HifiConstants { id: hifi; }
onVisibleChanged: {
if (!visible) {
resetLightbox();

View file

@ -27,7 +27,6 @@ Item {
property string referrerURL: (Account.metaverseServerURL + "/marketplace?");
readonly property int additionalDropdownHeight: usernameDropdown.height - myUsernameButton.anchors.bottomMargin;
property alias usernameDropdownVisible: usernameDropdown.visible;
property bool messagesWaiting: false;
height: mainContainer.height + additionalDropdownHeight;
@ -38,7 +37,6 @@ Item {
if (walletStatus === 0) {
sendToParent({method: "needsLogIn"});
} else if (walletStatus === 5) {
Commerce.getAvailableUpdates();
Commerce.getSecurityImage();
} else if (walletStatus > 5) {
console.log("ERROR in EmulatedMarketplaceHeader.qml: Unknown wallet status: " + walletStatus);
@ -59,14 +57,6 @@ Item {
securityImage.source = "image://security/securityImage";
}
}
onAvailableUpdatesResult: {
if (result.status !== 'success') {
console.log("Failed to get Available Updates", result.data.message);
} else {
root.messagesWaiting = result.data.updates.length > 0;
}
}
}
Component.onCompleted: {
@ -118,50 +108,6 @@ Item {
anchors.right: securityImage.left;
anchors.rightMargin: 6;
Rectangle {
id: myPurchasesLink;
anchors.right: myUsernameButton.left;
anchors.rightMargin: 8;
anchors.verticalCenter: parent.verticalCenter;
height: 40;
width: myPurchasesText.paintedWidth + 10;
RalewaySemiBold {
id: myPurchasesText;
text: "My Purchases";
// Text size
size: 18;
// Style
color: hifi.colors.blueAccent;
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
// Anchors
anchors.centerIn: parent;
}
MouseArea {
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
sendToParent({ method: 'header_goToPurchases', hasUpdates: root.messagesWaiting });
}
onEntered: myPurchasesText.color = hifi.colors.blueHighlight;
onExited: myPurchasesText.color = hifi.colors.blueAccent;
}
}
Rectangle {
id: messagesWaitingLight;
visible: root.messagesWaiting;
anchors.right: myPurchasesLink.left;
anchors.rightMargin: -2;
anchors.verticalCenter: parent.verticalCenter;
height: 10;
width: height;
radius: height/2;
color: "red";
}
TextMetrics {
id: textMetrics;
font.family: "Raleway"
@ -267,7 +213,7 @@ Item {
anchors.topMargin: -buttonAndUsernameContainer.anchors.bottomMargin;
anchors.right: buttonAndUsernameContainer.right;
height: childrenRect.height;
width: 100;
width: 150;
Rectangle {
id: myItemsButton;
@ -279,7 +225,7 @@ Item {
RalewaySemiBold {
anchors.fill: parent;
text: "My Items"
text: "My Submissions"
color: hifi.colors.baseGray;
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;

View file

@ -87,7 +87,7 @@ Rectangle {
}
RalewayRegular {
id: introText2;
text: "My Purchases";
text: "Inventory";
// Text size
size: 22;
// Anchors
@ -116,7 +116,7 @@ Rectangle {
RalewayRegular {
id: step1text;
text: "The <b>'REZ IT'</b> button makes your purchase appear in front of you.";
text: "The <b>'REZ IT'</b> button makes your item appear in front of you.";
// Text size
size: 20;
// Anchors

View file

@ -73,6 +73,10 @@ Item {
}
onTransferAssetToNodeResult: {
if (!root.visible) {
return;
}
root.isCurrentlySendingAsset = false;
if (result.status === 'success') {
@ -92,6 +96,10 @@ Item {
}
onTransferAssetToUsernameResult: {
if (!root.visible) {
return;
}
root.isCurrentlySendingAsset = false;
if (result.status === 'success') {
@ -1309,13 +1317,13 @@ Item {
Rectangle {
anchors.top: parent.top;
anchors.topMargin: root.assetName === "" ? 15 : 150;
anchors.topMargin: root.assetName === "" ? 15 : 125;
anchors.left: parent.left;
anchors.leftMargin: root.assetName === "" ? 15 : 50;
anchors.right: parent.right;
anchors.rightMargin: root.assetName === "" ? 15 : 50;
anchors.bottom: parent.bottom;
anchors.bottomMargin: root.assetName === "" ? 15 : 240;
anchors.bottomMargin: root.assetName === "" ? 15 : 125;
color: "#FFFFFF";
RalewaySemiBold {

View file

@ -28,7 +28,7 @@ Rectangle {
property string itemName: "--";
property string itemOwner: "--";
property string itemEdition: "--";
property string dateOfPurchase: "--";
property string dateAcquired: "--";
property string itemCost: "--";
property string certTitleTextColor: hifi.colors.darkGray;
property string certTextColor: hifi.colors.white;
@ -64,7 +64,7 @@ Rectangle {
root.itemName = "";
root.itemEdition = "";
root.itemOwner = "";
root.dateOfPurchase = "";
root.dateAcquired = "";
root.itemCost = "";
errorText.text = "Information about this certificate is currently unavailable. Please try again later.";
}
@ -77,8 +77,9 @@ Rectangle {
// "\u2022" is the Unicode character 'BULLET' - it's what's used in password fields on the web, etc
root.itemOwner = root.isMyCert ? Account.username :
"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
root.dateOfPurchase = root.isMyCert ? getFormattedDate(result.data.transfer_created_at * 1000) : "Undisclosed";
root.itemCost = (root.isMyCert && result.data.cost !== undefined) ? result.data.cost : "Undisclosed";
root.dateAcquired = root.isMyCert ? getFormattedDate(result.data.transfer_created_at * 1000) : "Undisclosed";
root.itemCost = (root.isMyCert && result.data.cost !== undefined) ?
(parseInt(result.data.cost) > 0 ? result.data.cost : "Free") : "Undisclosed";
}
if (root.certInfoReplaceMode > 4) {
root.itemEdition = result.data.edition_number + "/" + (result.data.limited_run === -1 ? "\u221e" : result.data.limited_run);
@ -86,7 +87,7 @@ Rectangle {
if (root.certificateStatus === 4) { // CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED
if (root.isMyCert) {
errorText.text = "This item is an uncertified copy of an item you purchased.";
errorText.text = "This item is an uncertified copy of an item you acquired.";
} else {
errorText.text = "The person who placed this item doesn't own it.";
}
@ -102,8 +103,8 @@ Rectangle {
showInMarketplaceButton.visible = false;
// "Edition" text previously set above in this function
// "Owner" text previously set above in this function
// "Purchase Date" text previously set above in this function
// "Purchase Price" text previously set above in this function
// "Acquisition Date" text previously set above in this function
// "Acquisition Price" text previously set above in this function
if (result.data.invalid_reason) {
errorText.text = result.data.invalid_reason;
}
@ -117,8 +118,8 @@ Rectangle {
showInMarketplaceButton.visible = true;
// "Edition" text previously set above in this function
// "Owner" text previously set above in this function
// "Purchase Date" text previously set above in this function
// "Purchase Price" text previously set above in this function
// "Acquisition Date" text previously set above in this function
// "Acquisition Price" text previously set above in this function
errorText.text = "The status of this item is still pending confirmation. If the purchase is not confirmed, " +
"this entity will be cleaned up by the domain.";
}
@ -145,8 +146,8 @@ Rectangle {
// "Item Name" text will be set in "onCertificateInfoResult()"
// "Edition" text will be set in "onCertificateInfoResult()"
// "Owner" text will be set in "onCertificateInfoResult()"
// "Purchase Date" text will be set in "onCertificateInfoResult()"
// "Purchase Price" text will be set in "onCertificateInfoResult()"
// "Acquisition Date" text will be set in "onCertificateInfoResult()"
// "Acquisition Price" text will be set in "onCertificateInfoResult()"
errorText.text = "";
} else if (root.certificateStatus === 2) { // CERTIFICATE_STATUS_VERIFICATION_TIMEOUT
root.useGoldCert = false;
@ -160,7 +161,7 @@ Rectangle {
root.itemName = "";
root.itemEdition = "";
root.itemOwner = "";
root.dateOfPurchase = "";
root.dateAcquired = "";
root.itemCost = "";
errorText.text = "Your request to inspect this item timed out. Please try again later.";
} else if (root.certificateStatus === 3) { // CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED
@ -175,8 +176,8 @@ Rectangle {
// "Item Name" text will be set in "onCertificateInfoResult()"
// "Edition" text will be set in "onCertificateInfoResult()"
// "Owner" text will be set in "onCertificateInfoResult()"
// "Purchase Date" text will be set in "onCertificateInfoResult()"
// "Purchase Price" text will be set in "onCertificateInfoResult()"
// "Acquisition Date" text will be set in "onCertificateInfoResult()"
// "Acquisition Price" text will be set in "onCertificateInfoResult()"
errorText.text = "The information associated with this item has been modified and it no longer matches the original certified item.";
} else if (root.certificateStatus === 4) { // CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED
root.useGoldCert = false;
@ -190,8 +191,8 @@ Rectangle {
// "Item Name" text will be set in "onCertificateInfoResult()"
root.itemEdition = "Uncertified Copy"
// "Owner" text will be set in "onCertificateInfoResult()"
// "Purchase Date" text will be set in "onCertificateInfoResult()"
// "Purchase Price" text will be set in "onCertificateInfoResult()"
// "Acquisition Date" text will be set in "onCertificateInfoResult()"
// "Acquisition Price" text will be set in "onCertificateInfoResult()"
// "Error Text" text will be set in "onCertificateInfoResult()"
} else {
console.log("Unknown certificate status received from ledger signal!");
@ -485,8 +486,8 @@ Rectangle {
}
RalewayRegular {
id: dateOfPurchaseHeader;
text: "PURCHASE DATE";
id: dateAcquiredHeader;
text: "ACQUISITION DATE";
// Text size
size: 16;
// Anchors
@ -500,15 +501,15 @@ Rectangle {
color: hifi.colors.darkGray;
}
AnonymousProRegular {
id: dateOfPurchase;
text: root.dateOfPurchase;
id: dateAcquired;
text: root.dateAcquired;
// Text size
size: 18;
// Anchors
anchors.top: dateOfPurchaseHeader.bottom;
anchors.top: dateAcquiredHeader.bottom;
anchors.topMargin: 8;
anchors.left: dateOfPurchaseHeader.left;
anchors.right: dateOfPurchaseHeader.right;
anchors.left: dateAcquiredHeader.left;
anchors.right: dateAcquiredHeader.right;
height: paintedHeight;
// Style
color: root.infoTextColor;
@ -516,7 +517,7 @@ Rectangle {
RalewayRegular {
id: priceHeader;
text: "PURCHASE PRICE";
text: "ACQUISITION PRICE";
// Text size
size: 16;
// Anchors
@ -530,7 +531,7 @@ Rectangle {
}
HiFiGlyphs {
id: hfcGlyph;
visible: priceText.text !== "Undisclosed" && priceText.text !== "";
visible: priceText.text !== "Undisclosed" && priceText.text !== "" && priceText.text !== "Free";
text: hifi.glyphs.hfc;
// Size
size: 24;
@ -618,7 +619,7 @@ Rectangle {
root.itemName = "--";
root.itemOwner = "--";
root.itemEdition = "--";
root.dateOfPurchase = "--";
root.dateAcquired = "--";
root.marketplaceUrl = "";
root.itemCost = "--";
root.isMyCert = false;

View file

@ -47,8 +47,7 @@ Item {
property string wornEntityID;
property string upgradeUrl;
property string upgradeTitle;
property bool updateAvailable: root.upgradeUrl !== "" && !root.isShowingMyItems;
property bool isShowingMyItems;
property bool updateAvailable: root.upgradeUrl !== "";
property bool valid;
property string originalStatusText;
@ -231,7 +230,7 @@ Item {
Loader {
id: giftButton;
visible: !root.isShowingMyItems;
visible: root.itemEdition > 0;
sourceComponent: contextCardButton;
anchors.right: parent.right;
anchors.top: parent.top;
@ -345,6 +344,7 @@ Item {
Rectangle {
id: permissionExplanationCard;
visible: false;
anchors.left: parent.left;
anchors.leftMargin: 30;
anchors.top: parent.top;

View file

@ -19,7 +19,6 @@ import "../../../controls" as HifiControls
import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere.
import "../wallet" as HifiWallet
import "../common" as HifiCommerceCommon
import "../inspectionCertificate" as HifiInspectionCertificate
import "../common/sendAsset" as HifiSendAsset
import "../.." as HifiCommon
@ -34,7 +33,6 @@ Rectangle {
property bool securityImageResultReceived: false;
property bool purchasesReceived: false;
property bool punctuationMode: false;
property bool isShowingMyItems: false;
property bool isDebuggingFirstUseTutorial: false;
property string installedApps;
property bool keyboardRaised: false;
@ -92,7 +90,6 @@ Rectangle {
if (result.status !== 'success') {
console.log("Failed to get Available Updates", result.data.message);
} else {
sendToScript({method: 'purchases_availableUpdatesReceived', numUpdates: result.data.updates.length });
root.numUpdatesAvailable = result.total_entries;
}
}
@ -106,10 +103,6 @@ Rectangle {
}
}
onIsShowingMyItemsChanged: {
getPurchases();
}
Timer {
id: notSetUpTimer;
interval: 200;
@ -118,19 +111,6 @@ Rectangle {
}
}
HifiInspectionCertificate.InspectionCertificate {
id: inspectionCertificate;
z: 998;
visible: false;
anchors.fill: parent;
Connections {
onSendToScript: {
sendToScript(message);
}
}
}
HifiCommerceCommon.CommerceLightbox {
id: lightboxPopup;
z: 999;
@ -180,7 +160,8 @@ Rectangle {
HifiCommerceCommon.EmulatedMarketplaceHeader {
id: titleBarContainer;
z: 997;
visible: !needsLogIn.visible;
visible: false;
height: 100;
// Size
width: parent.width;
// Anchors
@ -199,11 +180,6 @@ Rectangle {
lightboxPopup.button1method = function() {
lightboxPopup.visible = false;
}
lightboxPopup.button2text = "GO TO WALLET";
lightboxPopup.button2method = function() {
sendToScript({method: 'purchases_openWallet'});
lightboxPopup.visible = false;
};
lightboxPopup.visible = true;
} else {
sendToScript(msg);
@ -475,7 +451,7 @@ Rectangle {
anchors.left: parent.left;
anchors.leftMargin: 16;
width: paintedWidth;
text: isShowingMyItems ? "My Items" : "My Purchases";
text: "Items";
color: hifi.colors.black;
size: 22;
}
@ -517,8 +493,13 @@ Rectangle {
"filterName": "wearable"
},
{
"separator" : true,
"displayName": "Updatable",
"filterName": "updated"
},
{
"displayName": "My Submissions",
"filterName": "proofs"
}
]
filterBar.primaryFilterChoices.clear();
@ -533,6 +514,7 @@ Rectangle {
onTextChanged: {
purchasesModel.searchFilter = filterBar.text;
filterBar.previousText = filterBar.text;
}
}
}
@ -556,10 +538,18 @@ Rectangle {
listModelName: 'purchases';
listView: purchasesContentsList;
getPage: function () {
console.debug('getPage', purchasesModel.listModelName, root.isShowingMyItems, filterBar.primaryFilter_filterName, purchasesModel.currentPageToRetrieve, purchasesModel.itemsPerPage);
console.debug('getPage', purchasesModel.listModelName, filterBar.primaryFilter_filterName, purchasesModel.currentPageToRetrieve, purchasesModel.itemsPerPage);
var editionFilter = "";
var primaryFilter = "";
if (filterBar.primaryFilter_filterName === "proofs") {
editionFilter = "proofs";
} else {
primaryFilter = filterBar.primaryFilter_filterName;
}
Commerce.inventory(
root.isShowingMyItems ? "proofs" : "purchased",
filterBar.primaryFilter_filterName,
editionFilter,
primaryFilter,
filterBar.text,
purchasesModel.currentPageToRetrieve,
purchasesModel.itemsPerPage
@ -609,7 +599,6 @@ Rectangle {
upgradeUrl: model.upgrade_url;
upgradeTitle: model.upgrade_title;
itemType: model.item_type;
isShowingMyItems: root.isShowingMyItems;
valid: model.valid;
anchors.topMargin: 10;
anchors.bottomMargin: 10;
@ -626,8 +615,6 @@ Rectangle {
sendToScript({ method: 'purchases_updateWearables' });
}
} else if (msg.method === 'purchases_itemCertificateClicked') {
inspectionCertificate.visible = true;
inspectionCertificate.isLightbox = true;
sendToScript(msg);
} else if (msg.method === "showInvalidatedLightbox") {
lightboxPopup.titleText = "Item Invalidated";
@ -640,7 +627,7 @@ Rectangle {
lightboxPopup.visible = true;
} else if (msg.method === "showPendingLightbox") {
lightboxPopup.titleText = "Item Pending";
lightboxPopup.bodyText = 'Your item is marked "pending" while your purchase is being confirmed. ' +
lightboxPopup.bodyText = 'Your item is marked "pending" while the transfer is being confirmed. ' +
"Usually, purchases take about 90 seconds to confirm.";
lightboxPopup.button1text = "CLOSE";
lightboxPopup.button1method = function() {
@ -822,7 +809,8 @@ Rectangle {
Rectangle {
id: updatesAvailableBanner;
visible: root.numUpdatesAvailable > 0 && !root.isShowingMyItems;
visible: root.numUpdatesAvailable > 0 &&
filterBar.primaryFilter_filterName !== "proofs";
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
@ -883,9 +871,8 @@ Rectangle {
id: noItemsAlertContainer;
visible: !purchasesContentsList.visible &&
root.purchasesReceived &&
root.isShowingMyItems &&
filterBar.text === "" &&
filterBar.primaryFilter_displayName === "";
filterBar.primaryFilter_filterName === "proofs";
anchors.top: filterBarContainer.bottom;
anchors.topMargin: 12;
anchors.left: parent.left;
@ -895,7 +882,7 @@ Rectangle {
// Explanitory text
RalewayRegular {
id: noItemsYet;
text: "<b>You haven't submitted anything to the Marketplace yet!</b><br><br>Submit an item to the Marketplace to add it to My Items.";
text: "<b>You haven't submitted anything to the Marketplace yet!</b><br><br>Submit an item to the Marketplace to add it to My Submissions.";
// Text size
size: 22;
// Anchors
@ -933,7 +920,6 @@ Rectangle {
id: noPurchasesAlertContainer;
visible: !purchasesContentsList.visible &&
root.purchasesReceived &&
!root.isShowingMyItems &&
filterBar.text === "" &&
filterBar.primaryFilter_displayName === "";
anchors.top: filterBarContainer.bottom;
@ -945,7 +931,7 @@ Rectangle {
// Explanitory text
RalewayRegular {
id: haventPurchasedYet;
text: "<b>You haven't purchased anything yet!</b><br><br>Get an item from <b>Marketplace</b> to add it to My Purchases.";
text: "<b>You haven't gotten anything yet!</b><br><br>Get an item from <b>Marketplace</b> to add it to your Inventory.";
// Text size
size: 22;
// Anchors
@ -1065,11 +1051,10 @@ Rectangle {
titleBarContainer.referrerURL = message.referrerURL || "";
filterBar.text = message.filterText ? message.filterText : "";
break;
case 'inspectionCertificate_setCertificateId':
inspectionCertificate.fromScript(message);
break;
case 'purchases_showMyItems':
root.isShowingMyItems = true;
filterBar.primaryFilter_filterName = "proofs";
filterBar.primaryFilter_displayName = "Proofs";
filterBar.primaryFilter_index = 6;
break;
case 'updateConnections':
sendAsset.updateConnections(message.connections);

View file

@ -62,7 +62,7 @@ Item {
isExpanded: false;
question: "How can I get HFC?";
answer: "High Fidelity commerce is in open beta right now. Want more HFC? \
Get it by going to <br><br><b><font color='#0093C5'><a href='#bank'>BankOfHighFidelity.</a></font></b> and meeting with the banker!";
Get it by going to <b><font color='#0093C5'><a href='#bank'>BankOfHighFidelity</a></font></b> and meeting with the banker!";
}
ListElement {
isExpanded: false;
@ -74,8 +74,7 @@ In your Wallet's Send Money tab, choose from your list of connections, or choose
isExpanded: false;
question: "What is a Security Pic?"
answer: "Your Security Pic acts as an extra layer of Wallet security. \
When you see your Security Pic, you know that your actions and data are securely making use of your account. \
<br><br><b><font color='#0093C5'><a href='#securitypic'>Tap here to change your Security Pic.</a></font></b>";
When you see your Security Pic, you know that your actions and data are securely making use of your account.";
}
ListElement {
isExpanded: false;
@ -217,8 +216,6 @@ At the moment, there is currently no way to convert HFC to other currencies. Sta
}
} else if (link === "#support") {
Qt.openUrlExternally("mailto:support@highfidelity.com");
} else if (link === "#securitypic") {
sendSignalToWallet({method: 'walletSecurity_changeSecurityImage'});
}
}
}

View file

@ -93,7 +93,7 @@ Item {
// Text below helper text
RalewayRegular {
id: loginDetailText;
text: "To buy/sell items on the <b>Marketplace</b>, or to use your <b>Wallet</b>, you must first log in to High Fidelity.";
text: "To get items on the <b>Marketplace</b>, or to use your <b>Assets</b>, you must first log in to High Fidelity.";
// Text size
size: 18;
// Anchors

View file

@ -1,246 +0,0 @@
//
// Security.qml
// qml/hifi/commerce/wallet
//
// Security
//
// Created by Zach Fox on 2017-08-18
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0 as Hifi
import QtQuick 2.5
import QtGraphicalEffects 1.0
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
// references XXX from root context
Item {
HifiConstants { id: hifi; }
id: root;
property string keyFilePath;
Connections {
target: Commerce;
onKeyFilePathIfExistsResult: {
root.keyFilePath = path;
}
}
// Username Text
RalewayRegular {
id: usernameText;
text: Account.username;
// Text size
size: 24;
// Style
color: hifi.colors.white;
elide: Text.ElideRight;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: parent.width/2;
height: 80;
}
Item {
id: securityContainer;
anchors.top: usernameText.bottom;
anchors.topMargin: 20;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: parent.bottom;
RalewaySemiBold {
id: securityText;
text: "Security";
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 20;
anchors.right: parent.right;
height: 30;
// Text size
size: 18;
// Style
color: hifi.colors.blueHighlight;
}
Rectangle {
id: securityTextSeparator;
// Size
width: parent.width;
height: 1;
// Anchors
anchors.left: parent.left;
anchors.right: parent.right;
anchors.top: securityText.bottom;
anchors.topMargin: 8;
// Style
color: hifi.colors.faintGray;
}
Item {
id: changeSecurityImageContainer;
anchors.top: securityTextSeparator.bottom;
anchors.topMargin: 8;
anchors.left: parent.left;
anchors.leftMargin: 40;
anchors.right: parent.right;
anchors.rightMargin: 55;
height: 75;
HiFiGlyphs {
id: changeSecurityImageImage;
text: hifi.glyphs.securityImage;
// Size
size: 80;
// Anchors
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
// Style
color: hifi.colors.white;
}
RalewaySemiBold {
text: "Security Pic";
// Anchors
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.left: changeSecurityImageImage.right;
anchors.leftMargin: 30;
width: 50;
// Text size
size: 18;
// Style
color: hifi.colors.white;
}
// "Change Security Pic" button
HifiControlsUit.Button {
id: changeSecurityImageButton;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.right: parent.right;
anchors.verticalCenter: parent.verticalCenter;
width: 140;
height: 40;
text: "Change";
onClicked: {
sendSignalToWallet({method: 'walletSecurity_changeSecurityImage'});
}
}
}
Item {
id: autoLogoutContainer;
anchors.top: changeSecurityImageContainer.bottom;
anchors.topMargin: 8;
anchors.left: parent.left;
anchors.leftMargin: 40;
anchors.right: parent.right;
anchors.rightMargin: 55;
height: 75;
HiFiGlyphs {
id: autoLogoutImage;
text: hifi.glyphs.walletKey;
// Size
size: 80;
// Anchors
anchors.top: parent.top;
anchors.topMargin: 20;
anchors.left: parent.left;
// Style
color: hifi.colors.white;
}
HifiControlsUit.CheckBox {
id: autoLogoutCheckbox;
checked: Settings.getValue("wallet/autoLogout", false);
text: "Automatically Log Out when Exiting Interface"
// Anchors
anchors.verticalCenter: autoLogoutImage.verticalCenter;
anchors.left: autoLogoutImage.right;
anchors.leftMargin: 20;
anchors.right: autoLogoutHelp.left;
anchors.rightMargin: 12;
boxSize: 28;
labelFontSize: 18;
color: hifi.colors.white;
onCheckedChanged: {
Settings.setValue("wallet/autoLogout", checked);
if (checked) {
Settings.setValue("wallet/savedUsername", Account.username);
} else {
Settings.setValue("wallet/savedUsername", "");
}
}
}
RalewaySemiBold {
id: autoLogoutHelp;
text: '[?]';
// Anchors
anchors.verticalCenter: autoLogoutImage.verticalCenter;
anchors.right: parent.right;
width: 30;
height: 30;
// Text size
size: 18;
// Style
color: hifi.colors.blueHighlight;
MouseArea {
anchors.fill: parent;
hoverEnabled: true;
onEntered: {
parent.color = hifi.colors.blueAccent;
}
onExited: {
parent.color = hifi.colors.blueHighlight;
}
onClicked: {
sendSignalToWallet({method: 'walletSecurity_autoLogoutHelp'});
}
}
}
}
}
//
// FUNCTION DEFINITIONS START
//
//
// Function Name: fromScript()
//
// Relevant Variables:
// None
//
// Arguments:
// message: The message sent from the JavaScript.
// Messages are in format "{method, params}", like json-rpc.
//
// Description:
// Called when a message is received from a script.
//
function fromScript(message) {
switch (message.method) {
default:
console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
}
}
signal sendSignalToWallet(var msg);
//
// FUNCTION DEFINITIONS END
//
}

View file

@ -20,6 +20,8 @@ import "../../../controls" as HifiControls
import "../common" as HifiCommerceCommon
import "../common/sendAsset"
import "../.." as HifiCommon
import "../purchases" as HifiPurchases
import "../inspectionCertificate" as HifiInspectionCertificate
Rectangle {
HifiConstants { id: hifi; }
@ -27,6 +29,7 @@ Rectangle {
id: root;
property string activeView: "initialize";
property string initialActiveViewAfterStatus5: "walletInventory";
property bool keyboardRaised: false;
property bool isPassword: false;
@ -64,7 +67,8 @@ Rectangle {
}
} else if (walletStatus === 5) {
if (root.activeView !== "walletSetup") {
root.activeView = "walletHome";
root.activeView = root.initialActiveViewAfterStatus5;
Commerce.getAvailableUpdates();
Commerce.getSecurityImage();
}
} else {
@ -86,6 +90,21 @@ Rectangle {
titleBarSecurityImage.source = "image://security/securityImage";
}
}
onAvailableUpdatesResult: {
if (result.status !== 'success') {
console.log("Failed to get Available Updates", result.data.message);
} else {
exchangeMoneyButtonContainer.messagesWaiting = result.data.updates.length > 0;
}
}
}
onActiveViewChanged: {
if (activeView === "walletHome") {
walletHomeButtonContainer.messagesWaiting = false;
sendToScript({method: 'clearShouldShowDotHistory'});
}
}
HifiCommerceCommon.CommerceLightbox {
@ -108,29 +127,32 @@ Rectangle {
anchors.top: parent.top;
// Wallet icon
HiFiGlyphs {
Image {
id: walletIcon;
text: hifi.glyphs.wallet;
// Size
size: parent.height * 0.8;
// Anchors
source: "../../../../icons/tablet-icons/inventory-a.svg";
height: parent.height * 0.5;
width: walletIcon.height;
anchors.left: parent.left;
anchors.leftMargin: 8;
anchors.verticalCenter: parent.verticalCenter;
// Style
visible: false; // When we use a white .svg instead of a glyph with color property, we set to invisible and use the following ColorOverlay.
}
ColorOverlay {
anchors.fill: walletIcon;
source: walletIcon;
color: hifi.colors.blueHighlight;
}
// Title Bar text
RalewaySemiBold {
id: titleBarText;
text: "WALLET";
text: "INVENTORY";
// Text size
size: hifi.fontSizes.overlayTitle;
// Anchors
anchors.top: parent.top;
anchors.left: walletIcon.right;
anchors.leftMargin: 4;
anchors.leftMargin: 6;
anchors.bottom: parent.bottom;
width: paintedWidth;
// Style
@ -142,7 +164,7 @@ Rectangle {
Image {
id: titleBarSecurityImage;
source: "";
visible: titleBarSecurityImage.source !== "" && !securityImageChange.visible;
visible: titleBarSecurityImage.source !== "";
anchors.right: parent.right;
anchors.rightMargin: 6;
anchors.top: parent.top;
@ -232,10 +254,6 @@ Rectangle {
root.isPassword = msg.isPasswordField;
} else if (msg.method === 'walletSetup_lowerKeyboard') {
root.keyboardRaised = false;
} else if (msg.method === 'walletSecurity_changePassphraseCancelled') {
root.activeView = "security";
} else if (msg.method === 'walletSecurity_changePassphraseSuccess') {
root.activeView = "security";
} else {
sendToScript(msg);
}
@ -245,27 +263,6 @@ Rectangle {
}
}
}
SecurityImageChange {
id: securityImageChange;
visible: root.activeView === "securityImageChange";
z: 997;
anchors.top: titleBarContainer.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: parent.bottom;
Connections {
onSendSignalToWallet: {
if (msg.method === 'walletSecurity_changeSecurityImageCancelled') {
root.activeView = "security";
} else if (msg.method === 'walletSecurity_changeSecurityImageSuccess') {
root.activeView = "security";
} else {
sendToScript(msg);
}
}
}
}
//
// TAB CONTENTS START
@ -344,6 +341,39 @@ Rectangle {
}
}
HifiInspectionCertificate.InspectionCertificate {
id: inspectionCertificate;
z: 998;
visible: false;
anchors.fill: parent;
Connections {
onSendToScript: {
sendToScript(message);
}
}
}
HifiPurchases.Purchases {
id: walletInventory;
visible: root.activeView === "walletInventory";
anchors.top: titleBarContainer.bottom;
anchors.bottom: !WalletScriptingInterface.limitedCommerce ? tabButtonsContainer.top : parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
Connections {
onSendToScript: {
if (message.method === 'purchases_itemCertificateClicked') {
inspectionCertificate.visible = true;
inspectionCertificate.isLightbox = true;
sendToScript(message);
} else {
sendToScript(message);
}
}
}
}
HifiCommon.RootHttpRequest {
id: http;
}
@ -366,39 +396,6 @@ Rectangle {
}
}
Security {
id: security;
visible: root.activeView === "security";
anchors.top: titleBarContainer.bottom;
anchors.bottom: tabButtonsContainer.top;
anchors.left: parent.left;
anchors.right: parent.right;
Connections {
onSendSignalToWallet: {
if (msg.method === 'walletSecurity_changePassphrase') {
root.activeView = "passphraseChange";
passphraseChange.clearPassphraseFields();
passphraseChange.resetSubmitButton();
} else if (msg.method === 'walletSecurity_changeSecurityImage') {
securityImageChange.initModel();
root.activeView = "securityImageChange";
} else if (msg.method === 'walletSecurity_autoLogoutHelp') {
lightboxPopup.titleText = "Automatically Log Out";
lightboxPopup.bodyText = "By default, after you log in to High Fidelity, you will stay logged in to your High Fidelity " +
"account even after you close and re-open Interface. This means anyone who opens Interface on your computer " +
"could make purchases with your Wallet.\n\n" +
"If you do not want to stay logged in across Interface sessions, check this box.";
lightboxPopup.button1text = "CLOSE";
lightboxPopup.button1method = function() {
lightboxPopup.visible = false;
}
lightboxPopup.visible = true;
}
}
}
}
Help {
id: help;
visible: root.activeView === "help";
@ -407,14 +404,6 @@ Rectangle {
anchors.left: parent.left;
anchors.right: parent.right;
Connections {
onSendSignalToWallet: {
if (msg.method === 'walletSecurity_changeSecurityImage') {
securityImageChange.initModel();
root.activeView = "securityImageChange";
}
}
}
}
@ -427,8 +416,8 @@ Rectangle {
//
Item {
id: tabButtonsContainer;
visible: !needsLogIn.visible && root.activeView !== "passphraseChange" && root.activeView !== "securityImageChange" && sendMoney.currentActiveView !== "sendAssetStep";
property int numTabs: 5;
visible: !needsLogIn.visible && root.activeView !== "passphraseChange" && sendMoney.currentActiveView !== "sendAssetStep" && !WalletScriptingInterface.limitedCommerce;
property int numTabs: 4;
// Size
width: root.width;
height: 90;
@ -446,16 +435,17 @@ Rectangle {
// "WALLET HOME" tab button
Rectangle {
id: walletHomeButtonContainer;
property bool messagesWaiting: false;
visible: !walletSetup.visible;
color: root.activeView === "walletHome" ? hifi.colors.blueAccent : hifi.colors.black;
anchors.top: parent.top;
anchors.left: parent.left;
anchors.left: exchangeMoneyButtonContainer.right;
anchors.bottom: parent.bottom;
width: parent.width / tabButtonsContainer.numTabs;
HiFiGlyphs {
id: homeTabIcon;
text: hifi.glyphs.home2;
text: hifi.glyphs.leftRightArrows;
// Size
size: 50;
// Anchors
@ -463,11 +453,24 @@ Rectangle {
anchors.top: parent.top;
anchors.topMargin: -2;
// Style
color: root.activeView === "walletHome" || walletHomeTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight;
color: WalletScriptingInterface.limitedCommerce ? hifi.colors.lightGray50 : ((root.activeView === "walletHome" || walletHomeTabMouseArea.containsMouse) ? hifi.colors.white : hifi.colors.blueHighlight);
}
Rectangle {
id: recentActivityMessagesWaitingLight;
visible: parent.messagesWaiting;
anchors.right: homeTabIcon.left;
anchors.rightMargin: -4;
anchors.top: homeTabIcon.top;
anchors.topMargin: 16;
height: 10;
width: height;
radius: height/2;
color: "red";
}
RalewaySemiBold {
text: "WALLET HOME";
text: "RECENT ACTIVITY";
// Text size
size: 16;
// Anchors
@ -478,7 +481,7 @@ Rectangle {
anchors.right: parent.right;
anchors.rightMargin: 4;
// Style
color: root.activeView === "walletHome" || walletHomeTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight;
color: WalletScriptingInterface.limitedCommerce ? hifi.colors.lightGray50 : ((root.activeView === "walletHome" || walletHomeTabMouseArea.containsMouse) ? hifi.colors.white : hifi.colors.blueHighlight);
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
@ -487,6 +490,7 @@ Rectangle {
MouseArea {
id: walletHomeTabMouseArea;
anchors.fill: parent;
enabled: !WalletScriptingInterface.limitedCommerce;
hoverEnabled: enabled;
onClicked: {
root.activeView = "walletHome";
@ -500,28 +504,46 @@ Rectangle {
// "EXCHANGE MONEY" tab button
Rectangle {
id: exchangeMoneyButtonContainer;
property bool messagesWaiting: false;
visible: !walletSetup.visible;
color: hifi.colors.black;
color: root.activeView === "walletInventory" ? hifi.colors.blueAccent : hifi.colors.black;
anchors.top: parent.top;
anchors.left: walletHomeButtonContainer.right;
anchors.left: parent.left;
anchors.bottom: parent.bottom;
width: parent.width / tabButtonsContainer.numTabs;
HiFiGlyphs {
Image {
id: exchangeMoneyTabIcon;
text: hifi.glyphs.leftRightArrows;
// Size
size: 50;
// Anchors
source: "images/items-tab-a.svg";
height: 25;
width: exchangeMoneyTabIcon.height;
anchors.horizontalCenter: parent.horizontalCenter;
anchors.top: parent.top;
anchors.topMargin: -2;
// Style
color: hifi.colors.lightGray50;
anchors.topMargin: 10;
visible: false; // When we use a white .svg instead of a glyph with color property, we set to invisible and use the following ColorOverlay.
}
ColorOverlay {
anchors.fill: exchangeMoneyTabIcon;
source: exchangeMoneyTabIcon;
color: root.activeView === "walletInventory" || inventoryTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight;
}
Rectangle {
id: exchangeMoneyMessagesWaitingLight;
visible: parent.messagesWaiting;
anchors.right: exchangeMoneyTabIcon.left;
anchors.rightMargin: 9;
anchors.top: exchangeMoneyTabIcon.top;
anchors.topMargin: 4;
height: 10;
width: height;
radius: height/2;
color: "red";
}
RalewaySemiBold {
text: "EXCHANGE MONEY";
text: "ITEMS";
// Text size
size: 16;
// Anchors
@ -532,12 +554,24 @@ Rectangle {
anchors.right: parent.right;
anchors.rightMargin: 4;
// Style
color: hifi.colors.lightGray50;
color: root.activeView === "walletInventory" || inventoryTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignTop;
}
MouseArea {
id: inventoryTabMouseArea;
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
root.activeView = "walletInventory";
tabButtonsContainer.resetTabButtonColors();
}
onEntered: parent.color = hifi.colors.blueHighlight;
onExited: parent.color = root.activeView === "walletInventory" ? hifi.colors.blueAccent : hifi.colors.black;
}
}
@ -547,7 +581,7 @@ Rectangle {
visible: !walletSetup.visible;
color: root.activeView === "sendMoney" ? hifi.colors.blueAccent : hifi.colors.black;
anchors.top: parent.top;
anchors.left: exchangeMoneyButtonContainer.right;
anchors.left: walletHomeButtonContainer.right;
anchors.bottom: parent.bottom;
width: parent.width / tabButtonsContainer.numTabs;
@ -561,7 +595,7 @@ Rectangle {
anchors.top: parent.top;
anchors.topMargin: -2;
// Style
color: root.activeView === "sendMoney" || sendMoneyTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight;
color: WalletScriptingInterface.limitedCommerce ? hifi.colors.lightGray50 : ((root.activeView === "sendMoney" || sendMoneyTabMouseArea.containsMouse) ? hifi.colors.white : hifi.colors.blueHighlight);
}
RalewaySemiBold {
@ -576,7 +610,7 @@ Rectangle {
anchors.right: parent.right;
anchors.rightMargin: 4;
// Style
color: root.activeView === "sendMoney" || sendMoneyTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight;
color: WalletScriptingInterface.limitedCommerce ? hifi.colors.lightGray50 : ((root.activeView === "sendMoney" || sendMoneyTabMouseArea.containsMouse) ? hifi.colors.white : hifi.colors.blueHighlight);
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
@ -586,6 +620,7 @@ Rectangle {
MouseArea {
id: sendMoneyTabMouseArea;
anchors.fill: parent;
enabled: !WalletScriptingInterface.limitedCommerce;
hoverEnabled: enabled;
onClicked: {
root.activeView = "sendMoney";
@ -596,67 +631,13 @@ Rectangle {
}
}
// "SECURITY" tab button
Rectangle {
id: securityButtonContainer;
visible: !walletSetup.visible;
color: root.activeView === "security" ? hifi.colors.blueAccent : hifi.colors.black;
anchors.top: parent.top;
anchors.left: sendMoneyButtonContainer.right;
anchors.bottom: parent.bottom;
width: parent.width / tabButtonsContainer.numTabs;
HiFiGlyphs {
id: securityTabIcon;
text: hifi.glyphs.lock;
// Size
size: 38;
// Anchors
anchors.horizontalCenter: parent.horizontalCenter;
anchors.top: parent.top;
anchors.topMargin: 2;
// Style
color: root.activeView === "security" || securityTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight;
}
RalewaySemiBold {
text: "SECURITY";
// Text size
size: 16;
// Anchors
anchors.bottom: parent.bottom;
height: parent.height/2;
anchors.left: parent.left;
anchors.leftMargin: 4;
anchors.right: parent.right;
anchors.rightMargin: 4;
// Style
color: root.activeView === "security" || securityTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignTop;
}
MouseArea {
id: securityTabMouseArea;
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
root.activeView = "security";
tabButtonsContainer.resetTabButtonColors();
}
onEntered: parent.color = hifi.colors.blueHighlight;
onExited: parent.color = root.activeView === "security" ? hifi.colors.blueAccent : hifi.colors.black;
}
}
// "HELP" tab button
Rectangle {
id: helpButtonContainer;
visible: !walletSetup.visible;
color: root.activeView === "help" ? hifi.colors.blueAccent : hifi.colors.black;
anchors.top: parent.top;
anchors.left: securityButtonContainer.right;
anchors.left: sendMoneyButtonContainer.right;
anchors.bottom: parent.bottom;
width: parent.width / tabButtonsContainer.numTabs;
@ -708,16 +689,16 @@ Rectangle {
function resetTabButtonColors() {
walletHomeButtonContainer.color = hifi.colors.black;
sendMoneyButtonContainer.color = hifi.colors.black;
securityButtonContainer.color = hifi.colors.black;
helpButtonContainer.color = hifi.colors.black;
exchangeMoneyButtonContainer.color = hifi.colors.black;
if (root.activeView === "walletHome") {
walletHomeButtonContainer.color = hifi.colors.blueAccent;
} else if (root.activeView === "sendMoney") {
sendMoneyButtonContainer.color = hifi.colors.blueAccent;
} else if (root.activeView === "security") {
securityButtonContainer.color = hifi.colors.blueAccent;
} else if (root.activeView === "help") {
helpButtonContainer.color = hifi.colors.blueAccent;
} else if (root.activeView == "walletInventory") {
exchangeMoneyButtonContainer.color = hifi.colors.blueAccent;
}
}
}
@ -783,18 +764,40 @@ Rectangle {
break;
case 'updateConnections':
sendMoney.updateConnections(message.connections);
walletInventory.fromScript(message);
break;
case 'selectRecipient':
case 'updateSelectedRecipientUsername':
sendMoney.fromScript(message);
walletInventory.fromScript(message);
break;
case 'http.response':
http.handleHttpResponse(message);
// Duplicate handler is required because we don't track referrer for `http`
walletInventory.fromScript(message);
break;
case 'palIsStale':
case 'avatarDisconnected':
// Because we don't have "channels" for sending messages to a specific QML object, the messages are broadcast to all QML Items. If an Item of yours happens to be visible when some script sends a message with a method you don't expect, you'll get "Unrecognized message..." logs.
break;
case 'inspectionCertificate_setCertificateId':
inspectionCertificate.fromScript(message);
break;
case 'updatePurchases':
case 'purchases_showMyItems':
case 'updateWearables':
walletInventory.fromScript(message);
break;
case 'updateRecentActivityMessageLight':
walletHomeButtonContainer.messagesWaiting = message.messagesWaiting;
break;
case 'checkout_openRecentActivity':
if (root.activeView === "initialize") {
root.initialActiveViewAfterStatus5 = "walletHome";
} else {
root.activeView = "walletHome";
}
break;
default:
console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
}
@ -842,7 +845,8 @@ Rectangle {
root.activeView = "initialize";
Commerce.getWalletStatus();
} else if (msg.referrer === 'purchases') {
sendToScript({method: 'goToPurchases'});
root.activeView = "walletInventory";
tabButtonsContainer.resetTabButtonColors();
} else if (msg.referrer === 'marketplace cta' || msg.referrer === 'mainPage') {
sendToScript({method: 'goToMarketplaceMainPage', itemId: msg.referrer});
} else {

View file

@ -179,28 +179,6 @@ Item {
color: hifi.colors.baseGrayHighlight;
}
RalewaySemiBold {
id: myPurchasesLink;
text: '<font color="#0093C5"><a href="#myPurchases">My Purchases</a></font>';
// Anchors
anchors.top: parent.top;
anchors.topMargin: 26;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: paintedWidth;
height: 30;
y: 4;
// Text size
size: 18;
// Style
color: hifi.colors.baseGrayHighlight;
horizontalAlignment: Text.AlignRight;
onLinkActivated: {
sendSignalToWallet({method: 'goToPurchases_fromWalletHome'});
}
}
HifiModels.PSFListModel {
id: transactionHistoryModel;
property int lastPendingCount: 0;

View file

@ -243,7 +243,6 @@ Item {
height: 50;
text: "Set Up Wallet";
onClicked: {
securityImageSelection.initModel();
root.activeView = "step_2";
}
}
@ -267,124 +266,6 @@ Item {
// FIRST PAGE END
//
//
// SECURITY IMAGE SELECTION START
//
Item {
id: securityImageContainer;
visible: root.activeView === "step_2";
// Anchors
anchors.top: titleBarContainer.bottom;
anchors.topMargin: 30;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
// Text below title bar
RalewayRegular {
id: securityImageTitleHelper;
text: "Choose a Security Pic:";
// Text size
size: 24;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
height: 50;
width: paintedWidth;
// Style
color: hifi.colors.white;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
SecurityImageSelection {
id: securityImageSelection;
// Anchors
anchors.top: securityImageTitleHelper.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
height: 300;
Connections {
onSendSignalToWallet: {
sendSignalToWallet(msg);
}
}
}
// Text below security images
RalewayRegular {
text: "<b>Your security picture shows you that the service asking for your passphrase is authorized.</b> You can change your secure picture at any time.";
// Text size
size: 18;
// Anchors
anchors.top: securityImageSelection.bottom;
anchors.topMargin: 40;
anchors.left: parent.left;
anchors.right: parent.right;
height: paintedHeight;
// Style
color: hifi.colors.white;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
// Navigation Bar
Item {
// Size
width: parent.width;
height: 50;
// Anchors:
anchors.left: parent.left;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 50;
// "Back" button
HifiControlsUit.Button {
color: hifi.buttons.noneBorderlessWhite;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: 200;
text: "Back"
onClicked: {
securityImageSelection.resetSelection();
root.activeView = "step_1";
}
}
// "Next" button
HifiControlsUit.Button {
enabled: securityImageSelection.currentIndex !== -1;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: 200;
text: "Next";
onClicked: {
root.lastPage = "step_2";
var securityImagePath = securityImageSelection.getImagePathFromImageID(securityImageSelection.getSelectedImageIndex())
Commerce.chooseSecurityImage(securityImagePath);
root.activeView = "step_3";
passphraseSelection.clearPassphraseFields();
}
}
}
}
//
// SECURITY IMAGE SELECTION END
//
//
// SECURE PASSPHRASE SELECTION START
//
@ -525,7 +406,6 @@ Item {
width: 200;
text: "Back"
onClicked: {
securityImageSelection.resetSelection();
root.lastPage = "step_3";
root.activeView = "step_2";
}

View file

@ -0,0 +1,8 @@
<svg width="26" height="24" viewBox="0 0 26 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<line x1="6" y1="2" x2="26" y2="2" stroke="white" stroke-width="4"/>
<line x1="1.74846e-07" y1="2" x2="4" y2="2" stroke="white" stroke-width="4"/>
<line x1="6" y1="12" x2="26" y2="12" stroke="white" stroke-width="4"/>
<line x1="1.74846e-07" y1="12" x2="4" y2="12" stroke="white" stroke-width="4"/>
<line x1="6" y1="22" x2="26" y2="22" stroke="white" stroke-width="4"/>
<line x1="1.74846e-07" y1="22" x2="4" y2="22" stroke="white" stroke-width="4"/>
</svg>

After

Width:  |  Height:  |  Size: 552 B

View file

@ -0,0 +1,344 @@
//
// Security.qml
// qml\hifi\dialogs\security
//
// Security
//
// Created by Zach Fox on 2018-10-31
// Copyright 2018 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
//
import Hifi 1.0 as Hifi
import QtQuick 2.5
import QtGraphicalEffects 1.0
import "qrc:////qml//styles-uit" as HifiStylesUit
import "qrc:////qml//controls-uit" as HifiControlsUit
import "qrc:////qml//controls" as HifiControls
import "qrc:////qml//hifi//commerce//common" as HifiCommerceCommon
Rectangle {
HifiStylesUit.HifiConstants { id: hifi; }
id: root;
color: hifi.colors.baseGray;
property string title: "Security Settings";
property bool walletSetUp;
QtObject {
id: margins
property real paddings: root.width / 20.25
property real sizeCheckBox: root.width / 13.5
property real sizeText: root.width / 2.5
property real sizeLevel: root.width / 5.8
property real sizeDesktop: root.width / 5.8
property real sizeVR: root.width / 13.5
}
Connections {
target: Commerce;
onWalletStatusResult: {
if (walletStatus === 5) {
Commerce.getSecurityImage();
root.walletSetUp = true;
} else {
root.walletSetUp = false;
}
}
onSecurityImageResult: {
if (exists) {
currentSecurityPicture.source = "";
currentSecurityPicture.source = "image://security/securityImage";
}
}
}
Component.onCompleted: {
Commerce.getWalletStatus();
}
HifiCommerceCommon.CommerceLightbox {
z: 996;
id: lightboxPopup;
visible: false;
anchors.fill: parent;
}
SecurityImageChange {
id: securityImageChange;
visible: false;
z: 997;
anchors.top: usernameText.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: parent.bottom;
Connections {
onSendSignalToParent: {
securityImageChange.visible = false;
}
}
}
// Username Text
HifiStylesUit.RalewayRegular {
id: usernameText;
text: Account.username === "Unknown user" ? "Please Log In" : Account.username;
// Text size
size: 24;
// Style
color: hifi.colors.white;
elide: Text.ElideRight;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 20;
anchors.right: parent.right;
anchors.rightMargin: 20;
height: 60;
}
Item {
id: pleaseLogInContainer;
visible: Account.username === "Unknown user";
anchors.top: usernameText.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: parent.bottom;
HifiStylesUit.RalewayRegular {
text: "Please log in for security settings."
// Text size
size: 24;
// Style
color: hifi.colors.white;
// Anchors
anchors.bottom: openLoginButton.top;
anchors.left: parent.left;
anchors.right: parent.right;
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
height: 60;
}
HifiControlsUit.Button {
id: openLoginButton;
color: hifi.buttons.white;
colorScheme: hifi.colorSchemes.dark;
anchors.centerIn: parent;
width: 140;
height: 40;
text: "Log In";
onClicked: {
DialogsManager.showLoginDialog();
}
}
}
Item {
id: securitySettingsContainer;
visible: !pleaseLogInContainer.visible;
anchors.top: usernameText.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: parent.bottom;
Item {
id: accountContainer;
anchors.top: securitySettingsContainer.top;
anchors.left: parent.left;
anchors.right: parent.right;
height: childrenRect.height;
Rectangle {
id: accountHeaderContainer;
anchors.top: parent.top;
anchors.left: parent.left;
anchors.right: parent.right;
height: 55;
color: hifi.colors.baseGrayHighlight;
HifiStylesUit.RalewaySemiBold {
text: "Account";
anchors.fill: parent;
anchors.leftMargin: 20;
color: hifi.colors.white;
size: 18;
}
}
Item {
id: keepMeLoggedInContainer;
anchors.top: accountHeaderContainer.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
height: 80;
HifiControlsUit.CheckBox {
id: autoLogoutCheckbox;
checked: Settings.getValue("keepMeLoggedIn", false);
text: "Keep Me Logged In"
// Anchors
anchors.verticalCenter: parent.verticalCenter;
anchors.left: parent.left;
anchors.leftMargin: 20;
boxSize: 24;
labelFontSize: 18;
colorScheme: hifi.colorSchemes.dark
color: hifi.colors.white;
width: 240;
onCheckedChanged: {
Settings.setValue("keepMeLoggedIn", checked);
if (checked) {
Settings.setValue("keepMeLoggedIn/savedUsername", Account.username);
} else {
Settings.setValue("keepMeLoggedIn/savedUsername", "");
}
}
}
HifiStylesUit.RalewaySemiBold {
id: autoLogoutHelp;
text: '[?]';
// Anchors
anchors.verticalCenter: parent.verticalCenter;
anchors.right: autoLogoutCheckbox.right;
width: 30;
height: 30;
// Text size
size: 18;
// Style
color: hifi.colors.blueHighlight;
MouseArea {
anchors.fill: parent;
hoverEnabled: true;
onEntered: {
parent.color = hifi.colors.blueAccent;
}
onExited: {
parent.color = hifi.colors.blueHighlight;
}
onClicked: {
lightboxPopup.titleText = "Keep Me Logged In";
lightboxPopup.bodyText = "If you choose to stay logged in, ensure that this is a trusted device.\n\n" +
"Also, remember that logging out may not disconnect you from a domain.";
lightboxPopup.button1text = "OK";
lightboxPopup.button1method = function() {
lightboxPopup.visible = false;
}
lightboxPopup.visible = true;
}
}
}
}
}
Item {
id: walletContainer;
anchors.top: accountContainer.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
height: childrenRect.height;
Rectangle {
id: walletHeaderContainer;
anchors.top: parent.top;
anchors.left: parent.left;
anchors.right: parent.right;
height: 55;
color: hifi.colors.baseGrayHighlight;
HifiStylesUit.RalewaySemiBold {
text: "Wallet";
anchors.fill: parent;
anchors.leftMargin: 20;
color: hifi.colors.white;
size: 18;
}
}
Item {
id: walletSecurityPictureContainer;
visible: root.walletSetUp;
anchors.top: walletHeaderContainer.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
height: 80;
Image {
id: currentSecurityPicture;
source: "";
visible: true;
anchors.left: parent.left;
anchors.leftMargin: 20;
anchors.verticalCenter: parent.verticalCenter;
height: 40;
width: height;
mipmap: true;
cache: false;
}
HifiStylesUit.RalewaySemiBold {
id: securityPictureText;
text: "Wallet Security Picture";
// Anchors
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.left: currentSecurityPicture.right;
anchors.leftMargin: 12;
width: paintedWidth;
// Text size
size: 18;
// Style
color: hifi.colors.white;
}
// "Change Security Pic" button
HifiControlsUit.Button {
id: changeSecurityImageButton;
color: hifi.buttons.white;
colorScheme: hifi.colorSchemes.dark;
anchors.left: securityPictureText.right;
anchors.leftMargin: 12;
anchors.verticalCenter: parent.verticalCenter;
width: 140;
height: 40;
text: "Change";
onClicked: {
securityImageChange.visible = true;
securityImageChange.initModel();
}
}
}
Item {
id: walletNotSetUpContainer;
visible: !root.walletSetUp;
anchors.top: walletHeaderContainer.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
height: 60;
HifiStylesUit.RalewayRegular {
text: "Your wallet is not set up.\n" +
"Open the ASSETS app to get started.";
// Anchors
anchors.fill: parent;
// Text size
size: 18;
// Style
color: hifi.colors.white;
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
}
}
}
}

View file

@ -1,11 +1,11 @@
//
// SecurityImageChange.qml
// qml/hifi/commerce/wallet
// qml\hifi\dialogs\security
//
// SecurityImageChange
// Security
//
// Created by Zach Fox on 2017-08-18
// Copyright 2017 High Fidelity, Inc.
// Created by Zach Fox on 2018-10-31
// Copyright 2018 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
@ -13,16 +13,17 @@
import Hifi 1.0 as Hifi
import QtQuick 2.5
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
import "qrc:////qml//styles-uit" as HifiStylesUit
import "qrc:////qml//controls-uit" as HifiControlsUit
import "qrc:////qml//controls" as HifiControls
// references XXX from root context
Item {
HifiConstants { id: hifi; }
Rectangle {
HifiStylesUit.HifiConstants { id: hifi; }
id: root;
color: hifi.colors.baseGray;
property bool justSubmitted: false;
Connections {
@ -33,7 +34,7 @@ Item {
securityImageChangePageSecurityImage.source = "image://security/securityImage";
if (exists) { // Success submitting new security image
if (root.justSubmitted) {
sendSignalToWallet({method: "walletSecurity_changeSecurityImageSuccess"});
sendSignalToParent({method: "walletSecurity_changeSecurityImageSuccess"});
root.justSubmitted = false;
}
} else if (root.justSubmitted) {
@ -72,7 +73,7 @@ Item {
anchors.bottom: parent.bottom;
height: 22;
// Lock icon
HiFiGlyphs {
HifiStylesUit.HiFiGlyphs {
id: lockIcon;
text: hifi.glyphs.lock;
anchors.bottom: parent.bottom;
@ -83,8 +84,8 @@ Item {
verticalAlignment: Text.AlignBottom;
color: hifi.colors.white;
}
// "Security image" text below pic
RalewayRegular {
// "Security image" text below image
HifiStylesUit.RalewayRegular {
id: securityImageText;
text: "SECURITY PIC";
// Text size
@ -116,9 +117,9 @@ Item {
anchors.bottom: parent.bottom;
// "Change Security Image" text
RalewaySemiBold {
HifiStylesUit.RalewaySemiBold {
id: securityImageTitle;
text: "Change Security Pic:";
text: "Change Security Image:";
// Text size
size: 18;
anchors.top: parent.top;
@ -139,12 +140,6 @@ Item {
anchors.right: parent.right;
anchors.rightMargin: 16;
height: 300;
Connections {
onSendSignalToWallet: {
sendSignalToWallet(msg);
}
}
}
// Navigation Bar
@ -169,7 +164,7 @@ Item {
width: 150;
text: "Cancel"
onClicked: {
sendSignalToWallet({method: "walletSecurity_changeSecurityImageCancelled"});
sendSignalToParent({method: "walletSecurity_changeSecurityImageCancelled"});
}
}
@ -197,7 +192,7 @@ Item {
// SECURITY IMAGE SELECTION END
//
signal sendSignalToWallet(var msg);
signal sendSignalToParent(var msg);
function initModel() {
securityImageSelection.initModel();

View file

@ -1,11 +1,11 @@
//
// SecurityImageModel.qml
// qml/hifi/commerce
// qml\hifi\dialogs\security
//
// SecurityImageModel
// Security
//
// Created by Zach Fox on 2017-08-17
// Copyright 2017 High Fidelity, Inc.
// Created by Zach Fox on 2018-10-31
// Copyright 2018 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

View file

@ -1,11 +1,11 @@
//
// SecurityImageSelection.qml
// qml/hifi/commerce/wallet
// qml\hifi\dialogs\security
//
// SecurityImageSelection
// Security
//
// Created by Zach Fox on 2017-08-17
// Copyright 2017 High Fidelity, Inc.
// Created by Zach Fox on 2018-10-31
// Copyright 2018 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
@ -13,14 +13,14 @@
import Hifi 1.0 as Hifi
import QtQuick 2.5
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
import "qrc:////qml//styles-uit" as HifiStylesUit
import "qrc:////qml//controls-uit" as HifiControlsUit
import "qrc:////qml//controls" as HifiControls
// references XXX from root context
Item {
HifiConstants { id: hifi; }
HifiStylesUit.HifiConstants { id: hifi; }
id: root;
property alias currentIndex: securityImageGrid.currentIndex;
@ -73,8 +73,6 @@ Item {
//
// FUNCTION DEFINITIONS START
//
signal sendSignalToWallet(var msg);
function getImagePathFromImageID(imageID) {
return (imageID ? gridModel.getImagePathFromImageID(imageID) : "");
}

View file

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View file

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View file

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 76 KiB

View file

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View file

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 59 KiB

View file

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

View file

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View file

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

View file

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

View file

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View file

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View file

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View file

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View file

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 53 KiB

View file

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 68 KiB

View file

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View file

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View file

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View file

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View file

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 79 KiB

View file

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View file

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View file

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 51 KiB

View file

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

View file

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View file

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View file

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View file

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

View file

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View file

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 53 KiB

View file

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View file

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 53 KiB

View file

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 59 KiB

View file

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

View file

@ -383,7 +383,7 @@ static const int INTERVAL_TO_CHECK_HMD_WORN_STATUS = 500; // milliseconds
static const QString DESKTOP_DISPLAY_PLUGIN_NAME = "Desktop";
static const QString ACTIVE_DISPLAY_PLUGIN_SETTING_NAME = "activeDisplayPlugin";
static const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system";
static const QString AUTO_LOGOUT_SETTING_NAME = "wallet/autoLogout";
static const QString KEEP_ME_LOGGED_IN_SETTING_NAME = "keepMeLoggedIn";
const std::vector<std::pair<QString, Application::AcceptURLMethod>> Application::_acceptedExtensions {
{ SVO_EXTENSION, &Application::importSVOFromURL },
@ -1150,7 +1150,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// set the OCULUS_STORE property so the oculus plugin can know if we ran from the Oculus Store
static const QString OCULUS_STORE_ARG = "--oculus-store";
setProperty(hifi::properties::OCULUS_STORE, arguments().indexOf(OCULUS_STORE_ARG) != -1);
bool isStore = arguments().indexOf(OCULUS_STORE_ARG) != -1;
setProperty(hifi::properties::OCULUS_STORE, isStore);
DependencyManager::get<WalletScriptingInterface>()->setLimitedCommerce(isStore); // Or we could make it a separate arg, or if either arg is set, etc. And should this instead by a hifi::properties?
updateHeartbeat();
@ -2569,8 +2571,8 @@ void Application::cleanupBeforeQuit() {
}
DependencyManager::destroy<ScriptEngines>();
bool autoLogout = Setting::Handle<bool>(AUTO_LOGOUT_SETTING_NAME, false).get();
if (autoLogout) {
bool keepMeLoggedIn = Setting::Handle<bool>(KEEP_ME_LOGGED_IN_SETTING_NAME, false).get();
if (!keepMeLoggedIn) {
DependencyManager::get<AccountManager>()->removeAccountFromFile();
}
@ -2946,14 +2948,14 @@ void Application::initializeUi() {
QUrl{ "hifi/commerce/wallet/PassphraseChange.qml" },
QUrl{ "hifi/commerce/wallet/PassphraseModal.qml" },
QUrl{ "hifi/commerce/wallet/PassphraseSelection.qml" },
QUrl{ "hifi/commerce/wallet/Security.qml" },
QUrl{ "hifi/commerce/wallet/SecurityImageChange.qml" },
QUrl{ "hifi/commerce/wallet/SecurityImageModel.qml" },
QUrl{ "hifi/commerce/wallet/SecurityImageSelection.qml" },
QUrl{ "hifi/commerce/wallet/Wallet.qml" },
QUrl{ "hifi/commerce/wallet/WalletHome.qml" },
QUrl{ "hifi/commerce/wallet/WalletSetup.qml" },
}, commerceCallback);
QUrl{ "hifi/dialogs/security/Security.qml" },
QUrl{ "hifi/dialogs/security/SecurityImageChange.qml" },
QUrl{ "hifi/dialogs/security/SecurityImageModel.qml" },
QUrl{ "hifi/dialogs/security/SecurityImageSelection.qml" },
}, callback);
QmlContextCallback ttsCallback = [](QQmlContext* context) {
context->setContextProperty("TextToSpeech", DependencyManager::get<TTSScriptingInterface>().data());
};
@ -3160,7 +3162,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) {
surfaceContext->setContextProperty("AvatarInputs", AvatarInputs::getInstance());
surfaceContext->setContextProperty("Selection", DependencyManager::get<SelectionScriptingInterface>().data());
surfaceContext->setContextProperty("ContextOverlay", DependencyManager::get<ContextOverlayInterface>().data());
surfaceContext->setContextProperty("Wallet", DependencyManager::get<WalletScriptingInterface>().data());
surfaceContext->setContextProperty("WalletScriptingInterface", DependencyManager::get<WalletScriptingInterface>().data());
surfaceContext->setContextProperty("HiFiAbout", AboutUtil::getInstance());
surfaceContext->setContextProperty("ResourceRequestObserver", DependencyManager::get<ResourceRequestObserver>().data());
@ -6882,7 +6884,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
scriptEngine->registerGlobalObject("AvatarInputs", AvatarInputs::getInstance());
scriptEngine->registerGlobalObject("Selection", DependencyManager::get<SelectionScriptingInterface>().data());
scriptEngine->registerGlobalObject("ContextOverlay", DependencyManager::get<ContextOverlayInterface>().data());
scriptEngine->registerGlobalObject("Wallet", DependencyManager::get<WalletScriptingInterface>().data());
scriptEngine->registerGlobalObject("WalletScriptingInterface", DependencyManager::get<WalletScriptingInterface>().data());
scriptEngine->registerGlobalObject("AddressManager", DependencyManager::get<AddressManager>().data());
scriptEngine->registerGlobalObject("HifiAbout", AboutUtil::getInstance());
scriptEngine->registerGlobalObject("ResourceRequestObserver", DependencyManager::get<ResourceRequestObserver>().data());
@ -7887,6 +7889,7 @@ void Application::loadAvatarBrowser() const {
auto tablet = dynamic_cast<TabletProxy*>(DependencyManager::get<TabletScriptingInterface>()->getTablet("com.highfidelity.interface.tablet.system"));
// construct the url to the marketplace item
QString url = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/marketplace?category=avatars";
QString MARKETPLACES_INJECT_SCRIPT_PATH = "file:///" + qApp->applicationDirPath() + "/scripts/system/html/js/marketplacesInject.js";
tablet->gotoWebScreen(url, MARKETPLACES_INJECT_SCRIPT_PATH);
DependencyManager::get<HMDScriptingInterface>()->openTablet();

View file

@ -265,6 +265,18 @@ Menu::Menu() {
QString("hifi/tablet/TabletGraphicsPreferences.qml"), "GraphicsPreferencesDialog");
});
// Settings > Security...
action = addActionToQMenuAndActionHash(settingsMenu, "Security...");
connect(action, &QAction::triggered, [] {
auto tablet = DependencyManager::get<TabletScriptingInterface>()->getTablet("com.highfidelity.interface.tablet.system");
auto hmd = DependencyManager::get<HMDScriptingInterface>();
tablet->pushOntoStack("hifi/dialogs/security/Security.qml");
if (!hmd->getShouldShowTablet()) {
hmd->toggleShouldShowTablet();
}
});
// Settings > Developer Menu
addCheckableActionToQMenuAndActionHash(settingsMenu, "Developer Menu", 0, false, this, SLOT(toggleDeveloperMenus()));

View file

@ -109,10 +109,10 @@ bool ModelPackager::loadModel() {
qCDebug(interfaceapp) << "Reading FBX file : " << _fbxInfo.filePath();
QByteArray fbxContents = fbx.readAll();
_geometry.reset(readFBX(fbxContents, QVariantHash(), _fbxInfo.filePath()));
_hfmModel.reset(readFBX(fbxContents, QVariantHash(), _fbxInfo.filePath()));
// make sure we have some basic mappings
populateBasicMapping(_mapping, _fbxInfo.filePath(), *_geometry);
populateBasicMapping(_mapping, _fbxInfo.filePath(), *_hfmModel);
} catch (const QString& error) {
qCDebug(interfaceapp) << "Error reading " << _fbxInfo.filePath() << ": " << error;
return false;
@ -122,7 +122,7 @@ bool ModelPackager::loadModel() {
bool ModelPackager::editProperties() {
// open the dialog to configure the rest
ModelPropertiesDialog properties(_modelType, _mapping, _modelFile.path(), *_geometry);
ModelPropertiesDialog properties(_modelType, _mapping, _modelFile.path(), *_hfmModel);
if (properties.exec() == QDialog::Rejected) {
return false;
}
@ -235,18 +235,18 @@ bool ModelPackager::zipModel() {
return true;
}
void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename, const FBXGeometry& geometry) {
void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename, const HFMModel& hfmModel) {
bool isBodyType = _modelType == FSTReader::BODY_ONLY_MODEL || _modelType == FSTReader::HEAD_AND_BODY_MODEL;
// mixamo files - in the event that a mixamo file was edited by some other tool, it's likely the applicationName will
// be rewritten, so we detect the existence of several different blendshapes which indicate we're likely a mixamo file
bool likelyMixamoFile = geometry.applicationName == "mixamo.com" ||
(geometry.blendshapeChannelNames.contains("BrowsDown_Right") &&
geometry.blendshapeChannelNames.contains("MouthOpen") &&
geometry.blendshapeChannelNames.contains("Blink_Left") &&
geometry.blendshapeChannelNames.contains("Blink_Right") &&
geometry.blendshapeChannelNames.contains("Squint_Right"));
bool likelyMixamoFile = hfmModel.applicationName == "mixamo.com" ||
(hfmModel.blendshapeChannelNames.contains("BrowsDown_Right") &&
hfmModel.blendshapeChannelNames.contains("MouthOpen") &&
hfmModel.blendshapeChannelNames.contains("Blink_Left") &&
hfmModel.blendshapeChannelNames.contains("Blink_Right") &&
hfmModel.blendshapeChannelNames.contains("Squint_Right"));
if (!mapping.contains(NAME_FIELD)) {
mapping.insert(NAME_FIELD, QFileInfo(filename).baseName());
@ -268,15 +268,15 @@ void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename
}
QVariantHash joints = mapping.value(JOINT_FIELD).toHash();
if (!joints.contains("jointEyeLeft")) {
joints.insert("jointEyeLeft", geometry.jointIndices.contains("jointEyeLeft") ? "jointEyeLeft" :
(geometry.jointIndices.contains("EyeLeft") ? "EyeLeft" : "LeftEye"));
joints.insert("jointEyeLeft", hfmModel.jointIndices.contains("jointEyeLeft") ? "jointEyeLeft" :
(hfmModel.jointIndices.contains("EyeLeft") ? "EyeLeft" : "LeftEye"));
}
if (!joints.contains("jointEyeRight")) {
joints.insert("jointEyeRight", geometry.jointIndices.contains("jointEyeRight") ? "jointEyeRight" :
geometry.jointIndices.contains("EyeRight") ? "EyeRight" : "RightEye");
joints.insert("jointEyeRight", hfmModel.jointIndices.contains("jointEyeRight") ? "jointEyeRight" :
hfmModel.jointIndices.contains("EyeRight") ? "EyeRight" : "RightEye");
}
if (!joints.contains("jointNeck")) {
joints.insert("jointNeck", geometry.jointIndices.contains("jointNeck") ? "jointNeck" : "Neck");
joints.insert("jointNeck", hfmModel.jointIndices.contains("jointNeck") ? "jointNeck" : "Neck");
}
if (isBodyType) {
@ -296,7 +296,7 @@ void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename
if (!joints.contains("jointHead")) {
const char* topName = likelyMixamoFile ? "HeadTop_End" : "HeadEnd";
joints.insert("jointHead", geometry.jointIndices.contains(topName) ? topName : "Head");
joints.insert("jointHead", hfmModel.jointIndices.contains(topName) ? topName : "Head");
}
mapping.insert(JOINT_FIELD, joints);
@ -370,7 +370,7 @@ void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename
void ModelPackager::listTextures() {
_textures.clear();
foreach (const FBXMaterial mat, _geometry->materials) {
foreach (const HFMMaterial mat, _hfmModel->materials) {
if (!mat.albedoTexture.filename.isEmpty() && mat.albedoTexture.content.isEmpty() &&
!_textures.contains(mat.albedoTexture.filename)) {
_textures << mat.albedoTexture.filename;

View file

@ -19,7 +19,7 @@
#include "ui/ModelsBrowser.h"
class FBXGeometry;
class HFMModel;
class ModelPackager : public QObject {
public:
@ -32,7 +32,7 @@ private:
bool editProperties();
bool zipModel();
void populateBasicMapping(QVariantHash& mapping, QString filename, const FBXGeometry& geometry);
void populateBasicMapping(QVariantHash& mapping, QString filename, const HFMModel& hfmModel);
void listTextures();
bool copyTextures(const QString& oldDir, const QDir& newDir);
@ -44,7 +44,7 @@ private:
QString _scriptDir;
QVariantHash _mapping;
std::unique_ptr<FBXGeometry> _geometry;
std::unique_ptr<HFMModel> _hfmModel;
QStringList _textures;
QStringList _scripts;
};

View file

@ -27,11 +27,11 @@
ModelPropertiesDialog::ModelPropertiesDialog(FSTReader::ModelType modelType, const QVariantHash& originalMapping,
const QString& basePath, const FBXGeometry& geometry) :
const QString& basePath, const HFMModel& hfmModel) :
_modelType(modelType),
_originalMapping(originalMapping),
_basePath(basePath),
_geometry(geometry)
_hfmModel(hfmModel)
{
setWindowTitle("Set Model Properties");
@ -108,8 +108,8 @@ QVariantHash ModelPropertiesDialog::getMapping() const {
// update the joint indices
QVariantHash jointIndices;
for (int i = 0; i < _geometry.joints.size(); i++) {
jointIndices.insert(_geometry.joints.at(i).name, QString::number(i));
for (int i = 0; i < _hfmModel.joints.size(); i++) {
jointIndices.insert(_hfmModel.joints.at(i).name, QString::number(i));
}
mapping.insert(JOINT_INDEX_FIELD, jointIndices);
@ -118,10 +118,10 @@ QVariantHash ModelPropertiesDialog::getMapping() const {
if (_modelType == FSTReader::ATTACHMENT_MODEL) {
glm::vec3 pivot;
if (_pivotAboutCenter->isChecked()) {
pivot = (_geometry.meshExtents.minimum + _geometry.meshExtents.maximum) * 0.5f;
pivot = (_hfmModel.meshExtents.minimum + _hfmModel.meshExtents.maximum) * 0.5f;
} else if (_pivotJoint->currentIndex() != 0) {
pivot = extractTranslation(_geometry.joints.at(_pivotJoint->currentIndex() - 1).transform);
pivot = extractTranslation(_hfmModel.joints.at(_pivotJoint->currentIndex() - 1).transform);
}
mapping.insert(TRANSLATION_X_FIELD, -pivot.x * (float)_scale->value() + (float)_translationX->value());
mapping.insert(TRANSLATION_Y_FIELD, -pivot.y * (float)_scale->value() + (float)_translationY->value());
@ -191,7 +191,7 @@ void ModelPropertiesDialog::reset() {
}
foreach (const QVariant& joint, _originalMapping.values(FREE_JOINT_FIELD)) {
QString jointName = joint.toString();
if (_geometry.jointIndices.contains(jointName)) {
if (_hfmModel.jointIndices.contains(jointName)) {
createNewFreeJoint(jointName);
}
}
@ -249,8 +249,8 @@ QComboBox* ModelPropertiesDialog::createJointBox(bool withNone) const {
if (withNone) {
box->addItem("(none)");
}
foreach (const FBXJoint& joint, _geometry.joints) {
if (joint.isSkeletonJoint || !_geometry.hasSkeletonJoints) {
foreach (const HFMJoint& joint, _hfmModel.joints) {
if (joint.isSkeletonJoint || !_hfmModel.hasSkeletonJoints) {
box->addItem(joint.name);
}
}
@ -266,7 +266,7 @@ QDoubleSpinBox* ModelPropertiesDialog::createTranslationBox() const {
}
void ModelPropertiesDialog::insertJointMapping(QVariantHash& joints, const QString& joint, const QString& name) const {
if (_geometry.jointIndices.contains(name)) {
if (_hfmModel.jointIndices.contains(name)) {
joints.insert(joint, name);
} else {
joints.remove(joint);

View file

@ -30,7 +30,7 @@ class ModelPropertiesDialog : public QDialog {
public:
ModelPropertiesDialog(FSTReader::ModelType modelType, const QVariantHash& originalMapping,
const QString& basePath, const FBXGeometry& geometry);
const QString& basePath, const HFMModel& hfmModel);
QVariantHash getMapping() const;
@ -50,7 +50,7 @@ private:
FSTReader::ModelType _modelType;
QVariantHash _originalMapping;
QString _basePath;
FBXGeometry _geometry;
HFMModel _hfmModel;
QLineEdit* _name = nullptr;
QPushButton* _textureDirectory = nullptr;
QPushButton* _scriptDirectory = nullptr;

View file

@ -155,8 +155,8 @@ MyAvatar::MyAvatar(QThread* thread) :
});
connect(_skeletonModel.get(), &Model::rigReady, this, [this]() {
if (_shouldLoadScripts) {
auto geometry = getSkeletonModel()->getFBXGeometry();
qApp->loadAvatarScripts(geometry.scripts);
auto hfmModel = getSkeletonModel()->getHFMModel();
qApp->loadAvatarScripts(hfmModel.scripts);
_shouldLoadScripts = false;
}
// Load and convert old attachments to avatar entities
@ -2429,10 +2429,10 @@ void MyAvatar::attachmentDataToEntityProperties(const AttachmentData& data, Enti
void MyAvatar::initHeadBones() {
int neckJointIndex = -1;
if (_skeletonModel->isLoaded()) {
neckJointIndex = _skeletonModel->getFBXGeometry().neckJointIndex;
neckJointIndex = _skeletonModel->getHFMModel().neckJointIndex;
}
if (neckJointIndex == -1) {
neckJointIndex = (_skeletonModel->getFBXGeometry().headJointIndex - 1);
neckJointIndex = (_skeletonModel->getHFMModel().headJointIndex - 1);
if (neckJointIndex < 0) {
// return if the head is not even there. can't cauterize!!
return;
@ -2443,7 +2443,7 @@ void MyAvatar::initHeadBones() {
q.push(neckJointIndex);
_headBoneSet.insert(neckJointIndex);
// fbxJoints only hold links to parents not children, so we have to do a bit of extra work here.
// hfmJoints only hold links to parents not children, so we have to do a bit of extra work here.
while (q.size() > 0) {
int jointIndex = q.front();
for (int i = 0; i < _skeletonModel->getJointStateCount(); i++) {
@ -2592,11 +2592,11 @@ void MyAvatar::postUpdate(float deltaTime, const render::ScenePointer& scene) {
if (_skeletonModel && _skeletonModel->isLoaded()) {
const Rig& rig = _skeletonModel->getRig();
const FBXGeometry& geometry = _skeletonModel->getFBXGeometry();
const HFMModel& hfmModel = _skeletonModel->getHFMModel();
for (int i = 0; i < rig.getJointStateCount(); i++) {
AnimPose jointPose;
rig.getAbsoluteJointPoseInRigFrame(i, jointPose);
const FBXJointShapeInfo& shapeInfo = geometry.joints[i].shapeInfo;
const HFMJointShapeInfo& shapeInfo = hfmModel.joints[i].shapeInfo;
const AnimPose pose = rigToWorldPose * jointPose;
for (size_t j = 0; j < shapeInfo.debugLines.size() / 2; j++) {
glm::vec3 pointA = pose.xformPoint(shapeInfo.debugLines[2 * j]);
@ -4012,7 +4012,7 @@ float MyAvatar::getSitStandStateChange() const {
}
QVector<QString> MyAvatar::getScriptUrls() {
QVector<QString> scripts = _skeletonModel->isLoaded() ? _skeletonModel->getFBXGeometry().scripts : QVector<QString>();
QVector<QString> scripts = _skeletonModel->isLoaded() ? _skeletonModel->getHFMModel().scripts : QVector<QString>();
return scripts;
}

View file

@ -90,7 +90,7 @@ static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) {
// Called within Model::simulate call, below.
void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
const FBXGeometry& geometry = getFBXGeometry();
const HFMModel& hfmModel = getHFMModel();
Head* head = _owningAvatar->getHead();
@ -268,19 +268,19 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
// pass detailed torso k-dops to rig.
int hipsJoint = _rig.indexOfJoint("Hips");
if (hipsJoint >= 0) {
params.hipsShapeInfo = geometry.joints[hipsJoint].shapeInfo;
params.hipsShapeInfo = hfmModel.joints[hipsJoint].shapeInfo;
}
int spineJoint = _rig.indexOfJoint("Spine");
if (spineJoint >= 0) {
params.spineShapeInfo = geometry.joints[spineJoint].shapeInfo;
params.spineShapeInfo = hfmModel.joints[spineJoint].shapeInfo;
}
int spine1Joint = _rig.indexOfJoint("Spine1");
if (spine1Joint >= 0) {
params.spine1ShapeInfo = geometry.joints[spine1Joint].shapeInfo;
params.spine1ShapeInfo = hfmModel.joints[spine1Joint].shapeInfo;
}
int spine2Joint = _rig.indexOfJoint("Spine2");
if (spine2Joint >= 0) {
params.spine2ShapeInfo = geometry.joints[spine2Joint].shapeInfo;
params.spine2ShapeInfo = hfmModel.joints[spine2Joint].shapeInfo;
}
_rig.updateFromControllerParameters(params, deltaTime);
@ -300,8 +300,8 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
eyeParams.eyeSaccade = head->getSaccade();
eyeParams.modelRotation = getRotation();
eyeParams.modelTranslation = getTranslation();
eyeParams.leftEyeJointIndex = geometry.leftEyeJointIndex;
eyeParams.rightEyeJointIndex = geometry.rightEyeJointIndex;
eyeParams.leftEyeJointIndex = hfmModel.leftEyeJointIndex;
eyeParams.rightEyeJointIndex = hfmModel.rightEyeJointIndex;
_rig.updateFromEyeParameters(eyeParams);

View file

@ -219,7 +219,11 @@ QString transactionString(const QJsonObject& valueObject) {
if (!message.isEmpty()) {
result += QString("<br>with memo: <i>\"%1\"</i>").arg(message);
}
} else if (sentMoney <= 0 && receivedMoney <= 0 && (sentCerts > 0 || receivedCerts > 0) && !KNOWN_USERS.contains(valueObject["sender_name"].toString())) {
} else if (sentMoney <= 0 && receivedMoney <= 0 &&
(sentCerts > 0 || receivedCerts > 0) &&
!KNOWN_USERS.contains(valueObject["sender_name"].toString()) &&
!KNOWN_USERS.contains(valueObject["recipient_name"].toString())
) {
// this is a non-HFC asset transfer.
if (sentCerts > 0) {
QString recipient = userLink(valueObject["recipient_name"].toString(), valueObject["place_name"].toString());
@ -240,7 +244,6 @@ QString transactionString(const QJsonObject& valueObject) {
return result;
}
static const QString MARKETPLACE_ITEMS_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/marketplace/items/";
void Ledger::historySuccess(QNetworkReply* reply) {
// here we send a historyResult with some extra stuff in it
// Namely, the styled text we'd like to show. The issue is the

View file

@ -687,7 +687,7 @@ void Wallet::chooseSecurityImage(const QString& filename) {
delete _securityImage;
}
QString path = PathUtils::resourcesPath();
path.append("/qml/hifi/commerce/wallet/");
path.append("/qml/hifi/dialogs/security/");
path.append(filename);
// now create a new security image pixmap

View file

@ -131,7 +131,7 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha
// should never fall in here when collision model not fully loaded
// TODO: assert that all geometries exist and are loaded
//assert(_model && _model->isLoaded() && _compoundShapeResource && _compoundShapeResource->isLoaded());
const FBXGeometry& collisionGeometry = resource->getFBXGeometry();
const HFMModel& collisionModel = resource->getHFMModel();
ShapeInfo::PointCollection& pointCollection = shapeInfo.getPointCollection();
pointCollection.clear();
@ -139,15 +139,15 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha
// the way OBJ files get read, each section under a "g" line is its own meshPart. We only expect
// to find one actual "mesh" (with one or more meshParts in it), but we loop over the meshes, just in case.
foreach (const FBXMesh& mesh, collisionGeometry.meshes) {
foreach (const HFMMesh& mesh, collisionModel.meshes) {
// each meshPart is a convex hull
foreach (const FBXMeshPart &meshPart, mesh.parts) {
foreach (const HFMMeshPart &meshPart, mesh.parts) {
pointCollection.push_back(QVector<glm::vec3>());
ShapeInfo::PointList& pointsInPart = pointCollection[i];
// run through all the triangles and (uniquely) add each point to the hull
uint32_t numIndices = (uint32_t)meshPart.triangleIndices.size();
// TODO: assert rather than workaround after we start sanitizing FBXMesh higher up
// TODO: assert rather than workaround after we start sanitizing HFMMesh higher up
//assert(numIndices % TRIANGLE_STRIDE == 0);
numIndices -= numIndices % TRIANGLE_STRIDE; // WORKAROUND lack of sanity checking in FBXReader
@ -168,7 +168,7 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha
// run through all the quads and (uniquely) add each point to the hull
numIndices = (uint32_t)meshPart.quadIndices.size();
// TODO: assert rather than workaround after we start sanitizing FBXMesh higher up
// TODO: assert rather than workaround after we start sanitizing HFMMesh higher up
//assert(numIndices % QUAD_STRIDE == 0);
numIndices -= numIndices % QUAD_STRIDE; // WORKAROUND lack of sanity checking in FBXReader
@ -206,7 +206,7 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha
// to the visual model and apply them to the collision model (without regard for the
// collision model's extents).
glm::vec3 scaleToFit = dimensions / resource->getFBXGeometry().getUnscaledMeshExtents().size();
glm::vec3 scaleToFit = dimensions / resource->getHFMModel().getUnscaledMeshExtents().size();
// multiply each point by scale
for (int32_t i = 0; i < pointCollection.size(); i++) {
for (int32_t j = 0; j < pointCollection[i].size(); j++) {
@ -216,11 +216,11 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha
}
shapeInfo.setParams(type, dimensions, resource->getURL().toString());
} else if (type >= SHAPE_TYPE_SIMPLE_HULL && type <= SHAPE_TYPE_STATIC_MESH) {
const FBXGeometry& fbxGeometry = resource->getFBXGeometry();
int numFbxMeshes = fbxGeometry.meshes.size();
const HFMModel& hfmModel = resource->getHFMModel();
int numHFMMeshes = hfmModel.meshes.size();
int totalNumVertices = 0;
for (int i = 0; i < numFbxMeshes; i++) {
const FBXMesh& mesh = fbxGeometry.meshes.at(i);
for (int i = 0; i < numHFMMeshes; i++) {
const HFMMesh& mesh = hfmModel.meshes.at(i);
totalNumVertices += mesh.vertices.size();
}
const int32_t MAX_VERTICES_PER_STATIC_MESH = 1e6;
@ -230,7 +230,7 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha
return;
}
auto& meshes = resource->getFBXGeometry().meshes;
auto& meshes = resource->getHFMModel().meshes;
int32_t numMeshes = (int32_t)(meshes.size());
const int MAX_ALLOWED_MESH_COUNT = 1000;
@ -285,12 +285,12 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha
if (type == SHAPE_TYPE_STATIC_MESH) {
// copy into triangleIndices
size_t triangleIndicesCount = 0;
for (const FBXMeshPart& meshPart : mesh.parts) {
for (const HFMMeshPart& meshPart : mesh.parts) {
triangleIndicesCount += meshPart.triangleIndices.count();
}
triangleIndices.reserve((int)triangleIndicesCount);
for (const FBXMeshPart& meshPart : mesh.parts) {
for (const HFMMeshPart& meshPart : mesh.parts) {
const int* indexItr = meshPart.triangleIndices.cbegin();
while (indexItr != meshPart.triangleIndices.cend()) {
triangleIndices.push_back(*indexItr);
@ -299,11 +299,11 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha
}
} else if (type == SHAPE_TYPE_SIMPLE_COMPOUND) {
// for each mesh copy unique part indices, separated by special bogus (flag) index values
for (const FBXMeshPart& meshPart : mesh.parts) {
for (const HFMMeshPart& meshPart : mesh.parts) {
// collect unique list of indices for this part
std::set<int32_t> uniqueIndices;
auto numIndices = meshPart.triangleIndices.count();
// TODO: assert rather than workaround after we start sanitizing FBXMesh higher up
// TODO: assert rather than workaround after we start sanitizing HFMMesh higher up
//assert(numIndices% TRIANGLE_STRIDE == 0);
numIndices -= numIndices % TRIANGLE_STRIDE; // WORKAROUND lack of sanity checking in FBXReader

View file

@ -10,13 +10,15 @@
//
#include "WalletScriptingInterface.h"
#include <SettingHandle.h>
CheckoutProxy::CheckoutProxy(QObject* qmlObject, QObject* parent) : QmlWrapper(qmlObject, parent) {
Q_ASSERT(QThread::currentThread() == qApp->thread());
}
WalletScriptingInterface::WalletScriptingInterface() {
connect(DependencyManager::get<AccountManager>().data(),
&AccountManager::limitedCommerceChanged, this, &WalletScriptingInterface::limitedCommerceChanged);
}
void WalletScriptingInterface::refreshWalletStatus() {

View file

@ -15,13 +15,13 @@
#include <QtCore/QObject>
#include <DependencyManager.h>
#include "scripting/HMDScriptingInterface.h"
#include <ui/TabletScriptingInterface.h>
#include <ui/QmlWrapper.h>
#include <OffscreenUi.h>
#include "Application.h"
#include "commerce/Wallet.h"
#include "ui/overlays/ContextOverlayInterface.h"
#include <AccountManager.h>
class CheckoutProxy : public QmlWrapper {
Q_OBJECT
@ -29,7 +29,6 @@ public:
CheckoutProxy(QObject* qmlObject, QObject* parent = nullptr);
};
/**jsdoc
* @namespace Wallet
*
@ -37,28 +36,31 @@ public:
* @hifi-client-entity
*
* @property {number} walletStatus
* @property {bool} limitedCommerce
*/
class WalletScriptingInterface : public QObject, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
Q_PROPERTY(uint walletStatus READ getWalletStatus WRITE setWalletStatus NOTIFY walletStatusChanged)
Q_PROPERTY(bool limitedCommerce READ getLimitedCommerce WRITE setLimitedCommerce NOTIFY limitedCommerceChanged)
public:
WalletScriptingInterface();
/**jsdoc
* @function Wallet.refreshWalletStatus
* @function WalletScriptingInterface.refreshWalletStatus
*/
Q_INVOKABLE void refreshWalletStatus();
/**jsdoc
* @function Wallet.getWalletStatus
* @function WalletScriptingInterface.getWalletStatus
* @returns {number}
*/
Q_INVOKABLE uint getWalletStatus() { return _walletStatus; }
/**jsdoc
* @function Wallet.proveAvatarEntityOwnershipVerification
* @function WalletScriptingInterface.proveAvatarEntityOwnershipVerification
* @param {Uuid} entityID
*/
Q_INVOKABLE void proveAvatarEntityOwnershipVerification(const QUuid& entityID);
@ -67,29 +69,38 @@ public:
// scripts could cause the Wallet to incorrectly report its status.
void setWalletStatus(const uint& status);
bool getLimitedCommerce() { return DependencyManager::get<AccountManager>()->getLimitedCommerce(); }
void setLimitedCommerce(bool isLimited) { DependencyManager::get<AccountManager>()->setLimitedCommerce(isLimited); };
signals:
/**jsdoc
* @function Wallet.walletStatusChanged
* @function WalletScriptingInterface.walletStatusChanged
* @returns {Signal}
*/
void walletStatusChanged();
/**jsdoc
* @function Wallet.walletNotSetup
* @function WalletScriptingInterface.limitedCommerceChanged
* @returns {Signal}
*/
void limitedCommerceChanged();
/**jsdoc
* @function WalletScriptingInterface.walletNotSetup
* @returns {Signal}
*/
void walletNotSetup();
/**jsdoc
* @function Wallet.ownershipVerificationSuccess
* @function WalletScriptingInterface.ownershipVerificationSuccess
* @param {Uuid} entityID
* @returns {Signal}
*/
void ownershipVerificationSuccess(const QUuid& entityID);
/**jsdoc
* @function Wallet.ownershipVerificationFailed
* @function WalletScriptingInterface.ownershipVerificationFailed
* @param {Uuid} entityID
* @returns {Signal}
*/

View file

@ -223,12 +223,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID&
bool ContextOverlayInterface::contextOverlayFilterPassed(const EntityItemID& entityItemID) {
EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags);
Setting::Handle<bool> _settingSwitch{ "commerce", true };
if (_settingSwitch.get()) {
return (entityProperties.getCertificateID().length() != 0);
} else {
return (entityProperties.getMarketplaceID().length() != 0);
}
}
bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) {

View file

@ -446,7 +446,7 @@ QVariant ModelOverlay::getProperty(const QString& property) {
if (property == "jointNames") {
if (_model && _model->isActive()) {
// note: going through Rig because Model::getJointNames() (which proxies to FBXGeometry) was always empty
// note: going through Rig because Model::getJointNames() (which proxies to HFMModel) was always empty
const Rig* rig = &(_model->getRig());
return mapJoints<QStringList, QString>([rig](int jointIndex) -> QString {
return rig->nameOfJoint(jointIndex);
@ -574,7 +574,7 @@ void ModelOverlay::animate() {
QVector<JointData> jointsData;
const QVector<FBXAnimationFrame>& frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy
const QVector<HFMAnimationFrame>& frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy
int frameCount = frames.size();
if (frameCount <= 0) {
return;
@ -605,11 +605,11 @@ void ModelOverlay::animate() {
return;
}
QStringList animationJointNames = _animation->getGeometry().getJointNames();
auto& fbxJoints = _animation->getGeometry().joints;
QStringList animationJointNames = _animation->getHFMModel().getJointNames();
auto& hfmJoints = _animation->getHFMModel().joints;
auto& originalFbxJoints = _model->getFBXGeometry().joints;
auto& originalFbxIndices = _model->getFBXGeometry().jointIndices;
auto& originalHFMJoints = _model->getHFMModel().joints;
auto& originalHFMIndices = _model->getHFMModel().jointIndices;
const QVector<glm::quat>& rotations = frames[_lastKnownCurrentFrame].rotations;
const QVector<glm::vec3>& translations = frames[_lastKnownCurrentFrame].translations;
@ -626,23 +626,23 @@ void ModelOverlay::animate() {
translationMat = glm::translate(translations[index]);
}
} else if (index < animationJointNames.size()) {
QString jointName = fbxJoints[index].name;
QString jointName = hfmJoints[index].name;
if (originalFbxIndices.contains(jointName)) {
if (originalHFMIndices.contains(jointName)) {
// Making sure the joint names exist in the original model the animation is trying to apply onto. If they do, then remap and get its translation.
int remappedIndex = originalFbxIndices[jointName] - 1; // JointIndeces seem to always start from 1 and the found index is always 1 higher than actual.
translationMat = glm::translate(originalFbxJoints[remappedIndex].translation);
int remappedIndex = originalHFMIndices[jointName] - 1; // JointIndeces seem to always start from 1 and the found index is always 1 higher than actual.
translationMat = glm::translate(originalHFMJoints[remappedIndex].translation);
}
}
glm::mat4 rotationMat;
if (index < rotations.size()) {
rotationMat = glm::mat4_cast(fbxJoints[index].preRotation * rotations[index] * fbxJoints[index].postRotation);
rotationMat = glm::mat4_cast(hfmJoints[index].preRotation * rotations[index] * hfmJoints[index].postRotation);
} else {
rotationMat = glm::mat4_cast(fbxJoints[index].preRotation * fbxJoints[index].postRotation);
rotationMat = glm::mat4_cast(hfmJoints[index].preRotation * hfmJoints[index].postRotation);
}
glm::mat4 finalMat = (translationMat * fbxJoints[index].preTransform *
rotationMat * fbxJoints[index].postTransform);
glm::mat4 finalMat = (translationMat * hfmJoints[index].preTransform *
rotationMat * hfmJoints[index].postTransform);
auto& jointData = jointsData[j];
jointData.translation = extractTranslation(finalMat);
jointData.translationIsDefaultPose = false;

View file

@ -53,6 +53,7 @@
#include "ui/AvatarInputs.h"
#include "avatar/AvatarManager.h"
#include "scripting/AccountServicesScriptingInterface.h"
#include "scripting/WalletScriptingInterface.h"
#include <plugins/InputConfiguration.h>
#include "ui/Snapshot.h"
#include "SoundCacheScriptingInterface.h"
@ -270,6 +271,7 @@ void Web3DOverlay::setupQmlSurface(bool isTablet) {
_webSurface->getSurfaceContext()->setContextProperty("Window", DependencyManager::get<WindowScriptingInterface>().data());
_webSurface->getSurfaceContext()->setContextProperty("Reticle", qApp->getApplicationCompositor().getReticleInterface());
_webSurface->getSurfaceContext()->setContextProperty("HiFiAbout", AboutUtil::getInstance());
_webSurface->getSurfaceContext()->setContextProperty("WalletScriptingInterface", DependencyManager::get<WalletScriptingInterface>().data());
_webSurface->getSurfaceContext()->setContextProperty("ResourceRequestObserver", DependencyManager::get<ResourceRequestObserver>().data());
// Override min fps for tablet UI, for silky smooth scrolling

View file

@ -101,8 +101,8 @@ void AnimClip::copyFromNetworkAnim() {
// build a mapping from animation joint indices to skeleton joint indices.
// by matching joints with the same name.
const FBXGeometry& geom = _networkAnim->getGeometry();
AnimSkeleton animSkeleton(geom);
const HFMModel& hfmModel = _networkAnim->getHFMModel();
AnimSkeleton animSkeleton(hfmModel);
const auto animJointCount = animSkeleton.getNumJoints();
const auto skeletonJointCount = _skeleton->getNumJoints();
std::vector<int> jointMap;
@ -115,12 +115,12 @@ void AnimClip::copyFromNetworkAnim() {
jointMap.push_back(skeletonJoint);
}
const int frameCount = geom.animationFrames.size();
const int frameCount = hfmModel.animationFrames.size();
_anim.resize(frameCount);
for (int frame = 0; frame < frameCount; frame++) {
const FBXAnimationFrame& fbxAnimFrame = geom.animationFrames[frame];
const HFMAnimationFrame& hfmAnimFrame = hfmModel.animationFrames[frame];
// init all joints in animation to default pose
// this will give us a resonable result for bones in the model skeleton but not in the animation.
@ -132,8 +132,8 @@ void AnimClip::copyFromNetworkAnim() {
for (int animJoint = 0; animJoint < animJointCount; animJoint++) {
int skeletonJoint = jointMap[animJoint];
const glm::vec3& fbxAnimTrans = fbxAnimFrame.translations[animJoint];
const glm::quat& fbxAnimRot = fbxAnimFrame.rotations[animJoint];
const glm::vec3& hfmAnimTrans = hfmAnimFrame.translations[animJoint];
const glm::quat& hfmAnimRot = hfmAnimFrame.rotations[animJoint];
// skip joints that are in the animation but not in the skeleton.
if (skeletonJoint >= 0 && skeletonJoint < skeletonJointCount) {
@ -146,19 +146,19 @@ void AnimClip::copyFromNetworkAnim() {
preRot.scale() = glm::vec3(1.0f);
postRot.scale() = glm::vec3(1.0f);
AnimPose rot(glm::vec3(1.0f), fbxAnimRot, glm::vec3());
AnimPose rot(glm::vec3(1.0f), hfmAnimRot, glm::vec3());
// adjust translation offsets, so large translation animatons on the reference skeleton
// will be adjusted when played on a skeleton with short limbs.
const glm::vec3& fbxZeroTrans = geom.animationFrames[0].translations[animJoint];
const glm::vec3& hfmZeroTrans = hfmModel.animationFrames[0].translations[animJoint];
const AnimPose& relDefaultPose = _skeleton->getRelativeDefaultPose(skeletonJoint);
float boneLengthScale = 1.0f;
const float EPSILON = 0.0001f;
if (fabsf(glm::length(fbxZeroTrans)) > EPSILON) {
boneLengthScale = glm::length(relDefaultPose.trans()) / glm::length(fbxZeroTrans);
if (fabsf(glm::length(hfmZeroTrans)) > EPSILON) {
boneLengthScale = glm::length(relDefaultPose.trans()) / glm::length(hfmZeroTrans);
}
AnimPose trans = AnimPose(glm::vec3(1.0f), glm::quat(), relDefaultPose.trans() + boneLengthScale * (fbxAnimTrans - fbxZeroTrans));
AnimPose trans = AnimPose(glm::vec3(1.0f), glm::quat(), relDefaultPose.trans() + boneLengthScale * (hfmAnimTrans - hfmZeroTrans));
_anim[frame][skeletonJoint] = trans * preRot * rot * postRot;
}

View file

@ -16,17 +16,17 @@
#include "AnimationLogging.h"
AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry) {
AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) {
// convert to std::vector of joints
std::vector<FBXJoint> joints;
joints.reserve(fbxGeometry.joints.size());
for (auto& joint : fbxGeometry.joints) {
std::vector<HFMJoint> joints;
joints.reserve(hfmModel.joints.size());
for (auto& joint : hfmModel.joints) {
joints.push_back(joint);
}
buildSkeletonFromJoints(joints);
}
AnimSkeleton::AnimSkeleton(const std::vector<FBXJoint>& joints) {
AnimSkeleton::AnimSkeleton(const std::vector<HFMJoint>& joints) {
buildSkeletonFromJoints(joints);
}
@ -166,7 +166,7 @@ void AnimSkeleton::mirrorAbsolutePoses(AnimPoseVec& poses) const {
}
}
void AnimSkeleton::buildSkeletonFromJoints(const std::vector<FBXJoint>& joints) {
void AnimSkeleton::buildSkeletonFromJoints(const std::vector<HFMJoint>& joints) {
_joints = joints;
_jointsSize = (int)joints.size();
// build a cache of bind poses
@ -177,7 +177,7 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector<FBXJoint>& joints)
_relativePreRotationPoses.reserve(_jointsSize);
_relativePostRotationPoses.reserve(_jointsSize);
// iterate over FBXJoints and extract the bind pose information.
// iterate over HFMJoints and extract the bind pose information.
for (int i = 0; i < _jointsSize; i++) {
// build pre and post transforms
@ -240,7 +240,7 @@ void AnimSkeleton::dump(bool verbose) const {
qCDebug(animation) << " absDefaultPose =" << getAbsoluteDefaultPose(i);
qCDebug(animation) << " relDefaultPose =" << getRelativeDefaultPose(i);
if (verbose) {
qCDebug(animation) << " fbxJoint =";
qCDebug(animation) << " hfmJoint =";
qCDebug(animation) << " isFree =" << _joints[i].isFree;
qCDebug(animation) << " freeLineage =" << _joints[i].freeLineage;
qCDebug(animation) << " parentIndex =" << _joints[i].parentIndex;

View file

@ -23,8 +23,8 @@ public:
using Pointer = std::shared_ptr<AnimSkeleton>;
using ConstPointer = std::shared_ptr<const AnimSkeleton>;
explicit AnimSkeleton(const FBXGeometry& fbxGeometry);
explicit AnimSkeleton(const std::vector<FBXJoint>& joints);
explicit AnimSkeleton(const HFMModel& hfmModel);
explicit AnimSkeleton(const std::vector<HFMJoint>& joints);
int nameToJointIndex(const QString& jointName) const;
const QString& getJointName(int jointIndex) const;
int getNumJoints() const;
@ -64,9 +64,9 @@ public:
std::vector<int> lookUpJointIndices(const std::vector<QString>& jointNames) const;
protected:
void buildSkeletonFromJoints(const std::vector<FBXJoint>& joints);
void buildSkeletonFromJoints(const std::vector<HFMJoint>& joints);
std::vector<FBXJoint> _joints;
std::vector<HFMJoint> _joints;
int _jointsSize { 0 };
AnimPoseVec _relativeDefaultPoses;
AnimPoseVec _absoluteDefaultPoses;

View file

@ -69,14 +69,14 @@ void AnimationReader::run() {
if (urlValid) {
// Parse the FBX directly from the QNetworkReply
FBXGeometry::Pointer fbxgeo;
HFMModel::Pointer hfmModel;
if (_url.path().toLower().endsWith(".fbx")) {
fbxgeo.reset(readFBX(_data, QVariantHash(), _url.path()));
hfmModel.reset(readFBX(_data, QVariantHash(), _url.path()));
} else {
QString errorStr("usupported format");
emit onError(299, errorStr);
}
emit onSuccess(fbxgeo);
emit onSuccess(hfmModel);
} else {
throw QString("url is invalid");
}
@ -88,7 +88,7 @@ void AnimationReader::run() {
}
bool Animation::isLoaded() const {
return _loaded && _geometry;
return _loaded && _hfmModel;
}
QStringList Animation::getJointNames() const {
@ -99,45 +99,45 @@ QStringList Animation::getJointNames() const {
return result;
}
QStringList names;
if (_geometry) {
foreach (const FBXJoint& joint, _geometry->joints) {
if (_hfmModel) {
foreach (const HFMJoint& joint, _hfmModel->joints) {
names.append(joint.name);
}
}
return names;
}
QVector<FBXAnimationFrame> Animation::getFrames() const {
QVector<HFMAnimationFrame> Animation::getFrames() const {
if (QThread::currentThread() != thread()) {
QVector<FBXAnimationFrame> result;
QVector<HFMAnimationFrame> result;
BLOCKING_INVOKE_METHOD(const_cast<Animation*>(this), "getFrames",
Q_RETURN_ARG(QVector<FBXAnimationFrame>, result));
Q_RETURN_ARG(QVector<HFMAnimationFrame>, result));
return result;
}
if (_geometry) {
return _geometry->animationFrames;
if (_hfmModel) {
return _hfmModel->animationFrames;
} else {
return QVector<FBXAnimationFrame>();
return QVector<HFMAnimationFrame>();
}
}
const QVector<FBXAnimationFrame>& Animation::getFramesReference() const {
return _geometry->animationFrames;
const QVector<HFMAnimationFrame>& Animation::getFramesReference() const {
return _hfmModel->animationFrames;
}
void Animation::downloadFinished(const QByteArray& data) {
// parse the animation/fbx file on a background thread.
AnimationReader* animationReader = new AnimationReader(_url, data);
connect(animationReader, SIGNAL(onSuccess(FBXGeometry::Pointer)), SLOT(animationParseSuccess(FBXGeometry::Pointer)));
connect(animationReader, SIGNAL(onSuccess(HFMModel::Pointer)), SLOT(animationParseSuccess(HFMModel::Pointer)));
connect(animationReader, SIGNAL(onError(int, QString)), SLOT(animationParseError(int, QString)));
QThreadPool::globalInstance()->start(animationReader);
}
void Animation::animationParseSuccess(FBXGeometry::Pointer geometry) {
void Animation::animationParseSuccess(HFMModel::Pointer hfmModel) {
qCDebug(animation) << "Animation parse success" << _url.toDisplayString();
_geometry = geometry;
_hfmModel = hfmModel;
finishedLoading(true);
}

View file

@ -66,7 +66,7 @@ public:
QString getType() const override { return "Animation"; }
const FBXGeometry& getGeometry() const { return *_geometry; }
const HFMModel& getHFMModel() const { return *_hfmModel; }
virtual bool isLoaded() const override;
@ -80,20 +80,20 @@ public:
* @function AnimationObject.getFrames
* @returns {FBXAnimationFrame[]}
*/
Q_INVOKABLE QVector<FBXAnimationFrame> getFrames() const;
Q_INVOKABLE QVector<HFMAnimationFrame> getFrames() const;
const QVector<FBXAnimationFrame>& getFramesReference() const;
const QVector<HFMAnimationFrame>& getFramesReference() const;
protected:
virtual void downloadFinished(const QByteArray& data) override;
protected slots:
void animationParseSuccess(FBXGeometry::Pointer geometry);
void animationParseSuccess(HFMModel::Pointer hfmModel);
void animationParseError(int error, QString str);
private:
FBXGeometry::Pointer _geometry;
HFMModel::Pointer _hfmModel;
};
/// Reads geometry in a worker thread.
@ -105,7 +105,7 @@ public:
virtual void run() override;
signals:
void onSuccess(FBXGeometry::Pointer geometry);
void onSuccess(HFMModel::Pointer hfmModel);
void onError(int error, QString str);
private:

View file

@ -19,17 +19,17 @@ QStringList AnimationObject::getJointNames() const {
return qscriptvalue_cast<AnimationPointer>(thisObject())->getJointNames();
}
QVector<FBXAnimationFrame> AnimationObject::getFrames() const {
QVector<HFMAnimationFrame> AnimationObject::getFrames() const {
return qscriptvalue_cast<AnimationPointer>(thisObject())->getFrames();
}
QVector<glm::quat> AnimationFrameObject::getRotations() const {
return qscriptvalue_cast<FBXAnimationFrame>(thisObject()).rotations;
return qscriptvalue_cast<HFMAnimationFrame>(thisObject()).rotations;
}
void registerAnimationTypes(QScriptEngine* engine) {
qScriptRegisterSequenceMetaType<QVector<FBXAnimationFrame> >(engine);
engine->setDefaultPrototype(qMetaTypeId<FBXAnimationFrame>(), engine->newQObject(
qScriptRegisterSequenceMetaType<QVector<HFMAnimationFrame> >(engine);
engine->setDefaultPrototype(qMetaTypeId<HFMAnimationFrame>(), engine->newQObject(
new AnimationFrameObject(), QScriptEngine::ScriptOwnership));
engine->setDefaultPrototype(qMetaTypeId<AnimationPointer>(), engine->newQObject(
new AnimationObject(), QScriptEngine::ScriptOwnership));

View file

@ -23,13 +23,13 @@ class QScriptEngine;
class AnimationObject : public QObject, protected QScriptable {
Q_OBJECT
Q_PROPERTY(QStringList jointNames READ getJointNames)
Q_PROPERTY(QVector<FBXAnimationFrame> frames READ getFrames)
Q_PROPERTY(QVector<HFMAnimationFrame> frames READ getFrames)
public:
Q_INVOKABLE QStringList getJointNames() const;
Q_INVOKABLE QVector<FBXAnimationFrame> getFrames() const;
Q_INVOKABLE QVector<HFMAnimationFrame> getFrames() const;
};
/// Scriptable wrapper for animation frames.

View file

@ -260,14 +260,14 @@ void Rig::destroyAnimGraph() {
_rightEyeJointChildren.clear();
}
void Rig::initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOffset) {
_geometryOffset = AnimPose(geometry.offset);
void Rig::initJointStates(const HFMModel& hfmModel, const glm::mat4& modelOffset) {
_geometryOffset = AnimPose(hfmModel.offset);
_invGeometryOffset = _geometryOffset.inverse();
_geometryToRigTransform = modelOffset * geometry.offset;
_geometryToRigTransform = modelOffset * hfmModel.offset;
_rigToGeometryTransform = glm::inverse(_geometryToRigTransform);
setModelOffset(modelOffset);
_animSkeleton = std::make_shared<AnimSkeleton>(geometry);
_animSkeleton = std::make_shared<AnimSkeleton>(hfmModel);
_internalPoseSet._relativePoses.clear();
_internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses();
@ -293,24 +293,24 @@ void Rig::initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOff
buildAbsoluteRigPoses(_animSkeleton->getRelativeDefaultPoses(), _absoluteDefaultPoses);
_rootJointIndex = geometry.rootJointIndex;
_leftEyeJointIndex = geometry.leftEyeJointIndex;
_rightEyeJointIndex = geometry.rightEyeJointIndex;
_leftHandJointIndex = geometry.leftHandJointIndex;
_leftElbowJointIndex = _leftHandJointIndex >= 0 ? geometry.joints.at(_leftHandJointIndex).parentIndex : -1;
_leftShoulderJointIndex = _leftElbowJointIndex >= 0 ? geometry.joints.at(_leftElbowJointIndex).parentIndex : -1;
_rightHandJointIndex = geometry.rightHandJointIndex;
_rightElbowJointIndex = _rightHandJointIndex >= 0 ? geometry.joints.at(_rightHandJointIndex).parentIndex : -1;
_rightShoulderJointIndex = _rightElbowJointIndex >= 0 ? geometry.joints.at(_rightElbowJointIndex).parentIndex : -1;
_rootJointIndex = hfmModel.rootJointIndex;
_leftEyeJointIndex = hfmModel.leftEyeJointIndex;
_rightEyeJointIndex = hfmModel.rightEyeJointIndex;
_leftHandJointIndex = hfmModel.leftHandJointIndex;
_leftElbowJointIndex = _leftHandJointIndex >= 0 ? hfmModel.joints.at(_leftHandJointIndex).parentIndex : -1;
_leftShoulderJointIndex = _leftElbowJointIndex >= 0 ? hfmModel.joints.at(_leftElbowJointIndex).parentIndex : -1;
_rightHandJointIndex = hfmModel.rightHandJointIndex;
_rightElbowJointIndex = _rightHandJointIndex >= 0 ? hfmModel.joints.at(_rightHandJointIndex).parentIndex : -1;
_rightShoulderJointIndex = _rightElbowJointIndex >= 0 ? hfmModel.joints.at(_rightElbowJointIndex).parentIndex : -1;
_leftEyeJointChildren = _animSkeleton->getChildrenOfJoint(geometry.leftEyeJointIndex);
_rightEyeJointChildren = _animSkeleton->getChildrenOfJoint(geometry.rightEyeJointIndex);
_leftEyeJointChildren = _animSkeleton->getChildrenOfJoint(hfmModel.leftEyeJointIndex);
_rightEyeJointChildren = _animSkeleton->getChildrenOfJoint(hfmModel.rightEyeJointIndex);
}
void Rig::reset(const FBXGeometry& geometry) {
_geometryOffset = AnimPose(geometry.offset);
void Rig::reset(const HFMModel& hfmModel) {
_geometryOffset = AnimPose(hfmModel.offset);
_invGeometryOffset = _geometryOffset.inverse();
_animSkeleton = std::make_shared<AnimSkeleton>(geometry);
_animSkeleton = std::make_shared<AnimSkeleton>(hfmModel);
_internalPoseSet._relativePoses.clear();
_internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses();
@ -338,18 +338,18 @@ void Rig::reset(const FBXGeometry& geometry) {
buildAbsoluteRigPoses(_animSkeleton->getRelativeDefaultPoses(), _absoluteDefaultPoses);
_rootJointIndex = geometry.rootJointIndex;
_leftEyeJointIndex = geometry.leftEyeJointIndex;
_rightEyeJointIndex = geometry.rightEyeJointIndex;
_leftHandJointIndex = geometry.leftHandJointIndex;
_leftElbowJointIndex = _leftHandJointIndex >= 0 ? geometry.joints.at(_leftHandJointIndex).parentIndex : -1;
_leftShoulderJointIndex = _leftElbowJointIndex >= 0 ? geometry.joints.at(_leftElbowJointIndex).parentIndex : -1;
_rightHandJointIndex = geometry.rightHandJointIndex;
_rightElbowJointIndex = _rightHandJointIndex >= 0 ? geometry.joints.at(_rightHandJointIndex).parentIndex : -1;
_rightShoulderJointIndex = _rightElbowJointIndex >= 0 ? geometry.joints.at(_rightElbowJointIndex).parentIndex : -1;
_rootJointIndex = hfmModel.rootJointIndex;
_leftEyeJointIndex = hfmModel.leftEyeJointIndex;
_rightEyeJointIndex = hfmModel.rightEyeJointIndex;
_leftHandJointIndex = hfmModel.leftHandJointIndex;
_leftElbowJointIndex = _leftHandJointIndex >= 0 ? hfmModel.joints.at(_leftHandJointIndex).parentIndex : -1;
_leftShoulderJointIndex = _leftElbowJointIndex >= 0 ? hfmModel.joints.at(_leftElbowJointIndex).parentIndex : -1;
_rightHandJointIndex = hfmModel.rightHandJointIndex;
_rightElbowJointIndex = _rightHandJointIndex >= 0 ? hfmModel.joints.at(_rightHandJointIndex).parentIndex : -1;
_rightShoulderJointIndex = _rightElbowJointIndex >= 0 ? hfmModel.joints.at(_rightElbowJointIndex).parentIndex : -1;
_leftEyeJointChildren = _animSkeleton->getChildrenOfJoint(geometry.leftEyeJointIndex);
_rightEyeJointChildren = _animSkeleton->getChildrenOfJoint(geometry.rightEyeJointIndex);
_leftEyeJointChildren = _animSkeleton->getChildrenOfJoint(hfmModel.leftEyeJointIndex);
_rightEyeJointChildren = _animSkeleton->getChildrenOfJoint(hfmModel.rightEyeJointIndex);
if (!_animGraphURL.isEmpty()) {
_animNode.reset();
@ -1253,7 +1253,7 @@ const glm::vec3 DOP14_NORMALS[DOP14_COUNT] = {
// returns true if the given point lies inside of the k-dop, specified by shapeInfo & shapePose.
// if the given point does lie within the k-dop, it also returns the amount of displacement necessary to push that point outward
// such that it lies on the surface of the kdop.
static bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& shapePose, const FBXJointShapeInfo& shapeInfo, glm::vec3& displacementOut) {
static bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& shapePose, const HFMJointShapeInfo& shapeInfo, glm::vec3& displacementOut) {
// transform point into local space of jointShape.
glm::vec3 localPoint = shapePose.inverse().xformPoint(point);
@ -1299,8 +1299,8 @@ static bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& sh
}
}
glm::vec3 Rig::deflectHandFromTorso(const glm::vec3& handPosition, const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo,
const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo) const {
glm::vec3 Rig::deflectHandFromTorso(const glm::vec3& handPosition, const HFMJointShapeInfo& hipsShapeInfo, const HFMJointShapeInfo& spineShapeInfo,
const HFMJointShapeInfo& spine1ShapeInfo, const HFMJointShapeInfo& spine2ShapeInfo) const {
glm::vec3 position = handPosition;
glm::vec3 displacement;
int hipsJoint = indexOfJoint("Hips");
@ -1349,8 +1349,8 @@ glm::vec3 Rig::deflectHandFromTorso(const glm::vec3& handPosition, const FBXJoin
void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool hipsEstimated,
bool leftArmEnabled, bool rightArmEnabled, bool headEnabled, float dt,
const AnimPose& leftHandPose, const AnimPose& rightHandPose,
const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo,
const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo,
const HFMJointShapeInfo& hipsShapeInfo, const HFMJointShapeInfo& spineShapeInfo,
const HFMJointShapeInfo& spine1ShapeInfo, const HFMJointShapeInfo& spine2ShapeInfo,
const glm::mat4& rigToSensorMatrix, const glm::mat4& sensorToRigMatrix) {
const bool ENABLE_POLE_VECTORS = true;
@ -1938,7 +1938,7 @@ void Rig::copyJointsIntoJointData(QVector<JointData>& jointDataVec) const {
data.rotationIsDefaultPose = isEqual(data.rotation, defaultAbsRot);
// translations are in relative frame but scaled so that they are in meters,
// instead of geometry units.
// instead of model units.
glm::vec3 defaultRelTrans = _geometryOffset.scale() * _animSkeleton->getRelativeDefaultPose(i).trans();
data.translation = _geometryOffset.scale() * (!_sendNetworkNode ? _internalPoseSet._relativePoses[i].trans() : _networkPoseSet._relativePoses[i].trans());
data.translationIsDefaultPose = isEqual(data.translation, defaultRelTrans);
@ -1963,7 +1963,7 @@ void Rig::copyJointsFromJointData(const QVector<JointData>& jointDataVec) {
return;
}
// make a vector of rotations in absolute-geometry-frame
// make a vector of rotations in absolute-model-frame
std::vector<glm::quat> rotations;
rotations.reserve(numJoints);
const glm::quat rigToGeometryRot(glmExtractRotation(_rigToGeometryTransform));
@ -1972,7 +1972,7 @@ void Rig::copyJointsFromJointData(const QVector<JointData>& jointDataVec) {
if (data.rotationIsDefaultPose) {
rotations.push_back(absoluteDefaultPoses[i].rot());
} else {
// JointData rotations are in absolute rig-frame so we rotate them to absolute geometry-frame
// JointData rotations are in absolute rig-frame so we rotate them to absolute model-frame
rotations.push_back(rigToGeometryRot * data.rotation);
}
}
@ -2008,7 +2008,7 @@ void Rig::computeExternalPoses(const glm::mat4& modelOffsetMat) {
}
void Rig::computeAvatarBoundingCapsule(
const FBXGeometry& geometry,
const HFMModel& hfmModel,
float& radiusOut,
float& heightOut,
glm::vec3& localOffsetOut) const {
@ -2041,7 +2041,7 @@ void Rig::computeAvatarBoundingCapsule(
// from the head to the hips when computing the rest of the bounding capsule.
int index = indexOfJoint("Head");
while (index != -1) {
const FBXJointShapeInfo& shapeInfo = geometry.joints.at(index).shapeInfo;
const HFMJointShapeInfo& shapeInfo = hfmModel.joints.at(index).shapeInfo;
AnimPose pose = _animSkeleton->getAbsoluteDefaultPose(index);
if (shapeInfo.points.size() > 0) {
for (auto& point : shapeInfo.points) {

View file

@ -86,10 +86,10 @@ public:
AnimPose secondaryControllerPoses[NumSecondaryControllerTypes]; // rig space
uint8_t secondaryControllerFlags[NumSecondaryControllerTypes];
bool isTalking;
FBXJointShapeInfo hipsShapeInfo;
FBXJointShapeInfo spineShapeInfo;
FBXJointShapeInfo spine1ShapeInfo;
FBXJointShapeInfo spine2ShapeInfo;
HFMJointShapeInfo hipsShapeInfo;
HFMJointShapeInfo spineShapeInfo;
HFMJointShapeInfo spine1ShapeInfo;
HFMJointShapeInfo spine2ShapeInfo;
};
struct EyeParameters {
@ -122,8 +122,8 @@ public:
void overrideRoleAnimation(const QString& role, const QString& url, float fps, bool loop, float firstFrame, float lastFrame);
void restoreRoleAnimation(const QString& role);
void initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOffset);
void reset(const FBXGeometry& geometry);
void initJointStates(const HFMModel& hfmModel, const glm::mat4& modelOffset);
void reset(const HFMModel& hfmModel);
bool jointStatesEmpty();
int getJointStateCount() const;
int indexOfJoint(const QString& jointName) const;
@ -210,7 +210,7 @@ public:
void copyJointsFromJointData(const QVector<JointData>& jointDataVec);
void computeExternalPoses(const glm::mat4& modelOffsetMat);
void computeAvatarBoundingCapsule(const FBXGeometry& geometry, float& radiusOut, float& heightOut, glm::vec3& offsetOut) const;
void computeAvatarBoundingCapsule(const HFMModel& hfmModel, float& radiusOut, float& heightOut, glm::vec3& offsetOut) const;
void setEnableInverseKinematics(bool enable);
void setEnableAnimations(bool enable);
@ -245,8 +245,8 @@ protected:
void updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool hipsEstimated,
bool leftArmEnabled, bool rightArmEnabled, bool headEnabled, float dt,
const AnimPose& leftHandPose, const AnimPose& rightHandPose,
const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo,
const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo,
const HFMJointShapeInfo& hipsShapeInfo, const HFMJointShapeInfo& spineShapeInfo,
const HFMJointShapeInfo& spine1ShapeInfo, const HFMJointShapeInfo& spine2ShapeInfo,
const glm::mat4& rigToSensorMatrix, const glm::mat4& sensorToRigMatrix);
void updateFeet(bool leftFootEnabled, bool rightFootEnabled, bool headEnabled,
const AnimPose& leftFootPose, const AnimPose& rightFootPose,
@ -257,8 +257,8 @@ protected:
bool calculateElbowPoleVector(int handIndex, int elbowIndex, int armIndex, int oppositeArmIndex, glm::vec3& poleVector) const;
glm::vec3 calculateKneePoleVector(int footJointIndex, int kneeJoint, int upLegIndex, int hipsIndex, const AnimPose& targetFootPose) const;
glm::vec3 deflectHandFromTorso(const glm::vec3& handPosition, const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo,
const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo) const;
glm::vec3 deflectHandFromTorso(const glm::vec3& handPosition, const HFMJointShapeInfo& hipsShapeInfo, const HFMJointShapeInfo& spineShapeInfo,
const HFMJointShapeInfo& spine1ShapeInfo, const HFMJointShapeInfo& spine2ShapeInfo) const;
AnimPose _modelOffset; // model to rig space

View file

@ -38,7 +38,6 @@
#include <QtCore/QBuffer>
#include <QtMultimedia/QAudioInput>
#include <QtMultimedia/QAudioOutput>
#include <PathUtils.h>
#include <ThreadHelpers.h>
#include <NodeList.h>
@ -83,7 +82,8 @@ static const int STARVE_DETECTION_PERIOD = 10 * 1000; // 10 Seconds
Setting::Handle<bool> dynamicJitterBufferEnabled("dynamicJitterBuffersEnabled",
InboundAudioStream::DEFAULT_DYNAMIC_JITTER_BUFFER_ENABLED);
Setting::Handle<int> staticJitterBufferFrames("staticJitterBufferFrames", InboundAudioStream::DEFAULT_STATIC_JITTER_FRAMES);
Setting::Handle<int> staticJitterBufferFrames("staticJitterBufferFrames",
InboundAudioStream::DEFAULT_STATIC_JITTER_FRAMES);
// protect the Qt internal device list
using Mutex = std::mutex;
@ -144,6 +144,7 @@ QList<QAudioDeviceInfo> AudioClient::getAudioDevices(QAudio::Mode mode) const {
static void channelUpmix(int16_t* source, int16_t* dest, int numSamples, int numExtraChannels) {
for (int i = 0; i < numSamples/2; i++) {
// read 2 samples
int16_t left = *source++;
int16_t right = *source++;
@ -159,6 +160,7 @@ static void channelUpmix(int16_t* source, int16_t* dest, int numSamples, int num
static void channelDownmix(int16_t* source, int16_t* dest, int numSamples) {
for (int i = 0; i < numSamples/2; i++) {
// read 2 samples
int16_t left = *source++;
int16_t right = *source++;
@ -173,24 +175,50 @@ static inline float convertToFloat(int16_t sample) {
}
AudioClient::AudioClient() :
AbstractAudioInterface(), _gate(this), _audioInput(NULL), _dummyAudioInput(NULL), _desiredInputFormat(), _inputFormat(),
_numInputCallbackBytes(0), _audioOutput(NULL), _desiredOutputFormat(), _outputFormat(), _outputFrameSize(0),
_numOutputCallbackBytes(0), _loopbackAudioOutput(NULL), _loopbackOutputDevice(NULL), _inputRingBuffer(0),
_localInjectorsStream(0, 1), _receivedAudioStream(RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES), _isStereoInput(false),
_outputStarveDetectionStartTimeMsec(0), _outputStarveDetectionCount(0),
AbstractAudioInterface(),
_gate(this),
_audioInput(NULL),
_dummyAudioInput(NULL),
_desiredInputFormat(),
_inputFormat(),
_numInputCallbackBytes(0),
_audioOutput(NULL),
_desiredOutputFormat(),
_outputFormat(),
_outputFrameSize(0),
_numOutputCallbackBytes(0),
_loopbackAudioOutput(NULL),
_loopbackOutputDevice(NULL),
_inputRingBuffer(0),
_localInjectorsStream(0, 1),
_receivedAudioStream(RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES),
_isStereoInput(false),
_outputStarveDetectionStartTimeMsec(0),
_outputStarveDetectionCount(0),
_outputBufferSizeFrames("audioOutputBufferFrames", DEFAULT_BUFFER_FRAMES),
_sessionOutputBufferSizeFrames(_outputBufferSizeFrames.get()),
_outputStarveDetectionEnabled("audioOutputStarveDetectionEnabled", DEFAULT_STARVE_DETECTION_ENABLED),
_lastInputLoudness(0.0f), _timeSinceLastClip(-1.0f), _muted(false), _shouldEchoLocally(false), _shouldEchoToServer(false),
_isNoiseGateEnabled(true), _reverb(false), _reverbOptions(&_scriptReverbOptions), _inputToNetworkResampler(NULL),
_networkToOutputResampler(NULL), _localToOutputResampler(NULL),
_audioLimiter(AudioConstants::SAMPLE_RATE, OUTPUT_CHANNEL_COUNT), _outgoingAvatarAudioSequenceNumber(0),
_audioOutputIODevice(_localInjectorsStream, _receivedAudioStream, this), _stats(&_receivedAudioStream),
_positionGetter(DEFAULT_POSITION_GETTER), _orientationGetter(DEFAULT_ORIENTATION_GETTER),
_lastInputLoudness(0.0f),
_timeSinceLastClip(-1.0f),
_muted(false),
_shouldEchoLocally(false),
_shouldEchoToServer(false),
_isNoiseGateEnabled(true),
_reverb(false),
_reverbOptions(&_scriptReverbOptions),
_inputToNetworkResampler(NULL),
_networkToOutputResampler(NULL),
_localToOutputResampler(NULL),
_audioLimiter(AudioConstants::SAMPLE_RATE, OUTPUT_CHANNEL_COUNT),
_outgoingAvatarAudioSequenceNumber(0),
_audioOutputIODevice(_localInjectorsStream, _receivedAudioStream, this),
_stats(&_receivedAudioStream),
_positionGetter(DEFAULT_POSITION_GETTER),
#if defined(Q_OS_ANDROID)
_checkInputTimer(this), _isHeadsetPluggedIn(false),
_checkInputTimer(this),
_isHeadsetPluggedIn(false),
#endif
_TTSTimer(this) {
_orientationGetter(DEFAULT_ORIENTATION_GETTER) {
// avoid putting a lock in the device callback
assert(_localSamplesAvailable.is_lock_free());
@ -198,20 +226,16 @@ AudioClient::AudioClient() :
{
Setting::Handle<int>::Deprecated("maxFramesOverDesired", InboundAudioStream::MAX_FRAMES_OVER_DESIRED);
Setting::Handle<int>::Deprecated("windowStarveThreshold", InboundAudioStream::WINDOW_STARVE_THRESHOLD);
Setting::Handle<int>::Deprecated("windowSecondsForDesiredCalcOnTooManyStarves",
InboundAudioStream::WINDOW_SECONDS_FOR_DESIRED_CALC_ON_TOO_MANY_STARVES);
Setting::Handle<int>::Deprecated("windowSecondsForDesiredReduction",
InboundAudioStream::WINDOW_SECONDS_FOR_DESIRED_REDUCTION);
Setting::Handle<int>::Deprecated("windowSecondsForDesiredCalcOnTooManyStarves", InboundAudioStream::WINDOW_SECONDS_FOR_DESIRED_CALC_ON_TOO_MANY_STARVES);
Setting::Handle<int>::Deprecated("windowSecondsForDesiredReduction", InboundAudioStream::WINDOW_SECONDS_FOR_DESIRED_REDUCTION);
Setting::Handle<bool>::Deprecated("useStDevForJitterCalc", InboundAudioStream::USE_STDEV_FOR_JITTER);
Setting::Handle<bool>::Deprecated("repetitionWithFade", InboundAudioStream::REPETITION_WITH_FADE);
}
connect(&_receivedAudioStream, &MixedProcessedAudioStream::processSamples, this, &AudioClient::processReceivedSamples,
Qt::DirectConnection);
connect(&_receivedAudioStream, &MixedProcessedAudioStream::processSamples,
this, &AudioClient::processReceivedSamples, Qt::DirectConnection);
connect(this, &AudioClient::changeDevice, this, [=](const QAudioDeviceInfo& outputDeviceInfo) {
qCDebug(audioclient)
<< "got AudioClient::changeDevice signal, about to call switchOutputToAudioDevice() outputDeviceInfo: ["
<< outputDeviceInfo.deviceName() << "]";
qCDebug(audioclient) << "got AudioClient::changeDevice signal, about to call switchOutputToAudioDevice() outputDeviceInfo: [" << outputDeviceInfo.deviceName() << "]";
switchOutputToAudioDevice(outputDeviceInfo);
});
@ -223,15 +247,24 @@ AudioClient::AudioClient() :
// start a thread to detect any device changes
_checkDevicesTimer = new QTimer(this);
connect(_checkDevicesTimer, &QTimer::timeout, this,
[this] { QtConcurrent::run(QThreadPool::globalInstance(), [this] { checkDevices(); }); });
const unsigned long DEVICE_CHECK_INTERVAL_MSECS = 2 * 1000;
connect(_checkDevicesTimer, &QTimer::timeout, this, [=] {
QtConcurrent::run(QThreadPool::globalInstance(), [=] {
checkDevices();
// On some systems (Ubuntu) checking all the audio devices can take more than 2 seconds. To
// avoid consuming all of the thread pool, don't start the check interval until the previous
// check has completed.
QMetaObject::invokeMethod(_checkDevicesTimer, "start", Q_ARG(int, DEVICE_CHECK_INTERVAL_MSECS));
});
});
_checkDevicesTimer->setSingleShot(true);
_checkDevicesTimer->start(DEVICE_CHECK_INTERVAL_MSECS);
// start a thread to detect peak value changes
_checkPeakValuesTimer = new QTimer(this);
connect(_checkPeakValuesTimer, &QTimer::timeout, this,
[this] { QtConcurrent::run(QThreadPool::globalInstance(), [this] { checkPeakValues(); }); });
connect(_checkPeakValuesTimer, &QTimer::timeout, this, [this] {
QtConcurrent::run(QThreadPool::globalInstance(), [this] { checkPeakValues(); });
});
const unsigned long PEAK_VALUES_CHECK_INTERVAL_MSECS = 50;
_checkPeakValuesTimer->start(PEAK_VALUES_CHECK_INTERVAL_MSECS);
@ -263,11 +296,11 @@ void AudioClient::customDeleter() {
}
void AudioClient::handleMismatchAudioFormat(SharedNodePointer node, const QString& currentCodec, const QString& recievedCodec) {
qCDebug(audioclient) << __FUNCTION__ << "sendingNode:" << *node << "currentCodec:" << currentCodec
<< "recievedCodec:" << recievedCodec;
qCDebug(audioclient) << __FUNCTION__ << "sendingNode:" << *node << "currentCodec:" << currentCodec << "recievedCodec:" << recievedCodec;
selectAudioFormat(recievedCodec);
}
void AudioClient::reset() {
_receivedAudioStream.reset();
_stats.reset();
@ -330,8 +363,7 @@ QString AudioClient::getWinDeviceName(wchar_t* guid) {
HRESULT hr = S_OK;
CoInitialize(nullptr);
IMMDeviceEnumerator* pMMDeviceEnumerator = nullptr;
CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator),
(void**)&pMMDeviceEnumerator);
CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pMMDeviceEnumerator);
IMMDevice* pEndpoint;
hr = pMMDeviceEnumerator->GetDevice(guid, &pEndpoint);
if (hr == E_NOTFOUND) {
@ -355,22 +387,30 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) {
if (getAvailableDevices(mode).size() > 1) {
AudioDeviceID defaultDeviceID = 0;
uint32_t propertySize = sizeof(AudioDeviceID);
AudioObjectPropertyAddress propertyAddress = { kAudioHardwarePropertyDefaultInputDevice,
kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
AudioObjectPropertyAddress propertyAddress = {
kAudioHardwarePropertyDefaultInputDevice,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
if (mode == QAudio::AudioOutput) {
propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
}
OSStatus getPropertyError =
AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &propertySize, &defaultDeviceID);
OSStatus getPropertyError = AudioObjectGetPropertyData(kAudioObjectSystemObject,
&propertyAddress,
0,
NULL,
&propertySize,
&defaultDeviceID);
if (!getPropertyError && propertySize) {
CFStringRef deviceName = NULL;
propertySize = sizeof(deviceName);
propertyAddress.mSelector = kAudioDevicePropertyDeviceNameCFString;
getPropertyError =
AudioObjectGetPropertyData(defaultDeviceID, &propertyAddress, 0, NULL, &propertySize, &deviceName);
getPropertyError = AudioObjectGetPropertyData(defaultDeviceID, &propertyAddress, 0,
NULL, &propertySize, &deviceName);
if (!getPropertyError && propertySize) {
// find a device in the list that matches the name we have and return it
@ -408,11 +448,9 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) {
HRESULT hr = S_OK;
CoInitialize(NULL);
IMMDeviceEnumerator* pMMDeviceEnumerator = NULL;
CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator),
(void**)&pMMDeviceEnumerator);
CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pMMDeviceEnumerator);
IMMDevice* pEndpoint;
hr = pMMDeviceEnumerator->GetDefaultAudioEndpoint(mode == QAudio::AudioOutput ? eRender : eCapture, eMultimedia,
&pEndpoint);
hr = pMMDeviceEnumerator->GetDefaultAudioEndpoint(mode == QAudio::AudioOutput ? eRender : eCapture, eMultimedia, &pEndpoint);
if (hr == E_NOTFOUND) {
printf("Audio Error: device not found\n");
deviceName = QString("NONE");
@ -426,8 +464,8 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) {
CoUninitialize();
}
qCDebug(audioclient) << "defaultAudioDeviceForMode mode: " << (mode == QAudio::AudioOutput ? "Output" : "Input") << " ["
<< deviceName << "] [" << getNamedAudioDeviceForMode(mode, deviceName).deviceName() << "]";
qCDebug(audioclient) << "defaultAudioDeviceForMode mode: " << (mode == QAudio::AudioOutput ? "Output" : "Input")
<< " [" << deviceName << "] [" << getNamedAudioDeviceForMode(mode, deviceName).deviceName() << "]";
return getNamedAudioDeviceForMode(mode, deviceName);
#endif
@ -455,8 +493,11 @@ bool AudioClient::getNamedAudioDeviceForModeExists(QAudio::Mode mode, const QStr
return (getNamedAudioDeviceForMode(mode, deviceName).deviceName() == deviceName);
}
// attempt to use the native sample rate and channel count
bool nativeFormatForAudioDevice(const QAudioDeviceInfo& audioDevice, QAudioFormat& audioFormat) {
bool nativeFormatForAudioDevice(const QAudioDeviceInfo& audioDevice,
QAudioFormat& audioFormat) {
audioFormat = audioDevice.preferredFormat();
audioFormat.setCodec("audio/pcm");
@ -479,6 +520,7 @@ bool nativeFormatForAudioDevice(const QAudioDeviceInfo& audioDevice, QAudioForma
bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice,
const QAudioFormat& desiredAudioFormat,
QAudioFormat& adjustedAudioFormat) {
qCDebug(audioclient) << "The desired format for audio I/O is" << desiredAudioFormat;
#if defined(Q_OS_ANDROID) || defined(Q_OS_OSX)
@ -509,6 +551,7 @@ bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice,
for (int channelCount : (desiredAudioFormat.channelCount() == 1 ? inputChannels : outputChannels)) {
for (int sampleRate : sampleRates) {
adjustedAudioFormat.setChannelCount(channelCount);
adjustedAudioFormat.setSampleRate(sampleRate);
@ -521,11 +564,8 @@ bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice,
return false; // a supported format could not be found
}
bool sampleChannelConversion(const int16_t* sourceSamples,
int16_t* destinationSamples,
unsigned int numSourceSamples,
const int sourceChannelCount,
const int destinationChannelCount) {
bool sampleChannelConversion(const int16_t* sourceSamples, int16_t* destinationSamples, unsigned int numSourceSamples,
const int sourceChannelCount, const int destinationChannelCount) {
if (sourceChannelCount == 2 && destinationChannelCount == 1) {
// loop through the stereo input audio samples and average every two samples
for (uint i = 0; i < numSourceSamples; i += 2) {
@ -534,6 +574,7 @@ bool sampleChannelConversion(const int16_t* sourceSamples,
return true;
} else if (sourceChannelCount == 1 && destinationChannelCount == 2) {
// loop through the mono input audio and repeat each sample twice
for (uint i = 0; i < numSourceSamples; ++i) {
destinationSamples[i * 2] = destinationSamples[(i * 2) + 1] = sourceSamples[i];
@ -546,31 +587,32 @@ bool sampleChannelConversion(const int16_t* sourceSamples,
}
void possibleResampling(AudioSRC* resampler,
const int16_t* sourceSamples,
int16_t* destinationSamples,
unsigned int numSourceSamples,
unsigned int numDestinationSamples,
const int sourceChannelCount,
const int destinationChannelCount) {
const int16_t* sourceSamples, int16_t* destinationSamples,
unsigned int numSourceSamples, unsigned int numDestinationSamples,
const int sourceChannelCount, const int destinationChannelCount) {
if (numSourceSamples > 0) {
if (!resampler) {
if (!sampleChannelConversion(sourceSamples, destinationSamples, numSourceSamples, sourceChannelCount,
destinationChannelCount)) {
if (!sampleChannelConversion(sourceSamples, destinationSamples, numSourceSamples,
sourceChannelCount, destinationChannelCount)) {
// no conversion, we can copy the samples directly across
memcpy(destinationSamples, sourceSamples, numSourceSamples * AudioConstants::SAMPLE_SIZE);
}
} else {
if (sourceChannelCount != destinationChannelCount) {
int numChannelCoversionSamples = (numSourceSamples * destinationChannelCount) / sourceChannelCount;
int16_t* channelConversionSamples = new int16_t[numChannelCoversionSamples];
sampleChannelConversion(sourceSamples, channelConversionSamples, numSourceSamples, sourceChannelCount,
destinationChannelCount);
sampleChannelConversion(sourceSamples, channelConversionSamples, numSourceSamples,
sourceChannelCount, destinationChannelCount);
resampler->render(channelConversionSamples, destinationSamples, numChannelCoversionSamples);
delete[] channelConversionSamples;
} else {
unsigned int numAdjustedSourceSamples = numSourceSamples;
unsigned int numAdjustedDestinationSamples = numDestinationSamples;
@ -586,6 +628,7 @@ void possibleResampling(AudioSRC* resampler,
}
void AudioClient::start() {
// set up the desired audio format
_desiredInputFormat.setSampleRate(AudioConstants::SAMPLE_RATE);
_desiredInputFormat.setSampleSize(16);
@ -674,6 +717,7 @@ void AudioClient::handleAudioDataPacket(QSharedPointer<ReceivedMessage> message)
nodeList->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::ReceiveFirstAudioPacket);
if (_audioOutput) {
if (!_hasReceivedFirstPacket) {
_hasReceivedFirstPacket = true;
@ -690,8 +734,8 @@ void AudioClient::handleAudioDataPacket(QSharedPointer<ReceivedMessage> message)
}
}
AudioClient::Gate::Gate(AudioClient* audioClient) : _audioClient(audioClient) {
}
AudioClient::Gate::Gate(AudioClient* audioClient) :
_audioClient(audioClient) {}
void AudioClient::Gate::setIsSimulatingJitter(bool enable) {
std::lock_guard<std::mutex> lock(_mutex);
@ -744,6 +788,7 @@ void AudioClient::Gate::flush() {
_index = 0;
}
void AudioClient::handleNoisyMutePacket(QSharedPointer<ReceivedMessage> message) {
if (!_muted) {
setMuted(true);
@ -789,6 +834,7 @@ void AudioClient::handleSelectedAudioFormat(QSharedPointer<ReceivedMessage> mess
}
void AudioClient::selectAudioFormat(const QString& selectedCodecName) {
_selectedCodecName = selectedCodecName;
qCDebug(audioclient) << "Selected Codec:" << _selectedCodecName << "isStereoInput:" << _isStereoInput;
@ -806,12 +852,12 @@ void AudioClient::selectAudioFormat(const QString& selectedCodecName) {
if (_selectedCodecName == plugin->getName()) {
_codec = plugin;
_receivedAudioStream.setupCodec(plugin, _selectedCodecName, AudioConstants::STEREO);
_encoder = plugin->createEncoder(AudioConstants::SAMPLE_RATE,
_isStereoInput ? AudioConstants::STEREO : AudioConstants::MONO);
_encoder = plugin->createEncoder(AudioConstants::SAMPLE_RATE, _isStereoInput ? AudioConstants::STEREO : AudioConstants::MONO);
qCDebug(audioclient) << "Selected Codec Plugin:" << _codec.get();
break;
}
}
}
bool AudioClient::switchAudioDevice(QAudio::Mode mode, const QAudioDeviceInfo& deviceInfo) {
@ -876,6 +922,7 @@ void AudioClient::configureReverb() {
void AudioClient::updateReverbOptions() {
bool reverbChanged = false;
if (_receivedAudioStream.hasReverb()) {
if (_zoneReverbOptions.getReverbTime() != _receivedAudioStream.getRevebTime()) {
_zoneReverbOptions.setReverbTime(_receivedAudioStream.getRevebTime());
reverbChanged = true;
@ -939,7 +986,7 @@ void AudioClient::setReverbOptions(const AudioEffectOptions* options) {
}
}
void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray, const int& sampleRate, const int& channelCount) {
void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) {
// If there is server echo, reverb will be applied to the recieved audio stream so no need to have it here.
bool hasReverb = _reverb || _receivedAudioStream.hasReverb();
if (_muted || !_audioOutput || (!_shouldEchoLocally && !hasReverb)) {
@ -949,7 +996,7 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray, const int
// NOTE: we assume the inputFormat and the outputFormat are the same, since on any modern
// multimedia OS they should be. If there is a device that this is not true for, we can
// add back support to do resampling.
if (sampleRate != _outputFormat.sampleRate()) {
if (_inputFormat.sampleRate() != _outputFormat.sampleRate()) {
return;
}
@ -972,7 +1019,7 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray, const int
static QByteArray loopBackByteArray;
int numInputSamples = inputByteArray.size() / AudioConstants::SAMPLE_SIZE;
int numLoopbackSamples = (numInputSamples * OUTPUT_CHANNEL_COUNT) / channelCount;
int numLoopbackSamples = (numInputSamples * OUTPUT_CHANNEL_COUNT) / _inputFormat.channelCount();
loopBackByteArray.resize(numLoopbackSamples * AudioConstants::SAMPLE_SIZE);
@ -980,8 +1027,7 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray, const int
int16_t* loopbackSamples = reinterpret_cast<int16_t*>(loopBackByteArray.data());
// upmix mono to stereo
if (!sampleChannelConversion(inputSamples, loopbackSamples, numInputSamples, channelCount,
OUTPUT_CHANNEL_COUNT)) {
if (!sampleChannelConversion(inputSamples, loopbackSamples, numInputSamples, _inputFormat.channelCount(), OUTPUT_CHANNEL_COUNT)) {
// no conversion, just copy the samples
memcpy(loopbackSamples, inputSamples, numInputSamples * AudioConstants::SAMPLE_SIZE);
}
@ -995,9 +1041,11 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray, const int
// if required, upmix or downmix to deviceChannelCount
int deviceChannelCount = _outputFormat.channelCount();
if (deviceChannelCount == OUTPUT_CHANNEL_COUNT) {
_loopbackOutputDevice->write(loopBackByteArray);
} else {
static QByteArray deviceByteArray;
int numDeviceSamples = (numLoopbackSamples * deviceChannelCount) / OUTPUT_CHANNEL_COUNT;
@ -1088,7 +1136,8 @@ void AudioClient::handleAudioInput(QByteArray& audioBuffer) {
}
emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), _outgoingAvatarAudioSequenceNumber, _isStereoInput,
audioTransform, avatarBoundingBoxCorner, avatarBoundingBoxScale, packetType, _selectedCodecName);
audioTransform, avatarBoundingBoxCorner, avatarBoundingBoxScale,
packetType, _selectedCodecName);
_stats.sentPacket();
}
}
@ -1103,25 +1152,26 @@ void AudioClient::handleMicAudioInput() {
#endif
// input samples required to produce exactly NETWORK_FRAME_SAMPLES of output
const int inputSamplesRequired =
(_inputToNetworkResampler ? _inputToNetworkResampler->getMinInput(AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL)
: AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL) *
_inputFormat.channelCount();
const int inputSamplesRequired = (_inputToNetworkResampler ?
_inputToNetworkResampler->getMinInput(AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL) :
AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL) * _inputFormat.channelCount();
const auto inputAudioSamples = std::unique_ptr<int16_t[]>(new int16_t[inputSamplesRequired]);
QByteArray inputByteArray = _inputDevice->readAll();
handleLocalEchoAndReverb(inputByteArray, _inputFormat.sampleRate(), _inputFormat.channelCount());
handleLocalEchoAndReverb(inputByteArray);
_inputRingBuffer.writeData(inputByteArray.data(), inputByteArray.size());
float audioInputMsecsRead = inputByteArray.size() / (float)(_inputFormat.bytesForDuration(USECS_PER_MSEC));
_stats.updateInputMsRead(audioInputMsecsRead);
const int numNetworkBytes =
_isStereoInput ? AudioConstants::NETWORK_FRAME_BYTES_STEREO : AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL;
const int numNetworkSamples =
_isStereoInput ? AudioConstants::NETWORK_FRAME_SAMPLES_STEREO : AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL;
const int numNetworkBytes = _isStereoInput
? AudioConstants::NETWORK_FRAME_BYTES_STEREO
: AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL;
const int numNetworkSamples = _isStereoInput
? AudioConstants::NETWORK_FRAME_SAMPLES_STEREO
: AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL;
static int16_t networkAudioSamples[AudioConstants::NETWORK_FRAME_SAMPLES_STEREO];
@ -1130,8 +1180,10 @@ void AudioClient::handleMicAudioInput() {
_inputRingBuffer.shiftReadPosition(inputSamplesRequired);
} else {
_inputRingBuffer.readSamples(inputAudioSamples.get(), inputSamplesRequired);
possibleResampling(_inputToNetworkResampler, inputAudioSamples.get(), networkAudioSamples, inputSamplesRequired,
numNetworkSamples, _inputFormat.channelCount(), _desiredInputFormat.channelCount());
possibleResampling(_inputToNetworkResampler,
inputAudioSamples.get(), networkAudioSamples,
inputSamplesRequired, numNetworkSamples,
_inputFormat.channelCount(), _desiredInputFormat.channelCount());
}
int bytesInInputRingBuffer = _inputRingBuffer.samplesAvailable() * AudioConstants::SAMPLE_SIZE;
float msecsInInputRingBuffer = bytesInInputRingBuffer / (float)(_inputFormat.bytesForDuration(USECS_PER_MSEC));
@ -1143,8 +1195,9 @@ void AudioClient::handleMicAudioInput() {
}
void AudioClient::handleDummyAudioInput() {
const int numNetworkBytes =
_isStereoInput ? AudioConstants::NETWORK_FRAME_BYTES_STEREO : AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL;
const int numNetworkBytes = _isStereoInput
? AudioConstants::NETWORK_FRAME_BYTES_STEREO
: AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL;
QByteArray audioBuffer(numNetworkBytes, 0); // silent
handleAudioInput(audioBuffer);
@ -1155,51 +1208,6 @@ void AudioClient::handleRecordedAudioInput(const QByteArray& audio) {
handleAudioInput(audioBuffer);
}
int rawToWav(const char* rawData, const int& rawLength, const char* wavfn, long frequency, unsigned short channels) {
struct {
unsigned short wFormatTag;
unsigned short wChannels;
unsigned long dwSamplesPerSec;
unsigned long dwAvgBytesPerSec;
unsigned short wBlockAlign;
unsigned short wBitsPerSample;
} fmt;
long samplecount = rawLength / 2;
FILE* wav = fopen(wavfn, "wb");
if (!wav) {
return -1;
}
fwrite("RIFF", 1, 4, wav);
long riffsize = samplecount * 2 + 0x24;
fwrite(&riffsize, 4, 1, wav);
fwrite("WAVEfmt ", 1, 8, wav);
long chunksize = 0x10;
fwrite(&chunksize, 4, 1, wav);
fmt.wFormatTag = 1; // WAVE_FORMAT_PCM
fmt.wChannels = channels;
fmt.dwSamplesPerSec = frequency * 1;
fmt.wBitsPerSample = 16;
fmt.wBlockAlign = fmt.wChannels * fmt.wBitsPerSample / 8;
fmt.dwAvgBytesPerSec = fmt.dwSamplesPerSec * fmt.wBlockAlign;
fwrite(&fmt, sizeof(fmt), 1, wav);
fwrite("data", 1, 4, wav);
long datasize = samplecount * 2;
fwrite(&datasize, 4, 1, wav);
fwrite(rawData, 1, rawLength, wav);
fclose(wav);
return 0;
}
void AudioClient::prepareLocalAudioInjectors(std::unique_ptr<Lock> localAudioLock) {
bool doSynchronously = localAudioLock.operator bool();
if (!localAudioLock) {
@ -1222,7 +1230,8 @@ void AudioClient::prepareLocalAudioInjectors(std::unique_ptr<Lock> localAudioLoc
int bufferCapacity = _localInjectorsStream.getSampleCapacity();
int maxOutputSamples = AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * AudioConstants::STEREO;
if (_localToOutputResampler) {
maxOutputSamples = _localToOutputResampler->getMaxOutput(AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL) *
maxOutputSamples =
_localToOutputResampler->getMaxOutput(AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL) *
AudioConstants::STEREO;
}
@ -1255,7 +1264,8 @@ void AudioClient::prepareLocalAudioInjectors(std::unique_ptr<Lock> localAudioLoc
} else {
// write to local injectors' ring buffer
samples = AudioConstants::NETWORK_FRAME_SAMPLES_STEREO;
_localInjectorsStream.writeSamples(_localMixBuffer, AudioConstants::NETWORK_FRAME_SAMPLES_STEREO);
_localInjectorsStream.writeSamples(_localMixBuffer,
AudioConstants::NETWORK_FRAME_SAMPLES_STEREO);
}
_localSamplesAvailable.fetch_add(samples, std::memory_order_release);
@ -1280,17 +1290,18 @@ void AudioClient::prepareLocalAudioInjectors(std::unique_ptr<Lock> localAudioLoc
// the lock guarantees that injectorBuffer, if found, is invariant
AudioInjectorLocalBuffer* injectorBuffer = injector->getLocalBuffer();
if (injectorBuffer) {
static const int HRTF_DATASET_INDEX = 1;
int numChannels = injector->isAmbisonic()
? AudioConstants::AMBISONIC
: (injector->isStereo() ? AudioConstants::STEREO : AudioConstants::MONO);
int numChannels = injector->isAmbisonic() ? AudioConstants::AMBISONIC : (injector->isStereo() ? AudioConstants::STEREO : AudioConstants::MONO);
size_t bytesToRead = numChannels * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL;
// get one frame from the injector
memset(_localScratchBuffer, 0, bytesToRead);
if (0 < injectorBuffer->readData((char*)_localScratchBuffer, bytesToRead)) {
if (injector->isAmbisonic()) {
// no distance attenuation
float gain = injector->getVolume();
@ -1307,10 +1318,11 @@ void AudioClient::prepareLocalAudioInjectors(std::unique_ptr<Lock> localAudioLoc
float qz = relativeOrientation.y;
// Ambisonic gets spatialized into mixBuffer
injector->getLocalFOA().render(_localScratchBuffer, mixBuffer, HRTF_DATASET_INDEX, qw, qx, qy, qz, gain,
AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL);
injector->getLocalFOA().render(_localScratchBuffer, mixBuffer, HRTF_DATASET_INDEX,
qw, qx, qy, qz, gain, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL);
} else if (injector->isStereo()) {
// stereo gets directly mixed into mixBuffer
float gain = injector->getVolume();
for (int i = 0; i < AudioConstants::NETWORK_FRAME_SAMPLES_STEREO; i++) {
@ -1318,6 +1330,7 @@ void AudioClient::prepareLocalAudioInjectors(std::unique_ptr<Lock> localAudioLoc
}
} else {
// calculate distance, gain and azimuth for hrtf
glm::vec3 relativePosition = injector->getPosition() - _positionGetter();
float distance = glm::max(glm::length(relativePosition), EPSILON);
@ -1325,17 +1338,19 @@ void AudioClient::prepareLocalAudioInjectors(std::unique_ptr<Lock> localAudioLoc
float azimuth = azimuthForSource(relativePosition);
// mono gets spatialized into mixBuffer
injector->getLocalHRTF().render(_localScratchBuffer, mixBuffer, HRTF_DATASET_INDEX, azimuth, distance,
gain, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL);
injector->getLocalHRTF().render(_localScratchBuffer, mixBuffer, HRTF_DATASET_INDEX,
azimuth, distance, gain, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL);
}
} else {
qCDebug(audioclient) << "injector has no more data, marking finished for removal";
injector->finishLocalInjection();
injectorsToRemove.append(injector);
}
} else {
qCDebug(audioclient) << "injector has no local buffer, marking as finished for removal";
injector->finishLocalInjection();
injectorsToRemove.append(injector);
@ -1354,6 +1369,7 @@ void AudioClient::prepareLocalAudioInjectors(std::unique_ptr<Lock> localAudioLoc
}
void AudioClient::processReceivedSamples(const QByteArray& decodedBuffer, QByteArray& outputBuffer) {
const int16_t* decodedSamples = reinterpret_cast<const int16_t*>(decodedBuffer.data());
assert(decodedBuffer.size() == AudioConstants::NETWORK_FRAME_BYTES_STEREO);
@ -1422,6 +1438,7 @@ void AudioClient::prepareLocalAudioInjectors(std::unique_ptr<Lock> localAudioLoc
}
}
bool AudioClient::setIsStereoInput(bool isStereoInput) {
bool stereoInputChanged = false;
if (isStereoInput != _isStereoInput && _inputDeviceInfo.supportedChannelCounts().contains(2)) {
@ -1439,8 +1456,7 @@ void AudioClient::prepareLocalAudioInjectors(std::unique_ptr<Lock> localAudioLoc
if (_encoder) {
_codec->releaseEncoder(_encoder);
}
_encoder = _codec->createEncoder(AudioConstants::SAMPLE_RATE,
_isStereoInput ? AudioConstants::STEREO : AudioConstants::MONO);
_encoder = _codec->createEncoder(AudioConstants::SAMPLE_RATE, _isStereoInput ? AudioConstants::STEREO : AudioConstants::MONO);
}
qCDebug(audioclient) << "Reset Codec:" << _selectedCodecName << "isStereoInput:" << _isStereoInput;
@ -1479,8 +1495,7 @@ void AudioClient::prepareLocalAudioInjectors(std::unique_ptr<Lock> localAudioLoc
}
void AudioClient::outputFormatChanged() {
_outputFrameSize =
(AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * OUTPUT_CHANNEL_COUNT * _outputFormat.sampleRate()) /
_outputFrameSize = (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * OUTPUT_CHANNEL_COUNT * _outputFormat.sampleRate()) /
_desiredOutputFormat.sampleRate();
_receivedAudioStream.outputFormatChanged(_outputFormat.sampleRate(), OUTPUT_CHANNEL_COUNT);
}
@ -1546,15 +1561,15 @@ void AudioClient::prepareLocalAudioInjectors(std::unique_ptr<Lock> localAudioLoc
// we've got the best we can get for input
// if required, setup a resampler for this input to our desired network format
if (_inputFormat != _desiredInputFormat && _inputFormat.sampleRate() != _desiredInputFormat.sampleRate()) {
if (_inputFormat != _desiredInputFormat
&& _inputFormat.sampleRate() != _desiredInputFormat.sampleRate()) {
qCDebug(audioclient) << "Attemping to create a resampler for input format to network format.";
assert(_inputFormat.sampleSize() == 16);
assert(_desiredInputFormat.sampleSize() == 16);
int channelCount = (_inputFormat.channelCount() == 2 && _desiredInputFormat.channelCount() == 2) ? 2 : 1;
_inputToNetworkResampler =
new AudioSRC(_inputFormat.sampleRate(), _desiredInputFormat.sampleRate(), channelCount);
_inputToNetworkResampler = new AudioSRC(_inputFormat.sampleRate(), _desiredInputFormat.sampleRate(), channelCount);
} else {
qCDebug(audioclient) << "No resampling required for audio input to match desired network format.";
@ -1696,8 +1711,8 @@ void AudioClient::prepareLocalAudioInjectors(std::unique_ptr<Lock> localAudioLoc
int newOutputBufferSizeFrames = setOutputBufferSize(oldOutputBufferSizeFrames + 1, false);
if (newOutputBufferSizeFrames > oldOutputBufferSizeFrames) {
qCDebug(audioclient, "Starve threshold surpassed (%d starves in %d ms)",
_outputStarveDetectionCount, dt);
qCDebug(audioclient,
"Starve threshold surpassed (%d starves in %d ms)", _outputStarveDetectionCount, dt);
}
_outputStarveDetectionStartTimeMsec = now;
@ -1711,8 +1726,7 @@ void AudioClient::prepareLocalAudioInjectors(std::unique_ptr<Lock> localAudioLoc
bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo outputDeviceInfo, bool isShutdownRequest) {
Q_ASSERT_X(QThread::currentThread() == thread(), Q_FUNC_INFO, "Function invoked on wrong thread");
qCDebug(audioclient) << "AudioClient::switchOutputToAudioDevice() outputDeviceInfo: [" << outputDeviceInfo.deviceName()
<< "]";
qCDebug(audioclient) << "AudioClient::switchOutputToAudioDevice() outputDeviceInfo: [" << outputDeviceInfo.deviceName() << "]";
bool supportedFormat = false;
// NOTE: device start() uses the Qt internal device list
@ -1771,16 +1785,15 @@ void AudioClient::prepareLocalAudioInjectors(std::unique_ptr<Lock> localAudioLoc
// we've got the best we can get for input
// if required, setup a resampler for this input to our desired network format
if (_desiredOutputFormat != _outputFormat && _desiredOutputFormat.sampleRate() != _outputFormat.sampleRate()) {
if (_desiredOutputFormat != _outputFormat
&& _desiredOutputFormat.sampleRate() != _outputFormat.sampleRate()) {
qCDebug(audioclient) << "Attemping to create a resampler for network format to output format.";
assert(_desiredOutputFormat.sampleSize() == 16);
assert(_outputFormat.sampleSize() == 16);
_networkToOutputResampler =
new AudioSRC(_desiredOutputFormat.sampleRate(), _outputFormat.sampleRate(), OUTPUT_CHANNEL_COUNT);
_localToOutputResampler =
new AudioSRC(_desiredOutputFormat.sampleRate(), _outputFormat.sampleRate(), OUTPUT_CHANNEL_COUNT);
_networkToOutputResampler = new AudioSRC(_desiredOutputFormat.sampleRate(), _outputFormat.sampleRate(), OUTPUT_CHANNEL_COUNT);
_localToOutputResampler = new AudioSRC(_desiredOutputFormat.sampleRate(), _outputFormat.sampleRate(), OUTPUT_CHANNEL_COUNT);
} else {
qCDebug(audioclient) << "No resampling required for network output to match actual output format.";
@ -1792,9 +1805,7 @@ void AudioClient::prepareLocalAudioInjectors(std::unique_ptr<Lock> localAudioLoc
_audioOutput = new QAudioOutput(outputDeviceInfo, _outputFormat, this);
int deviceChannelCount = _outputFormat.channelCount();
int frameSize =
(AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * deviceChannelCount * _outputFormat.sampleRate()) /
_desiredOutputFormat.sampleRate();
int frameSize = (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * deviceChannelCount * _outputFormat.sampleRate()) / _desiredOutputFormat.sampleRate();
int requestedSize = _sessionOutputBufferSizeFrames * frameSize * AudioConstants::SAMPLE_SIZE;
_audioOutput->setBufferSize(requestedSize);
@ -1810,10 +1821,7 @@ void AudioClient::prepareLocalAudioInjectors(std::unique_ptr<Lock> localAudioLoc
_outputScratchBuffer = new int16_t[_outputPeriod];
// size local output mix buffer based on resampled network frame size
int networkPeriod =
_localToOutputResampler
? _localToOutputResampler->getMaxOutput(AudioConstants::NETWORK_FRAME_SAMPLES_STEREO)
: AudioConstants::NETWORK_FRAME_SAMPLES_STEREO;
int networkPeriod = _localToOutputResampler ? _localToOutputResampler->getMaxOutput(AudioConstants::NETWORK_FRAME_SAMPLES_STEREO) : AudioConstants::NETWORK_FRAME_SAMPLES_STEREO;
_localOutputMixBuffer = new float[networkPeriod];
// local period should be at least twice the output period,
@ -1863,8 +1871,7 @@ void AudioClient::prepareLocalAudioInjectors(std::unique_ptr<Lock> localAudioLoc
qCDebug(audioclient) << __FUNCTION__ << "numFrames:" << numFrames << "persist:" << persist;
numFrames = std::min(std::max(numFrames, MIN_BUFFER_FRAMES), MAX_BUFFER_FRAMES);
qCDebug(audioclient) << __FUNCTION__ << "clamped numFrames:" << numFrames
<< "_sessionOutputBufferSizeFrames:" << _sessionOutputBufferSizeFrames;
qCDebug(audioclient) << __FUNCTION__ << "clamped numFrames:" << numFrames << "_sessionOutputBufferSizeFrames:" << _sessionOutputBufferSizeFrames;
if (numFrames != _sessionOutputBufferSizeFrames) {
qCInfo(audioclient, "Audio output buffer set to %d frames", numFrames);
@ -1895,10 +1902,10 @@ void AudioClient::prepareLocalAudioInjectors(std::unique_ptr<Lock> localAudioLoc
#endif
int AudioClient::calculateNumberOfInputCallbackBytes(const QAudioFormat& format) const {
int numInputCallbackBytes = (int)(((AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL * format.channelCount() *
((float)format.sampleRate() / AudioConstants::SAMPLE_RATE)) /
CALLBACK_ACCELERATOR_RATIO) +
0.5f);
int numInputCallbackBytes = (int)(((AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL
* format.channelCount()
* ((float) format.sampleRate() / AudioConstants::SAMPLE_RATE))
/ CALLBACK_ACCELERATOR_RATIO) + 0.5f);
return numInputCallbackBytes;
}
@ -1920,6 +1927,7 @@ void AudioClient::prepareLocalAudioInjectors(std::unique_ptr<Lock> localAudioLoc
float rotatedSourcePositionLength2 = glm::length2(rotatedSourcePosition);
if (rotatedSourcePositionLength2 > SOURCE_DISTANCE_THRESHOLD) {
// produce an oriented angle about the y-axis
glm::vec3 direction = rotatedSourcePosition * (1.0f / fastSqrtf(rotatedSourcePositionLength2));
float angle = fastAcosf(glm::clamp(-direction.z, -1.0f, 1.0f)); // UNIT_NEG_Z is "forward"
@ -1932,6 +1940,7 @@ void AudioClient::prepareLocalAudioInjectors(std::unique_ptr<Lock> localAudioLoc
}
float AudioClient::gainForSource(float distance, float volume) {
// attenuation = -6dB * log2(distance)
// reference attenuation of 0dB at distance = 1.0m
float gain = volume / std::max(distance, HRTF_NEARFIELD_MIN);
@ -1940,6 +1949,7 @@ void AudioClient::prepareLocalAudioInjectors(std::unique_ptr<Lock> localAudioLoc
}
qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) {
// samples requested from OUTPUT_CHANNEL_COUNT
int deviceChannelCount = _audio->_outputFormat.channelCount();
int samplesRequested = (int)(maxSize / AudioConstants::SAMPLE_SIZE) * OUTPUT_CHANNEL_COUNT / deviceChannelCount;
@ -1951,8 +1961,7 @@ void AudioClient::prepareLocalAudioInjectors(std::unique_ptr<Lock> localAudioLoc
int networkSamplesPopped;
if ((networkSamplesPopped = _receivedAudioStream.popSamples(samplesRequested, false)) > 0) {
qCDebug(audiostream, "Read %d samples from buffer (%d available, %d requested)", networkSamplesPopped,
_receivedAudioStream.getSamplesAvailable(), samplesRequested);
qCDebug(audiostream, "Read %d samples from buffer (%d available, %d requested)", networkSamplesPopped, _receivedAudioStream.getSamplesAvailable(), samplesRequested);
AudioRingBuffer::ConstIterator lastPopOutput = _receivedAudioStream.getLastPopOutput();
lastPopOutput.readSamples(scratchBuffer, networkSamplesPopped);
for (int i = 0; i < networkSamplesPopped; i++) {
@ -1984,13 +1993,14 @@ void AudioClient::prepareLocalAudioInjectors(std::unique_ptr<Lock> localAudioLoc
samplesRequested = std::min(samplesRequested, samplesAvailable);
if ((injectorSamplesPopped = _localInjectorsStream.appendSamples(mixBuffer, samplesRequested, append)) > 0) {
_audio->_localSamplesAvailable.fetch_sub(injectorSamplesPopped, std::memory_order_release);
qCDebug(audiostream, "Read %d samples from injectors (%d available, %d requested)", injectorSamplesPopped,
_localInjectorsStream.samplesAvailable(), samplesRequested);
qCDebug(audiostream, "Read %d samples from injectors (%d available, %d requested)", injectorSamplesPopped, _localInjectorsStream.samplesAvailable(), samplesRequested);
}
}
// prepare injectors for the next callback
QtConcurrent::run(QThreadPool::globalInstance(), [this] { _audio->prepareLocalAudioInjectors(); });
QtConcurrent::run(QThreadPool::globalInstance(), [this] {
_audio->prepareLocalAudioInjectors();
});
int samplesPopped = std::max(networkSamplesPopped, injectorSamplesPopped);
int framesPopped = samplesPopped / AudioConstants::STEREO;
@ -2024,9 +2034,9 @@ void AudioClient::prepareLocalAudioInjectors(std::unique_ptr<Lock> localAudioLoc
_audio->_audioFileWav.addRawAudioChunk(reinterpret_cast<char*>(scratchBuffer), bytesWritten);
}
int bytesAudioOutputUnplayed = _audio->_audioOutput->bufferSize() - _audio->_audioOutput->bytesFree();
float msecsAudioOutputUnplayed =
bytesAudioOutputUnplayed / (float)_audio->_outputFormat.bytesForDuration(USECS_PER_MSEC);
float msecsAudioOutputUnplayed = bytesAudioOutputUnplayed / (float)_audio->_outputFormat.bytesForDuration(USECS_PER_MSEC);
_audio->_stats.updateOutputMsUnplayed(msecsAudioOutputUnplayed);
if (bytesAudioOutputUnplayed == 0) {
@ -2061,6 +2071,7 @@ void AudioClient::prepareLocalAudioInjectors(std::unique_ptr<Lock> localAudioLoc
for (auto& plugin : codecPlugins) {
qCDebug(audioclient) << "Codec available:" << plugin->getName();
}
}
void AudioClient::saveSettings() {
@ -2073,6 +2084,7 @@ void AudioClient::prepareLocalAudioInjectors(std::unique_ptr<Lock> localAudioLoc
avatarBoundingBoxScale = scale;
}
void AudioClient::startThread() {
moveToNewNamedThread(this, "Audio Thread", [this] { start(); }, QThread::TimeCriticalPriority);
}

View file

@ -1311,7 +1311,7 @@ glm::quat Avatar::getAbsoluteJointRotationInObjectFrame(int index) const {
case CAMERA_MATRIX_INDEX: {
glm::quat rotation;
if (_skeletonModel && _skeletonModel->isActive()) {
int headJointIndex = _skeletonModel->getFBXGeometry().headJointIndex;
int headJointIndex = _skeletonModel->getHFMModel().headJointIndex;
if (headJointIndex >= 0) {
_skeletonModel->getAbsoluteJointRotationInRigFrame(headJointIndex, rotation);
}
@ -1360,7 +1360,7 @@ glm::vec3 Avatar::getAbsoluteJointTranslationInObjectFrame(int index) const {
case CAMERA_MATRIX_INDEX: {
glm::vec3 translation;
if (_skeletonModel && _skeletonModel->isActive()) {
int headJointIndex = _skeletonModel->getFBXGeometry().headJointIndex;
int headJointIndex = _skeletonModel->getHFMModel().headJointIndex;
if (headJointIndex >= 0) {
_skeletonModel->getAbsoluteJointTranslationInRigFrame(headJointIndex, translation);
}
@ -1416,7 +1416,7 @@ void Avatar::withValidJointIndicesCache(std::function<void()> const& worker) con
if (!_modelJointsCached) {
_modelJointIndicesCache.clear();
if (_skeletonModel && _skeletonModel->isActive()) {
_modelJointIndicesCache = _skeletonModel->getFBXGeometry().jointIndices;
_modelJointIndicesCache = _skeletonModel->getHFMModel().jointIndices;
_modelJointsCached = true;
}
}

View file

@ -54,9 +54,9 @@ void SkeletonModel::setTextures(const QVariantMap& textures) {
}
void SkeletonModel::initJointStates() {
const FBXGeometry& geometry = getFBXGeometry();
const HFMModel& hfmModel = getHFMModel();
glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset);
_rig.initJointStates(geometry, modelOffset);
_rig.initJointStates(hfmModel, modelOffset);
{
// initialize _jointData with proper values for default joints
@ -66,7 +66,7 @@ void SkeletonModel::initJointStates() {
}
// Determine the default eye position for avatar scale = 1.0
int headJointIndex = geometry.headJointIndex;
int headJointIndex = hfmModel.headJointIndex;
if (0 > headJointIndex || headJointIndex >= _rig.getJointStateCount()) {
qCWarning(avatars_renderer) << "Bad head joint! Got:" << headJointIndex << "jointCount:" << _rig.getJointStateCount();
}
@ -74,7 +74,7 @@ void SkeletonModel::initJointStates() {
getEyeModelPositions(leftEyePosition, rightEyePosition);
glm::vec3 midEyePosition = (leftEyePosition + rightEyePosition) / 2.0f;
int rootJointIndex = geometry.rootJointIndex;
int rootJointIndex = hfmModel.rootJointIndex;
glm::vec3 rootModelPosition;
getJointPosition(rootJointIndex, rootModelPosition);
@ -96,7 +96,7 @@ void SkeletonModel::initJointStates() {
// Called within Model::simulate call, below.
void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
assert(!_owningAvatar->isMyAvatar());
const FBXGeometry& geometry = getFBXGeometry();
const HFMModel& hfmModel = getHFMModel();
Head* head = _owningAvatar->getHead();
@ -124,7 +124,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
// If the head is not positioned, updateEyeJoints won't get the math right
glm::quat headOrientation;
_rig.getJointRotation(geometry.headJointIndex, headOrientation);
_rig.getJointRotation(hfmModel.headJointIndex, headOrientation);
glm::vec3 eulers = safeEulerAngles(headOrientation);
head->setBasePitch(glm::degrees(-eulers.x));
head->setBaseYaw(glm::degrees(eulers.y));
@ -135,8 +135,8 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
eyeParams.eyeSaccade = glm::vec3(0.0f);
eyeParams.modelRotation = getRotation();
eyeParams.modelTranslation = getTranslation();
eyeParams.leftEyeJointIndex = geometry.leftEyeJointIndex;
eyeParams.rightEyeJointIndex = geometry.rightEyeJointIndex;
eyeParams.leftEyeJointIndex = hfmModel.leftEyeJointIndex;
eyeParams.rightEyeJointIndex = hfmModel.rightEyeJointIndex;
_rig.updateFromEyeParameters(eyeParams);
}
@ -259,45 +259,45 @@ bool SkeletonModel::getRightShoulderPosition(glm::vec3& position) const {
}
bool SkeletonModel::getHeadPosition(glm::vec3& headPosition) const {
return isActive() && getJointPositionInWorldFrame(getFBXGeometry().headJointIndex, headPosition);
return isActive() && getJointPositionInWorldFrame(getHFMModel().headJointIndex, headPosition);
}
bool SkeletonModel::getNeckPosition(glm::vec3& neckPosition) const {
return isActive() && getJointPositionInWorldFrame(getFBXGeometry().neckJointIndex, neckPosition);
return isActive() && getJointPositionInWorldFrame(getHFMModel().neckJointIndex, neckPosition);
}
bool SkeletonModel::getLocalNeckPosition(glm::vec3& neckPosition) const {
return isActive() && getJointPosition(getFBXGeometry().neckJointIndex, neckPosition);
return isActive() && getJointPosition(getHFMModel().neckJointIndex, neckPosition);
}
bool SkeletonModel::getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const {
if (!isActive()) {
return false;
}
const FBXGeometry& geometry = getFBXGeometry();
const HFMModel& hfmModel = getHFMModel();
if (getJointPosition(geometry.leftEyeJointIndex, firstEyePosition) &&
getJointPosition(geometry.rightEyeJointIndex, secondEyePosition)) {
if (getJointPosition(hfmModel.leftEyeJointIndex, firstEyePosition) &&
getJointPosition(hfmModel.rightEyeJointIndex, secondEyePosition)) {
return true;
}
// no eye joints; try to estimate based on head/neck joints
glm::vec3 neckPosition, headPosition;
if (getJointPosition(geometry.neckJointIndex, neckPosition) &&
getJointPosition(geometry.headJointIndex, headPosition)) {
if (getJointPosition(hfmModel.neckJointIndex, neckPosition) &&
getJointPosition(hfmModel.headJointIndex, headPosition)) {
const float EYE_PROPORTION = 0.6f;
glm::vec3 baseEyePosition = glm::mix(neckPosition, headPosition, EYE_PROPORTION);
glm::quat headRotation;
getJointRotation(geometry.headJointIndex, headRotation);
getJointRotation(hfmModel.headJointIndex, headRotation);
const float EYES_FORWARD = 0.25f;
const float EYE_SEPARATION = 0.1f;
float headHeight = glm::distance(neckPosition, headPosition);
firstEyePosition = baseEyePosition + headRotation * glm::vec3(EYE_SEPARATION, 0.0f, EYES_FORWARD) * headHeight;
secondEyePosition = baseEyePosition + headRotation * glm::vec3(-EYE_SEPARATION, 0.0f, EYES_FORWARD) * headHeight;
return true;
} else if (getJointPosition(geometry.headJointIndex, headPosition)) {
} else if (getJointPosition(hfmModel.headJointIndex, headPosition)) {
glm::vec3 baseEyePosition = headPosition;
glm::quat headRotation;
getJointRotation(geometry.headJointIndex, headRotation);
getJointRotation(hfmModel.headJointIndex, headRotation);
const float EYES_FORWARD_HEAD_ONLY = 0.30f;
const float EYE_SEPARATION = 0.1f;
firstEyePosition = baseEyePosition + headRotation * glm::vec3(EYE_SEPARATION, 0.0f, EYES_FORWARD_HEAD_ONLY);
@ -330,15 +330,15 @@ void SkeletonModel::computeBoundingShape() {
return;
}
const FBXGeometry& geometry = getFBXGeometry();
if (geometry.joints.isEmpty() || geometry.rootJointIndex == -1) {
const HFMModel& hfmModel = getHFMModel();
if (hfmModel.joints.isEmpty() || hfmModel.rootJointIndex == -1) {
// rootJointIndex == -1 if the avatar model has no skeleton
return;
}
float radius, height;
glm::vec3 offset;
_rig.computeAvatarBoundingCapsule(geometry, radius, height, offset);
_rig.computeAvatarBoundingCapsule(hfmModel, radius, height, offset);
float invScale = 1.0f / _owningAvatar->getModelScale();
_boundingCapsuleRadius = invScale * radius;
_boundingCapsuleHeight = invScale * height;
@ -369,7 +369,7 @@ void SkeletonModel::renderBoundingCollisionShapes(RenderArgs* args, gpu::Batch&
}
bool SkeletonModel::hasSkeleton() {
return isActive() ? getFBXGeometry().rootJointIndex != -1 : false;
return isActive() ? getHFMModel().rootJointIndex != -1 : false;
}
void SkeletonModel::onInvalidate() {

View file

@ -41,10 +41,10 @@ public:
void updateAttitude(const glm::quat& orientation);
/// Returns the index of the left hand joint, or -1 if not found.
int getLeftHandJointIndex() const { return isActive() ? getFBXGeometry().leftHandJointIndex : -1; }
int getLeftHandJointIndex() const { return isActive() ? getHFMModel().leftHandJointIndex : -1; }
/// Returns the index of the right hand joint, or -1 if not found.
int getRightHandJointIndex() const { return isActive() ? getFBXGeometry().rightHandJointIndex : -1; }
int getRightHandJointIndex() const { return isActive() ? getHFMModel().rightHandJointIndex : -1; }
bool getLeftGrabPosition(glm::vec3& position) const;
bool getRightGrabPosition(glm::vec3& position) const;

View file

@ -206,7 +206,7 @@ void FBXBaker::importScene() {
}
#endif
_geometry = reader.extractFBXGeometry({}, _modelURL.toString());
_hfmModel = reader.extractHFMModel({}, _modelURL.toString());
_textureContentMap = reader._textureContent;
}
@ -231,7 +231,7 @@ void FBXBaker::rewriteAndBakeSceneModels() {
for (FBXNode& objectChild : rootChild.children) {
if (objectChild.name == "Geometry") {
// TODO Pull this out of _geometry instead so we don't have to reprocess it
// TODO Pull this out of _hfmModel instead so we don't have to reprocess it
auto extractedMesh = FBXReader::extractMesh(objectChild, meshIndex, false);
// Callback to get MaterialID
@ -293,7 +293,7 @@ void FBXBaker::rewriteAndBakeSceneTextures() {
QHash<QString, image::TextureUsage::Type> textureTypes;
// enumerate the materials in the extracted geometry so we can determine the texture type for each texture ID
for (const auto& material : _geometry->materials) {
for (const auto& material : _hfmModel->materials) {
if (material.normalTexture.isBumpmap) {
textureTypes[material.normalTexture.id] = BUMP_TEXTURE;
} else {
@ -329,7 +329,7 @@ void FBXBaker::rewriteAndBakeSceneTextures() {
for (FBXNode& textureChild : object->children) {
if (textureChild.name == "RelativeFilename") {
QString fbxTextureFileName { textureChild.properties.at(0).toString() };
QString hfmTextureFileName { textureChild.properties.at(0).toString() };
// grab the ID for this texture so we can figure out the
// texture type from the loaded materials
@ -337,7 +337,7 @@ void FBXBaker::rewriteAndBakeSceneTextures() {
auto textureType = textureTypes[textureID];
// Compress the texture information and return the new filename to be added into the FBX scene
auto bakedTextureFile = compressTexture(fbxTextureFileName, textureType);
auto bakedTextureFile = compressTexture(hfmTextureFileName, textureType);
// If no errors or warnings have occurred during texture compression add the filename to the FBX scene
if (!bakedTextureFile.isNull()) {

View file

@ -53,7 +53,7 @@ private:
void rewriteAndBakeSceneModels();
void rewriteAndBakeSceneTextures();
FBXGeometry* _geometry;
HFMModel* _hfmModel;
QHash<QString, int> _textureNameMatchCount;
QHash<QUrl, QString> _remappedTexturePaths;

View file

@ -75,7 +75,7 @@ void ModelBaker::abort() {
}
}
bool ModelBaker::compressMesh(FBXMesh& mesh, bool hasDeformers, FBXNode& dracoMeshNode, GetMaterialIDCallback materialIDCallback) {
bool ModelBaker::compressMesh(HFMMesh& mesh, bool hasDeformers, FBXNode& dracoMeshNode, GetMaterialIDCallback materialIDCallback) {
if (mesh.wasCompressed) {
handleError("Cannot re-bake a file that contains compressed mesh");
return false;

View file

@ -39,7 +39,7 @@ public:
const QString& bakedOutputDirectory, const QString& originalOutputDirectory = "");
virtual ~ModelBaker();
bool compressMesh(FBXMesh& mesh, bool hasDeformers, FBXNode& dracoMeshNode, GetMaterialIDCallback materialIDCallback = nullptr);
bool compressMesh(HFMMesh& mesh, bool hasDeformers, FBXNode& dracoMeshNode, GetMaterialIDCallback materialIDCallback = nullptr);
QString compressTexture(QString textureFileName, image::TextureUsage::Type = image::TextureUsage::Type::DEFAULT_TEXTURE);
virtual void setWasAborted(bool wasAborted) override;

View file

@ -153,7 +153,7 @@ void OBJBaker::bakeOBJ() {
checkIfTexturesFinished();
}
void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) {
void OBJBaker::createFBXNodeTree(FBXNode& rootNode, HFMModel& hfmModel) {
// Generating FBX Header Node
FBXNode headerNode;
headerNode.name = FBX_HEADER_EXTENSION;
@ -199,7 +199,7 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) {
// Compress the mesh information and store in dracoNode
bool hasDeformers = false; // No concept of deformers for an OBJ
FBXNode dracoNode;
compressMesh(geometry.meshes[0], hasDeformers, dracoNode);
compressMesh(hfmModel.meshes[0], hasDeformers, dracoNode);
geometryNode.children.append(dracoNode);
// Generating Object node's child - Model node
@ -214,17 +214,17 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) {
objectNode.children = { geometryNode, modelNode };
// Generating Objects node's child - Material node
auto& meshParts = geometry.meshes[0].parts;
auto& meshParts = hfmModel.meshes[0].parts;
for (auto& meshPart : meshParts) {
FBXNode materialNode;
materialNode.name = MATERIAL_NODE_NAME;
if (geometry.materials.size() == 1) {
if (hfmModel.materials.size() == 1) {
// case when no material information is provided, OBJReader considers it as a single default material
for (auto& materialID : geometry.materials.keys()) {
setMaterialNodeProperties(materialNode, materialID, geometry);
for (auto& materialID : hfmModel.materials.keys()) {
setMaterialNodeProperties(materialNode, materialID, hfmModel);
}
} else {
setMaterialNodeProperties(materialNode, meshPart.materialID, geometry);
setMaterialNodeProperties(materialNode, meshPart.materialID, hfmModel);
}
objectNode.children.append(materialNode);
@ -235,7 +235,7 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) {
auto size = meshParts.size();
for (int i = 0; i < size; i++) {
QString material = meshParts[i].materialID;
FBXMaterial currentMaterial = geometry.materials[material];
HFMMaterial currentMaterial = hfmModel.materials[material];
if (!currentMaterial.albedoTexture.filename.isEmpty() || !currentMaterial.specularTexture.filename.isEmpty()) {
auto textureID = nextNodeID();
_mapTextureMaterial.emplace_back(textureID, i);
@ -325,12 +325,12 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) {
}
// Set properties for material nodes
void OBJBaker::setMaterialNodeProperties(FBXNode& materialNode, QString material, FBXGeometry& geometry) {
void OBJBaker::setMaterialNodeProperties(FBXNode& materialNode, QString material, HFMModel& hfmModel) {
auto materialID = nextNodeID();
_materialIDs.push_back(materialID);
materialNode.properties = { materialID, material, MESH };
FBXMaterial currentMaterial = geometry.materials[material];
HFMMaterial currentMaterial = hfmModel.materials[material];
// Setting the hierarchy: Material -> Properties70 -> P -> Properties
FBXNode properties70Node;

View file

@ -39,8 +39,8 @@ private slots:
private:
void loadOBJ();
void createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry);
void setMaterialNodeProperties(FBXNode& materialNode, QString material, FBXGeometry& geometry);
void createFBXNodeTree(FBXNode& rootNode, HFMModel& hfmModel);
void setMaterialNodeProperties(FBXNode& materialNode, QString material, HFMModel& hfmModel);
NodeID nextNodeID() { return _nodeID++; }

Some files were not shown because too many files have changed in this diff Show more