Merge branch 'instancing' of github.com:highfidelity/hifi into instancing_gltf
36
cmake/ports/bullet3/bullet-git-fix-build-clang-8.patch
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
From 7638b7c5a659dceb4e580ae87d4d60b00847ef94 Mon Sep 17 00:00:00 2001
|
||||||
|
From: =?UTF-8?q?Emil=20Nord=C3=A9n?= <emilnorden@yahoo.se>
|
||||||
|
Date: Sat, 4 May 2019 08:38:53 +0200
|
||||||
|
Subject: [PATCH] fixed build on latest version of clang
|
||||||
|
|
||||||
|
---
|
||||||
|
src/Bullet3Common/b3Vector3.h | 2 +-
|
||||||
|
src/LinearMath/btVector3.h | 2 +-
|
||||||
|
2 files changed, 2 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/src/Bullet3Common/b3Vector3.h b/src/Bullet3Common/b3Vector3.h
|
||||||
|
index 56e6c13311..a70d68d6e1 100644
|
||||||
|
--- a/src/Bullet3Common/b3Vector3.h
|
||||||
|
+++ b/src/Bullet3Common/b3Vector3.h
|
||||||
|
@@ -36,7 +36,7 @@ subject to the following restrictions:
|
||||||
|
#pragma warning(disable : 4556) // value of intrinsic immediate argument '4294967239' is out of range '0 - 255'
|
||||||
|
#endif
|
||||||
|
|
||||||
|
-#define B3_SHUFFLE(x, y, z, w) ((w) << 6 | (z) << 4 | (y) << 2 | (x))
|
||||||
|
+#define B3_SHUFFLE(x, y, z, w) (((w) << 6 | (z) << 4 | (y) << 2 | (x)) & 0xff)
|
||||||
|
//#define b3_pshufd_ps( _a, _mask ) (__m128) _mm_shuffle_epi32((__m128i)(_a), (_mask) )
|
||||||
|
#define b3_pshufd_ps(_a, _mask) _mm_shuffle_ps((_a), (_a), (_mask))
|
||||||
|
#define b3_splat3_ps(_a, _i) b3_pshufd_ps((_a), B3_SHUFFLE(_i, _i, _i, 3))
|
||||||
|
diff --git a/src/LinearMath/btVector3.h b/src/LinearMath/btVector3.h
|
||||||
|
index 61fd8d1e46..d65ed9808d 100644
|
||||||
|
--- a/src/LinearMath/btVector3.h
|
||||||
|
+++ b/src/LinearMath/btVector3.h
|
||||||
|
@@ -36,7 +36,7 @@ subject to the following restrictions:
|
||||||
|
#pragma warning(disable : 4556) // value of intrinsic immediate argument '4294967239' is out of range '0 - 255'
|
||||||
|
#endif
|
||||||
|
|
||||||
|
-#define BT_SHUFFLE(x, y, z, w) ((w) << 6 | (z) << 4 | (y) << 2 | (x))
|
||||||
|
+#define BT_SHUFFLE(x, y, z, w) (((w) << 6 | (z) << 4 | (y) << 2 | (x)) & 0xff)
|
||||||
|
//#define bt_pshufd_ps( _a, _mask ) (__m128) _mm_shuffle_epi32((__m128i)(_a), (_mask) )
|
||||||
|
#define bt_pshufd_ps(_a, _mask) _mm_shuffle_ps((_a), (_a), (_mask))
|
||||||
|
#define bt_splat3_ps(_a, _i) bt_pshufd_ps((_a), BT_SHUFFLE(_i, _i, _i, 3))
|
|
@ -27,6 +27,7 @@ vcpkg_from_github(
|
||||||
REF ab8f16961e19a86ee20c6a1d61f662392524cc77
|
REF ab8f16961e19a86ee20c6a1d61f662392524cc77
|
||||||
SHA512 927742db29867517283d45e475f0c534a9a57e165cae221f26e08e88057253a1682ac9919b2dc547b9cf388ba0b931b175623461d44f28c9184796ba90b1ed55
|
SHA512 927742db29867517283d45e475f0c534a9a57e165cae221f26e08e88057253a1682ac9919b2dc547b9cf388ba0b931b175623461d44f28c9184796ba90b1ed55
|
||||||
HEAD_REF master
|
HEAD_REF master
|
||||||
|
PATCHES "bullet-git-fix-build-clang-8.patch"
|
||||||
)
|
)
|
||||||
|
|
||||||
vcpkg_configure_cmake(
|
vcpkg_configure_cmake(
|
||||||
|
|
|
@ -5251,7 +5251,7 @@
|
||||||
{
|
{
|
||||||
"easingType": "easeInOutQuad",
|
"easingType": "easeInOutQuad",
|
||||||
"id": "idle",
|
"id": "idle",
|
||||||
"interpDuration": 20,
|
"interpDuration": 15,
|
||||||
"interpTarget": 20,
|
"interpTarget": 20,
|
||||||
"interpType": "evaluateBoth",
|
"interpType": "evaluateBoth",
|
||||||
"transitions": [
|
"transitions": [
|
||||||
|
@ -5383,8 +5383,8 @@
|
||||||
{
|
{
|
||||||
"easingType": "easeInOutQuad",
|
"easingType": "easeInOutQuad",
|
||||||
"id": "idleSettle",
|
"id": "idleSettle",
|
||||||
"interpDuration": 15,
|
"interpDuration": 13,
|
||||||
"interpTarget": 15,
|
"interpTarget": 14,
|
||||||
"interpType": "snapshotPrev",
|
"interpType": "snapshotPrev",
|
||||||
"transitions": [
|
"transitions": [
|
||||||
{
|
{
|
||||||
|
@ -5477,7 +5477,7 @@
|
||||||
"transitions": [
|
"transitions": [
|
||||||
{
|
{
|
||||||
"state": "idleSettle",
|
"state": "idleSettle",
|
||||||
"var": "isNotInput"
|
"var": "isNotInputSlow"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": "WALKBWD",
|
"state": "WALKBWD",
|
||||||
|
@ -5541,7 +5541,7 @@
|
||||||
"transitions": [
|
"transitions": [
|
||||||
{
|
{
|
||||||
"state": "idleSettle",
|
"state": "idleSettle",
|
||||||
"var": "isNotInput"
|
"var": "isNotInputSlow"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": "WALKFWD",
|
"state": "WALKFWD",
|
||||||
|
@ -5605,7 +5605,7 @@
|
||||||
"transitions": [
|
"transitions": [
|
||||||
{
|
{
|
||||||
"state": "idleSettle",
|
"state": "idleSettle",
|
||||||
"var": "isNotInput"
|
"var": "isNotInputSlow"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": "WALKFWD",
|
"state": "WALKFWD",
|
||||||
|
@ -5669,7 +5669,7 @@
|
||||||
"transitions": [
|
"transitions": [
|
||||||
{
|
{
|
||||||
"state": "idleSettle",
|
"state": "idleSettle",
|
||||||
"var": "isNotInput"
|
"var": "isNotInputSlow"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": "WALKFWD",
|
"state": "WALKFWD",
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import QtQuick 2.10
|
import QtQuick 2.10
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
import "../simplifiedConstants" as SimplifiedConstants
|
import "../simplifiedConstants" as SimplifiedConstants
|
||||||
import "../simplifiedControls" as SimplifiedControls
|
import "../simplifiedControls" as SimplifiedControls
|
||||||
import "./components" as AvatarAppComponents
|
import "./components" as AvatarAppComponents
|
||||||
|
@ -79,7 +80,11 @@ Rectangle {
|
||||||
errorText.text = "There was a problem while retrieving your inventory. " +
|
errorText.text = "There was a problem while retrieving your inventory. " +
|
||||||
"Please try closing and re-opening the Avatar app.\n\nInventory status: " + result.status + "\nMessage: " + result.message;
|
"Please try closing and re-opening the Avatar app.\n\nInventory status: " + result.status + "\nMessage: " + result.message;
|
||||||
} else if (result.data && result.data.assets && result.data.assets.length === 0 && avatarAppInventoryModel.count === 0) {
|
} else if (result.data && result.data.assets && result.data.assets.length === 0 && avatarAppInventoryModel.count === 0) {
|
||||||
errorText.text = "You have not created any avatars yet! Create an avatar with the Avatar Creator, then close and re-open the Avatar App."
|
emptyInventoryContainer.visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Settings.getValue("simplifiedUI/debugFTUE", 0) === 4) {
|
||||||
|
emptyInventoryContainer.visible = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
avatarAppInventoryModel.handlePage(result.status !== "success" && result.message, result);
|
avatarAppInventoryModel.handlePage(result.status !== "success" && result.message, result);
|
||||||
|
@ -140,8 +145,95 @@ Rectangle {
|
||||||
anchors.rightMargin: 24
|
anchors.rightMargin: 24
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: emptyInventoryContainer
|
||||||
|
visible: false
|
||||||
|
anchors.top: displayNameHeader.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
|
||||||
|
Flickable {
|
||||||
|
id: emptyInventoryFlickable
|
||||||
|
anchors.fill: parent
|
||||||
|
contentWidth: parent.width
|
||||||
|
contentHeight: emptyInventoryLayout.height
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: emptyInventoryLayout
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 26
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 26
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
HifiStylesUit.GraphikSemiBold {
|
||||||
|
text: "Stand out from the crowd!"
|
||||||
|
Layout.preferredWidth: parent.width
|
||||||
|
Layout.preferredHeight: paintedHeight
|
||||||
|
Layout.topMargin: 16
|
||||||
|
size: 28
|
||||||
|
color: simplifiedUI.colors.text.white
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
}
|
||||||
|
|
||||||
|
HifiStylesUit.GraphikRegular {
|
||||||
|
text: "Create your custom avatar."
|
||||||
|
Layout.preferredWidth: parent.width
|
||||||
|
Layout.preferredHeight: paintedHeight
|
||||||
|
Layout.topMargin: 2
|
||||||
|
size: 18
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
color: simplifiedUI.colors.text.white
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: avatarImage;
|
||||||
|
source: "images/avatarProfilePic.png"
|
||||||
|
Layout.preferredWidth: parent.width
|
||||||
|
Layout.preferredHeight: 450
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
mipmap: true
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
source: "images/qrCode.jpg"
|
||||||
|
Layout.preferredWidth: 190
|
||||||
|
Layout.preferredHeight: 190
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.topMargin: -160
|
||||||
|
mipmap: true
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
}
|
||||||
|
|
||||||
|
HifiStylesUit.GraphikSemiBold {
|
||||||
|
text: "Scan for Mobile App"
|
||||||
|
Layout.preferredWidth: parent.width
|
||||||
|
Layout.preferredHeight: paintedHeight
|
||||||
|
Layout.topMargin: 12
|
||||||
|
size: 28
|
||||||
|
color: simplifiedUI.colors.text.white
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SimplifiedControls.VerticalScrollBar {
|
||||||
|
parent: emptyInventoryFlickable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: avatarInfoTextContainer
|
id: avatarInfoTextContainer
|
||||||
|
visible: !emptyInventoryContainer.visible
|
||||||
width: parent.implicitWidth
|
width: parent.implicitWidth
|
||||||
height: childrenRect.height
|
height: childrenRect.height
|
||||||
anchors.top: displayNameHeader.bottom
|
anchors.top: displayNameHeader.bottom
|
||||||
|
@ -164,7 +256,7 @@ Rectangle {
|
||||||
id: yourAvatarsSubtitle
|
id: yourAvatarsSubtitle
|
||||||
text: "These are the avatars that you've created and uploaded via the Avatar Creator."
|
text: "These are the avatars that you've created and uploaded via the Avatar Creator."
|
||||||
width: parent.width
|
width: parent.width
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.Wrap
|
||||||
anchors.top: yourAvatarsTitle.bottom
|
anchors.top: yourAvatarsTitle.bottom
|
||||||
anchors.topMargin: 6
|
anchors.topMargin: 6
|
||||||
verticalAlignment: TextInput.AlignVCenter
|
verticalAlignment: TextInput.AlignVCenter
|
||||||
|
@ -208,9 +300,10 @@ Rectangle {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
|
visible: !emptyInventoryContainer.visible
|
||||||
|
|
||||||
AnimatedImage {
|
AnimatedImage {
|
||||||
visible: !inventoryContentsList.visible && !errorText.visible
|
visible: !(inventoryContentsList.visible || errorText.visible)
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
width: 72
|
width: 72
|
||||||
height: width
|
height: width
|
||||||
|
@ -271,6 +364,8 @@ Rectangle {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
root.avatarPreviewUrl = "../../images/defaultAvatar.svg";
|
||||||
}
|
}
|
||||||
|
|
||||||
function fromScript(message) {
|
function fromScript(message) {
|
||||||
|
|
After Width: | Height: | Size: 319 KiB |
After Width: | Height: | Size: 159 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 145 KiB |
After Width: | Height: | Size: 151 KiB |
After Width: | Height: | Size: 150 KiB |
After Width: | Height: | Size: 148 KiB |
After Width: | Height: | Size: 138 KiB |
After Width: | Height: | Size: 148 KiB |
|
@ -54,8 +54,8 @@ Rectangle {
|
||||||
|
|
||||||
if ((MyAvatar.skeletonModelURL.indexOf("defaultAvatar") > -1 || MyAvatar.skeletonModelURL.indexOf("fst") === -1) &&
|
if ((MyAvatar.skeletonModelURL.indexOf("defaultAvatar") > -1 || MyAvatar.skeletonModelURL.indexOf("fst") === -1) &&
|
||||||
topBarInventoryModel.count > 0) {
|
topBarInventoryModel.count > 0) {
|
||||||
Settings.setValue("simplifiedUI/alreadyAutoSelectedAvatar", true);
|
Settings.setValue("simplifiedUI/alreadyAutoSelectedAvatarFromInventory", true);
|
||||||
MyAvatar.useFullAvatarURL = topBarInventoryModel.get(0).download_url;
|
MyAvatar.useFullAvatarURL(topBarInventoryModel.get(0).download_url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,7 @@ Rectangle {
|
||||||
if (isLoggedIn) {
|
if (isLoggedIn) {
|
||||||
Commerce.getWalletStatus();
|
Commerce.getWalletStatus();
|
||||||
} else {
|
} else {
|
||||||
// Show some error to the user
|
// Show some error to the user in the UI?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,12 +113,68 @@ Rectangle {
|
||||||
topBarInventoryModel.getNextPage();
|
topBarInventoryModel.getNextPage();
|
||||||
} else {
|
} else {
|
||||||
inventoryFullyReceived = true;
|
inventoryFullyReceived = true;
|
||||||
|
var scriptExecutionCount = Settings.getValue("simplifiedUI/SUIScriptExecutionCount");
|
||||||
|
var currentAvatarURL = MyAvatar.skeletonModelURL;
|
||||||
|
var currentAvatarURLContainsDefaultAvatar = currentAvatarURL.indexOf("defaultAvatar") > -1;
|
||||||
|
var currentAvatarURLContainsFST = currentAvatarURL.indexOf("fst") > -1;
|
||||||
|
var currentAvatarURLContainsSimplifiedAvatar = currentAvatarURL.indexOf("simplifiedAvatar") > -1;
|
||||||
|
var alreadyAutoSelectedAvatarFromInventory = Settings.getValue("simplifiedUI/alreadyAutoSelectedAvatarFromInventory", false);
|
||||||
|
var userHasValidAvatarInInventory = topBarInventoryModel.count > 0 &&
|
||||||
|
topBarInventoryModel.get(0).download_url.indexOf(".fst") > -1;
|
||||||
|
var simplifiedAvatarPrefix = "https://content.highfidelity.com/Experiences/Releases/simplifiedUI/simplifiedFTUE/avatars/simplifiedAvatar_";
|
||||||
|
var simplifiedAvatarColors = ["Blue", "Cyan", "Green", "Magenta", "Red"];
|
||||||
|
var simplifiedAvatarSuffix = "/avatar.fst";
|
||||||
|
|
||||||
// If we have an avatar in our inventory AND we haven't already auto-selected an avatar...
|
// Use `Settings.setValue("simplifiedUI/debugFTUE", 0);` to turn off FTUE Debug Mode.
|
||||||
if ((!Settings.getValue("simplifiedUI/alreadyAutoSelectedAvatar", false) ||
|
// Use `Settings.setValue("simplifiedUI/debugFTUE", 1);` to debug FTUE Screen 1.
|
||||||
MyAvatar.skeletonModelURL.indexOf("defaultAvatar") > -1 || MyAvatar.skeletonModelURL.indexOf("fst") === -1) && topBarInventoryModel.count > 0) {
|
// Use `Settings.setValue("simplifiedUI/debugFTUE", 2);` to debug FTUE Screen 2.
|
||||||
Settings.setValue("simplifiedUI/alreadyAutoSelectedAvatar", true);
|
// Use `Settings.setValue("simplifiedUI/debugFTUE", 3);` to debug FTUE Screen 3.
|
||||||
MyAvatar.skeletonModelURL = topBarInventoryModel.get(0).download_url;
|
// Use `Settings.setValue("simplifiedUI/debugFTUE", 4);` to force the UI to show what would happen if the user had an empty Inventory.
|
||||||
|
|
||||||
|
var debugFTUE = Settings.getValue("simplifiedUI/debugFTUE", 0);
|
||||||
|
if (debugFTUE === 1 || debugFTUE === 2) {
|
||||||
|
scriptExecutionCount = 1;
|
||||||
|
currentAvatarURLContainsDefaultAvatar = true;
|
||||||
|
if (debugFTUE === 1) {
|
||||||
|
userHasValidAvatarInInventory = false;
|
||||||
|
currentAvatarURLContainsSimplifiedAvatar = false;
|
||||||
|
}
|
||||||
|
} else if (debugFTUE === 3) {
|
||||||
|
scriptExecutionCount = 2;
|
||||||
|
currentAvatarURLContainsDefaultAvatar = false;
|
||||||
|
currentAvatarURLContainsSimplifiedAvatar = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have never auto-selected and the user is still using a default avatar or if the current avatar is not valid (fst), or if
|
||||||
|
// the current avatar is the old default (Woody), use top avatar from inventory or one of the new defaults.
|
||||||
|
|
||||||
|
// If the current avatar URL is invalid, OR the user is using the "default avatar" (Woody)...
|
||||||
|
if (!currentAvatarURLContainsFST || currentAvatarURLContainsDefaultAvatar) {
|
||||||
|
// If the user has a valid avatar in their inventory...
|
||||||
|
if (userHasValidAvatarInInventory) {
|
||||||
|
// ...use the first avatar in the user's inventory.
|
||||||
|
MyAvatar.useFullAvatarURL(topBarInventoryModel.get(0).download_url);
|
||||||
|
Settings.setValue("simplifiedUI/alreadyAutoSelectedAvatarFromInventory", true);
|
||||||
|
// Else if the user isn't wearing a "Simplified Avatar"
|
||||||
|
} else if (!currentAvatarURLContainsSimplifiedAvatar) {
|
||||||
|
// ...assign to the user a new "Simplified Avatar" (i.e. a simple avatar of random color)
|
||||||
|
var avatarColor = simplifiedAvatarColors[Math.floor(Math.random() * simplifiedAvatarColors.length)];
|
||||||
|
var simplifiedAvatarModelURL = simplifiedAvatarPrefix + avatarColor + simplifiedAvatarSuffix;
|
||||||
|
MyAvatar.useFullAvatarURL(simplifiedAvatarModelURL);
|
||||||
|
currentAvatarURLContainsSimplifiedAvatar = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scriptExecutionCount === 1) {
|
||||||
|
sendToScript({
|
||||||
|
"source": "SimplifiedTopBar.qml",
|
||||||
|
"method": "displayInitialLaunchWindow"
|
||||||
|
});
|
||||||
|
} else if (scriptExecutionCount === 2 && currentAvatarURLContainsSimplifiedAvatar) {
|
||||||
|
sendToScript({
|
||||||
|
"source": "SimplifiedTopBar.qml",
|
||||||
|
"method": "displaySecondLaunchWindow"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -556,7 +612,7 @@ Rectangle {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function updatePreviewUrl() {
|
function updatePreviewUrl() {
|
||||||
var previewUrl = "";
|
var previewUrl = "";
|
||||||
var downloadUrl = "";
|
var downloadUrl = "";
|
||||||
for (var i = 0; i < topBarInventoryModel.count; ++i) {
|
for (var i = 0; i < topBarInventoryModel.count; ++i) {
|
||||||
|
@ -570,6 +626,8 @@ Rectangle {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
avatarButtonImage.source = "../images/defaultAvatar.svg";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3618,7 +3618,7 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) {
|
||||||
_myCamera.setPosition(extractTranslation(camMat));
|
_myCamera.setPosition(extractTranslation(camMat));
|
||||||
_myCamera.setOrientation(glmExtractRotation(camMat));
|
_myCamera.setOrientation(glmExtractRotation(camMat));
|
||||||
} else {
|
} else {
|
||||||
_myCamera.setPosition(myAvatar->getLookAtPivotPoint());
|
_myCamera.setPosition(myAvatar->getCameraEyesPosition(deltaTime));
|
||||||
_myCamera.setOrientation(myAvatar->getLookAtRotation());
|
_myCamera.setOrientation(myAvatar->getLookAtRotation());
|
||||||
}
|
}
|
||||||
} else if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE) {
|
} else if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE) {
|
||||||
|
|
|
@ -152,7 +152,7 @@ static int triggerReactionNameToIndex(const QString& reactionName) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static int beginEndReactionNameToIndex(const QString& reactionName) {
|
static int beginEndReactionNameToIndex(const QString& reactionName) {
|
||||||
assert(NUM_AVATAR_BEGIN_END_REACTIONS == TRIGGER_REACTION_NAMES.size());
|
assert(NUM_AVATAR_BEGIN_END_REACTIONS == BEGIN_END_REACTION_NAMES.size());
|
||||||
return BEGIN_END_REACTION_NAMES.indexOf(reactionName);
|
return BEGIN_END_REACTION_NAMES.indexOf(reactionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2719,7 +2719,7 @@ void MyAvatar::updateMotors() {
|
||||||
if (_characterController.getState() == CharacterController::State::Hover ||
|
if (_characterController.getState() == CharacterController::State::Hover ||
|
||||||
_characterController.computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS) {
|
_characterController.computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS) {
|
||||||
CameraMode mode = qApp->getCamera().getMode();
|
CameraMode mode = qApp->getCamera().getMode();
|
||||||
if (mode == CAMERA_MODE_FIRST_PERSON || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE) {
|
if (!qApp->isHMDMode() && (mode == CAMERA_MODE_FIRST_PERSON || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE)) {
|
||||||
motorRotation = getLookAtRotation();
|
motorRotation = getLookAtRotation();
|
||||||
} else {
|
} else {
|
||||||
motorRotation = getMyHead()->getHeadOrientation();
|
motorRotation = getMyHead()->getHeadOrientation();
|
||||||
|
@ -6812,6 +6812,50 @@ glm::vec3 MyAvatar::getLookAtPivotPoint() {
|
||||||
return yAxisEyePosition;
|
return yAxisEyePosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glm::vec3 MyAvatar::getCameraEyesPosition(float deltaTime) {
|
||||||
|
glm::vec3 defaultEyesPosition = getLookAtPivotPoint();
|
||||||
|
if (isFlying()) {
|
||||||
|
return defaultEyesPosition;
|
||||||
|
}
|
||||||
|
glm::vec3 avatarFrontVector = getWorldOrientation() * Vectors::FRONT;
|
||||||
|
glm::vec3 avatarUpVector = getWorldOrientation() * Vectors::UP;
|
||||||
|
// Compute the offset between the default and real eye positions.
|
||||||
|
glm::vec3 defaultEyesToEyesVector = getHead()->getEyePosition() - defaultEyesPosition;
|
||||||
|
float FRONT_OFFSET_IDLE_MULTIPLIER = 2.0f;
|
||||||
|
float FRONT_OFFSET_JUMP_MULTIPLIER = 1.5f;
|
||||||
|
float frontOffset = FRONT_OFFSET_IDLE_MULTIPLIER * glm::length(defaultEyesPosition - getDefaultEyePosition());
|
||||||
|
|
||||||
|
// Looking down will aproximate move the camera forward to meet the real eye position
|
||||||
|
float mixAlpha = glm::dot(_lookAtPitch * Vectors::FRONT, -avatarUpVector);
|
||||||
|
|
||||||
|
// When jumping the camera should follow the real eye on the Y coordenate
|
||||||
|
float upOffset = 0.0f;
|
||||||
|
if (isJumping() || _characterController.getState() == CharacterController::State::Takeoff) {
|
||||||
|
upOffset = glm::dot(defaultEyesToEyesVector, avatarUpVector);
|
||||||
|
|
||||||
|
frontOffset = glm::dot(defaultEyesToEyesVector, avatarFrontVector) * FRONT_OFFSET_JUMP_MULTIPLIER;
|
||||||
|
mixAlpha = 1.0f;
|
||||||
|
} else {
|
||||||
|
// Limit the range effect from 45 to 90 degrees
|
||||||
|
const float HEAD_OFFSET_DOT_THRESHOLD = 0.7f; // 45 degrees aprox
|
||||||
|
mixAlpha = mixAlpha < HEAD_OFFSET_DOT_THRESHOLD ? 0.0f : (mixAlpha - HEAD_OFFSET_DOT_THRESHOLD) /
|
||||||
|
(1.0f - HEAD_OFFSET_DOT_THRESHOLD);
|
||||||
|
}
|
||||||
|
const float FPS = 60.0f;
|
||||||
|
float timeScale = deltaTime * FPS;
|
||||||
|
frontOffset = frontOffset < 0.0f ? 0.0f : mixAlpha * frontOffset;
|
||||||
|
glm::vec3 cameraOffset = upOffset * Vectors::UP + frontOffset * Vectors::FRONT;
|
||||||
|
const float TAU = 0.1f;
|
||||||
|
_cameraEyesOffset = _cameraEyesOffset + (cameraOffset - _cameraEyesOffset) * min(1.0f, TAU * timeScale);
|
||||||
|
glm::vec3 estimatedCameraPosition = defaultEyesPosition + getWorldOrientation() * _cameraEyesOffset;
|
||||||
|
return estimatedCameraPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MyAvatar::isJumping() {
|
||||||
|
return (_characterController.getState() == CharacterController::State::InAir ||
|
||||||
|
_characterController.getState() == CharacterController::State::Takeoff) && !isFlying();
|
||||||
|
}
|
||||||
|
|
||||||
bool MyAvatar::setPointAt(const glm::vec3& pointAtTarget) {
|
bool MyAvatar::setPointAt(const glm::vec3& pointAtTarget) {
|
||||||
if (QThread::currentThread() != thread()) {
|
if (QThread::currentThread() != thread()) {
|
||||||
bool result = false;
|
bool result = false;
|
||||||
|
|
|
@ -1919,6 +1919,8 @@ public:
|
||||||
|
|
||||||
bool getIsJointOverridden(int jointIndex) const;
|
bool getIsJointOverridden(int jointIndex) const;
|
||||||
glm::vec3 getLookAtPivotPoint();
|
glm::vec3 getLookAtPivotPoint();
|
||||||
|
glm::vec3 getCameraEyesPosition(float deltaTime);
|
||||||
|
bool isJumping();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
|
||||||
|
@ -2973,6 +2975,8 @@ private:
|
||||||
|
|
||||||
// used to prevent character from jumping after endSit is called.
|
// used to prevent character from jumping after endSit is called.
|
||||||
bool _endSitKeyPressComplete { false };
|
bool _endSitKeyPressComplete { false };
|
||||||
|
|
||||||
|
glm::vec3 _cameraEyesOffset;
|
||||||
};
|
};
|
||||||
|
|
||||||
QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode);
|
QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode);
|
||||||
|
|
|
@ -52,6 +52,21 @@ static const QVariantMap DOCK_AREA {
|
||||||
{ "RIGHT", DockArea::RIGHT }
|
{ "RIGHT", DockArea::RIGHT }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* The possible "relative position anchors" of an <code>InteractiveWindow</code>. Used when defining the `relativePosition` property of an `InteractiveWindow`.
|
||||||
|
* @typedef {object} InteractiveWindow.RelativePositionAnchors
|
||||||
|
* @property {InteractiveWindow.RelativePositionAnchor} TOP_LEFT - Specifies that the `relativePosition` of the `InteractiveWindow` will be offset from the top left of the Interface window.
|
||||||
|
* @property {InteractiveWindow.RelativePositionAnchor} TOP_RIGHT - Specifies that the `relativePosition` of the `InteractiveWindow` will be offset from the top right of the Interface window.
|
||||||
|
* @property {InteractiveWindow.RelativePositionAnchor} BOTTOM_RIGHT - Specifies that the `relativePosition` of the `InteractiveWindow` will be offset from the bottom right of the Interface window.
|
||||||
|
* @property {InteractiveWindow.RelativePositionAnchor} BOTTOM_LEFT - Specifies that the `relativePosition` of the `InteractiveWindow` will be offset from the bottom left of the Interface window.
|
||||||
|
*/
|
||||||
|
static const QVariantMap RELATIVE_POSITION_ANCHOR {
|
||||||
|
{ "TOP_LEFT", RelativePositionAnchor::TOP_LEFT },
|
||||||
|
{ "TOP_RIGHT", RelativePositionAnchor::TOP_RIGHT },
|
||||||
|
{ "BOTTOM_RIGHT", RelativePositionAnchor::BOTTOM_RIGHT },
|
||||||
|
{ "BOTTOM_LEFT", RelativePositionAnchor::BOTTOM_LEFT }
|
||||||
|
};
|
||||||
|
|
||||||
DesktopScriptingInterface::DesktopScriptingInterface(QObject* parent, bool restricted)
|
DesktopScriptingInterface::DesktopScriptingInterface(QObject* parent, bool restricted)
|
||||||
: QObject(parent), _restricted(restricted) { }
|
: QObject(parent), _restricted(restricted) { }
|
||||||
|
|
||||||
|
@ -99,6 +114,10 @@ QVariantMap DesktopScriptingInterface::getDockArea() {
|
||||||
return DOCK_AREA;
|
return DOCK_AREA;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVariantMap DesktopScriptingInterface::getRelativePositionAnchor() {
|
||||||
|
return RELATIVE_POSITION_ANCHOR;
|
||||||
|
}
|
||||||
|
|
||||||
void DesktopScriptingInterface::setHUDAlpha(float alpha) {
|
void DesktopScriptingInterface::setHUDAlpha(float alpha) {
|
||||||
qApp->getApplicationCompositor().setAlpha(alpha);
|
qApp->getApplicationCompositor().setAlpha(alpha);
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,9 @@
|
||||||
* @property {InteractiveWindow.DockAreas} DockArea - The possible docking locations of an {@link InteractiveWindow}: top,
|
* @property {InteractiveWindow.DockAreas} DockArea - The possible docking locations of an {@link InteractiveWindow}: top,
|
||||||
* bottom, left, or right of the Interface window.
|
* bottom, left, or right of the Interface window.
|
||||||
* <em>Read-only.</em>
|
* <em>Read-only.</em>
|
||||||
|
* @property {InteractiveWindow.RelativePositionAnchors} RelativePositionAnchor - The possible "relative position anchors" for an {@link InteractiveWindow}: top left,
|
||||||
|
* top right, bottom right, or bottom left of the Interface window.
|
||||||
|
* <em>Read-only.</em>
|
||||||
*/
|
*/
|
||||||
class DesktopScriptingInterface : public QObject, public Dependency {
|
class DesktopScriptingInterface : public QObject, public Dependency {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -50,6 +53,7 @@ class DesktopScriptingInterface : public QObject, public Dependency {
|
||||||
|
|
||||||
Q_PROPERTY(QVariantMap PresentationMode READ getPresentationMode CONSTANT FINAL)
|
Q_PROPERTY(QVariantMap PresentationMode READ getPresentationMode CONSTANT FINAL)
|
||||||
Q_PROPERTY(QVariantMap DockArea READ getDockArea CONSTANT FINAL)
|
Q_PROPERTY(QVariantMap DockArea READ getDockArea CONSTANT FINAL)
|
||||||
|
Q_PROPERTY(QVariantMap RelativePositionAnchor READ getRelativePositionAnchor CONSTANT FINAL)
|
||||||
Q_PROPERTY(int ALWAYS_ON_TOP READ flagAlwaysOnTop CONSTANT FINAL)
|
Q_PROPERTY(int ALWAYS_ON_TOP READ flagAlwaysOnTop CONSTANT FINAL)
|
||||||
Q_PROPERTY(int CLOSE_BUTTON_HIDES READ flagCloseButtonHides CONSTANT FINAL)
|
Q_PROPERTY(int CLOSE_BUTTON_HIDES READ flagCloseButtonHides CONSTANT FINAL)
|
||||||
|
|
||||||
|
@ -106,7 +110,7 @@ private:
|
||||||
Q_INVOKABLE InteractiveWindowPointer createWindowOnThread(const QString& sourceUrl, const QVariantMap& properties, QThread* targetThread);
|
Q_INVOKABLE InteractiveWindowPointer createWindowOnThread(const QString& sourceUrl, const QVariantMap& properties, QThread* targetThread);
|
||||||
|
|
||||||
static QVariantMap getDockArea();
|
static QVariantMap getDockArea();
|
||||||
|
static QVariantMap getRelativePositionAnchor();
|
||||||
Q_INVOKABLE static QVariantMap getPresentationMode();
|
Q_INVOKABLE static QVariantMap getPresentationMode();
|
||||||
const bool _restricted;
|
const bool _restricted;
|
||||||
};
|
};
|
||||||
|
|
|
@ -39,6 +39,9 @@ static const char* const ADDITIONAL_FLAGS_PROPERTY = "additionalFlags";
|
||||||
static const char* const OVERRIDE_FLAGS_PROPERTY = "overrideFlags";
|
static const char* const OVERRIDE_FLAGS_PROPERTY = "overrideFlags";
|
||||||
static const char* const SOURCE_PROPERTY = "source";
|
static const char* const SOURCE_PROPERTY = "source";
|
||||||
static const char* const TITLE_PROPERTY = "title";
|
static const char* const TITLE_PROPERTY = "title";
|
||||||
|
static const char* const RELATIVE_POSITION_ANCHOR_PROPERTY = "relativePositionAnchor";
|
||||||
|
static const char* const RELATIVE_POSITION_PROPERTY = "relativePosition";
|
||||||
|
static const char* const IS_FULL_SCREEN_WINDOW = "isFullScreenWindow";
|
||||||
static const char* const POSITION_PROPERTY = "position";
|
static const char* const POSITION_PROPERTY = "position";
|
||||||
static const char* const INTERACTIVE_WINDOW_POSITION_PROPERTY = "interactiveWindowPosition";
|
static const char* const INTERACTIVE_WINDOW_POSITION_PROPERTY = "interactiveWindowPosition";
|
||||||
static const char* const SIZE_PROPERTY = "size";
|
static const char* const SIZE_PROPERTY = "size";
|
||||||
|
@ -112,6 +115,14 @@ void InteractiveWindow::forwardKeyReleaseEvent(int key, int modifiers) {
|
||||||
QCoreApplication::postEvent(QCoreApplication::instance(), event);
|
QCoreApplication::postEvent(QCoreApplication::instance(), event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InteractiveWindow::onMainWindowGeometryChanged(QRect geometry) {
|
||||||
|
if (_isFullScreenWindow) {
|
||||||
|
repositionAndResizeFullScreenWindow();
|
||||||
|
} else {
|
||||||
|
setPositionUsingRelativePositionAndAnchor(geometry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void InteractiveWindow::emitMainWindowResizeEvent() {
|
void InteractiveWindow::emitMainWindowResizeEvent() {
|
||||||
emit qApp->getWindow()->windowGeometryChanged(qApp->getWindow()->geometry());
|
emit qApp->getWindow()->windowGeometryChanged(qApp->getWindow()->geometry());
|
||||||
}
|
}
|
||||||
|
@ -184,22 +195,32 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap
|
||||||
*/
|
*/
|
||||||
if (nativeWindowInfo.contains(DOCK_AREA_PROPERTY)) {
|
if (nativeWindowInfo.contains(DOCK_AREA_PROPERTY)) {
|
||||||
DockArea dockedArea = (DockArea) nativeWindowInfo[DOCK_AREA_PROPERTY].toInt();
|
DockArea dockedArea = (DockArea) nativeWindowInfo[DOCK_AREA_PROPERTY].toInt();
|
||||||
|
int tempWidth = 0;
|
||||||
|
int tempHeight = 0;
|
||||||
switch (dockedArea) {
|
switch (dockedArea) {
|
||||||
case DockArea::TOP:
|
case DockArea::TOP:
|
||||||
dockArea = Qt::TopDockWidgetArea;
|
dockArea = Qt::TopDockWidgetArea;
|
||||||
_dockWidget->setFixedHeight(windowSize.height());
|
tempHeight = windowSize.height();
|
||||||
|
_dockWidget->setFixedHeight(tempHeight);
|
||||||
|
qApp->getWindow()->setDockedWidgetRelativePositionOffset(QSize(0, -tempHeight));
|
||||||
break;
|
break;
|
||||||
case DockArea::BOTTOM:
|
case DockArea::BOTTOM:
|
||||||
dockArea = Qt::BottomDockWidgetArea;
|
dockArea = Qt::BottomDockWidgetArea;
|
||||||
_dockWidget->setFixedHeight(windowSize.height());
|
tempHeight = windowSize.height();
|
||||||
|
_dockWidget->setFixedHeight(tempHeight);
|
||||||
|
qApp->getWindow()->setDockedWidgetRelativePositionOffset(QSize(0, tempHeight));
|
||||||
break;
|
break;
|
||||||
case DockArea::LEFT:
|
case DockArea::LEFT:
|
||||||
dockArea = Qt::LeftDockWidgetArea;
|
dockArea = Qt::LeftDockWidgetArea;
|
||||||
_dockWidget->setFixedWidth(windowSize.width());
|
tempWidth = windowSize.width();
|
||||||
|
_dockWidget->setFixedWidth(tempWidth);
|
||||||
|
qApp->getWindow()->setDockedWidgetRelativePositionOffset(QSize(-tempWidth, 0));
|
||||||
break;
|
break;
|
||||||
case DockArea::RIGHT:
|
case DockArea::RIGHT:
|
||||||
dockArea = Qt::RightDockWidgetArea;
|
dockArea = Qt::RightDockWidgetArea;
|
||||||
_dockWidget->setFixedWidth(windowSize.width());
|
tempWidth = windowSize.width();
|
||||||
|
_dockWidget->setFixedWidth(tempWidth);
|
||||||
|
qApp->getWindow()->setDockedWidgetRelativePositionOffset(QSize(tempWidth, 0));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -255,6 +276,9 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap
|
||||||
if (properties.contains(TITLE_PROPERTY)) {
|
if (properties.contains(TITLE_PROPERTY)) {
|
||||||
object->setProperty(TITLE_PROPERTY, properties[TITLE_PROPERTY].toString());
|
object->setProperty(TITLE_PROPERTY, properties[TITLE_PROPERTY].toString());
|
||||||
}
|
}
|
||||||
|
if (properties.contains(VISIBLE_PROPERTY)) {
|
||||||
|
object->setProperty(VISIBLE_PROPERTY, properties[INTERACTIVE_WINDOW_VISIBLE_PROPERTY].toBool());
|
||||||
|
}
|
||||||
if (properties.contains(SIZE_PROPERTY)) {
|
if (properties.contains(SIZE_PROPERTY)) {
|
||||||
const auto size = vec2FromVariant(properties[SIZE_PROPERTY]);
|
const auto size = vec2FromVariant(properties[SIZE_PROPERTY]);
|
||||||
object->setProperty(INTERACTIVE_WINDOW_SIZE_PROPERTY, QSize(size.x, size.y));
|
object->setProperty(INTERACTIVE_WINDOW_SIZE_PROPERTY, QSize(size.x, size.y));
|
||||||
|
@ -263,8 +287,21 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap
|
||||||
const auto position = vec2FromVariant(properties[POSITION_PROPERTY]);
|
const auto position = vec2FromVariant(properties[POSITION_PROPERTY]);
|
||||||
object->setProperty(INTERACTIVE_WINDOW_POSITION_PROPERTY, QPointF(position.x, position.y));
|
object->setProperty(INTERACTIVE_WINDOW_POSITION_PROPERTY, QPointF(position.x, position.y));
|
||||||
}
|
}
|
||||||
if (properties.contains(VISIBLE_PROPERTY)) {
|
if (properties.contains(RELATIVE_POSITION_ANCHOR_PROPERTY)) {
|
||||||
object->setProperty(VISIBLE_PROPERTY, properties[INTERACTIVE_WINDOW_VISIBLE_PROPERTY].toBool());
|
_relativePositionAnchor = static_cast<RelativePositionAnchor>(properties[RELATIVE_POSITION_ANCHOR_PROPERTY].toInt());
|
||||||
|
}
|
||||||
|
if (properties.contains(RELATIVE_POSITION_PROPERTY)) {
|
||||||
|
_relativePosition = vec2FromVariant(properties[RELATIVE_POSITION_PROPERTY]);
|
||||||
|
setPositionUsingRelativePositionAndAnchor(qApp->getWindow()->geometry());
|
||||||
|
}
|
||||||
|
if (properties.contains(IS_FULL_SCREEN_WINDOW)) {
|
||||||
|
_isFullScreenWindow = properties[IS_FULL_SCREEN_WINDOW].toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_isFullScreenWindow) {
|
||||||
|
QRect geo = qApp->getWindow()->geometry();
|
||||||
|
object->setProperty(INTERACTIVE_WINDOW_POSITION_PROPERTY, QPointF(geo.x(), geo.y()));
|
||||||
|
object->setProperty(INTERACTIVE_WINDOW_SIZE_PROPERTY, QSize(geo.width(), geo.height()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// The qmlToScript method handles the thread-safety of this call. Because the QVariant argument
|
// The qmlToScript method handles the thread-safety of this call. Because the QVariant argument
|
||||||
|
@ -288,6 +325,8 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap
|
||||||
connect(object, SIGNAL(interactiveWindowVisibleChanged()), this, SLOT(parentNativeWindowToMainWindow()), Qt::QueuedConnection);
|
connect(object, SIGNAL(interactiveWindowVisibleChanged()), this, SLOT(parentNativeWindowToMainWindow()), Qt::QueuedConnection);
|
||||||
connect(object, SIGNAL(presentationModeChanged()), this, SLOT(parentNativeWindowToMainWindow()), Qt::QueuedConnection);
|
connect(object, SIGNAL(presentationModeChanged()), this, SLOT(parentNativeWindowToMainWindow()), Qt::QueuedConnection);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
connect(qApp->getWindow(), &MainWindow::windowGeometryChanged, this, &InteractiveWindow::onMainWindowGeometryChanged, Qt::QueuedConnection);
|
||||||
|
|
||||||
QUrl sourceURL{ sourceUrl };
|
QUrl sourceURL{ sourceUrl };
|
||||||
// If the passed URL doesn't correspond to a known scheme, assume it's a local file path
|
// If the passed URL doesn't correspond to a known scheme, assume it's a local file path
|
||||||
|
@ -414,6 +453,68 @@ void InteractiveWindow::setPosition(const glm::vec2& position) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RelativePositionAnchor InteractiveWindow::getRelativePositionAnchor() const {
|
||||||
|
return _relativePositionAnchor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InteractiveWindow::setRelativePositionAnchor(const RelativePositionAnchor& relativePositionAnchor) {
|
||||||
|
_relativePositionAnchor = relativePositionAnchor;
|
||||||
|
setPositionUsingRelativePositionAndAnchor(qApp->getWindow()->geometry());
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec2 InteractiveWindow::getRelativePosition() const {
|
||||||
|
return _relativePosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InteractiveWindow::setRelativePosition(const glm::vec2& relativePosition) {
|
||||||
|
_relativePosition = relativePosition;
|
||||||
|
setPositionUsingRelativePositionAndAnchor(qApp->getWindow()->geometry());
|
||||||
|
}
|
||||||
|
|
||||||
|
void InteractiveWindow::setPositionUsingRelativePositionAndAnchor(const QRect& mainWindowGeometry) {
|
||||||
|
RelativePositionAnchor relativePositionAnchor = getRelativePositionAnchor();
|
||||||
|
glm::vec2 relativePosition = getRelativePosition();
|
||||||
|
|
||||||
|
glm::vec2 newPosition;
|
||||||
|
|
||||||
|
switch (relativePositionAnchor) {
|
||||||
|
case RelativePositionAnchor::TOP_LEFT:
|
||||||
|
newPosition.x = mainWindowGeometry.x() + relativePosition.x;
|
||||||
|
newPosition.y = mainWindowGeometry.y() + relativePosition.y;
|
||||||
|
break;
|
||||||
|
case RelativePositionAnchor::TOP_RIGHT:
|
||||||
|
newPosition.x = mainWindowGeometry.x() + mainWindowGeometry.width() - relativePosition.x;
|
||||||
|
newPosition.y = mainWindowGeometry.y() + relativePosition.y;
|
||||||
|
break;
|
||||||
|
case RelativePositionAnchor::BOTTOM_RIGHT:
|
||||||
|
newPosition.x = mainWindowGeometry.x() + mainWindowGeometry.width() - relativePosition.x;
|
||||||
|
newPosition.y = mainWindowGeometry.y() + mainWindowGeometry.height() - relativePosition.y;
|
||||||
|
break;
|
||||||
|
case RelativePositionAnchor::BOTTOM_LEFT:
|
||||||
|
newPosition.x = mainWindowGeometry.x() + relativePosition.x;
|
||||||
|
newPosition.y = mainWindowGeometry.y() + mainWindowGeometry.height() - relativePosition.y;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we include the dimensions of the docked widget!
|
||||||
|
QSize dockedWidgetRelativePositionOffset = qApp->getWindow()->getDockedWidgetRelativePositionOffset();
|
||||||
|
newPosition.x = newPosition.x + dockedWidgetRelativePositionOffset.width();
|
||||||
|
newPosition.y = newPosition.y + dockedWidgetRelativePositionOffset.height();
|
||||||
|
|
||||||
|
if (_qmlWindowProxy) {
|
||||||
|
QMetaObject::invokeMethod(_qmlWindowProxy.get(), "writeProperty", Q_ARG(QString, INTERACTIVE_WINDOW_POSITION_PROPERTY),
|
||||||
|
Q_ARG(QVariant, QPointF(newPosition.x, newPosition.y)));
|
||||||
|
}
|
||||||
|
setPosition(newPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InteractiveWindow::repositionAndResizeFullScreenWindow() {
|
||||||
|
QRect windowGeometry = qApp->getWindow()->geometry();
|
||||||
|
|
||||||
|
setPosition(glm::vec2(windowGeometry.x(), windowGeometry.y()));
|
||||||
|
setSize(glm::vec2(windowGeometry.width(), windowGeometry.height()));
|
||||||
|
}
|
||||||
|
|
||||||
glm::vec2 InteractiveWindow::getSize() const {
|
glm::vec2 InteractiveWindow::getSize() const {
|
||||||
if (!_qmlWindowProxy) {
|
if (!_qmlWindowProxy) {
|
||||||
return {};
|
return {};
|
||||||
|
|
|
@ -89,6 +89,14 @@ namespace InteractiveWindowEnums {
|
||||||
RIGHT
|
RIGHT
|
||||||
};
|
};
|
||||||
Q_ENUM_NS(DockArea);
|
Q_ENUM_NS(DockArea);
|
||||||
|
|
||||||
|
enum RelativePositionAnchor {
|
||||||
|
TOP_LEFT,
|
||||||
|
TOP_RIGHT,
|
||||||
|
BOTTOM_RIGHT,
|
||||||
|
BOTTOM_LEFT
|
||||||
|
};
|
||||||
|
Q_ENUM_NS(RelativePositionAnchor);
|
||||||
}
|
}
|
||||||
|
|
||||||
using namespace InteractiveWindowEnums;
|
using namespace InteractiveWindowEnums;
|
||||||
|
@ -121,6 +129,8 @@ class InteractiveWindow : public QObject {
|
||||||
|
|
||||||
Q_PROPERTY(QString title READ getTitle WRITE setTitle)
|
Q_PROPERTY(QString title READ getTitle WRITE setTitle)
|
||||||
Q_PROPERTY(glm::vec2 position READ getPosition WRITE setPosition)
|
Q_PROPERTY(glm::vec2 position READ getPosition WRITE setPosition)
|
||||||
|
Q_PROPERTY(RelativePositionAnchor relativePositionAnchor READ getRelativePositionAnchor WRITE setRelativePositionAnchor)
|
||||||
|
Q_PROPERTY(glm::vec2 relativePosition READ getRelativePosition WRITE setRelativePosition)
|
||||||
Q_PROPERTY(glm::vec2 size READ getSize WRITE setSize)
|
Q_PROPERTY(glm::vec2 size READ getSize WRITE setSize)
|
||||||
Q_PROPERTY(bool visible READ isVisible WRITE setVisible)
|
Q_PROPERTY(bool visible READ isVisible WRITE setVisible)
|
||||||
Q_PROPERTY(int presentationMode READ getPresentationMode WRITE setPresentationMode)
|
Q_PROPERTY(int presentationMode READ getPresentationMode WRITE setPresentationMode)
|
||||||
|
@ -136,6 +146,21 @@ private:
|
||||||
|
|
||||||
Q_INVOKABLE glm::vec2 getPosition() const;
|
Q_INVOKABLE glm::vec2 getPosition() const;
|
||||||
Q_INVOKABLE void setPosition(const glm::vec2& position);
|
Q_INVOKABLE void setPosition(const glm::vec2& position);
|
||||||
|
|
||||||
|
RelativePositionAnchor _relativePositionAnchor{ RelativePositionAnchor::TOP_LEFT };
|
||||||
|
Q_INVOKABLE RelativePositionAnchor getRelativePositionAnchor() const;
|
||||||
|
Q_INVOKABLE void setRelativePositionAnchor(const RelativePositionAnchor& position);
|
||||||
|
|
||||||
|
// This "relative position" is relative to the "relative position anchor" and excludes the window frame.
|
||||||
|
// This position will ALWAYS include the geometry of a docked widget, if one is present.
|
||||||
|
glm::vec2 _relativePosition{ 0.0f, 0.0f };
|
||||||
|
Q_INVOKABLE glm::vec2 getRelativePosition() const;
|
||||||
|
Q_INVOKABLE void setRelativePosition(const glm::vec2& position);
|
||||||
|
|
||||||
|
Q_INVOKABLE void setPositionUsingRelativePositionAndAnchor(const QRect& mainWindowGeometry);
|
||||||
|
|
||||||
|
bool _isFullScreenWindow{ false };
|
||||||
|
Q_INVOKABLE void repositionAndResizeFullScreenWindow();
|
||||||
|
|
||||||
Q_INVOKABLE glm::vec2 getSize() const;
|
Q_INVOKABLE glm::vec2 getSize() const;
|
||||||
Q_INVOKABLE void setSize(const glm::vec2& size);
|
Q_INVOKABLE void setSize(const glm::vec2& size);
|
||||||
|
@ -320,6 +345,7 @@ protected slots:
|
||||||
void forwardKeyPressEvent(int key, int modifiers);
|
void forwardKeyPressEvent(int key, int modifiers);
|
||||||
void forwardKeyReleaseEvent(int key, int modifiers);
|
void forwardKeyReleaseEvent(int key, int modifiers);
|
||||||
void emitMainWindowResizeEvent();
|
void emitMainWindowResizeEvent();
|
||||||
|
void onMainWindowGeometryChanged(QRect geometry);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<QmlWindowProxy> _qmlWindowProxy;
|
std::shared_ptr<QmlWindowProxy> _qmlWindowProxy;
|
||||||
|
|
|
@ -55,6 +55,8 @@ public:
|
||||||
float getFrame() const { return _frame; }
|
float getFrame() const { return _frame; }
|
||||||
void loadURL(const QString& url);
|
void loadURL(const QString& url);
|
||||||
|
|
||||||
|
AnimBlendType getBlendType() const { return _blendType; };
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
virtual void setCurrentFrameInternal(float frame) override;
|
virtual void setCurrentFrameInternal(float frame) override;
|
||||||
|
|
|
@ -30,6 +30,34 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) {
|
||||||
|
|
||||||
// we make a copy of the inverseBindMatrices in order to prevent mutating the model bind pose
|
// we make a copy of the inverseBindMatrices in order to prevent mutating the model bind pose
|
||||||
// when we are dealing with a joint offset in the model
|
// when we are dealing with a joint offset in the model
|
||||||
|
for (int i = 0; i < (int)hfmModel.skinDeformers.size(); i++) {
|
||||||
|
const auto& defor = hfmModel.skinDeformers[i];
|
||||||
|
std::vector<HFMCluster> dummyClustersList;
|
||||||
|
|
||||||
|
for (int j = 0; j < defor.clusters.size(); j++) {
|
||||||
|
std::vector<glm::mat4> bindMatrices;
|
||||||
|
// cast into a non-const reference, so we can mutate the FBXCluster
|
||||||
|
HFMCluster& cluster = const_cast<HFMCluster&>(defor.clusters.at(j));
|
||||||
|
|
||||||
|
HFMCluster localCluster;
|
||||||
|
localCluster.jointIndex = cluster.jointIndex;
|
||||||
|
localCluster.inverseBindMatrix = cluster.inverseBindMatrix;
|
||||||
|
localCluster.inverseBindTransform.evalFromRawMatrix(localCluster.inverseBindMatrix);
|
||||||
|
|
||||||
|
// if we have a joint offset in the fst file then multiply its inverse by the
|
||||||
|
// model cluster inverse bind matrix
|
||||||
|
if (hfmModel.jointRotationOffsets.contains(cluster.jointIndex)) {
|
||||||
|
AnimPose localOffset(hfmModel.jointRotationOffsets[cluster.jointIndex], glm::vec3());
|
||||||
|
localCluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix;
|
||||||
|
localCluster.inverseBindTransform.evalFromRawMatrix(localCluster.inverseBindMatrix);
|
||||||
|
}
|
||||||
|
dummyClustersList.push_back(localCluster);
|
||||||
|
}
|
||||||
|
_clusterBindMatrixOriginalValues.push_back(dummyClustersList);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
for (int i = 0; i < (int)hfmModel.meshes.size(); i++) {
|
for (int i = 0; i < (int)hfmModel.meshes.size(); i++) {
|
||||||
const HFMMesh& mesh = hfmModel.meshes.at(i);
|
const HFMMesh& mesh = hfmModel.meshes.at(i);
|
||||||
std::vector<HFMCluster> dummyClustersList;
|
std::vector<HFMCluster> dummyClustersList;
|
||||||
|
@ -55,6 +83,7 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) {
|
||||||
}
|
}
|
||||||
_clusterBindMatrixOriginalValues.push_back(dummyClustersList);
|
_clusterBindMatrixOriginalValues.push_back(dummyClustersList);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimSkeleton::AnimSkeleton(const std::vector<HFMJoint>& joints, const QMap<int, glm::quat> jointOffsets) {
|
AnimSkeleton::AnimSkeleton(const std::vector<HFMJoint>& joints, const QMap<int, glm::quat> jointOffsets) {
|
||||||
|
|
|
@ -70,6 +70,8 @@ public:
|
||||||
std::vector<int> lookUpJointIndices(const std::vector<QString>& jointNames) const;
|
std::vector<int> lookUpJointIndices(const std::vector<QString>& jointNames) const;
|
||||||
const HFMCluster getClusterBindMatricesOriginalValues(const int meshIndex, const int clusterIndex) const { return _clusterBindMatrixOriginalValues[meshIndex][clusterIndex]; }
|
const HFMCluster getClusterBindMatricesOriginalValues(const int meshIndex, const int clusterIndex) const { return _clusterBindMatrixOriginalValues[meshIndex][clusterIndex]; }
|
||||||
|
|
||||||
|
// const HFMCluster getClusterBindMatricesOriginalValues(const int meshIndex, const int clusterIndex) const { return _clusterBindMatrixOriginalValues[meshIndex][clusterIndex]; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void buildSkeletonFromJoints(const std::vector<HFMJoint>& joints, const QMap<int, glm::quat> jointOffsets);
|
void buildSkeletonFromJoints(const std::vector<HFMJoint>& joints, const QMap<int, glm::quat> jointOffsets);
|
||||||
|
|
||||||
|
|
|
@ -545,7 +545,8 @@ QStringList Rig::getAnimationRoles() const {
|
||||||
auto clipNode = std::dynamic_pointer_cast<AnimClip>(node);
|
auto clipNode = std::dynamic_pointer_cast<AnimClip>(node);
|
||||||
if (clipNode) {
|
if (clipNode) {
|
||||||
// filter out the userAnims, they are for internal use only.
|
// filter out the userAnims, they are for internal use only.
|
||||||
if (!clipNode->getID().startsWith("userAnim")) {
|
// also don't return additive blend node clips as valid roles.
|
||||||
|
if (!clipNode->getID().startsWith("userAnim") && clipNode->getBlendType() == AnimBlendType_Normal) {
|
||||||
list.append(node->getID());
|
list.append(node->getID());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1432,6 +1433,69 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
|
||||||
}
|
}
|
||||||
_lastEnableInverseKinematics = _enableInverseKinematics;
|
_lastEnableInverseKinematics = _enableInverseKinematics;
|
||||||
|
|
||||||
|
|
||||||
|
//stategraph vars based on input
|
||||||
|
const float INPUT_DEADZONE_THRESHOLD = 0.05f;
|
||||||
|
const float SLOW_SPEED_THRESHOLD = 1.5f;
|
||||||
|
|
||||||
|
if (fabsf(_previousControllerParameters.inputX) <= INPUT_DEADZONE_THRESHOLD &&
|
||||||
|
fabsf(_previousControllerParameters.inputZ) <= INPUT_DEADZONE_THRESHOLD) {
|
||||||
|
// no WASD input
|
||||||
|
if (fabsf(forwardSpeed) <= SLOW_SPEED_THRESHOLD && fabsf(lateralSpeed) <= SLOW_SPEED_THRESHOLD) {
|
||||||
|
_animVars.set("isInputForward", false);
|
||||||
|
_animVars.set("isInputBackward", false);
|
||||||
|
_animVars.set("isInputRight", false);
|
||||||
|
_animVars.set("isInputLeft", false);
|
||||||
|
_animVars.set("isNotInput", true);
|
||||||
|
_animVars.set("isNotInputSlow", true);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
_animVars.set("isInputForward", false);
|
||||||
|
_animVars.set("isInputBackward", false);
|
||||||
|
_animVars.set("isInputRight", false);
|
||||||
|
_animVars.set("isInputLeft", false);
|
||||||
|
_animVars.set("isNotInput", true);
|
||||||
|
_animVars.set("isNotInputSlow", false);
|
||||||
|
}
|
||||||
|
} else if (fabsf(_previousControllerParameters.inputZ) >= fabsf(_previousControllerParameters.inputX)) {
|
||||||
|
if (_previousControllerParameters.inputZ > 0.0f) {
|
||||||
|
// forward
|
||||||
|
_animVars.set("isInputForward", true);
|
||||||
|
_animVars.set("isInputBackward", false);
|
||||||
|
_animVars.set("isInputRight", false);
|
||||||
|
_animVars.set("isInputLeft", false);
|
||||||
|
_animVars.set("isNotInput", false);
|
||||||
|
_animVars.set("isNotInputSlow", false);
|
||||||
|
} else {
|
||||||
|
// backward
|
||||||
|
_animVars.set("isInputForward", false);
|
||||||
|
_animVars.set("isInputBackward", true);
|
||||||
|
_animVars.set("isInputRight", false);
|
||||||
|
_animVars.set("isInputLeft", false);
|
||||||
|
_animVars.set("isNotInput", false);
|
||||||
|
_animVars.set("isNotInputSlow", false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (_previousControllerParameters.inputX > 0.0f) {
|
||||||
|
// right
|
||||||
|
_animVars.set("isInputForward", false);
|
||||||
|
_animVars.set("isInputBackward", false);
|
||||||
|
_animVars.set("isInputRight", true);
|
||||||
|
_animVars.set("isInputLeft", false);
|
||||||
|
_animVars.set("isNotInput", false);
|
||||||
|
_animVars.set("isNotInputSlow", false);
|
||||||
|
} else {
|
||||||
|
// left
|
||||||
|
_animVars.set("isInputForward", false);
|
||||||
|
_animVars.set("isInputBackward", false);
|
||||||
|
_animVars.set("isInputRight", false);
|
||||||
|
_animVars.set("isInputLeft", true);
|
||||||
|
_animVars.set("isNotInput", false);
|
||||||
|
_animVars.set("isNotInputSlow", false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
_lastForward = forward;
|
_lastForward = forward;
|
||||||
_lastPosition = worldPosition;
|
_lastPosition = worldPosition;
|
||||||
|
@ -2160,50 +2224,6 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//deadzone constant
|
|
||||||
const float INPUT_DEADZONE_THRESHOLD = 0.05f;
|
|
||||||
|
|
||||||
if (fabsf(params.inputX) <= INPUT_DEADZONE_THRESHOLD && fabsf(params.inputZ) <= INPUT_DEADZONE_THRESHOLD) {
|
|
||||||
// no WASD input
|
|
||||||
_animVars.set("isInputForward", false);
|
|
||||||
_animVars.set("isInputBackward", false);
|
|
||||||
_animVars.set("isInputRight", false);
|
|
||||||
_animVars.set("isInputLeft", false);
|
|
||||||
_animVars.set("isNotInput", true);
|
|
||||||
} else if (fabsf(params.inputZ) >= fabsf(params.inputX)) {
|
|
||||||
if (params.inputZ > 0.0f) {
|
|
||||||
// forward
|
|
||||||
_animVars.set("isInputForward", true);
|
|
||||||
_animVars.set("isInputBackward", false);
|
|
||||||
_animVars.set("isInputRight", false);
|
|
||||||
_animVars.set("isInputLeft", false);
|
|
||||||
_animVars.set("isNotInput", false);
|
|
||||||
} else {
|
|
||||||
// backward
|
|
||||||
_animVars.set("isInputForward", false);
|
|
||||||
_animVars.set("isInputBackward", true);
|
|
||||||
_animVars.set("isInputRight", false);
|
|
||||||
_animVars.set("isInputLeft", false);
|
|
||||||
_animVars.set("isNotInput", false);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (params.inputX > 0.0f) {
|
|
||||||
// right
|
|
||||||
_animVars.set("isInputForward", false);
|
|
||||||
_animVars.set("isInputBackward", false);
|
|
||||||
_animVars.set("isInputRight", true);
|
|
||||||
_animVars.set("isInputLeft", false);
|
|
||||||
_animVars.set("isNotInput", false);
|
|
||||||
} else {
|
|
||||||
// left
|
|
||||||
_animVars.set("isInputForward", false);
|
|
||||||
_animVars.set("isInputBackward", false);
|
|
||||||
_animVars.set("isInputRight", false);
|
|
||||||
_animVars.set("isInputLeft", true);
|
|
||||||
_animVars.set("isNotInput", false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
_headEnabled = params.primaryControllerFlags[PrimaryControllerType_Head] & (uint8_t)ControllerFlags::Enabled;
|
_headEnabled = params.primaryControllerFlags[PrimaryControllerType_Head] & (uint8_t)ControllerFlags::Enabled;
|
||||||
bool leftHandEnabled = params.primaryControllerFlags[PrimaryControllerType_LeftHand] & (uint8_t)ControllerFlags::Enabled;
|
bool leftHandEnabled = params.primaryControllerFlags[PrimaryControllerType_LeftHand] & (uint8_t)ControllerFlags::Enabled;
|
||||||
|
|
|
@ -88,8 +88,8 @@ public:
|
||||||
AnimPose secondaryControllerPoses[NumSecondaryControllerTypes]; // rig space
|
AnimPose secondaryControllerPoses[NumSecondaryControllerTypes]; // rig space
|
||||||
uint8_t secondaryControllerFlags[NumSecondaryControllerTypes];
|
uint8_t secondaryControllerFlags[NumSecondaryControllerTypes];
|
||||||
bool isTalking;
|
bool isTalking;
|
||||||
float inputX;
|
float inputX = 0.0f;
|
||||||
float inputZ;
|
float inputZ = 0.0f;
|
||||||
bool reactionEnabledFlags[NUM_AVATAR_BEGIN_END_REACTIONS];
|
bool reactionEnabledFlags[NUM_AVATAR_BEGIN_END_REACTIONS];
|
||||||
bool reactionTriggers[NUM_AVATAR_TRIGGER_REACTIONS];
|
bool reactionTriggers[NUM_AVATAR_TRIGGER_REACTIONS];
|
||||||
HFMJointShapeInfo hipsShapeInfo;
|
HFMJointShapeInfo hipsShapeInfo;
|
||||||
|
|
|
@ -335,8 +335,8 @@ namespace controller {
|
||||||
makeAxisPair(Action::STEP_PITCH, "StepPitch"),
|
makeAxisPair(Action::STEP_PITCH, "StepPitch"),
|
||||||
makeAxisPair(Action::STEP_ROLL, "StepRoll"),
|
makeAxisPair(Action::STEP_ROLL, "StepRoll"),
|
||||||
makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateX"),
|
makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateX"),
|
||||||
makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateY"),
|
makeAxisPair(Action::STEP_TRANSLATE_Y, "StepTranslateY"),
|
||||||
makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateZ"),
|
makeAxisPair(Action::STEP_TRANSLATE_Z, "StepTranslateZ"),
|
||||||
|
|
||||||
makePosePair(Action::LEFT_HAND, "LeftHand"),
|
makePosePair(Action::LEFT_HAND, "LeftHand"),
|
||||||
makePosePair(Action::RIGHT_HAND, "RightHand"),
|
makePosePair(Action::RIGHT_HAND, "RightHand"),
|
||||||
|
|
|
@ -1463,9 +1463,6 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const
|
||||||
// see if any materials have texture children
|
// see if any materials have texture children
|
||||||
bool materialsHaveTextures = checkMaterialsHaveTextures(_hfmMaterials, _textureFilenames, _connectionChildMap);
|
bool materialsHaveTextures = checkMaterialsHaveTextures(_hfmMaterials, _textureFilenames, _connectionChildMap);
|
||||||
|
|
||||||
// Note that the transforms in the TransformNodes are initially in world-space, and need to be converted to parent-space
|
|
||||||
std::vector<hfm::TransformNode> transformNodes;
|
|
||||||
|
|
||||||
for (QMap<QString, ExtractedMesh>::iterator it = meshes.begin(); it != meshes.end(); it++) {
|
for (QMap<QString, ExtractedMesh>::iterator it = meshes.begin(); it != meshes.end(); it++) {
|
||||||
const QString& meshID = it.key();
|
const QString& meshID = it.key();
|
||||||
const ExtractedMesh& extracted = it.value();
|
const ExtractedMesh& extracted = it.value();
|
||||||
|
@ -1509,7 +1506,7 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const
|
||||||
hfm::Shape& shape = partShapes[i];
|
hfm::Shape& shape = partShapes[i];
|
||||||
shape.mesh = meshIndex;
|
shape.mesh = meshIndex;
|
||||||
shape.meshPart = i;
|
shape.meshPart = i;
|
||||||
shape.transform = transformIndex;
|
shape.joint = transformIndex;
|
||||||
|
|
||||||
auto matName = mesh.parts[i].materialID;
|
auto matName = mesh.parts[i].materialID;
|
||||||
auto materialIt = materialNameToID.find(matName.toStdString());
|
auto materialIt = materialNameToID.find(matName.toStdString());
|
||||||
|
@ -1593,10 +1590,10 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const
|
||||||
}
|
}
|
||||||
|
|
||||||
// whether we're skinned depends on how many clusters are attached
|
// whether we're skinned depends on how many clusters are attached
|
||||||
if (clusterIDs.size() > 1) {
|
if (clusterIDs.size() > 0) {
|
||||||
hfm::DynamicTransform dynamicTransform;
|
hfm::SkinDeformer skinDeformer;
|
||||||
auto& clusters = dynamicTransform.clusters;
|
auto& clusters = skinDeformer.clusters;
|
||||||
std::vector<hfm::Deformer> deformers;
|
std::vector<hfm::SkinCluster> skinClusters;
|
||||||
for (const auto& clusterID : clusterIDs) {
|
for (const auto& clusterID : clusterIDs) {
|
||||||
HFMCluster hfmCluster;
|
HFMCluster hfmCluster;
|
||||||
const Cluster& fbxCluster = fbxClusters[clusterID];
|
const Cluster& fbxCluster = fbxClusters[clusterID];
|
||||||
|
@ -1642,38 +1639,43 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const
|
||||||
clusters.push_back(cluster);
|
clusters.push_back(cluster);
|
||||||
|
|
||||||
// Skinned mesh instances have a dynamic transform
|
// Skinned mesh instances have a dynamic transform
|
||||||
dynamicTransform.deformers.reserve(clusterIDs.size());
|
skinDeformer.skinClusterIndices.reserve(clusterIDs.size());
|
||||||
clusters.reserve(clusterIDs.size());
|
|
||||||
for (const auto& clusterID : clusterIDs) {
|
for (const auto& clusterID : clusterIDs) {
|
||||||
const Cluster& fbxCluster = fbxClusters[clusterID];
|
const Cluster& fbxCluster = fbxClusters[clusterID];
|
||||||
dynamicTransform.deformers.emplace_back();
|
skinDeformer.skinClusterIndices.emplace_back();
|
||||||
deformers.emplace_back();
|
skinClusters.emplace_back();
|
||||||
hfm::Deformer& deformer = deformers.back();
|
hfm::SkinCluster& skinCluster = skinClusters.back();
|
||||||
size_t indexWeightPairs = (size_t)std::min(fbxCluster.indices.size(), fbxCluster.weights.size());
|
size_t indexWeightPairs = (size_t)std::min(fbxCluster.indices.size(), fbxCluster.weights.size());
|
||||||
deformer.indices.reserve(indexWeightPairs);
|
skinCluster.indices.reserve(indexWeightPairs);
|
||||||
deformer.weights.reserve(indexWeightPairs);
|
skinCluster.weights.reserve(indexWeightPairs);
|
||||||
for (int i = 0; i < (int)indexWeightPairs; i++) {
|
|
||||||
int oldIndex = fbxCluster.indices[i];
|
for (int j = 0; j < fbxCluster.indices.size(); j++) {
|
||||||
uint32_t newIndex = (uint32_t)extracted.newIndices.value(oldIndex);
|
int oldIndex = fbxCluster.indices.at(j);
|
||||||
deformer.indices.push_back(newIndex);
|
float weight = fbxCluster.weights.at(j);
|
||||||
deformer.indices.push_back((float)fbxCluster.weights[i]);
|
for (QMultiHash<int, int>::const_iterator it = extracted.newIndices.constFind(oldIndex);
|
||||||
|
it != extracted.newIndices.end() && it.key() == oldIndex; it++) {
|
||||||
|
int newIndex = it.value();
|
||||||
|
|
||||||
|
skinCluster.indices.push_back(newIndex);
|
||||||
|
skinCluster.weights.push_back(weight);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store this model's deformers, this dynamic transform's deformer IDs
|
// Store this model's deformers, this dynamic transform's deformer IDs
|
||||||
uint32_t deformerMinID = (uint32_t)hfmModel.deformers.size();
|
uint32_t deformerMinID = (uint32_t)hfmModel.skinClusters.size();
|
||||||
hfmModel.deformers.insert(hfmModel.deformers.end(), deformers.cbegin(), deformers.cend());
|
hfmModel.skinClusters.insert(hfmModel.skinClusters.end(), skinClusters.cbegin(), skinClusters.cend());
|
||||||
dynamicTransform.deformers.resize(deformers.size());
|
skinDeformer.skinClusterIndices.resize(skinClusters.size());
|
||||||
std::iota(dynamicTransform.deformers.begin(), dynamicTransform.deformers.end(), deformerMinID);
|
std::iota(skinDeformer.skinClusterIndices.begin(), skinDeformer.skinClusterIndices.end(), deformerMinID);
|
||||||
|
|
||||||
// Store the model's dynamic transform, and put its ID in the shapes
|
// Store the model's dynamic transform, and put its ID in the shapes
|
||||||
hfmModel.dynamicTransforms.push_back(dynamicTransform);
|
hfmModel.skinDeformers.push_back(skinDeformer);
|
||||||
uint32_t dynamicTransformID = (uint32_t)(hfmModel.dynamicTransforms.size() - 1);
|
uint32_t skinDeformerID = (uint32_t)(hfmModel.skinDeformers.size() - 1);
|
||||||
for (hfm::Shape& shape : partShapes) {
|
for (hfm::Shape& shape : partShapes) {
|
||||||
shape.dynamicTransform = dynamicTransformID;
|
shape.skinDeformer = skinDeformerID;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// this is a single-joint mesh
|
// this is a no cluster mesh
|
||||||
HFMJoint& joint = hfmModel.joints[transformIndex];
|
HFMJoint& joint = hfmModel.joints[transformIndex];
|
||||||
|
|
||||||
// Apply geometric offset, if present, by transforming the vertices directly
|
// Apply geometric offset, if present, by transforming the vertices directly
|
||||||
|
|
|
@ -242,17 +242,20 @@ public:
|
||||||
QVector<glm::vec3> colors;
|
QVector<glm::vec3> colors;
|
||||||
QVector<glm::vec2> texCoords;
|
QVector<glm::vec2> texCoords;
|
||||||
QVector<glm::vec2> texCoords1;
|
QVector<glm::vec2> texCoords1;
|
||||||
QVector<uint16_t> clusterIndices; // DEPRECATED (see hfm::Shape::dynamicTransform, hfm::DynamicTransform::deformers, hfm::Deformer)
|
|
||||||
QVector<uint16_t> clusterWeights; // DEPRECATED (see hfm::Shape::dynamicTransform, hfm::DynamicTransform::deformers, hfm::Deformer)
|
|
||||||
QVector<int32_t> originalIndices;
|
|
||||||
|
|
||||||
QVector<Cluster> clusters; // DEPRECATED (see hfm::Shape::dynamicTransform, hfm::DynamicTransform::clusters)
|
QVector<Cluster> clusters; // DEPRECATED (see hfm::Shape::dynamicTransform, hfm::DynamicTransform::clusters)
|
||||||
|
|
||||||
Extents meshExtents; // DEPRECATED (see hfm::Shape::transformedExtents)
|
Extents meshExtents; // DEPRECATED (see hfm::Shape::transformedExtents)
|
||||||
glm::mat4 modelTransform; // DEPRECATED (see hfm::Joint::globalTransform, hfm::Shape::transform, hfm::Model::joints)
|
glm::mat4 modelTransform; // DEPRECATED (see hfm::Joint::globalTransform, hfm::Shape::transform, hfm::Model::joints)
|
||||||
|
|
||||||
|
// Skinning cluster attributes
|
||||||
|
QVector<uint16_t> clusterIndices;
|
||||||
|
QVector<uint16_t> clusterWeights;
|
||||||
|
|
||||||
|
// Blendshape attributes
|
||||||
QVector<Blendshape> blendshapes;
|
QVector<Blendshape> blendshapes;
|
||||||
|
|
||||||
|
|
||||||
|
QVector<int32_t> originalIndices; // Original indices of the vertices
|
||||||
unsigned int meshIndex; // the order the meshes appeared in the object file
|
unsigned int meshIndex; // the order the meshes appeared in the object file
|
||||||
|
|
||||||
graphics::MeshPointer _mesh;
|
graphics::MeshPointer _mesh;
|
||||||
|
@ -293,27 +296,17 @@ public:
|
||||||
bool shouldInitCollisions() const { return _collisionsConfig.size() > 0; }
|
bool shouldInitCollisions() const { return _collisionsConfig.size() > 0; }
|
||||||
};
|
};
|
||||||
|
|
||||||
// DEPRECATED in favor of using hfm::Joint
|
|
||||||
class TransformNode {
|
|
||||||
public:
|
|
||||||
static const uint32_t INVALID_PARENT_INDEX{ (uint32_t)-1 };
|
|
||||||
uint32_t parent { INVALID_PARENT_INDEX };
|
|
||||||
Transform transform;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Formerly contained in hfm::Mesh
|
// Formerly contained in hfm::Mesh
|
||||||
class Deformer {
|
class SkinCluster {
|
||||||
public:
|
public:
|
||||||
std::vector<uint32_t> indices;
|
std::vector<uint32_t> indices;
|
||||||
std::vector<float> weights;
|
std::vector<float> weights;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DynamicTransform {
|
class SkinDeformer {
|
||||||
public:
|
public:
|
||||||
std::vector<uint16_t> deformers;
|
std::vector<uint16_t> skinClusterIndices;
|
||||||
std::vector<Cluster> clusters; // affect the deformer of the same index
|
std::vector<Cluster> clusters;
|
||||||
std::vector<uint32_t> blendshapes;
|
|
||||||
// There are also the meshExtents and modelTransform, which for now are left in hfm::Mesh
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// The lightweight model part description.
|
// The lightweight model part description.
|
||||||
|
@ -322,10 +315,10 @@ public:
|
||||||
uint32_t mesh { UNDEFINED_KEY };
|
uint32_t mesh { UNDEFINED_KEY };
|
||||||
uint32_t meshPart { UNDEFINED_KEY };
|
uint32_t meshPart { UNDEFINED_KEY };
|
||||||
uint32_t material { UNDEFINED_KEY };
|
uint32_t material { UNDEFINED_KEY };
|
||||||
uint32_t transform { UNDEFINED_KEY }; // The hfm::Joint associated with this shape, containing transform information
|
uint32_t joint { UNDEFINED_KEY }; // The hfm::Joint associated with this shape, containing transform information
|
||||||
// TODO: Have all serializers calculate hfm::Shape::transformedExtents in world space where they previously calculated hfm::Mesh::meshExtents. Change all code that uses hfm::Mesh::meshExtents to use this instead.
|
// TODO: Have all serializers calculate hfm::Shape::transformedExtents in world space where they previously calculated hfm::Mesh::meshExtents. Change all code that uses hfm::Mesh::meshExtents to use this instead.
|
||||||
Extents transformedExtents; // The precise extents of the meshPart vertices in world space, after transform information is applied, while not taking into account rigging/skinning
|
Extents transformedExtents; // The precise extents of the meshPart vertices in world space, after transform information is applied, while not taking into account rigging/skinning
|
||||||
uint32_t dynamicTransform { UNDEFINED_KEY };
|
uint32_t skinDeformer { UNDEFINED_KEY };
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The runtime model format.
|
/// The runtime model format.
|
||||||
|
@ -342,10 +335,9 @@ public:
|
||||||
|
|
||||||
std::vector<Mesh> meshes;
|
std::vector<Mesh> meshes;
|
||||||
std::vector<Material> materials;
|
std::vector<Material> materials;
|
||||||
std::vector<Deformer> deformers;
|
|
||||||
|
|
||||||
std::vector<TransformNode> transforms;
|
std::vector<SkinDeformer> skinDeformers;
|
||||||
std::vector<DynamicTransform> dynamicTransforms;
|
std::vector<SkinCluster> skinClusters;
|
||||||
|
|
||||||
std::vector<Joint> joints;
|
std::vector<Joint> joints;
|
||||||
QHash<QString, int> jointIndices; ///< 1-based, so as to more easily detect missing indices
|
QHash<QString, int> jointIndices; ///< 1-based, so as to more easily detect missing indices
|
||||||
|
|
|
@ -30,7 +30,7 @@ namespace baker {
|
||||||
class GetModelPartsTask {
|
class GetModelPartsTask {
|
||||||
public:
|
public:
|
||||||
using Input = hfm::Model::Pointer;
|
using Input = hfm::Model::Pointer;
|
||||||
using Output = VaryingSet9<std::vector<hfm::Mesh>, hifi::URL, baker::MeshIndicesToModelNames, baker::BlendshapesPerMesh, std::vector<hfm::Joint>, std::vector<hfm::Shape>, std::vector<hfm::DynamicTransform>, std::vector<hfm::Deformer>, Extents>;
|
using Output = VaryingSet9<std::vector<hfm::Mesh>, hifi::URL, baker::MeshIndicesToModelNames, baker::BlendshapesPerMesh, std::vector<hfm::Joint>, std::vector<hfm::Shape>, std::vector<hfm::SkinDeformer>, std::vector<hfm::SkinCluster>, Extents>;
|
||||||
using JobModel = Job::ModelIO<GetModelPartsTask, Input, Output>;
|
using JobModel = Job::ModelIO<GetModelPartsTask, Input, Output>;
|
||||||
|
|
||||||
void run(const BakeContextPointer& context, const Input& input, Output& output) {
|
void run(const BakeContextPointer& context, const Input& input, Output& output) {
|
||||||
|
@ -45,8 +45,8 @@ namespace baker {
|
||||||
}
|
}
|
||||||
output.edit4() = hfmModelIn->joints;
|
output.edit4() = hfmModelIn->joints;
|
||||||
output.edit5() = hfmModelIn->shapes;
|
output.edit5() = hfmModelIn->shapes;
|
||||||
output.edit6() = hfmModelIn->dynamicTransforms;
|
output.edit6() = hfmModelIn->skinDeformers;
|
||||||
output.edit7() = hfmModelIn->deformers;
|
output.edit7() = hfmModelIn->skinClusters;
|
||||||
output.edit8() = hfmModelIn->meshExtents;
|
output.edit8() = hfmModelIn->meshExtents;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -147,8 +147,8 @@ namespace baker {
|
||||||
const auto blendshapesPerMeshIn = modelPartsIn.getN<GetModelPartsTask::Output>(3);
|
const auto blendshapesPerMeshIn = modelPartsIn.getN<GetModelPartsTask::Output>(3);
|
||||||
const auto jointsIn = modelPartsIn.getN<GetModelPartsTask::Output>(4);
|
const auto jointsIn = modelPartsIn.getN<GetModelPartsTask::Output>(4);
|
||||||
const auto shapesIn = modelPartsIn.getN<GetModelPartsTask::Output>(5);
|
const auto shapesIn = modelPartsIn.getN<GetModelPartsTask::Output>(5);
|
||||||
const auto dynamicTransformsIn = modelPartsIn.getN<GetModelPartsTask::Output>(6);
|
const auto skinDeformersIn = modelPartsIn.getN<GetModelPartsTask::Output>(6);
|
||||||
const auto deformersIn = modelPartsIn.getN<GetModelPartsTask::Output>(7);
|
const auto skinClustersIn = modelPartsIn.getN<GetModelPartsTask::Output>(7);
|
||||||
const auto modelExtentsIn = modelPartsIn.getN<GetModelPartsTask::Output>(8);
|
const auto modelExtentsIn = modelPartsIn.getN<GetModelPartsTask::Output>(8);
|
||||||
|
|
||||||
// Calculate normals and tangents for meshes and blendshapes if they do not exist
|
// Calculate normals and tangents for meshes and blendshapes if they do not exist
|
||||||
|
@ -163,14 +163,14 @@ namespace baker {
|
||||||
|
|
||||||
// Skinning weight calculations
|
// Skinning weight calculations
|
||||||
// NOTE: Due to limitations in the current graphics::MeshPointer representation, the output list of ReweightedDeformers is per-mesh. An element is empty if there are no deformers for the mesh of the same index.
|
// NOTE: Due to limitations in the current graphics::MeshPointer representation, the output list of ReweightedDeformers is per-mesh. An element is empty if there are no deformers for the mesh of the same index.
|
||||||
const auto reweightDeformersInputs = ReweightDeformersTask::Input(meshesIn, shapesIn, dynamicTransformsIn, deformersIn).asVarying();
|
const auto reweightDeformersInputs = ReweightDeformersTask::Input(meshesIn, shapesIn, skinDeformersIn, skinClustersIn).asVarying();
|
||||||
const auto reweightedDeformers = model.addJob<ReweightDeformersTask>("ReweightDeformers", reweightDeformersInputs);
|
const auto reweightedDeformers = model.addJob<ReweightDeformersTask>("ReweightDeformers", reweightDeformersInputs);
|
||||||
// Shape vertices are included/rejected based on skinning weight, and thus must use the reweighted deformers.
|
// Shape vertices are included/rejected based on skinning weight, and thus must use the reweighted deformers.
|
||||||
const auto collectShapeVerticesInputs = CollectShapeVerticesTask::Input(meshesIn, shapesIn, jointsIn, dynamicTransformsIn, reweightedDeformers).asVarying();
|
const auto collectShapeVerticesInputs = CollectShapeVerticesTask::Input(meshesIn, shapesIn, jointsIn, skinDeformersIn, reweightedDeformers).asVarying();
|
||||||
const auto shapeVerticesPerJoint = model.addJob<CollectShapeVerticesTask>("CollectShapeVertices", collectShapeVerticesInputs);
|
const auto shapeVerticesPerJoint = model.addJob<CollectShapeVerticesTask>("CollectShapeVertices", collectShapeVerticesInputs);
|
||||||
|
|
||||||
// Build the graphics::MeshPointer for each hfm::Mesh
|
// Build the graphics::MeshPointer for each hfm::Mesh
|
||||||
const auto buildGraphicsMeshInputs = BuildGraphicsMeshTask::Input(meshesIn, url, meshIndicesToModelNames, normalsPerMesh, tangentsPerMesh, shapesIn, dynamicTransformsIn, reweightedDeformers).asVarying();
|
const auto buildGraphicsMeshInputs = BuildGraphicsMeshTask::Input(meshesIn, url, meshIndicesToModelNames, normalsPerMesh, tangentsPerMesh, shapesIn, skinDeformersIn, reweightedDeformers).asVarying();
|
||||||
const auto graphicsMeshes = model.addJob<BuildGraphicsMeshTask>("BuildGraphicsMesh", buildGraphicsMeshInputs);
|
const auto graphicsMeshes = model.addJob<BuildGraphicsMeshTask>("BuildGraphicsMesh", buildGraphicsMeshInputs);
|
||||||
|
|
||||||
// Prepare joint information
|
// Prepare joint information
|
||||||
|
|
|
@ -95,7 +95,7 @@ void buildGraphicsMesh(const hfm::Mesh& hfmMesh, graphics::MeshPointer& graphics
|
||||||
HIFI_FCDEBUG_ID(model_baker(), repeatMessageID, "BuildGraphicsMeshTask -- The number of indices and weights for a deformer had different sizes and have been trimmed to match");
|
HIFI_FCDEBUG_ID(model_baker(), repeatMessageID, "BuildGraphicsMeshTask -- The number of indices and weights for a deformer had different sizes and have been trimmed to match");
|
||||||
}
|
}
|
||||||
// Record cluster sizes
|
// Record cluster sizes
|
||||||
const size_t numVertClusters = (reweightedDeformers.weightsPerVertex ? hfmMesh.clusterIndices.size() / reweightedDeformers.weightsPerVertex : 0);
|
const size_t numVertClusters = (reweightedDeformers.weightsPerVertex ? reweightedDeformers.indices.size() / reweightedDeformers.weightsPerVertex : 0);
|
||||||
const size_t clusterIndicesSize = numVertClusters * clusterIndiceElement.getSize();
|
const size_t clusterIndicesSize = numVertClusters * clusterIndiceElement.getSize();
|
||||||
const size_t clusterWeightsSize = numVertClusters * clusterWeightElement.getSize();
|
const size_t clusterWeightsSize = numVertClusters * clusterWeightElement.getSize();
|
||||||
|
|
||||||
|
@ -381,16 +381,16 @@ void BuildGraphicsMeshTask::run(const baker::BakeContextPointer& context, const
|
||||||
const auto& normalsPerMesh = input.get3();
|
const auto& normalsPerMesh = input.get3();
|
||||||
const auto& tangentsPerMesh = input.get4();
|
const auto& tangentsPerMesh = input.get4();
|
||||||
const auto& shapes = input.get5();
|
const auto& shapes = input.get5();
|
||||||
const auto& dynamicTransforms = input.get6();
|
const auto& skinDeformers = input.get6();
|
||||||
const auto& reweightedDeformersPerMesh = input.get7();
|
const auto& reweightedDeformersPerMesh = input.get7();
|
||||||
|
|
||||||
// Currently, there is only (at most) one dynamicTransform per mesh
|
// Currently, there is only (at most) one skinDeformer per mesh
|
||||||
// An undefined shape.dynamicTransform has the value hfm::UNDEFINED_KEY
|
// An undefined shape.skinDeformer has the value hfm::UNDEFINED_KEY
|
||||||
std::vector<uint32_t> dynamicTransformPerMesh;
|
std::vector<uint32_t> skinDeformerPerMesh;
|
||||||
dynamicTransformPerMesh.resize(meshes.size(), hfm::UNDEFINED_KEY);
|
skinDeformerPerMesh.resize(meshes.size(), hfm::UNDEFINED_KEY);
|
||||||
for (const auto& shape : shapes) {
|
for (const auto& shape : shapes) {
|
||||||
uint32_t dynamicTransformIndex = shape.dynamicTransform;
|
uint32_t skinDeformerIndex = shape.skinDeformer;
|
||||||
dynamicTransformPerMesh[shape.mesh] = dynamicTransformIndex;
|
skinDeformerPerMesh[shape.mesh] = skinDeformerIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& graphicsMeshes = output;
|
auto& graphicsMeshes = output;
|
||||||
|
@ -403,10 +403,10 @@ void BuildGraphicsMeshTask::run(const baker::BakeContextPointer& context, const
|
||||||
|
|
||||||
uint16_t numDeformerControllers = 0;
|
uint16_t numDeformerControllers = 0;
|
||||||
if (reweightedDeformers.weightsPerVertex != 0) {
|
if (reweightedDeformers.weightsPerVertex != 0) {
|
||||||
uint32_t dynamicTransformIndex = dynamicTransformPerMesh[i];
|
uint32_t skinDeformerIndex = skinDeformerPerMesh[i];
|
||||||
if (dynamicTransformIndex != hfm::UNDEFINED_KEY) {
|
if (skinDeformerIndex != hfm::UNDEFINED_KEY) {
|
||||||
const hfm::DynamicTransform& dynamicTransform = dynamicTransforms[dynamicTransformIndex];
|
const hfm::SkinDeformer& skinDeformer = skinDeformers[skinDeformerIndex];
|
||||||
numDeformerControllers = (uint16_t)dynamicTransform.deformers.size();
|
numDeformerControllers = (uint16_t)skinDeformer.skinClusterIndices.size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
class BuildGraphicsMeshTask {
|
class BuildGraphicsMeshTask {
|
||||||
public:
|
public:
|
||||||
using Input = baker::VaryingSet8<std::vector<hfm::Mesh>, hifi::URL, baker::MeshIndicesToModelNames, baker::NormalsPerMesh, baker::TangentsPerMesh, std::vector<hfm::Shape>, std::vector<hfm::DynamicTransform>, std::vector<baker::ReweightedDeformers>>;
|
using Input = baker::VaryingSet8<std::vector<hfm::Mesh>, hifi::URL, baker::MeshIndicesToModelNames, baker::NormalsPerMesh, baker::TangentsPerMesh, std::vector<hfm::Shape>, std::vector<hfm::SkinDeformer>, std::vector<baker::ReweightedDeformers>>;
|
||||||
using Output = std::vector<graphics::MeshPointer>;
|
using Output = std::vector<graphics::MeshPointer>;
|
||||||
using JobModel = baker::Job::ModelIO<BuildGraphicsMeshTask, Input, Output>;
|
using JobModel = baker::Job::ModelIO<BuildGraphicsMeshTask, Input, Output>;
|
||||||
|
|
||||||
|
|
|
@ -13,15 +13,15 @@
|
||||||
|
|
||||||
#include <glm/gtx/transform.hpp>
|
#include <glm/gtx/transform.hpp>
|
||||||
|
|
||||||
// Used to track and avoid duplicate shape vertices, as multiple shapes can have the same mesh and dynamicTransform
|
// Used to track and avoid duplicate shape vertices, as multiple shapes can have the same mesh and skinDeformer
|
||||||
class VertexSource {
|
class VertexSource {
|
||||||
public:
|
public:
|
||||||
uint32_t mesh;
|
uint32_t mesh;
|
||||||
uint32_t dynamicTransform;
|
uint32_t skinDeformer;
|
||||||
|
|
||||||
bool operator==(const VertexSource& other) const {
|
bool operator==(const VertexSource& other) const {
|
||||||
return mesh == other.mesh &&
|
return mesh == other.mesh &&
|
||||||
dynamicTransform == other.dynamicTransform;
|
skinDeformer == other.skinDeformer;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ void CollectShapeVerticesTask::run(const baker::BakeContextPointer& context, con
|
||||||
const auto& meshes = input.get0();
|
const auto& meshes = input.get0();
|
||||||
const auto& shapes = input.get1();
|
const auto& shapes = input.get1();
|
||||||
const auto& joints = input.get2();
|
const auto& joints = input.get2();
|
||||||
const auto& dynamicTransforms = input.get3();
|
const auto& skinDeformers = input.get3();
|
||||||
const auto& reweightedDeformers = input.get4();
|
const auto& reweightedDeformers = input.get4();
|
||||||
auto& shapeVerticesPerJoint = output;
|
auto& shapeVerticesPerJoint = output;
|
||||||
|
|
||||||
|
@ -38,18 +38,18 @@ void CollectShapeVerticesTask::run(const baker::BakeContextPointer& context, con
|
||||||
vertexSourcesPerJoint.resize(joints.size());
|
vertexSourcesPerJoint.resize(joints.size());
|
||||||
for (size_t i = 0; i < shapes.size(); ++i) {
|
for (size_t i = 0; i < shapes.size(); ++i) {
|
||||||
const auto& shape = shapes[i];
|
const auto& shape = shapes[i];
|
||||||
const uint32_t dynamicTransformKey = shape.dynamicTransform;
|
const uint32_t skinDeformerKey = shape.skinDeformer;
|
||||||
if (dynamicTransformKey == hfm::UNDEFINED_KEY) {
|
if (skinDeformerKey == hfm::UNDEFINED_KEY) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
VertexSource vertexSource;
|
VertexSource vertexSource;
|
||||||
vertexSource.mesh = shape.mesh;
|
vertexSource.mesh = shape.mesh;
|
||||||
vertexSource.dynamicTransform = dynamicTransformKey;
|
vertexSource.skinDeformer = skinDeformerKey;
|
||||||
|
|
||||||
const auto& dynamicTransform = dynamicTransforms[dynamicTransformKey];
|
const auto& skinDeformer = skinDeformers[skinDeformerKey];
|
||||||
for (size_t j = 0; j < dynamicTransform.clusters.size(); ++j) {
|
for (size_t j = 0; j < skinDeformer.clusters.size(); ++j) {
|
||||||
const auto& cluster = dynamicTransform.clusters[j];
|
const auto& cluster = skinDeformer.clusters[j];
|
||||||
const uint32_t jointIndex = cluster.jointIndex;
|
const uint32_t jointIndex = cluster.jointIndex;
|
||||||
|
|
||||||
auto& vertexSources = vertexSourcesPerJoint[jointIndex];
|
auto& vertexSources = vertexSourcesPerJoint[jointIndex];
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
class CollectShapeVerticesTask {
|
class CollectShapeVerticesTask {
|
||||||
public:
|
public:
|
||||||
using Input = baker::VaryingSet5<std::vector<hfm::Mesh>, std::vector<hfm::Shape>, std::vector<hfm::Joint>, std::vector<hfm::DynamicTransform>, std::vector<baker::ReweightedDeformers>>;
|
using Input = baker::VaryingSet5<std::vector<hfm::Mesh>, std::vector<hfm::Shape>, std::vector<hfm::Joint>, std::vector<hfm::SkinDeformer>, std::vector<baker::ReweightedDeformers>>;
|
||||||
using Output = std::vector<ShapeVertices>;
|
using Output = std::vector<ShapeVertices>;
|
||||||
using JobModel = baker::Job::ModelIO<CollectShapeVerticesTask, Input, Output>;
|
using JobModel = baker::Job::ModelIO<CollectShapeVerticesTask, Input, Output>;
|
||||||
|
|
||||||
|
|
|
@ -11,30 +11,30 @@
|
||||||
|
|
||||||
#include "ReweightDeformersTask.h"
|
#include "ReweightDeformersTask.h"
|
||||||
|
|
||||||
baker::ReweightedDeformers getReweightedDeformers(size_t numMeshVertices, const std::vector<const hfm::Deformer*> deformers, const uint16_t weightsPerVertex) {
|
baker::ReweightedDeformers getReweightedDeformers(size_t numMeshVertices, const std::vector<const hfm::SkinCluster*> skinClusters, const uint16_t weightsPerVertex) {
|
||||||
baker::ReweightedDeformers reweightedDeformers;
|
baker::ReweightedDeformers reweightedDeformers;
|
||||||
if (deformers.size() == 0) {
|
if (skinClusters.size() == 0) {
|
||||||
return reweightedDeformers;
|
return reweightedDeformers;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t numClusterIndices = numMeshVertices * weightsPerVertex;
|
size_t numClusterIndices = numMeshVertices * weightsPerVertex;
|
||||||
reweightedDeformers.weightsPerVertex = weightsPerVertex;
|
reweightedDeformers.weightsPerVertex = weightsPerVertex;
|
||||||
// TODO: Consider having a rootCluster property in the DynamicTransform rather than appending the root to the end of the cluster list.
|
// TODO: Consider having a rootCluster property in the DynamicTransform rather than appending the root to the end of the cluster list.
|
||||||
reweightedDeformers.indices.resize(numClusterIndices, (uint16_t)(deformers.size() - 1));
|
reweightedDeformers.indices.resize(numClusterIndices, (uint16_t)(skinClusters.size() - 1));
|
||||||
reweightedDeformers.weights.resize(numClusterIndices, 0);
|
reweightedDeformers.weights.resize(numClusterIndices, 0);
|
||||||
|
|
||||||
std::vector<float> weightAccumulators;
|
std::vector<float> weightAccumulators;
|
||||||
weightAccumulators.resize(numClusterIndices, 0.0f);
|
weightAccumulators.resize(numClusterIndices, 0.0f);
|
||||||
for (uint16_t i = 0; i < (uint16_t)deformers.size(); ++i) {
|
for (uint16_t i = 0; i < (uint16_t)skinClusters.size(); ++i) {
|
||||||
const hfm::Deformer& deformer = *deformers[i];
|
const hfm::SkinCluster& skinCluster = *skinClusters[i];
|
||||||
|
|
||||||
if (deformer.indices.size() != deformer.weights.size()) {
|
if (skinCluster.indices.size() != skinCluster.weights.size()) {
|
||||||
reweightedDeformers.trimmedToMatch = true;
|
reweightedDeformers.trimmedToMatch = true;
|
||||||
}
|
}
|
||||||
size_t numIndicesOrWeights = std::min(deformer.indices.size(), deformer.weights.size());
|
size_t numIndicesOrWeights = std::min(skinCluster.indices.size(), skinCluster.weights.size());
|
||||||
for (size_t j = 0; j < numIndicesOrWeights; ++j) {
|
for (size_t j = 0; j < numIndicesOrWeights; ++j) {
|
||||||
uint32_t index = deformer.indices[j];
|
uint32_t index = skinCluster.indices[j];
|
||||||
float weight = deformer.weights[j];
|
float weight = skinCluster.weights[j];
|
||||||
|
|
||||||
// look for an unused slot in the weights vector
|
// look for an unused slot in the weights vector
|
||||||
uint32_t weightIndex = index * weightsPerVertex;
|
uint32_t weightIndex = index * weightsPerVertex;
|
||||||
|
@ -90,34 +90,34 @@ void ReweightDeformersTask::run(const baker::BakeContextPointer& context, const
|
||||||
|
|
||||||
const auto& meshes = input.get0();
|
const auto& meshes = input.get0();
|
||||||
const auto& shapes = input.get1();
|
const auto& shapes = input.get1();
|
||||||
const auto& dynamicTransforms = input.get2();
|
const auto& skinDeformers = input.get2();
|
||||||
const auto& deformers = input.get3();
|
const auto& skinClusters = input.get3();
|
||||||
auto& reweightedDeformers = output;
|
auto& reweightedDeformers = output;
|
||||||
|
|
||||||
// Currently, there is only (at most) one dynamicTransform per mesh
|
// Currently, there is only (at most) one skinDeformer per mesh
|
||||||
// An undefined shape.dynamicTransform has the value hfm::UNDEFINED_KEY
|
// An undefined shape.skinDeformer has the value hfm::UNDEFINED_KEY
|
||||||
std::vector<uint32_t> dynamicTransformPerMesh;
|
std::vector<uint32_t> skinDeformerPerMesh;
|
||||||
dynamicTransformPerMesh.resize(meshes.size(), hfm::UNDEFINED_KEY);
|
skinDeformerPerMesh.resize(meshes.size(), hfm::UNDEFINED_KEY);
|
||||||
for (const auto& shape : shapes) {
|
for (const auto& shape : shapes) {
|
||||||
uint32_t dynamicTransformIndex = shape.dynamicTransform;
|
uint32_t skinDeformerIndex = shape.skinDeformer;
|
||||||
dynamicTransformPerMesh[shape.mesh] = dynamicTransformIndex;
|
skinDeformerPerMesh[shape.mesh] = skinDeformerIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
reweightedDeformers.reserve(meshes.size());
|
reweightedDeformers.reserve(meshes.size());
|
||||||
for (size_t i = 0; i < meshes.size(); ++i) {
|
for (size_t i = 0; i < meshes.size(); ++i) {
|
||||||
const auto& mesh = meshes[i];
|
const auto& mesh = meshes[i];
|
||||||
uint32_t dynamicTransformIndex = dynamicTransformPerMesh[i];
|
uint32_t skinDeformerIndex = skinDeformerPerMesh[i];
|
||||||
|
|
||||||
const hfm::DynamicTransform* dynamicTransform = nullptr;
|
const hfm::SkinDeformer* skinDeformer = nullptr;
|
||||||
std::vector<const hfm::Deformer*> meshDeformers;
|
std::vector<const hfm::SkinCluster*> meshSkinClusters;
|
||||||
if (dynamicTransformIndex != hfm::UNDEFINED_KEY) {
|
if (skinDeformerIndex != hfm::UNDEFINED_KEY) {
|
||||||
dynamicTransform = &dynamicTransforms[dynamicTransformIndex];
|
skinDeformer = &skinDeformers[skinDeformerIndex];
|
||||||
for (const auto& deformerIndex : dynamicTransform->deformers) {
|
for (const auto& skinClusterIndex : skinDeformer->skinClusterIndices) {
|
||||||
const auto& deformer = deformers[deformerIndex];
|
const auto& skinCluster = skinClusters[skinClusterIndex];
|
||||||
meshDeformers.push_back(&deformer);
|
meshSkinClusters.push_back(&skinCluster);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reweightedDeformers.push_back(getReweightedDeformers((size_t)mesh.vertices.size(), meshDeformers, NUM_WEIGHTS_PER_VERTEX));
|
reweightedDeformers.push_back(getReweightedDeformers((size_t)mesh.vertices.size(), meshSkinClusters, NUM_WEIGHTS_PER_VERTEX));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
class ReweightDeformersTask {
|
class ReweightDeformersTask {
|
||||||
public:
|
public:
|
||||||
using Input = baker::VaryingSet4<std::vector<hfm::Mesh>, std::vector<hfm::Shape>, std::vector<hfm::DynamicTransform>, std::vector<hfm::Deformer>>;
|
using Input = baker::VaryingSet4<std::vector<hfm::Mesh>, std::vector<hfm::Shape>, std::vector<hfm::SkinDeformer>, std::vector<hfm::SkinCluster>>;
|
||||||
using Output = std::vector<baker::ReweightedDeformers>;
|
using Output = std::vector<baker::ReweightedDeformers>;
|
||||||
using JobModel = baker::Job::ModelIO<ReweightDeformersTask, Input, Output>;
|
using JobModel = baker::Job::ModelIO<ReweightDeformersTask, Input, Output>;
|
||||||
|
|
||||||
|
|
|
@ -295,7 +295,6 @@ void ModelResource::onGeometryMappingLoaded(bool success) {
|
||||||
if (success && _modelResource) {
|
if (success && _modelResource) {
|
||||||
_hfmModel = _modelResource->_hfmModel;
|
_hfmModel = _modelResource->_hfmModel;
|
||||||
_materialMapping = _modelResource->_materialMapping;
|
_materialMapping = _modelResource->_materialMapping;
|
||||||
// _meshParts = _modelResource->_meshParts;
|
|
||||||
_meshes = _modelResource->_meshes;
|
_meshes = _modelResource->_meshes;
|
||||||
_materials = _modelResource->_materials;
|
_materials = _modelResource->_materials;
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
|
|
||||||
using namespace render;
|
using namespace render;
|
||||||
|
|
||||||
CauterizedMeshPartPayload::CauterizedMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform)
|
CauterizedMeshPartPayload::CauterizedMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform)
|
||||||
: ModelMeshPartPayload(model, meshIndex, partIndex, shapeIndex, transform, offsetTransform) {}
|
: ModelMeshPartPayload(model, meshIndex, partIndex, shapeIndex, transform) {}
|
||||||
|
|
||||||
void CauterizedMeshPartPayload::updateClusterBuffer(const std::vector<glm::mat4>& clusterMatrices,
|
void CauterizedMeshPartPayload::updateClusterBuffer(const std::vector<glm::mat4>& clusterMatrices,
|
||||||
const std::vector<glm::mat4>& cauterizedClusterMatrices) {
|
const std::vector<glm::mat4>& cauterizedClusterMatrices) {
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
class CauterizedMeshPartPayload : public ModelMeshPartPayload {
|
class CauterizedMeshPartPayload : public ModelMeshPartPayload {
|
||||||
public:
|
public:
|
||||||
CauterizedMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform);
|
CauterizedMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform);
|
||||||
|
|
||||||
// matrix palette skinning
|
// matrix palette skinning
|
||||||
void updateClusterBuffer(const std::vector<glm::mat4>& clusterMatrices,
|
void updateClusterBuffer(const std::vector<glm::mat4>& clusterMatrices,
|
||||||
|
|
|
@ -32,8 +32,40 @@ bool CauterizedModel::updateGeometry() {
|
||||||
bool needsFullUpdate = Model::updateGeometry();
|
bool needsFullUpdate = Model::updateGeometry();
|
||||||
if (_isCauterized && needsFullUpdate) {
|
if (_isCauterized && needsFullUpdate) {
|
||||||
assert(_cauterizeMeshStates.empty());
|
assert(_cauterizeMeshStates.empty());
|
||||||
|
/* const HFMModel& hfmModel = getHFMModel();
|
||||||
|
const auto& hfmDynamicTransforms = hfmModel.skinDeformers;
|
||||||
|
for (int i = 0; i < hfmDynamicTransforms.size(); i++) {
|
||||||
|
const auto& dynT = hfmDynamicTransforms[i];
|
||||||
|
MeshState state;
|
||||||
|
if (_useDualQuaternionSkinning) {
|
||||||
|
state.clusterDualQuaternions.resize(dynT.clusters.size());
|
||||||
|
} else {
|
||||||
|
state.clusterMatrices.resize(dynT.clusters.size());
|
||||||
|
}
|
||||||
|
_cauterizeMeshStates.append(state);
|
||||||
|
_meshStates.push_back(state);
|
||||||
|
}*/
|
||||||
|
|
||||||
const HFMModel& hfmModel = getHFMModel();
|
const HFMModel& hfmModel = getHFMModel();
|
||||||
foreach (const HFMMesh& mesh, hfmModel.meshes) {
|
const auto& hfmDynamicTransforms = hfmModel.skinDeformers;
|
||||||
|
int i = 0;
|
||||||
|
/* for (const auto& mesh: hfmModel.meshes) {
|
||||||
|
MeshState state;
|
||||||
|
state.clusterDualQuaternions.resize(mesh.clusters.size());
|
||||||
|
state.clusterMatrices.resize(mesh.clusters.size());
|
||||||
|
_meshStates.push_back(state);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
for (int i = 0; i < hfmDynamicTransforms.size(); i++) {
|
||||||
|
const auto& dynT = hfmDynamicTransforms[i];
|
||||||
|
MeshState state;
|
||||||
|
state.clusterDualQuaternions.resize(dynT.clusters.size());
|
||||||
|
state.clusterMatrices.resize(dynT.clusters.size());
|
||||||
|
_cauterizeMeshStates.push_back(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* foreach (const HFMMesh& mesh, hfmModel.meshes) {
|
||||||
Model::MeshState state;
|
Model::MeshState state;
|
||||||
if (_useDualQuaternionSkinning) {
|
if (_useDualQuaternionSkinning) {
|
||||||
state.clusterDualQuaternions.resize(mesh.clusters.size());
|
state.clusterDualQuaternions.resize(mesh.clusters.size());
|
||||||
|
@ -42,7 +74,7 @@ bool CauterizedModel::updateGeometry() {
|
||||||
state.clusterMatrices.resize(mesh.clusters.size());
|
state.clusterMatrices.resize(mesh.clusters.size());
|
||||||
_cauterizeMeshStates.append(state);
|
_cauterizeMeshStates.append(state);
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
return needsFullUpdate;
|
return needsFullUpdate;
|
||||||
}
|
}
|
||||||
|
@ -52,11 +84,6 @@ void CauterizedModel::createRenderItemSet() {
|
||||||
assert(isLoaded());
|
assert(isLoaded());
|
||||||
const auto& meshes = _renderGeometry->getMeshes();
|
const auto& meshes = _renderGeometry->getMeshes();
|
||||||
|
|
||||||
// all of our mesh vectors must match in size
|
|
||||||
if (meshes.size() != _meshStates.size()) {
|
|
||||||
qCDebug(renderutils) << "WARNING!!!! Mesh Sizes don't match! We will not segregate mesh groups yet.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We should not have any existing renderItems if we enter this section of code
|
// We should not have any existing renderItems if we enter this section of code
|
||||||
Q_ASSERT(_modelMeshRenderItems.isEmpty());
|
Q_ASSERT(_modelMeshRenderItems.isEmpty());
|
||||||
|
@ -73,8 +100,23 @@ void CauterizedModel::createRenderItemSet() {
|
||||||
offset.setScale(_scale);
|
offset.setScale(_scale);
|
||||||
offset.postTranslate(_offset);
|
offset.postTranslate(_offset);
|
||||||
|
|
||||||
|
Transform::mult(transform, transform, offset);
|
||||||
|
|
||||||
// Run through all of the meshes, and place them into their segregated, but unsorted buckets
|
// Run through all of the meshes, and place them into their segregated, but unsorted buckets
|
||||||
|
// Run through all of the meshes, and place them into their segregated, but unsorted buckets
|
||||||
int shapeID = 0;
|
int shapeID = 0;
|
||||||
|
const auto& shapes = _renderGeometry->getHFMModel().shapes;
|
||||||
|
for (shapeID; shapeID < shapes.size(); shapeID++) {
|
||||||
|
const auto& shape = shapes[shapeID];
|
||||||
|
|
||||||
|
_modelMeshRenderItems << std::make_shared<CauterizedMeshPartPayload>(shared_from_this(), shape.mesh, shape.meshPart, shapeID, transform);
|
||||||
|
|
||||||
|
auto material = getNetworkModel()->getShapeMaterial(shapeID);
|
||||||
|
_modelMeshMaterialNames.push_back(material ? material->getName() : "");
|
||||||
|
_modelMeshRenderItemShapes.emplace_back(ShapeInfo{ (int)shape.mesh, shape.skinDeformer });
|
||||||
|
}
|
||||||
|
|
||||||
|
/* int shapeID = 0;
|
||||||
uint32_t numMeshes = (uint32_t)meshes.size();
|
uint32_t numMeshes = (uint32_t)meshes.size();
|
||||||
for (uint32_t i = 0; i < numMeshes; i++) {
|
for (uint32_t i = 0; i < numMeshes; i++) {
|
||||||
const auto& mesh = meshes.at(i);
|
const auto& mesh = meshes.at(i);
|
||||||
|
@ -85,14 +127,14 @@ void CauterizedModel::createRenderItemSet() {
|
||||||
// Create the render payloads
|
// Create the render payloads
|
||||||
int numParts = (int)mesh->getNumParts();
|
int numParts = (int)mesh->getNumParts();
|
||||||
for (int partIndex = 0; partIndex < numParts; partIndex++) {
|
for (int partIndex = 0; partIndex < numParts; partIndex++) {
|
||||||
auto ptr = std::make_shared<CauterizedMeshPartPayload>(shared_from_this(), i, partIndex, shapeID, transform, offset);
|
auto ptr = std::make_shared<CauterizedMeshPartPayload>(shared_from_this(), i, partIndex, shapeID, transform);
|
||||||
_modelMeshRenderItems << std::static_pointer_cast<ModelMeshPartPayload>(ptr);
|
_modelMeshRenderItems << std::static_pointer_cast<ModelMeshPartPayload>(ptr);
|
||||||
auto material = getNetworkModel()->getShapeMaterial(shapeID);
|
auto material = getNetworkModel()->getShapeMaterial(shapeID);
|
||||||
_modelMeshMaterialNames.push_back(material ? material->getName() : "");
|
_modelMeshMaterialNames.push_back(material ? material->getName() : "");
|
||||||
_modelMeshRenderItemShapes.emplace_back(ShapeInfo{ (int)i });
|
_modelMeshRenderItemShapes.emplace_back(ShapeInfo{ (int)i });
|
||||||
shapeID++;
|
shapeID++;
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
} else {
|
} else {
|
||||||
Model::createRenderItemSet();
|
Model::createRenderItemSet();
|
||||||
}
|
}
|
||||||
|
@ -108,6 +150,38 @@ void CauterizedModel::updateClusterMatrices() {
|
||||||
updateShapeStatesFromRig();
|
updateShapeStatesFromRig();
|
||||||
|
|
||||||
_needsUpdateClusterMatrices = false;
|
_needsUpdateClusterMatrices = false;
|
||||||
|
|
||||||
|
|
||||||
|
const HFMModel& hfmModel = getHFMModel();
|
||||||
|
const auto& hfmDynamicTransforms = hfmModel.skinDeformers;
|
||||||
|
for (int i = 0; i < (int)_meshStates.size(); i++) {
|
||||||
|
MeshState& state = _meshStates[i];
|
||||||
|
const auto& deformer = hfmDynamicTransforms[i];
|
||||||
|
|
||||||
|
int meshIndex = i;
|
||||||
|
int clusterIndex = 0;
|
||||||
|
|
||||||
|
for (int d = 0; d < deformer.clusters.size(); d++) {
|
||||||
|
const auto& cluster = deformer.clusters[d];
|
||||||
|
clusterIndex = d;
|
||||||
|
|
||||||
|
const auto& cbmov = _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex);
|
||||||
|
|
||||||
|
if (_useDualQuaternionSkinning) {
|
||||||
|
auto jointPose = _rig.getJointPose(cluster.jointIndex);
|
||||||
|
Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans());
|
||||||
|
Transform clusterTransform;
|
||||||
|
Transform::mult(clusterTransform, jointTransform, cbmov.inverseBindTransform);
|
||||||
|
state.clusterDualQuaternions[d] = Model::TransformDualQuaternion(clusterTransform);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
auto jointMatrix = _rig.getJointTransform(cluster.jointIndex);
|
||||||
|
glm_mat4u_mul(jointMatrix, cbmov.inverseBindMatrix, state.clusterMatrices[d]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
const HFMModel& hfmModel = getHFMModel();
|
const HFMModel& hfmModel = getHFMModel();
|
||||||
for (int i = 0; i < (int)_meshStates.size(); i++) {
|
for (int i = 0; i < (int)_meshStates.size(); i++) {
|
||||||
Model::MeshState& state = _meshStates[i];
|
Model::MeshState& state = _meshStates[i];
|
||||||
|
@ -131,7 +205,7 @@ void CauterizedModel::updateClusterMatrices() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
// as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty.
|
// as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty.
|
||||||
if (!_cauterizeBoneSet.empty()) {
|
if (!_cauterizeBoneSet.empty()) {
|
||||||
|
|
||||||
|
@ -224,64 +298,69 @@ void CauterizedModel::updateRenderItems() {
|
||||||
auto meshIndex = self->_modelMeshRenderItemShapes[i].meshIndex;
|
auto meshIndex = self->_modelMeshRenderItemShapes[i].meshIndex;
|
||||||
|
|
||||||
const auto& shapeState = self->getShapeState(i);
|
const auto& shapeState = self->getShapeState(i);
|
||||||
const auto& meshState = self->getMeshState(meshIndex);
|
|
||||||
const auto& cauterizedMeshState = self->getCauterizeMeshState(meshIndex);
|
auto deformerIndex = self->_modelMeshRenderItemShapes[i].deformerIndex;
|
||||||
|
bool isDeformed = (deformerIndex != hfm::UNDEFINED_KEY);
|
||||||
|
|
||||||
|
|
||||||
|
// auto meshIndex = self->_modelMeshRenderItemShapes[i].meshIndex;
|
||||||
|
// auto deformerIndex = self->_modelMeshRenderItemShapes[i].meshIndex;
|
||||||
|
|
||||||
|
// const auto& shapeState = self->getShapeState(i);
|
||||||
|
|
||||||
|
|
||||||
bool invalidatePayloadShapeKey = self->shouldInvalidatePayloadShapeKey(meshIndex);
|
bool invalidatePayloadShapeKey = self->shouldInvalidatePayloadShapeKey(meshIndex);
|
||||||
bool useDualQuaternionSkinning = self->getUseDualQuaternionSkinning();
|
bool useDualQuaternionSkinning = self->getUseDualQuaternionSkinning();
|
||||||
|
|
||||||
transaction.updateItem<ModelMeshPartPayload>(itemID, [modelTransform, shapeState, meshState, useDualQuaternionSkinning, cauterizedMeshState, invalidatePayloadShapeKey,
|
|
||||||
primitiveMode, renderItemKeyGlobalFlags, enableCauterization](ModelMeshPartPayload& mmppData) {
|
|
||||||
CauterizedMeshPartPayload& data = static_cast<CauterizedMeshPartPayload&>(mmppData);
|
|
||||||
if (useDualQuaternionSkinning) {
|
|
||||||
data.updateClusterBuffer(meshState.clusterDualQuaternions,
|
|
||||||
cauterizedMeshState.clusterDualQuaternions);
|
|
||||||
data.computeAdjustedLocalBound(meshState.clusterDualQuaternions);
|
|
||||||
} else {
|
|
||||||
data.updateClusterBuffer(meshState.clusterMatrices,
|
|
||||||
cauterizedMeshState.clusterMatrices);
|
|
||||||
data.computeAdjustedLocalBound(meshState.clusterMatrices);
|
|
||||||
}
|
|
||||||
|
|
||||||
Transform renderTransform = modelTransform;
|
|
||||||
/*if (useDualQuaternionSkinning) {
|
if (isDeformed) {
|
||||||
if (meshState.clusterDualQuaternions.size() == 1 || meshState.clusterDualQuaternions.size() == 2) {
|
|
||||||
const auto& dq = meshState.clusterDualQuaternions[0];
|
const auto& meshState = self->getMeshState(deformerIndex);
|
||||||
Transform transform(dq.getRotation(),
|
const auto& cauterizedMeshState = self->getCauterizeMeshState(deformerIndex);
|
||||||
dq.getScale(),
|
|
||||||
dq.getTranslation());
|
transaction.updateItem<ModelMeshPartPayload>(itemID,
|
||||||
renderTransform = modelTransform.worldTransform(transform);
|
[modelTransform, shapeState, meshState, useDualQuaternionSkinning, cauterizedMeshState, invalidatePayloadShapeKey,
|
||||||
}
|
primitiveMode, renderItemKeyGlobalFlags, enableCauterization](ModelMeshPartPayload& mmppData) {
|
||||||
} else {
|
CauterizedMeshPartPayload& data = static_cast<CauterizedMeshPartPayload&>(mmppData);
|
||||||
if (meshState.clusterMatrices.size() == 1 || meshState.clusterMatrices.size() == 2) {
|
if (useDualQuaternionSkinning) {
|
||||||
renderTransform = modelTransform.worldTransform(Transform(meshState.clusterMatrices[0]));
|
data.updateClusterBuffer(meshState.clusterDualQuaternions,
|
||||||
}
|
cauterizedMeshState.clusterDualQuaternions);
|
||||||
}*/
|
} else {
|
||||||
if (meshState.clusterMatrices.size() <= 1) {
|
data.updateClusterBuffer(meshState.clusterMatrices,
|
||||||
|
cauterizedMeshState.clusterMatrices);
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform renderTransform = modelTransform;
|
||||||
|
// if (meshState.clusterMatrices.size() <= 2) {
|
||||||
|
// renderTransform = modelTransform.worldTransform(shapeState._rootFromJointTransform);
|
||||||
|
// }
|
||||||
|
data.updateTransform(renderTransform);
|
||||||
|
data.updateTransformForCauterizedMesh(renderTransform);
|
||||||
|
data.updateTransformAndBound(modelTransform.worldTransform(shapeState._rootFromJointTransform));
|
||||||
|
|
||||||
|
data.setEnableCauterization(enableCauterization);
|
||||||
|
data.updateKey(renderItemKeyGlobalFlags);
|
||||||
|
data.setShapeKey(invalidatePayloadShapeKey, primitiveMode, useDualQuaternionSkinning);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
transaction.updateItem<ModelMeshPartPayload>(itemID,
|
||||||
|
[modelTransform, shapeState, invalidatePayloadShapeKey, primitiveMode, renderItemKeyGlobalFlags, enableCauterization]
|
||||||
|
(ModelMeshPartPayload& mmppData) {
|
||||||
|
CauterizedMeshPartPayload& data = static_cast<CauterizedMeshPartPayload&>(mmppData);
|
||||||
|
|
||||||
|
Transform renderTransform = modelTransform;
|
||||||
|
|
||||||
renderTransform = modelTransform.worldTransform(shapeState._rootFromJointTransform);
|
renderTransform = modelTransform.worldTransform(shapeState._rootFromJointTransform);
|
||||||
}
|
data.updateTransform(renderTransform);
|
||||||
data.updateTransformForSkinnedMesh(renderTransform, modelTransform);
|
data.updateTransformForCauterizedMesh(renderTransform);
|
||||||
|
|
||||||
renderTransform = modelTransform;
|
data.setEnableCauterization(enableCauterization);
|
||||||
if (useDualQuaternionSkinning) {
|
data.updateKey(renderItemKeyGlobalFlags);
|
||||||
if (cauterizedMeshState.clusterDualQuaternions.size() == 1 || cauterizedMeshState.clusterDualQuaternions.size() == 2) {
|
data.setShapeKey(invalidatePayloadShapeKey, primitiveMode, false);
|
||||||
const auto& dq = cauterizedMeshState.clusterDualQuaternions[0];
|
});
|
||||||
Transform transform(dq.getRotation(),
|
|
||||||
dq.getScale(),
|
}
|
||||||
dq.getTranslation());
|
|
||||||
renderTransform = modelTransform.worldTransform(Transform(transform));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (cauterizedMeshState.clusterMatrices.size() == 1 || cauterizedMeshState.clusterMatrices.size() == 2) {
|
|
||||||
renderTransform = modelTransform.worldTransform(Transform(cauterizedMeshState.clusterMatrices[0]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
data.updateTransformForCauterizedMesh(renderTransform);
|
|
||||||
|
|
||||||
data.setEnableCauterization(enableCauterization);
|
|
||||||
data.updateKey(renderItemKeyGlobalFlags);
|
|
||||||
data.setShapeKey(invalidatePayloadShapeKey, primitiveMode, useDualQuaternionSkinning);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
scene->enqueueTransaction(transaction);
|
scene->enqueueTransaction(transaction);
|
||||||
|
|
|
@ -64,11 +64,15 @@ void MeshPartPayload::updateMeshPart(const std::shared_ptr<const graphics::Mesh>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeshPartPayload::updateTransform(const Transform& transform, const Transform& offsetTransform) {
|
void MeshPartPayload::updateTransform(const Transform& transform) {
|
||||||
_transform = transform;
|
_worldFromLocalTransform = transform;
|
||||||
Transform::mult(_drawTransform, _transform, offsetTransform);
|
|
||||||
_worldBound = _localBound;
|
_worldBound = _localBound;
|
||||||
_worldBound.transform(_drawTransform);
|
_worldBound.transform(_worldFromLocalTransform);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshPartPayload::updateTransformAndBound(const Transform& transform) {
|
||||||
|
_worldBound = _localBound;
|
||||||
|
_worldBound.transform(transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeshPartPayload::addMaterial(graphics::MaterialLayer material) {
|
void MeshPartPayload::addMaterial(graphics::MaterialLayer material) {
|
||||||
|
@ -134,7 +138,7 @@ void MeshPartPayload::bindMesh(gpu::Batch& batch) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeshPartPayload::bindTransform(gpu::Batch& batch, RenderArgs::RenderMode renderMode) const {
|
void MeshPartPayload::bindTransform(gpu::Batch& batch, RenderArgs::RenderMode renderMode) const {
|
||||||
batch.setModelTransform(_drawTransform);
|
batch.setModelTransform(_worldFromLocalTransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -196,7 +200,7 @@ template <> void payloadRender(const ModelMeshPartPayload::Pointer& payload, Ren
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform) :
|
ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform) :
|
||||||
_meshIndex(meshIndex),
|
_meshIndex(meshIndex),
|
||||||
_shapeID(shapeIndex) {
|
_shapeID(shapeIndex) {
|
||||||
|
|
||||||
|
@ -210,38 +214,16 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in
|
||||||
|
|
||||||
auto& modelMesh = model->getNetworkModel()->getMeshes().at(_meshIndex);
|
auto& modelMesh = model->getNetworkModel()->getMeshes().at(_meshIndex);
|
||||||
_meshNumVertices = (int)modelMesh->getNumVertices();
|
_meshNumVertices = (int)modelMesh->getNumVertices();
|
||||||
const Model::MeshState& state = model->getMeshState(_meshIndex);
|
// const Model::MeshState& state = model->getMeshState(_meshIndex);
|
||||||
|
|
||||||
updateMeshPart(modelMesh, partIndex);
|
updateMeshPart(modelMesh, partIndex);
|
||||||
|
|
||||||
if (useDualQuaternionSkinning) {
|
Transform renderTransform = transform;
|
||||||
computeAdjustedLocalBound(state.clusterDualQuaternions);
|
|
||||||
} else {
|
|
||||||
computeAdjustedLocalBound(state.clusterMatrices);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateTransform(transform, offsetTransform);
|
|
||||||
|
|
||||||
Transform renderTransform = transform;
|
|
||||||
|
|
||||||
/* if (useDualQuaternionSkinning) {
|
|
||||||
if (state.clusterDualQuaternions.size() == 1) {
|
|
||||||
const auto& dq = state.clusterDualQuaternions[0];
|
|
||||||
Transform transform(dq.getRotation(),
|
|
||||||
dq.getScale(),
|
|
||||||
dq.getTranslation());
|
|
||||||
renderTransform = transform.worldTransform(Transform(transform));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (state.clusterMatrices.size() == 1) {
|
|
||||||
renderTransform = transform.worldTransform(Transform(state.clusterMatrices[0]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
const Model::ShapeState& shapeState = model->getShapeState(shapeIndex);
|
const Model::ShapeState& shapeState = model->getShapeState(shapeIndex);
|
||||||
renderTransform = transform.worldTransform(shapeState._rootFromJointTransform);
|
renderTransform = transform.worldTransform(shapeState._rootFromJointTransform);
|
||||||
updateTransformForSkinnedMesh(renderTransform, transform);
|
updateTransform(renderTransform);
|
||||||
|
|
||||||
|
_deformerIndex = shape.skinDeformer;
|
||||||
|
|
||||||
initCache(model);
|
initCache(model);
|
||||||
|
|
||||||
|
@ -264,7 +246,9 @@ void ModelMeshPartPayload::initCache(const ModelPointer& model) {
|
||||||
if (_drawMesh) {
|
if (_drawMesh) {
|
||||||
auto vertexFormat = _drawMesh->getVertexFormat();
|
auto vertexFormat = _drawMesh->getVertexFormat();
|
||||||
_hasColorAttrib = vertexFormat->hasAttribute(gpu::Stream::COLOR);
|
_hasColorAttrib = vertexFormat->hasAttribute(gpu::Stream::COLOR);
|
||||||
_isSkinned = vertexFormat->hasAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT) && vertexFormat->hasAttribute(gpu::Stream::SKIN_CLUSTER_INDEX);
|
if (_deformerIndex != hfm::UNDEFINED_KEY) {
|
||||||
|
_isSkinned = vertexFormat->hasAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT) && vertexFormat->hasAttribute(gpu::Stream::SKIN_CLUSTER_INDEX);
|
||||||
|
}
|
||||||
|
|
||||||
const HFMModel& hfmModel = model->getHFMModel();
|
const HFMModel& hfmModel = model->getHFMModel();
|
||||||
const HFMMesh& mesh = hfmModel.meshes.at(_meshIndex);
|
const HFMMesh& mesh = hfmModel.meshes.at(_meshIndex);
|
||||||
|
@ -323,13 +307,6 @@ void ModelMeshPartPayload::updateClusterBuffer(const std::vector<Model::Transfor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& renderTransform, const Transform& boundTransform) {
|
|
||||||
_transform = renderTransform;
|
|
||||||
_worldBound = _adjustedLocalBound;
|
|
||||||
// _worldBound.transform(boundTransform);
|
|
||||||
_worldBound.transform(renderTransform);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note that this method is called for models but not for shapes
|
// Note that this method is called for models but not for shapes
|
||||||
void ModelMeshPartPayload::updateKey(const render::ItemKey& key) {
|
void ModelMeshPartPayload::updateKey(const render::ItemKey& key) {
|
||||||
ItemKey::Builder builder(key);
|
ItemKey::Builder builder(key);
|
||||||
|
@ -420,7 +397,7 @@ void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, RenderArgs::RenderMo
|
||||||
if (_clusterBuffer) {
|
if (_clusterBuffer) {
|
||||||
batch.setUniformBuffer(graphics::slot::buffer::Skinning, _clusterBuffer);
|
batch.setUniformBuffer(graphics::slot::buffer::Skinning, _clusterBuffer);
|
||||||
}
|
}
|
||||||
batch.setModelTransform(_transform);
|
batch.setModelTransform(_worldFromLocalTransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelMeshPartPayload::render(RenderArgs* args) {
|
void ModelMeshPartPayload::render(RenderArgs* args) {
|
||||||
|
@ -458,38 +435,6 @@ void ModelMeshPartPayload::render(RenderArgs* args) {
|
||||||
args->_details._trianglesRendered += _drawPart._numIndices / INDICES_PER_TRIANGLE;
|
args->_details._trianglesRendered += _drawPart._numIndices / INDICES_PER_TRIANGLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector<glm::mat4>& clusterMatrices) {
|
|
||||||
_adjustedLocalBound = _localBound;
|
|
||||||
if (clusterMatrices.size() > 0) {
|
|
||||||
_adjustedLocalBound.transform(clusterMatrices.back());
|
|
||||||
|
|
||||||
for (int i = 0; i < (int)clusterMatrices.size() - 1; ++i) {
|
|
||||||
AABox clusterBound = _localBound;
|
|
||||||
clusterBound.transform(clusterMatrices[i]);
|
|
||||||
_adjustedLocalBound += clusterBound;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector<Model::TransformDualQuaternion>& clusterDualQuaternions) {
|
|
||||||
_adjustedLocalBound = _localBound;
|
|
||||||
if (clusterDualQuaternions.size() > 0) {
|
|
||||||
Transform rootTransform(clusterDualQuaternions.back().getRotation(),
|
|
||||||
clusterDualQuaternions.back().getScale(),
|
|
||||||
clusterDualQuaternions.back().getTranslation());
|
|
||||||
_adjustedLocalBound.transform(rootTransform);
|
|
||||||
|
|
||||||
for (int i = 0; i < (int)clusterDualQuaternions.size() - 1; ++i) {
|
|
||||||
AABox clusterBound = _localBound;
|
|
||||||
Transform transform(clusterDualQuaternions[i].getRotation(),
|
|
||||||
clusterDualQuaternions[i].getScale(),
|
|
||||||
clusterDualQuaternions[i].getTranslation());
|
|
||||||
clusterBound.transform(transform);
|
|
||||||
_adjustedLocalBound += clusterBound;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModelMeshPartPayload::setBlendshapeBuffer(const std::unordered_map<int, gpu::BufferPointer>& blendshapeBuffers, const QVector<int>& blendedMeshSizes) {
|
void ModelMeshPartPayload::setBlendshapeBuffer(const std::unordered_map<int, gpu::BufferPointer>& blendshapeBuffers, const QVector<int>& blendedMeshSizes) {
|
||||||
if (_meshIndex < blendedMeshSizes.length() && blendedMeshSizes.at(_meshIndex) == _meshNumVertices) {
|
if (_meshIndex < blendedMeshSizes.length() && blendedMeshSizes.at(_meshIndex) == _meshNumVertices) {
|
||||||
auto blendshapeBuffer = blendshapeBuffers.find(_meshIndex);
|
auto blendshapeBuffer = blendshapeBuffers.find(_meshIndex);
|
||||||
|
|
|
@ -38,7 +38,8 @@ public:
|
||||||
virtual void updateMeshPart(const std::shared_ptr<const graphics::Mesh>& drawMesh, int partIndex);
|
virtual void updateMeshPart(const std::shared_ptr<const graphics::Mesh>& drawMesh, int partIndex);
|
||||||
|
|
||||||
virtual void notifyLocationChanged() {}
|
virtual void notifyLocationChanged() {}
|
||||||
void updateTransform(const Transform& transform, const Transform& offsetTransform);
|
void updateTransform(const Transform& transform);
|
||||||
|
void updateTransformAndBound(const Transform& transform );
|
||||||
|
|
||||||
// Render Item interface
|
// Render Item interface
|
||||||
virtual render::ItemKey getKey() const;
|
virtual render::ItemKey getKey() const;
|
||||||
|
@ -52,13 +53,11 @@ public:
|
||||||
virtual void bindTransform(gpu::Batch& batch, RenderArgs::RenderMode renderMode) const;
|
virtual void bindTransform(gpu::Batch& batch, RenderArgs::RenderMode renderMode) const;
|
||||||
|
|
||||||
// Payload resource cached values
|
// Payload resource cached values
|
||||||
Transform _drawTransform;
|
Transform _worldFromLocalTransform;
|
||||||
Transform _transform;
|
|
||||||
int _partIndex = 0;
|
int _partIndex = 0;
|
||||||
bool _hasColorAttrib { false };
|
bool _hasColorAttrib { false };
|
||||||
|
|
||||||
graphics::Box _localBound;
|
graphics::Box _localBound;
|
||||||
graphics::Box _adjustedLocalBound;
|
|
||||||
mutable graphics::Box _worldBound;
|
mutable graphics::Box _worldBound;
|
||||||
std::shared_ptr<const graphics::Mesh> _drawMesh;
|
std::shared_ptr<const graphics::Mesh> _drawMesh;
|
||||||
|
|
||||||
|
@ -86,7 +85,7 @@ namespace render {
|
||||||
|
|
||||||
class ModelMeshPartPayload : public MeshPartPayload {
|
class ModelMeshPartPayload : public MeshPartPayload {
|
||||||
public:
|
public:
|
||||||
ModelMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform);
|
ModelMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform);
|
||||||
|
|
||||||
typedef render::Payload<ModelMeshPartPayload> Payload;
|
typedef render::Payload<ModelMeshPartPayload> Payload;
|
||||||
typedef Payload::DataPointer Pointer;
|
typedef Payload::DataPointer Pointer;
|
||||||
|
@ -100,7 +99,6 @@ public:
|
||||||
|
|
||||||
// dual quaternion skinning
|
// dual quaternion skinning
|
||||||
void updateClusterBuffer(const std::vector<Model::TransformDualQuaternion>& clusterDualQuaternions);
|
void updateClusterBuffer(const std::vector<Model::TransformDualQuaternion>& clusterDualQuaternions);
|
||||||
void updateTransformForSkinnedMesh(const Transform& renderTransform, const Transform& boundTransform);
|
|
||||||
|
|
||||||
// Render Item interface
|
// Render Item interface
|
||||||
render::ShapeKey getShapeKey() const override; // shape interface
|
render::ShapeKey getShapeKey() const override; // shape interface
|
||||||
|
@ -113,12 +111,6 @@ public:
|
||||||
void bindMesh(gpu::Batch& batch) override;
|
void bindMesh(gpu::Batch& batch) override;
|
||||||
void bindTransform(gpu::Batch& batch, RenderArgs::RenderMode renderMode) const override;
|
void bindTransform(gpu::Batch& batch, RenderArgs::RenderMode renderMode) const override;
|
||||||
|
|
||||||
// matrix palette skinning
|
|
||||||
void computeAdjustedLocalBound(const std::vector<glm::mat4>& clusterMatrices);
|
|
||||||
|
|
||||||
// dual quaternion skinning
|
|
||||||
void computeAdjustedLocalBound(const std::vector<Model::TransformDualQuaternion>& clusterDualQuaternions);
|
|
||||||
|
|
||||||
gpu::BufferPointer _clusterBuffer;
|
gpu::BufferPointer _clusterBuffer;
|
||||||
|
|
||||||
enum class ClusterBufferType { Matrices, DualQuaternions };
|
enum class ClusterBufferType { Matrices, DualQuaternions };
|
||||||
|
@ -126,6 +118,7 @@ public:
|
||||||
|
|
||||||
int _meshIndex;
|
int _meshIndex;
|
||||||
int _shapeID;
|
int _shapeID;
|
||||||
|
int _deformerIndex;
|
||||||
|
|
||||||
bool _isSkinned{ false };
|
bool _isSkinned{ false };
|
||||||
bool _isBlendShaped { false };
|
bool _isBlendShaped { false };
|
||||||
|
|
|
@ -186,7 +186,7 @@ bool Model::shouldInvalidatePayloadShapeKey(int meshIndex) {
|
||||||
const auto& networkMeshes = getNetworkModel()->getMeshes();
|
const auto& networkMeshes = getNetworkModel()->getMeshes();
|
||||||
// if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown
|
// if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown
|
||||||
// to false to rebuild out mesh groups.
|
// to false to rebuild out mesh groups.
|
||||||
if (meshIndex < 0 || meshIndex >= (int)networkMeshes.size() || meshIndex >= (int)hfmModel.meshes.size() || meshIndex >= (int)_meshStates.size()) {
|
if (meshIndex < 0 || meshIndex >= (int)networkMeshes.size() || meshIndex >= (int)hfmModel.meshes.size() /* || meshIndex >= (int)_meshStates.size()*/) {
|
||||||
_needsFixupInScene = true; // trigger remove/add cycle
|
_needsFixupInScene = true; // trigger remove/add cycle
|
||||||
invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid
|
invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid
|
||||||
return true;
|
return true;
|
||||||
|
@ -233,45 +233,52 @@ void Model::updateRenderItems() {
|
||||||
auto meshIndex = self->_modelMeshRenderItemShapes[i].meshIndex;
|
auto meshIndex = self->_modelMeshRenderItemShapes[i].meshIndex;
|
||||||
|
|
||||||
const auto& shapeState = self->getShapeState(i);
|
const auto& shapeState = self->getShapeState(i);
|
||||||
const auto& meshState = self->getMeshState(meshIndex);
|
|
||||||
|
auto deformerIndex = self->_modelMeshRenderItemShapes[i].deformerIndex;
|
||||||
|
bool isDeformed = (deformerIndex != hfm::UNDEFINED_KEY);
|
||||||
|
|
||||||
bool invalidatePayloadShapeKey = self->shouldInvalidatePayloadShapeKey(meshIndex);
|
bool invalidatePayloadShapeKey = self->shouldInvalidatePayloadShapeKey(meshIndex);
|
||||||
bool useDualQuaternionSkinning = self->getUseDualQuaternionSkinning();
|
|
||||||
|
|
||||||
transaction.updateItem<ModelMeshPartPayload>(itemID, [modelTransform, shapeState, meshState, useDualQuaternionSkinning,
|
|
||||||
invalidatePayloadShapeKey, primitiveMode, renderItemKeyGlobalFlags, cauterized](ModelMeshPartPayload& data) {
|
if (isDeformed) {
|
||||||
if (useDualQuaternionSkinning) {
|
|
||||||
data.updateClusterBuffer(meshState.clusterDualQuaternions);
|
|
||||||
data.computeAdjustedLocalBound(meshState.clusterDualQuaternions);
|
|
||||||
} else {
|
|
||||||
data.updateClusterBuffer(meshState.clusterMatrices);
|
|
||||||
data.computeAdjustedLocalBound(meshState.clusterMatrices);
|
|
||||||
}
|
|
||||||
|
|
||||||
Transform renderTransform = modelTransform;
|
const auto& meshState = self->getMeshState(deformerIndex);
|
||||||
|
// MeshState meshState;
|
||||||
|
bool useDualQuaternionSkinning = self->getUseDualQuaternionSkinning();
|
||||||
|
|
||||||
/*if (useDualQuaternionSkinning) {
|
|
||||||
if (meshState.clusterDualQuaternions.size() == 1 || meshState.clusterDualQuaternions.size() == 2) {
|
transaction.updateItem<ModelMeshPartPayload>(itemID, [modelTransform, shapeState, meshState, useDualQuaternionSkinning,
|
||||||
const auto& dq = meshState.clusterDualQuaternions[0];
|
invalidatePayloadShapeKey, primitiveMode, renderItemKeyGlobalFlags, cauterized](ModelMeshPartPayload& data) {
|
||||||
Transform transform(dq.getRotation(),
|
if (useDualQuaternionSkinning) {
|
||||||
dq.getScale(),
|
data.updateClusterBuffer(meshState.clusterDualQuaternions);
|
||||||
dq.getTranslation());
|
} else {
|
||||||
renderTransform = modelTransform.worldTransform(Transform(transform));
|
data.updateClusterBuffer(meshState.clusterMatrices);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (meshState.clusterMatrices.size() == 1 || meshState.clusterMatrices.size() == 2) {
|
Transform renderTransform = modelTransform;
|
||||||
renderTransform = modelTransform.worldTransform(Transform(meshState.clusterMatrices[0]));
|
// if (meshState.clusterMatrices.size() <= 1) {
|
||||||
}
|
// renderTransform = modelTransform.worldTransform(shapeState._rootFromJointTransform);
|
||||||
}*/
|
// }
|
||||||
if (meshState.clusterMatrices.size() <= 1) {
|
data.updateTransform(renderTransform);
|
||||||
|
data.updateTransformAndBound(modelTransform.worldTransform(shapeState._rootFromJointTransform));
|
||||||
|
|
||||||
|
data.setCauterized(cauterized);
|
||||||
|
data.updateKey(renderItemKeyGlobalFlags);
|
||||||
|
data.setShapeKey(invalidatePayloadShapeKey, primitiveMode, useDualQuaternionSkinning);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
transaction.updateItem<ModelMeshPartPayload>(itemID, [modelTransform, shapeState, invalidatePayloadShapeKey, primitiveMode, renderItemKeyGlobalFlags](ModelMeshPartPayload& data) {
|
||||||
|
|
||||||
|
Transform renderTransform = modelTransform;
|
||||||
|
// if (meshState.clusterMatrices.size() <= 1) {
|
||||||
renderTransform = modelTransform.worldTransform(shapeState._rootFromJointTransform);
|
renderTransform = modelTransform.worldTransform(shapeState._rootFromJointTransform);
|
||||||
}
|
// }
|
||||||
data.updateTransformForSkinnedMesh(renderTransform, modelTransform);
|
data.updateTransform(renderTransform);
|
||||||
|
|
||||||
data.setCauterized(cauterized);
|
data.updateKey(renderItemKeyGlobalFlags);
|
||||||
data.updateKey(renderItemKeyGlobalFlags);
|
data.setShapeKey(invalidatePayloadShapeKey, primitiveMode, false);
|
||||||
data.setShapeKey(invalidatePayloadShapeKey, primitiveMode, useDualQuaternionSkinning);
|
});
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction);
|
AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction);
|
||||||
|
@ -304,9 +311,9 @@ void Model::updateShapeStatesFromRig() {
|
||||||
const auto& shapes = hfmModel.shapes;
|
const auto& shapes = hfmModel.shapes;
|
||||||
_shapeStates.resize(shapes.size());
|
_shapeStates.resize(shapes.size());
|
||||||
for (int s = 0; s < shapes.size(); ++s) {
|
for (int s = 0; s < shapes.size(); ++s) {
|
||||||
uint32_t jointId = shapes[s].transform;
|
uint32_t jointId = shapes[s].joint;
|
||||||
if (jointId < _rig.getJointStateCount()) {
|
if (jointId < (uint32_t) _rig.getJointStateCount()) {
|
||||||
_shapeStates[s]._rootFromJointTransform = _rig.getJointTransform(shapes[s].transform);
|
_shapeStates[s]._rootFromJointTransform = _rig.getJointTransform(jointId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -329,14 +336,24 @@ bool Model::updateGeometry() {
|
||||||
updateShapeStatesFromRig();
|
updateShapeStatesFromRig();
|
||||||
|
|
||||||
const HFMModel& hfmModel = getHFMModel();
|
const HFMModel& hfmModel = getHFMModel();
|
||||||
|
const auto& hfmSkinDeformers = hfmModel.skinDeformers;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
foreach (const HFMMesh& mesh, hfmModel.meshes) {
|
/* for (const auto& mesh: hfmModel.meshes) {
|
||||||
MeshState state;
|
MeshState state;
|
||||||
state.clusterDualQuaternions.resize(mesh.clusters.size());
|
state.clusterDualQuaternions.resize(mesh.clusters.size());
|
||||||
state.clusterMatrices.resize(mesh.clusters.size());
|
state.clusterMatrices.resize(mesh.clusters.size());
|
||||||
_meshStates.push_back(state);
|
_meshStates.push_back(state);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
for (int i = 0; i < hfmSkinDeformers.size(); i++) {
|
||||||
|
const auto& dynT = hfmSkinDeformers[i];
|
||||||
|
MeshState state;
|
||||||
|
state.clusterDualQuaternions.resize(dynT.clusters.size());
|
||||||
|
state.clusterMatrices.resize(dynT.clusters.size());
|
||||||
|
_meshStates.push_back(state);
|
||||||
|
}
|
||||||
|
|
||||||
needFullUpdate = true;
|
needFullUpdate = true;
|
||||||
emit rigReady();
|
emit rigReady();
|
||||||
}
|
}
|
||||||
|
@ -1410,8 +1427,34 @@ void Model::updateClusterMatrices() {
|
||||||
|
|
||||||
_needsUpdateClusterMatrices = false;
|
_needsUpdateClusterMatrices = false;
|
||||||
const HFMModel& hfmModel = getHFMModel();
|
const HFMModel& hfmModel = getHFMModel();
|
||||||
|
const auto& hfmSkinDeformers = hfmModel.skinDeformers;
|
||||||
for (int i = 0; i < (int) _meshStates.size(); i++) {
|
for (int i = 0; i < (int) _meshStates.size(); i++) {
|
||||||
MeshState& state = _meshStates[i];
|
MeshState& state = _meshStates[i];
|
||||||
|
const auto& deformer = hfmSkinDeformers[i];
|
||||||
|
|
||||||
|
int meshIndex = i;
|
||||||
|
int clusterIndex = 0;
|
||||||
|
|
||||||
|
for (int d = 0; d < deformer.clusters.size(); d++) {
|
||||||
|
const auto& cluster = deformer.clusters[d];
|
||||||
|
clusterIndex = d;
|
||||||
|
|
||||||
|
const auto& cbmov = _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex);
|
||||||
|
|
||||||
|
if (_useDualQuaternionSkinning) {
|
||||||
|
auto jointPose = _rig.getJointPose(cluster.jointIndex);
|
||||||
|
Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans());
|
||||||
|
Transform clusterTransform;
|
||||||
|
Transform::mult(clusterTransform, jointTransform, cbmov.inverseBindTransform);
|
||||||
|
state.clusterDualQuaternions[d] = Model::TransformDualQuaternion(clusterTransform);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
auto jointMatrix = _rig.getJointTransform(cluster.jointIndex);
|
||||||
|
glm_mat4u_mul(jointMatrix, cbmov.inverseBindMatrix, state.clusterMatrices[d]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
/*
|
||||||
int meshIndex = i;
|
int meshIndex = i;
|
||||||
const HFMMesh& mesh = hfmModel.meshes.at(i);
|
const HFMMesh& mesh = hfmModel.meshes.at(i);
|
||||||
for (int j = 0; j < mesh.clusters.size(); j++) {
|
for (int j = 0; j < mesh.clusters.size(); j++) {
|
||||||
|
@ -1428,7 +1471,7 @@ void Model::updateClusterMatrices() {
|
||||||
auto jointMatrix = _rig.getJointTransform(cluster.jointIndex);
|
auto jointMatrix = _rig.getJointTransform(cluster.jointIndex);
|
||||||
glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindMatrix, state.clusterMatrices[j]);
|
glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindMatrix, state.clusterMatrices[j]);
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
// post the blender if we're not currently waiting for one to finish
|
// post the blender if we're not currently waiting for one to finish
|
||||||
|
@ -1477,12 +1520,6 @@ void Model::createRenderItemSet() {
|
||||||
assert(isLoaded());
|
assert(isLoaded());
|
||||||
const auto& meshes = _renderGeometry->getMeshes();
|
const auto& meshes = _renderGeometry->getMeshes();
|
||||||
|
|
||||||
// all of our mesh vectors must match in size
|
|
||||||
if (meshes.size() != _meshStates.size()) {
|
|
||||||
qCDebug(renderutils) << "WARNING!!!! Mesh Sizes don't match! " << meshes.size() << _meshStates.size() << " We will not segregate mesh groups yet.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We should not have any existing renderItems if we enter this section of code
|
// We should not have any existing renderItems if we enter this section of code
|
||||||
Q_ASSERT(_modelMeshRenderItems.isEmpty());
|
Q_ASSERT(_modelMeshRenderItems.isEmpty());
|
||||||
|
|
||||||
|
@ -1500,6 +1537,17 @@ void Model::createRenderItemSet() {
|
||||||
|
|
||||||
// Run through all of the meshes, and place them into their segregated, but unsorted buckets
|
// Run through all of the meshes, and place them into their segregated, but unsorted buckets
|
||||||
int shapeID = 0;
|
int shapeID = 0;
|
||||||
|
const auto& shapes = _renderGeometry->getHFMModel().shapes;
|
||||||
|
for (shapeID; shapeID < shapes.size(); shapeID++) {
|
||||||
|
const auto& shape = shapes[shapeID];
|
||||||
|
|
||||||
|
_modelMeshRenderItems << std::make_shared<ModelMeshPartPayload>(shared_from_this(), shape.mesh, shape.meshPart, shapeID, transform);
|
||||||
|
|
||||||
|
auto material = getNetworkModel()->getShapeMaterial(shapeID);
|
||||||
|
_modelMeshMaterialNames.push_back(material ? material->getName() : "");
|
||||||
|
_modelMeshRenderItemShapes.emplace_back(ShapeInfo{ (int)shape.mesh, shape.skinDeformer });
|
||||||
|
}
|
||||||
|
/*
|
||||||
uint32_t numMeshes = (uint32_t)meshes.size();
|
uint32_t numMeshes = (uint32_t)meshes.size();
|
||||||
for (uint32_t i = 0; i < numMeshes; i++) {
|
for (uint32_t i = 0; i < numMeshes; i++) {
|
||||||
const auto& mesh = meshes.at(i);
|
const auto& mesh = meshes.at(i);
|
||||||
|
@ -1510,17 +1558,17 @@ void Model::createRenderItemSet() {
|
||||||
// Create the render payloads
|
// Create the render payloads
|
||||||
int numParts = (int)mesh->getNumParts();
|
int numParts = (int)mesh->getNumParts();
|
||||||
for (int partIndex = 0; partIndex < numParts; partIndex++) {
|
for (int partIndex = 0; partIndex < numParts; partIndex++) {
|
||||||
_modelMeshRenderItems << std::make_shared<ModelMeshPartPayload>(shared_from_this(), i, partIndex, shapeID, transform, offset);
|
_modelMeshRenderItems << std::make_shared<ModelMeshPartPayload>(shared_from_this(), i, partIndex, shapeID, transform);
|
||||||
auto material = getNetworkModel()->getShapeMaterial(shapeID);
|
auto material = getNetworkModel()->getShapeMaterial(shapeID);
|
||||||
_modelMeshMaterialNames.push_back(material ? material->getName() : "");
|
_modelMeshMaterialNames.push_back(material ? material->getName() : "");
|
||||||
_modelMeshRenderItemShapes.emplace_back(ShapeInfo{ (int)i });
|
_modelMeshRenderItemShapes.emplace_back(ShapeInfo{ (int)i });
|
||||||
shapeID++;
|
shapeID++;
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Model::isRenderable() const {
|
bool Model::isRenderable() const {
|
||||||
return (!_shapeStates.empty() && !_meshStates.empty()) || (isLoaded() && _renderGeometry->getMeshes().empty());
|
return (!_shapeStates.empty() /* && !_meshStates.empty()*/) || (isLoaded() && _renderGeometry->getMeshes().empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::set<unsigned int> Model::getMeshIDsFromMaterialID(QString parentMaterialName) {
|
std::set<unsigned int> Model::getMeshIDsFromMaterialID(QString parentMaterialName) {
|
||||||
|
|
|
@ -473,7 +473,7 @@ protected:
|
||||||
QVector<std::shared_ptr<ModelMeshPartPayload>> _modelMeshRenderItems;
|
QVector<std::shared_ptr<ModelMeshPartPayload>> _modelMeshRenderItems;
|
||||||
QMap<render::ItemID, render::PayloadPointer> _modelMeshRenderItemsMap;
|
QMap<render::ItemID, render::PayloadPointer> _modelMeshRenderItemsMap;
|
||||||
render::ItemIDs _modelMeshRenderItemIDs;
|
render::ItemIDs _modelMeshRenderItemIDs;
|
||||||
using ShapeInfo = struct { int meshIndex; };
|
using ShapeInfo = struct { int meshIndex; uint32_t deformerIndex{ hfm::UNDEFINED_KEY }; };
|
||||||
std::vector<ShapeInfo> _modelMeshRenderItemShapes;
|
std::vector<ShapeInfo> _modelMeshRenderItemShapes;
|
||||||
std::vector<std::string> _modelMeshMaterialNames;
|
std::vector<std::string> _modelMeshMaterialNames;
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,10 @@ public:
|
||||||
~MainWindow();
|
~MainWindow();
|
||||||
|
|
||||||
static QWindow* findMainWindow();
|
static QWindow* findMainWindow();
|
||||||
|
|
||||||
|
// This offset is used for positioning children window relative to the main window.
|
||||||
|
void setDockedWidgetRelativePositionOffset(const QSize& newOffset) { _dockedWidgetRelativePositionOffset.setWidth(newOffset.width()); _dockedWidgetRelativePositionOffset.setHeight(newOffset.height()); }
|
||||||
|
QSize getDockedWidgetRelativePositionOffset() { return _dockedWidgetRelativePositionOffset; }
|
||||||
public slots:
|
public slots:
|
||||||
void restoreGeometry();
|
void restoreGeometry();
|
||||||
void saveGeometry();
|
void saveGeometry();
|
||||||
|
@ -46,6 +50,7 @@ protected:
|
||||||
private:
|
private:
|
||||||
Setting::Handle<QRect> _windowGeometry;
|
Setting::Handle<QRect> _windowGeometry;
|
||||||
Setting::Handle<int> _windowState;
|
Setting::Handle<int> _windowState;
|
||||||
|
QSize _dockedWidgetRelativePositionOffset{ 0, 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* defined(__hifi__MainWindow__) */
|
#endif /* defined(__hifi__MainWindow__) */
|
||||||
|
|
|
@ -444,17 +444,9 @@ function updateEmoteIndicatorIcon(iconURL) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function onGeometryChanged(rect) {
|
|
||||||
updateEmoteAppBarPosition();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function onWindowMinimizedChanged(isMinimized) {
|
function onWindowMinimizedChanged(isMinimized) {
|
||||||
if (isMinimized) {
|
isWindowMinimized = isMinimized;
|
||||||
handleEmoteIndicatorVisibleChanged(false);
|
maybeChangeEmoteIndicatorVisibility(!isMinimized);
|
||||||
} else if (!HMD.active) {
|
|
||||||
handleEmoteIndicatorVisibleChanged(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -539,10 +531,11 @@ function showEmoteAppBar() {
|
||||||
x: EMOTE_APP_BAR_WIDTH_PX,
|
x: EMOTE_APP_BAR_WIDTH_PX,
|
||||||
y: EMOTE_APP_BAR_HEIGHT_PX
|
y: EMOTE_APP_BAR_HEIGHT_PX
|
||||||
},
|
},
|
||||||
position: {
|
relativePosition: {
|
||||||
x: Window.x + EMOTE_APP_BAR_LEFT_MARGIN,
|
x: EMOTE_APP_BAR_LEFT_MARGIN,
|
||||||
y: Window.y + Window.innerHeight - EMOTE_APP_BAR_BOTTOM_MARGIN
|
y: EMOTE_APP_BAR_BOTTOM_MARGIN
|
||||||
},
|
},
|
||||||
|
relativePositionAnchor: Desktop.RelativePositionAnchor.BOTTOM_LEFT,
|
||||||
overrideFlags: EMOTE_APP_BAR_WINDOW_FLAGS
|
overrideFlags: EMOTE_APP_BAR_WINDOW_FLAGS
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -550,10 +543,18 @@ function showEmoteAppBar() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function handleEmoteIndicatorVisibleChanged(shouldBeVisible) {
|
// There is currently no property in the Window Scripting Interface to determine
|
||||||
if (shouldBeVisible && !emoteAppBarWindow) {
|
// whether the Interface window is currently minimized. This feels like an oversight.
|
||||||
|
// We should add that functionality to the Window Scripting Interface, and remove `isWindowMinimized` below.
|
||||||
|
var isWindowMinimized = false;
|
||||||
|
function maybeChangeEmoteIndicatorVisibility(desiredVisibility) {
|
||||||
|
if (isWindowMinimized || HMD.active) {
|
||||||
|
desiredVisibility = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (desiredVisibility && !emoteAppBarWindow) {
|
||||||
showEmoteAppBar();
|
showEmoteAppBar();
|
||||||
} else if (emoteAppBarWindow) {
|
} else if (!desiredVisibility && emoteAppBarWindow) {
|
||||||
emoteAppBarWindow.fromQml.disconnect(onMessageFromEmoteAppBar);
|
emoteAppBarWindow.fromQml.disconnect(onMessageFromEmoteAppBar);
|
||||||
emoteAppBarWindow.close();
|
emoteAppBarWindow.close();
|
||||||
emoteAppBarWindow = false;
|
emoteAppBarWindow = false;
|
||||||
|
@ -561,23 +562,25 @@ function handleEmoteIndicatorVisibleChanged(shouldBeVisible) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function handleFTUEScreensVisibilityChanged(ftueScreenVisible) {
|
||||||
|
maybeChangeEmoteIndicatorVisibility(!ftueScreenVisible);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function onDisplayModeChanged(isHMDMode) {
|
function onDisplayModeChanged(isHMDMode) {
|
||||||
reactionsBegun.forEach(function(react) {
|
reactionsBegun.forEach(function(react) {
|
||||||
endReactionWrapper(react);
|
endReactionWrapper(react);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isHMDMode) {
|
maybeChangeEmoteIndicatorVisibility(!isHMDMode);
|
||||||
handleEmoteIndicatorVisibleChanged(false);
|
|
||||||
} else {
|
|
||||||
handleEmoteIndicatorVisibleChanged(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var emojiAPI = Script.require("./emojiApp/simplifiedEmoji.js?" + Date.now());
|
var emojiAPI = Script.require("./emojiApp/simplifiedEmoji.js");
|
||||||
var keyPressSignalsConnected = false;
|
var keyPressSignalsConnected = false;
|
||||||
var emojiCodeMap;
|
var emojiCodeMap;
|
||||||
var customEmojiCodeMap;
|
var customEmojiCodeMap;
|
||||||
|
var _this;
|
||||||
function setup() {
|
function setup() {
|
||||||
deleteOldReticles();
|
deleteOldReticles();
|
||||||
|
|
||||||
|
@ -605,16 +608,25 @@ function setup() {
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
Window.minimizedChanged.connect(onWindowMinimizedChanged);
|
Window.minimizedChanged.connect(onWindowMinimizedChanged);
|
||||||
Window.geometryChanged.connect(onGeometryChanged);
|
|
||||||
HMD.displayModeChanged.connect(onDisplayModeChanged);
|
HMD.displayModeChanged.connect(onDisplayModeChanged);
|
||||||
|
|
||||||
getSounds();
|
getSounds();
|
||||||
handleEmoteIndicatorVisibleChanged(true);
|
maybeChangeEmoteIndicatorVisibility(true);
|
||||||
|
|
||||||
Controller.keyPressEvent.connect(keyPressHandler);
|
Controller.keyPressEvent.connect(keyPressHandler);
|
||||||
Controller.keyReleaseEvent.connect(keyReleaseHandler);
|
Controller.keyReleaseEvent.connect(keyReleaseHandler);
|
||||||
keyPressSignalsConnected = true;
|
keyPressSignalsConnected = true;
|
||||||
Script.scriptEnding.connect(unload);
|
Script.scriptEnding.connect(unload);
|
||||||
|
|
||||||
|
function Emote() {
|
||||||
|
_this = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Emote.prototype = {
|
||||||
|
handleFTUEScreensVisibilityChanged: handleFTUEScreensVisibilityChanged
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Emote();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -638,7 +650,6 @@ function unload() {
|
||||||
maybeDeleteRemoteIndicatorTimeout();
|
maybeDeleteRemoteIndicatorTimeout();
|
||||||
|
|
||||||
Window.minimizedChanged.disconnect(onWindowMinimizedChanged);
|
Window.minimizedChanged.disconnect(onWindowMinimizedChanged);
|
||||||
Window.geometryChanged.disconnect(onGeometryChanged);
|
|
||||||
HMD.displayModeChanged.disconnect(onDisplayModeChanged);
|
HMD.displayModeChanged.disconnect(onDisplayModeChanged);
|
||||||
|
|
||||||
if (keyPressSignalsConnected) {
|
if (keyPressSignalsConnected) {
|
||||||
|
@ -671,7 +682,6 @@ function unload() {
|
||||||
// #region EMOJI_UTILITY
|
// #region EMOJI_UTILITY
|
||||||
|
|
||||||
|
|
||||||
var EMOJI_52_BASE_URL = "../../resources/images/emojis/52px/";
|
|
||||||
function selectedEmoji(code) {
|
function selectedEmoji(code) {
|
||||||
emojiAPI.addEmoji(code);
|
emojiAPI.addEmoji(code);
|
||||||
// this URL needs to be relative to SimplifiedEmoteIndicator.qml
|
// this URL needs to be relative to SimplifiedEmoteIndicator.qml
|
||||||
|
@ -786,4 +796,6 @@ function toggleEmojiApp() {
|
||||||
// END EMOJI
|
// END EMOJI
|
||||||
// *************************************
|
// *************************************
|
||||||
|
|
||||||
setup();
|
var emote = setup();
|
||||||
|
|
||||||
|
module.exports = emote;
|
|
@ -1,15 +1,40 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!-- Generator: Adobe Illustrator 23.0.6, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
<!-- Generator: Adobe Illustrator 23.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"
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
|
||||||
viewBox="0 0 93.7 127.8" style="enable-background:new 0 0 93.7 127.8;" xml:space="preserve">
|
<!ENTITY ns_extend "http://ns.adobe.com/Extensibility/1.0/">
|
||||||
<path d="M91.9,97c-7.6,9.6-17.8,16.7-29.3,20.3h-0.1c-0.4,0.1-0.8,0.3-1.3,0.4c-4.6,1.3-9.5,2-14.4,2c-4.9,0-9.8-0.7-14.4-2
|
<!ENTITY ns_ai "http://ns.adobe.com/AdobeIllustrator/10.0/">
|
||||||
c-0.4-0.1-0.8-0.3-1.3-0.4h-0.1C19.6,113.8,9.4,106.6,1.8,97c0,0-0.5-0.5-0.9,0.1C0.6,97.6,1,98.5,1,98.5
|
<!ENTITY ns_graphs "http://ns.adobe.com/Graphs/1.0/">
|
||||||
C9.9,116,27,127.8,46.8,127.8l0,0c0,0,0.1,0,0.1,0c0,0,0.1,0,0.1,0l0,0c19.7-0.1,36.8-11.8,45.8-29.3c0,0,0.4-0.9,0.1-1.4
|
<!ENTITY ns_vars "http://ns.adobe.com/Variables/1.0/">
|
||||||
C92.4,96.6,91.9,97,91.9,97z M91.9,78.8C84.3,88.4,74.1,95.5,62.6,99h-0.1c-0.4,0.1-0.8,0.3-1.3,0.4c-4.6,1.3-9.5,2-14.4,2
|
<!ENTITY ns_imrep "http://ns.adobe.com/ImageReplacement/1.0/">
|
||||||
c-4.9,0-9.8-0.7-14.4-2c-0.4-0.1-0.8-0.3-1.3-0.4h-0.1C19.6,95.5,9.4,88.4,1.8,78.8c0,0-0.5-0.5-0.9,0.1C0.6,79.3,1,80.2,1,80.2
|
<!ENTITY ns_sfw "http://ns.adobe.com/SaveForWeb/1.0/">
|
||||||
c8.9,17.5,26.1,29.2,45.8,29.3l0,0c0,0,0.1,0,0.1,0c0,0,0.1,0,0.1,0l0,0c19.7-0.1,36.8-11.8,45.8-29.3c0,0,0.4-0.9,0.1-1.4
|
<!ENTITY ns_custom "http://ns.adobe.com/GenericCustomNamespace/1.0/">
|
||||||
C92.4,78.3,91.9,78.8,91.9,78.8z M46.9,92.7c25.8,0,46.9-20.7,46.9-46.4S72.7,0,46.9,0S0,20.7,0,46.4S20.9,92.7,46.9,92.7z
|
<!ENTITY ns_adobe_xpath "http://ns.adobe.com/XPath/1.0/">
|
||||||
M65.5,30.8c2.8,0,5,2.2,5,5s-2.2,5-5,5c-2.8,0-5-2.2-5-5S62.8,30.8,65.5,30.8z M36,54.3c2.7,3.2,6.6,5,10.8,5s8.2-1.9,10.8-5
|
]>
|
||||||
c0.9-1.1,2.7-1.2,3.8-0.4c1.1,0.9,1.3,2.6,0.3,3.7c-3.7,4.4-9.1,6.9-14.9,6.9c-5.8,0-11.2-2.5-14.9-6.9c-0.9-1.1-0.8-2.7,0.3-3.7
|
<svg version="1.1" id="Layer_1" xmlns:x="&ns_extend;" xmlns:i="&ns_ai;" xmlns:graph="&ns_graphs;"
|
||||||
C33.4,53.1,35,53.2,36,54.3z M28.1,30.8c2.8,0,5,2.2,5,5s-2.2,5-5,5c-2.8,0-5-2.2-5-5S25.3,30.8,28.1,30.8z"/>
|
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 52.8 49"
|
||||||
|
style="enable-background:new 0 0 52.8 49;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{opacity:0.7;}
|
||||||
|
.st1{fill:#FFFFFF;}
|
||||||
|
</style>
|
||||||
|
<metadata>
|
||||||
|
<sfw xmlns="&ns_sfw;">
|
||||||
|
<slices></slices>
|
||||||
|
<sliceSourceBounds bottomLeftOrigin="true" height="49" width="52.8" x="8.3" y="11.1"></sliceSourceBounds>
|
||||||
|
</sfw>
|
||||||
|
</metadata>
|
||||||
|
<g class="st0">
|
||||||
|
<path class="st1" d="M10.3,47.8c5.1,0.8,10.3,1.2,15.3,1.2c0.3,0,0.6,0,0.8,0c5.4-0.1,10.8-0.6,16.2-1.5c4-0.8,7.8-4.5,8.7-8.5
|
||||||
|
c1-4.8,1.5-9.8,1.5-14.6s-0.5-9.8-1.5-14.6c-0.8-3.9-4.7-7.6-8.7-8.4C37.3,0.6,31.8,0,26.4,0c-5.3-0.1-10.8,0.3-16.2,1.2
|
||||||
|
c-4,0.7-7.9,4.5-8.7,8.5c-1,5-1.5,9.9-1.5,14.9s0.5,9.9,1.5,14.9C2.4,43.3,6.2,47.1,10.3,47.8z M6.2,10.6C6.7,8.6,9,6.3,11,5.9
|
||||||
|
c4.9-0.8,9.7-1.2,14.6-1.2c0.3,0,0.6,0,0.8,0c5.1,0.1,10.3,0.6,15.3,1.4c2,0.4,4.4,2.7,4.8,4.6c0.9,4.5,1.3,9.1,1.3,13.7
|
||||||
|
s-0.4,9.1-1.3,13.6c-0.4,1.9-2.8,4.3-4.8,4.7c-5.1,0.9-10.3,1.4-15.3,1.4c-5.1,0.1-10.3-0.3-15.3-1.2c-2-0.3-4.4-2.7-4.8-4.6
|
||||||
|
c-0.9-4.6-1.4-9.3-1.4-14C4.9,19.9,5.3,15.1,6.2,10.6z"/>
|
||||||
|
<path class="st1" d="M14.1,32.3c-0.3,0.9-0.1,1.9,0.6,2.6c0.2,0.2,5,4.5,11,4.5c0.6,0,1.2-0.1,1.8-0.1c6.8-1,10.7-7.6,11-7.9
|
||||||
|
c0.5-0.8,0.4-1.9-0.1-2.7c-0.6-0.8-1.6-1.1-2.6-0.9c-6.4,1.7-12.9,2.7-19.5,2.9C15.3,30.8,14.5,31.4,14.1,32.3z"/>
|
||||||
|
<path class="st1" d="M17.1,25.7c3.4,0,6.2-2.8,6.2-6.2c0-3.4-2.8-6.2-6.2-6.2c-3.4,0-6.2,2.8-6.2,6.2C10.8,23,13.6,25.7,17.1,25.7z
|
||||||
|
"/>
|
||||||
|
<path class="st1" d="M34.3,20.4h4.2c1.3,0,2.3-1,2.3-2.3c0-1.3-1-2.3-2.3-2.3h-4.2c-1.3,0-2.3,1-2.3,2.3
|
||||||
|
C32,19.5,33.1,20.4,34.3,20.4z"/>
|
||||||
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 2.5 KiB |
350
scripts/simplifiedUI/ui/simplifiedFTUE/InitialLaunchWindow.qml
Normal file
|
@ -0,0 +1,350 @@
|
||||||
|
//
|
||||||
|
// InitialLaunchWindow.qml
|
||||||
|
//
|
||||||
|
// Copyright 2019 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
import QtQuick 2.10
|
||||||
|
import QtQuick.Controls 2.3
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
import stylesUit 1.0 as HifiStylesUit
|
||||||
|
import TabletScriptingInterface 1.0
|
||||||
|
import hifi.simplifiedUI.simplifiedConstants 1.0 as SimplifiedConstants
|
||||||
|
import hifi.simplifiedUI.simplifiedControls 1.0 as SimplifiedControls
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
color: simplifiedUI.colors.white
|
||||||
|
anchors.fill: parent
|
||||||
|
property bool landscapeOrientation: root.width > root.height
|
||||||
|
|
||||||
|
SimplifiedConstants.SimplifiedConstants {
|
||||||
|
id: simplifiedUI
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
var debugFTUE = Settings.getValue("simplifiedUI/debugFTUE", 0);
|
||||||
|
|
||||||
|
if ((debugFTUE !== 1 &&
|
||||||
|
(Settings.getValue("simplifiedUI/alreadyAutoSelectedAvatarFromInventory", false) ||
|
||||||
|
Settings.getValue("simplifiedUI/closedAvatarPageOfInitialLaunchWindow", false))) ||
|
||||||
|
debugFTUE === 2) {
|
||||||
|
tempAvatarPageContainer.visible = false;
|
||||||
|
controlsContainer.visible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: tempAvatarPageContainer
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: contentContainer
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: firstPageBottomBarContainer.top
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: avatarImage
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
height: Math.max(parent.height - 48, 350)
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 12
|
||||||
|
source: resourceDirectoryUrl + "qml/hifi/simplifiedUI/avatarApp/images/" +
|
||||||
|
MyAvatar.skeletonModelURL.substring(MyAvatar.skeletonModelURL.indexOf("simplifiedAvatar"), MyAvatar.skeletonModelURL.lastIndexOf("/")) + ".png"
|
||||||
|
mipmap: true
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
}
|
||||||
|
|
||||||
|
Flickable {
|
||||||
|
id: textContainer
|
||||||
|
clip: true
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 128
|
||||||
|
anchors.bottom: qrAndInstructionsContainer.top
|
||||||
|
anchors.bottomMargin: 32
|
||||||
|
anchors.left: avatarImage.right
|
||||||
|
anchors.leftMargin: 48
|
||||||
|
anchors.right: parent.right
|
||||||
|
contentWidth: width
|
||||||
|
contentHeight: contentItem.childrenRect.height
|
||||||
|
interactive: contentHeight > height
|
||||||
|
|
||||||
|
HifiStylesUit.RalewayBold {
|
||||||
|
id: headerText
|
||||||
|
text: "We know this isn't you..."
|
||||||
|
color: simplifiedUI.colors.text.black
|
||||||
|
size: 48
|
||||||
|
height: paintedHeight
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 16
|
||||||
|
}
|
||||||
|
|
||||||
|
HifiStylesUit.RalewayRegular {
|
||||||
|
id: descriptionText
|
||||||
|
anchors.top: headerText.bottom
|
||||||
|
anchors.topMargin: 10
|
||||||
|
anchors.left: parent.left
|
||||||
|
width: Math.min(700, parent.width) - headerText.anchors.rightMargin
|
||||||
|
height: paintedHeight
|
||||||
|
text: "...but we've given you this <b>temporary avatar</b> to use " +
|
||||||
|
"for today. If you see this avatar in-world, walk up and " +
|
||||||
|
"say hello to other new users!<br><br>" +
|
||||||
|
"<b>We want you to be you</b> so we've built " +
|
||||||
|
'<a href="https://www.highfidelity.com/knowledge/virtual-you/">Virtual You</a>, an Avatar Creator ' +
|
||||||
|
"App. Creating an avatar is as easy as taking a selfie and picking your " +
|
||||||
|
"outfits! Available now on iOS and Android."
|
||||||
|
color: simplifiedUI.colors.text.black
|
||||||
|
size: 22
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
|
||||||
|
onLinkActivated: {
|
||||||
|
Qt.openUrlExternally(link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: qrAndInstructionsContainer
|
||||||
|
anchors.left: avatarImage.right
|
||||||
|
anchors.leftMargin: 48
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 16
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
height: 130
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: avatarAppQRCodeImage
|
||||||
|
source: resourceDirectoryUrl + "qml/hifi/simplifiedUI/avatarApp/images/qrCode.jpg"
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
width: 130
|
||||||
|
mipmap: true
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
}
|
||||||
|
|
||||||
|
HifiStylesUit.RalewayBold {
|
||||||
|
id: instructionText
|
||||||
|
anchors.top: avatarAppQRCodeImage.top
|
||||||
|
anchors.bottom: avatarAppQRCodeImage.bottom
|
||||||
|
anchors.left: avatarAppQRCodeImage.right
|
||||||
|
anchors.leftMargin: 30
|
||||||
|
anchors.right: parent.right
|
||||||
|
text: "Use your mobile phone to scan this QR code."
|
||||||
|
color: simplifiedUI.colors.text.black
|
||||||
|
size: 22
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SimplifiedControls.VerticalScrollBar {
|
||||||
|
parent: textContainer
|
||||||
|
visible: parent.contentHeight > parent.height
|
||||||
|
size: parent.height / parent.contentHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: firstPageBottomBarContainer
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 32
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 32
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
height: continueLink.height + 48
|
||||||
|
|
||||||
|
HifiStylesUit.RalewayBold {
|
||||||
|
id: continueLink
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: "Continue >"
|
||||||
|
width: parent.width
|
||||||
|
height: paintedHeight
|
||||||
|
color: simplifiedUI.colors.text.lightBlue
|
||||||
|
opacity: continueMouseArea.containsMouse ? 1.0 : 0.7
|
||||||
|
size: 36
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: continueMouseArea
|
||||||
|
hoverEnabled: true
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
Tablet.playSound(TabletEnums.ButtonClick);
|
||||||
|
tempAvatarPageContainer.visible = false;
|
||||||
|
Settings.setValue("simplifiedUI/closedAvatarPageOfInitialLaunchWindow", true);
|
||||||
|
controlsContainer.visible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: controlsContainer
|
||||||
|
visible: false
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
HifiStylesUit.RalewayRegular {
|
||||||
|
id: controlsDescriptionText
|
||||||
|
text: "Use these avatar controls to<br><b>interact with and move around in your new HQ.</b>"
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 48
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 32
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 32
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
height: paintedHeight
|
||||||
|
color: simplifiedUI.colors.text.black
|
||||||
|
size: 36
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
anchors.top: controlsDescriptionText.bottom
|
||||||
|
anchors.topMargin: 16
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: bottomBarContainer.top
|
||||||
|
|
||||||
|
GridView {
|
||||||
|
id: controlsGrid
|
||||||
|
property int maxColumns: 2
|
||||||
|
property int idealCellWidth: 361
|
||||||
|
anchors.fill: parent
|
||||||
|
clip: true
|
||||||
|
cellWidth: width / Math.min(Math.floor(width / idealCellWidth), maxColumns)
|
||||||
|
cellHeight: 225
|
||||||
|
model: ListModel {
|
||||||
|
ListElement {
|
||||||
|
imageHeight: 198
|
||||||
|
imageSource: "images/walkingControls.png"
|
||||||
|
}
|
||||||
|
ListElement {
|
||||||
|
imageHeight: 193
|
||||||
|
imageSource: "images/mouseControls.png"
|
||||||
|
}
|
||||||
|
ListElement {
|
||||||
|
imageHeight: 146
|
||||||
|
imageSource: "images/runJumpControls.png"
|
||||||
|
}
|
||||||
|
ListElement {
|
||||||
|
imageHeight: 96
|
||||||
|
imageSource: "images/cameraControls.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delegate: Rectangle {
|
||||||
|
height: GridView.view.cellHeight
|
||||||
|
width: GridView.view.cellWidth
|
||||||
|
Image {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: parent.GridView.view.idealCellWidth
|
||||||
|
height: model.imageHeight
|
||||||
|
source: model.imageSource
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SimplifiedControls.VerticalScrollBar {
|
||||||
|
parent: controlsGrid
|
||||||
|
anchors.topMargin: 96
|
||||||
|
anchors.bottomMargin: anchors.topMargin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: bottomBarContainer
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 32
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 32
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
height: iHaveAGoodGrip.height + learnMoreLink.height + 48
|
||||||
|
|
||||||
|
HifiStylesUit.RalewayBold {
|
||||||
|
id: iHaveAGoodGrip
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: "I've got a good grip on the controls."
|
||||||
|
width: parent.width
|
||||||
|
height: paintedHeight
|
||||||
|
color: simplifiedUI.colors.text.lightBlue
|
||||||
|
opacity: goodGripMouseArea.containsMouse ? 1.0 : 0.7
|
||||||
|
size: 36
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: goodGripMouseArea
|
||||||
|
hoverEnabled: true
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
Tablet.playSound(TabletEnums.ButtonClick);
|
||||||
|
sendToScript({
|
||||||
|
"source": "InitialLaunchWindow.qml",
|
||||||
|
"method": "closeInitialLaunchWindow"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HifiStylesUit.RalewayBold {
|
||||||
|
id: learnMoreLink
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 16
|
||||||
|
anchors.top: iHaveAGoodGrip.bottom
|
||||||
|
anchors.topMargin: 8
|
||||||
|
text: "Learn more about our controls."
|
||||||
|
width: paintedWidth
|
||||||
|
height: paintedHeight
|
||||||
|
color: simplifiedUI.colors.text.lightBlue
|
||||||
|
opacity: learnMoreAboutControlsMouseArea.containsMouse ? 1.0 : 0.7
|
||||||
|
size: 14
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: learnMoreAboutControlsMouseArea
|
||||||
|
hoverEnabled: true
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
Tablet.playSound(TabletEnums.ButtonClick);
|
||||||
|
Qt.openUrlExternally("https://www.highfidelity.com/knowledge/get-around");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: topLeftAccentImage
|
||||||
|
width: 400
|
||||||
|
height: 180
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.top: parent.top
|
||||||
|
source: "images/defaultTopLeft.png"
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: bottomRightAccentImage
|
||||||
|
width: 80
|
||||||
|
height: 250
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
source: "images/defaultBottomRight.png"
|
||||||
|
}
|
||||||
|
|
||||||
|
signal sendToScript(var message);
|
||||||
|
}
|
186
scripts/simplifiedUI/ui/simplifiedFTUE/SecondLaunchWindow.qml
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
//
|
||||||
|
// SecondLaunchWindow.qml
|
||||||
|
//
|
||||||
|
// Copyright 2019 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
import QtQuick 2.10
|
||||||
|
import QtQuick.Controls 2.3
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
import stylesUit 1.0 as HifiStylesUit
|
||||||
|
import TabletScriptingInterface 1.0
|
||||||
|
import hifi.simplifiedUI.simplifiedConstants 1.0 as SimplifiedConstants
|
||||||
|
import hifi.simplifiedUI.simplifiedControls 1.0 as SimplifiedControls
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
color: simplifiedUI.colors.white
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
SimplifiedConstants.SimplifiedConstants {
|
||||||
|
id: simplifiedUI
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: contentContainer
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: continueLink.top
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: avatarImage
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
height: Math.max(parent.height - 48, 350)
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 12
|
||||||
|
source: resourceDirectoryUrl + "qml/hifi/simplifiedUI/avatarApp/images/hero.png"
|
||||||
|
mipmap: true
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 196
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 32
|
||||||
|
anchors.left: avatarImage.right
|
||||||
|
anchors.leftMargin: 48
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
Flickable {
|
||||||
|
id: textContainer
|
||||||
|
clip: true
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
width: Math.min(700, parent.width)
|
||||||
|
contentWidth: width
|
||||||
|
contentHeight: contentItem.childrenRect.height
|
||||||
|
interactive: contentHeight > height
|
||||||
|
|
||||||
|
HifiStylesUit.RalewayBold {
|
||||||
|
id: headerText
|
||||||
|
text: "Stand out from the crowd!"
|
||||||
|
color: simplifiedUI.colors.text.black
|
||||||
|
size: 48
|
||||||
|
height: paintedHeight
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 16
|
||||||
|
}
|
||||||
|
|
||||||
|
HifiStylesUit.RalewayRegular {
|
||||||
|
id: descriptionText
|
||||||
|
anchors.top: headerText.bottom
|
||||||
|
anchors.topMargin: 10
|
||||||
|
anchors.left: parent.left
|
||||||
|
width: parent.width - headerText.anchors.rightMargin
|
||||||
|
height: paintedHeight
|
||||||
|
text: "You can create and upload custom avatars from our Avatar Creator App. " +
|
||||||
|
"It's as easy as taking a selfie.<br>Available now on iOS and Android Platforms."
|
||||||
|
color: simplifiedUI.colors.text.black
|
||||||
|
size: 22
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: qrAndInstructionsContainer
|
||||||
|
anchors.top: descriptionText.bottom
|
||||||
|
anchors.topMargin: 24
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 16
|
||||||
|
height: avatarAppQRCodeImage.height
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: avatarAppQRCodeImage
|
||||||
|
source: resourceDirectoryUrl + "qml/hifi/simplifiedUI/avatarApp/images/qrCode.jpg"
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
width: 130
|
||||||
|
height: width
|
||||||
|
mipmap: true
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
}
|
||||||
|
|
||||||
|
HifiStylesUit.RalewayBold {
|
||||||
|
id: instructionText
|
||||||
|
anchors.top: avatarAppQRCodeImage.top
|
||||||
|
anchors.bottom: avatarAppQRCodeImage.bottom
|
||||||
|
anchors.left: avatarAppQRCodeImage.right
|
||||||
|
anchors.leftMargin: 30
|
||||||
|
anchors.right: parent.right
|
||||||
|
text: "Use your mobile phone to scan this QR code."
|
||||||
|
color: simplifiedUI.colors.text.black
|
||||||
|
size: 22
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SimplifiedControls.VerticalScrollBar {
|
||||||
|
parent: textContainer
|
||||||
|
visible: parent.contentHeight > parent.height
|
||||||
|
size: parent.height / parent.contentHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HifiStylesUit.RalewayBold {
|
||||||
|
id: continueLink
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 16
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 16
|
||||||
|
height: 96
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
text: "No thanks, I'll keep using my default avatar."
|
||||||
|
color: simplifiedUI.colors.text.lightBlue
|
||||||
|
opacity: continueMouseArea.containsMouse ? 1.0 : 0.7
|
||||||
|
size: 24
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: continueMouseArea
|
||||||
|
hoverEnabled: true
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
Tablet.playSound(TabletEnums.ButtonClick);
|
||||||
|
sendToScript({
|
||||||
|
"source": "SecondLaunchWindow.qml",
|
||||||
|
"method": "closeSecondLaunchWindow"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: topLeftAccentImage
|
||||||
|
width: 130
|
||||||
|
height: 320
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.top: parent.top
|
||||||
|
source: "images/standOutTopLeft.png"
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: bottomRightAccentImage
|
||||||
|
width: 250
|
||||||
|
height: 80
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
source: "images/standOutBottomRight.png"
|
||||||
|
}
|
||||||
|
|
||||||
|
signal sendToScript(var message);
|
||||||
|
}
|
BIN
scripts/simplifiedUI/ui/simplifiedFTUE/images/cameraControls.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
After Width: | Height: | Size: 2.8 KiB |
BIN
scripts/simplifiedUI/ui/simplifiedFTUE/images/defaultTopLeft.png
Normal file
After Width: | Height: | Size: 2 KiB |
BIN
scripts/simplifiedUI/ui/simplifiedFTUE/images/mouseControls.png
Normal file
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 5.7 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 9.7 KiB |
|
@ -14,7 +14,6 @@
|
||||||
|
|
||||||
|
|
||||||
// START CONFIG OPTIONS
|
// START CONFIG OPTIONS
|
||||||
var DOCKED_QML_SUPPORTED = true;
|
|
||||||
var TOOLBAR_NAME = "com.highfidelity.interface.toolbar.system";
|
var TOOLBAR_NAME = "com.highfidelity.interface.toolbar.system";
|
||||||
var DEFAULT_SCRIPTS_PATH_PREFIX = ScriptDiscoveryService.defaultScriptsPath + "/";
|
var DEFAULT_SCRIPTS_PATH_PREFIX = ScriptDiscoveryService.defaultScriptsPath + "/";
|
||||||
// END CONFIG OPTIONS
|
// END CONFIG OPTIONS
|
||||||
|
@ -284,28 +283,21 @@ function maybeDeleteOutputDeviceMutedOverlay() {
|
||||||
|
|
||||||
var outputDeviceMutedOverlay = false;
|
var outputDeviceMutedOverlay = false;
|
||||||
var OUTPUT_DEVICE_MUTED_OVERLAY_DEFAULT_DIMS_PX = 300;
|
var OUTPUT_DEVICE_MUTED_OVERLAY_DEFAULT_DIMS_PX = 300;
|
||||||
var OUTPUT_DEVICE_MUTED_MARGIN_BOTTOM_PX = 20;
|
var OUTPUT_DEVICE_MUTED_OVERLAY_DEFAULT_MARGINS_PX = 20;
|
||||||
var OUTPUT_DEVICE_MUTED_MARGIN_LEFT_RIGHT_PX = 20;
|
var OUTPUT_DEVICE_MUTED_DIMS_RATIO_TO_WINDOW_SIZE = 0.8;
|
||||||
function updateOutputDeviceMutedOverlay(isMuted) {
|
function updateOutputDeviceMutedOverlay(isMuted) {
|
||||||
if (isMuted) {
|
if (isMuted) {
|
||||||
var props = {
|
var props = {
|
||||||
imageURL: Script.resolvePath("images/outputDeviceMuted.svg"),
|
imageURL: Script.resolvePath("images/outputDeviceMuted.svg"),
|
||||||
alpha: 0.5
|
alpha: 0.5
|
||||||
};
|
};
|
||||||
|
|
||||||
var overlayDims = OUTPUT_DEVICE_MUTED_OVERLAY_DEFAULT_DIMS_PX;
|
var overlayDims = OUTPUT_DEVICE_MUTED_OVERLAY_DEFAULT_DIMS_PX;
|
||||||
props.x = Window.innerWidth / 2 - overlayDims / 2;
|
var overlayWithMarginsDims = overlayDims + 2 * OUTPUT_DEVICE_MUTED_OVERLAY_DEFAULT_MARGINS_PX;
|
||||||
props.y = Window.innerHeight / 2 - overlayDims / 2;
|
|
||||||
|
|
||||||
var outputDeviceMutedOverlayBottomY = props.y + overlayDims;
|
if (overlayWithMarginsDims > Window.innerHeight || overlayWithMarginsDims > Window.innerWidth) {
|
||||||
var inputDeviceMutedOverlayTopY = INPUT_DEVICE_MUTED_MARGIN_TOP_PX;
|
var minWindowDims = Math.min(Window.innerHeight, Window.innerWidth);
|
||||||
if (outputDeviceMutedOverlayBottomY + OUTPUT_DEVICE_MUTED_MARGIN_BOTTOM_PX > inputDeviceMutedOverlayTopY) {
|
overlayDims = Math.round(minWindowDims * OUTPUT_DEVICE_MUTED_DIMS_RATIO_TO_WINDOW_SIZE);
|
||||||
overlayDims = 2 * (inputDeviceMutedOverlayTopY - Window.innerHeight / 2 - OUTPUT_DEVICE_MUTED_MARGIN_BOTTOM_PX);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (overlayDims + OUTPUT_DEVICE_MUTED_MARGIN_LEFT_RIGHT_PX > Window.innerWidth) {
|
|
||||||
overlayDims = Math.min(Window.innerWidth - OUTPUT_DEVICE_MUTED_MARGIN_LEFT_RIGHT_PX, overlayDims);
|
|
||||||
} else {
|
|
||||||
overlayDims = Math.min(OUTPUT_DEVICE_MUTED_OVERLAY_DEFAULT_DIMS_PX, overlayDims);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
props.width = overlayDims;
|
props.width = overlayDims;
|
||||||
|
@ -359,6 +351,124 @@ function setOutputMuted(outputMuted) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var TOP_BAR_HEIGHT_PX = 48;
|
||||||
|
var INITIAL_LAUNCH_QML_PATH = Script.resolvePath("./simplifiedFTUE/InitialLaunchWindow.qml");
|
||||||
|
var INITIAL_LAUNCH_WINDOW_TITLE = "Initial Launch";
|
||||||
|
var INITIAL_LAUNCH_PRESENTATION_MODE = Desktop.PresentationMode.NATIVE;
|
||||||
|
var INITIAL_WINDOW_FLAGS = 0x00000001 | // Qt::Window
|
||||||
|
0x00000008 | // Qt::Popup
|
||||||
|
0x00000002 | // Qt::Tool
|
||||||
|
0x00000800 | // Qt::FramelessWindowHint
|
||||||
|
0x40000000; // Qt::NoDropShadowWindowHint
|
||||||
|
var initialLaunchWindow = false;
|
||||||
|
function displayInitialLaunchWindow() {
|
||||||
|
if (initialLaunchWindow) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
simplifiedEmote.handleFTUEScreensVisibilityChanged(true);
|
||||||
|
|
||||||
|
initialLaunchWindow = Desktop.createWindow(INITIAL_LAUNCH_QML_PATH, {
|
||||||
|
title: INITIAL_LAUNCH_WINDOW_TITLE,
|
||||||
|
presentationMode: INITIAL_LAUNCH_PRESENTATION_MODE,
|
||||||
|
isFullScreenWindow: true,
|
||||||
|
overrideFlags: INITIAL_WINDOW_FLAGS
|
||||||
|
});
|
||||||
|
|
||||||
|
initialLaunchWindow.fromQml.connect(onMessageFromInitialLaunchWindow);
|
||||||
|
|
||||||
|
Window.location = "file:///~/serverless/tutorial.json";
|
||||||
|
}
|
||||||
|
|
||||||
|
var SECOND_LAUNCH_QML_PATH = Script.resolvePath("simplifiedFTUE/SecondLaunchWindow.qml");
|
||||||
|
var SECOND_LAUNCH_WINDOW_TITLE = "Second Launch";
|
||||||
|
var SECOND_LAUNCH_PRESENTATION_MODE = Desktop.PresentationMode.NATIVE;
|
||||||
|
var SECOND_WINDOW_FLAGS = 0x00000001 | // Qt::Window
|
||||||
|
0x00000008 | // Qt::Popup
|
||||||
|
0x00000002 | // Qt::Tool
|
||||||
|
0x00000800 | // Qt::FramelessWindowHint
|
||||||
|
0x40000000; // Qt::NoDropShadowWindowHint
|
||||||
|
var secondLaunchWindow = false;
|
||||||
|
function displaySecondLaunchWindow() {
|
||||||
|
if (secondLaunchWindow) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
simplifiedEmote.handleFTUEScreensVisibilityChanged(true);
|
||||||
|
|
||||||
|
secondLaunchWindow = Desktop.createWindow(SECOND_LAUNCH_QML_PATH, {
|
||||||
|
title: SECOND_LAUNCH_WINDOW_TITLE,
|
||||||
|
presentationMode: SECOND_LAUNCH_PRESENTATION_MODE,
|
||||||
|
isFullScreenWindow: true,
|
||||||
|
overrideFlags: SECOND_WINDOW_FLAGS
|
||||||
|
});
|
||||||
|
|
||||||
|
secondLaunchWindow.fromQml.connect(onMessageFromSecondLaunchWindow);
|
||||||
|
|
||||||
|
Window.location = "file:///~/serverless/tutorial.json";
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeInitialLaunchWindow() {
|
||||||
|
if (initialLaunchWindow) {
|
||||||
|
initialLaunchWindow.fromQml.disconnect(onMessageFromInitialLaunchWindow);
|
||||||
|
initialLaunchWindow.close();
|
||||||
|
initialLaunchWindow = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
simplifiedEmote.handleFTUEScreensVisibilityChanged(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeSecondLaunchWindow() {
|
||||||
|
if (secondLaunchWindow) {
|
||||||
|
secondLaunchWindow.fromQml.disconnect(onMessageFromSecondLaunchWindow);
|
||||||
|
secondLaunchWindow.close();
|
||||||
|
secondLaunchWindow = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
simplifiedEmote.handleFTUEScreensVisibilityChanged(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
var INITIAL_LAUNCH_WINDOW_MESSAGE_SOURCE = "InitialLaunchWindow.qml";
|
||||||
|
function onMessageFromInitialLaunchWindow(message) {
|
||||||
|
if (message.source !== INITIAL_LAUNCH_WINDOW_MESSAGE_SOURCE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (message.method) {
|
||||||
|
case "closeInitialLaunchWindow":
|
||||||
|
closeInitialLaunchWindow();
|
||||||
|
var homeLocation = LocationBookmarks.getAddress("hqhome");
|
||||||
|
if (homeLocation) {
|
||||||
|
Window.location = homeLocation;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
console.log("Unrecognized message from " + INITIAL_LAUNCH_WINDOW_MESSAGE_SOURCE + ": " + JSON.stringify(message));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var SECOND_LAUNCH_WINDOW_MESSAGE_SOURCE = "SecondLaunchWindow.qml";
|
||||||
|
function onMessageFromSecondLaunchWindow(message) {
|
||||||
|
if (message.source !== SECOND_LAUNCH_WINDOW_MESSAGE_SOURCE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (message.method) {
|
||||||
|
case "closeSecondLaunchWindow":
|
||||||
|
closeSecondLaunchWindow();
|
||||||
|
var homeLocation = LocationBookmarks.getAddress("hqhome");
|
||||||
|
if (homeLocation) {
|
||||||
|
Window.location = homeLocation;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
console.log("Unrecognized message from " + SECOND_LAUNCH_WINDOW_MESSAGE_SOURCE + ": " + JSON.stringify(message));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var WAIT_FOR_TOP_BAR_MS = 1000;
|
var WAIT_FOR_TOP_BAR_MS = 1000;
|
||||||
function sendLocalStatusToQml() {
|
function sendLocalStatusToQml() {
|
||||||
|
@ -404,6 +514,14 @@ function onMessageFromTopBar(message) {
|
||||||
si.toggleStatus();
|
si.toggleStatus();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "displayInitialLaunchWindow":
|
||||||
|
displayInitialLaunchWindow();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "displaySecondLaunchWindow":
|
||||||
|
displaySecondLaunchWindow();
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
console.log("Unrecognized message from " + TOP_BAR_MESSAGE_SOURCE + ": " + JSON.stringify(message));
|
console.log("Unrecognized message from " + TOP_BAR_MESSAGE_SOURCE + ": " + JSON.stringify(message));
|
||||||
break;
|
break;
|
||||||
|
@ -432,7 +550,6 @@ var TOP_BAR_QML_PATH = Script.resourcesPath() + "qml/hifi/simplifiedUI/topBar/Si
|
||||||
var TOP_BAR_WINDOW_TITLE = "Simplified Top Bar";
|
var TOP_BAR_WINDOW_TITLE = "Simplified Top Bar";
|
||||||
var TOP_BAR_PRESENTATION_MODE = Desktop.PresentationMode.NATIVE;
|
var TOP_BAR_PRESENTATION_MODE = Desktop.PresentationMode.NATIVE;
|
||||||
var TOP_BAR_WIDTH_PX = Window.innerWidth;
|
var TOP_BAR_WIDTH_PX = Window.innerWidth;
|
||||||
var TOP_BAR_HEIGHT_PX = 48;
|
|
||||||
var topBarWindow = false;
|
var topBarWindow = false;
|
||||||
function loadSimplifiedTopBar() {
|
function loadSimplifiedTopBar() {
|
||||||
var windowProps = {
|
var windowProps = {
|
||||||
|
@ -443,16 +560,9 @@ function loadSimplifiedTopBar() {
|
||||||
y: TOP_BAR_HEIGHT_PX
|
y: TOP_BAR_HEIGHT_PX
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (DOCKED_QML_SUPPORTED) {
|
windowProps.presentationWindowInfo = {
|
||||||
windowProps.presentationWindowInfo = {
|
dockArea: Desktop.DockArea.TOP
|
||||||
dockArea: Desktop.DockArea.TOP
|
};
|
||||||
};
|
|
||||||
} else {
|
|
||||||
windowProps.position = {
|
|
||||||
x: Window.x,
|
|
||||||
y: Window.y
|
|
||||||
};
|
|
||||||
}
|
|
||||||
topBarWindow = Desktop.createWindow(TOP_BAR_QML_PATH, windowProps);
|
topBarWindow = Desktop.createWindow(TOP_BAR_QML_PATH, windowProps);
|
||||||
|
|
||||||
topBarWindow.fromQml.connect(onMessageFromTopBar);
|
topBarWindow.fromQml.connect(onMessageFromTopBar);
|
||||||
|
@ -517,21 +627,38 @@ function onHMDInputDeviceMutedChanged(isMuted) {
|
||||||
function onGeometryChanged(rect) {
|
function onGeometryChanged(rect) {
|
||||||
updateInputDeviceMutedOverlay(Audio.muted);
|
updateInputDeviceMutedOverlay(Audio.muted);
|
||||||
updateOutputDeviceMutedOverlay(isOutputMuted());
|
updateOutputDeviceMutedOverlay(isOutputMuted());
|
||||||
if (topBarWindow && !DOCKED_QML_SUPPORTED) {
|
}
|
||||||
topBarWindow.size = {
|
|
||||||
"x": rect.width,
|
var initialLaunchWindowIsMinimized = false;
|
||||||
"y": TOP_BAR_HEIGHT_PX
|
var secondLaunchWindowIsMinimized = false;
|
||||||
};
|
function onWindowMinimizedChanged(isMinimized) {
|
||||||
topBarWindow.position = {
|
if (isMinimized) {
|
||||||
"x": rect.x,
|
handleInitialLaunchWindowVisibleChanged(false);
|
||||||
"y": rect.y
|
handleSecondLaunchWindowVisibleChanged(false);
|
||||||
};
|
} else if (!HMD.active) {
|
||||||
|
handleInitialLaunchWindowVisibleChanged(true);
|
||||||
|
handleSecondLaunchWindowVisibleChanged(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onWindowMinimizedChanged() {
|
function handleInitialLaunchWindowVisibleChanged(shouldBeVisible) {
|
||||||
// prerequisite placeholder for Reduce Friction of Customer Acquisition sub task: https://highfidelity.atlassian.net/browse/DEV-585
|
if (shouldBeVisible && !initialLaunchWindow && initialLaunchWindowIsMinimized) {
|
||||||
print("WINDOW MINIMIZED CHANGED SIGNAL");
|
displayInitialLaunchWindow();
|
||||||
|
initialLaunchWindowIsMinimized = false;
|
||||||
|
} else if (!shouldBeVisible && initialLaunchWindow) {
|
||||||
|
closeInitialLaunchWindow();
|
||||||
|
initialLaunchWindowIsMinimized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSecondLaunchWindowVisibleChanged(shouldBeVisible) {
|
||||||
|
if (shouldBeVisible && !secondLaunchWindow && secondLaunchWindowIsMinimized) {
|
||||||
|
displaySecondLaunchWindow();
|
||||||
|
secondLaunchWindowIsMinimized = false;
|
||||||
|
} else if (!shouldBeVisible && secondLaunchWindow) {
|
||||||
|
closeSecondLaunchWindow();
|
||||||
|
secondLaunchWindowIsMinimized = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onDisplayModeChanged(isHMDMode) {
|
function onDisplayModeChanged(isHMDMode) {
|
||||||
|
@ -541,8 +668,12 @@ function onDisplayModeChanged(isHMDMode) {
|
||||||
|
|
||||||
if (isHMDMode) {
|
if (isHMDMode) {
|
||||||
onHMDInputDeviceMutedChanged(Audio.mutedHMD);
|
onHMDInputDeviceMutedChanged(Audio.mutedHMD);
|
||||||
|
handleInitialLaunchWindowVisibleChanged(false);
|
||||||
|
handleSecondLaunchWindowVisibleChanged(false);
|
||||||
} else {
|
} else {
|
||||||
onDesktopInputDeviceMutedChanged(Audio.mutedDesktop);
|
onDesktopInputDeviceMutedChanged(Audio.mutedDesktop);
|
||||||
|
handleInitialLaunchWindowVisibleChanged(true);
|
||||||
|
handleSecondLaunchWindowVisibleChanged(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -585,9 +716,9 @@ function restoreLODSettings() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var nametag = Script.require("./simplifiedNametag/simplifiedNametag.js?" + Date.now());
|
var nametag = Script.require("./simplifiedNametag/simplifiedNametag.js");
|
||||||
var si = Script.require("./simplifiedStatusIndicator/simplifiedStatusIndicator.js?" + Date.now());
|
var si = Script.require("./simplifiedStatusIndicator/simplifiedStatusIndicator.js");
|
||||||
var emote = Script.require("../simplifiedEmote/simplifiedEmote.js?" + Date.now());
|
var simplifiedEmote = Script.require("../simplifiedEmote/simplifiedEmote.js");
|
||||||
var oldShowAudioTools;
|
var oldShowAudioTools;
|
||||||
var oldShowBubbleTools;
|
var oldShowBubbleTools;
|
||||||
var keepExistingUIAndScriptsSetting = Settings.getValue("simplifiedUI/keepExistingUIAndScripts", false);
|
var keepExistingUIAndScriptsSetting = Settings.getValue("simplifiedUI/keepExistingUIAndScripts", false);
|
||||||
|
@ -654,6 +785,14 @@ function shutdown() {
|
||||||
settingsAppWindow.close();
|
settingsAppWindow.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (initialLaunchWindow) {
|
||||||
|
closeInitialLaunchWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (secondLaunchWindow) {
|
||||||
|
closeSecondLaunchWindow();
|
||||||
|
}
|
||||||
|
|
||||||
maybeDeleteInputDeviceMutedOverlay();
|
maybeDeleteInputDeviceMutedOverlay();
|
||||||
maybeDeleteOutputDeviceMutedOverlay();
|
maybeDeleteOutputDeviceMutedOverlay();
|
||||||
|
|
||||||
|
|
|
@ -50,11 +50,11 @@ var OVERLAY_DATA_HMD = {
|
||||||
};
|
};
|
||||||
|
|
||||||
var AWAY_INTRO = {
|
var AWAY_INTRO = {
|
||||||
url: "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/kneel.fbx",
|
url: "https://hifi-content.s3.amazonaws.com/doug/animation/fbx/afk_texting.fbx",
|
||||||
playbackRate: 30.0,
|
playbackRate: 30.0,
|
||||||
loopFlag: false,
|
loopFlag: true,
|
||||||
startFrame: 0.0,
|
startFrame: 1.0,
|
||||||
endFrame: 83.0
|
endFrame: 489.0
|
||||||
};
|
};
|
||||||
|
|
||||||
// MAIN CONTROL
|
// MAIN CONTROL
|
||||||
|
|