Merge branch 'instancing' of github.com:highfidelity/hifi into instancing_gltf

This commit is contained in:
sabrina-shanman 2019-10-16 14:03:01 -07:00
commit 0c3e81f52f
59 changed files with 1687 additions and 471 deletions

View 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))

View file

@ -27,6 +27,7 @@ vcpkg_from_github(
REF ab8f16961e19a86ee20c6a1d61f662392524cc77
SHA512 927742db29867517283d45e475f0c534a9a57e165cae221f26e08e88057253a1682ac9919b2dc547b9cf388ba0b931b175623461d44f28c9184796ba90b1ed55
HEAD_REF master
PATCHES "bullet-git-fix-build-clang-8.patch"
)
vcpkg_configure_cmake(

View file

@ -5251,7 +5251,7 @@
{
"easingType": "easeInOutQuad",
"id": "idle",
"interpDuration": 20,
"interpDuration": 15,
"interpTarget": 20,
"interpType": "evaluateBoth",
"transitions": [
@ -5383,8 +5383,8 @@
{
"easingType": "easeInOutQuad",
"id": "idleSettle",
"interpDuration": 15,
"interpTarget": 15,
"interpDuration": 13,
"interpTarget": 14,
"interpType": "snapshotPrev",
"transitions": [
{
@ -5477,7 +5477,7 @@
"transitions": [
{
"state": "idleSettle",
"var": "isNotInput"
"var": "isNotInputSlow"
},
{
"state": "WALKBWD",
@ -5541,7 +5541,7 @@
"transitions": [
{
"state": "idleSettle",
"var": "isNotInput"
"var": "isNotInputSlow"
},
{
"state": "WALKFWD",
@ -5605,7 +5605,7 @@
"transitions": [
{
"state": "idleSettle",
"var": "isNotInput"
"var": "isNotInputSlow"
},
{
"state": "WALKFWD",
@ -5669,7 +5669,7 @@
"transitions": [
{
"state": "idleSettle",
"var": "isNotInput"
"var": "isNotInputSlow"
},
{
"state": "WALKFWD",

View file

@ -9,6 +9,7 @@
//
import QtQuick 2.10
import QtQuick.Layouts 1.3
import "../simplifiedConstants" as SimplifiedConstants
import "../simplifiedControls" as SimplifiedControls
import "./components" as AvatarAppComponents
@ -79,7 +80,11 @@ Rectangle {
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;
} 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);
@ -140,8 +145,95 @@ Rectangle {
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 {
id: avatarInfoTextContainer
visible: !emptyInventoryContainer.visible
width: parent.implicitWidth
height: childrenRect.height
anchors.top: displayNameHeader.bottom
@ -164,7 +256,7 @@ Rectangle {
id: yourAvatarsSubtitle
text: "These are the avatars that you've created and uploaded via the Avatar Creator."
width: parent.width
wrapMode: Text.WordWrap
wrapMode: Text.Wrap
anchors.top: yourAvatarsTitle.bottom
anchors.topMargin: 6
verticalAlignment: TextInput.AlignVCenter
@ -208,9 +300,10 @@ Rectangle {
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
visible: !emptyInventoryContainer.visible
AnimatedImage {
visible: !inventoryContentsList.visible && !errorText.visible
visible: !(inventoryContentsList.visible || errorText.visible)
anchors.centerIn: parent
width: 72
height: width
@ -271,6 +364,8 @@ Rectangle {
return;
}
}
root.avatarPreviewUrl = "../../images/defaultAvatar.svg";
}
function fromScript(message) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 319 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

View file

@ -54,8 +54,8 @@ Rectangle {
if ((MyAvatar.skeletonModelURL.indexOf("defaultAvatar") > -1 || MyAvatar.skeletonModelURL.indexOf("fst") === -1) &&
topBarInventoryModel.count > 0) {
Settings.setValue("simplifiedUI/alreadyAutoSelectedAvatar", true);
MyAvatar.useFullAvatarURL = topBarInventoryModel.get(0).download_url;
Settings.setValue("simplifiedUI/alreadyAutoSelectedAvatarFromInventory", true);
MyAvatar.useFullAvatarURL(topBarInventoryModel.get(0).download_url);
}
}
}
@ -71,7 +71,7 @@ Rectangle {
if (isLoggedIn) {
Commerce.getWalletStatus();
} else {
// Show some error to the user
// Show some error to the user in the UI?
}
}
@ -113,12 +113,68 @@ Rectangle {
topBarInventoryModel.getNextPage();
} else {
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...
if ((!Settings.getValue("simplifiedUI/alreadyAutoSelectedAvatar", false) ||
MyAvatar.skeletonModelURL.indexOf("defaultAvatar") > -1 || MyAvatar.skeletonModelURL.indexOf("fst") === -1) && topBarInventoryModel.count > 0) {
Settings.setValue("simplifiedUI/alreadyAutoSelectedAvatar", true);
MyAvatar.skeletonModelURL = topBarInventoryModel.get(0).download_url;
// Use `Settings.setValue("simplifiedUI/debugFTUE", 0);` to turn off FTUE Debug Mode.
// Use `Settings.setValue("simplifiedUI/debugFTUE", 1);` to debug FTUE Screen 1.
// Use `Settings.setValue("simplifiedUI/debugFTUE", 2);` to debug FTUE Screen 2.
// Use `Settings.setValue("simplifiedUI/debugFTUE", 3);` to debug FTUE Screen 3.
// 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 downloadUrl = "";
for (var i = 0; i < topBarInventoryModel.count; ++i) {
@ -570,6 +626,8 @@ Rectangle {
return;
}
}
avatarButtonImage.source = "../images/defaultAvatar.svg";
}

View file

@ -3618,7 +3618,7 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) {
_myCamera.setPosition(extractTranslation(camMat));
_myCamera.setOrientation(glmExtractRotation(camMat));
} else {
_myCamera.setPosition(myAvatar->getLookAtPivotPoint());
_myCamera.setPosition(myAvatar->getCameraEyesPosition(deltaTime));
_myCamera.setOrientation(myAvatar->getLookAtRotation());
}
} else if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE) {

View file

@ -152,7 +152,7 @@ static int triggerReactionNameToIndex(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);
}
@ -2719,7 +2719,7 @@ void MyAvatar::updateMotors() {
if (_characterController.getState() == CharacterController::State::Hover ||
_characterController.computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS) {
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();
} else {
motorRotation = getMyHead()->getHeadOrientation();
@ -6812,6 +6812,50 @@ glm::vec3 MyAvatar::getLookAtPivotPoint() {
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) {
if (QThread::currentThread() != thread()) {
bool result = false;

View file

@ -1919,6 +1919,8 @@ public:
bool getIsJointOverridden(int jointIndex) const;
glm::vec3 getLookAtPivotPoint();
glm::vec3 getCameraEyesPosition(float deltaTime);
bool isJumping();
public slots:
@ -2973,6 +2975,8 @@ private:
// used to prevent character from jumping after endSit is called.
bool _endSitKeyPressComplete { false };
glm::vec3 _cameraEyesOffset;
};
QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode);

View file

@ -52,6 +52,21 @@ static const QVariantMap DOCK_AREA {
{ "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)
: QObject(parent), _restricted(restricted) { }
@ -99,6 +114,10 @@ QVariantMap DesktopScriptingInterface::getDockArea() {
return DOCK_AREA;
}
QVariantMap DesktopScriptingInterface::getRelativePositionAnchor() {
return RELATIVE_POSITION_ANCHOR;
}
void DesktopScriptingInterface::setHUDAlpha(float alpha) {
qApp->getApplicationCompositor().setAlpha(alpha);
}

View file

@ -42,6 +42,9 @@
* @property {InteractiveWindow.DockAreas} DockArea - The possible docking locations of an {@link InteractiveWindow}: top,
* bottom, left, or right of the Interface window.
* <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 {
Q_OBJECT
@ -50,6 +53,7 @@ class DesktopScriptingInterface : public QObject, public Dependency {
Q_PROPERTY(QVariantMap PresentationMode READ getPresentationMode 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 CLOSE_BUTTON_HIDES READ flagCloseButtonHides CONSTANT FINAL)
@ -106,7 +110,7 @@ private:
Q_INVOKABLE InteractiveWindowPointer createWindowOnThread(const QString& sourceUrl, const QVariantMap& properties, QThread* targetThread);
static QVariantMap getDockArea();
static QVariantMap getRelativePositionAnchor();
Q_INVOKABLE static QVariantMap getPresentationMode();
const bool _restricted;
};

View file

@ -39,6 +39,9 @@ static const char* const ADDITIONAL_FLAGS_PROPERTY = "additionalFlags";
static const char* const OVERRIDE_FLAGS_PROPERTY = "overrideFlags";
static const char* const SOURCE_PROPERTY = "source";
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 INTERACTIVE_WINDOW_POSITION_PROPERTY = "interactiveWindowPosition";
static const char* const SIZE_PROPERTY = "size";
@ -112,6 +115,14 @@ void InteractiveWindow::forwardKeyReleaseEvent(int key, int modifiers) {
QCoreApplication::postEvent(QCoreApplication::instance(), event);
}
void InteractiveWindow::onMainWindowGeometryChanged(QRect geometry) {
if (_isFullScreenWindow) {
repositionAndResizeFullScreenWindow();
} else {
setPositionUsingRelativePositionAndAnchor(geometry);
}
}
void InteractiveWindow::emitMainWindowResizeEvent() {
emit qApp->getWindow()->windowGeometryChanged(qApp->getWindow()->geometry());
}
@ -184,22 +195,32 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap
*/
if (nativeWindowInfo.contains(DOCK_AREA_PROPERTY)) {
DockArea dockedArea = (DockArea) nativeWindowInfo[DOCK_AREA_PROPERTY].toInt();
int tempWidth = 0;
int tempHeight = 0;
switch (dockedArea) {
case DockArea::TOP:
dockArea = Qt::TopDockWidgetArea;
_dockWidget->setFixedHeight(windowSize.height());
tempHeight = windowSize.height();
_dockWidget->setFixedHeight(tempHeight);
qApp->getWindow()->setDockedWidgetRelativePositionOffset(QSize(0, -tempHeight));
break;
case DockArea::BOTTOM:
dockArea = Qt::BottomDockWidgetArea;
_dockWidget->setFixedHeight(windowSize.height());
tempHeight = windowSize.height();
_dockWidget->setFixedHeight(tempHeight);
qApp->getWindow()->setDockedWidgetRelativePositionOffset(QSize(0, tempHeight));
break;
case DockArea::LEFT:
dockArea = Qt::LeftDockWidgetArea;
_dockWidget->setFixedWidth(windowSize.width());
tempWidth = windowSize.width();
_dockWidget->setFixedWidth(tempWidth);
qApp->getWindow()->setDockedWidgetRelativePositionOffset(QSize(-tempWidth, 0));
break;
case DockArea::RIGHT:
dockArea = Qt::RightDockWidgetArea;
_dockWidget->setFixedWidth(windowSize.width());
tempWidth = windowSize.width();
_dockWidget->setFixedWidth(tempWidth);
qApp->getWindow()->setDockedWidgetRelativePositionOffset(QSize(tempWidth, 0));
break;
default:
@ -255,6 +276,9 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap
if (properties.contains(TITLE_PROPERTY)) {
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)) {
const auto size = vec2FromVariant(properties[SIZE_PROPERTY]);
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]);
object->setProperty(INTERACTIVE_WINDOW_POSITION_PROPERTY, QPointF(position.x, position.y));
}
if (properties.contains(VISIBLE_PROPERTY)) {
object->setProperty(VISIBLE_PROPERTY, properties[INTERACTIVE_WINDOW_VISIBLE_PROPERTY].toBool());
if (properties.contains(RELATIVE_POSITION_ANCHOR_PROPERTY)) {
_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
@ -288,6 +325,8 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap
connect(object, SIGNAL(interactiveWindowVisibleChanged()), this, SLOT(parentNativeWindowToMainWindow()), Qt::QueuedConnection);
connect(object, SIGNAL(presentationModeChanged()), this, SLOT(parentNativeWindowToMainWindow()), Qt::QueuedConnection);
#endif
connect(qApp->getWindow(), &MainWindow::windowGeometryChanged, this, &InteractiveWindow::onMainWindowGeometryChanged, Qt::QueuedConnection);
QUrl sourceURL{ sourceUrl };
// 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 {
if (!_qmlWindowProxy) {
return {};

View file

@ -89,6 +89,14 @@ namespace InteractiveWindowEnums {
RIGHT
};
Q_ENUM_NS(DockArea);
enum RelativePositionAnchor {
TOP_LEFT,
TOP_RIGHT,
BOTTOM_RIGHT,
BOTTOM_LEFT
};
Q_ENUM_NS(RelativePositionAnchor);
}
using namespace InteractiveWindowEnums;
@ -121,6 +129,8 @@ class InteractiveWindow : public QObject {
Q_PROPERTY(QString title READ getTitle WRITE setTitle)
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(bool visible READ isVisible WRITE setVisible)
Q_PROPERTY(int presentationMode READ getPresentationMode WRITE setPresentationMode)
@ -136,6 +146,21 @@ private:
Q_INVOKABLE glm::vec2 getPosition() const;
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 void setSize(const glm::vec2& size);
@ -320,6 +345,7 @@ protected slots:
void forwardKeyPressEvent(int key, int modifiers);
void forwardKeyReleaseEvent(int key, int modifiers);
void emitMainWindowResizeEvent();
void onMainWindowGeometryChanged(QRect geometry);
private:
std::shared_ptr<QmlWindowProxy> _qmlWindowProxy;

View file

@ -55,6 +55,8 @@ public:
float getFrame() const { return _frame; }
void loadURL(const QString& url);
AnimBlendType getBlendType() const { return _blendType; };
protected:
virtual void setCurrentFrameInternal(float frame) override;

View file

@ -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
// 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++) {
const HFMMesh& mesh = hfmModel.meshes.at(i);
std::vector<HFMCluster> dummyClustersList;
@ -55,6 +83,7 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) {
}
_clusterBindMatrixOriginalValues.push_back(dummyClustersList);
}
*/
}
AnimSkeleton::AnimSkeleton(const std::vector<HFMJoint>& joints, const QMap<int, glm::quat> jointOffsets) {

View file

@ -70,6 +70,8 @@ public:
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]; }
protected:
void buildSkeletonFromJoints(const std::vector<HFMJoint>& joints, const QMap<int, glm::quat> jointOffsets);

View file

@ -545,7 +545,8 @@ QStringList Rig::getAnimationRoles() const {
auto clipNode = std::dynamic_pointer_cast<AnimClip>(node);
if (clipNode) {
// 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());
}
}
@ -1432,6 +1433,69 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
}
_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;
_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;
bool leftHandEnabled = params.primaryControllerFlags[PrimaryControllerType_LeftHand] & (uint8_t)ControllerFlags::Enabled;

View file

@ -88,8 +88,8 @@ public:
AnimPose secondaryControllerPoses[NumSecondaryControllerTypes]; // rig space
uint8_t secondaryControllerFlags[NumSecondaryControllerTypes];
bool isTalking;
float inputX;
float inputZ;
float inputX = 0.0f;
float inputZ = 0.0f;
bool reactionEnabledFlags[NUM_AVATAR_BEGIN_END_REACTIONS];
bool reactionTriggers[NUM_AVATAR_TRIGGER_REACTIONS];
HFMJointShapeInfo hipsShapeInfo;

View file

@ -335,8 +335,8 @@ namespace controller {
makeAxisPair(Action::STEP_PITCH, "StepPitch"),
makeAxisPair(Action::STEP_ROLL, "StepRoll"),
makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateX"),
makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateY"),
makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateZ"),
makeAxisPair(Action::STEP_TRANSLATE_Y, "StepTranslateY"),
makeAxisPair(Action::STEP_TRANSLATE_Z, "StepTranslateZ"),
makePosePair(Action::LEFT_HAND, "LeftHand"),
makePosePair(Action::RIGHT_HAND, "RightHand"),

View file

@ -1463,9 +1463,6 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const
// see if any materials have texture children
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++) {
const QString& meshID = it.key();
const ExtractedMesh& extracted = it.value();
@ -1509,7 +1506,7 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const
hfm::Shape& shape = partShapes[i];
shape.mesh = meshIndex;
shape.meshPart = i;
shape.transform = transformIndex;
shape.joint = transformIndex;
auto matName = mesh.parts[i].materialID;
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
if (clusterIDs.size() > 1) {
hfm::DynamicTransform dynamicTransform;
auto& clusters = dynamicTransform.clusters;
std::vector<hfm::Deformer> deformers;
if (clusterIDs.size() > 0) {
hfm::SkinDeformer skinDeformer;
auto& clusters = skinDeformer.clusters;
std::vector<hfm::SkinCluster> skinClusters;
for (const auto& clusterID : clusterIDs) {
HFMCluster hfmCluster;
const Cluster& fbxCluster = fbxClusters[clusterID];
@ -1642,38 +1639,43 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const
clusters.push_back(cluster);
// Skinned mesh instances have a dynamic transform
dynamicTransform.deformers.reserve(clusterIDs.size());
clusters.reserve(clusterIDs.size());
skinDeformer.skinClusterIndices.reserve(clusterIDs.size());
for (const auto& clusterID : clusterIDs) {
const Cluster& fbxCluster = fbxClusters[clusterID];
dynamicTransform.deformers.emplace_back();
deformers.emplace_back();
hfm::Deformer& deformer = deformers.back();
skinDeformer.skinClusterIndices.emplace_back();
skinClusters.emplace_back();
hfm::SkinCluster& skinCluster = skinClusters.back();
size_t indexWeightPairs = (size_t)std::min(fbxCluster.indices.size(), fbxCluster.weights.size());
deformer.indices.reserve(indexWeightPairs);
deformer.weights.reserve(indexWeightPairs);
for (int i = 0; i < (int)indexWeightPairs; i++) {
int oldIndex = fbxCluster.indices[i];
uint32_t newIndex = (uint32_t)extracted.newIndices.value(oldIndex);
deformer.indices.push_back(newIndex);
deformer.indices.push_back((float)fbxCluster.weights[i]);
skinCluster.indices.reserve(indexWeightPairs);
skinCluster.weights.reserve(indexWeightPairs);
for (int j = 0; j < fbxCluster.indices.size(); j++) {
int oldIndex = fbxCluster.indices.at(j);
float weight = fbxCluster.weights.at(j);
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
uint32_t deformerMinID = (uint32_t)hfmModel.deformers.size();
hfmModel.deformers.insert(hfmModel.deformers.end(), deformers.cbegin(), deformers.cend());
dynamicTransform.deformers.resize(deformers.size());
std::iota(dynamicTransform.deformers.begin(), dynamicTransform.deformers.end(), deformerMinID);
uint32_t deformerMinID = (uint32_t)hfmModel.skinClusters.size();
hfmModel.skinClusters.insert(hfmModel.skinClusters.end(), skinClusters.cbegin(), skinClusters.cend());
skinDeformer.skinClusterIndices.resize(skinClusters.size());
std::iota(skinDeformer.skinClusterIndices.begin(), skinDeformer.skinClusterIndices.end(), deformerMinID);
// Store the model's dynamic transform, and put its ID in the shapes
hfmModel.dynamicTransforms.push_back(dynamicTransform);
uint32_t dynamicTransformID = (uint32_t)(hfmModel.dynamicTransforms.size() - 1);
hfmModel.skinDeformers.push_back(skinDeformer);
uint32_t skinDeformerID = (uint32_t)(hfmModel.skinDeformers.size() - 1);
for (hfm::Shape& shape : partShapes) {
shape.dynamicTransform = dynamicTransformID;
shape.skinDeformer = skinDeformerID;
}
} else {
// this is a single-joint mesh
// this is a no cluster mesh
HFMJoint& joint = hfmModel.joints[transformIndex];
// Apply geometric offset, if present, by transforming the vertices directly

View file

@ -242,17 +242,20 @@ public:
QVector<glm::vec3> colors;
QVector<glm::vec2> texCoords;
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)
Extents meshExtents; // DEPRECATED (see hfm::Shape::transformedExtents)
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<int32_t> originalIndices; // Original indices of the vertices
unsigned int meshIndex; // the order the meshes appeared in the object file
graphics::MeshPointer _mesh;
@ -293,27 +296,17 @@ public:
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
class Deformer {
class SkinCluster {
public:
std::vector<uint32_t> indices;
std::vector<float> weights;
};
class DynamicTransform {
class SkinDeformer {
public:
std::vector<uint16_t> deformers;
std::vector<Cluster> clusters; // affect the deformer of the same index
std::vector<uint32_t> blendshapes;
// There are also the meshExtents and modelTransform, which for now are left in hfm::Mesh
std::vector<uint16_t> skinClusterIndices;
std::vector<Cluster> clusters;
};
// The lightweight model part description.
@ -322,10 +315,10 @@ public:
uint32_t mesh { UNDEFINED_KEY };
uint32_t meshPart { 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.
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.
@ -342,10 +335,9 @@ public:
std::vector<Mesh> meshes;
std::vector<Material> materials;
std::vector<Deformer> deformers;
std::vector<TransformNode> transforms;
std::vector<DynamicTransform> dynamicTransforms;
std::vector<SkinDeformer> skinDeformers;
std::vector<SkinCluster> skinClusters;
std::vector<Joint> joints;
QHash<QString, int> jointIndices; ///< 1-based, so as to more easily detect missing indices

View file

@ -30,7 +30,7 @@ namespace baker {
class GetModelPartsTask {
public:
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>;
void run(const BakeContextPointer& context, const Input& input, Output& output) {
@ -45,8 +45,8 @@ namespace baker {
}
output.edit4() = hfmModelIn->joints;
output.edit5() = hfmModelIn->shapes;
output.edit6() = hfmModelIn->dynamicTransforms;
output.edit7() = hfmModelIn->deformers;
output.edit6() = hfmModelIn->skinDeformers;
output.edit7() = hfmModelIn->skinClusters;
output.edit8() = hfmModelIn->meshExtents;
}
};
@ -147,8 +147,8 @@ namespace baker {
const auto blendshapesPerMeshIn = modelPartsIn.getN<GetModelPartsTask::Output>(3);
const auto jointsIn = modelPartsIn.getN<GetModelPartsTask::Output>(4);
const auto shapesIn = modelPartsIn.getN<GetModelPartsTask::Output>(5);
const auto dynamicTransformsIn = modelPartsIn.getN<GetModelPartsTask::Output>(6);
const auto deformersIn = modelPartsIn.getN<GetModelPartsTask::Output>(7);
const auto skinDeformersIn = modelPartsIn.getN<GetModelPartsTask::Output>(6);
const auto skinClustersIn = modelPartsIn.getN<GetModelPartsTask::Output>(7);
const auto modelExtentsIn = modelPartsIn.getN<GetModelPartsTask::Output>(8);
// Calculate normals and tangents for meshes and blendshapes if they do not exist
@ -163,14 +163,14 @@ namespace baker {
// 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.
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);
// 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);
// 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);
// Prepare joint information

View file

@ -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");
}
// 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 clusterWeightsSize = numVertClusters * clusterWeightElement.getSize();
@ -381,16 +381,16 @@ void BuildGraphicsMeshTask::run(const baker::BakeContextPointer& context, const
const auto& normalsPerMesh = input.get3();
const auto& tangentsPerMesh = input.get4();
const auto& shapes = input.get5();
const auto& dynamicTransforms = input.get6();
const auto& skinDeformers = input.get6();
const auto& reweightedDeformersPerMesh = input.get7();
// Currently, there is only (at most) one dynamicTransform per mesh
// An undefined shape.dynamicTransform has the value hfm::UNDEFINED_KEY
std::vector<uint32_t> dynamicTransformPerMesh;
dynamicTransformPerMesh.resize(meshes.size(), hfm::UNDEFINED_KEY);
// Currently, there is only (at most) one skinDeformer per mesh
// An undefined shape.skinDeformer has the value hfm::UNDEFINED_KEY
std::vector<uint32_t> skinDeformerPerMesh;
skinDeformerPerMesh.resize(meshes.size(), hfm::UNDEFINED_KEY);
for (const auto& shape : shapes) {
uint32_t dynamicTransformIndex = shape.dynamicTransform;
dynamicTransformPerMesh[shape.mesh] = dynamicTransformIndex;
uint32_t skinDeformerIndex = shape.skinDeformer;
skinDeformerPerMesh[shape.mesh] = skinDeformerIndex;
}
auto& graphicsMeshes = output;
@ -403,10 +403,10 @@ void BuildGraphicsMeshTask::run(const baker::BakeContextPointer& context, const
uint16_t numDeformerControllers = 0;
if (reweightedDeformers.weightsPerVertex != 0) {
uint32_t dynamicTransformIndex = dynamicTransformPerMesh[i];
if (dynamicTransformIndex != hfm::UNDEFINED_KEY) {
const hfm::DynamicTransform& dynamicTransform = dynamicTransforms[dynamicTransformIndex];
numDeformerControllers = (uint16_t)dynamicTransform.deformers.size();
uint32_t skinDeformerIndex = skinDeformerPerMesh[i];
if (skinDeformerIndex != hfm::UNDEFINED_KEY) {
const hfm::SkinDeformer& skinDeformer = skinDeformers[skinDeformerIndex];
numDeformerControllers = (uint16_t)skinDeformer.skinClusterIndices.size();
}
}

View file

@ -20,7 +20,7 @@
class BuildGraphicsMeshTask {
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 JobModel = baker::Job::ModelIO<BuildGraphicsMeshTask, Input, Output>;

View file

@ -13,15 +13,15 @@
#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 {
public:
uint32_t mesh;
uint32_t dynamicTransform;
uint32_t skinDeformer;
bool operator==(const VertexSource& other) const {
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& shapes = input.get1();
const auto& joints = input.get2();
const auto& dynamicTransforms = input.get3();
const auto& skinDeformers = input.get3();
const auto& reweightedDeformers = input.get4();
auto& shapeVerticesPerJoint = output;
@ -38,18 +38,18 @@ void CollectShapeVerticesTask::run(const baker::BakeContextPointer& context, con
vertexSourcesPerJoint.resize(joints.size());
for (size_t i = 0; i < shapes.size(); ++i) {
const auto& shape = shapes[i];
const uint32_t dynamicTransformKey = shape.dynamicTransform;
if (dynamicTransformKey == hfm::UNDEFINED_KEY) {
const uint32_t skinDeformerKey = shape.skinDeformer;
if (skinDeformerKey == hfm::UNDEFINED_KEY) {
continue;
}
VertexSource vertexSource;
vertexSource.mesh = shape.mesh;
vertexSource.dynamicTransform = dynamicTransformKey;
vertexSource.skinDeformer = skinDeformerKey;
const auto& dynamicTransform = dynamicTransforms[dynamicTransformKey];
for (size_t j = 0; j < dynamicTransform.clusters.size(); ++j) {
const auto& cluster = dynamicTransform.clusters[j];
const auto& skinDeformer = skinDeformers[skinDeformerKey];
for (size_t j = 0; j < skinDeformer.clusters.size(); ++j) {
const auto& cluster = skinDeformer.clusters[j];
const uint32_t jointIndex = cluster.jointIndex;
auto& vertexSources = vertexSourcesPerJoint[jointIndex];

View file

@ -19,7 +19,7 @@
class CollectShapeVerticesTask {
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 JobModel = baker::Job::ModelIO<CollectShapeVerticesTask, Input, Output>;

View file

@ -11,30 +11,30 @@
#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;
if (deformers.size() == 0) {
if (skinClusters.size() == 0) {
return reweightedDeformers;
}
size_t numClusterIndices = numMeshVertices * 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.
reweightedDeformers.indices.resize(numClusterIndices, (uint16_t)(deformers.size() - 1));
reweightedDeformers.indices.resize(numClusterIndices, (uint16_t)(skinClusters.size() - 1));
reweightedDeformers.weights.resize(numClusterIndices, 0);
std::vector<float> weightAccumulators;
weightAccumulators.resize(numClusterIndices, 0.0f);
for (uint16_t i = 0; i < (uint16_t)deformers.size(); ++i) {
const hfm::Deformer& deformer = *deformers[i];
for (uint16_t i = 0; i < (uint16_t)skinClusters.size(); ++i) {
const hfm::SkinCluster& skinCluster = *skinClusters[i];
if (deformer.indices.size() != deformer.weights.size()) {
if (skinCluster.indices.size() != skinCluster.weights.size()) {
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) {
uint32_t index = deformer.indices[j];
float weight = deformer.weights[j];
uint32_t index = skinCluster.indices[j];
float weight = skinCluster.weights[j];
// look for an unused slot in the weights vector
uint32_t weightIndex = index * weightsPerVertex;
@ -90,34 +90,34 @@ void ReweightDeformersTask::run(const baker::BakeContextPointer& context, const
const auto& meshes = input.get0();
const auto& shapes = input.get1();
const auto& dynamicTransforms = input.get2();
const auto& deformers = input.get3();
const auto& skinDeformers = input.get2();
const auto& skinClusters = input.get3();
auto& reweightedDeformers = output;
// Currently, there is only (at most) one dynamicTransform per mesh
// An undefined shape.dynamicTransform has the value hfm::UNDEFINED_KEY
std::vector<uint32_t> dynamicTransformPerMesh;
dynamicTransformPerMesh.resize(meshes.size(), hfm::UNDEFINED_KEY);
// Currently, there is only (at most) one skinDeformer per mesh
// An undefined shape.skinDeformer has the value hfm::UNDEFINED_KEY
std::vector<uint32_t> skinDeformerPerMesh;
skinDeformerPerMesh.resize(meshes.size(), hfm::UNDEFINED_KEY);
for (const auto& shape : shapes) {
uint32_t dynamicTransformIndex = shape.dynamicTransform;
dynamicTransformPerMesh[shape.mesh] = dynamicTransformIndex;
uint32_t skinDeformerIndex = shape.skinDeformer;
skinDeformerPerMesh[shape.mesh] = skinDeformerIndex;
}
reweightedDeformers.reserve(meshes.size());
for (size_t i = 0; i < meshes.size(); ++i) {
const auto& mesh = meshes[i];
uint32_t dynamicTransformIndex = dynamicTransformPerMesh[i];
uint32_t skinDeformerIndex = skinDeformerPerMesh[i];
const hfm::DynamicTransform* dynamicTransform = nullptr;
std::vector<const hfm::Deformer*> meshDeformers;
if (dynamicTransformIndex != hfm::UNDEFINED_KEY) {
dynamicTransform = &dynamicTransforms[dynamicTransformIndex];
for (const auto& deformerIndex : dynamicTransform->deformers) {
const auto& deformer = deformers[deformerIndex];
meshDeformers.push_back(&deformer);
const hfm::SkinDeformer* skinDeformer = nullptr;
std::vector<const hfm::SkinCluster*> meshSkinClusters;
if (skinDeformerIndex != hfm::UNDEFINED_KEY) {
skinDeformer = &skinDeformers[skinDeformerIndex];
for (const auto& skinClusterIndex : skinDeformer->skinClusterIndices) {
const auto& skinCluster = skinClusters[skinClusterIndex];
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));
}
}

View file

@ -19,7 +19,7 @@
class ReweightDeformersTask {
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 JobModel = baker::Job::ModelIO<ReweightDeformersTask, Input, Output>;

View file

@ -295,7 +295,6 @@ void ModelResource::onGeometryMappingLoaded(bool success) {
if (success && _modelResource) {
_hfmModel = _modelResource->_hfmModel;
_materialMapping = _modelResource->_materialMapping;
// _meshParts = _modelResource->_meshParts;
_meshes = _modelResource->_meshes;
_materials = _modelResource->_materials;

View file

@ -18,8 +18,8 @@
using namespace render;
CauterizedMeshPartPayload::CauterizedMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform)
: ModelMeshPartPayload(model, meshIndex, partIndex, shapeIndex, transform, offsetTransform) {}
CauterizedMeshPartPayload::CauterizedMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform)
: ModelMeshPartPayload(model, meshIndex, partIndex, shapeIndex, transform) {}
void CauterizedMeshPartPayload::updateClusterBuffer(const std::vector<glm::mat4>& clusterMatrices,
const std::vector<glm::mat4>& cauterizedClusterMatrices) {

View file

@ -13,7 +13,7 @@
class CauterizedMeshPartPayload : public ModelMeshPartPayload {
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
void updateClusterBuffer(const std::vector<glm::mat4>& clusterMatrices,

View file

@ -32,8 +32,40 @@ bool CauterizedModel::updateGeometry() {
bool needsFullUpdate = Model::updateGeometry();
if (_isCauterized && needsFullUpdate) {
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();
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;
if (_useDualQuaternionSkinning) {
state.clusterDualQuaternions.resize(mesh.clusters.size());
@ -42,7 +74,7 @@ bool CauterizedModel::updateGeometry() {
state.clusterMatrices.resize(mesh.clusters.size());
_cauterizeMeshStates.append(state);
}
}
}*/
}
return needsFullUpdate;
}
@ -52,11 +84,6 @@ void CauterizedModel::createRenderItemSet() {
assert(isLoaded());
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
Q_ASSERT(_modelMeshRenderItems.isEmpty());
@ -73,8 +100,23 @@ void CauterizedModel::createRenderItemSet() {
offset.setScale(_scale);
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
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();
for (uint32_t i = 0; i < numMeshes; i++) {
const auto& mesh = meshes.at(i);
@ -85,14 +127,14 @@ void CauterizedModel::createRenderItemSet() {
// Create the render payloads
int numParts = (int)mesh->getNumParts();
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);
auto material = getNetworkModel()->getShapeMaterial(shapeID);
_modelMeshMaterialNames.push_back(material ? material->getName() : "");
_modelMeshRenderItemShapes.emplace_back(ShapeInfo{ (int)i });
shapeID++;
}
}
}*/
} else {
Model::createRenderItemSet();
}
@ -108,6 +150,38 @@ void CauterizedModel::updateClusterMatrices() {
updateShapeStatesFromRig();
_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();
for (int i = 0; i < (int)_meshStates.size(); 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.
if (!_cauterizeBoneSet.empty()) {
@ -224,64 +298,69 @@ void CauterizedModel::updateRenderItems() {
auto meshIndex = self->_modelMeshRenderItemShapes[i].meshIndex;
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 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 (meshState.clusterDualQuaternions.size() == 1 || meshState.clusterDualQuaternions.size() == 2) {
const auto& dq = meshState.clusterDualQuaternions[0];
Transform transform(dq.getRotation(),
dq.getScale(),
dq.getTranslation());
renderTransform = modelTransform.worldTransform(transform);
}
} else {
if (meshState.clusterMatrices.size() == 1 || meshState.clusterMatrices.size() == 2) {
renderTransform = modelTransform.worldTransform(Transform(meshState.clusterMatrices[0]));
}
}*/
if (meshState.clusterMatrices.size() <= 1) {
if (isDeformed) {
const auto& meshState = self->getMeshState(deformerIndex);
const auto& cauterizedMeshState = self->getCauterizeMeshState(deformerIndex);
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);
} else {
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);
}
data.updateTransformForSkinnedMesh(renderTransform, modelTransform);
data.updateTransform(renderTransform);
data.updateTransformForCauterizedMesh(renderTransform);
renderTransform = modelTransform;
if (useDualQuaternionSkinning) {
if (cauterizedMeshState.clusterDualQuaternions.size() == 1 || cauterizedMeshState.clusterDualQuaternions.size() == 2) {
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);
});
data.setEnableCauterization(enableCauterization);
data.updateKey(renderItemKeyGlobalFlags);
data.setShapeKey(invalidatePayloadShapeKey, primitiveMode, false);
});
}
}
scene->enqueueTransaction(transaction);

View file

@ -64,11 +64,15 @@ void MeshPartPayload::updateMeshPart(const std::shared_ptr<const graphics::Mesh>
}
}
void MeshPartPayload::updateTransform(const Transform& transform, const Transform& offsetTransform) {
_transform = transform;
Transform::mult(_drawTransform, _transform, offsetTransform);
void MeshPartPayload::updateTransform(const Transform& transform) {
_worldFromLocalTransform = transform;
_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) {
@ -134,7 +138,7 @@ void MeshPartPayload::bindMesh(gpu::Batch& batch) {
}
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),
_shapeID(shapeIndex) {
@ -210,38 +214,16 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in
auto& modelMesh = model->getNetworkModel()->getMeshes().at(_meshIndex);
_meshNumVertices = (int)modelMesh->getNumVertices();
const Model::MeshState& state = model->getMeshState(_meshIndex);
// const Model::MeshState& state = model->getMeshState(_meshIndex);
updateMeshPart(modelMesh, partIndex);
if (useDualQuaternionSkinning) {
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]));
}
}
*/
Transform renderTransform = transform;
const Model::ShapeState& shapeState = model->getShapeState(shapeIndex);
renderTransform = transform.worldTransform(shapeState._rootFromJointTransform);
updateTransformForSkinnedMesh(renderTransform, transform);
updateTransform(renderTransform);
_deformerIndex = shape.skinDeformer;
initCache(model);
@ -264,7 +246,9 @@ void ModelMeshPartPayload::initCache(const ModelPointer& model) {
if (_drawMesh) {
auto vertexFormat = _drawMesh->getVertexFormat();
_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 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
void ModelMeshPartPayload::updateKey(const render::ItemKey& key) {
ItemKey::Builder builder(key);
@ -420,7 +397,7 @@ void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, RenderArgs::RenderMo
if (_clusterBuffer) {
batch.setUniformBuffer(graphics::slot::buffer::Skinning, _clusterBuffer);
}
batch.setModelTransform(_transform);
batch.setModelTransform(_worldFromLocalTransform);
}
void ModelMeshPartPayload::render(RenderArgs* args) {
@ -458,38 +435,6 @@ void ModelMeshPartPayload::render(RenderArgs* args) {
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) {
if (_meshIndex < blendedMeshSizes.length() && blendedMeshSizes.at(_meshIndex) == _meshNumVertices) {
auto blendshapeBuffer = blendshapeBuffers.find(_meshIndex);

View file

@ -38,7 +38,8 @@ public:
virtual void updateMeshPart(const std::shared_ptr<const graphics::Mesh>& drawMesh, int partIndex);
virtual void notifyLocationChanged() {}
void updateTransform(const Transform& transform, const Transform& offsetTransform);
void updateTransform(const Transform& transform);
void updateTransformAndBound(const Transform& transform );
// Render Item interface
virtual render::ItemKey getKey() const;
@ -52,13 +53,11 @@ public:
virtual void bindTransform(gpu::Batch& batch, RenderArgs::RenderMode renderMode) const;
// Payload resource cached values
Transform _drawTransform;
Transform _transform;
Transform _worldFromLocalTransform;
int _partIndex = 0;
bool _hasColorAttrib { false };
graphics::Box _localBound;
graphics::Box _adjustedLocalBound;
mutable graphics::Box _worldBound;
std::shared_ptr<const graphics::Mesh> _drawMesh;
@ -86,7 +85,7 @@ namespace render {
class ModelMeshPartPayload : public MeshPartPayload {
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 Payload::DataPointer Pointer;
@ -100,7 +99,6 @@ public:
// dual quaternion skinning
void updateClusterBuffer(const std::vector<Model::TransformDualQuaternion>& clusterDualQuaternions);
void updateTransformForSkinnedMesh(const Transform& renderTransform, const Transform& boundTransform);
// Render Item interface
render::ShapeKey getShapeKey() const override; // shape interface
@ -113,12 +111,6 @@ public:
void bindMesh(gpu::Batch& batch) 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;
enum class ClusterBufferType { Matrices, DualQuaternions };
@ -126,6 +118,7 @@ public:
int _meshIndex;
int _shapeID;
int _deformerIndex;
bool _isSkinned{ false };
bool _isBlendShaped { false };

View file

@ -186,7 +186,7 @@ bool Model::shouldInvalidatePayloadShapeKey(int meshIndex) {
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
// 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
invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid
return true;
@ -233,45 +233,52 @@ void Model::updateRenderItems() {
auto meshIndex = self->_modelMeshRenderItemShapes[i].meshIndex;
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 useDualQuaternionSkinning = self->getUseDualQuaternionSkinning();
transaction.updateItem<ModelMeshPartPayload>(itemID, [modelTransform, shapeState, meshState, useDualQuaternionSkinning,
invalidatePayloadShapeKey, primitiveMode, renderItemKeyGlobalFlags, cauterized](ModelMeshPartPayload& data) {
if (useDualQuaternionSkinning) {
data.updateClusterBuffer(meshState.clusterDualQuaternions);
data.computeAdjustedLocalBound(meshState.clusterDualQuaternions);
} else {
data.updateClusterBuffer(meshState.clusterMatrices);
data.computeAdjustedLocalBound(meshState.clusterMatrices);
}
if (isDeformed) {
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) {
const auto& dq = meshState.clusterDualQuaternions[0];
Transform transform(dq.getRotation(),
dq.getScale(),
dq.getTranslation());
renderTransform = modelTransform.worldTransform(Transform(transform));
transaction.updateItem<ModelMeshPartPayload>(itemID, [modelTransform, shapeState, meshState, useDualQuaternionSkinning,
invalidatePayloadShapeKey, primitiveMode, renderItemKeyGlobalFlags, cauterized](ModelMeshPartPayload& data) {
if (useDualQuaternionSkinning) {
data.updateClusterBuffer(meshState.clusterDualQuaternions);
} else {
data.updateClusterBuffer(meshState.clusterMatrices);
}
} else {
if (meshState.clusterMatrices.size() == 1 || meshState.clusterMatrices.size() == 2) {
renderTransform = modelTransform.worldTransform(Transform(meshState.clusterMatrices[0]));
}
}*/
if (meshState.clusterMatrices.size() <= 1) {
Transform renderTransform = modelTransform;
// if (meshState.clusterMatrices.size() <= 1) {
// renderTransform = modelTransform.worldTransform(shapeState._rootFromJointTransform);
// }
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);
}
data.updateTransformForSkinnedMesh(renderTransform, modelTransform);
// }
data.updateTransform(renderTransform);
data.setCauterized(cauterized);
data.updateKey(renderItemKeyGlobalFlags);
data.setShapeKey(invalidatePayloadShapeKey, primitiveMode, useDualQuaternionSkinning);
});
data.updateKey(renderItemKeyGlobalFlags);
data.setShapeKey(invalidatePayloadShapeKey, primitiveMode, false);
});
}
}
AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction);
@ -304,9 +311,9 @@ void Model::updateShapeStatesFromRig() {
const auto& shapes = hfmModel.shapes;
_shapeStates.resize(shapes.size());
for (int s = 0; s < shapes.size(); ++s) {
uint32_t jointId = shapes[s].transform;
if (jointId < _rig.getJointStateCount()) {
_shapeStates[s]._rootFromJointTransform = _rig.getJointTransform(shapes[s].transform);
uint32_t jointId = shapes[s].joint;
if (jointId < (uint32_t) _rig.getJointStateCount()) {
_shapeStates[s]._rootFromJointTransform = _rig.getJointTransform(jointId);
}
}
}
@ -329,14 +336,24 @@ bool Model::updateGeometry() {
updateShapeStatesFromRig();
const HFMModel& hfmModel = getHFMModel();
const auto& hfmSkinDeformers = hfmModel.skinDeformers;
int i = 0;
foreach (const HFMMesh& mesh, hfmModel.meshes) {
/* 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 < 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;
emit rigReady();
}
@ -1410,8 +1427,34 @@ void Model::updateClusterMatrices() {
_needsUpdateClusterMatrices = false;
const HFMModel& hfmModel = getHFMModel();
const auto& hfmSkinDeformers = hfmModel.skinDeformers;
for (int i = 0; i < (int) _meshStates.size(); 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;
const HFMMesh& mesh = hfmModel.meshes.at(i);
for (int j = 0; j < mesh.clusters.size(); j++) {
@ -1428,7 +1471,7 @@ void Model::updateClusterMatrices() {
auto jointMatrix = _rig.getJointTransform(cluster.jointIndex);
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
@ -1477,12 +1520,6 @@ void Model::createRenderItemSet() {
assert(isLoaded());
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
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
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();
for (uint32_t i = 0; i < numMeshes; i++) {
const auto& mesh = meshes.at(i);
@ -1510,17 +1558,17 @@ void Model::createRenderItemSet() {
// Create the render payloads
int numParts = (int)mesh->getNumParts();
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);
_modelMeshMaterialNames.push_back(material ? material->getName() : "");
_modelMeshRenderItemShapes.emplace_back(ShapeInfo{ (int)i });
shapeID++;
}
}
}*/
}
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) {

View file

@ -473,7 +473,7 @@ protected:
QVector<std::shared_ptr<ModelMeshPartPayload>> _modelMeshRenderItems;
QMap<render::ItemID, render::PayloadPointer> _modelMeshRenderItemsMap;
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<std::string> _modelMeshMaterialNames;

View file

@ -24,6 +24,10 @@ public:
~MainWindow();
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:
void restoreGeometry();
void saveGeometry();
@ -46,6 +50,7 @@ protected:
private:
Setting::Handle<QRect> _windowGeometry;
Setting::Handle<int> _windowState;
QSize _dockedWidgetRelativePositionOffset{ 0, 0 };
};
#endif /* defined(__hifi__MainWindow__) */

View file

@ -444,17 +444,9 @@ function updateEmoteIndicatorIcon(iconURL) {
}
function onGeometryChanged(rect) {
updateEmoteAppBarPosition();
}
function onWindowMinimizedChanged(isMinimized) {
if (isMinimized) {
handleEmoteIndicatorVisibleChanged(false);
} else if (!HMD.active) {
handleEmoteIndicatorVisibleChanged(true);
}
isWindowMinimized = isMinimized;
maybeChangeEmoteIndicatorVisibility(!isMinimized);
}
@ -539,10 +531,11 @@ function showEmoteAppBar() {
x: EMOTE_APP_BAR_WIDTH_PX,
y: EMOTE_APP_BAR_HEIGHT_PX
},
position: {
x: Window.x + EMOTE_APP_BAR_LEFT_MARGIN,
y: Window.y + Window.innerHeight - EMOTE_APP_BAR_BOTTOM_MARGIN
relativePosition: {
x: EMOTE_APP_BAR_LEFT_MARGIN,
y: EMOTE_APP_BAR_BOTTOM_MARGIN
},
relativePositionAnchor: Desktop.RelativePositionAnchor.BOTTOM_LEFT,
overrideFlags: EMOTE_APP_BAR_WINDOW_FLAGS
});
@ -550,10 +543,18 @@ function showEmoteAppBar() {
}
function handleEmoteIndicatorVisibleChanged(shouldBeVisible) {
if (shouldBeVisible && !emoteAppBarWindow) {
// There is currently no property in the Window Scripting Interface to determine
// 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();
} else if (emoteAppBarWindow) {
} else if (!desiredVisibility && emoteAppBarWindow) {
emoteAppBarWindow.fromQml.disconnect(onMessageFromEmoteAppBar);
emoteAppBarWindow.close();
emoteAppBarWindow = false;
@ -561,23 +562,25 @@ function handleEmoteIndicatorVisibleChanged(shouldBeVisible) {
}
function handleFTUEScreensVisibilityChanged(ftueScreenVisible) {
maybeChangeEmoteIndicatorVisibility(!ftueScreenVisible);
}
function onDisplayModeChanged(isHMDMode) {
reactionsBegun.forEach(function(react) {
endReactionWrapper(react);
});
if (isHMDMode) {
handleEmoteIndicatorVisibleChanged(false);
} else {
handleEmoteIndicatorVisibleChanged(true);
}
maybeChangeEmoteIndicatorVisibility(!isHMDMode);
}
var emojiAPI = Script.require("./emojiApp/simplifiedEmoji.js?" + Date.now());
var emojiAPI = Script.require("./emojiApp/simplifiedEmoji.js");
var keyPressSignalsConnected = false;
var emojiCodeMap;
var customEmojiCodeMap;
var _this;
function setup() {
deleteOldReticles();
@ -605,16 +608,25 @@ function setup() {
}, {});
Window.minimizedChanged.connect(onWindowMinimizedChanged);
Window.geometryChanged.connect(onGeometryChanged);
HMD.displayModeChanged.connect(onDisplayModeChanged);
getSounds();
handleEmoteIndicatorVisibleChanged(true);
maybeChangeEmoteIndicatorVisibility(true);
Controller.keyPressEvent.connect(keyPressHandler);
Controller.keyReleaseEvent.connect(keyReleaseHandler);
keyPressSignalsConnected = true;
Script.scriptEnding.connect(unload);
function Emote() {
_this = this;
}
Emote.prototype = {
handleFTUEScreensVisibilityChanged: handleFTUEScreensVisibilityChanged
};
return new Emote();
}
@ -638,7 +650,6 @@ function unload() {
maybeDeleteRemoteIndicatorTimeout();
Window.minimizedChanged.disconnect(onWindowMinimizedChanged);
Window.geometryChanged.disconnect(onGeometryChanged);
HMD.displayModeChanged.disconnect(onDisplayModeChanged);
if (keyPressSignalsConnected) {
@ -671,7 +682,6 @@ function unload() {
// #region EMOJI_UTILITY
var EMOJI_52_BASE_URL = "../../resources/images/emojis/52px/";
function selectedEmoji(code) {
emojiAPI.addEmoji(code);
// this URL needs to be relative to SimplifiedEmoteIndicator.qml
@ -786,4 +796,6 @@ function toggleEmojiApp() {
// END EMOJI
// *************************************
setup();
var emote = setup();
module.exports = emote;

View file

@ -1,15 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 23.0.6, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 93.7 127.8" style="enable-background:new 0 0 93.7 127.8;" xml:space="preserve">
<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
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
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
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
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
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
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
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
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"/>
<!-- Generator: Adobe Illustrator 23.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
<!ENTITY ns_extend "http://ns.adobe.com/Extensibility/1.0/">
<!ENTITY ns_ai "http://ns.adobe.com/AdobeIllustrator/10.0/">
<!ENTITY ns_graphs "http://ns.adobe.com/Graphs/1.0/">
<!ENTITY ns_vars "http://ns.adobe.com/Variables/1.0/">
<!ENTITY ns_imrep "http://ns.adobe.com/ImageReplacement/1.0/">
<!ENTITY ns_sfw "http://ns.adobe.com/SaveForWeb/1.0/">
<!ENTITY ns_custom "http://ns.adobe.com/GenericCustomNamespace/1.0/">
<!ENTITY ns_adobe_xpath "http://ns.adobe.com/XPath/1.0/">
]>
<svg version="1.1" id="Layer_1" xmlns:x="&ns_extend;" xmlns:i="&ns_ai;" xmlns:graph="&ns_graphs;"
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>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View 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);
}

View 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);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

View file

@ -14,7 +14,6 @@
// START CONFIG OPTIONS
var DOCKED_QML_SUPPORTED = true;
var TOOLBAR_NAME = "com.highfidelity.interface.toolbar.system";
var DEFAULT_SCRIPTS_PATH_PREFIX = ScriptDiscoveryService.defaultScriptsPath + "/";
// END CONFIG OPTIONS
@ -284,28 +283,21 @@ function maybeDeleteOutputDeviceMutedOverlay() {
var outputDeviceMutedOverlay = false;
var OUTPUT_DEVICE_MUTED_OVERLAY_DEFAULT_DIMS_PX = 300;
var OUTPUT_DEVICE_MUTED_MARGIN_BOTTOM_PX = 20;
var OUTPUT_DEVICE_MUTED_MARGIN_LEFT_RIGHT_PX = 20;
var OUTPUT_DEVICE_MUTED_OVERLAY_DEFAULT_MARGINS_PX = 20;
var OUTPUT_DEVICE_MUTED_DIMS_RATIO_TO_WINDOW_SIZE = 0.8;
function updateOutputDeviceMutedOverlay(isMuted) {
if (isMuted) {
var props = {
imageURL: Script.resolvePath("images/outputDeviceMuted.svg"),
alpha: 0.5
};
var overlayDims = OUTPUT_DEVICE_MUTED_OVERLAY_DEFAULT_DIMS_PX;
props.x = Window.innerWidth / 2 - overlayDims / 2;
props.y = Window.innerHeight / 2 - overlayDims / 2;
var overlayWithMarginsDims = overlayDims + 2 * OUTPUT_DEVICE_MUTED_OVERLAY_DEFAULT_MARGINS_PX;
var outputDeviceMutedOverlayBottomY = props.y + overlayDims;
var inputDeviceMutedOverlayTopY = INPUT_DEVICE_MUTED_MARGIN_TOP_PX;
if (outputDeviceMutedOverlayBottomY + OUTPUT_DEVICE_MUTED_MARGIN_BOTTOM_PX > inputDeviceMutedOverlayTopY) {
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);
if (overlayWithMarginsDims > Window.innerHeight || overlayWithMarginsDims > Window.innerWidth) {
var minWindowDims = Math.min(Window.innerHeight, Window.innerWidth);
overlayDims = Math.round(minWindowDims * OUTPUT_DEVICE_MUTED_DIMS_RATIO_TO_WINDOW_SIZE);
}
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;
function sendLocalStatusToQml() {
@ -404,6 +514,14 @@ function onMessageFromTopBar(message) {
si.toggleStatus();
break;
case "displayInitialLaunchWindow":
displayInitialLaunchWindow();
break;
case "displaySecondLaunchWindow":
displaySecondLaunchWindow();
break;
default:
console.log("Unrecognized message from " + TOP_BAR_MESSAGE_SOURCE + ": " + JSON.stringify(message));
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_PRESENTATION_MODE = Desktop.PresentationMode.NATIVE;
var TOP_BAR_WIDTH_PX = Window.innerWidth;
var TOP_BAR_HEIGHT_PX = 48;
var topBarWindow = false;
function loadSimplifiedTopBar() {
var windowProps = {
@ -443,16 +560,9 @@ function loadSimplifiedTopBar() {
y: TOP_BAR_HEIGHT_PX
}
};
if (DOCKED_QML_SUPPORTED) {
windowProps.presentationWindowInfo = {
dockArea: Desktop.DockArea.TOP
};
} else {
windowProps.position = {
x: Window.x,
y: Window.y
};
}
windowProps.presentationWindowInfo = {
dockArea: Desktop.DockArea.TOP
};
topBarWindow = Desktop.createWindow(TOP_BAR_QML_PATH, windowProps);
topBarWindow.fromQml.connect(onMessageFromTopBar);
@ -517,21 +627,38 @@ function onHMDInputDeviceMutedChanged(isMuted) {
function onGeometryChanged(rect) {
updateInputDeviceMutedOverlay(Audio.muted);
updateOutputDeviceMutedOverlay(isOutputMuted());
if (topBarWindow && !DOCKED_QML_SUPPORTED) {
topBarWindow.size = {
"x": rect.width,
"y": TOP_BAR_HEIGHT_PX
};
topBarWindow.position = {
"x": rect.x,
"y": rect.y
};
}
var initialLaunchWindowIsMinimized = false;
var secondLaunchWindowIsMinimized = false;
function onWindowMinimizedChanged(isMinimized) {
if (isMinimized) {
handleInitialLaunchWindowVisibleChanged(false);
handleSecondLaunchWindowVisibleChanged(false);
} else if (!HMD.active) {
handleInitialLaunchWindowVisibleChanged(true);
handleSecondLaunchWindowVisibleChanged(true);
}
}
function onWindowMinimizedChanged() {
// prerequisite placeholder for Reduce Friction of Customer Acquisition sub task: https://highfidelity.atlassian.net/browse/DEV-585
print("WINDOW MINIMIZED CHANGED SIGNAL");
function handleInitialLaunchWindowVisibleChanged(shouldBeVisible) {
if (shouldBeVisible && !initialLaunchWindow && initialLaunchWindowIsMinimized) {
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) {
@ -541,8 +668,12 @@ function onDisplayModeChanged(isHMDMode) {
if (isHMDMode) {
onHMDInputDeviceMutedChanged(Audio.mutedHMD);
handleInitialLaunchWindowVisibleChanged(false);
handleSecondLaunchWindowVisibleChanged(false);
} else {
onDesktopInputDeviceMutedChanged(Audio.mutedDesktop);
handleInitialLaunchWindowVisibleChanged(true);
handleSecondLaunchWindowVisibleChanged(true);
}
}
@ -585,9 +716,9 @@ function restoreLODSettings() {
}
var nametag = Script.require("./simplifiedNametag/simplifiedNametag.js?" + Date.now());
var si = Script.require("./simplifiedStatusIndicator/simplifiedStatusIndicator.js?" + Date.now());
var emote = Script.require("../simplifiedEmote/simplifiedEmote.js?" + Date.now());
var nametag = Script.require("./simplifiedNametag/simplifiedNametag.js");
var si = Script.require("./simplifiedStatusIndicator/simplifiedStatusIndicator.js");
var simplifiedEmote = Script.require("../simplifiedEmote/simplifiedEmote.js");
var oldShowAudioTools;
var oldShowBubbleTools;
var keepExistingUIAndScriptsSetting = Settings.getValue("simplifiedUI/keepExistingUIAndScripts", false);
@ -654,6 +785,14 @@ function shutdown() {
settingsAppWindow.close();
}
if (initialLaunchWindow) {
closeInitialLaunchWindow();
}
if (secondLaunchWindow) {
closeSecondLaunchWindow();
}
maybeDeleteInputDeviceMutedOverlay();
maybeDeleteOutputDeviceMutedOverlay();

View file

@ -50,11 +50,11 @@ var OVERLAY_DATA_HMD = {
};
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,
loopFlag: false,
startFrame: 0.0,
endFrame: 83.0
loopFlag: true,
startFrame: 1.0,
endFrame: 489.0
};
// MAIN CONTROL