mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-14 02:06:30 +02:00
Merge branch 'master' of github.com:highfidelity/hifi into 20816-installOnAndroid
This commit is contained in:
commit
79f096fb7e
166 changed files with 4251 additions and 3265 deletions
|
@ -81,7 +81,7 @@ if (ANDROID)
|
|||
set(GLES_OPTION ON)
|
||||
set(PLATFORM_QT_COMPONENTS AndroidExtras WebView)
|
||||
else ()
|
||||
set(PLATFORM_QT_COMPONENTS WebEngine Xml)
|
||||
set(PLATFORM_QT_COMPONENTS WebEngine)
|
||||
endif ()
|
||||
|
||||
if (USE_GLES AND (NOT ANDROID))
|
||||
|
|
|
@ -32,8 +32,8 @@ To produce an executable installer on Windows, the following are required:
|
|||
|
||||
1. [InetC Plug-in for Nullsoft](http://nsis.sourceforge.net/Inetc_plug-in) - 1.0
|
||||
1. Extract Zip
|
||||
1. Copy `Plugins\x86-ansi\InetC.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-ansi\`
|
||||
1. Copy `Plugins\x86-unicode\InetC.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-unicode\`
|
||||
1. Copy `Plugin\x86-ansi\InetC.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-ansi\`
|
||||
1. Copy `Plugin\x86-unicode\InetC.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-unicode\`
|
||||
|
||||
1. [NSISpcre Plug-in for Nullsoft](http://nsis.sourceforge.net/NSISpcre_plug-in) - 1.0
|
||||
1. Extract Zip
|
||||
|
|
|
@ -68,6 +68,13 @@ AudioMixer::AudioMixer(ReceivedMessage& message) :
|
|||
// hash the available codecs (on the mixer)
|
||||
_availableCodecs.clear(); // Make sure struct is clean
|
||||
auto pluginManager = DependencyManager::set<PluginManager>();
|
||||
// Only load codec plugins; for now assume codec plugins have 'codec' in their name.
|
||||
auto codecPluginFilter = [](const QJsonObject& metaData) {
|
||||
QJsonValue nameValue = metaData["MetaData"]["name"];
|
||||
return nameValue.toString().contains("codec", Qt::CaseInsensitive);
|
||||
};
|
||||
pluginManager->setPluginFilter(codecPluginFilter);
|
||||
|
||||
auto codecPlugins = pluginManager->getCodecPlugins();
|
||||
for_each(codecPlugins.cbegin(), codecPlugins.cend(),
|
||||
[&](const CodecPluginPointer& codec) {
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
#
|
||||
# FixupNitpick.cmake
|
||||
# cmake/macros
|
||||
#
|
||||
# Copyright 2019 High Fidelity, Inc.
|
||||
# Created by Nissim Hadar on January 14th, 2016
|
||||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
|
||||
macro(fixup_nitpick)
|
||||
if (APPLE)
|
||||
string(REPLACE " " "\\ " ESCAPED_BUNDLE_NAME ${NITPICK_BUNDLE_NAME})
|
||||
string(REPLACE " " "\\ " ESCAPED_INSTALL_PATH ${NITPICK_INSTALL_DIR})
|
||||
set(_NITPICK_INSTALL_PATH "${ESCAPED_INSTALL_PATH}/${ESCAPED_BUNDLE_NAME}.app")
|
||||
|
||||
find_program(MACDEPLOYQT_COMMAND macdeployqt PATHS "${QT_DIR}/bin" NO_DEFAULT_PATH)
|
||||
|
||||
if (NOT MACDEPLOYQT_COMMAND AND (PRODUCTION_BUILD OR PR_BUILD))
|
||||
message(FATAL_ERROR "Could not find macdeployqt at ${QT_DIR}/bin.\
|
||||
It is required to produce a relocatable nitpick application.\
|
||||
Check that the environment variable QT_DIR points to your Qt installation.\
|
||||
")
|
||||
endif ()
|
||||
|
||||
install(CODE "
|
||||
execute_process(COMMAND ${MACDEPLOYQT_COMMAND}\
|
||||
\${CMAKE_INSTALL_PREFIX}/${_NITPICK_INSTALL_PATH}/\
|
||||
-verbose=2 -qmldir=${CMAKE_SOURCE_DIR}/interface/resources/qml/\
|
||||
)"
|
||||
COMPONENT ${CLIENT_COMPONENT}
|
||||
)
|
||||
|
||||
endif ()
|
||||
endmacro()
|
|
@ -246,6 +246,7 @@ void AssetsBackupHandler::createBackup(const QString& backupName, QuaZip& zip) {
|
|||
|
||||
if (_assetServerEnabled && _lastMappingsRefresh.time_since_epoch().count() == 0) {
|
||||
qCWarning(asset_backup) << "Current mappings not yet loaded.";
|
||||
_backups.emplace_back(backupName, AssetUtils::Mappings(), true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -54,7 +54,8 @@ FocusScope {
|
|||
Image {
|
||||
z: -10
|
||||
id: loginDialogBackground
|
||||
source: "LoginDialog/images/background.jpg"
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
source: "LoginDialog/images/background.png"
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
|
@ -119,6 +120,6 @@ FocusScope {
|
|||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
bodyLoader.setSource("LoginDialog/LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "linkSteam": false });
|
||||
bodyLoader.setSource("LoginDialog/LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "linkSteam": false, "linkOculus": false });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,11 +22,16 @@ Item {
|
|||
width: root.width
|
||||
height: root.height
|
||||
readonly property string termsContainerText: qsTr("By creating this user profile, you agree to High Fidelity's Terms of Service")
|
||||
readonly property string termsContainerOculusText: qsTr("By signing up, you agree to High Fidelity's Terms of Service")
|
||||
readonly property int textFieldHeight: 31
|
||||
readonly property string fontFamily: "Raleway"
|
||||
readonly property int fontSize: 15
|
||||
readonly property bool fontBold: true
|
||||
readonly property int textFieldFontSize: 18
|
||||
readonly property var passwordImageRatio: 16 / 23
|
||||
|
||||
readonly property bool withSteam: withSteam
|
||||
property bool withOculus: withOculus
|
||||
property bool withSteam: withSteam
|
||||
property string errorString: errorString
|
||||
|
||||
readonly property bool loginDialogPoppedUp: loginDialog.getLoginDialogPoppedUp()
|
||||
|
@ -61,15 +66,20 @@ Item {
|
|||
|
||||
Item {
|
||||
id: contentItem
|
||||
anchors.fill: parent
|
||||
width: parent.width
|
||||
height: errorContainer.height + fields.height + buttons.height + additionalTextContainer.height +
|
||||
termsContainer.height
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: root.bannerHeight + 0.25 * parent.height
|
||||
anchors.left: parent.left
|
||||
|
||||
Item {
|
||||
id: errorContainer
|
||||
width: parent.width
|
||||
width: root.bannerWidth
|
||||
height: loginErrorMessageTextMetrics.height
|
||||
anchors {
|
||||
bottom: buttons.top;
|
||||
bottomMargin: hifi.dimensions.contentSpacing.y;
|
||||
bottom: completeProfileBody.withOculus ? fields.top : buttons.top;
|
||||
bottomMargin: 1.5 * hifi.dimensions.contentSpacing.y;
|
||||
left: buttons.left;
|
||||
}
|
||||
TextMetrics {
|
||||
|
@ -79,8 +89,8 @@ Item {
|
|||
}
|
||||
Text {
|
||||
id: loginErrorMessage;
|
||||
width: root.bannerWidth
|
||||
color: "red";
|
||||
width: root.bannerWidth;
|
||||
font.family: completeProfileBody.fontFamily
|
||||
font.pixelSize: 18
|
||||
font.bold: completeProfileBody.fontBold
|
||||
|
@ -88,13 +98,196 @@ Item {
|
|||
horizontalAlignment: Text.AlignHCenter
|
||||
text: completeProfileBody.errorString
|
||||
visible: true
|
||||
onTextChanged: {
|
||||
mainContainer.recalculateErrorMessage();
|
||||
}
|
||||
Component.onCompleted: {
|
||||
mainContainer.recalculateErrorMessage();
|
||||
}
|
||||
}
|
||||
Component.onCompleted: {
|
||||
if (loginErrorMessageTextMetrics.width > root.bannerWidth && root.isTablet) {
|
||||
loginErrorMessage.wrapMode = Text.WordWrap;
|
||||
loginErrorMessage.verticalAlignment = Text.AlignLeft;
|
||||
loginErrorMessage.horizontalAlignment = Text.AlignLeft;
|
||||
errorContainer.height = 3 * loginErrorMessageTextMetrics.height;
|
||||
}
|
||||
|
||||
Item {
|
||||
id: fields
|
||||
width: root.bannerWidth
|
||||
height: 3 * completeProfileBody.textFieldHeight + 2 * hifi.dimensions.contentSpacing.y
|
||||
visible: completeProfileBody.withOculus
|
||||
anchors {
|
||||
left: parent.left
|
||||
leftMargin: (parent.width - root.bannerWidth) / 2
|
||||
bottom: buttons.top
|
||||
bottomMargin: hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
|
||||
HifiControlsUit.TextField {
|
||||
id: usernameField
|
||||
width: root.bannerWidth
|
||||
height: completeProfileBody.textFieldHeight
|
||||
placeholderText: "Username"
|
||||
font.pixelSize: completeProfileBody.textFieldFontSize
|
||||
styleRenderType: Text.QtRendering
|
||||
anchors {
|
||||
top: parent.top
|
||||
}
|
||||
Keys.onPressed: {
|
||||
if (!usernameField.visible) {
|
||||
return;
|
||||
}
|
||||
switch (event.key) {
|
||||
case Qt.Key_Tab:
|
||||
event.accepted = true;
|
||||
if (event.modifiers === Qt.ShiftModifier) {
|
||||
passwordField.focus = true;
|
||||
} else {
|
||||
emailField.focus = true;
|
||||
}
|
||||
break;
|
||||
case Qt.Key_Backtab:
|
||||
event.accepted = true;
|
||||
passwordField.focus = true;
|
||||
break;
|
||||
case Qt.Key_Enter:
|
||||
case Qt.Key_Return:
|
||||
event.accepted = true;
|
||||
loginDialog.createAccountFromOculus(emailField.text, usernameField.text, passwordField.text);
|
||||
bodyLoader.setSource("LoggingInBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": completeProfileBody.withSteam,
|
||||
"linkSteam": false, "withOculus": completeProfileBody.withOculus, "linkOculus": false, "createOculus": true });
|
||||
break;
|
||||
}
|
||||
}
|
||||
onFocusChanged: {
|
||||
root.text = "";
|
||||
if (focus) {
|
||||
root.isPassword = false;
|
||||
}
|
||||
}
|
||||
Component.onCompleted: {
|
||||
var userID = "";
|
||||
if (completeProfileBody.withOculus) {
|
||||
userID = loginDialog.oculusUserID();
|
||||
}
|
||||
usernameField.text = userID;
|
||||
}
|
||||
}
|
||||
HifiControlsUit.TextField {
|
||||
id: emailField
|
||||
width: root.bannerWidth
|
||||
height: completeProfileBody.textFieldHeight
|
||||
anchors {
|
||||
top: usernameField.bottom
|
||||
topMargin: hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
placeholderText: "Email"
|
||||
font.pixelSize: completeProfileBody.textFieldFontSize
|
||||
styleRenderType: Text.QtRendering
|
||||
activeFocusOnPress: true
|
||||
Keys.onPressed: {
|
||||
switch (event.key) {
|
||||
case Qt.Key_Tab:
|
||||
event.accepted = true;
|
||||
if (event.modifiers === Qt.ShiftModifier) {
|
||||
usernameField.focus = true;
|
||||
} else {
|
||||
passwordField.focus = true;
|
||||
}
|
||||
break;
|
||||
case Qt.Key_Backtab:
|
||||
event.accepted = true;
|
||||
usernameField.focus = true;
|
||||
break;
|
||||
case Qt.Key_Enter:
|
||||
case Qt.Key_Return:
|
||||
event.accepted = true;
|
||||
loginDialog.createAccountFromOculus(emailField.text, usernameField.text, passwordField.text);
|
||||
bodyLoader.setSource("LoggingInBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": completeProfileBody.withSteam,
|
||||
"linkSteam": false, "withOculus": completeProfileBody.withOculus, "linkOculus": false, "createOculus": true });
|
||||
break;
|
||||
}
|
||||
}
|
||||
onFocusChanged: {
|
||||
root.text = "";
|
||||
if (focus) {
|
||||
root.isPassword = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
HifiControlsUit.TextField {
|
||||
id: passwordField
|
||||
width: root.bannerWidth
|
||||
height: completeProfileBody.textFieldHeight
|
||||
placeholderText: "Password (optional)"
|
||||
font.pixelSize: completeProfileBody.textFieldFontSize
|
||||
styleRenderType: Text.QtRendering
|
||||
activeFocusOnPress: true
|
||||
echoMode: passwordFieldMouseArea.showPassword ? TextInput.Normal : TextInput.Password
|
||||
anchors {
|
||||
top: emailField.bottom
|
||||
topMargin: hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
|
||||
onFocusChanged: {
|
||||
root.text = "";
|
||||
root.isPassword = focus;
|
||||
}
|
||||
|
||||
Item {
|
||||
id: showPasswordContainer
|
||||
z: 10
|
||||
// width + image's rightMargin
|
||||
width: showPasswordImage.width + 8
|
||||
height: parent.height
|
||||
anchors {
|
||||
right: parent.right
|
||||
}
|
||||
|
||||
Image {
|
||||
id: showPasswordImage
|
||||
width: passwordField.height * passwordImageRatio
|
||||
height: passwordField.height * passwordImageRatio
|
||||
anchors {
|
||||
right: parent.right
|
||||
rightMargin: 8
|
||||
top: parent.top
|
||||
topMargin: passwordFieldMouseArea.showPassword ? 6 : 8
|
||||
bottom: parent.bottom
|
||||
bottomMargin: passwordFieldMouseArea.showPassword ? 5 : 8
|
||||
}
|
||||
source: passwordFieldMouseArea.showPassword ? "../../images/eyeClosed.svg" : "../../images/eyeOpen.svg"
|
||||
MouseArea {
|
||||
id: passwordFieldMouseArea
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton
|
||||
property bool showPassword: false
|
||||
onClicked: {
|
||||
showPassword = !showPassword;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Keys.onPressed: {
|
||||
switch (event.key) {
|
||||
case Qt.Key_Tab:
|
||||
event.accepted = true;
|
||||
if (event.modifiers === Qt.ShiftModifier) {
|
||||
emailField.focus = true;
|
||||
} else if (usernameField.visible) {
|
||||
usernameField.focus = true;
|
||||
} else {
|
||||
emailField.focus = true;
|
||||
}
|
||||
break;
|
||||
case Qt.Key_Backtab:
|
||||
event.accepted = true;
|
||||
emailField.focus = true;
|
||||
break;
|
||||
case Qt.Key_Enter:
|
||||
case Qt.Key_Return:
|
||||
event.accepted = true;
|
||||
loginDialog.createAccountFromOculus(emailField.text, usernameField.text, passwordField.text);
|
||||
bodyLoader.setSource("LoggingInBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": completeProfileBody.withSteam,
|
||||
"linkSteam": false, "withOculus": completeProfileBody.withOculus, "linkOculus": false, "createOculus": true });
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -105,7 +298,7 @@ Item {
|
|||
height: d.minHeightButton
|
||||
anchors {
|
||||
top: parent.top
|
||||
topMargin: (parent.height - additionalTextContainer.height) / 2 - hifi.dimensions.contentSpacing.y
|
||||
topMargin: (parent.height - additionalTextContainer.height + fields.height) / 2 - hifi.dimensions.contentSpacing.y
|
||||
left: parent.left
|
||||
leftMargin: (parent.width - root.bannerWidth) / 2
|
||||
}
|
||||
|
@ -144,7 +337,7 @@ Item {
|
|||
width: (parent.width - hifi.dimensions.contentSpacing.x) / 2
|
||||
height: d.minHeightButton
|
||||
|
||||
text: qsTr("Create your profile")
|
||||
text: completeProfileBody.withOculus ? qsTr("Sign Up") : qsTr("Create your profile")
|
||||
color: hifi.buttons.blue
|
||||
|
||||
fontFamily: completeProfileBody.fontFamily
|
||||
|
@ -158,55 +351,12 @@ Item {
|
|||
UserActivityLogger.logAction("encourageLoginDialog", data);
|
||||
}
|
||||
loginErrorMessage.visible = false;
|
||||
loginDialog.createAccountFromSteam();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: additionalTextContainer
|
||||
width: parent.width
|
||||
height: additionalTextMetrics.height
|
||||
anchors {
|
||||
top: buttons.bottom
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
topMargin: hifi.dimensions.contentSpacing.y
|
||||
left: parent.left
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: additionalTextMetrics
|
||||
font: additionalText.font
|
||||
text: "Already have a High Fidelity profile? Link to an existing profile here."
|
||||
}
|
||||
|
||||
HifiStylesUit.ShortcutText {
|
||||
id: additionalText
|
||||
text: "<a href='https://fake.link'>Already have a High Fidelity profile? Link to an existing profile here.</a>"
|
||||
|
||||
font.family: completeProfileBody.fontFamily
|
||||
font.pixelSize: completeProfileBody.fontSize
|
||||
font.bold: completeProfileBody.fontBold
|
||||
wrapMode: Text.NoWrap
|
||||
lineHeight: 1
|
||||
lineHeightMode: Text.ProportionalHeight
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
linkColor: hifi.colors.blueAccent
|
||||
|
||||
onLinkActivated: {
|
||||
loginDialog.isLogIn = true;
|
||||
bodyLoader.setSource("LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "errorString": "", "withSteam": true, "linkSteam": true });
|
||||
}
|
||||
Component.onCompleted: {
|
||||
if (additionalTextMetrics.width > root.bannerWidth && root.isTablet) {
|
||||
additionalText.width = root.bannerWidth;
|
||||
additionalText.wrapMode = Text.WordWrap;
|
||||
additionalText.verticalAlignment = Text.AlignLeft;
|
||||
additionalText.horizontalAlignment = Text.AlignLeft;
|
||||
additionalTextContainer.height = (additionalTextMetrics.width / root.bannerWidth) * additionalTextMetrics.height;
|
||||
additionalTextContainer.anchors.left = buttons.left;
|
||||
} else {
|
||||
additionalText.anchors.centerIn = additionalTextContainer;
|
||||
if (completeProfileBody.withOculus) {
|
||||
loginDialog.createAccountFromOculus(emailField.text, usernameField.text, passwordField.text);
|
||||
bodyLoader.setSource("LoggingInBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": completeProfileBody.withSteam,
|
||||
"linkSteam": false, "withOculus": completeProfileBody.withOculus, "linkOculus": false, "createOculus": true });
|
||||
} else if (completeProfileBody.withSteam) {
|
||||
loginDialog.createAccountFromSteam();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -217,29 +367,33 @@ Item {
|
|||
width: parent.width
|
||||
height: termsTextMetrics.height
|
||||
anchors {
|
||||
top: additionalTextContainer.bottom
|
||||
top: buttons.bottom
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
topMargin: 2 * hifi.dimensions.contentSpacing.y
|
||||
topMargin: hifi.dimensions.contentSpacing.y
|
||||
left: parent.left
|
||||
}
|
||||
TextMetrics {
|
||||
id: termsTextMetrics
|
||||
font: termsText.font
|
||||
text: completeProfileBody.termsContainerText
|
||||
text: completeProfileBody.withOculus ? completeProfileBody.termsContainerOculusText : completeProfileBody.termsContainerText
|
||||
Component.onCompleted: {
|
||||
// with the link.
|
||||
termsText.text = qsTr("By creating this user profile, you agree to <a href='https://highfidelity.com/terms'>High Fidelity's Terms of Service</a>")
|
||||
if (completeProfileBody.withOculus) {
|
||||
termsText.text = qsTr("By signing up, you agree to <a href='https://highfidelity.com/terms'>High Fidelity's Terms of Service</a>")
|
||||
} else {
|
||||
termsText.text = qsTr("By creating this user profile, you agree to <a href='https://highfidelity.com/terms'>High Fidelity's Terms of Service</a>")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HifiStylesUit.InfoItem {
|
||||
id: termsText
|
||||
text: completeProfileBody.termsContainerText
|
||||
text: completeProfileBody.withOculus ? completeProfileBody.termsContainerOculusText : completeProfileBody.termsContainerText
|
||||
font.family: completeProfileBody.fontFamily
|
||||
font.pixelSize: completeProfileBody.fontSize
|
||||
font.bold: completeProfileBody.fontBold
|
||||
wrapMode: Text.WordWrap
|
||||
color: hifi.colors.lightGray
|
||||
color: hifi.colors.white
|
||||
linkColor: hifi.colors.blueAccent
|
||||
lineHeight: 1
|
||||
lineHeightMode: Text.ProportionalHeight
|
||||
|
@ -247,7 +401,7 @@ Item {
|
|||
onLinkActivated: loginDialog.openUrl(link);
|
||||
|
||||
Component.onCompleted: {
|
||||
if (termsTextMetrics.width > root.bannerWidth && root.isTablet) {
|
||||
if (termsTextMetrics.width > root.bannerWidth) {
|
||||
termsText.width = root.bannerWidth;
|
||||
termsText.wrapMode = Text.WordWrap;
|
||||
additionalText.verticalAlignment = Text.AlignLeft;
|
||||
|
@ -260,14 +414,86 @@ Item {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: additionalTextContainer
|
||||
width: parent.width
|
||||
height: additionalTextMetrics.height
|
||||
anchors {
|
||||
top: termsContainer.bottom
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
topMargin: 2 * hifi.dimensions.contentSpacing.y
|
||||
left: parent.left
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: additionalTextMetrics
|
||||
font: additionalText.font
|
||||
text: "Already have a High Fidelity profile? Link to an existing profile here."
|
||||
}
|
||||
|
||||
HifiStylesUit.ShortcutText {
|
||||
id: additionalText
|
||||
text: "<a href='https://fake.link'>Already have a High Fidelity profile? Link to an existing profile here.</a>"
|
||||
width: root.bannerWidth;
|
||||
font.family: completeProfileBody.fontFamily
|
||||
font.pixelSize: completeProfileBody.fontSize
|
||||
font.bold: completeProfileBody.fontBold
|
||||
wrapMode: Text.NoWrap
|
||||
lineHeight: 1
|
||||
lineHeightMode: Text.ProportionalHeight
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
linkColor: hifi.colors.blueAccent
|
||||
|
||||
onLinkActivated: {
|
||||
bodyLoader.setSource("LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "errorString": "",
|
||||
"withSteam": completeProfileBody.withSteam, "linkSteam": completeProfileBody.withSteam, "withOculus": completeProfileBody.withOculus,
|
||||
"linkOculus": completeProfileBody.withOculus });
|
||||
}
|
||||
Component.onCompleted: {
|
||||
if (additionalTextMetrics.width > root.bannerWidth) {
|
||||
additionalText.wrapMode = Text.WordWrap;
|
||||
additionalText.verticalAlignment = Text.AlignLeft;
|
||||
additionalText.horizontalAlignment = Text.AlignLeft;
|
||||
additionalTextContainer.height = (additionalTextMetrics.width / root.bannerWidth) * additionalTextMetrics.height;
|
||||
additionalTextContainer.anchors.left = buttons.left;
|
||||
} else {
|
||||
additionalText.anchors.centerIn = additionalTextContainer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function recalculateErrorMessage() {
|
||||
if (completeProfileBody.errorString !== "") {
|
||||
loginErrorMessage.visible = true;
|
||||
var errorLength = completeProfileBody.errorString.split(/\r\n|\r|\n/).length;
|
||||
var errorStringEdited = completeProfileBody.errorString.replace(/[\n\r]+/g, "\n");
|
||||
loginErrorMessage.text = errorStringEdited;
|
||||
if (errorLength > 1.0) {
|
||||
loginErrorMessage.wrapMode = Text.WordWrap;
|
||||
loginErrorMessage.verticalAlignment = Text.AlignLeft;
|
||||
loginErrorMessage.horizontalAlignment = Text.AlignLeft;
|
||||
errorContainer.height = errorLength * loginErrorMessageTextMetrics.height;
|
||||
} else if (loginErrorMessageTextMetrics.width > root.bannerWidth) {
|
||||
loginErrorMessage.wrapMode = Text.WordWrap;
|
||||
loginErrorMessage.verticalAlignment = Text.AlignLeft;
|
||||
loginErrorMessage.horizontalAlignment = Text.AlignLeft;
|
||||
errorContainer.height = (loginErrorMessageTextMetrics.width / root.bannerWidth) * loginErrorMessageTextMetrics.height;
|
||||
} else {
|
||||
loginErrorMessage.wrapMode = Text.NoWrap;
|
||||
loginErrorMessage.verticalAlignment = Text.AlignVCenter;
|
||||
loginErrorMessage.horizontalAlignment = Text.AlignHCenter;
|
||||
errorContainer.height = loginErrorMessageTextMetrics.height;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: loginDialog
|
||||
onHandleCreateCompleted: {
|
||||
console.log("Create Succeeded")
|
||||
|
||||
console.log("Create Succeeded");
|
||||
if (completeProfileBody.withSteam) {
|
||||
if (completeProfileBody.loginDialogPoppedUp) {
|
||||
var data = {
|
||||
|
@ -277,20 +503,24 @@ Item {
|
|||
}
|
||||
loginDialog.loginThroughSteam();
|
||||
}
|
||||
bodyLoader.setSource("LoggingInBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": completeProfileBody.withSteam, "linkSteam": false });
|
||||
bodyLoader.setSource("LoggingInBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": completeProfileBody.withSteam, "linkSteam": false,
|
||||
"withOculus": completeProfileBody.withOculus, "linkOculus": false });
|
||||
}
|
||||
onHandleCreateFailed: {
|
||||
console.log("Create Failed: " + error);
|
||||
if (completeProfileBody.withSteam) {
|
||||
if (completeProfileBody.withSteam || completeProfileBody.withOculus) {
|
||||
if (completeProfileBody.loginDialogPoppedUp) {
|
||||
action = completeProfileBody.withSteam ? "Steam" : "Oculus";
|
||||
var data = {
|
||||
"action": "user failed to create a profile with Steam from the complete profile screen"
|
||||
"action": "user failed to create a profile with " + action + " from the complete profile screen"
|
||||
}
|
||||
UserActivityLogger.logAction("encourageLoginDialog", data);
|
||||
}
|
||||
}
|
||||
|
||||
bodyLoader.setSource("UsernameCollisionBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": completeProfileBody.withSteam });
|
||||
if (!completeProfileBody.withOculus) {
|
||||
bodyLoader.setSource("UsernameCollisionBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": completeProfileBody.withSteam,
|
||||
"withOculus": completeProfileBody.withOculus });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -302,5 +532,6 @@ Item {
|
|||
}
|
||||
d.resize();
|
||||
root.text = "";
|
||||
usernameField.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,9 +36,10 @@ Item {
|
|||
property bool keyboardRaised: false
|
||||
property bool punctuationMode: false
|
||||
|
||||
property bool withSteam: false
|
||||
property bool withSteam: withSteam
|
||||
property bool linkSteam: linkSteam
|
||||
property bool withOculus: false
|
||||
property bool withOculus: withOculus
|
||||
property bool linkOculus: linkOculus
|
||||
property string errorString: errorString
|
||||
property bool lostFocus: false
|
||||
|
||||
|
@ -83,23 +84,24 @@ Item {
|
|||
}
|
||||
UserActivityLogger.logAction("encourageLoginDialog", data);
|
||||
}
|
||||
|
||||
bodyLoader.setSource("LoggingInBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": linkAccountBody.withSteam, "withOculus": linkAccountBody.withOculus, "linkSteam": linkAccountBody.linkSteam });
|
||||
bodyLoader.setSource("LoggingInBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": linkAccountBody.withSteam,
|
||||
"withOculus": linkAccountBody.withOculus, "linkSteam": linkAccountBody.linkSteam, "linkOculus": linkAccountBody.linkOculus });
|
||||
}
|
||||
|
||||
function init() {
|
||||
// going to/from sign in/up dialog.
|
||||
loginDialog.isLogIn = true;
|
||||
loginErrorMessage.text = linkAccountBody.errorString;
|
||||
loginErrorMessage.visible = (linkAccountBody.errorString !== "");
|
||||
loginButton.text = !linkAccountBody.linkSteam ? "Log In" : "Link Account";
|
||||
if (loginErrorMessageTextMetrics.width > emailField.width) {
|
||||
loginErrorMessage.wrapMode = Text.WordWrap;
|
||||
errorContainer.height = (loginErrorMessageTextMetrics.width / emailField.width) * loginErrorMessageTextMetrics.height;
|
||||
}
|
||||
loginButton.text = (!linkAccountBody.linkSteam && !linkAccountBody.linkOculus) ? "Log In" : "Link Account";
|
||||
loginButton.color = hifi.buttons.blue;
|
||||
emailField.placeholderText = "Username or Email";
|
||||
var savedUsername = Settings.getValue("keepMeLoggedIn/savedUsername", "");
|
||||
emailField.text = keepMeLoggedInCheckbox.checked ? savedUsername === "Unknown user" ? "" : savedUsername : "";
|
||||
if (linkAccountBody.linkSteam) {
|
||||
steamInfoText.anchors.top = passwordField.bottom;
|
||||
keepMeLoggedInCheckbox.anchors.top = steamInfoText.bottom;
|
||||
if (linkAccountBody.linkSteam || linkAccountBody.linkOculus) {
|
||||
loginButton.width = (passwordField.width - hifi.dimensions.contentSpacing.x) / 2;
|
||||
loginButton.anchors.right = emailField.right;
|
||||
} else {
|
||||
|
@ -125,7 +127,7 @@ Item {
|
|||
id: loginContainer
|
||||
width: emailField.width
|
||||
height: errorContainer.height + emailField.height + passwordField.height + 5.5 * hifi.dimensions.contentSpacing.y +
|
||||
keepMeLoggedInCheckbox.height + loginButton.height + cantAccessTextMetrics.height + continueButton.height + steamInfoTextMetrics.height
|
||||
keepMeLoggedInCheckbox.height + loginButton.height + cantAccessTextMetrics.height + continueButton.height
|
||||
anchors {
|
||||
top: parent.top
|
||||
topMargin: root.bannerHeight + 0.25 * parent.height
|
||||
|
@ -135,7 +137,7 @@ Item {
|
|||
|
||||
Item {
|
||||
id: errorContainer
|
||||
width: loginErrorMessageTextMetrics.width
|
||||
width: parent.width
|
||||
height: loginErrorMessageTextMetrics.height
|
||||
anchors {
|
||||
bottom: emailField.top;
|
||||
|
@ -304,7 +306,7 @@ Item {
|
|||
fontSize: linkAccountBody.fontSize
|
||||
fontBold: linkAccountBody.fontBold
|
||||
color: hifi.buttons.noneBorderlessWhite;
|
||||
visible: linkAccountBody.linkSteam
|
||||
visible: linkAccountBody.linkSteam || linkAccountBody.linkOculus
|
||||
anchors {
|
||||
top: keepMeLoggedInCheckbox.bottom
|
||||
topMargin: hifi.dimensions.contentSpacing.y
|
||||
|
@ -315,10 +317,9 @@ Item {
|
|||
"action": "user clicked cancel at link account screen"
|
||||
};
|
||||
UserActivityLogger.logAction("encourageLoginDialog", data);
|
||||
loginDialog.dismissLoginDialog();
|
||||
}
|
||||
|
||||
bodyLoader.setSource("CompleteProfileBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": linkAccountBody.withSteam, "errorString": "" });
|
||||
bodyLoader.setSource("CompleteProfileBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": linkAccountBody.withSteam,
|
||||
"withOculus": linkAccountBody.withOculus, "errorString": "" });
|
||||
}
|
||||
}
|
||||
HifiControlsUit.Button {
|
||||
|
@ -337,33 +338,6 @@ Item {
|
|||
linkAccountBody.login();
|
||||
}
|
||||
}
|
||||
TextMetrics {
|
||||
id: steamInfoTextMetrics
|
||||
font: steamInfoText.font
|
||||
text: steamInfoText.text
|
||||
}
|
||||
Text {
|
||||
id: steamInfoText
|
||||
width: root.bannerWidth
|
||||
visible: linkAccountBody.linkSteam
|
||||
anchors {
|
||||
top: loginButton.bottom
|
||||
topMargin: hifi.dimensions.contentSpacing.y
|
||||
left: emailField.left
|
||||
}
|
||||
|
||||
font.family: linkAccountBody.fontFamily
|
||||
font.pixelSize: linkAccountBody.textFieldFontSize
|
||||
color: "white"
|
||||
text: qsTr("Your Steam account information will not be exposed to others.");
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
Component.onCompleted: {
|
||||
if (steamInfoTextMetrics.width > root.bannerWidth) {
|
||||
steamInfoText.wrapMode = Text.WordWrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
TextMetrics {
|
||||
id: cantAccessTextMetrics
|
||||
font: cantAccessText.font
|
||||
|
@ -372,7 +346,7 @@ Item {
|
|||
HifiStylesUit.ShortcutText {
|
||||
id: cantAccessText
|
||||
z: 10
|
||||
visible: !linkAccountBody.linkSteam
|
||||
visible: !linkAccountBody.linkSteam && !linkAccountBody.linkOculus
|
||||
anchors {
|
||||
top: loginButton.bottom
|
||||
topMargin: hifi.dimensions.contentSpacing.y
|
||||
|
@ -423,10 +397,10 @@ Item {
|
|||
buttonGlyphSize: 24
|
||||
buttonGlyphRightMargin: 10
|
||||
onClicked: {
|
||||
// if (loginDialog.isOculusStoreRunning()) {
|
||||
// linkAccountBody.withOculus = true;
|
||||
// loginDialog.loginThroughSteam();
|
||||
// } else
|
||||
if (loginDialog.isOculusRunning()) {
|
||||
linkAccountBody.withOculus = true;
|
||||
loginDialog.loginThroughOculus();
|
||||
} else
|
||||
if (loginDialog.isSteamRunning()) {
|
||||
linkAccountBody.withSteam = true;
|
||||
loginDialog.loginThroughSteam();
|
||||
|
@ -446,18 +420,17 @@ Item {
|
|||
}
|
||||
|
||||
bodyLoader.setSource("LoggingInBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader,
|
||||
"withSteam": linkAccountBody.withSteam, "withOculus": linkAccountBody.withOculus, "linkSteam": linkAccountBody.linkSteam });
|
||||
"withSteam": linkAccountBody.withSteam, "withOculus": linkAccountBody.withOculus, "linkSteam": linkAccountBody.linkSteam, "linkOculus": linkAccountBody.linkOculus });
|
||||
}
|
||||
Component.onCompleted: {
|
||||
if (linkAccountBody.linkSteam) {
|
||||
if (linkAccountBody.linkSteam || linkAccountBody.linkOculus) {
|
||||
continueButton.visible = false;
|
||||
return;
|
||||
}
|
||||
// if (loginDialog.isOculusStoreRunning()) {
|
||||
// continueButton.text = qsTr("CONTINUE WITH OCULUS");
|
||||
// continueButton.buttonGlyph = hifi.glyphs.oculus;
|
||||
// } else
|
||||
if (loginDialog.isSteamRunning()) {
|
||||
if (loginDialog.isOculusRunning()) {
|
||||
continueButton.text = qsTr("CONTINUE WITH OCULUS");
|
||||
continueButton.buttonGlyph = hifi.glyphs.oculus;
|
||||
} else if (loginDialog.isSteamRunning()) {
|
||||
continueButton.text = qsTr("CONTINUE WITH STEAM");
|
||||
continueButton.buttonGlyph = hifi.glyphs.steamSquare;
|
||||
} else {
|
||||
|
@ -470,7 +443,7 @@ Item {
|
|||
id: signUpContainer
|
||||
width: loginContainer.width
|
||||
height: signUpTextMetrics.height
|
||||
visible: !linkAccountBody.linkSteam
|
||||
visible: !linkAccountBody.linkSteam && !linkAccountBody.linkOculus
|
||||
anchors {
|
||||
left: loginContainer.left
|
||||
top: loginContainer.bottom
|
||||
|
@ -519,7 +492,7 @@ Item {
|
|||
UserActivityLogger.logAction("encourageLoginDialog", data);
|
||||
}
|
||||
bodyLoader.setSource("SignUpBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader,
|
||||
"errorString": "", "linkSteam": linkAccountBody.linkSteam });
|
||||
"errorString": "" });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -543,7 +516,7 @@ Item {
|
|||
fontFamily: linkAccountBody.fontFamily
|
||||
fontSize: linkAccountBody.fontSize
|
||||
fontBold: linkAccountBody.fontBold
|
||||
visible: linkAccountBody.loginDialogPoppedUp && !linkAccountBody.linkSteam;
|
||||
visible: loginDialog.getLoginDialogPoppedUp() && !linkAccountBody.linkSteam && !linkAccountBody.linkOculus;
|
||||
onClicked: {
|
||||
if (linkAccountBody.loginDialogPoppedUp) {
|
||||
var data = {
|
||||
|
|
|
@ -29,6 +29,8 @@ Item {
|
|||
property bool withSteam: withSteam
|
||||
property bool withOculus: withOculus
|
||||
property bool linkSteam: linkSteam
|
||||
property bool linkOculus: linkOculus
|
||||
property bool createOculus: createOculus
|
||||
|
||||
readonly property bool loginDialogPoppedUp: loginDialog.getLoginDialogPoppedUp()
|
||||
|
||||
|
@ -75,15 +77,25 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: oculusSuccessTimer
|
||||
interval: 500;
|
||||
running: false;
|
||||
repeat: false;
|
||||
onTriggered: {
|
||||
loginDialog.loginThroughOculus();
|
||||
init();
|
||||
}
|
||||
}
|
||||
|
||||
function init() {
|
||||
// For the process of logging in.
|
||||
loggingInText.wrapMode = Text.NoWrap;
|
||||
|
||||
if (loggingInBody.linkSteam) {
|
||||
if (loggingInBody.createOculus) {
|
||||
loggingInGlyph.text = hifi.glyphs.oculus;
|
||||
loggingInGlyph.visible = true;
|
||||
loggingInText.text = "Linking to Steam";
|
||||
loggingInText.text = "Creating account with Oculus";
|
||||
loggingInText.x = loggingInHeader.width/2 - loggingInTextMetrics.width/2 + loggingInGlyphTextMetrics.width/2;
|
||||
loginDialog.linkSteam();
|
||||
} else if (loggingInBody.withSteam) {
|
||||
loggingInGlyph.visible = true;
|
||||
loggingInText.text = "Logging in to Steam";
|
||||
|
@ -100,12 +112,18 @@ Item {
|
|||
loggingInSpinner.visible = true;
|
||||
}
|
||||
function loadingSuccess() {
|
||||
loggingInSpinner.visible = false;
|
||||
if (loggingInBody.linkSteam) {
|
||||
loggingInText.text = "Linking to Steam";
|
||||
loggingInText.x = loggingInHeader.width/2 - loggingInTextMetrics.width/2 + loggingInGlyphTextMetrics.width/2;
|
||||
loginDialog.linkSteam();
|
||||
return;
|
||||
} else if (loggingInBody.linkOculus) {
|
||||
loggingInText.text = "Linking to Oculus";
|
||||
loggingInText.x = loggingInHeader.width/2 - loggingInTextMetrics.width/2 + loggingInGlyphTextMetrics.width/2;
|
||||
loginDialog.linkOculus();
|
||||
return;
|
||||
}
|
||||
loggingInSpinner.visible = false;
|
||||
if (loggingInBody.withSteam) {
|
||||
// reset the flag.
|
||||
loggingInGlyph.visible = false;
|
||||
|
@ -246,6 +264,26 @@ Item {
|
|||
verticalAlignment: Text.AlignVCenter;
|
||||
visible: false;
|
||||
}
|
||||
HifiControlsUit.Button {
|
||||
id: okButton;
|
||||
width: d.minWidthButton
|
||||
height: d.minHeightButton
|
||||
text: qsTr("OK")
|
||||
color: hifi.buttons.white
|
||||
anchors {
|
||||
top: loggedInGlyph.bottom
|
||||
topMargin: 3 * hifi.dimensions.contentSpacing.y
|
||||
left: parent.left
|
||||
leftMargin: (parent.width - width) / 2;
|
||||
}
|
||||
onClicked: {
|
||||
root.tryDestroy();
|
||||
if (loginDialog.getLoginDialogPoppedUp()) {
|
||||
loginDialog.dismissLoginDialog();
|
||||
}
|
||||
}
|
||||
visible: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -257,6 +295,34 @@ Item {
|
|||
|
||||
Connections {
|
||||
target: loginDialog
|
||||
onHandleCreateCompleted: {
|
||||
console.log("Create Succeeded")
|
||||
if (loggingInBody.withOculus) {
|
||||
if (loggingInBody.loginDialogPoppedUp) {
|
||||
var data = {
|
||||
"action": "user created Oculus account successfully"
|
||||
};
|
||||
UserActivityLogger.logAction("encourageLoginDialog", data);
|
||||
}
|
||||
loggingInBody.createOculus = false;
|
||||
loggingInText.text = "Account created!";
|
||||
loggingInText.x = loggingInHeader.width/2 - loggingInTextMetrics.width/2 + loggingInGlyphTextMetrics.width/2;
|
||||
oculusSuccessTimer.start();
|
||||
}
|
||||
}
|
||||
onHandleCreateFailed: {
|
||||
console.log("Create Failed: " + error);
|
||||
if (loggingInBody.withOculus) {
|
||||
if (loggingInBody.loginDialogPoppedUp) {
|
||||
var data = {
|
||||
"action": "user created Oculus account unsuccessfully"
|
||||
};
|
||||
UserActivityLogger.logAction("encourageLoginDialog", data);
|
||||
}
|
||||
bodyLoader.setSource("CompleteProfileBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": loggingInBody.withSteam,
|
||||
"withOculus": loggingInBody.withOculus, "errorString": error });
|
||||
}
|
||||
}
|
||||
onHandleLinkCompleted: {
|
||||
console.log("Link Succeeded");
|
||||
if (loggingInBody.linkSteam) {
|
||||
|
@ -267,21 +333,40 @@ Item {
|
|||
};
|
||||
UserActivityLogger.logAction("encourageLoginDialog", data);
|
||||
}
|
||||
|
||||
loggingInBody.loadingSuccess();
|
||||
} else if (loggingInBody.linkOculus) {
|
||||
loggingInBody.linkOculus = false;
|
||||
if (loggingInBody.loginDialogPoppedUp) {
|
||||
var data = {
|
||||
"action": "user linked Oculus with their hifi account credentials successfully"
|
||||
};
|
||||
UserActivityLogger.logAction("encourageLoginDialog", data);
|
||||
}
|
||||
}
|
||||
loggingInBody.loadingSuccess();
|
||||
}
|
||||
onHandleLinkFailed: {
|
||||
console.log("Link Failed: " + error);
|
||||
if (loggingInBody.linkSteam) {
|
||||
loggingInSpinner.visible = false;
|
||||
if (loggingInBody.linkOculus) {
|
||||
loggingInText.text = "Oculus failed to link";
|
||||
if (loggingInBody.loginDialogPoppedUp) {
|
||||
var data = {
|
||||
"action": "user linked Oculus unsuccessfully"
|
||||
};
|
||||
UserActivityLogger.logAction("encourageLoginDialog", data);
|
||||
}
|
||||
okButton.visible = true;
|
||||
} else if (loggingInBody.linkSteam){
|
||||
if (loggingInBody.loginDialogPoppedUp) {
|
||||
var data = {
|
||||
"action": "user linked Steam unsuccessfully"
|
||||
};
|
||||
UserActivityLogger.logAction("encourageLoginDialog", data);
|
||||
}
|
||||
} else {
|
||||
bodyLoader.setSource("LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "linkSteam": loggingInBody.linkSteam,
|
||||
"linkOculus": loggingInBody.linkOculus, "errorString": error });
|
||||
}
|
||||
bodyLoader.setSource("LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "linkSteam": true, "errorString": error });
|
||||
}
|
||||
|
||||
onHandleLoginCompleted: {
|
||||
|
@ -292,8 +377,19 @@ Item {
|
|||
onHandleLoginFailed: {
|
||||
console.log("Login Failed")
|
||||
loggingInSpinner.visible = false;
|
||||
loggingInGlyph.visible = false;
|
||||
var errorString = "";
|
||||
if (loggingInBody.linkSteam && loggingInBody.withSteam) {
|
||||
if (loggingInBody.linkOculus && loggingInBody.withOculus) {
|
||||
errorString = "Username or password is incorrect.";
|
||||
if (loggingInBody.loginDialogPoppedUp) {
|
||||
var data = {
|
||||
"action": "user failed to link Oculus with their hifi account credentials"
|
||||
};
|
||||
UserActivityLogger.logAction("encourageLoginDialog", data);
|
||||
}
|
||||
bodyLoader.setSource("LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": loggingInBody.withSteam,
|
||||
"withOculus": loggingInBody.withOculus, "linkSteam": loggingInBody.linkSteam, "linkOculus": loggingInBody.linkOculus, "errorString": errorString });
|
||||
} else if (loggingInBody.linkSteam && loggingInBody.withSteam) {
|
||||
errorString = "Username or password is incorrect.";
|
||||
if (loggingInBody.loginDialogPoppedUp) {
|
||||
var data = {
|
||||
|
@ -301,9 +397,9 @@ Item {
|
|||
};
|
||||
UserActivityLogger.logAction("encourageLoginDialog", data);
|
||||
}
|
||||
bodyLoader.setSource("LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": loggingInBody.withSteam, "linkSteam": loggingInBody.linkSteam, "errorString": errorString });
|
||||
bodyLoader.setSource("LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": loggingInBody.withSteam,
|
||||
"withOculus": loggingInBody.withOculus, "linkSteam": loggingInBody.linkSteam, "linkOculus": loggingInBody.linkOculus, "errorString": errorString });
|
||||
} else if (loggingInBody.withSteam) {
|
||||
loggingInGlyph.visible = false;
|
||||
errorString = "Your Steam authentication has failed. Please make sure you are logged into Steam and try again.";
|
||||
if (loggingInBody.loginDialogPoppedUp) {
|
||||
var data = {
|
||||
|
@ -311,19 +407,19 @@ Item {
|
|||
};
|
||||
UserActivityLogger.logAction("encourageLoginDialog", data);
|
||||
}
|
||||
bodyLoader.setSource("CompleteProfileBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": loggingInBody.withSteam, "errorString": errorString });
|
||||
bodyLoader.setSource("CompleteProfileBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": loggingInBody.withSteam,
|
||||
"withOculus": loggingInBody.withOculus, "linkSteam": loggingInBody.linkSteam, "linkOculus": loggingInBody.linkOculus, "errorString": errorString });
|
||||
} else if (loggingInBody.withOculus) {
|
||||
loggingInGlyph.visible = false;
|
||||
errorString = "Your Oculus authentication has failed. Please make sure you are logged into Oculus and try again."
|
||||
errorString = "Your Oculus account is not connected to an existing High Fidelity account. Please create a new one."
|
||||
if (loggingInBody.loginDialogPoppedUp) {
|
||||
var data = {
|
||||
"action": "user failed to authenticate with Oculus to log in"
|
||||
};
|
||||
UserActivityLogger.logAction("encourageLoginDialog", data);
|
||||
}
|
||||
bodyLoader.setSource("LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "errorString": errorString });
|
||||
}
|
||||
else {
|
||||
bodyLoader.setSource("CompleteProfileBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": loggingInBody.withSteam,
|
||||
"withOculus": loggingInBody.withOculus, "linkSteam": loggingInBody.linkSteam, "linkOculus": loggingInBody.linkOculus, "errorString": errorString });
|
||||
} else {
|
||||
errorString = "Username or password is incorrect.";
|
||||
if (loggingInBody.loginDialogPoppedUp) {
|
||||
var data = {
|
||||
|
|
|
@ -23,6 +23,7 @@ Item {
|
|||
clip: true
|
||||
height: root.height
|
||||
width: root.width
|
||||
readonly property string termsContainerText: qsTr("By signing up, you agree to High Fidelity's Terms of Service")
|
||||
property int textFieldHeight: 31
|
||||
property string fontFamily: "Raleway"
|
||||
property int fontSize: 15
|
||||
|
@ -37,7 +38,6 @@ Item {
|
|||
onKeyboardRaisedChanged: d.resize();
|
||||
|
||||
property string errorString: errorString
|
||||
property bool linkSteam: linkSteam
|
||||
property bool lostFocus: false
|
||||
|
||||
readonly property bool loginDialogPoppedUp: loginDialog.getLoginDialogPoppedUp()
|
||||
|
@ -73,7 +73,6 @@ Item {
|
|||
|
||||
function init() {
|
||||
// going to/from sign in/up dialog.
|
||||
loginDialog.isLogIn = false;
|
||||
emailField.placeholderText = "Email";
|
||||
emailField.text = "";
|
||||
emailField.anchors.top = usernameField.bottom;
|
||||
|
@ -353,7 +352,7 @@ Item {
|
|||
}
|
||||
UserActivityLogger.logAction("encourageLoginDialog", data);
|
||||
}
|
||||
bodyLoader.setSource("LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "linkSteam": signUpBody.linkSteam });
|
||||
bodyLoader.setSource("LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "linkSteam": false });
|
||||
}
|
||||
}
|
||||
HifiControlsUit.Button {
|
||||
|
@ -380,6 +379,54 @@ Item {
|
|||
signUpBody.signup();
|
||||
}
|
||||
}
|
||||
Item {
|
||||
id: termsContainer
|
||||
width: parent.width
|
||||
height: termsTextMetrics.height
|
||||
anchors {
|
||||
top: signUpButton.bottom
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
topMargin: 2 * hifi.dimensions.contentSpacing.y
|
||||
left: parent.left
|
||||
}
|
||||
TextMetrics {
|
||||
id: termsTextMetrics
|
||||
font: termsText.font
|
||||
text: signUpBody.termsContainerText
|
||||
Component.onCompleted: {
|
||||
// with the link.
|
||||
termsText.text = qsTr("By signing up, you agree to <a href='https://highfidelity.com/terms'>High Fidelity's Terms of Service</a>")
|
||||
}
|
||||
}
|
||||
|
||||
HifiStylesUit.InfoItem {
|
||||
id: termsText
|
||||
text: signUpBody.termsContainerText
|
||||
font.family: signUpBody.fontFamily
|
||||
font.pixelSize: signUpBody.fontSize
|
||||
font.bold: signUpBody.fontBold
|
||||
wrapMode: Text.WordWrap
|
||||
color: hifi.colors.white
|
||||
linkColor: hifi.colors.blueAccent
|
||||
lineHeight: 1
|
||||
lineHeightMode: Text.ProportionalHeight
|
||||
|
||||
onLinkActivated: loginDialog.openUrl(link);
|
||||
|
||||
Component.onCompleted: {
|
||||
if (termsTextMetrics.width > root.bannerWidth) {
|
||||
termsText.width = root.bannerWidth;
|
||||
termsText.wrapMode = Text.WordWrap;
|
||||
additionalText.verticalAlignment = Text.AlignLeft;
|
||||
additionalText.horizontalAlignment = Text.AlignLeft;
|
||||
termsContainer.height = (termsTextMetrics.width / root.bannerWidth) * termsTextMetrics.height;
|
||||
termsContainer.anchors.left = buttons.left;
|
||||
} else {
|
||||
termsText.anchors.centerIn = termsContainer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -433,14 +480,15 @@ Item {
|
|||
|
||||
if (errorString !== "") {
|
||||
loginErrorMessage.visible = true;
|
||||
var errorLength = errorString.split(/\r\n|\r|\n/).length;
|
||||
var errorStringEdited = errorString.replace(/[\n\r]+/g, "\n");
|
||||
loginErrorMessage.text = errorStringEdited;
|
||||
loginErrorMessageTextMetrics.text = errorString;
|
||||
if (loginErrorMessageTextMetrics.width > usernameField.width) {
|
||||
if (errorLength > 1.0) {
|
||||
loginErrorMessage.width = root.bannerWidth;
|
||||
loginErrorMessage.wrapMode = Text.WordWrap;
|
||||
loginErrorMessage.verticalAlignment = Text.AlignLeft;
|
||||
loginErrorMessage.horizontalAlignment = Text.AlignLeft;
|
||||
errorContainer.height = (loginErrorMessageTextMetrics.width / usernameField.width) * loginErrorMessageTextMetrics.height;
|
||||
errorContainer.height = errorLength * loginErrorMessageTextMetrics.height;
|
||||
}
|
||||
errorContainer.anchors.bottom = usernameField.top;
|
||||
errorContainer.anchors.bottomMargin = hifi.dimensions.contentSpacing.y;
|
||||
|
|
|
@ -19,6 +19,7 @@ import TabletScriptingInterface 1.0
|
|||
Item {
|
||||
id: usernameCollisionBody
|
||||
clip: true
|
||||
readonly property string termsContainerText: qsTr("By creating this user profile, you agree to High Fidelity's Terms of Service")
|
||||
width: root.width
|
||||
height: root.height
|
||||
readonly property string fontFamily: "Raleway"
|
||||
|
@ -26,13 +27,18 @@ Item {
|
|||
readonly property int textFieldFontSize: 18
|
||||
readonly property bool fontBold: true
|
||||
|
||||
readonly property bool withSteam: withSteam
|
||||
property bool withSteam: withSteam
|
||||
property bool withOculus: withOculus
|
||||
|
||||
readonly property bool loginDialogPoppedUp: loginDialog.getLoginDialogPoppedUp()
|
||||
|
||||
function create() {
|
||||
mainTextContainer.visible = false
|
||||
loginDialog.createAccountFromSteam(textField.text);
|
||||
if (usernameCollisionBody.withOculus) {
|
||||
loginDialog.createAccountFromOculus(textField.text);
|
||||
} else if (usernameCollisionBody.withSteam) {
|
||||
loginDialog.createAccountFromSteam(textField.text);
|
||||
}
|
||||
}
|
||||
|
||||
property bool keyboardEnabled: false
|
||||
|
@ -90,12 +96,19 @@ Item {
|
|||
font.family: usernameCollisionBody.fontFamily
|
||||
font.pixelSize: usernameCollisionBody.fontSize
|
||||
font.bold: usernameCollisionBody.fontBold
|
||||
text: qsTr("Your Steam username is not available.");
|
||||
text: qsTr("");
|
||||
wrapMode: Text.WordWrap
|
||||
color: hifi.colors.redAccent
|
||||
lineHeight: 1
|
||||
lineHeightMode: Text.ProportionalHeight
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
Component.onCompleted: {
|
||||
if (usernameCollisionBody.withOculus) {
|
||||
text = qsTr("Your Oculus username is not available.");
|
||||
} else if (usernameCollisionBody.withSteam) {
|
||||
text = qsTr("Your Steam username is not available.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -164,7 +177,8 @@ Item {
|
|||
fontSize: usernameCollisionBody.fontSize
|
||||
fontBold: usernameCollisionBody.fontBold
|
||||
onClicked: {
|
||||
bodyLoader.setSource("CompleteProfileBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "errorString": "" });
|
||||
bodyLoader.setSource("CompleteProfileBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": usernameCollisionBody.withSteam,
|
||||
"withOculus": usernameCollisionBody.withOculus, "errorString": "" });
|
||||
}
|
||||
}
|
||||
HifiControlsUit.Button {
|
||||
|
@ -187,6 +201,55 @@ Item {
|
|||
}
|
||||
}
|
||||
}
|
||||
Item {
|
||||
id: termsContainer
|
||||
width: parent.width
|
||||
height: termsTextMetrics.height
|
||||
anchors {
|
||||
top: buttons.bottom
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
topMargin: 2 * hifi.dimensions.contentSpacing.y
|
||||
left: parent.left
|
||||
leftMargin: (parent.width - buttons.width) / 2
|
||||
}
|
||||
TextMetrics {
|
||||
id: termsTextMetrics
|
||||
font: termsText.font
|
||||
text: usernameCollisionBody.termsContainerText
|
||||
Component.onCompleted: {
|
||||
// with the link.
|
||||
termsText.text = qsTr("By creating this user profile, you agree to <a href='https://highfidelity.com/terms'>High Fidelity's Terms of Service</a>")
|
||||
}
|
||||
}
|
||||
|
||||
HifiStylesUit.InfoItem {
|
||||
id: termsText
|
||||
text: usernameCollisionBody.termsContainerText
|
||||
font.family: usernameCollisionBody.fontFamily
|
||||
font.pixelSize: usernameCollisionBody.fontSize
|
||||
font.bold: usernameCollisionBody.fontBold
|
||||
wrapMode: Text.WordWrap
|
||||
color: hifi.colors.white
|
||||
linkColor: hifi.colors.blueAccent
|
||||
lineHeight: 1
|
||||
lineHeightMode: Text.ProportionalHeight
|
||||
|
||||
onLinkActivated: loginDialog.openUrl(link);
|
||||
|
||||
Component.onCompleted: {
|
||||
if (termsTextMetrics.width > root.bannerWidth) {
|
||||
termsText.width = root.bannerWidth;
|
||||
termsText.wrapMode = Text.WordWrap;
|
||||
additionalText.verticalAlignment = Text.AlignLeft;
|
||||
additionalText.horizontalAlignment = Text.AlignLeft;
|
||||
termsContainer.height = (termsTextMetrics.width / root.bannerWidth) * termsTextMetrics.height;
|
||||
termsContainer.anchors.left = buttons.left;
|
||||
} else {
|
||||
termsText.anchors.centerIn = termsContainer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
|
@ -201,18 +264,25 @@ Item {
|
|||
target: loginDialog
|
||||
onHandleCreateCompleted: {
|
||||
console.log("Create Succeeded");
|
||||
if (usernameCollisionBody.withSteam) {
|
||||
if (usernameCollisionBody.withOculus) {
|
||||
if (usernameCollisionBody.loginDialogPoppedUp) {
|
||||
var data = {
|
||||
"action": "user created a profile with Oculus successfully in the username collision screen"
|
||||
}
|
||||
UserActivityLogger.logAction("encourageLoginDialog", data);
|
||||
}
|
||||
loginDialog.loginThroughOculus();
|
||||
} else if (usernameCollisionBody.withSteam) {
|
||||
if (usernameCollisionBody.loginDialogPoppedUp) {
|
||||
var data = {
|
||||
"action": "user created a profile with Steam successfully in the username collision screen"
|
||||
}
|
||||
UserActivityLogger.logAction("encourageLoginDialog", data);
|
||||
}
|
||||
|
||||
loginDialog.loginThroughSteam();
|
||||
}
|
||||
|
||||
bodyLoader.setSource("LoggingInBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": usernameCollisionBody.withSteam, "linkSteam": false })
|
||||
bodyLoader.setSource("LoggingInBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": usernameCollisionBody.withSteam,
|
||||
"withOculus": usernameCollisionBody.withOculus, "linkSteam": false, "linkOculus": false })
|
||||
}
|
||||
onHandleCreateFailed: {
|
||||
console.log("Create Failed: " + error)
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 960 KiB |
BIN
interface/resources/qml/LoginDialog/images/background.png
Normal file
BIN
interface/resources/qml/LoginDialog/images/background.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.2 MiB |
Binary file not shown.
Before Width: | Height: | Size: 272 KiB |
BIN
interface/resources/qml/LoginDialog/images/background_tablet.png
Normal file
BIN
interface/resources/qml/LoginDialog/images/background_tablet.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 456 KiB |
|
@ -55,7 +55,8 @@ FocusScope {
|
|||
Image {
|
||||
z: -10
|
||||
id: loginDialogBackground
|
||||
source: "LoginDialog/images/background.jpg"
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
source: "LoginDialog/images/background.png"
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
|
@ -149,6 +150,6 @@ FocusScope {
|
|||
|
||||
Component.onCompleted: {
|
||||
keyboardTimer.start();
|
||||
bodyLoader.setSource("LoginDialog/LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "linkSteam": false });
|
||||
bodyLoader.setSource("LoginDialog/LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "linkSteam": false, "linkOculus": false });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
//
|
||||
// Web3DOverlay.qml
|
||||
//
|
||||
// Created by Gabriel Calero & Cristian Duarte on Jun 22, 2018
|
||||
// Copyright 2016 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.5
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
Item {
|
||||
|
||||
property string url
|
||||
RadialGradient {
|
||||
anchors.fill: parent
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 0.0; color: "#262626" }
|
||||
GradientStop { position: 1.0; color: "#000000" }
|
||||
}
|
||||
}
|
||||
|
||||
function shortUrl(url) {
|
||||
var hostBegin = url.indexOf("://");
|
||||
if (hostBegin > -1) {
|
||||
url = url.substring(hostBegin + 3);
|
||||
}
|
||||
|
||||
var portBegin = url.indexOf(":");
|
||||
if (portBegin > -1) {
|
||||
url = url.substring(0, portBegin);
|
||||
}
|
||||
|
||||
var pathBegin = url.indexOf("/");
|
||||
if (pathBegin > -1) {
|
||||
url = url.substring(0, pathBegin);
|
||||
}
|
||||
|
||||
if (url.length > 45) {
|
||||
url = url.substring(0, 45);
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
Text {
|
||||
id: urlText
|
||||
text: shortUrl(url)
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
anchors.fill: parent
|
||||
anchors.rightMargin: 10
|
||||
anchors.leftMargin: 10
|
||||
font.family: "Cairo"
|
||||
font.weight: Font.DemiBold
|
||||
font.pointSize: 48
|
||||
fontSizeMode: Text.Fit
|
||||
color: "#FFFFFF"
|
||||
minimumPixelSize: 5
|
||||
}
|
||||
|
||||
Image {
|
||||
id: hand
|
||||
source: "../../../icons/hand.svg"
|
||||
width: 300
|
||||
height: 300
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.bottomMargin: 100
|
||||
anchors.rightMargin: 100
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
//
|
||||
// WebEntityView.qml
|
||||
//
|
||||
// Created by Kunal Gosar on 16 March 2017
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
import "."
|
||||
|
||||
WebView {
|
||||
viewProfile: FileTypeProfile;
|
||||
|
||||
urlTag: "noDownload=true";
|
||||
}
|
|
@ -58,7 +58,7 @@ Item {
|
|||
keyItem.state = "mouseOver";
|
||||
|
||||
var globalPosition = keyItem.mapToGlobal(mouseArea1.mouseX, mouseArea1.mouseY);
|
||||
var pointerID = Web3DOverlay.deviceIdByTouchPoint(globalPosition.x, globalPosition.y);
|
||||
var pointerID = QmlSurface.deviceIdByTouchPoint(globalPosition.x, globalPosition.y);
|
||||
|
||||
if (Pointers.isLeftHand(pointerID)) {
|
||||
Controller.triggerHapticPulse(_HAPTIC_STRENGTH, _HAPTIC_DURATION, leftHand);
|
||||
|
|
|
@ -57,16 +57,23 @@ SpinBox {
|
|||
|
||||
locale: Qt.locale("en_US")
|
||||
|
||||
onValueModified: realValue = value/factor
|
||||
onValueChanged: realValue = value/factor
|
||||
onValueModified: {
|
||||
realValue = value / factor
|
||||
}
|
||||
|
||||
onValueChanged: {
|
||||
realValue = value / factor
|
||||
spinBox.editingFinished();
|
||||
}
|
||||
|
||||
onRealValueChanged: {
|
||||
var newValue = Math.round(realValue*factor);
|
||||
var newValue = Math.round(realValue * factor);
|
||||
if(value != newValue) {
|
||||
value = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
stepSize: realStepSize*factor
|
||||
stepSize: realStepSize * factor
|
||||
to : realTo*factor
|
||||
from : realFrom*factor
|
||||
|
||||
|
@ -90,11 +97,11 @@ SpinBox {
|
|||
}
|
||||
|
||||
textFromValue: function(value, locale) {
|
||||
return parseFloat(value/factor).toFixed(decimals);
|
||||
return parseFloat(value / factor).toFixed(decimals);
|
||||
}
|
||||
|
||||
valueFromText: function(text, locale) {
|
||||
return Number.fromLocaleString(locale, text)*factor;
|
||||
return Number.fromLocaleString(locale, text) * factor;
|
||||
}
|
||||
|
||||
|
||||
|
@ -102,7 +109,7 @@ SpinBox {
|
|||
id: spinboxText
|
||||
z: 2
|
||||
color: isLightColorScheme
|
||||
? (spinBox.activeFocus ? hifi.colors.black : hifi.colors.lightGray)
|
||||
? (spinBox.activeFocus ? hifi.colors.black : hifi.colors.faintGray)
|
||||
: (spinBox.activeFocus ? hifi.colors.white : hifi.colors.lightGrayText)
|
||||
selectedTextColor: hifi.colors.black
|
||||
selectionColor: hifi.colors.primaryHighlight
|
||||
|
@ -112,8 +119,6 @@ SpinBox {
|
|||
verticalAlignment: Qt.AlignVCenter
|
||||
leftPadding: spinBoxLabelInside.visible ? 30 : hifi.dimensions.textPadding
|
||||
width: spinBox.width - hifi.dimensions.spinnerSize
|
||||
onEditingFinished: spinBox.editingFinished()
|
||||
|
||||
Text {
|
||||
id: suffixText
|
||||
x: metrics.advanceWidth(spinboxText.text + '*')
|
||||
|
@ -125,7 +130,7 @@ SpinBox {
|
|||
}
|
||||
|
||||
color: isLightColorScheme
|
||||
? (spinBox.activeFocus ? hifi.colors.black : hifi.colors.lightGray)
|
||||
? (spinBox.activeFocus ? hifi.colors.black : hifi.colors.faintGray)
|
||||
: (spinBox.activeFocus ? hifi.colors.white : hifi.colors.lightGrayText)
|
||||
text: suffix
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
|
@ -170,6 +175,22 @@ SpinBox {
|
|||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
if (event.key === Qt.Key_Return) {
|
||||
if (!spinboxText.acceptableInput) {
|
||||
var number = spinBox.valueFromText(spinboxText.text, spinBox.locale) / spinBox.factor
|
||||
|
||||
if (number < spinBox.minimumValue) {
|
||||
number = spinBox.minimumValue;
|
||||
} else if (number > maximumValue) {
|
||||
number = spinBox.maximumValue;
|
||||
}
|
||||
|
||||
spinboxText.text = spinBox.textFromValue(Math.round(number * factor), spinBox.locale)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.Label {
|
||||
id: spinBoxLabel
|
||||
text: spinBox.label
|
||||
|
|
|
@ -14,6 +14,8 @@ import QtQuick 2.5
|
|||
import controlsUit 1.0 as HifiControlsUit
|
||||
import stylesUit 1.0 as HifiStylesUit
|
||||
|
||||
import TabletScriptingInterface 1.0
|
||||
|
||||
import "../LoginDialog"
|
||||
|
||||
FocusScope {
|
||||
|
@ -25,10 +27,9 @@ FocusScope {
|
|||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
signal sendToScript(var message);
|
||||
signal canceled();
|
||||
property var tabletProxy: Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
|
||||
property bool isHMD: false
|
||||
property bool isHMD: HMD.active
|
||||
property bool gotoPreviousApp: false;
|
||||
|
||||
property bool keyboardEnabled: false
|
||||
|
@ -52,6 +53,7 @@ FocusScope {
|
|||
}
|
||||
|
||||
function tryDestroy() {
|
||||
tabletProxy.gotoHomeScreen();
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
|
@ -76,7 +78,7 @@ FocusScope {
|
|||
interval: 200
|
||||
|
||||
onTriggered: {
|
||||
if (MenuInterface.isOptionChecked("Use 3D Keyboard")) {
|
||||
if (MenuInterface.isOptionChecked("Use 3D Keyboard") && root.isHMD) {
|
||||
KeyboardScriptingInterface.raised = true;
|
||||
}
|
||||
}
|
||||
|
@ -95,7 +97,8 @@ FocusScope {
|
|||
Image {
|
||||
z: -10
|
||||
id: loginDialogBackground
|
||||
source: "../LoginDialog/images/background_tablet.jpg"
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
source: "../LoginDialog/images/background_tablet.png"
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
|
@ -168,11 +171,13 @@ FocusScope {
|
|||
|
||||
Component.onDestruction: {
|
||||
loginKeyboard.raised = false;
|
||||
KeyboardScriptingInterface.raised = false;
|
||||
if (root.isHMD) {
|
||||
KeyboardScriptingInterface.raised = false;
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
keyboardTimer.start();
|
||||
bodyLoader.setSource("../LoginDialog/LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "linkSteam": false });
|
||||
bodyLoader.setSource("../LoginDialog/LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "linkSteam": false, "linkOculus": false });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -148,7 +148,7 @@ Windows.ScrollingWindow {
|
|||
}
|
||||
|
||||
function canAddToWorld(path) {
|
||||
var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i, /\.jpg\b/i, /\.png\b/i];
|
||||
var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i, /\.jpg\b/i, /\.png\b/i, /\.gltf\b/i];
|
||||
|
||||
if (selectedItemCount > 1) {
|
||||
return false;
|
||||
|
|
|
@ -254,6 +254,7 @@ Rectangle {
|
|||
onSaveClicked: function() {
|
||||
var avatarSettings = {
|
||||
dominantHand : settings.dominantHandIsLeft ? 'left' : 'right',
|
||||
hmdAvatarAlignmentType : settings.hmdAvatarAlignmentTypeIsEyes ? 'eyes' : 'head',
|
||||
collisionsEnabled : settings.environmentCollisionsOn,
|
||||
otherAvatarsCollisionsEnabled : settings.otherAvatarsCollisionsOn,
|
||||
animGraphOverrideUrl : settings.avatarAnimationOverrideJSON,
|
||||
|
|
|
@ -37,6 +37,7 @@ Rectangle {
|
|||
property alias dominantHandIsLeft: leftHandRadioButton.checked
|
||||
property alias otherAvatarsCollisionsOn: otherAvatarsCollisionsEnabledRadiobutton.checked
|
||||
property alias environmentCollisionsOn: environmentCollisionsEnabledRadiobutton.checked
|
||||
property alias hmdAvatarAlignmentTypeIsEyes: eyesRadioButton.checked
|
||||
property alias avatarAnimationOverrideJSON: avatarAnimationUrlInputText.text
|
||||
property alias avatarAnimationJSON: avatarAnimationUrlInputText.placeholderText
|
||||
property alias avatarCollisionSoundUrl: avatarCollisionSoundUrlInputText.text
|
||||
|
@ -65,6 +66,11 @@ Rectangle {
|
|||
} else {
|
||||
environmentCollisionsDisabledRadiobutton.checked = true;
|
||||
}
|
||||
if (settings.hmdAvatarAlignmentType === 'eyes') {
|
||||
eyesRadioButton.checked = true;
|
||||
} else {
|
||||
headRadioButton.checked = true;
|
||||
}
|
||||
|
||||
avatarAnimationJSON = settings.animGraphUrl;
|
||||
avatarAnimationOverrideJSON = settings.animGraphOverrideUrl;
|
||||
|
@ -210,7 +216,7 @@ Rectangle {
|
|||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
rows: 2
|
||||
rows: 4
|
||||
rowSpacing: 25
|
||||
|
||||
columns: 3
|
||||
|
@ -233,7 +239,7 @@ Rectangle {
|
|||
|
||||
Layout.row: 0
|
||||
Layout.column: 1
|
||||
Layout.leftMargin: -20
|
||||
Layout.leftMargin: -15
|
||||
|
||||
ButtonGroup.group: leftRight
|
||||
checked: true
|
||||
|
@ -249,7 +255,7 @@ Rectangle {
|
|||
id: rightHandRadioButton
|
||||
|
||||
Layout.row: 0
|
||||
Layout.column: 3
|
||||
Layout.column: 2
|
||||
Layout.rightMargin: -15
|
||||
|
||||
ButtonGroup.group: leftRight
|
||||
|
@ -260,7 +266,7 @@ Rectangle {
|
|||
text: "Right"
|
||||
boxSize: 20
|
||||
}
|
||||
|
||||
|
||||
HifiConstants {
|
||||
id: hifi
|
||||
}
|
||||
|
@ -272,17 +278,17 @@ Rectangle {
|
|||
Layout.column: 0
|
||||
text: "Avatar to avatar collision"
|
||||
}
|
||||
|
||||
|
||||
ButtonGroup {
|
||||
id: otherAvatarsOnOff
|
||||
}
|
||||
|
||||
|
||||
HifiControlsUit.RadioButton {
|
||||
id: otherAvatarsCollisionsEnabledRadiobutton
|
||||
|
||||
Layout.row: 1
|
||||
Layout.column: 1
|
||||
Layout.leftMargin: -20
|
||||
Layout.leftMargin: -15
|
||||
|
||||
ButtonGroup.group: otherAvatarsOnOff
|
||||
|
||||
|
@ -297,7 +303,7 @@ Rectangle {
|
|||
id: otherAvatarsCollisionsDisabledRadiobutton
|
||||
|
||||
Layout.row: 1
|
||||
Layout.column: 3
|
||||
Layout.column: 2
|
||||
Layout.rightMargin: -15
|
||||
|
||||
ButtonGroup.group: otherAvatarsOnOff
|
||||
|
@ -320,13 +326,13 @@ Rectangle {
|
|||
ButtonGroup {
|
||||
id: worldOnOff
|
||||
}
|
||||
|
||||
|
||||
HifiControlsUit.RadioButton {
|
||||
id: environmentCollisionsEnabledRadiobutton
|
||||
|
||||
Layout.row: 2
|
||||
Layout.column: 1
|
||||
Layout.leftMargin: -20
|
||||
Layout.leftMargin: -15
|
||||
|
||||
ButtonGroup.group: worldOnOff
|
||||
|
||||
|
@ -341,7 +347,7 @@ Rectangle {
|
|||
id: environmentCollisionsDisabledRadiobutton
|
||||
|
||||
Layout.row: 2
|
||||
Layout.column: 3
|
||||
Layout.column: 2
|
||||
Layout.rightMargin: -15
|
||||
|
||||
ButtonGroup.group: worldOnOff
|
||||
|
@ -352,6 +358,52 @@ Rectangle {
|
|||
text: "Off"
|
||||
boxSize: 20
|
||||
}
|
||||
|
||||
// TextStyle9
|
||||
RalewaySemiBold {
|
||||
size: 17;
|
||||
Layout.row: 3
|
||||
Layout.column: 0
|
||||
text: "HMD Alignment"
|
||||
}
|
||||
|
||||
ButtonGroup {
|
||||
id: headEyes
|
||||
}
|
||||
|
||||
HifiControlsUit.RadioButton {
|
||||
id: headRadioButton
|
||||
|
||||
Layout.row: 3
|
||||
Layout.column: 1
|
||||
Layout.leftMargin: -15
|
||||
|
||||
ButtonGroup.group: headEyes
|
||||
checked: true
|
||||
|
||||
colorScheme: hifi.colorSchemes.light
|
||||
fontSize: 17
|
||||
letterSpacing: 1.4
|
||||
text: "Head"
|
||||
boxSize: 20
|
||||
}
|
||||
|
||||
HifiControlsUit.RadioButton {
|
||||
id: eyesRadioButton
|
||||
|
||||
Layout.row: 3
|
||||
Layout.column: 2
|
||||
Layout.rightMargin: -15
|
||||
|
||||
ButtonGroup.group: headEyes
|
||||
|
||||
colorScheme: hifi.colorSchemes.light
|
||||
fontSize: 17
|
||||
letterSpacing: 1.4
|
||||
text: "Eyes"
|
||||
boxSize: 20
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
|
|
|
@ -148,7 +148,7 @@ Rectangle {
|
|||
}
|
||||
|
||||
function canAddToWorld(path) {
|
||||
var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i, /\.jpg\b/i, /\.png\b/i];
|
||||
var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i, /\.jpg\b/i, /\.png\b/i, /\.gltf\b/i];
|
||||
|
||||
if (selectedItemCount > 1) {
|
||||
return false;
|
||||
|
|
|
@ -70,7 +70,7 @@ Flickable {
|
|||
readonly property bool hmdDesktop: hmdInDesktop.checked
|
||||
|
||||
property int state: buttonState.disabled
|
||||
property var lastConfiguration: null
|
||||
property var lastConfiguration: null
|
||||
|
||||
HifiConstants { id: hifi }
|
||||
|
||||
|
@ -90,7 +90,6 @@ Flickable {
|
|||
anchors.fill: parent
|
||||
propagateComposedEvents: true
|
||||
onPressed: {
|
||||
parent.forceActiveFocus()
|
||||
mouse.accepted = false;
|
||||
}
|
||||
}
|
||||
|
@ -169,9 +168,7 @@ Flickable {
|
|||
boxRadius: 7
|
||||
visible: viveInDesktop.checked
|
||||
|
||||
anchors.top: viveInDesktop.bottom
|
||||
anchors.topMargin: 5
|
||||
anchors.left: openVrConfiguration.left
|
||||
anchors.leftMargin: leftMargin + 10
|
||||
|
||||
onClicked: {
|
||||
|
@ -214,13 +211,13 @@ Flickable {
|
|||
|
||||
onRealValueChanged: {
|
||||
sendConfigurationSettings();
|
||||
openVrConfiguration.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
HifiControls.SpinBox {
|
||||
id: headZOffset
|
||||
z: 10
|
||||
width: 112
|
||||
label: "Z Offset"
|
||||
minimumValue: -50
|
||||
|
@ -232,7 +229,6 @@ Flickable {
|
|||
|
||||
onRealValueChanged: {
|
||||
sendConfigurationSettings();
|
||||
openVrConfiguration.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -326,7 +322,6 @@ Flickable {
|
|||
|
||||
onRealValueChanged: {
|
||||
sendConfigurationSettings();
|
||||
openVrConfiguration.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -344,7 +339,6 @@ Flickable {
|
|||
|
||||
onRealValueChanged: {
|
||||
sendConfigurationSettings();
|
||||
openVrConfiguration.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -578,7 +572,6 @@ Flickable {
|
|||
|
||||
onRealValueChanged: {
|
||||
sendConfigurationSettings();
|
||||
openVrConfiguration.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -596,7 +589,6 @@ Flickable {
|
|||
|
||||
onRealValueChanged: {
|
||||
sendConfigurationSettings();
|
||||
openVrConfiguration.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -747,8 +739,8 @@ Flickable {
|
|||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
InputConfiguration.calibrationStatus.connect(calibrationStatusInfo);
|
||||
lastConfiguration = composeConfigurationSettings();
|
||||
InputConfiguration.calibrationStatus.connect(calibrationStatusInfo);
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
|
@ -777,7 +769,6 @@ Flickable {
|
|||
calibrationTimer.interval = realValue * 1000;
|
||||
openVrConfiguration.countDown = realValue;
|
||||
numberAnimation.duration = calibrationTimer.interval;
|
||||
openVrConfiguration.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1048,6 +1039,9 @@ Flickable {
|
|||
}
|
||||
|
||||
function updateButtonState() {
|
||||
if (lastConfiguration === null) {
|
||||
lastConfiguration = composeConfigurationSettings();
|
||||
}
|
||||
var settings = composeConfigurationSettings();
|
||||
var bodySetting = settings["bodyConfiguration"];
|
||||
var headSetting = settings["headConfiguration"];
|
||||
|
|
|
@ -120,6 +120,7 @@
|
|||
#include <plugins/PluginManager.h>
|
||||
#include <plugins/PluginUtils.h>
|
||||
#include <plugins/SteamClientPlugin.h>
|
||||
#include <plugins/OculusPlatformPlugin.h>
|
||||
#include <plugins/InputConfiguration.h>
|
||||
#include <RecordingScriptingInterface.h>
|
||||
#include <render/EngineStats.h>
|
||||
|
@ -151,6 +152,7 @@
|
|||
#include <trackers/EyeTracker.h>
|
||||
#include <avatars-renderer/ScriptAvatar.h>
|
||||
#include <RenderableEntityItem.h>
|
||||
#include <RenderableWebEntityItem.h>
|
||||
#include <model-networking/MaterialCache.h>
|
||||
#include "recording/ClipCache.h"
|
||||
|
||||
|
@ -799,7 +801,6 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
|
|||
if (auto steamClient = pluginManager->getSteamClientPlugin()) {
|
||||
steamClient->init();
|
||||
}
|
||||
|
||||
PROFILE_SET_THREAD_NAME("Main Thread");
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
|
@ -2320,6 +2321,76 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
return DependencyManager::get<AvatarManager>()->getMyAvatar()->getWorldOrientation() * Vectors::UP;
|
||||
});
|
||||
|
||||
render::entities::WebEntityRenderer::setAcquireWebSurfaceOperator([this](const QString& url, bool htmlContent, QSharedPointer<OffscreenQmlSurface>& webSurface, bool& cachedWebSurface) {
|
||||
bool isTablet = url == TabletScriptingInterface::QML;
|
||||
if (htmlContent) {
|
||||
webSurface = DependencyManager::get<OffscreenQmlSurfaceCache>()->acquire(render::entities::WebEntityRenderer::QML);
|
||||
cachedWebSurface = true;
|
||||
auto rootItemLoadedFunctor = [url, webSurface] {
|
||||
webSurface->getRootItem()->setProperty(render::entities::WebEntityRenderer::URL_PROPERTY, url);
|
||||
};
|
||||
if (webSurface->getRootItem()) {
|
||||
rootItemLoadedFunctor();
|
||||
} else {
|
||||
QObject::connect(webSurface.data(), &hifi::qml::OffscreenSurface::rootContextCreated, rootItemLoadedFunctor);
|
||||
}
|
||||
} else {
|
||||
// FIXME: the tablet should use the OffscreenQmlSurfaceCache
|
||||
webSurface = QSharedPointer<OffscreenQmlSurface>(new OffscreenQmlSurface(), [](OffscreenQmlSurface* webSurface) {
|
||||
AbstractViewStateInterface::instance()->sendLambdaEvent([webSurface] {
|
||||
// WebEngineView may run other threads (wasapi), so they must be deleted for a clean shutdown
|
||||
// if the application has already stopped its event loop, delete must be explicit
|
||||
delete webSurface;
|
||||
});
|
||||
});
|
||||
auto rootItemLoadedFunctor = [webSurface, url, isTablet] {
|
||||
Application::setupQmlSurface(webSurface->getSurfaceContext(), isTablet || url == OVERLAY_LOGIN_DIALOG.toString());
|
||||
};
|
||||
if (webSurface->getRootItem()) {
|
||||
rootItemLoadedFunctor();
|
||||
} else {
|
||||
QObject::connect(webSurface.data(), &hifi::qml::OffscreenSurface::rootContextCreated, rootItemLoadedFunctor);
|
||||
}
|
||||
webSurface->load(url);
|
||||
cachedWebSurface = false;
|
||||
}
|
||||
const uint8_t DEFAULT_MAX_FPS = 10;
|
||||
const uint8_t TABLET_FPS = 90;
|
||||
webSurface->setMaxFps(isTablet ? TABLET_FPS : DEFAULT_MAX_FPS);
|
||||
});
|
||||
render::entities::WebEntityRenderer::setReleaseWebSurfaceOperator([this](QSharedPointer<OffscreenQmlSurface>& webSurface, bool& cachedWebSurface, std::vector<QMetaObject::Connection>& connections) {
|
||||
QQuickItem* rootItem = webSurface->getRootItem();
|
||||
|
||||
// Fix for crash in QtWebEngineCore when rapidly switching domains
|
||||
// Call stop on the QWebEngineView before destroying OffscreenQMLSurface.
|
||||
if (rootItem) {
|
||||
// stop loading
|
||||
QMetaObject::invokeMethod(rootItem, "stop");
|
||||
}
|
||||
|
||||
webSurface->pause();
|
||||
|
||||
for (auto& connection : connections) {
|
||||
QObject::disconnect(connection);
|
||||
}
|
||||
connections.clear();
|
||||
|
||||
// If the web surface was fetched out of the cache, release it back into the cache
|
||||
if (cachedWebSurface) {
|
||||
// If it's going back into the cache make sure to explicitly set the URL to a blank page
|
||||
// in order to stop any resource consumption or audio related to the page.
|
||||
if (rootItem) {
|
||||
rootItem->setProperty("url", "about:blank");
|
||||
}
|
||||
auto offscreenCache = DependencyManager::get<OffscreenQmlSurfaceCache>();
|
||||
if (offscreenCache) {
|
||||
offscreenCache->release(render::entities::WebEntityRenderer::QML, webSurface);
|
||||
}
|
||||
cachedWebSurface = false;
|
||||
}
|
||||
webSurface.reset();
|
||||
});
|
||||
|
||||
// Preload Tablet sounds
|
||||
DependencyManager::get<TabletScriptingInterface>()->preloadSounds();
|
||||
DependencyManager::get<Keyboard>()->createKeyboard();
|
||||
|
@ -2666,6 +2737,7 @@ Application::~Application() {
|
|||
if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
|
||||
steamClient->shutdown();
|
||||
}
|
||||
|
||||
DependencyManager::destroy<PluginManager>();
|
||||
|
||||
DependencyManager::destroy<CompositorHelper>(); // must be destroyed before the FramebufferCache
|
||||
|
@ -3021,7 +3093,7 @@ void Application::initializeUi() {
|
|||
});
|
||||
|
||||
offscreenSurfaceCache->reserve(TabletScriptingInterface::QML, 1);
|
||||
offscreenSurfaceCache->reserve(Web3DOverlay::QML, 2);
|
||||
offscreenSurfaceCache->reserve(render::entities::WebEntityRenderer::QML, 2);
|
||||
#endif
|
||||
|
||||
flushMenuUpdates();
|
||||
|
@ -3161,6 +3233,61 @@ void Application::onDesktopRootItemCreated(QQuickItem* rootItem) {
|
|||
#endif
|
||||
}
|
||||
|
||||
void Application::setupQmlSurface(QQmlContext* surfaceContext, bool setAdditionalContextProperties) {
|
||||
surfaceContext->setContextProperty("Users", DependencyManager::get<UsersScriptingInterface>().data());
|
||||
surfaceContext->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
|
||||
surfaceContext->setContextProperty("UserActivityLogger", DependencyManager::get<UserActivityLoggerScriptingInterface>().data());
|
||||
surfaceContext->setContextProperty("Preferences", DependencyManager::get<Preferences>().data());
|
||||
surfaceContext->setContextProperty("Vec3", new Vec3());
|
||||
surfaceContext->setContextProperty("Quat", new Quat());
|
||||
surfaceContext->setContextProperty("MyAvatar", DependencyManager::get<AvatarManager>()->getMyAvatar().get());
|
||||
surfaceContext->setContextProperty("Entities", DependencyManager::get<EntityScriptingInterface>().data());
|
||||
surfaceContext->setContextProperty("Snapshot", DependencyManager::get<Snapshot>().data());
|
||||
|
||||
if (setAdditionalContextProperties) {
|
||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||
auto flags = tabletScriptingInterface->getFlags();
|
||||
|
||||
surfaceContext->setContextProperty("offscreenFlags", flags);
|
||||
surfaceContext->setContextProperty("AddressManager", DependencyManager::get<AddressManager>().data());
|
||||
|
||||
surfaceContext->setContextProperty("Settings", SettingsScriptingInterface::getInstance());
|
||||
surfaceContext->setContextProperty("MenuInterface", MenuScriptingInterface::getInstance());
|
||||
surfaceContext->setContextProperty("KeyboardScriptingInterface", DependencyManager::get<KeyboardScriptingInterface>().data());
|
||||
|
||||
surfaceContext->setContextProperty("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
|
||||
surfaceContext->setContextProperty("GlobalServices", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
|
||||
surfaceContext->setContextProperty("AccountServices", AccountServicesScriptingInterface::getInstance());
|
||||
|
||||
// in Qt 5.10.0 there is already an "Audio" object in the QML context
|
||||
// though I failed to find it (from QtMultimedia??). So.. let it be "AudioScriptingInterface"
|
||||
surfaceContext->setContextProperty("AudioScriptingInterface", DependencyManager::get<AudioScriptingInterface>().data());
|
||||
|
||||
surfaceContext->setContextProperty("AudioStats", DependencyManager::get<AudioClient>()->getStats().data());
|
||||
surfaceContext->setContextProperty("fileDialogHelper", new FileDialogHelper());
|
||||
surfaceContext->setContextProperty("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data());
|
||||
surfaceContext->setContextProperty("Assets", DependencyManager::get<AssetMappingsScriptingInterface>().data());
|
||||
surfaceContext->setContextProperty("LODManager", DependencyManager::get<LODManager>().data());
|
||||
surfaceContext->setContextProperty("OctreeStats", DependencyManager::get<OctreeStatsProvider>().data());
|
||||
surfaceContext->setContextProperty("DCModel", DependencyManager::get<DomainConnectionModel>().data());
|
||||
surfaceContext->setContextProperty("AvatarInputs", AvatarInputs::getInstance());
|
||||
surfaceContext->setContextProperty("AvatarList", DependencyManager::get<AvatarManager>().data());
|
||||
surfaceContext->setContextProperty("DialogsManager", DialogsManagerScriptingInterface::getInstance());
|
||||
surfaceContext->setContextProperty("InputConfiguration", DependencyManager::get<InputConfiguration>().data());
|
||||
surfaceContext->setContextProperty("SoundCache", DependencyManager::get<SoundCacheScriptingInterface>().data());
|
||||
surfaceContext->setContextProperty("AvatarBookmarks", DependencyManager::get<AvatarBookmarks>().data());
|
||||
surfaceContext->setContextProperty("Render", AbstractViewStateInterface::instance()->getRenderEngine()->getConfiguration().get());
|
||||
surfaceContext->setContextProperty("Workload", qApp->getGameWorkload()._engine->getConfiguration().get());
|
||||
surfaceContext->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data());
|
||||
surfaceContext->setContextProperty("Pointers", DependencyManager::get<PointerScriptingInterface>().data());
|
||||
surfaceContext->setContextProperty("Window", DependencyManager::get<WindowScriptingInterface>().data());
|
||||
surfaceContext->setContextProperty("Reticle", qApp->getApplicationCompositor().getReticleInterface());
|
||||
surfaceContext->setContextProperty("HiFiAbout", AboutUtil::getInstance());
|
||||
surfaceContext->setContextProperty("WalletScriptingInterface", DependencyManager::get<WalletScriptingInterface>().data());
|
||||
surfaceContext->setContextProperty("ResourceRequestObserver", DependencyManager::get<ResourceRequestObserver>().data());
|
||||
}
|
||||
}
|
||||
|
||||
void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) {
|
||||
PROFILE_RANGE(render, __FUNCTION__);
|
||||
PerformanceTimer perfTimer("updateCamera");
|
||||
|
@ -4747,6 +4874,10 @@ void Application::idle() {
|
|||
steamClient->runCallbacks();
|
||||
}
|
||||
|
||||
if (auto oculusPlugin = PluginManager::getInstance()->getOculusPlatformPlugin()) {
|
||||
oculusPlugin->handleOVREvents();
|
||||
}
|
||||
|
||||
float secondsSinceLastUpdate = (float)_lastTimeUpdated.nsecsElapsed() / NSECS_PER_MSEC / MSECS_PER_SECOND;
|
||||
_lastTimeUpdated.start();
|
||||
|
||||
|
@ -5931,6 +6062,13 @@ void Application::update(float deltaTime) {
|
|||
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
|
||||
controller::HmdAvatarAlignmentType hmdAvatarAlignmentType;
|
||||
if (myAvatar->getHmdAvatarAlignmentType() == "eyes") {
|
||||
hmdAvatarAlignmentType = controller::HmdAvatarAlignmentType::Eyes;
|
||||
} else {
|
||||
hmdAvatarAlignmentType = controller::HmdAvatarAlignmentType::Head;
|
||||
}
|
||||
|
||||
controller::InputCalibrationData calibrationData = {
|
||||
myAvatar->getSensorToWorldMatrix(),
|
||||
createMatFromQuatAndPos(myAvatar->getWorldOrientation(), myAvatar->getWorldPosition()),
|
||||
|
@ -5944,7 +6082,8 @@ void Application::update(float deltaTime) {
|
|||
myAvatar->getRightArmCalibrationMat(),
|
||||
myAvatar->getLeftArmCalibrationMat(),
|
||||
myAvatar->getRightHandCalibrationMat(),
|
||||
myAvatar->getLeftHandCalibrationMat()
|
||||
myAvatar->getLeftHandCalibrationMat(),
|
||||
hmdAvatarAlignmentType
|
||||
};
|
||||
|
||||
InputPluginPointer keyboardMousePlugin;
|
||||
|
|
|
@ -589,6 +589,8 @@ private:
|
|||
void maybeToggleMenuVisible(QMouseEvent* event) const;
|
||||
void toggleTabletUI(bool shouldOpen = false) const;
|
||||
|
||||
static void setupQmlSurface(QQmlContext* surfaceContext, bool setAdditionalContextProperties);
|
||||
|
||||
MainWindow* _window;
|
||||
QElapsedTimer& _sessionRunTimer;
|
||||
|
||||
|
|
2
interface/src/avatar/AvatarManager.cpp
Normal file → Executable file
2
interface/src/avatar/AvatarManager.cpp
Normal file → Executable file
|
@ -270,7 +270,6 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
if (avatar->getSkeletonModel()->isLoaded()) {
|
||||
// remove the orb if it is there
|
||||
avatar->removeOrb();
|
||||
avatar->updateCollisionGroup(_myAvatar->getOtherAvatarsCollisionsEnabled());
|
||||
if (avatar->needsPhysicsUpdate()) {
|
||||
_avatarsToChangeInPhysics.insert(avatar);
|
||||
}
|
||||
|
@ -376,7 +375,6 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
|
|||
}
|
||||
|
||||
AvatarSharedPointer AvatarManager::newSharedAvatar(const QUuid& sessionUUID) {
|
||||
|
||||
auto otherAvatar = new OtherAvatar(qApp->thread());
|
||||
otherAvatar->setSessionUUID(sessionUUID);
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
|
6
interface/src/avatar/AvatarMotionState.cpp
Normal file → Executable file
6
interface/src/avatar/AvatarMotionState.cpp
Normal file → Executable file
|
@ -15,7 +15,6 @@
|
|||
#include <PhysicsEngine.h>
|
||||
#include <PhysicsHelpers.h>
|
||||
|
||||
|
||||
AvatarMotionState::AvatarMotionState(OtherAvatarPointer avatar, const btCollisionShape* shape) : ObjectMotionState(shape), _avatar(avatar) {
|
||||
assert(_avatar);
|
||||
_type = MOTIONSTATE_TYPE_AVATAR;
|
||||
|
@ -172,7 +171,10 @@ QUuid AvatarMotionState::getSimulatorID() const {
|
|||
// virtual
|
||||
void AvatarMotionState::computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const {
|
||||
group = _collisionGroup;
|
||||
mask = _collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS ? 0 : Physics::getDefaultCollisionMask(group);
|
||||
mask = Physics::getDefaultCollisionMask(group);
|
||||
if (!_avatar->getCollideWithOtherAvatars()) {
|
||||
mask &= ~(BULLET_COLLISION_GROUP_MY_AVATAR | BULLET_COLLISION_GROUP_OTHER_AVATAR);
|
||||
}
|
||||
}
|
||||
|
||||
// virtual
|
||||
|
|
|
@ -95,6 +95,37 @@ const float CENTIMETERS_PER_METER = 100.0f;
|
|||
|
||||
const QString AVATAR_SETTINGS_GROUP_NAME { "Avatar" };
|
||||
|
||||
static const QString USER_RECENTER_MODEL_FORCE_SIT = QStringLiteral("ForceSit");
|
||||
static const QString USER_RECENTER_MODEL_FORCE_STAND = QStringLiteral("ForceStand");
|
||||
static const QString USER_RECENTER_MODEL_AUTO = QStringLiteral("Auto");
|
||||
static const QString USER_RECENTER_MODEL_DISABLE_HMD_LEAN = QStringLiteral("DisableHMDLean");
|
||||
|
||||
MyAvatar::SitStandModelType stringToUserRecenterModel(const QString& str) {
|
||||
if (str == USER_RECENTER_MODEL_FORCE_SIT) {
|
||||
return MyAvatar::ForceSit;
|
||||
} else if (str == USER_RECENTER_MODEL_FORCE_STAND) {
|
||||
return MyAvatar::ForceStand;
|
||||
} else if (str == USER_RECENTER_MODEL_DISABLE_HMD_LEAN) {
|
||||
return MyAvatar::DisableHMDLean;
|
||||
} else {
|
||||
return MyAvatar::Auto;
|
||||
}
|
||||
}
|
||||
|
||||
QString userRecenterModelToString(MyAvatar::SitStandModelType model) {
|
||||
switch (model) {
|
||||
case MyAvatar::ForceSit:
|
||||
return USER_RECENTER_MODEL_FORCE_SIT;
|
||||
case MyAvatar::ForceStand:
|
||||
return USER_RECENTER_MODEL_FORCE_STAND;
|
||||
case MyAvatar::DisableHMDLean:
|
||||
return USER_RECENTER_MODEL_DISABLE_HMD_LEAN;
|
||||
case MyAvatar::Auto:
|
||||
default:
|
||||
return USER_RECENTER_MODEL_AUTO;
|
||||
}
|
||||
}
|
||||
|
||||
MyAvatar::MyAvatar(QThread* thread) :
|
||||
Avatar(thread),
|
||||
_yawSpeed(YAW_SPEED_DEFAULT),
|
||||
|
@ -125,6 +156,7 @@ MyAvatar::MyAvatar(QThread* thread) :
|
|||
_prevShouldDrawHead(true),
|
||||
_audioListenerMode(FROM_HEAD),
|
||||
_dominantHandSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "dominantHand", DOMINANT_RIGHT_HAND),
|
||||
_hmdAvatarAlignmentTypeSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "hmdAvatarAlignmentType", DEFAULT_HMD_AVATAR_ALIGNMENT_TYPE),
|
||||
_headPitchSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "", 0.0f),
|
||||
_scaleSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "scale", _targetScale),
|
||||
_yawSpeedSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "yawSpeed", _yawSpeed),
|
||||
|
@ -138,7 +170,8 @@ MyAvatar::MyAvatar(QThread* thread) :
|
|||
_useSnapTurnSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "useSnapTurn", _useSnapTurn),
|
||||
_userHeightSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "userHeight", DEFAULT_AVATAR_HEIGHT),
|
||||
_flyingHMDSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "flyingHMD", _flyingPrefHMD),
|
||||
_avatarEntityCountSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "avatarEntityData" << "size", 0)
|
||||
_avatarEntityCountSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "avatarEntityData" << "size", 0),
|
||||
_userRecenterModelSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "userRecenterModel", USER_RECENTER_MODEL_AUTO)
|
||||
{
|
||||
_clientTraitsHandler.reset(new ClientTraitsHandler(this));
|
||||
|
||||
|
@ -205,12 +238,12 @@ MyAvatar::MyAvatar(QThread* thread) :
|
|||
if (recordingInterface->getPlayFromCurrentLocation()) {
|
||||
setRecordingBasis();
|
||||
}
|
||||
_previousCollisionGroup = _characterController.computeCollisionGroup();
|
||||
_previousCollisionMask = _characterController.computeCollisionMask();
|
||||
_characterController.setCollisionless(true);
|
||||
} else {
|
||||
clearRecordingBasis();
|
||||
useFullAvatarURL(_fullAvatarURLFromPreferences, _fullAvatarModelName);
|
||||
if (_previousCollisionGroup != BULLET_COLLISION_GROUP_COLLISIONLESS) {
|
||||
if (_previousCollisionMask != BULLET_COLLISION_MASK_COLLISIONLESS) {
|
||||
_characterController.setCollisionless(false);
|
||||
}
|
||||
}
|
||||
|
@ -286,10 +319,25 @@ MyAvatar::~MyAvatar() {
|
|||
_myScriptEngine = nullptr;
|
||||
}
|
||||
|
||||
QString MyAvatar::getDominantHand() const {
|
||||
return _dominantHand.get();
|
||||
}
|
||||
|
||||
void MyAvatar::setDominantHand(const QString& hand) {
|
||||
if (hand == DOMINANT_LEFT_HAND || hand == DOMINANT_RIGHT_HAND) {
|
||||
_dominantHand = hand;
|
||||
emit dominantHandChanged(_dominantHand);
|
||||
_dominantHand.set(hand);
|
||||
emit dominantHandChanged(hand);
|
||||
}
|
||||
}
|
||||
|
||||
QString MyAvatar::getHmdAvatarAlignmentType() const {
|
||||
return _hmdAvatarAlignmentType.get();
|
||||
}
|
||||
|
||||
void MyAvatar::setHmdAvatarAlignmentType(const QString& type) {
|
||||
if (type != _hmdAvatarAlignmentType.get()) {
|
||||
_hmdAvatarAlignmentType.set(type);
|
||||
emit hmdAvatarAlignmentTypeChanged(type);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -377,6 +425,7 @@ void MyAvatar::resetSensorsAndBody() {
|
|||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "resetSensorsAndBody");
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
qApp->getActiveDisplayPlugin()->resetSensors();
|
||||
|
@ -1277,7 +1326,8 @@ void MyAvatar::resizeAvatarEntitySettingHandles(uint32_t maxIndex) {
|
|||
}
|
||||
|
||||
void MyAvatar::saveData() {
|
||||
_dominantHandSetting.set(_dominantHand);
|
||||
_dominantHandSetting.set(getDominantHand());
|
||||
_hmdAvatarAlignmentTypeSetting.set(getHmdAvatarAlignmentType());
|
||||
_headPitchSetting.set(getHead()->getBasePitch());
|
||||
_scaleSetting.set(_targetScale);
|
||||
_yawSpeedSetting.set(_yawSpeed);
|
||||
|
@ -1300,6 +1350,7 @@ void MyAvatar::saveData() {
|
|||
_useSnapTurnSetting.set(_useSnapTurn);
|
||||
_userHeightSetting.set(getUserHeight());
|
||||
_flyingHMDSetting.set(getFlyingHMDPref());
|
||||
_userRecenterModelSetting.set(userRecenterModelToString(getUserRecenterModel()));
|
||||
|
||||
auto hmdInterface = DependencyManager::get<HMDScriptingInterface>();
|
||||
saveAvatarEntityDataToSettings();
|
||||
|
@ -1882,9 +1933,12 @@ void MyAvatar::loadData() {
|
|||
setCollisionSoundURL(_collisionSoundURLSetting.get(QUrl(DEFAULT_AVATAR_COLLISION_SOUND_URL)).toString());
|
||||
setSnapTurn(_useSnapTurnSetting.get());
|
||||
setDominantHand(_dominantHandSetting.get(DOMINANT_RIGHT_HAND).toLower());
|
||||
setHmdAvatarAlignmentType(_hmdAvatarAlignmentTypeSetting.get(DEFAULT_HMD_AVATAR_ALIGNMENT_TYPE).toLower());
|
||||
setUserHeight(_userHeightSetting.get(DEFAULT_AVATAR_HEIGHT));
|
||||
setTargetScale(_scaleSetting.get());
|
||||
|
||||
setUserRecenterModel(stringToUserRecenterModel(_userRecenterModelSetting.get(USER_RECENTER_MODEL_AUTO)));
|
||||
|
||||
setEnableMeshVisible(Menu::getInstance()->isOptionChecked(MenuOption::MeshVisible));
|
||||
_follow.setToggleHipsFollowing (Menu::getInstance()->isOptionChecked(MenuOption::ToggleHipsFollowing));
|
||||
setEnableDebugDrawBaseOfSupport(Menu::getInstance()->isOptionChecked(MenuOption::AnimDebugDrawBaseOfSupport));
|
||||
|
@ -2534,7 +2588,7 @@ void MyAvatar::updateMotors() {
|
|||
float verticalMotorTimescale;
|
||||
|
||||
if (_characterController.getState() == CharacterController::State::Hover ||
|
||||
_characterController.computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) {
|
||||
_characterController.computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS) {
|
||||
horizontalMotorTimescale = FLYING_MOTOR_TIMESCALE;
|
||||
verticalMotorTimescale = FLYING_MOTOR_TIMESCALE;
|
||||
} else {
|
||||
|
@ -2544,7 +2598,7 @@ void MyAvatar::updateMotors() {
|
|||
|
||||
if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) {
|
||||
if (_characterController.getState() == CharacterController::State::Hover ||
|
||||
_characterController.computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) {
|
||||
_characterController.computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS) {
|
||||
motorRotation = getMyHead()->getHeadOrientation();
|
||||
} else {
|
||||
// non-hovering = walking: follow camera twist about vertical but not lift
|
||||
|
@ -2599,7 +2653,7 @@ void MyAvatar::prepareForPhysicsSimulation() {
|
|||
qDebug() << "Warning: getParentVelocity failed" << getID();
|
||||
parentVelocity = glm::vec3();
|
||||
}
|
||||
_characterController.handleChangedCollisionGroup();
|
||||
_characterController.handleChangedCollisionMask();
|
||||
_characterController.setParentVelocity(parentVelocity);
|
||||
_characterController.setScaleFactor(getSensorToWorldScale());
|
||||
|
||||
|
@ -3279,7 +3333,7 @@ void MyAvatar::updateOrientation(float deltaTime) {
|
|||
head->setBaseRoll(ROLL(euler));
|
||||
} else {
|
||||
head->setBaseYaw(0.0f);
|
||||
head->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime
|
||||
head->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime
|
||||
+ getDriveKey(DELTA_PITCH) * _pitchSpeed / PITCH_SPEED_DEFAULT);
|
||||
head->setBaseRoll(0.0f);
|
||||
}
|
||||
|
@ -3325,7 +3379,7 @@ void MyAvatar::updateActionMotor(float deltaTime) {
|
|||
|
||||
glm::vec3 direction = forward + right;
|
||||
if (state == CharacterController::State::Hover ||
|
||||
_characterController.computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) {
|
||||
_characterController.computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS) {
|
||||
glm::vec3 up = (getDriveKey(TRANSLATE_Y)) * IDENTITY_UP;
|
||||
direction += up;
|
||||
}
|
||||
|
@ -3881,7 +3935,7 @@ void MyAvatar::setCollisionsEnabled(bool enabled) {
|
|||
bool MyAvatar::getCollisionsEnabled() {
|
||||
// may return 'false' even though the collisionless option was requested
|
||||
// because the zone may disallow collisionless avatars
|
||||
return _characterController.computeCollisionGroup() != BULLET_COLLISION_GROUP_COLLISIONLESS;
|
||||
return _characterController.computeCollisionMask() != BULLET_COLLISION_MASK_COLLISIONLESS;
|
||||
}
|
||||
|
||||
void MyAvatar::setOtherAvatarsCollisionsEnabled(bool enabled) {
|
||||
|
@ -3890,7 +3944,11 @@ void MyAvatar::setOtherAvatarsCollisionsEnabled(bool enabled) {
|
|||
QMetaObject::invokeMethod(this, "setOtherAvatarsCollisionsEnabled", Q_ARG(bool, enabled));
|
||||
return;
|
||||
}
|
||||
bool change = _collideWithOtherAvatars != enabled;
|
||||
_collideWithOtherAvatars = enabled;
|
||||
if (change) {
|
||||
setCollisionWithOtherAvatarsFlags();
|
||||
}
|
||||
emit otherAvatarsCollisionsEnabledChanged(enabled);
|
||||
}
|
||||
|
||||
|
@ -3898,6 +3956,11 @@ bool MyAvatar::getOtherAvatarsCollisionsEnabled() {
|
|||
return _collideWithOtherAvatars;
|
||||
}
|
||||
|
||||
void MyAvatar::setCollisionWithOtherAvatarsFlags() {
|
||||
_characterController.setCollideWithOtherAvatars(_collideWithOtherAvatars);
|
||||
_characterController.setPendingFlagsUpdateCollisionMask();
|
||||
}
|
||||
|
||||
void MyAvatar::updateCollisionCapsuleCache() {
|
||||
glm::vec3 start, end;
|
||||
float radius;
|
||||
|
|
30
interface/src/avatar/MyAvatar.h
Normal file → Executable file
30
interface/src/avatar/MyAvatar.h
Normal file → Executable file
|
@ -252,6 +252,7 @@ class MyAvatar : public Avatar {
|
|||
|
||||
const QString DOMINANT_LEFT_HAND = "left";
|
||||
const QString DOMINANT_RIGHT_HAND = "right";
|
||||
const QString DEFAULT_HMD_AVATAR_ALIGNMENT_TYPE = "head";
|
||||
|
||||
using Clock = std::chrono::system_clock;
|
||||
using TimePoint = Clock::time_point;
|
||||
|
@ -297,6 +298,8 @@ public:
|
|||
|
||||
void reset(bool andRecenter = false, bool andReload = true, bool andHead = true);
|
||||
|
||||
void setCollisionWithOtherAvatarsFlags() override;
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.resetSensorsAndBody
|
||||
*/
|
||||
|
@ -517,7 +520,18 @@ public:
|
|||
* @function MyAvatar.getDominantHand
|
||||
* @returns {string}
|
||||
*/
|
||||
Q_INVOKABLE QString getDominantHand() const { return _dominantHand; }
|
||||
Q_INVOKABLE QString getDominantHand() const;
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.setHmdAvatarAlignmentType
|
||||
* @param {string} hand
|
||||
*/
|
||||
Q_INVOKABLE void setHmdAvatarAlignmentType(const QString& hand);
|
||||
/**jsdoc
|
||||
* @function MyAvatar.setHmdAvatarAlignmentType
|
||||
* @returns {string}
|
||||
*/
|
||||
Q_INVOKABLE QString getHmdAvatarAlignmentType() const;
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.setCenterOfGravityModelEnabled
|
||||
|
@ -1583,6 +1597,13 @@ signals:
|
|||
*/
|
||||
void dominantHandChanged(const QString& hand);
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.hmdAvatarAlignmentTypeChanged
|
||||
* @param {string} type
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void hmdAvatarAlignmentTypeChanged(const QString& type);
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.sensorToWorldScaleChanged
|
||||
* @param {number} scale
|
||||
|
@ -1732,7 +1753,7 @@ private:
|
|||
SharedSoundPointer _collisionSound;
|
||||
|
||||
MyCharacterController _characterController;
|
||||
int32_t _previousCollisionGroup { BULLET_COLLISION_GROUP_MY_AVATAR };
|
||||
int32_t _previousCollisionMask { BULLET_COLLISION_MASK_MY_AVATAR };
|
||||
|
||||
AvatarWeakPointer _lookAtTargetAvatar;
|
||||
glm::vec3 _targetAvatarPosition;
|
||||
|
@ -1771,7 +1792,8 @@ private:
|
|||
ThreadSafeValueCache<QUrl> _prefOverrideAnimGraphUrl;
|
||||
QUrl _fstAnimGraphOverrideUrl;
|
||||
bool _useSnapTurn { true };
|
||||
QString _dominantHand { DOMINANT_RIGHT_HAND };
|
||||
ThreadSafeValueCache<QString> _dominantHand { DOMINANT_RIGHT_HAND };
|
||||
ThreadSafeValueCache<QString> _hmdAvatarAlignmentType { DEFAULT_HMD_AVATAR_ALIGNMENT_TYPE };
|
||||
|
||||
const float ROLL_CONTROL_DEAD_ZONE_DEFAULT = 8.0f; // degrees
|
||||
const float ROLL_CONTROL_RATE_DEFAULT = 114.0f; // degrees / sec
|
||||
|
@ -1944,6 +1966,7 @@ private:
|
|||
TimePoint _nextTraitsSendWindow;
|
||||
|
||||
Setting::Handle<QString> _dominantHandSetting;
|
||||
Setting::Handle<QString> _hmdAvatarAlignmentTypeSetting;
|
||||
Setting::Handle<float> _headPitchSetting;
|
||||
Setting::Handle<float> _scaleSetting;
|
||||
Setting::Handle<float> _yawSpeedSetting;
|
||||
|
@ -1960,6 +1983,7 @@ private:
|
|||
Setting::Handle<bool> _allowTeleportingSetting { "allowTeleporting", true };
|
||||
std::vector<Setting::Handle<QUuid>> _avatarEntityIDSettings;
|
||||
std::vector<Setting::Handle<QByteArray>> _avatarEntityDataSettings;
|
||||
Setting::Handle<QString> _userRecenterModelSetting;
|
||||
|
||||
// AvatarEntities stuff:
|
||||
// We cache the "map of unfortunately-formatted-binary-blobs" because they are expensive to compute
|
||||
|
|
|
@ -202,6 +202,29 @@ bool MyCharacterController::testRayShotgun(const glm::vec3& position, const glm:
|
|||
return result.hitFraction < 1.0f;
|
||||
}
|
||||
|
||||
int32_t MyCharacterController::computeCollisionMask() const {
|
||||
int32_t collisionMask = BULLET_COLLISION_MASK_MY_AVATAR;
|
||||
if (_collisionless && _collisionlessAllowed) {
|
||||
collisionMask = BULLET_COLLISION_MASK_COLLISIONLESS;
|
||||
} else if (!_collideWithOtherAvatars) {
|
||||
collisionMask &= ~BULLET_COLLISION_GROUP_OTHER_AVATAR;
|
||||
}
|
||||
return collisionMask;
|
||||
}
|
||||
|
||||
void MyCharacterController::handleChangedCollisionMask() {
|
||||
if (_pendingFlags & PENDING_FLAG_UPDATE_COLLISION_MASK) {
|
||||
// ATM the easiest way to update collision groups/masks is to remove/re-add the RigidBody
|
||||
if (_dynamicsWorld) {
|
||||
_dynamicsWorld->removeRigidBody(_rigidBody);
|
||||
int32_t collisionMask = computeCollisionMask();
|
||||
_dynamicsWorld->addRigidBody(_rigidBody, BULLET_COLLISION_GROUP_MY_AVATAR, collisionMask);
|
||||
}
|
||||
_pendingFlags &= ~PENDING_FLAG_UPDATE_COLLISION_MASK;
|
||||
updateCurrentGravity();
|
||||
}
|
||||
}
|
||||
|
||||
btConvexHullShape* MyCharacterController::computeShape() const {
|
||||
// HACK: the avatar collides using convex hull with a collision margin equal to
|
||||
// the old capsule radius. Two points define a capsule and additional points are
|
||||
|
|
6
interface/src/avatar/MyCharacterController.h
Normal file → Executable file
6
interface/src/avatar/MyCharacterController.h
Normal file → Executable file
|
@ -42,6 +42,12 @@ public:
|
|||
|
||||
void setDensity(btScalar density) { _density = density; }
|
||||
|
||||
int32_t computeCollisionMask() const override;
|
||||
void handleChangedCollisionMask() override;
|
||||
|
||||
bool _collideWithOtherAvatars{ true };
|
||||
void setCollideWithOtherAvatars(bool collideWithOtherAvatars) { _collideWithOtherAvatars = collideWithOtherAvatars; }
|
||||
|
||||
protected:
|
||||
void initRayShotgun(const btCollisionWorld* world);
|
||||
void updateMassProperties() override;
|
||||
|
|
4
interface/src/avatar/MySkeletonModel.cpp
Normal file → Executable file
4
interface/src/avatar/MySkeletonModel.cpp
Normal file → Executable file
|
@ -187,7 +187,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
|||
}
|
||||
}
|
||||
|
||||
bool isFlying = (myAvatar->getCharacterController()->getState() == CharacterController::State::Hover || myAvatar->getCharacterController()->computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS);
|
||||
bool isFlying = (myAvatar->getCharacterController()->getState() == CharacterController::State::Hover || myAvatar->getCharacterController()->computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS);
|
||||
if (isFlying != _prevIsFlying) {
|
||||
const float FLY_TO_IDLE_HIPS_TRANSITION_TIME = 0.5f;
|
||||
_flyIdleTimer = FLY_TO_IDLE_HIPS_TRANSITION_TIME;
|
||||
|
@ -198,7 +198,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
|||
|
||||
// if hips are not under direct control, estimate the hips position.
|
||||
if (avatarHeadPose.isValid() && !(params.primaryControllerFlags[Rig::PrimaryControllerType_Hips] & (uint8_t)Rig::ControllerFlags::Enabled)) {
|
||||
bool isFlying = (myAvatar->getCharacterController()->getState() == CharacterController::State::Hover || myAvatar->getCharacterController()->computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS);
|
||||
bool isFlying = (myAvatar->getCharacterController()->getState() == CharacterController::State::Hover || myAvatar->getCharacterController()->computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS);
|
||||
|
||||
// timescale in seconds
|
||||
const float TRANS_HORIZ_TIMESCALE = 0.15f;
|
||||
|
|
12
interface/src/avatar/OtherAvatar.cpp
Normal file → Executable file
12
interface/src/avatar/OtherAvatar.cpp
Normal file → Executable file
|
@ -135,17 +135,9 @@ void OtherAvatar::rebuildCollisionShape() {
|
|||
}
|
||||
}
|
||||
|
||||
void OtherAvatar::updateCollisionGroup(bool myAvatarCollide) {
|
||||
void OtherAvatar::setCollisionWithOtherAvatarsFlags() {
|
||||
if (_motionState) {
|
||||
bool collides = _motionState->getCollisionGroup() == BULLET_COLLISION_GROUP_OTHER_AVATAR && myAvatarCollide;
|
||||
if (_collideWithOtherAvatars != collides) {
|
||||
if (!myAvatarCollide) {
|
||||
_collideWithOtherAvatars = false;
|
||||
}
|
||||
auto newCollisionGroup = _collideWithOtherAvatars ? BULLET_COLLISION_GROUP_OTHER_AVATAR : BULLET_COLLISION_GROUP_COLLISIONLESS;
|
||||
_motionState->setCollisionGroup(newCollisionGroup);
|
||||
_motionState->addDirtyFlags(Simulation::DIRTY_COLLISION_GROUP);
|
||||
}
|
||||
_motionState->addDirtyFlags(Simulation::DIRTY_COLLISION_GROUP);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
4
interface/src/avatar/OtherAvatar.h
Normal file → Executable file
4
interface/src/avatar/OtherAvatar.h
Normal file → Executable file
|
@ -46,7 +46,9 @@ public:
|
|||
bool shouldBeInPhysicsSimulation() const;
|
||||
bool needsPhysicsUpdate() const;
|
||||
|
||||
void updateCollisionGroup(bool myAvatarCollide);
|
||||
bool getCollideWithOtherAvatars() const { return _collideWithOtherAvatars; }
|
||||
|
||||
void setCollisionWithOtherAvatarsFlags() override;
|
||||
|
||||
void simulate(float deltaTime, bool inView) override;
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include <plugins/PluginManager.h>
|
||||
#include <plugins/SteamClientPlugin.h>
|
||||
#include <plugins/OculusPlatformPlugin.h>
|
||||
#include <shared/GlobalAppProperties.h>
|
||||
#include <ui/TabletScriptingInterface.h>
|
||||
#include <UserActivityLogger.h>
|
||||
|
@ -109,8 +110,16 @@ bool LoginDialog::isSteamRunning() const {
|
|||
return steamClient && steamClient->isRunning();
|
||||
}
|
||||
|
||||
bool LoginDialog::isOculusStoreRunning() const {
|
||||
return qApp->property(hifi::properties::OCULUS_STORE).toBool();
|
||||
bool LoginDialog::isOculusRunning() const {
|
||||
auto oculusPlatformPlugin = PluginManager::getInstance()->getOculusPlatformPlugin();
|
||||
return (oculusPlatformPlugin && oculusPlatformPlugin->isRunning());
|
||||
}
|
||||
|
||||
QString LoginDialog::oculusUserID() const {
|
||||
if (auto oculusPlatformPlugin = PluginManager::getInstance()->getOculusPlatformPlugin()) {
|
||||
return oculusPlatformPlugin->getOculusUserID();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void LoginDialog::dismissLoginDialog() {
|
||||
|
@ -126,6 +135,79 @@ void LoginDialog::login(const QString& username, const QString& password) const
|
|||
DependencyManager::get<AccountManager>()->requestAccessToken(username, password);
|
||||
}
|
||||
|
||||
void LoginDialog::loginThroughOculus() {
|
||||
qDebug() << "Attempting to login through Oculus";
|
||||
if (auto oculusPlatformPlugin = PluginManager::getInstance()->getOculusPlatformPlugin()) {
|
||||
oculusPlatformPlugin->requestNonceAndUserID([this] (QString nonce, QString oculusID) {
|
||||
DependencyManager::get<AccountManager>()->requestAccessTokenWithOculus(nonce, oculusID);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void LoginDialog::linkOculus() {
|
||||
qDebug() << "Attempting to link Oculus account";
|
||||
if (auto oculusPlatformPlugin = PluginManager::getInstance()->getOculusPlatformPlugin()) {
|
||||
oculusPlatformPlugin->requestNonceAndUserID([this] (QString nonce, QString oculusID) {
|
||||
if (nonce.isEmpty() || oculusID.isEmpty()) {
|
||||
emit handleLoginFailed();
|
||||
return;
|
||||
}
|
||||
|
||||
JSONCallbackParameters callbackParams;
|
||||
callbackParams.callbackReceiver = this;
|
||||
callbackParams.jsonCallbackMethod = "linkCompleted";
|
||||
callbackParams.errorCallbackMethod = "linkFailed";
|
||||
const QString LINK_OCULUS_PATH = "api/v1/user/oculus/link";
|
||||
|
||||
QJsonObject payload;
|
||||
payload["oculus_nonce"] = nonce;
|
||||
payload["oculus_id"] = oculusID;
|
||||
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
accountManager->sendRequest(LINK_OCULUS_PATH, AccountManagerAuth::Required,
|
||||
QNetworkAccessManager::PostOperation, callbackParams,
|
||||
QJsonDocument(payload).toJson());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void LoginDialog::createAccountFromOculus(QString email, QString username, QString password) {
|
||||
qDebug() << "Attempting to create account from Oculus info";
|
||||
if (auto oculusPlatformPlugin = PluginManager::getInstance()->getOculusPlatformPlugin()) {
|
||||
oculusPlatformPlugin->requestNonceAndUserID([this, email, username, password] (QString nonce, QString oculusID) {
|
||||
if (nonce.isEmpty() || oculusID.isEmpty()) {
|
||||
emit handleLoginFailed();
|
||||
return;
|
||||
}
|
||||
|
||||
JSONCallbackParameters callbackParams;
|
||||
callbackParams.callbackReceiver = this;
|
||||
callbackParams.jsonCallbackMethod = "createCompleted";
|
||||
callbackParams.errorCallbackMethod = "createFailed";
|
||||
|
||||
const QString CREATE_ACCOUNT_FROM_OCULUS_PATH = "api/v1/user/oculus/create";
|
||||
|
||||
QJsonObject payload;
|
||||
payload["oculus_nonce"] = nonce;
|
||||
payload["oculus_id"] = oculusID;
|
||||
if (!email.isEmpty()) {
|
||||
payload["email"] = email;
|
||||
}
|
||||
if (!username.isEmpty()) {
|
||||
payload["username"] = username;
|
||||
}
|
||||
if (!password.isEmpty()) {
|
||||
payload["password"] = password;
|
||||
}
|
||||
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
accountManager->sendRequest(CREATE_ACCOUNT_FROM_OCULUS_PATH, AccountManagerAuth::None,
|
||||
QNetworkAccessManager::PostOperation, callbackParams,
|
||||
QJsonDocument(payload).toJson());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void LoginDialog::loginThroughSteam() {
|
||||
qDebug() << "Attempting to login through Steam";
|
||||
if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
|
||||
|
@ -157,7 +239,7 @@ void LoginDialog::linkSteam() {
|
|||
const QString LINK_STEAM_PATH = "api/v1/user/steam/link";
|
||||
|
||||
QJsonObject payload;
|
||||
payload.insert("steam_auth_ticket", QJsonValue::fromVariant(QVariant(ticket)));
|
||||
payload["steam_auth_ticket"] = QJsonValue::fromVariant(QVariant(ticket));
|
||||
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
accountManager->sendRequest(LINK_STEAM_PATH, AccountManagerAuth::Required,
|
||||
|
@ -184,9 +266,9 @@ void LoginDialog::createAccountFromSteam(QString username) {
|
|||
const QString CREATE_ACCOUNT_FROM_STEAM_PATH = "api/v1/user/steam/create";
|
||||
|
||||
QJsonObject payload;
|
||||
payload.insert("steam_auth_ticket", QJsonValue::fromVariant(QVariant(ticket)));
|
||||
payload["steam_auth_ticket"] = QJsonValue::fromVariant(QVariant(ticket));
|
||||
if (!username.isEmpty()) {
|
||||
payload.insert("username", QJsonValue::fromVariant(QVariant(username)));
|
||||
payload["username"] = username;
|
||||
}
|
||||
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
|
@ -214,6 +296,45 @@ void LoginDialog::createCompleted(QNetworkReply* reply) {
|
|||
}
|
||||
|
||||
void LoginDialog::createFailed(QNetworkReply* reply) {
|
||||
if (isOculusRunning()) {
|
||||
auto replyData = reply->readAll();
|
||||
QJsonParseError parseError;
|
||||
auto doc = QJsonDocument::fromJson(replyData, &parseError);
|
||||
if (parseError.error != QJsonParseError::NoError) {
|
||||
emit handleCreateFailed(reply->errorString());
|
||||
return;
|
||||
}
|
||||
auto data = doc["data"];
|
||||
auto error = data["error"];
|
||||
auto oculusError = data["oculus"];
|
||||
auto user = error["username"].toArray();
|
||||
auto uid = error["uid"].toArray();
|
||||
auto email = error["email"].toArray();
|
||||
auto password = error["password"].toArray();
|
||||
QString reply;
|
||||
if (uid[0].isString()) {
|
||||
emit handleCreateFailed("Oculus ID " + uid[0].toString() + ".");
|
||||
return;
|
||||
}
|
||||
if (user[0].isString()) {
|
||||
reply = "Username " + user[0].toString() + ".";
|
||||
}
|
||||
if (email[0].isString()) {
|
||||
reply.append((!reply.isEmpty()) ? "\n" : "");
|
||||
reply.append("Email " + email[0].toString() + ".");
|
||||
}
|
||||
if (password[0].isString()) {
|
||||
reply.append((!reply.isEmpty()) ? "\n" : "");
|
||||
reply.append("Password " + password[0].toString() + ".");
|
||||
}
|
||||
if (!oculusError.isNull() && !oculusError.isUndefined()) {
|
||||
emit handleCreateFailed("Could not verify token with Oculus. Please try again.");
|
||||
return;
|
||||
} else {
|
||||
emit handleCreateFailed(reply);
|
||||
return;
|
||||
}
|
||||
}
|
||||
emit handleCreateFailed(reply->errorString());
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,6 @@ extern const QUrl OVERLAY_LOGIN_DIALOG;
|
|||
|
||||
class LoginDialog : public OffscreenQmlDialog {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool isLogIn READ getIsLogIn WRITE setIsLogIn)
|
||||
HIFI_QML_DECL
|
||||
|
||||
public:
|
||||
|
@ -67,24 +66,23 @@ protected slots:
|
|||
Q_INVOKABLE void dismissLoginDialog();
|
||||
|
||||
Q_INVOKABLE bool isSteamRunning() const;
|
||||
Q_INVOKABLE bool isOculusStoreRunning() const;
|
||||
Q_INVOKABLE bool isOculusRunning() const;
|
||||
|
||||
Q_INVOKABLE QString oculusUserID() const;
|
||||
|
||||
Q_INVOKABLE void login(const QString& username, const QString& password) const;
|
||||
Q_INVOKABLE void loginThroughSteam();
|
||||
Q_INVOKABLE void linkSteam();
|
||||
Q_INVOKABLE void createAccountFromSteam(QString username = QString());
|
||||
Q_INVOKABLE void loginThroughOculus();
|
||||
Q_INVOKABLE void linkOculus();
|
||||
Q_INVOKABLE void createAccountFromOculus(QString email = QString(), QString username = QString(), QString password = QString());
|
||||
|
||||
Q_INVOKABLE void signup(const QString& email, const QString& username, const QString& password);
|
||||
|
||||
Q_INVOKABLE void openUrl(const QString& url) const;
|
||||
|
||||
Q_INVOKABLE bool getLoginDialogPoppedUp() const;
|
||||
|
||||
private:
|
||||
bool getIsLogIn() const { return _isLogIn; }
|
||||
void setIsLogIn(const bool isLogIn) { _isLogIn = isLogIn; }
|
||||
|
||||
bool _isLogIn{ false };
|
||||
};
|
||||
|
||||
#endif // hifi_LoginDialog_h
|
||||
|
|
|
@ -114,14 +114,9 @@ void ModelOverlay::update(float deltatime) {
|
|||
_model->setVisibleInScene(getVisible(), scene);
|
||||
metaDirty = true;
|
||||
}
|
||||
if (_drawInFrontDirty) {
|
||||
_drawInFrontDirty = false;
|
||||
_model->setLayeredInFront(getDrawInFront(), scene);
|
||||
metaDirty = true;
|
||||
}
|
||||
if (_drawInHUDDirty) {
|
||||
_drawInHUDDirty = false;
|
||||
_model->setLayeredInHUD(getDrawHUDLayer(), scene);
|
||||
if (_renderLayerDirty) {
|
||||
_renderLayerDirty = false;
|
||||
_model->setHifiRenderLayer(_drawHUDLayer ? render::hifi::LAYER_3D_HUD : (_drawInFront ? render::hifi::LAYER_3D_FRONT : render::hifi::LAYER_3D), scene);
|
||||
metaDirty = true;
|
||||
}
|
||||
if (_groupCulledDirty) {
|
||||
|
@ -175,14 +170,14 @@ void ModelOverlay::setVisible(bool visible) {
|
|||
void ModelOverlay::setDrawInFront(bool drawInFront) {
|
||||
if (drawInFront != getDrawInFront()) {
|
||||
Base3DOverlay::setDrawInFront(drawInFront);
|
||||
_drawInFrontDirty = true;
|
||||
_renderLayerDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ModelOverlay::setDrawHUDLayer(bool drawHUDLayer) {
|
||||
if (drawHUDLayer != getDrawHUDLayer()) {
|
||||
Base3DOverlay::setDrawHUDLayer(drawHUDLayer);
|
||||
_drawInHUDDirty = true;
|
||||
_renderLayerDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -126,8 +126,7 @@ private:
|
|||
QVector<int> _jointMapping; // domain is index into model-joints, range is index into animation-joints
|
||||
|
||||
bool _visibleDirty { true };
|
||||
bool _drawInFrontDirty { false };
|
||||
bool _drawInHUDDirty { false };
|
||||
bool _renderLayerDirty { false };
|
||||
bool _isGroupCulled { false };
|
||||
bool _groupCulledDirty { false };
|
||||
|
||||
|
|
|
@ -64,20 +64,13 @@
|
|||
#include "AboutUtil.h"
|
||||
#include "ResourceRequestObserver.h"
|
||||
|
||||
#include <RenderableWebEntityItem.h>
|
||||
|
||||
static int MAX_WINDOW_SIZE = 4096;
|
||||
static const float METERS_TO_INCHES = 39.3701f;
|
||||
static const float OPAQUE_ALPHA_THRESHOLD = 0.99f;
|
||||
|
||||
const QString Web3DOverlay::TYPE = "web3d";
|
||||
const QString Web3DOverlay::QML = "Web3DOverlay.qml";
|
||||
|
||||
static auto qmlSurfaceDeleter = [](OffscreenQmlSurface* surface) {
|
||||
AbstractViewStateInterface::instance()->sendLambdaEvent([surface] {
|
||||
// WebEngineView may run other threads (wasapi), so they must be deleted for a clean shutdown
|
||||
// if the application has already stopped its event loop, delete must be explicit
|
||||
delete surface;
|
||||
});
|
||||
};
|
||||
|
||||
Web3DOverlay::Web3DOverlay() {
|
||||
_touchDevice.setCapabilities(QTouchDevice::Position);
|
||||
|
@ -87,34 +80,21 @@ Web3DOverlay::Web3DOverlay() {
|
|||
|
||||
_geometryId = DependencyManager::get<GeometryCache>()->allocateID();
|
||||
connect(this, &Web3DOverlay::requestWebSurface, this, &Web3DOverlay::buildWebSurface);
|
||||
connect(this, &Web3DOverlay::releaseWebSurface, this, &Web3DOverlay::destroyWebSurface);
|
||||
connect(this, &Web3DOverlay::resizeWebSurface, this, &Web3DOverlay::onResizeWebSurface);
|
||||
|
||||
//need to be intialized before Tablet 1st open
|
||||
_webSurface = DependencyManager::get<OffscreenQmlSurfaceCache>()->acquire(QML);
|
||||
_cachedWebSurface = true;
|
||||
_webSurface->getSurfaceContext()->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
|
||||
_webSurface->getSurfaceContext()->setContextProperty("GlobalServices", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
|
||||
_webSurface->getSurfaceContext()->setContextProperty("AccountServices", AccountServicesScriptingInterface::getInstance());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("AddressManager", DependencyManager::get<AddressManager>().data());
|
||||
buildWebSurface(true);
|
||||
}
|
||||
|
||||
Web3DOverlay::Web3DOverlay(const Web3DOverlay* Web3DOverlay) :
|
||||
Billboard3DOverlay(Web3DOverlay),
|
||||
_url(Web3DOverlay->_url),
|
||||
_scriptURL(Web3DOverlay->_scriptURL),
|
||||
_dpi(Web3DOverlay->_dpi),
|
||||
_showKeyboardFocusHighlight(Web3DOverlay->_showKeyboardFocusHighlight)
|
||||
_dpi(Web3DOverlay->_dpi)
|
||||
{
|
||||
_geometryId = DependencyManager::get<GeometryCache>()->allocateID();
|
||||
}
|
||||
|
||||
Web3DOverlay::~Web3DOverlay() {
|
||||
disconnect(this, &Web3DOverlay::requestWebSurface, this, nullptr);
|
||||
disconnect(this, &Web3DOverlay::releaseWebSurface, this, nullptr);
|
||||
disconnect(this, &Web3DOverlay::resizeWebSurface, this, nullptr);
|
||||
|
||||
destroyWebSurface();
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
if (geometryCache) {
|
||||
|
@ -128,74 +108,22 @@ void Web3DOverlay::rebuildWebSurface() {
|
|||
}
|
||||
|
||||
void Web3DOverlay::destroyWebSurface() {
|
||||
if (!_webSurface) {
|
||||
return;
|
||||
if (_webSurface) {
|
||||
render::entities::WebEntityRenderer::releaseWebSurface(_webSurface, _cachedWebSurface, _connections);
|
||||
}
|
||||
|
||||
QQuickItem* rootItem = _webSurface->getRootItem();
|
||||
|
||||
// Fix for crash in QtWebEngineCore when rapidly switching domains
|
||||
// Call stop on the QWebEngineView before destroying OffscreenQMLSurface.
|
||||
if (rootItem) {
|
||||
// stop loading
|
||||
QMetaObject::invokeMethod(rootItem, "stop");
|
||||
}
|
||||
|
||||
_webSurface->pause();
|
||||
|
||||
QObject::disconnect(this, &Web3DOverlay::scriptEventReceived, _webSurface.data(), &OffscreenQmlSurface::emitScriptEvent);
|
||||
QObject::disconnect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, this, &Web3DOverlay::webEventReceived);
|
||||
|
||||
// If the web surface was fetched out of the cache, release it back into the cache
|
||||
if (_cachedWebSurface) {
|
||||
// If it's going back into the cache make sure to explicitly set the URL to a blank page
|
||||
// in order to stop any resource consumption or audio related to the page.
|
||||
if (rootItem) {
|
||||
rootItem->setProperty("url", "about:blank");
|
||||
}
|
||||
auto offscreenCache = DependencyManager::get<OffscreenQmlSurfaceCache>();
|
||||
// FIXME prevents crash on shutdown, but we shoudln't have to do this check
|
||||
if (offscreenCache) {
|
||||
offscreenCache->release(QML, _webSurface);
|
||||
}
|
||||
_cachedWebSurface = false;
|
||||
}
|
||||
_webSurface.reset();
|
||||
}
|
||||
|
||||
void Web3DOverlay::buildWebSurface() {
|
||||
void Web3DOverlay::buildWebSurface(bool overrideWeb) {
|
||||
if (_webSurface) {
|
||||
return;
|
||||
}
|
||||
// FIXME the context save here is most likely unecessary since the QML surfaces now render
|
||||
// off the main thread, and all GL context work is done off the main thread (I *think*)
|
||||
gl::withSavedContext([&] {
|
||||
// FIXME, the max FPS could be better managed by being dynamic (based on the number of current surfaces
|
||||
// and the current rendering load)
|
||||
if (_currentMaxFPS != _desiredMaxFPS) {
|
||||
setMaxFPS(_desiredMaxFPS);
|
||||
}
|
||||
|
||||
if (isWebContent()) {
|
||||
_webSurface = DependencyManager::get<OffscreenQmlSurfaceCache>()->acquire(QML);
|
||||
_cachedWebSurface = true;
|
||||
_webSurface->getRootItem()->setProperty("url", _url);
|
||||
_webSurface->getRootItem()->setProperty("scriptURL", _scriptURL);
|
||||
} else {
|
||||
_webSurface = QSharedPointer<OffscreenQmlSurface>(new OffscreenQmlSurface(), qmlSurfaceDeleter);
|
||||
connect(_webSurface.data(), &hifi::qml::OffscreenSurface::rootContextCreated, [this](QQmlContext* surfaceContext) {
|
||||
setupQmlSurface(_url == TabletScriptingInterface::QML, _url == OVERLAY_LOGIN_DIALOG.toString());
|
||||
});
|
||||
_webSurface->load(_url);
|
||||
_cachedWebSurface = false;
|
||||
}
|
||||
_webSurface->getSurfaceContext()->setContextProperty("globalPosition", vec3toVariant(getWorldPosition()));
|
||||
onResizeWebSurface();
|
||||
_webSurface->resume();
|
||||
});
|
||||
render::entities::WebEntityRenderer::acquireWebSurface(_url, overrideWeb || isWebContent(), _webSurface, _cachedWebSurface);
|
||||
onResizeWebSurface();
|
||||
_webSurface->resume();
|
||||
|
||||
QObject::connect(this, &Web3DOverlay::scriptEventReceived, _webSurface.data(), &OffscreenQmlSurface::emitScriptEvent);
|
||||
QObject::connect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, this, &Web3DOverlay::webEventReceived);
|
||||
_connections.push_back(QObject::connect(this, &Web3DOverlay::scriptEventReceived, _webSurface.data(), &OffscreenQmlSurface::emitScriptEvent));
|
||||
_connections.push_back(QObject::connect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, this, &Web3DOverlay::webEventReceived));
|
||||
}
|
||||
|
||||
void Web3DOverlay::update(float deltatime) {
|
||||
|
@ -215,69 +143,8 @@ bool Web3DOverlay::isWebContent() const {
|
|||
return false;
|
||||
}
|
||||
|
||||
void Web3DOverlay::setupQmlSurface(bool isTablet, bool isLoginDialog) {
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Users", DependencyManager::get<UsersScriptingInterface>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("UserActivityLogger", DependencyManager::get<UserActivityLoggerScriptingInterface>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Preferences", DependencyManager::get<Preferences>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Vec3", new Vec3());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Quat", new Quat());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("MyAvatar", DependencyManager::get<AvatarManager>()->getMyAvatar().get());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Entities", DependencyManager::get<EntityScriptingInterface>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Snapshot", DependencyManager::get<Snapshot>().data());
|
||||
|
||||
if (isTablet || isLoginDialog) {
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Settings", SettingsScriptingInterface::getInstance());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("MenuInterface", MenuScriptingInterface::getInstance());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("KeyboardScriptingInterface", DependencyManager::get<KeyboardScriptingInterface>().data());
|
||||
}
|
||||
if (isTablet) {
|
||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||
auto flags = tabletScriptingInterface->getFlags();
|
||||
|
||||
_webSurface->getSurfaceContext()->setContextProperty("offscreenFlags", flags);
|
||||
_webSurface->getSurfaceContext()->setContextProperty("AddressManager", DependencyManager::get<AddressManager>().data());
|
||||
|
||||
_webSurface->getSurfaceContext()->setContextProperty("GlobalServices", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
|
||||
_webSurface->getSurfaceContext()->setContextProperty("AccountServices", AccountServicesScriptingInterface::getInstance());
|
||||
|
||||
// in Qt 5.10.0 there is already an "Audio" object in the QML context
|
||||
// though I failed to find it (from QtMultimedia??). So.. let it be "AudioScriptingInterface"
|
||||
_webSurface->getSurfaceContext()->setContextProperty("AudioScriptingInterface", DependencyManager::get<AudioScriptingInterface>().data());
|
||||
|
||||
_webSurface->getSurfaceContext()->setContextProperty("AudioStats", DependencyManager::get<AudioClient>()->getStats().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("fileDialogHelper", new FileDialogHelper());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("MyAvatar", DependencyManager::get<AvatarManager>()->getMyAvatar().get());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Assets", DependencyManager::get<AssetMappingsScriptingInterface>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("LODManager", DependencyManager::get<LODManager>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("OctreeStats", DependencyManager::get<OctreeStatsProvider>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("DCModel", DependencyManager::get<DomainConnectionModel>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("AvatarInputs", AvatarInputs::getInstance());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("AvatarList", DependencyManager::get<AvatarManager>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("DialogsManager", DialogsManagerScriptingInterface::getInstance());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("InputConfiguration", DependencyManager::get<InputConfiguration>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("SoundCache", DependencyManager::get<SoundCacheScriptingInterface>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("AvatarBookmarks", DependencyManager::get<AvatarBookmarks>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Render", AbstractViewStateInterface::instance()->getRenderEngine()->getConfiguration().get());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Workload", qApp->getGameWorkload()._engine->getConfiguration().get());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Pointers", DependencyManager::get<PointerScriptingInterface>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Web3DOverlay", this);
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Window", DependencyManager::get<WindowScriptingInterface>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Reticle", qApp->getApplicationCompositor().getReticleInterface());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("HiFiAbout", AboutUtil::getInstance());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("WalletScriptingInterface", DependencyManager::get<WalletScriptingInterface>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("ResourceRequestObserver", DependencyManager::get<ResourceRequestObserver>().data());
|
||||
|
||||
// Override min fps for tablet UI, for silky smooth scrolling
|
||||
setMaxFPS(90);
|
||||
}
|
||||
}
|
||||
|
||||
void Web3DOverlay::setMaxFPS(uint8_t maxFPS) {
|
||||
// FIXME, the max FPS could be better managed by being dynamic (based on the number of current surfaces and the current rendering load)
|
||||
_desiredMaxFPS = maxFPS;
|
||||
if (_webSurface) {
|
||||
_webSurface->setMaxFps(_desiredMaxFPS);
|
||||
|
@ -298,21 +165,13 @@ void Web3DOverlay::onResizeWebSurface() {
|
|||
_webSurface->resize(QSize(dims.x, dims.y));
|
||||
}
|
||||
|
||||
unsigned int Web3DOverlay::deviceIdByTouchPoint(qreal x, qreal y) {
|
||||
if (_webSurface) {
|
||||
return _webSurface->deviceIdByTouchPoint(x, y);
|
||||
} else {
|
||||
return PointerEvent::INVALID_POINTER_ID;
|
||||
}
|
||||
}
|
||||
|
||||
void Web3DOverlay::render(RenderArgs* args) {
|
||||
if (!_renderVisible || !getParentVisible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_webSurface) {
|
||||
emit requestWebSurface();
|
||||
emit requestWebSurface(false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -499,11 +358,6 @@ void Web3DOverlay::setProperties(const QVariantMap& properties) {
|
|||
_desiredMaxFPS = maxFPS.toInt();
|
||||
}
|
||||
|
||||
auto showKeyboardFocusHighlight = properties["showKeyboardFocusHighlight"];
|
||||
if (showKeyboardFocusHighlight.isValid()) {
|
||||
_showKeyboardFocusHighlight = showKeyboardFocusHighlight.toBool();
|
||||
}
|
||||
|
||||
auto inputModeValue = properties["inputMode"];
|
||||
if (inputModeValue.isValid()) {
|
||||
QString inputModeStr = inputModeValue.toString();
|
||||
|
@ -566,8 +420,6 @@ void Web3DOverlay::setProperties(const QVariantMap& properties) {
|
|||
* @property {Vec2} dimensions=1,1 - The size of the overlay to display the Web page on, in meters. Synonyms:
|
||||
* <code>scale</code>, <code>size</code>.
|
||||
* @property {number} maxFPS=10 - The maximum update rate for the Web overlay content, in frames/second.
|
||||
* @property {boolean} showKeyboardFocusHighlight=true - If <code>true</code>, the Web overlay is highlighted when it has
|
||||
* keyboard focus.
|
||||
* @property {string} inputMode=Touch - The user input mode to use - either <code>"Touch"</code> or <code>"Mouse"</code>.
|
||||
*/
|
||||
QVariant Web3DOverlay::getProperty(const QString& property) {
|
||||
|
@ -583,9 +435,6 @@ QVariant Web3DOverlay::getProperty(const QString& property) {
|
|||
if (property == "maxFPS") {
|
||||
return _desiredMaxFPS;
|
||||
}
|
||||
if (property == "showKeyboardFocusHighlight") {
|
||||
return _showKeyboardFocusHighlight;
|
||||
}
|
||||
|
||||
if (property == "inputMode") {
|
||||
if (_inputMode == Mouse) {
|
||||
|
@ -605,14 +454,14 @@ void Web3DOverlay::setURL(const QString& url) {
|
|||
if (wasWebContent && isWebContent()) {
|
||||
// If we're just targeting a new web URL, then switch to that without messing around
|
||||
// with the underlying QML
|
||||
AbstractViewStateInterface::instance()->postLambdaEvent([this, url] {
|
||||
_webSurface->getRootItem()->setProperty("url", _url);
|
||||
AbstractViewStateInterface::instance()->postLambdaEvent([this] {
|
||||
_webSurface->getRootItem()->setProperty(render::entities::WebEntityRenderer::URL_PROPERTY, _url);
|
||||
_webSurface->getRootItem()->setProperty("scriptURL", _scriptURL);
|
||||
});
|
||||
} else {
|
||||
// If we're switching to or from web content, or between different QML content
|
||||
// we need to destroy and rebuild the entire QML surface
|
||||
AbstractViewStateInterface::instance()->postLambdaEvent([this, url] {
|
||||
AbstractViewStateInterface::instance()->postLambdaEvent([this] {
|
||||
rebuildWebSurface();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -22,8 +22,6 @@ class Web3DOverlay : public Billboard3DOverlay {
|
|||
using Parent = Billboard3DOverlay;
|
||||
|
||||
public:
|
||||
|
||||
static const QString QML;
|
||||
static QString const TYPE;
|
||||
virtual QString getType() const override { return TYPE; }
|
||||
|
||||
|
@ -59,12 +57,10 @@ public:
|
|||
Mouse
|
||||
};
|
||||
|
||||
void buildWebSurface();
|
||||
void buildWebSurface(bool overrideWeb = false);
|
||||
void destroyWebSurface();
|
||||
void onResizeWebSurface();
|
||||
|
||||
Q_INVOKABLE unsigned int deviceIdByTouchPoint(qreal x, qreal y);
|
||||
|
||||
public slots:
|
||||
void emitScriptEvent(const QVariant& scriptMessage);
|
||||
|
||||
|
@ -72,26 +68,23 @@ signals:
|
|||
void scriptEventReceived(const QVariant& message);
|
||||
void webEventReceived(const QVariant& message);
|
||||
void resizeWebSurface();
|
||||
void requestWebSurface();
|
||||
void releaseWebSurface();
|
||||
void requestWebSurface(bool overrideWeb);
|
||||
|
||||
protected:
|
||||
Transform evalRenderTransform() override;
|
||||
|
||||
private:
|
||||
void setupQmlSurface(bool isTablet, bool isLoginDialog);
|
||||
void rebuildWebSurface();
|
||||
bool isWebContent() const;
|
||||
|
||||
InputMode _inputMode { Touch };
|
||||
QSharedPointer<OffscreenQmlSurface> _webSurface;
|
||||
bool _cachedWebSurface{ false };
|
||||
bool _cachedWebSurface { false };
|
||||
gpu::TexturePointer _texture;
|
||||
QString _url;
|
||||
QString _scriptURL;
|
||||
float _dpi { 30.0f };
|
||||
int _geometryId { 0 };
|
||||
bool _showKeyboardFocusHighlight { true };
|
||||
|
||||
QTouchDevice _touchDevice;
|
||||
|
||||
|
@ -99,6 +92,8 @@ private:
|
|||
uint8_t _currentMaxFPS { 0 };
|
||||
|
||||
bool _mayNeedResize { false };
|
||||
|
||||
std::vector<QMetaObject::Connection> _connections;
|
||||
};
|
||||
|
||||
#endif // hifi_Web3DOverlay_h
|
||||
|
|
|
@ -237,8 +237,17 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector<HFMJoint>& joints,
|
|||
_relativeDefaultPoses = _absoluteDefaultPoses;
|
||||
convertAbsolutePosesToRelative(_relativeDefaultPoses);
|
||||
|
||||
// build _jointIndicesByName hash
|
||||
for (int i = 0; i < _jointsSize; i++) {
|
||||
_jointIndicesByName[_joints[i].name] = i;
|
||||
auto iter = _jointIndicesByName.find(_joints[i].name);
|
||||
if (iter != _jointIndicesByName.end()) {
|
||||
// prefer joints over meshes if there is a name collision.
|
||||
if (_joints[i].isSkeletonJoint && !_joints[iter.value()].isSkeletonJoint) {
|
||||
iter.value() = i;
|
||||
}
|
||||
} else {
|
||||
_jointIndicesByName.insert(_joints[i].name, i);
|
||||
}
|
||||
}
|
||||
|
||||
// build mirror map.
|
||||
|
|
19
libraries/avatars/src/AvatarData.cpp
Normal file → Executable file
19
libraries/avatars/src/AvatarData.cpp
Normal file → Executable file
|
@ -639,7 +639,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
|||
|
||||
// compute maxTranslationDimension before we send any joint data.
|
||||
float maxTranslationDimension = 0.001f;
|
||||
for (int i = sendStatus.rotationsSent; i < numJoints; ++i) {
|
||||
for (int i = sendStatus.translationsSent; i < numJoints; ++i) {
|
||||
const JointData& data = jointData[i];
|
||||
if (!data.translationIsDefaultPose) {
|
||||
maxTranslationDimension = glm::max(fabsf(data.translation.x), maxTranslationDimension);
|
||||
|
@ -1172,6 +1172,9 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
|
||||
sourceBuffer += sizeof(AvatarDataPacket::AdditionalFlags);
|
||||
|
||||
if (collideWithOtherAvatarsChanged) {
|
||||
setCollisionWithOtherAvatarsFlags();
|
||||
}
|
||||
if (somethingChanged) {
|
||||
_additionalFlagsChanged = now;
|
||||
}
|
||||
|
@ -2428,7 +2431,8 @@ static const QString JSON_AVATAR_VERSION = QStringLiteral("version");
|
|||
enum class JsonAvatarFrameVersion : int {
|
||||
JointRotationsInRelativeFrame = 0,
|
||||
JointRotationsInAbsoluteFrame,
|
||||
JointDefaultPoseBits
|
||||
JointDefaultPoseBits,
|
||||
JointUnscaledTranslations,
|
||||
};
|
||||
|
||||
QJsonValue toJsonValue(const JointData& joint) {
|
||||
|
@ -2445,7 +2449,16 @@ JointData jointDataFromJsonValue(int version, const QJsonValue& json) {
|
|||
if (json.isArray()) {
|
||||
QJsonArray array = json.toArray();
|
||||
result.rotation = quatFromJsonValue(array[0]);
|
||||
|
||||
result.translation = vec3FromJsonValue(array[1]);
|
||||
|
||||
// In old recordings, translations are scaled by _geometryOffset. Undo that scaling.
|
||||
if (version < (int)JsonAvatarFrameVersion::JointUnscaledTranslations) {
|
||||
// because we don't have access to the actual _geometryOffset used. we have to guess.
|
||||
// most avatar FBX files were authored in centimeters.
|
||||
const float METERS_TO_CENTIMETERS = 100.0f;
|
||||
result.translation *= METERS_TO_CENTIMETERS;
|
||||
}
|
||||
if (version >= (int)JsonAvatarFrameVersion::JointDefaultPoseBits) {
|
||||
result.rotationIsDefaultPose = array[2].toBool();
|
||||
result.translationIsDefaultPose = array[3].toBool();
|
||||
|
@ -2464,7 +2477,7 @@ void AvatarData::avatarEntityDataToJson(QJsonObject& root) const {
|
|||
QJsonObject AvatarData::toJson() const {
|
||||
QJsonObject root;
|
||||
|
||||
root[JSON_AVATAR_VERSION] = (int)JsonAvatarFrameVersion::JointDefaultPoseBits;
|
||||
root[JSON_AVATAR_VERSION] = (int)JsonAvatarFrameVersion::JointUnscaledTranslations;
|
||||
|
||||
if (!getSkeletonModelURL().isEmpty()) {
|
||||
root[JSON_AVATAR_BODY_MODEL] = getSkeletonModelURL().toString();
|
||||
|
|
2
libraries/avatars/src/AvatarData.h
Normal file → Executable file
2
libraries/avatars/src/AvatarData.h
Normal file → Executable file
|
@ -496,6 +496,8 @@ public:
|
|||
/// \return number of bytes parsed
|
||||
virtual int parseDataFromBuffer(const QByteArray& buffer);
|
||||
|
||||
virtual void setCollisionWithOtherAvatarsFlags() {};
|
||||
|
||||
// Body Rotation (degrees)
|
||||
float getBodyYaw() const;
|
||||
void setBodyYaw(float bodyYaw);
|
||||
|
|
|
@ -15,6 +15,11 @@
|
|||
|
||||
namespace controller {
|
||||
|
||||
enum class HmdAvatarAlignmentType {
|
||||
Eyes = 0, // align the user's eyes with the avatars eyes
|
||||
Head // align the user's head with the avatars head
|
||||
};
|
||||
|
||||
struct InputCalibrationData {
|
||||
glm::mat4 sensorToWorldMat; // sensor to world
|
||||
glm::mat4 avatarMat; // avatar to world
|
||||
|
@ -29,6 +34,7 @@ struct InputCalibrationData {
|
|||
glm::mat4 defaultLeftArm; // default pose for leftArm joint in sensor space
|
||||
glm::mat4 defaultRightHand; // default pose for rightHand joint in sensor space
|
||||
glm::mat4 defaultLeftHand; // default pose for leftHand joint in sensor space
|
||||
HmdAvatarAlignmentType hmdAvatarAlignmentType;
|
||||
};
|
||||
|
||||
enum class ChannelType {
|
||||
|
|
|
@ -31,6 +31,8 @@ public:
|
|||
|
||||
virtual void compositeExtra() override;
|
||||
|
||||
virtual void pluginUpdate() override {};
|
||||
|
||||
protected:
|
||||
mutable bool _isThrottled = false;
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ public:
|
|||
QImage getScreenshot(float aspectRatio = 0.0f) const override;
|
||||
QImage getSecondaryCameraScreenshot() const override;
|
||||
void copyTextureToQuickFramebuffer(NetworkTexturePointer source, QOpenGLFramebufferObject* target, GLsync* fenceSync) override {};
|
||||
void pluginUpdate() override {};
|
||||
private:
|
||||
static const QString NAME;
|
||||
};
|
||||
|
|
|
@ -46,6 +46,8 @@ public:
|
|||
|
||||
virtual bool onDisplayTextureReset() override { _clearPreviewFlag = true; return true; };
|
||||
|
||||
void pluginUpdate() override {};
|
||||
|
||||
signals:
|
||||
void hmdMountedChanged();
|
||||
void hmdVisibleChanged(bool visible);
|
||||
|
|
|
@ -28,6 +28,8 @@ public:
|
|||
// to the HMD plugins.
|
||||
//virtual glm::mat4 getEyeToHeadTransform(Eye eye) const override;
|
||||
|
||||
virtual void pluginUpdate() override {};
|
||||
|
||||
protected:
|
||||
virtual bool internalActivate() override;
|
||||
virtual void internalDeactivate() override;
|
||||
|
|
|
@ -159,20 +159,40 @@ Item::Bound EntityRenderer::getBound() {
|
|||
return _bound;
|
||||
}
|
||||
|
||||
ShapeKey EntityRenderer::getShapeKey() {
|
||||
if (_primitiveMode == PrimitiveMode::LINES) {
|
||||
return ShapeKey::Builder().withOwnPipeline().withWireframe();
|
||||
}
|
||||
return ShapeKey::Builder().withOwnPipeline();
|
||||
}
|
||||
|
||||
render::hifi::Tag EntityRenderer::getTagMask() const {
|
||||
return _isVisibleInSecondaryCamera ? render::hifi::TAG_ALL_VIEWS : render::hifi::TAG_MAIN_VIEW;
|
||||
}
|
||||
|
||||
render::hifi::Layer EntityRenderer::getHifiRenderLayer() const {
|
||||
switch (_renderLayer) {
|
||||
case RenderLayer::WORLD:
|
||||
return render::hifi::LAYER_3D;
|
||||
case RenderLayer::FRONT:
|
||||
return render::hifi::LAYER_3D_FRONT;
|
||||
case RenderLayer::HUD:
|
||||
return render::hifi::LAYER_3D_HUD;
|
||||
default:
|
||||
return render::hifi::LAYER_3D;
|
||||
}
|
||||
}
|
||||
|
||||
ItemKey EntityRenderer::getKey() {
|
||||
if (isTransparent()) {
|
||||
return ItemKey::Builder::transparentShape().withTypeMeta().withTagBits(getTagMask());
|
||||
return ItemKey::Builder::transparentShape().withTypeMeta().withTagBits(getTagMask()).withLayer(getHifiRenderLayer());
|
||||
}
|
||||
|
||||
// This allows shapes to cast shadows
|
||||
if (_canCastShadow) {
|
||||
return ItemKey::Builder::opaqueShape().withTypeMeta().withTagBits(getTagMask()).withShadowCaster();
|
||||
return ItemKey::Builder::opaqueShape().withTypeMeta().withTagBits(getTagMask()).withShadowCaster().withLayer(getHifiRenderLayer());
|
||||
} else {
|
||||
return ItemKey::Builder::opaqueShape().withTypeMeta().withTagBits(getTagMask());
|
||||
return ItemKey::Builder::opaqueShape().withTypeMeta().withTagBits(getTagMask()).withLayer(getHifiRenderLayer());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -411,6 +431,8 @@ void EntityRenderer::doRenderUpdateSynchronous(const ScenePointer& scene, Transa
|
|||
_moving = entity->isMovingRelativeToParent();
|
||||
_visible = entity->getVisible();
|
||||
setIsVisibleInSecondaryCamera(entity->isVisibleInSecondaryCamera());
|
||||
setRenderLayer(entity->getRenderLayer());
|
||||
setPrimitiveMode(entity->getPrimitiveMode());
|
||||
_canCastShadow = entity->getCanCastShadow();
|
||||
_cauterized = entity->getCauterized();
|
||||
_needsRenderUpdate = false;
|
||||
|
|
|
@ -72,11 +72,12 @@ protected:
|
|||
|
||||
// Implementing the PayloadProxyInterface methods
|
||||
virtual ItemKey getKey() override;
|
||||
virtual ShapeKey getShapeKey() override { return ShapeKey::Builder::ownPipeline(); }
|
||||
virtual ShapeKey getShapeKey() override;
|
||||
virtual Item::Bound getBound() override;
|
||||
virtual void render(RenderArgs* args) override final;
|
||||
virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) override;
|
||||
virtual render::hifi::Tag getTagMask() const;
|
||||
virtual render::hifi::Layer getHifiRenderLayer() const;
|
||||
|
||||
// Returns true if the item in question needs to have updateInScene called because of internal rendering state changes
|
||||
virtual bool needsRenderUpdate() const;
|
||||
|
@ -103,6 +104,8 @@ protected:
|
|||
inline bool isValidRenderItem() const { return _renderItemID != Item::INVALID_ITEM_ID; }
|
||||
|
||||
virtual void setIsVisibleInSecondaryCamera(bool value) { _isVisibleInSecondaryCamera = value; }
|
||||
virtual void setRenderLayer(RenderLayer value) { _renderLayer = value; }
|
||||
virtual void setPrimitiveMode(PrimitiveMode value) { _primitiveMode = value; }
|
||||
|
||||
template <typename F, typename T>
|
||||
T withReadLockResult(const std::function<T()>& f) {
|
||||
|
@ -136,6 +139,8 @@ protected:
|
|||
bool _visible { false };
|
||||
bool _isVisibleInSecondaryCamera { false };
|
||||
bool _canCastShadow { false };
|
||||
RenderLayer _renderLayer { RenderLayer::WORLD };
|
||||
PrimitiveMode _primitiveMode { PrimitiveMode::SOLID };
|
||||
bool _cauterized { false };
|
||||
bool _moving { false };
|
||||
bool _needsRenderUpdate { false };
|
||||
|
|
|
@ -97,6 +97,10 @@ ShapeKey GridEntityRenderer::getShapeKey() {
|
|||
builder.withTranslucent();
|
||||
}
|
||||
|
||||
if (_primitiveMode == PrimitiveMode::LINES) {
|
||||
builder.withWireframe();
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
|
|
|
@ -123,6 +123,10 @@ ShapeKey ImageEntityRenderer::getShapeKey() {
|
|||
if (_emissive) {
|
||||
builder.withUnlit();
|
||||
}
|
||||
|
||||
if (_primitiveMode == PrimitiveMode::LINES) {
|
||||
builder.withWireframe();
|
||||
}
|
||||
});
|
||||
|
||||
return builder.build();
|
||||
|
|
|
@ -55,7 +55,7 @@ void MaterialEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer&
|
|||
|
||||
ItemKey MaterialEntityRenderer::getKey() {
|
||||
ItemKey::Builder builder;
|
||||
builder.withTypeShape().withTagBits(getTagMask());
|
||||
builder.withTypeShape().withTagBits(getTagMask()).withLayer(getHifiRenderLayer());
|
||||
|
||||
if (!_visible) {
|
||||
builder.withInvisible();
|
||||
|
@ -98,6 +98,10 @@ ShapeKey MaterialEntityRenderer::getShapeKey() {
|
|||
builder.withUnlit();
|
||||
}
|
||||
|
||||
if (_primitiveMode == PrimitiveMode::LINES) {
|
||||
builder.withWireframe();
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
|
|
|
@ -1074,10 +1074,16 @@ ModelEntityRenderer::ModelEntityRenderer(const EntityItemPointer& entity) : Pare
|
|||
}
|
||||
|
||||
void ModelEntityRenderer::setKey(bool didVisualGeometryRequestSucceed) {
|
||||
auto builder = ItemKey::Builder().withTypeMeta().withTagBits(getTagMask()).withLayer(getHifiRenderLayer());
|
||||
|
||||
if (_model && _model->isGroupCulled()) {
|
||||
builder.withMetaCullGroup();
|
||||
}
|
||||
|
||||
if (didVisualGeometryRequestSucceed) {
|
||||
_itemKey = ItemKey::Builder().withTypeMeta().withTagBits(getTagMask());
|
||||
_itemKey = builder.build();
|
||||
} else {
|
||||
_itemKey = ItemKey::Builder().withTypeMeta().withTypeShape().withTagBits(getTagMask());
|
||||
_itemKey = builder.withTypeShape().build();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1295,6 +1301,10 @@ bool ModelEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoin
|
|||
model->getRegistrationPoint() != entity->getRegistrationPoint()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (model->isGroupCulled() != entity->getGroupCulled()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -1351,6 +1361,8 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
connect(model.get(), &Model::requestRenderUpdate, this, &ModelEntityRenderer::requestRenderUpdate);
|
||||
connect(model.get(), &Model::setURLFinished, this, [&](bool didVisualGeometryRequestSucceed) {
|
||||
setKey(didVisualGeometryRequestSucceed);
|
||||
_model->setTagMask(getTagMask());
|
||||
_model->setHifiRenderLayer(getHifiRenderLayer());
|
||||
emit requestRenderUpdate();
|
||||
if(didVisualGeometryRequestSucceed) {
|
||||
emit DependencyManager::get<scriptable::ModelProviderFactory>()->
|
||||
|
@ -1437,6 +1449,14 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
model->setCanCastShadow(_canCastShadow, scene);
|
||||
}
|
||||
|
||||
{
|
||||
bool groupCulled = entity->getGroupCulled();
|
||||
if (model->isGroupCulled() != groupCulled) {
|
||||
model->setGroupCulled(groupCulled);
|
||||
setKey(_didLastVisualGeometryRequestSucceed);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
DETAILED_PROFILE_RANGE(simulation_physics, "Fixup");
|
||||
if (model->needsFixupInScene()) {
|
||||
|
@ -1494,6 +1514,24 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
void ModelEntityRenderer::setIsVisibleInSecondaryCamera(bool value) {
|
||||
Parent::setIsVisibleInSecondaryCamera(value);
|
||||
setKey(_didLastVisualGeometryRequestSucceed);
|
||||
if (_model) {
|
||||
_model->setTagMask(getTagMask());
|
||||
}
|
||||
}
|
||||
|
||||
void ModelEntityRenderer::setRenderLayer(RenderLayer value) {
|
||||
Parent::setRenderLayer(value);
|
||||
setKey(_didLastVisualGeometryRequestSucceed);
|
||||
if (_model) {
|
||||
_model->setHifiRenderLayer(getHifiRenderLayer());
|
||||
}
|
||||
}
|
||||
|
||||
void ModelEntityRenderer::setPrimitiveMode(PrimitiveMode value) {
|
||||
Parent::setPrimitiveMode(value);
|
||||
if (_model) {
|
||||
_model->setPrimitiveMode(_primitiveMode);
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: this only renders the "meta" portion of the Model, namely it renders debugging items
|
||||
|
|
|
@ -169,6 +169,8 @@ protected:
|
|||
render::hifi::Tag getTagMask() const override;
|
||||
|
||||
void setIsVisibleInSecondaryCamera(bool value) override;
|
||||
void setRenderLayer(RenderLayer value) override;
|
||||
void setPrimitiveMode(PrimitiveMode value) override;
|
||||
|
||||
private:
|
||||
void animate(const TypedEntityPointer& entity);
|
||||
|
|
|
@ -159,14 +159,18 @@ void ParticleEffectEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEn
|
|||
|
||||
ItemKey ParticleEffectEntityRenderer::getKey() {
|
||||
if (_visible) {
|
||||
return ItemKey::Builder::transparentShape().withTagBits(getTagMask());
|
||||
return ItemKey::Builder::transparentShape().withTagBits(getTagMask()).withLayer(getHifiRenderLayer());
|
||||
} else {
|
||||
return ItemKey::Builder().withInvisible().withTagBits(getTagMask()).build();
|
||||
return ItemKey::Builder().withInvisible().withTagBits(getTagMask()).withLayer(getHifiRenderLayer()).build();
|
||||
}
|
||||
}
|
||||
|
||||
ShapeKey ParticleEffectEntityRenderer::getShapeKey() {
|
||||
return ShapeKey::Builder().withCustom(CUSTOM_PIPELINE_NUMBER).withTranslucent().build();
|
||||
auto builder = ShapeKey::Builder().withCustom(CUSTOM_PIPELINE_NUMBER).withTranslucent();
|
||||
if (_primitiveMode == PrimitiveMode::LINES) {
|
||||
builder.withWireframe();
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
Item::Bound ParticleEffectEntityRenderer::getBound() {
|
||||
|
|
|
@ -55,11 +55,15 @@ void PolyLineEntityRenderer::buildPipeline() {
|
|||
}
|
||||
|
||||
ItemKey PolyLineEntityRenderer::getKey() {
|
||||
return ItemKey::Builder::transparentShape().withTypeMeta().withTagBits(getTagMask());
|
||||
return ItemKey::Builder::transparentShape().withTypeMeta().withTagBits(getTagMask()).withLayer(getHifiRenderLayer());
|
||||
}
|
||||
|
||||
ShapeKey PolyLineEntityRenderer::getShapeKey() {
|
||||
return ShapeKey::Builder().withOwnPipeline().withTranslucent().withoutCullFace();
|
||||
auto builder = ShapeKey::Builder().withOwnPipeline().withTranslucent().withoutCullFace();
|
||||
if (_primitiveMode == PrimitiveMode::LINES) {
|
||||
builder.withWireframe();
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
bool PolyLineEntityRenderer::needsRenderUpdate() const {
|
||||
|
|
|
@ -1613,7 +1613,11 @@ PolyVoxEntityRenderer::PolyVoxEntityRenderer(const EntityItemPointer& entity) :
|
|||
}
|
||||
|
||||
ShapeKey PolyVoxEntityRenderer::getShapeKey() {
|
||||
return ShapeKey::Builder().withCustom(CUSTOM_PIPELINE_NUMBER).build();
|
||||
auto builder = ShapeKey::Builder().withCustom(CUSTOM_PIPELINE_NUMBER);
|
||||
if (_primitiveMode == PrimitiveMode::LINES) {
|
||||
builder.withWireframe();
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
bool PolyVoxEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const {
|
||||
|
|
|
@ -173,7 +173,7 @@ public:
|
|||
}
|
||||
|
||||
protected:
|
||||
virtual ItemKey getKey() override { return ItemKey::Builder::opaqueShape().withTagBits(getTagMask()); }
|
||||
virtual ItemKey getKey() override { return ItemKey::Builder::opaqueShape().withTagBits(getTagMask()).withLayer(getHifiRenderLayer()); }
|
||||
virtual ShapeKey getShapeKey() override;
|
||||
virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override;
|
||||
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;
|
||||
|
|
|
@ -42,10 +42,23 @@ ShapeEntityRenderer::ShapeEntityRenderer(const EntityItemPointer& entity) : Pare
|
|||
// TODO: move into Procedural.cpp
|
||||
PrepareStencil::testMaskDrawShape(*_procedural._opaqueState);
|
||||
PrepareStencil::testMask(*_procedural._transparentState);
|
||||
|
||||
addMaterial(graphics::MaterialLayer(_material, 0), "0");
|
||||
}
|
||||
|
||||
bool ShapeEntityRenderer::needsRenderUpdate() const {
|
||||
if (_procedural.isEnabled() && _procedural.isFading()) {
|
||||
if (resultWithReadLock<bool>([&] {
|
||||
if (_procedural.isEnabled() && _procedural.isFading()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto mat = _materials.find("0");
|
||||
if (mat != _materials.end() && mat->second.needsUpdate()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
})) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -56,7 +69,11 @@ bool ShapeEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoin
|
|||
if (_lastUserData != entity->getUserData()) {
|
||||
return true;
|
||||
}
|
||||
if (_material != entity->getMaterial()) {
|
||||
|
||||
if (_color != entity->getColor()) {
|
||||
return true;
|
||||
}
|
||||
if (_alpha != entity->getAlpha()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -79,10 +96,6 @@ void ShapeEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
_procedural.setProceduralData(ProceduralData::parse(_lastUserData));
|
||||
}
|
||||
|
||||
removeMaterial(_material, "0");
|
||||
_material = entity->getMaterial();
|
||||
addMaterial(graphics::MaterialLayer(_material, 0), "0");
|
||||
|
||||
_shape = entity->getShape();
|
||||
});
|
||||
|
||||
|
@ -111,6 +124,20 @@ void ShapeEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint
|
|||
_procedural.setIsFading(isFading);
|
||||
}
|
||||
});
|
||||
|
||||
glm::u8vec3 color = entity->getColor();
|
||||
float alpha = entity->getAlpha();
|
||||
if (_color != color || _alpha != alpha) {
|
||||
_color = color;
|
||||
_alpha = alpha;
|
||||
_material->setAlbedo(toGlm(_color));
|
||||
_material->setOpacity(_alpha);
|
||||
|
||||
auto materials = _materials.find("0");
|
||||
if (materials != _materials.end()) {
|
||||
materials->second.setNeedsUpdate(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ShapeEntityRenderer::isTransparent() const {
|
||||
|
@ -120,18 +147,15 @@ bool ShapeEntityRenderer::isTransparent() const {
|
|||
|
||||
auto mat = _materials.find("0");
|
||||
if (mat != _materials.end()) {
|
||||
if (mat->second.top().material) {
|
||||
auto matKey = mat->second.top().material->getKey();
|
||||
if (matKey.isTranslucent()) {
|
||||
return true;
|
||||
}
|
||||
if (mat->second.getMaterialKey().isTranslucent()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return Parent::isTransparent();
|
||||
}
|
||||
|
||||
bool ShapeEntityRenderer::useMaterialPipeline() const {
|
||||
bool ShapeEntityRenderer::useMaterialPipeline(const graphics::MultiMaterial& materials) const {
|
||||
bool proceduralReady = resultWithReadLock<bool>([&] {
|
||||
return _procedural.isReady();
|
||||
});
|
||||
|
@ -139,12 +163,7 @@ bool ShapeEntityRenderer::useMaterialPipeline() const {
|
|||
return false;
|
||||
}
|
||||
|
||||
graphics::MaterialKey drawMaterialKey;
|
||||
auto mat = _materials.find("0");
|
||||
if (mat != _materials.end() && mat->second.top().material) {
|
||||
drawMaterialKey = mat->second.top().material->getKey();
|
||||
}
|
||||
|
||||
graphics::MaterialKey drawMaterialKey = materials.getMaterialKey();
|
||||
if (drawMaterialKey.isEmissive() || drawMaterialKey.isUnlit() || drawMaterialKey.isMetallic() || drawMaterialKey.isScattering()) {
|
||||
return true;
|
||||
}
|
||||
|
@ -159,11 +178,13 @@ bool ShapeEntityRenderer::useMaterialPipeline() const {
|
|||
}
|
||||
|
||||
ShapeKey ShapeEntityRenderer::getShapeKey() {
|
||||
if (useMaterialPipeline()) {
|
||||
graphics::MaterialKey drawMaterialKey;
|
||||
if (_materials["0"].top().material) {
|
||||
drawMaterialKey = _materials["0"].top().material->getKey();
|
||||
}
|
||||
auto mat = _materials.find("0");
|
||||
if (mat != _materials.end() && mat->second.needsUpdate()) {
|
||||
RenderPipelines::updateMultiMaterial(mat->second);
|
||||
}
|
||||
|
||||
if (mat != _materials.end() && useMaterialPipeline(mat->second)) {
|
||||
graphics::MaterialKey drawMaterialKey = mat->second.getMaterialKey();
|
||||
|
||||
bool isTranslucent = drawMaterialKey.isTranslucent();
|
||||
bool hasTangents = drawMaterialKey.isNormalMap();
|
||||
|
@ -186,6 +207,10 @@ ShapeKey ShapeEntityRenderer::getShapeKey() {
|
|||
builder.withUnlit();
|
||||
}
|
||||
|
||||
if (_primitiveMode == PrimitiveMode::LINES) {
|
||||
builder.withWireframe();
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
} else {
|
||||
ShapeKey::Builder builder;
|
||||
|
@ -198,6 +223,10 @@ ShapeKey ShapeEntityRenderer::getShapeKey() {
|
|||
if (isTransparent()) {
|
||||
builder.withTranslucent();
|
||||
}
|
||||
|
||||
if (_primitiveMode == PrimitiveMode::LINES) {
|
||||
builder.withWireframe();
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
|
@ -208,7 +237,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
|
|||
|
||||
gpu::Batch& batch = *args->_batch;
|
||||
|
||||
std::shared_ptr<graphics::Material> mat;
|
||||
graphics::MultiMaterial materials;
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
GeometryCache::Shape geometryShape;
|
||||
bool proceduralRender = false;
|
||||
|
@ -216,40 +245,40 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
|
|||
withReadLock([&] {
|
||||
geometryShape = geometryCache->getShapeForEntityShape(_shape);
|
||||
batch.setModelTransform(_renderTransform); // use a transform with scale, rotation, registration point and translation
|
||||
mat = _materials["0"].top().material;
|
||||
if (mat) {
|
||||
outColor = glm::vec4(mat->getAlbedo(), mat->getOpacity());
|
||||
if (_procedural.isReady()) {
|
||||
outColor = _procedural.getColor(outColor);
|
||||
outColor.a *= _procedural.isFading() ? Interpolate::calculateFadeRatio(_procedural.getFadeStartTime()) : 1.0f;
|
||||
_procedural.prepare(batch, _position, _dimensions, _orientation, ProceduralProgramKey(outColor.a < 1.0f));
|
||||
proceduralRender = true;
|
||||
}
|
||||
materials = _materials["0"];
|
||||
auto& schema = materials.getSchemaBuffer().get<graphics::MultiMaterial::Schema>();
|
||||
outColor = glm::vec4(schema._albedo, schema._opacity);
|
||||
if (_procedural.isReady()) {
|
||||
outColor = _procedural.getColor(outColor);
|
||||
outColor.a *= _procedural.isFading() ? Interpolate::calculateFadeRatio(_procedural.getFadeStartTime()) : 1.0f;
|
||||
_procedural.prepare(batch, _position, _dimensions, _orientation, ProceduralProgramKey(outColor.a < 1.0f));
|
||||
proceduralRender = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (!mat) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (proceduralRender) {
|
||||
if (render::ShapeKey(args->_globalShapeKey).isWireframe()) {
|
||||
geometryCache->renderWireShape(batch, geometryShape, outColor);
|
||||
} else {
|
||||
geometryCache->renderShape(batch, geometryShape, outColor);
|
||||
}
|
||||
} else if (!useMaterialPipeline()) {
|
||||
} else if (!useMaterialPipeline(materials)) {
|
||||
// FIXME, support instanced multi-shape rendering using multidraw indirect
|
||||
outColor.a *= _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
|
||||
auto pipeline = outColor.a < 1.0f ? geometryCache->getTransparentShapePipeline() : geometryCache->getOpaqueShapePipeline();
|
||||
if (render::ShapeKey(args->_globalShapeKey).isWireframe()) {
|
||||
render::ShapePipelinePointer pipeline;
|
||||
if (_renderLayer == RenderLayer::WORLD) {
|
||||
pipeline = outColor.a < 1.0f ? geometryCache->getTransparentShapePipeline() : geometryCache->getOpaqueShapePipeline();
|
||||
} else {
|
||||
pipeline = outColor.a < 1.0f ? geometryCache->getForwardTransparentShapePipeline() : geometryCache->getForwardOpaqueShapePipeline();
|
||||
}
|
||||
if (render::ShapeKey(args->_globalShapeKey).isWireframe() || _primitiveMode == PrimitiveMode::LINES) {
|
||||
geometryCache->renderWireShapeInstance(args, batch, geometryShape, outColor, pipeline);
|
||||
} else {
|
||||
geometryCache->renderSolidShapeInstance(args, batch, geometryShape, outColor, pipeline);
|
||||
}
|
||||
} else {
|
||||
if (args->_renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE) {
|
||||
RenderPipelines::bindMaterial(mat, batch, args->_enableTexturing);
|
||||
RenderPipelines::bindMaterials(materials, batch, args->_enableTexturing);
|
||||
args->_details._materialSwitches++;
|
||||
}
|
||||
|
||||
|
@ -268,8 +297,9 @@ scriptable::ScriptableModelBase ShapeEntityRenderer::getScriptableModel() {
|
|||
{
|
||||
std::lock_guard<std::mutex> lock(_materialsLock);
|
||||
result.appendMaterials(_materials);
|
||||
if (_materials["0"].top().material) {
|
||||
vertexColor = _materials["0"].top().material->getAlbedo();
|
||||
auto materials = _materials.find("0");
|
||||
if (materials != _materials.end()) {
|
||||
vertexColor = materials->second.getSchemaBuffer().get<graphics::MultiMaterial::Schema>()._albedo;
|
||||
}
|
||||
}
|
||||
if (auto mesh = geometryCache->meshFromShape(geometryShape, vertexColor)) {
|
||||
|
|
|
@ -35,12 +35,14 @@ private:
|
|||
virtual void doRender(RenderArgs* args) override;
|
||||
virtual bool isTransparent() const override;
|
||||
|
||||
bool useMaterialPipeline() const;
|
||||
bool useMaterialPipeline(const graphics::MultiMaterial& materials) const;
|
||||
|
||||
Procedural _procedural;
|
||||
QString _lastUserData;
|
||||
entity::Shape _shape { entity::Sphere };
|
||||
std::shared_ptr<graphics::Material> _material;
|
||||
std::shared_ptr<graphics::Material> _material { std::make_shared<graphics::Material>() };
|
||||
glm::u8vec3 _color;
|
||||
float _alpha;
|
||||
glm::vec3 _position;
|
||||
glm::vec3 _dimensions;
|
||||
glm::quat _orientation;
|
||||
|
|
|
@ -49,6 +49,9 @@ ShapeKey TextEntityRenderer::getShapeKey() {
|
|||
if (isTransparent()) {
|
||||
builder.withTranslucent();
|
||||
}
|
||||
if (_primitiveMode == PrimitiveMode::LINES) {
|
||||
builder.withWireframe();
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
|
|
|
@ -30,22 +30,26 @@
|
|||
using namespace render;
|
||||
using namespace render::entities;
|
||||
|
||||
static const QString WEB_ENTITY_QML = "controls/WebEntityView.qml";
|
||||
const QString WebEntityRenderer::QML = "Web3DSurface.qml";
|
||||
const char* WebEntityRenderer::URL_PROPERTY = "url";
|
||||
|
||||
std::function<void(QString, bool, QSharedPointer<OffscreenQmlSurface>&, bool&)> WebEntityRenderer::_acquireWebSurfaceOperator = nullptr;
|
||||
std::function<void(QSharedPointer<OffscreenQmlSurface>&, bool&, std::vector<QMetaObject::Connection>&)> WebEntityRenderer::_releaseWebSurfaceOperator = nullptr;
|
||||
|
||||
static int MAX_WINDOW_SIZE = 4096;
|
||||
const float METERS_TO_INCHES = 39.3701f;
|
||||
static uint32_t _currentWebCount{ 0 };
|
||||
// Don't allow more than 20 concurrent web views
|
||||
static const uint32_t MAX_CONCURRENT_WEB_VIEWS = 20;
|
||||
static float OPAQUE_ALPHA_THRESHOLD = 0.99f;
|
||||
|
||||
// If a web-view hasn't been rendered for 30 seconds, de-allocate the framebuffer
|
||||
static uint64_t MAX_NO_RENDER_INTERVAL = 30 * USECS_PER_SECOND;
|
||||
|
||||
static int MAX_WINDOW_SIZE = 4096;
|
||||
static float OPAQUE_ALPHA_THRESHOLD = 0.99f;
|
||||
static int DEFAULT_MAX_FPS = 10;
|
||||
static int YOUTUBE_MAX_FPS = 30;
|
||||
static uint8_t YOUTUBE_MAX_FPS = 30;
|
||||
|
||||
// Don't allow more than 20 concurrent web views
|
||||
static uint32_t _currentWebCount { 0 };
|
||||
static const uint32_t MAX_CONCURRENT_WEB_VIEWS = 20;
|
||||
|
||||
static QTouchDevice _touchDevice;
|
||||
static const char* URL_PROPERTY = "url";
|
||||
|
||||
WebEntityRenderer::ContentType WebEntityRenderer::getContentType(const QString& urlString) {
|
||||
if (urlString.isEmpty()) {
|
||||
|
@ -71,13 +75,18 @@ WebEntityRenderer::WebEntityRenderer(const EntityItemPointer& entity) : Parent(e
|
|||
_touchDevice.setMaximumTouchPoints(4);
|
||||
});
|
||||
_geometryId = DependencyManager::get<GeometryCache>()->allocateID();
|
||||
|
||||
_texture = gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda());
|
||||
_texture->setSource(__FUNCTION__);
|
||||
|
||||
_contentType = ContentType::HtmlContent;
|
||||
buildWebSurface(entity, "");
|
||||
|
||||
_timer.setInterval(MSECS_PER_SECOND);
|
||||
connect(&_timer, &QTimer::timeout, this, &WebEntityRenderer::onTimeout);
|
||||
}
|
||||
|
||||
void WebEntityRenderer::onRemoveFromSceneTyped(const TypedEntityPointer& entity) {
|
||||
WebEntityRenderer::~WebEntityRenderer() {
|
||||
destroyWebSurface();
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
@ -86,6 +95,11 @@ void WebEntityRenderer::onRemoveFromSceneTyped(const TypedEntityPointer& entity)
|
|||
}
|
||||
}
|
||||
|
||||
bool WebEntityRenderer::isTransparent() const {
|
||||
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
|
||||
return fadeRatio < OPAQUE_ALPHA_THRESHOLD || _alpha < 1.0f;
|
||||
}
|
||||
|
||||
bool WebEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const {
|
||||
if (_contextPosition != entity->getWorldPosition()) {
|
||||
return true;
|
||||
|
@ -101,11 +115,31 @@ bool WebEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointe
|
|||
}
|
||||
}
|
||||
|
||||
if (_lastSourceUrl != entity->getSourceUrl()) {
|
||||
if (_color != entity->getColor()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_lastDPI != entity->getDPI()) {
|
||||
if (_alpha != entity->getAlpha()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_sourceURL != entity->getSourceUrl()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_dpi != entity->getDPI()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_scriptURL != entity->getScriptURL()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_maxFPS != entity->getMaxFPS()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_inputMode != entity->getInputMode()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -113,35 +147,25 @@ bool WebEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointe
|
|||
}
|
||||
|
||||
bool WebEntityRenderer::needsRenderUpdate() const {
|
||||
{
|
||||
QSharedPointer<OffscreenQmlSurface> webSurface;
|
||||
withReadLock([&] {
|
||||
webSurface = _webSurface;
|
||||
});
|
||||
if (!webSurface) {
|
||||
// If we have rendered recently, and there is no web surface, we're going to create one
|
||||
return true;
|
||||
}
|
||||
if (resultWithReadLock<bool>([this] {
|
||||
return !_webSurface;
|
||||
})) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return Parent::needsRenderUpdate();
|
||||
}
|
||||
|
||||
void WebEntityRenderer::onTimeout() {
|
||||
bool needsCheck = resultWithReadLock<bool>([&] {
|
||||
uint64_t lastRenderTime;
|
||||
if (!resultWithReadLock<bool>([&] {
|
||||
lastRenderTime = _lastRenderTime;
|
||||
return (_lastRenderTime != 0 && (bool)_webSurface);
|
||||
});
|
||||
|
||||
if (!needsCheck) {
|
||||
})) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t interval;
|
||||
withReadLock([&] {
|
||||
interval = usecTimestampNow() - _lastRenderTime;
|
||||
});
|
||||
|
||||
if (interval > MAX_NO_RENDER_INTERVAL) {
|
||||
if (usecTimestampNow() - lastRenderTime > MAX_NO_RENDER_INTERVAL) {
|
||||
destroyWebSurface();
|
||||
}
|
||||
}
|
||||
|
@ -151,80 +175,100 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene
|
|||
// destroy the existing surface (because surfaces don't support changing the root
|
||||
// object, so subsequent loads of content just overlap the existing content
|
||||
bool urlChanged = false;
|
||||
auto newSourceURL = entity->getSourceUrl();
|
||||
{
|
||||
auto newSourceUrl = entity->getSourceUrl();
|
||||
auto newContentType = getContentType(newSourceUrl);
|
||||
auto currentContentType = ContentType::NoContent;
|
||||
auto newContentType = getContentType(newSourceURL);
|
||||
ContentType currentContentType;
|
||||
withReadLock([&] {
|
||||
urlChanged = _lastSourceUrl != newSourceUrl;
|
||||
urlChanged = _sourceURL != newSourceURL;
|
||||
currentContentType = _contentType;
|
||||
});
|
||||
|
||||
if (urlChanged) {
|
||||
if (newContentType != ContentType::HtmlContent || currentContentType != ContentType::HtmlContent) {
|
||||
destroyWebSurface();
|
||||
// If we destroyed the surface, the URL change will be implicitly handled by the re-creation
|
||||
urlChanged = false;
|
||||
}
|
||||
|
||||
withWriteLock([&] {
|
||||
_lastSourceUrl = newSourceUrl;
|
||||
_contentType = newContentType;
|
||||
});
|
||||
|
||||
if (newContentType != ContentType::HtmlContent || currentContentType != ContentType::HtmlContent) {
|
||||
destroyWebSurface();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
withWriteLock([&] {
|
||||
_inputMode = entity->getInputMode();
|
||||
_dpi = entity->getDPI();
|
||||
_color = entity->getColor();
|
||||
_alpha = entity->getAlpha();
|
||||
|
||||
if (_contentType == ContentType::NoContent) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This work must be done on the main thread
|
||||
// If we couldn't create a new web surface, exit
|
||||
if (!hasWebSurface() && !buildWebSurface(entity)) {
|
||||
return;
|
||||
if (!_webSurface) {
|
||||
buildWebSurface(entity, newSourceURL);
|
||||
}
|
||||
|
||||
if (urlChanged && _contentType == ContentType::HtmlContent) {
|
||||
_webSurface->getRootItem()->setProperty(URL_PROPERTY, _lastSourceUrl);
|
||||
}
|
||||
|
||||
void* key = (void*)this;
|
||||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity] () {
|
||||
withWriteLock([&] {
|
||||
if (_contextPosition != entity->getWorldPosition()) {
|
||||
// update globalPosition
|
||||
_contextPosition = entity->getWorldPosition();
|
||||
_webSurface->getSurfaceContext()->setContextProperty("globalPosition", vec3toVariant(_contextPosition));
|
||||
if (_webSurface && _webSurface->getRootItem()) {
|
||||
if (_webSurface->getRootItem()) {
|
||||
if (_contentType == ContentType::HtmlContent && urlChanged) {
|
||||
_webSurface->getRootItem()->setProperty(URL_PROPERTY, newSourceURL);
|
||||
_sourceURL = newSourceURL;
|
||||
}
|
||||
|
||||
_lastDPI = entity->getDPI();
|
||||
{
|
||||
auto scriptURL = entity->getScriptURL();
|
||||
if (_scriptURL != scriptURL) {
|
||||
_webSurface->getRootItem()->setProperty("scriptURL", _scriptURL);
|
||||
_scriptURL = scriptURL;
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec2 windowSize = getWindowSize(entity);
|
||||
_webSurface->resize(QSize(windowSize.x, windowSize.y));
|
||||
updateModelTransformAndBound();
|
||||
_renderTransform = getModelTransform();
|
||||
_renderTransform.postScale(entity->getScaledDimensions());
|
||||
{
|
||||
auto maxFPS = entity->getMaxFPS();
|
||||
if (_maxFPS != maxFPS) {
|
||||
// We special case YouTube URLs since we know they are videos that we should play with at least 30 FPS.
|
||||
// FIXME this doesn't handle redirects or shortened URLs, consider using a signaling method from the web entity
|
||||
if (QUrl(_sourceURL).host().endsWith("youtube.com", Qt::CaseInsensitive)) {
|
||||
_webSurface->setMaxFps(YOUTUBE_MAX_FPS);
|
||||
} else {
|
||||
_webSurface->setMaxFps(maxFPS);
|
||||
}
|
||||
_maxFPS = maxFPS;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto contextPosition = entity->getWorldPosition();
|
||||
if (_contextPosition != contextPosition) {
|
||||
_webSurface->getSurfaceContext()->setContextProperty("globalPosition", vec3toVariant(contextPosition));
|
||||
_contextPosition = contextPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void* key = (void*)this;
|
||||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity]() {
|
||||
withWriteLock([&] {
|
||||
glm::vec2 windowSize = getWindowSize(entity);
|
||||
_webSurface->resize(QSize(windowSize.x, windowSize.y));
|
||||
updateModelTransformAndBound();
|
||||
_renderTransform = getModelTransform();
|
||||
_renderTransform.postScale(entity->getScaledDimensions());
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void WebEntityRenderer::doRender(RenderArgs* args) {
|
||||
PerformanceTimer perfTimer("WebEntityRenderer::render");
|
||||
withWriteLock([&] {
|
||||
_lastRenderTime = usecTimestampNow();
|
||||
});
|
||||
|
||||
#ifdef WANT_EXTRA_DEBUGGING
|
||||
{
|
||||
gpu::Batch& batch = *args->_batch;
|
||||
batch.setModelTransform(getTransformToCenter()); // we want to include the scale as well
|
||||
glm::vec4 cubeColor{ 1.0f, 0.0f, 0.0f, 1.0f };
|
||||
DependencyManager::get<GeometryCache>()->renderWireCube(batch, 1.0f, cubeColor);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Try to update the texture
|
||||
{
|
||||
QSharedPointer<OffscreenQmlSurface> webSurface;
|
||||
|
@ -242,111 +286,61 @@ void WebEntityRenderer::doRender(RenderArgs* args) {
|
|||
}
|
||||
}
|
||||
|
||||
PerformanceTimer perfTimer("WebEntityRenderer::render");
|
||||
static const glm::vec2 texMin(0.0f), texMax(1.0f), topLeft(-0.5f), bottomRight(0.5f);
|
||||
|
||||
gpu::Batch& batch = *args->_batch;
|
||||
glm::vec4 color;
|
||||
withReadLock([&] {
|
||||
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
|
||||
color = glm::vec4(toGlm(_color), _alpha * fadeRatio);
|
||||
batch.setModelTransform(_renderTransform);
|
||||
});
|
||||
batch.setResourceTexture(0, _texture);
|
||||
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
|
||||
|
||||
// Turn off jitter for these entities
|
||||
batch.pushProjectionJitter();
|
||||
DependencyManager::get<GeometryCache>()->bindWebBrowserProgram(batch, fadeRatio < OPAQUE_ALPHA_THRESHOLD);
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f, 1.0f, 1.0f, fadeRatio), _geometryId);
|
||||
DependencyManager::get<GeometryCache>()->bindWebBrowserProgram(batch, color.a < OPAQUE_ALPHA_THRESHOLD);
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, color, _geometryId);
|
||||
batch.popProjectionJitter();
|
||||
batch.setResourceTexture(0, nullptr);
|
||||
}
|
||||
|
||||
bool WebEntityRenderer::hasWebSurface() {
|
||||
return (bool)_webSurface && _webSurface->getRootItem();
|
||||
}
|
||||
|
||||
static const auto WebSurfaceDeleter = [](OffscreenQmlSurface* webSurface) {
|
||||
AbstractViewStateInterface::instance()->sendLambdaEvent([webSurface] {
|
||||
// WebEngineView may run other threads (wasapi), so they must be deleted for a clean shutdown
|
||||
// if the application has already stopped its event loop, delete must be explicit
|
||||
delete webSurface;
|
||||
});
|
||||
};
|
||||
|
||||
bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) {
|
||||
void WebEntityRenderer::buildWebSurface(const EntityItemPointer& entity, const QString& newSourceURL) {
|
||||
if (_currentWebCount >= MAX_CONCURRENT_WEB_VIEWS) {
|
||||
qWarning() << "Too many concurrent web views to create new view";
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
++_currentWebCount;
|
||||
|
||||
// FIXME use the surface cache instead of explicit creation
|
||||
_webSurface = QSharedPointer<OffscreenQmlSurface>(new OffscreenQmlSurface(), WebSurfaceDeleter);
|
||||
// FIXME, the max FPS could be better managed by being dynamic (based on the number of current surfaces
|
||||
// and the current rendering load)
|
||||
_webSurface->setMaxFps(DEFAULT_MAX_FPS);
|
||||
QObject::connect(_webSurface.data(), &OffscreenQmlSurface::rootContextCreated, [](QQmlContext* surfaceContext) {
|
||||
// FIXME - Keyboard HMD only: Possibly add "HMDinfo" object to context for WebView.qml.
|
||||
surfaceContext->setContextProperty("desktop", QVariant());
|
||||
// Let us interact with the keyboard
|
||||
surfaceContext->setContextProperty("tabletInterface", DependencyManager::get<TabletScriptingInterface>().data());
|
||||
});
|
||||
|
||||
// forward web events to EntityScriptingInterface
|
||||
auto entities = DependencyManager::get<EntityScriptingInterface>();
|
||||
const EntityItemID entityItemID = entity->getID();
|
||||
QObject::connect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, [=](const QVariant& message) {
|
||||
emit entities->webEventReceived(entityItemID, message);
|
||||
});
|
||||
|
||||
if (_contentType == ContentType::HtmlContent) {
|
||||
// We special case YouTube URLs since we know they are videos that we should play with at least 30 FPS.
|
||||
// FIXME this doesn't handle redirects or shortened URLs, consider using a signaling method from the
|
||||
// web entity
|
||||
if (QUrl(_lastSourceUrl).host().endsWith("youtube.com", Qt::CaseInsensitive)) {
|
||||
_webSurface->setMaxFps(YOUTUBE_MAX_FPS);
|
||||
} else {
|
||||
_webSurface->setMaxFps(DEFAULT_MAX_FPS);
|
||||
}
|
||||
_webSurface->load("controls/WebEntityView.qml", [this](QQmlContext* context, QObject* item) {
|
||||
item->setProperty(URL_PROPERTY, _lastSourceUrl);
|
||||
});
|
||||
} else if (_contentType == ContentType::QmlContent) {
|
||||
_webSurface->load(_lastSourceUrl);
|
||||
}
|
||||
WebEntityRenderer::acquireWebSurface(newSourceURL, _contentType == ContentType::HtmlContent, _webSurface, _cachedWebSurface);
|
||||
_fadeStartTime = usecTimestampNow();
|
||||
_webSurface->resume();
|
||||
|
||||
return _webSurface->getRootItem();
|
||||
_connections.push_back(QObject::connect(this, &WebEntityRenderer::scriptEventReceived, _webSurface.data(), &OffscreenQmlSurface::emitScriptEvent));
|
||||
_connections.push_back(QObject::connect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, this, &WebEntityRenderer::webEventReceived));
|
||||
const EntityItemID entityItemID = entity->getID();
|
||||
_connections.push_back(QObject::connect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, this, [entityItemID](const QVariant& message) {
|
||||
emit DependencyManager::get<EntityScriptingInterface>()->webEventReceived(entityItemID, message);
|
||||
}));
|
||||
}
|
||||
|
||||
void WebEntityRenderer::destroyWebSurface() {
|
||||
QSharedPointer<OffscreenQmlSurface> webSurface;
|
||||
ContentType contentType{ ContentType::NoContent };
|
||||
ContentType contentType = ContentType::NoContent;
|
||||
withWriteLock([&] {
|
||||
webSurface.swap(_webSurface);
|
||||
std::swap(contentType, _contentType);
|
||||
_contentType = contentType;
|
||||
});
|
||||
|
||||
if (webSurface) {
|
||||
--_currentWebCount;
|
||||
QQuickItem* rootItem = webSurface->getRootItem();
|
||||
|
||||
// Fix for crash in QtWebEngineCore when rapidly switching domains
|
||||
// Call stop on the QWebEngineView before destroying OffscreenQMLSurface.
|
||||
if (rootItem && contentType == ContentType::HtmlContent) {
|
||||
// stop loading
|
||||
QMetaObject::invokeMethod(rootItem, "stop");
|
||||
}
|
||||
|
||||
webSurface->pause();
|
||||
webSurface.reset();
|
||||
WebEntityRenderer::releaseWebSurface(webSurface, _cachedWebSurface, _connections);
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec2 WebEntityRenderer::getWindowSize(const TypedEntityPointer& entity) const {
|
||||
glm::vec2 dims = glm::vec2(entity->getScaledDimensions());
|
||||
dims *= METERS_TO_INCHES * _lastDPI;
|
||||
dims *= METERS_TO_INCHES * _dpi;
|
||||
|
||||
// ensure no side is never larger then MAX_WINDOW_SIZE
|
||||
float max = (dims.x > dims.y) ? dims.x : dims.y;
|
||||
|
@ -358,29 +352,84 @@ glm::vec2 WebEntityRenderer::getWindowSize(const TypedEntityPointer& entity) con
|
|||
}
|
||||
|
||||
void WebEntityRenderer::hoverEnterEntity(const PointerEvent& event) {
|
||||
if (_webSurface) {
|
||||
if (_inputMode == WebInputMode::MOUSE) {
|
||||
handlePointerEvent(event);
|
||||
} else if (_webSurface) {
|
||||
qDebug() << "boop5" << this << _webSurface << _webSurface->getRootItem();
|
||||
PointerEvent webEvent = event;
|
||||
webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _lastDPI));
|
||||
webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _dpi));
|
||||
_webSurface->hoverBeginEvent(webEvent, _touchDevice);
|
||||
}
|
||||
}
|
||||
|
||||
void WebEntityRenderer::hoverLeaveEntity(const PointerEvent& event) {
|
||||
if (_webSurface) {
|
||||
if (_inputMode == WebInputMode::MOUSE) {
|
||||
PointerEvent endEvent(PointerEvent::Release, event.getID(), event.getPos2D(), event.getPos3D(), event.getNormal(), event.getDirection(),
|
||||
event.getButton(), event.getButtons(), event.getKeyboardModifiers());
|
||||
handlePointerEvent(endEvent);
|
||||
// QML onReleased is only triggered if a click has happened first. We need to send this "fake" mouse move event to properly trigger an onExited.
|
||||
PointerEvent endMoveEvent(PointerEvent::Move, event.getID());
|
||||
handlePointerEvent(endMoveEvent);
|
||||
} else if (_webSurface) {
|
||||
PointerEvent webEvent = event;
|
||||
webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _lastDPI));
|
||||
webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _dpi));
|
||||
_webSurface->hoverEndEvent(webEvent, _touchDevice);
|
||||
}
|
||||
}
|
||||
|
||||
void WebEntityRenderer::handlePointerEvent(const PointerEvent& event) {
|
||||
if (_inputMode == WebInputMode::TOUCH) {
|
||||
handlePointerEventAsTouch(event);
|
||||
} else {
|
||||
handlePointerEventAsMouse(event);
|
||||
}
|
||||
}
|
||||
|
||||
void WebEntityRenderer::handlePointerEventAsTouch(const PointerEvent& event) {
|
||||
if (_webSurface) {
|
||||
PointerEvent webEvent = event;
|
||||
webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _lastDPI));
|
||||
webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _dpi));
|
||||
_webSurface->handlePointerEvent(webEvent, _touchDevice);
|
||||
}
|
||||
}
|
||||
|
||||
void WebEntityRenderer::handlePointerEventAsMouse(const PointerEvent& event) {
|
||||
if (!_webSurface) {
|
||||
return;
|
||||
}
|
||||
|
||||
glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _dpi);
|
||||
QPointF windowPoint(windowPos.x, windowPos.y);
|
||||
|
||||
Qt::MouseButtons buttons = Qt::NoButton;
|
||||
if (event.getButtons() & PointerEvent::PrimaryButton) {
|
||||
buttons |= Qt::LeftButton;
|
||||
}
|
||||
|
||||
Qt::MouseButton button = Qt::NoButton;
|
||||
if (event.getButton() == PointerEvent::PrimaryButton) {
|
||||
button = Qt::LeftButton;
|
||||
}
|
||||
|
||||
QEvent::Type type;
|
||||
switch (event.getType()) {
|
||||
case PointerEvent::Press:
|
||||
type = QEvent::MouseButtonPress;
|
||||
break;
|
||||
case PointerEvent::Release:
|
||||
type = QEvent::MouseButtonRelease;
|
||||
break;
|
||||
case PointerEvent::Move:
|
||||
type = QEvent::MouseMove;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
QMouseEvent mouseEvent(type, windowPoint, windowPoint, windowPoint, button, buttons, event.getKeyboardModifiers());
|
||||
QCoreApplication::sendEvent(_webSurface->getWindow(), &mouseEvent);
|
||||
}
|
||||
|
||||
void WebEntityRenderer::setProxyWindow(QWindow* proxyWindow) {
|
||||
if (_webSurface) {
|
||||
_webSurface->setProxyWindow(proxyWindow);
|
||||
|
@ -394,8 +443,6 @@ QObject* WebEntityRenderer::getEventHandler() {
|
|||
return _webSurface->getEventHandler();
|
||||
}
|
||||
|
||||
bool WebEntityRenderer::isTransparent() const {
|
||||
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
|
||||
return fadeRatio < OPAQUE_ALPHA_THRESHOLD;
|
||||
}
|
||||
|
||||
void WebEntityRenderer::emitScriptEvent(const QVariant& message) {
|
||||
QMetaObject::invokeMethod(this, "scriptEventReceived", Q_ARG(QVariant, message));
|
||||
}
|
|
@ -24,13 +24,30 @@ class WebEntityRenderer : public TypedEntityRenderer<WebEntityItem> {
|
|||
|
||||
public:
|
||||
WebEntityRenderer(const EntityItemPointer& entity);
|
||||
~WebEntityRenderer();
|
||||
|
||||
Q_INVOKABLE void hoverEnterEntity(const PointerEvent& event);
|
||||
Q_INVOKABLE void hoverLeaveEntity(const PointerEvent& event);
|
||||
Q_INVOKABLE void handlePointerEvent(const PointerEvent& event);
|
||||
|
||||
static const QString QML;
|
||||
static const char* URL_PROPERTY;
|
||||
|
||||
static void setAcquireWebSurfaceOperator(std::function<void(const QString&, bool, QSharedPointer<OffscreenQmlSurface>&, bool&)> acquireWebSurfaceOperator) { _acquireWebSurfaceOperator = acquireWebSurfaceOperator; }
|
||||
static void acquireWebSurface(const QString& url, bool htmlContent, QSharedPointer<OffscreenQmlSurface>& webSurface, bool& cachedWebSurface) {
|
||||
if (_acquireWebSurfaceOperator) {
|
||||
_acquireWebSurfaceOperator(url, htmlContent, webSurface, cachedWebSurface);
|
||||
}
|
||||
}
|
||||
|
||||
static void setReleaseWebSurfaceOperator(std::function<void(QSharedPointer<OffscreenQmlSurface>&, bool&, std::vector<QMetaObject::Connection>&)> releaseWebSurfaceOperator) { _releaseWebSurfaceOperator = releaseWebSurfaceOperator; }
|
||||
static void releaseWebSurface(QSharedPointer<OffscreenQmlSurface>& webSurface, bool& cachedWebSurface, std::vector<QMetaObject::Connection>& connections) {
|
||||
if (_releaseWebSurfaceOperator) {
|
||||
_releaseWebSurfaceOperator(webSurface, cachedWebSurface, connections);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void onRemoveFromSceneTyped(const TypedEntityPointer& entity) override;
|
||||
virtual bool needsRenderUpdate() const override;
|
||||
virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override;
|
||||
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;
|
||||
|
@ -39,40 +56,59 @@ protected:
|
|||
|
||||
virtual bool wantsHandControllerPointerEvents() const override { return true; }
|
||||
virtual bool wantsKeyboardFocus() const override { return true; }
|
||||
|
||||
virtual void setProxyWindow(QWindow* proxyWindow) override;
|
||||
virtual QObject* getEventHandler() override;
|
||||
|
||||
void handlePointerEventAsTouch(const PointerEvent& event);
|
||||
void handlePointerEventAsMouse(const PointerEvent& event);
|
||||
|
||||
private:
|
||||
void onTimeout();
|
||||
bool buildWebSurface(const TypedEntityPointer& entity);
|
||||
void buildWebSurface(const EntityItemPointer& entity, const QString& newSourceURL);
|
||||
void destroyWebSurface();
|
||||
bool hasWebSurface();
|
||||
glm::vec2 getWindowSize(const TypedEntityPointer& entity) const;
|
||||
|
||||
|
||||
int _geometryId{ 0 };
|
||||
enum class ContentType {
|
||||
NoContent,
|
||||
HtmlContent,
|
||||
QmlContent
|
||||
};
|
||||
|
||||
static ContentType getContentType(const QString& urlString);
|
||||
ContentType _contentType { ContentType::NoContent };
|
||||
|
||||
ContentType _contentType{ ContentType::NoContent };
|
||||
QSharedPointer<OffscreenQmlSurface> _webSurface;
|
||||
glm::vec3 _contextPosition;
|
||||
QSharedPointer<OffscreenQmlSurface> _webSurface { nullptr };
|
||||
bool _cachedWebSurface { false };
|
||||
gpu::TexturePointer _texture;
|
||||
QString _lastSourceUrl;
|
||||
uint16_t _lastDPI;
|
||||
|
||||
glm::u8vec3 _color;
|
||||
float _alpha { 1.0f };
|
||||
|
||||
QString _sourceURL;
|
||||
uint16_t _dpi;
|
||||
QString _scriptURL;
|
||||
uint8_t _maxFPS;
|
||||
WebInputMode _inputMode;
|
||||
|
||||
glm::vec3 _contextPosition;
|
||||
|
||||
QTimer _timer;
|
||||
uint64_t _lastRenderTime { 0 };
|
||||
|
||||
std::vector<QMetaObject::Connection> _connections;
|
||||
|
||||
static std::function<void(QString, bool, QSharedPointer<OffscreenQmlSurface>&, bool&)> _acquireWebSurfaceOperator;
|
||||
static std::function<void(QSharedPointer<OffscreenQmlSurface>&, bool&, std::vector<QMetaObject::Connection>&)> _releaseWebSurfaceOperator;
|
||||
|
||||
public slots:
|
||||
void emitScriptEvent(const QVariant& scriptMessage);
|
||||
|
||||
signals:
|
||||
void scriptEventReceived(const QVariant& message);
|
||||
void webEventReceived(const QVariant& message);
|
||||
};
|
||||
|
||||
} } // namespace
|
||||
|
||||
#if 0
|
||||
virtual void emitScriptEvent(const QVariant& message) override;
|
||||
#endif
|
||||
} }
|
||||
|
||||
#endif // hifi_RenderableWebEntityItem_h
|
||||
|
|
|
@ -95,6 +95,9 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
|
|||
requestedProperties += PROP_QUERY_AA_CUBE;
|
||||
requestedProperties += PROP_CAN_CAST_SHADOW;
|
||||
// requestedProperties += PROP_VISIBLE_IN_SECONDARY_CAMERA; // not sent over the wire
|
||||
requestedProperties += PROP_RENDER_LAYER;
|
||||
requestedProperties += PROP_PRIMITIVE_MODE;
|
||||
requestedProperties += PROP_IGNORE_PICK_INTERSECTION;
|
||||
withReadLock([&] {
|
||||
requestedProperties += _grabProperties.getEntityProperties(params);
|
||||
});
|
||||
|
@ -263,8 +266,8 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
|
|||
APPEND_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, getRegistrationPoint());
|
||||
APPEND_ENTITY_PROPERTY(PROP_CREATED, getCreated());
|
||||
APPEND_ENTITY_PROPERTY(PROP_LAST_EDITED_BY, getLastEditedBy());
|
||||
// APPEND_ENTITY_PROPERTY(PROP_ENTITY_HOST_TYPE, getEntityHostType()); // not sent over the wire
|
||||
// APPEND_ENTITY_PROPERTY(PROP_OWNING_AVATAR_ID, getOwningAvatarID()); // not sent over the wire
|
||||
// APPEND_ENTITY_PROPERTY(PROP_ENTITY_HOST_TYPE, (uint32_t)getEntityHostType()); // not sent over the wire
|
||||
// APPEND_ENTITY_PROPERTY(PROP_OWNING_AVATAR_ID, getOwningAvatarID()); // not sent over the wire
|
||||
// convert AVATAR_SELF_ID to actual sessionUUID.
|
||||
QUuid actualParentID = getParentID();
|
||||
if (actualParentID == AVATAR_SELF_ID) {
|
||||
|
@ -276,6 +279,9 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
|
|||
APPEND_ENTITY_PROPERTY(PROP_QUERY_AA_CUBE, getQueryAACube());
|
||||
APPEND_ENTITY_PROPERTY(PROP_CAN_CAST_SHADOW, getCanCastShadow());
|
||||
// APPEND_ENTITY_PROPERTY(PROP_VISIBLE_IN_SECONDARY_CAMERA, getIsVisibleInSecondaryCamera()); // not sent over the wire
|
||||
APPEND_ENTITY_PROPERTY(PROP_RENDER_LAYER, (uint32_t)getRenderLayer());
|
||||
APPEND_ENTITY_PROPERTY(PROP_PRIMITIVE_MODE, (uint32_t)getPrimitiveMode());
|
||||
APPEND_ENTITY_PROPERTY(PROP_IGNORE_PICK_INTERSECTION, getIgnorePickIntersection());
|
||||
withReadLock([&] {
|
||||
_grabProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties,
|
||||
propertyFlags, propertiesDidntFit, propertyCount, appendState);
|
||||
|
@ -842,6 +848,9 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
}
|
||||
READ_ENTITY_PROPERTY(PROP_CAN_CAST_SHADOW, bool, setCanCastShadow);
|
||||
// READ_ENTITY_PROPERTY(PROP_VISIBLE_IN_SECONDARY_CAMERA, bool, setIsVisibleInSecondaryCamera); // not sent over the wire
|
||||
READ_ENTITY_PROPERTY(PROP_RENDER_LAYER, RenderLayer, setRenderLayer);
|
||||
READ_ENTITY_PROPERTY(PROP_PRIMITIVE_MODE, PrimitiveMode, setPrimitiveMode);
|
||||
READ_ENTITY_PROPERTY(PROP_IGNORE_PICK_INTERSECTION, bool, setIgnorePickIntersection);
|
||||
withWriteLock([&] {
|
||||
int bytesFromGrab = _grabProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
|
||||
propertyFlags, overwriteLocalData,
|
||||
|
@ -1313,6 +1322,9 @@ EntityItemProperties EntityItem::getProperties(const EntityPropertyFlags& desire
|
|||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(queryAACube, getQueryAACube);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(canCastShadow, getCanCastShadow);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(isVisibleInSecondaryCamera, isVisibleInSecondaryCamera);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(renderLayer, getRenderLayer);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(primitiveMode, getPrimitiveMode);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(ignorePickIntersection, getIgnorePickIntersection);
|
||||
withReadLock([&] {
|
||||
_grabProperties.getProperties(properties);
|
||||
});
|
||||
|
@ -1457,6 +1469,9 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(queryAACube, setQueryAACube);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(canCastShadow, setCanCastShadow);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(isVisibleInSecondaryCamera, setIsVisibleInSecondaryCamera);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(renderLayer, setRenderLayer);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(primitiveMode, setPrimitiveMode);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(ignorePickIntersection, setIgnorePickIntersection);
|
||||
withWriteLock([&] {
|
||||
bool grabPropertiesChanged = _grabProperties.setProperties(properties);
|
||||
somethingChanged |= grabPropertiesChanged;
|
||||
|
@ -2934,6 +2949,58 @@ void EntityItem::setIsVisibleInSecondaryCamera(bool value) {
|
|||
}
|
||||
}
|
||||
|
||||
RenderLayer EntityItem::getRenderLayer() const {
|
||||
return resultWithReadLock<RenderLayer>([&] {
|
||||
return _renderLayer;
|
||||
});
|
||||
}
|
||||
|
||||
void EntityItem::setRenderLayer(RenderLayer value) {
|
||||
bool changed = false;
|
||||
withWriteLock([&] {
|
||||
if (_renderLayer != value) {
|
||||
changed = true;
|
||||
_renderLayer = value;
|
||||
}
|
||||
});
|
||||
|
||||
if (changed) {
|
||||
emit requestRenderUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
PrimitiveMode EntityItem::getPrimitiveMode() const {
|
||||
return resultWithReadLock<PrimitiveMode>([&] {
|
||||
return _primitiveMode;
|
||||
});
|
||||
}
|
||||
|
||||
void EntityItem::setPrimitiveMode(PrimitiveMode value) {
|
||||
bool changed = false;
|
||||
withWriteLock([&] {
|
||||
if (_primitiveMode != value) {
|
||||
changed = true;
|
||||
_primitiveMode = value;
|
||||
}
|
||||
});
|
||||
|
||||
if (changed) {
|
||||
emit requestRenderUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
bool EntityItem::getIgnorePickIntersection() const {
|
||||
return resultWithReadLock<bool>([&] {
|
||||
return _ignorePickIntersection;
|
||||
});
|
||||
}
|
||||
|
||||
void EntityItem::setIgnorePickIntersection(bool value) {
|
||||
withWriteLock([&] {
|
||||
_ignorePickIntersection = value;
|
||||
});
|
||||
}
|
||||
|
||||
bool EntityItem::getCanCastShadow() const {
|
||||
bool result;
|
||||
withReadLock([&] {
|
||||
|
|
|
@ -293,6 +293,15 @@ public:
|
|||
bool isVisibleInSecondaryCamera() const;
|
||||
void setIsVisibleInSecondaryCamera(bool value);
|
||||
|
||||
RenderLayer getRenderLayer() const;
|
||||
void setRenderLayer(RenderLayer value);
|
||||
|
||||
PrimitiveMode getPrimitiveMode() const;
|
||||
void setPrimitiveMode(PrimitiveMode value);
|
||||
|
||||
bool getIgnorePickIntersection() const;
|
||||
void setIgnorePickIntersection(bool value);
|
||||
|
||||
bool getCanCastShadow() const;
|
||||
void setCanCastShadow(bool value);
|
||||
|
||||
|
@ -621,7 +630,10 @@ protected:
|
|||
float _angularDamping { ENTITY_ITEM_DEFAULT_ANGULAR_DAMPING };
|
||||
bool _visible { ENTITY_ITEM_DEFAULT_VISIBLE };
|
||||
bool _isVisibleInSecondaryCamera { ENTITY_ITEM_DEFAULT_VISIBLE_IN_SECONDARY_CAMERA };
|
||||
RenderLayer _renderLayer { RenderLayer::WORLD };
|
||||
PrimitiveMode _primitiveMode { PrimitiveMode::SOLID };
|
||||
bool _canCastShadow{ ENTITY_ITEM_DEFAULT_CAN_CAST_SHADOW };
|
||||
bool _ignorePickIntersection { false };
|
||||
bool _collisionless { ENTITY_ITEM_DEFAULT_COLLISIONLESS };
|
||||
uint16_t _collisionMask { ENTITY_COLLISION_MASK_DEFAULT };
|
||||
bool _dynamic { ENTITY_ITEM_DEFAULT_DYNAMIC };
|
||||
|
|
|
@ -340,17 +340,96 @@ QString EntityItemProperties::getBillboardModeAsString() const {
|
|||
return BillboardModeHelpers::getNameForBillboardMode(_billboardMode);
|
||||
}
|
||||
|
||||
void EntityItemProperties::setBillboardModeFromString(const QString& materialMappingMode) {
|
||||
void EntityItemProperties::setBillboardModeFromString(const QString& billboardMode) {
|
||||
if (stringToBillboardModeLookup.empty()) {
|
||||
buildStringToBillboardModeLookup();
|
||||
}
|
||||
auto billboardModeItr = stringToBillboardModeLookup.find(materialMappingMode.toLower());
|
||||
auto billboardModeItr = stringToBillboardModeLookup.find(billboardMode.toLower());
|
||||
if (billboardModeItr != stringToBillboardModeLookup.end()) {
|
||||
_billboardMode = billboardModeItr.value();
|
||||
_billboardModeChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
QHash<QString, RenderLayer> stringToRenderLayerLookup;
|
||||
|
||||
void addRenderLayer(RenderLayer mode) {
|
||||
stringToRenderLayerLookup[RenderLayerHelpers::getNameForRenderLayer(mode)] = mode;
|
||||
}
|
||||
|
||||
void buildStringToRenderLayerLookup() {
|
||||
addRenderLayer(RenderLayer::WORLD);
|
||||
addRenderLayer(RenderLayer::FRONT);
|
||||
addRenderLayer(RenderLayer::HUD);
|
||||
}
|
||||
|
||||
QString EntityItemProperties::getRenderLayerAsString() const {
|
||||
return RenderLayerHelpers::getNameForRenderLayer(_renderLayer);
|
||||
}
|
||||
|
||||
void EntityItemProperties::setRenderLayerFromString(const QString& renderLayer) {
|
||||
if (stringToRenderLayerLookup.empty()) {
|
||||
buildStringToRenderLayerLookup();
|
||||
}
|
||||
auto renderLayerItr = stringToRenderLayerLookup.find(renderLayer.toLower());
|
||||
if (renderLayerItr != stringToRenderLayerLookup.end()) {
|
||||
_renderLayer = renderLayerItr.value();
|
||||
_renderLayerChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
QHash<QString, PrimitiveMode> stringToPrimitiveModeLookup;
|
||||
|
||||
void addPrimitiveMode(PrimitiveMode mode) {
|
||||
stringToPrimitiveModeLookup[PrimitiveModeHelpers::getNameForPrimitiveMode(mode)] = mode;
|
||||
}
|
||||
|
||||
void buildStringToPrimitiveModeLookup() {
|
||||
addPrimitiveMode(PrimitiveMode::SOLID);
|
||||
addPrimitiveMode(PrimitiveMode::LINES);
|
||||
}
|
||||
|
||||
QString EntityItemProperties::getPrimitiveModeAsString() const {
|
||||
return PrimitiveModeHelpers::getNameForPrimitiveMode(_primitiveMode);
|
||||
}
|
||||
|
||||
void EntityItemProperties::setPrimitiveModeFromString(const QString& primitiveMode) {
|
||||
if (stringToPrimitiveModeLookup.empty()) {
|
||||
buildStringToPrimitiveModeLookup();
|
||||
}
|
||||
auto primitiveModeItr = stringToPrimitiveModeLookup.find(primitiveMode.toLower());
|
||||
if (primitiveModeItr != stringToPrimitiveModeLookup.end()) {
|
||||
_primitiveMode = primitiveModeItr.value();
|
||||
_primitiveModeChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
QHash<QString, WebInputMode> stringToWebInputModeLookup;
|
||||
|
||||
void addWebInputMode(WebInputMode mode) {
|
||||
stringToWebInputModeLookup[WebInputModeHelpers::getNameForWebInputMode(mode)] = mode;
|
||||
}
|
||||
|
||||
void buildStringToWebInputModeLookup() {
|
||||
addWebInputMode(WebInputMode::TOUCH);
|
||||
addWebInputMode(WebInputMode::MOUSE);
|
||||
}
|
||||
|
||||
QString EntityItemProperties::getInputModeAsString() const {
|
||||
return WebInputModeHelpers::getNameForWebInputMode(_inputMode);
|
||||
}
|
||||
|
||||
void EntityItemProperties::setInputModeFromString(const QString& webInputMode) {
|
||||
if (stringToWebInputModeLookup.empty()) {
|
||||
buildStringToWebInputModeLookup();
|
||||
}
|
||||
auto webInputModeItr = stringToWebInputModeLookup.find(webInputMode.toLower());
|
||||
if (webInputModeItr != stringToWebInputModeLookup.end()) {
|
||||
_inputMode = webInputModeItr.value();
|
||||
_inputModeChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
||||
EntityPropertyFlags changedProperties;
|
||||
|
||||
|
@ -375,6 +454,9 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
CHECK_PROPERTY_CHANGE(PROP_QUERY_AA_CUBE, queryAACube);
|
||||
CHECK_PROPERTY_CHANGE(PROP_CAN_CAST_SHADOW, canCastShadow);
|
||||
CHECK_PROPERTY_CHANGE(PROP_VISIBLE_IN_SECONDARY_CAMERA, isVisibleInSecondaryCamera);
|
||||
CHECK_PROPERTY_CHANGE(PROP_RENDER_LAYER, renderLayer);
|
||||
CHECK_PROPERTY_CHANGE(PROP_PRIMITIVE_MODE, primitiveMode);
|
||||
CHECK_PROPERTY_CHANGE(PROP_IGNORE_PICK_INTERSECTION, ignorePickIntersection);
|
||||
changedProperties += _grab.getChangedProperties();
|
||||
|
||||
// Physics
|
||||
|
@ -474,6 +556,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
CHECK_PROPERTY_CHANGE(PROP_JOINT_TRANSLATIONS_SET, jointTranslationsSet);
|
||||
CHECK_PROPERTY_CHANGE(PROP_JOINT_TRANSLATIONS, jointTranslations);
|
||||
CHECK_PROPERTY_CHANGE(PROP_RELAY_PARENT_JOINTS, relayParentJoints);
|
||||
CHECK_PROPERTY_CHANGE(PROP_GROUP_CULLED, groupCulled);
|
||||
changedProperties += _animation.getChangedProperties();
|
||||
|
||||
// Light
|
||||
|
@ -528,6 +611,9 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
// Web
|
||||
CHECK_PROPERTY_CHANGE(PROP_SOURCE_URL, sourceUrl);
|
||||
CHECK_PROPERTY_CHANGE(PROP_DPI, dpi);
|
||||
CHECK_PROPERTY_CHANGE(PROP_SCRIPT_URL, scriptURL);
|
||||
CHECK_PROPERTY_CHANGE(PROP_MAX_FPS, maxFPS);
|
||||
CHECK_PROPERTY_CHANGE(PROP_INPUT_MODE, inputMode);
|
||||
|
||||
// Polyline
|
||||
CHECK_PROPERTY_CHANGE(PROP_LINE_POINTS, linePoints);
|
||||
|
@ -579,15 +665,15 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* the <code>shape</code> property set for entities of these types.) <em>Read-only.</em>
|
||||
* @property {EntityHostType} entityHostType="domain" - How this entity will behave, including if and how it is sent to other people.
|
||||
* The value can only be set at entity creation by using the <code>entityHostType</code> parameter in
|
||||
* {@link Entities.addEntity}.
|
||||
* {@link Entities.addEntity}. Read-only.
|
||||
* @property {boolean} avatarEntity=false - If <code>true</code> then the entity is an avatar entity; An avatar entity follows you to each domain you visit,
|
||||
* rendering at the same world coordinates unless it's parented to your avatar. <em>Value cannot be changed after the entity is created.</em><br />
|
||||
* The value can only be set at entity creation by using the <code>entityHostType</code> parameter in
|
||||
* {@link Entities.addEntity}. <code>clientOnly</code> is an alias.
|
||||
* {@link Entities.addEntity}. <code>clientOnly</code> is an alias. Read-only.
|
||||
* @property {boolean} localEntity=false - If <code>true</code> then the entity is a local entity; Local entities only render for you and are not sent over the wire.
|
||||
* <em>Value cannot be changed after the entity is created.</em><br />
|
||||
* The value can only be set at entity creation by using the <code>entityHostType</code> parameter in
|
||||
* {@link Entities.addEntity}.
|
||||
* {@link Entities.addEntity}. Read-only.
|
||||
* @property {Uuid} owningAvatarID=Uuid.NULL - The session ID of the owning avatar if <code>avatarEntity</code> is
|
||||
* <code>true</code>, otherwise {@link Uuid|Uuid.NULL}. <em>Read-only.</em>
|
||||
*
|
||||
|
@ -611,6 +697,9 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* {@link Entities.EntityType|Zone} entity with <code>castShadows</code> enabled in its
|
||||
* {@link Entities.EntityProperties-Zone|keyLight} property.
|
||||
* @property {boolean} isVisibleInSecondaryCamera=true - Whether or not the entity is rendered in the secondary camera. If <code>true</code> then the entity is rendered.
|
||||
* @property {RenderLayer} renderLayer="world" - In which layer this entity renders.
|
||||
* @property {PrimitiveMode} primitiveMode="solid" - How this entity's geometry is rendered.
|
||||
* @property {boolean} ignorePickIntersection=false - If <code>true</code>, picks ignore the entity.
|
||||
*
|
||||
* @property {Vec3} position=0,0,0 - The position of the entity.
|
||||
* @property {Quat} rotation=0,0,0,1 - The orientation of the entity with respect to world coordinates.
|
||||
|
@ -904,7 +993,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* parse the JSON string into a JavaScript object of name, URL pairs. <em>Read-only.</em>
|
||||
*
|
||||
* @property {ShapeType} shapeType="none" - The shape of the collision hull used if collisions are enabled.
|
||||
* @property {string} compoundShapeURL="" - The OBJ file to use for the compound shape if <code>shapeType</code> is
|
||||
* @property {string} compoundShapeURL="" - The model file to use for the compound shape if <code>shapeType</code> is
|
||||
* <code>"compound"</code>.
|
||||
*
|
||||
* @property {Entities.AnimationProperties} animation - An animation to play on the model.
|
||||
|
@ -929,6 +1018,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* {@link Entities.getJointIndex|getJointIndex}.
|
||||
* @property {boolean} relayParentJoints=false - If <code>true</code> and the entity is parented to an avatar, then the
|
||||
* avatar's joint rotations are applied to the entity's joints.
|
||||
* @property {boolean} groupCulled=false - If <code>true</code>, the mesh parts of the model are LOD culled as a group.
|
||||
* If <code>false</code>, separate mesh parts will be LOD culled individually.
|
||||
*
|
||||
* @example <caption>Rez a Vive tracker puck.</caption>
|
||||
* var entity = Entities.addEntity({
|
||||
|
@ -1209,11 +1300,16 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* The entity has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}.
|
||||
* @typedef {object} Entities.EntityProperties-Web
|
||||
* @property {Vec3} dimensions=0.1,0.1,0.01 - The dimensions of the entity.
|
||||
* @property {Color} color=255,255,255 - The color of the web surface.
|
||||
* @property {number} alpha=1 - The alpha of the web surface.
|
||||
* @property {string} sourceUrl="" - The URL of the Web page to display. This value does not change as you or others navigate
|
||||
* on the Web entity.
|
||||
* @property {number} dpi=30 - The resolution to display the page at, in dots per inch. If you convert this to dots per meter
|
||||
* (multiply by 1 / 0.0254 = 39.3701) then multiply <code>dimensions.x</code> and <code>dimensions.y</code> by that value
|
||||
* you get the resolution in pixels.
|
||||
* @property {string} scriptURL="" - The URL of a JavaScript file to inject into the Web page.
|
||||
* @property {number} maxFPS=10 - The maximum update rate for the Web content, in frames/second.
|
||||
* @property {WebInputMode} inputMode="touch" - The user input mode to use.
|
||||
* @example <caption>Create a Web entity displaying at 1920 x 1080 resolution.</caption>
|
||||
* var METERS_TO_INCHES = 39.3701;
|
||||
* var entity = Entities.addEntity({
|
||||
|
@ -1242,7 +1338,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* @property {ShapeType} shapeType="box" - The shape of the volume in which the zone's lighting effects and avatar
|
||||
* permissions have effect. Reverts to the default value if set to <code>"none"</code>, or set to <code>"compound"</code>
|
||||
* and <code>compoundShapeURL</code> is <code>""</code>.
|
||||
* @property {string} compoundShapeURL="" - The OBJ file to use for the compound shape if <code>shapeType</code> is
|
||||
* @property {string} compoundShapeURL="" - The model file to use for the compound shape if <code>shapeType</code> is
|
||||
* <code>"compound"</code>.
|
||||
*
|
||||
* @property {string} keyLightMode="inherit" - Configures the key light in the zone. Possible values:<br />
|
||||
|
@ -1420,6 +1516,9 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_QUERY_AA_CUBE, queryAACube);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CAN_CAST_SHADOW, canCastShadow);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_VISIBLE_IN_SECONDARY_CAMERA, isVisibleInSecondaryCamera);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_RENDER_LAYER, renderLayer, getRenderLayerAsString());
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_PRIMITIVE_MODE, primitiveMode, getPrimitiveModeAsString());
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_IGNORE_PICK_INTERSECTION, ignorePickIntersection);
|
||||
_grab.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties);
|
||||
|
||||
// Physics
|
||||
|
@ -1536,6 +1635,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_JOINT_TRANSLATIONS_SET, jointTranslationsSet);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_JOINT_TRANSLATIONS, jointTranslations);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RELAY_PARENT_JOINTS, relayParentJoints);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_GROUP_CULLED, groupCulled);
|
||||
if (!psuedoPropertyFlagsButDesiredEmpty) {
|
||||
_animation.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties);
|
||||
}
|
||||
|
@ -1606,8 +1706,14 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
|
||||
// Web only
|
||||
if (_type == EntityTypes::Web) {
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha);
|
||||
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOURCE_URL, sourceUrl);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_DPI, dpi);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SCRIPT_URL, scriptURL);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MAX_FPS, maxFPS);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_INPUT_MODE, inputMode, getInputModeAsString());
|
||||
}
|
||||
|
||||
// PolyVoxel only
|
||||
|
@ -1801,6 +1907,9 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
|
|||
COPY_PROPERTY_FROM_QSCRIPTVALUE(queryAACube, AACube, setQueryAACube); // TODO: should scripts be able to set this?
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(canCastShadow, bool, setCanCastShadow);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(isVisibleInSecondaryCamera, bool, setIsVisibleInSecondaryCamera);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(renderLayer, RenderLayer);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(primitiveMode, PrimitiveMode);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(ignorePickIntersection, bool, setIgnorePickIntersection);
|
||||
_grab.copyFromScriptValue(object, _defaultSettings);
|
||||
|
||||
// Physics
|
||||
|
@ -1905,6 +2014,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
|
|||
COPY_PROPERTY_FROM_QSCRIPTVALUE(jointTranslationsSet, qVectorBool, setJointTranslationsSet);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(jointTranslations, qVectorVec3, setJointTranslations);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(relayParentJoints, bool, setRelayParentJoints);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(groupCulled, bool, setGroupCulled);
|
||||
_animation.copyFromScriptValue(object, _defaultSettings);
|
||||
|
||||
// Light
|
||||
|
@ -1959,6 +2069,9 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
|
|||
// Web
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(sourceUrl, QString, setSourceUrl);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(dpi, uint16_t, setDPI);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(scriptURL, QString, setScriptURL);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(maxFPS, uint8_t, setMaxFPS);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(inputMode, InputMode);
|
||||
|
||||
// Polyline
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(linePoints, qVectorVec3, setLinePoints);
|
||||
|
@ -2072,6 +2185,9 @@ void EntityItemProperties::merge(const EntityItemProperties& other) {
|
|||
COPY_PROPERTY_IF_CHANGED(queryAACube);
|
||||
COPY_PROPERTY_IF_CHANGED(canCastShadow);
|
||||
COPY_PROPERTY_IF_CHANGED(isVisibleInSecondaryCamera);
|
||||
COPY_PROPERTY_IF_CHANGED(renderLayer);
|
||||
COPY_PROPERTY_IF_CHANGED(primitiveMode);
|
||||
COPY_PROPERTY_IF_CHANGED(ignorePickIntersection);
|
||||
_grab.merge(other._grab);
|
||||
|
||||
// Physics
|
||||
|
@ -2171,6 +2287,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) {
|
|||
COPY_PROPERTY_IF_CHANGED(jointTranslationsSet);
|
||||
COPY_PROPERTY_IF_CHANGED(jointTranslations);
|
||||
COPY_PROPERTY_IF_CHANGED(relayParentJoints);
|
||||
COPY_PROPERTY_IF_CHANGED(groupCulled);
|
||||
_animation.merge(other._animation);
|
||||
|
||||
// Light
|
||||
|
@ -2225,6 +2342,9 @@ void EntityItemProperties::merge(const EntityItemProperties& other) {
|
|||
// Web
|
||||
COPY_PROPERTY_IF_CHANGED(sourceUrl);
|
||||
COPY_PROPERTY_IF_CHANGED(dpi);
|
||||
COPY_PROPERTY_IF_CHANGED(scriptURL);
|
||||
COPY_PROPERTY_IF_CHANGED(maxFPS);
|
||||
COPY_PROPERTY_IF_CHANGED(inputMode);
|
||||
|
||||
// Polyline
|
||||
COPY_PROPERTY_IF_CHANGED(linePoints);
|
||||
|
@ -2342,6 +2462,9 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr
|
|||
ADD_PROPERTY_TO_MAP(PROP_QUERY_AA_CUBE, QueryAACube, queryAACube, AACube);
|
||||
ADD_PROPERTY_TO_MAP(PROP_CAN_CAST_SHADOW, CanCastShadow, canCastShadow, bool);
|
||||
ADD_PROPERTY_TO_MAP(PROP_VISIBLE_IN_SECONDARY_CAMERA, IsVisibleInSecondaryCamera, isVisibleInSecondaryCamera, bool);
|
||||
ADD_PROPERTY_TO_MAP(PROP_RENDER_LAYER, RenderLayer, renderLayer, RenderLayer);
|
||||
ADD_PROPERTY_TO_MAP(PROP_PRIMITIVE_MODE, PrimitiveMode, primitiveMode, PrimitiveMode);
|
||||
ADD_PROPERTY_TO_MAP(PROP_IGNORE_PICK_INTERSECTION, IgnorePickIntersection, ignorePickIntersection, bool);
|
||||
{ // Grab
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_GRABBABLE, Grab, grab, Grabbable, grabbable);
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_KINEMATIC, Grab, grab, GrabKinematic, grabKinematic);
|
||||
|
@ -2495,6 +2618,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr
|
|||
ADD_PROPERTY_TO_MAP(PROP_JOINT_TRANSLATIONS_SET, JointTranslationsSet, jointTranslationsSet, QVector<bool>);
|
||||
ADD_PROPERTY_TO_MAP(PROP_JOINT_TRANSLATIONS, JointTranslations, jointTranslations, QVector<vec3>);
|
||||
ADD_PROPERTY_TO_MAP(PROP_RELAY_PARENT_JOINTS, RelayParentJoints, relayParentJoints, bool);
|
||||
ADD_PROPERTY_TO_MAP(PROP_GROUP_CULLED, GroupCulled, groupCulled, bool);
|
||||
{ // Animation
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_URL, Animation, animation, URL, url);
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_ALLOW_TRANSLATION, Animation, animation, AllowTranslation, allowTranslation);
|
||||
|
@ -2591,6 +2715,9 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr
|
|||
// Web
|
||||
ADD_PROPERTY_TO_MAP(PROP_SOURCE_URL, SourceUrl, sourceUrl, QString);
|
||||
ADD_PROPERTY_TO_MAP(PROP_DPI, DPI, dpi, uint16_t);
|
||||
ADD_PROPERTY_TO_MAP(PROP_SCRIPT_URL, ScriptURL, scriptURL, QString);
|
||||
ADD_PROPERTY_TO_MAP(PROP_MAX_FPS, MaxFPS, maxFPS, uint8_t);
|
||||
ADD_PROPERTY_TO_MAP(PROP_INPUT_MODE, InputMode, inputMode, WebInputMode);
|
||||
|
||||
// Polyline
|
||||
ADD_PROPERTY_TO_MAP(PROP_LINE_POINTS, LinePoints, linePoints, QVector<vec3>);
|
||||
|
@ -2771,6 +2898,9 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
|
|||
APPEND_ENTITY_PROPERTY(PROP_QUERY_AA_CUBE, properties.getQueryAACube());
|
||||
APPEND_ENTITY_PROPERTY(PROP_CAN_CAST_SHADOW, properties.getCanCastShadow());
|
||||
// APPEND_ENTITY_PROPERTY(PROP_VISIBLE_IN_SECONDARY_CAMERA, properties.getIsVisibleInSecondaryCamera()); // not sent over the wire
|
||||
APPEND_ENTITY_PROPERTY(PROP_RENDER_LAYER, (uint32_t)properties.getRenderLayer());
|
||||
APPEND_ENTITY_PROPERTY(PROP_PRIMITIVE_MODE, (uint32_t)properties.getPrimitiveMode());
|
||||
APPEND_ENTITY_PROPERTY(PROP_IGNORE_PICK_INTERSECTION, properties.getIgnorePickIntersection());
|
||||
_staticGrab.setProperties(properties);
|
||||
_staticGrab.appendToEditPacket(packetData, requestedProperties, propertyFlags,
|
||||
propertiesDidntFit, propertyCount, appendState);
|
||||
|
@ -2877,6 +3007,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
|
|||
APPEND_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS_SET, properties.getJointTranslationsSet());
|
||||
APPEND_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS, properties.getJointTranslations());
|
||||
APPEND_ENTITY_PROPERTY(PROP_RELAY_PARENT_JOINTS, properties.getRelayParentJoints());
|
||||
APPEND_ENTITY_PROPERTY(PROP_GROUP_CULLED, properties.getGroupCulled());
|
||||
|
||||
_staticAnimation.setProperties(properties);
|
||||
_staticAnimation.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState);
|
||||
|
@ -2951,8 +3082,14 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
|
|||
}
|
||||
|
||||
if (properties.getType() == EntityTypes::Web) {
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ALPHA, properties.getAlpha());
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, properties.getSourceUrl());
|
||||
APPEND_ENTITY_PROPERTY(PROP_DPI, properties.getDPI());
|
||||
APPEND_ENTITY_PROPERTY(PROP_SCRIPT_URL, properties.getScriptURL());
|
||||
APPEND_ENTITY_PROPERTY(PROP_MAX_FPS, properties.getMaxFPS());
|
||||
APPEND_ENTITY_PROPERTY(PROP_INPUT_MODE, (uint32_t)properties.getInputMode());
|
||||
}
|
||||
|
||||
if (properties.getType() == EntityTypes::Line) {
|
||||
|
@ -3212,6 +3349,9 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_QUERY_AA_CUBE, AACube, setQueryAACube);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CAN_CAST_SHADOW, bool, setCanCastShadow);
|
||||
// READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VISIBLE_IN_SECONDARY_CAMERA, bool, setIsVisibleInSecondaryCamera); // not sent over the wire
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RENDER_LAYER, RenderLayer, setRenderLayer);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PRIMITIVE_MODE, PrimitiveMode, setPrimitiveMode);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_IGNORE_PICK_INTERSECTION, bool, setIgnorePickIntersection);
|
||||
properties.getGrab().decodeFromEditPacket(propertyFlags, dataAt, processedBytes);
|
||||
|
||||
// Physics
|
||||
|
@ -3316,6 +3456,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_JOINT_TRANSLATIONS_SET, QVector<bool>, setJointTranslationsSet);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_JOINT_TRANSLATIONS, QVector<vec3>, setJointTranslations);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RELAY_PARENT_JOINTS, bool, setRelayParentJoints);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GROUP_CULLED, bool, setGroupCulled);
|
||||
|
||||
properties.getAnimation().decodeFromEditPacket(propertyFlags, dataAt, processedBytes);
|
||||
}
|
||||
|
@ -3381,8 +3522,14 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
}
|
||||
|
||||
if (properties.getType() == EntityTypes::Web) {
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA, float, setAlpha);
|
||||
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOURCE_URL, QString, setSourceUrl);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DPI, uint16_t, setDPI);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SCRIPT_URL, QString, setScriptURL);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MAX_FPS, uint8_t, setMaxFPS);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_INPUT_MODE, WebInputMode, setInputMode);
|
||||
}
|
||||
|
||||
if (properties.getType() == EntityTypes::Line) {
|
||||
|
@ -3594,6 +3741,9 @@ void EntityItemProperties::markAllChanged() {
|
|||
_queryAACubeChanged = true;
|
||||
_canCastShadowChanged = true;
|
||||
_isVisibleInSecondaryCameraChanged = true;
|
||||
_renderLayerChanged = true;
|
||||
_primitiveModeChanged = true;
|
||||
_ignorePickIntersectionChanged = true;
|
||||
_grab.markAllChanged();
|
||||
|
||||
// Physics
|
||||
|
@ -3686,6 +3836,7 @@ void EntityItemProperties::markAllChanged() {
|
|||
_jointTranslationsSetChanged = true;
|
||||
_jointTranslationsChanged = true;
|
||||
_relayParentJointsChanged = true;
|
||||
_groupCulledChanged = true;
|
||||
_animation.markAllChanged();
|
||||
|
||||
// Light
|
||||
|
@ -3740,6 +3891,9 @@ void EntityItemProperties::markAllChanged() {
|
|||
// Web
|
||||
_sourceUrlChanged = true;
|
||||
_dpiChanged = true;
|
||||
_scriptURLChanged = true;
|
||||
_maxFPSChanged = true;
|
||||
_inputModeChanged = true;
|
||||
|
||||
// Polyline
|
||||
_linePointsChanged = true;
|
||||
|
@ -3966,6 +4120,15 @@ QList<QString> EntityItemProperties::listChangedProperties() {
|
|||
if (isVisibleInSecondaryCameraChanged()) {
|
||||
out += "isVisibleInSecondaryCamera";
|
||||
}
|
||||
if (renderLayerChanged()) {
|
||||
out += "renderLayer";
|
||||
}
|
||||
if (primitiveModeChanged()) {
|
||||
out += "primitiveMode";
|
||||
}
|
||||
if (ignorePickIntersectionChanged()) {
|
||||
out += "ignorePickIntersection";
|
||||
}
|
||||
getGrab().listChangedProperties(out);
|
||||
|
||||
// Physics
|
||||
|
@ -4212,6 +4375,9 @@ QList<QString> EntityItemProperties::listChangedProperties() {
|
|||
if (relayParentJointsChanged()) {
|
||||
out += "relayParentJoints";
|
||||
}
|
||||
if (groupCulledChanged()) {
|
||||
out += "groupCulled";
|
||||
}
|
||||
getAnimation().listChangedProperties(out);
|
||||
|
||||
// Light
|
||||
|
@ -4342,6 +4508,15 @@ QList<QString> EntityItemProperties::listChangedProperties() {
|
|||
if (dpiChanged()) {
|
||||
out += "dpi";
|
||||
}
|
||||
if (scriptURLChanged()) {
|
||||
out += "scriptURL";
|
||||
}
|
||||
if (maxFPSChanged()) {
|
||||
out += "maxFPS";
|
||||
}
|
||||
if (inputModeChanged()) {
|
||||
out += "inputMode";
|
||||
}
|
||||
|
||||
// Polyline
|
||||
if (linePointsChanged()) {
|
||||
|
|
|
@ -30,27 +30,33 @@
|
|||
#include <ShapeInfo.h>
|
||||
#include <ColorUtils.h>
|
||||
|
||||
#include "AnimationPropertyGroup.h"
|
||||
#include "EntityItemID.h"
|
||||
#include "EntityItemPropertiesDefaults.h"
|
||||
#include "EntityItemPropertiesMacros.h"
|
||||
#include "EntityTypes.h"
|
||||
#include "EntityPropertyFlags.h"
|
||||
#include "EntityPsuedoPropertyFlags.h"
|
||||
#include "LightEntityItem.h"
|
||||
#include "LineEntityItem.h"
|
||||
#include "ParticleEffectEntityItem.h"
|
||||
#include "PolyVoxEntityItem.h"
|
||||
#include "SimulationOwner.h"
|
||||
|
||||
#include "TextEntityItem.h"
|
||||
#include "WebEntityItem.h"
|
||||
#include "ParticleEffectEntityItem.h"
|
||||
#include "LineEntityItem.h"
|
||||
#include "PolyVoxEntityItem.h"
|
||||
#include "GridEntityItem.h"
|
||||
#include "LightEntityItem.h"
|
||||
#include "ZoneEntityItem.h"
|
||||
|
||||
#include "AnimationPropertyGroup.h"
|
||||
#include "SkyboxPropertyGroup.h"
|
||||
#include "HazePropertyGroup.h"
|
||||
#include "BloomPropertyGroup.h"
|
||||
#include "TextEntityItem.h"
|
||||
#include "ZoneEntityItem.h"
|
||||
#include "GridEntityItem.h"
|
||||
|
||||
#include "MaterialMappingMode.h"
|
||||
#include "BillboardMode.h"
|
||||
#include "RenderLayer.h"
|
||||
#include "PrimitiveMode.h"
|
||||
#include "WebInputMode.h"
|
||||
|
||||
const quint64 UNKNOWN_CREATED_TIME = 0;
|
||||
|
||||
|
@ -169,6 +175,9 @@ public:
|
|||
DEFINE_PROPERTY_REF(PROP_QUERY_AA_CUBE, QueryAACube, queryAACube, AACube, AACube());
|
||||
DEFINE_PROPERTY(PROP_CAN_CAST_SHADOW, CanCastShadow, canCastShadow, bool, ENTITY_ITEM_DEFAULT_CAN_CAST_SHADOW);
|
||||
DEFINE_PROPERTY(PROP_VISIBLE_IN_SECONDARY_CAMERA, IsVisibleInSecondaryCamera, isVisibleInSecondaryCamera, bool, ENTITY_ITEM_DEFAULT_VISIBLE_IN_SECONDARY_CAMERA);
|
||||
DEFINE_PROPERTY_REF_ENUM(PROP_RENDER_LAYER, RenderLayer, renderLayer, RenderLayer, RenderLayer::WORLD);
|
||||
DEFINE_PROPERTY_REF_ENUM(PROP_PRIMITIVE_MODE, PrimitiveMode, primitiveMode, PrimitiveMode, PrimitiveMode::SOLID);
|
||||
DEFINE_PROPERTY(PROP_IGNORE_PICK_INTERSECTION, IgnorePickIntersection, ignorePickIntersection, bool, false);
|
||||
DEFINE_PROPERTY_GROUP(Grab, grab, GrabPropertyGroup);
|
||||
|
||||
// Physics
|
||||
|
@ -268,6 +277,7 @@ public:
|
|||
DEFINE_PROPERTY_REF(PROP_JOINT_TRANSLATIONS_SET, JointTranslationsSet, jointTranslationsSet, QVector<bool>, QVector<bool>());
|
||||
DEFINE_PROPERTY_REF(PROP_JOINT_TRANSLATIONS, JointTranslations, jointTranslations, QVector<glm::vec3>, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC);
|
||||
DEFINE_PROPERTY(PROP_RELAY_PARENT_JOINTS, RelayParentJoints, relayParentJoints, bool, ENTITY_ITEM_DEFAULT_RELAY_PARENT_JOINTS);
|
||||
DEFINE_PROPERTY_REF(PROP_GROUP_CULLED, GroupCulled, groupCulled, bool, false);
|
||||
DEFINE_PROPERTY_GROUP(Animation, animation, AnimationPropertyGroup);
|
||||
|
||||
// Light
|
||||
|
@ -320,8 +330,11 @@ public:
|
|||
DEFINE_PROPERTY_REF(PROP_Z_P_NEIGHBOR_ID, ZPNeighborID, zPNeighborID, EntityItemID, UNKNOWN_ENTITY_ID);
|
||||
|
||||
// Web
|
||||
DEFINE_PROPERTY_REF(PROP_SOURCE_URL, SourceUrl, sourceUrl, QString, "");
|
||||
DEFINE_PROPERTY_REF(PROP_SOURCE_URL, SourceUrl, sourceUrl, QString, WebEntityItem::DEFAULT_SOURCE_URL);
|
||||
DEFINE_PROPERTY_REF(PROP_DPI, DPI, dpi, uint16_t, ENTITY_ITEM_DEFAULT_DPI);
|
||||
DEFINE_PROPERTY_REF(PROP_SCRIPT_URL, ScriptURL, scriptURL, QString, "");
|
||||
DEFINE_PROPERTY_REF(PROP_MAX_FPS, MaxFPS, maxFPS, uint8_t, WebEntityItem::DEFAULT_MAX_FPS);
|
||||
DEFINE_PROPERTY_REF_ENUM(PROP_INPUT_MODE, InputMode, inputMode, WebInputMode, WebInputMode::TOUCH);
|
||||
|
||||
// Polyline
|
||||
DEFINE_PROPERTY_REF(PROP_LINE_POINTS, LinePoints, linePoints, QVector<glm::vec3>, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC);
|
||||
|
|
|
@ -39,6 +39,9 @@ enum EntityPropertyList {
|
|||
PROP_QUERY_AA_CUBE,
|
||||
PROP_CAN_CAST_SHADOW,
|
||||
PROP_VISIBLE_IN_SECONDARY_CAMERA, // not sent over the wire
|
||||
PROP_RENDER_LAYER,
|
||||
PROP_PRIMITIVE_MODE,
|
||||
PROP_IGNORE_PICK_INTERSECTION,
|
||||
// Grab
|
||||
PROP_GRAB_GRABBABLE,
|
||||
PROP_GRAB_KINEMATIC,
|
||||
|
@ -198,16 +201,17 @@ enum EntityPropertyList {
|
|||
PROP_JOINT_TRANSLATIONS_SET = PROP_DERIVED_3,
|
||||
PROP_JOINT_TRANSLATIONS = PROP_DERIVED_4,
|
||||
PROP_RELAY_PARENT_JOINTS = PROP_DERIVED_5,
|
||||
PROP_GROUP_CULLED = PROP_DERIVED_6,
|
||||
// Animation
|
||||
PROP_ANIMATION_URL = PROP_DERIVED_6,
|
||||
PROP_ANIMATION_ALLOW_TRANSLATION = PROP_DERIVED_7,
|
||||
PROP_ANIMATION_FPS = PROP_DERIVED_8,
|
||||
PROP_ANIMATION_FRAME_INDEX = PROP_DERIVED_9,
|
||||
PROP_ANIMATION_PLAYING = PROP_DERIVED_10,
|
||||
PROP_ANIMATION_LOOP = PROP_DERIVED_11,
|
||||
PROP_ANIMATION_FIRST_FRAME = PROP_DERIVED_12,
|
||||
PROP_ANIMATION_LAST_FRAME = PROP_DERIVED_13,
|
||||
PROP_ANIMATION_HOLD = PROP_DERIVED_14,
|
||||
PROP_ANIMATION_URL = PROP_DERIVED_7,
|
||||
PROP_ANIMATION_ALLOW_TRANSLATION = PROP_DERIVED_8,
|
||||
PROP_ANIMATION_FPS = PROP_DERIVED_9,
|
||||
PROP_ANIMATION_FRAME_INDEX = PROP_DERIVED_10,
|
||||
PROP_ANIMATION_PLAYING = PROP_DERIVED_11,
|
||||
PROP_ANIMATION_LOOP = PROP_DERIVED_12,
|
||||
PROP_ANIMATION_FIRST_FRAME = PROP_DERIVED_13,
|
||||
PROP_ANIMATION_LAST_FRAME = PROP_DERIVED_14,
|
||||
PROP_ANIMATION_HOLD = PROP_DERIVED_15,
|
||||
|
||||
// Light
|
||||
PROP_IS_SPOTLIGHT = PROP_DERIVED_0,
|
||||
|
@ -284,6 +288,9 @@ enum EntityPropertyList {
|
|||
// Web
|
||||
PROP_SOURCE_URL = PROP_DERIVED_0,
|
||||
PROP_DPI = PROP_DERIVED_1,
|
||||
PROP_SCRIPT_URL = PROP_DERIVED_2,
|
||||
PROP_MAX_FPS = PROP_DERIVED_3,
|
||||
PROP_INPUT_MODE = PROP_DERIVED_4,
|
||||
|
||||
// Polyline
|
||||
PROP_LINE_POINTS = PROP_DERIVED_0,
|
||||
|
|
|
@ -2707,7 +2707,6 @@ void convertGrabUserDataToProperties(EntityItemProperties& properties) {
|
|||
bool EntityTree::readFromMap(QVariantMap& map) {
|
||||
// These are needed to deal with older content (before adding inheritance modes)
|
||||
int contentVersion = map["Version"].toInt();
|
||||
bool needsConversion = (contentVersion < (int)EntityVersion::ZoneLightInheritModes);
|
||||
|
||||
if (map.contains("Id")) {
|
||||
_persistID = map["Id"].toUuid();
|
||||
|
@ -2782,7 +2781,7 @@ bool EntityTree::readFromMap(QVariantMap& map) {
|
|||
}
|
||||
|
||||
// Fix for older content not containing mode fields in the zones
|
||||
if (needsConversion && (properties.getType() == EntityTypes::EntityType::Zone)) {
|
||||
if (contentVersion < (int)EntityVersion::ZoneLightInheritModes && (properties.getType() == EntityTypes::EntityType::Zone)) {
|
||||
// The legacy version had no keylight mode - this is set to on
|
||||
properties.setKeyLightMode(COMPONENT_MODE_ENABLED);
|
||||
|
||||
|
|
|
@ -187,6 +187,10 @@ EntityItemID EntityTreeElement::evalDetailedRayIntersection(const glm::vec3& ori
|
|||
// only called if we do intersect our bounding cube, but find if we actually intersect with entities...
|
||||
EntityItemID entityID;
|
||||
forEachEntity([&](EntityItemPointer entity) {
|
||||
if (entity->getIgnorePickIntersection()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// use simple line-sphere for broadphase check
|
||||
// (this is faster and more likely to cull results than the filter check below so we do it first)
|
||||
bool success;
|
||||
|
@ -327,6 +331,10 @@ EntityItemID EntityTreeElement::evalDetailedParabolaIntersection(const glm::vec3
|
|||
// only called if we do intersect our bounding cube, but find if we actually intersect with entities...
|
||||
EntityItemID entityID;
|
||||
forEachEntity([&](EntityItemPointer entity) {
|
||||
if (entity->getIgnorePickIntersection()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// use simple line-sphere for broadphase check
|
||||
// (this is faster and more likely to cull results than the filter check below so we do it first)
|
||||
bool success;
|
||||
|
|
|
@ -67,6 +67,7 @@ EntityItemProperties ModelEntityItem::getProperties(const EntityPropertyFlags& d
|
|||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(jointTranslationsSet, getJointTranslationsSet);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(jointTranslations, getJointTranslations);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(relayParentJoints, getRelayParentJoints);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(groupCulled, getGroupCulled);
|
||||
withReadLock([&] {
|
||||
_animationProperties.getProperties(properties);
|
||||
});
|
||||
|
@ -88,6 +89,7 @@ bool ModelEntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(jointTranslationsSet, setJointTranslationsSet);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(jointTranslations, setJointTranslations);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(relayParentJoints, setRelayParentJoints);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(groupCulled, setGroupCulled);
|
||||
|
||||
withWriteLock([&] {
|
||||
AnimationPropertyGroup animationProperties = _animationProperties;
|
||||
|
@ -130,6 +132,7 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
|||
READ_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS_SET, QVector<bool>, setJointTranslationsSet);
|
||||
READ_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS, QVector<glm::vec3>, setJointTranslations);
|
||||
READ_ENTITY_PROPERTY(PROP_RELAY_PARENT_JOINTS, bool, setRelayParentJoints);
|
||||
READ_ENTITY_PROPERTY(PROP_GROUP_CULLED, bool, setGroupCulled);
|
||||
|
||||
// grab a local copy of _animationProperties to avoid multiple locks
|
||||
int bytesFromAnimation;
|
||||
|
@ -166,6 +169,7 @@ EntityPropertyFlags ModelEntityItem::getEntityProperties(EncodeBitstreamParams&
|
|||
requestedProperties += PROP_JOINT_TRANSLATIONS_SET;
|
||||
requestedProperties += PROP_JOINT_TRANSLATIONS;
|
||||
requestedProperties += PROP_RELAY_PARENT_JOINTS;
|
||||
requestedProperties += PROP_GROUP_CULLED;
|
||||
requestedProperties += _animationProperties.getEntityProperties(params);
|
||||
|
||||
return requestedProperties;
|
||||
|
@ -192,6 +196,7 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit
|
|||
APPEND_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS_SET, getJointTranslationsSet());
|
||||
APPEND_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS, getJointTranslations());
|
||||
APPEND_ENTITY_PROPERTY(PROP_RELAY_PARENT_JOINTS, getRelayParentJoints());
|
||||
APPEND_ENTITY_PROPERTY(PROP_GROUP_CULLED, getGroupCulled());
|
||||
|
||||
withReadLock([&] {
|
||||
_animationProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties,
|
||||
|
@ -548,6 +553,18 @@ bool ModelEntityItem::getRelayParentJoints() const {
|
|||
});
|
||||
}
|
||||
|
||||
void ModelEntityItem::setGroupCulled(bool value) {
|
||||
withWriteLock([&] {
|
||||
_groupCulled = value;
|
||||
});
|
||||
}
|
||||
|
||||
bool ModelEntityItem::getGroupCulled() const {
|
||||
return resultWithReadLock<bool>([&] {
|
||||
return _groupCulled;
|
||||
});
|
||||
}
|
||||
|
||||
QString ModelEntityItem::getCompoundShapeURL() const {
|
||||
return _compoundShapeURL.get();
|
||||
}
|
||||
|
|
|
@ -100,6 +100,9 @@ public:
|
|||
void setRelayParentJoints(bool relayJoints);
|
||||
bool getRelayParentJoints() const;
|
||||
|
||||
void setGroupCulled(bool value);
|
||||
bool getGroupCulled() const;
|
||||
|
||||
bool getAnimationIsPlaying() const;
|
||||
float getAnimationCurrentFrame() const;
|
||||
float getAnimationFPS() const;
|
||||
|
@ -154,6 +157,7 @@ protected:
|
|||
glm::u8vec3 _color;
|
||||
QString _modelURL;
|
||||
bool _relayParentJoints;
|
||||
bool _groupCulled { false };
|
||||
|
||||
ThreadSafeValueCache<QString> _compoundShapeURL;
|
||||
|
||||
|
|
|
@ -112,7 +112,6 @@ EntityItemPointer ShapeEntityItem::sphereFactory(const EntityItemID& entityID, c
|
|||
ShapeEntityItem::ShapeEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) {
|
||||
_type = EntityTypes::Shape;
|
||||
_volumeMultiplier *= PI / 6.0f;
|
||||
_material = std::make_shared<graphics::Material>();
|
||||
}
|
||||
|
||||
EntityItemProperties ShapeEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const {
|
||||
|
@ -215,7 +214,6 @@ void ShapeEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit
|
|||
void ShapeEntityItem::setColor(const glm::u8vec3& value) {
|
||||
withWriteLock([&] {
|
||||
_color = value;
|
||||
_material->setAlbedo(toGlm(_color));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -228,7 +226,12 @@ glm::u8vec3 ShapeEntityItem::getColor() const {
|
|||
void ShapeEntityItem::setAlpha(float alpha) {
|
||||
withWriteLock([&] {
|
||||
_alpha = alpha;
|
||||
_material->setOpacity(alpha);
|
||||
});
|
||||
}
|
||||
|
||||
float ShapeEntityItem::getAlpha() const {
|
||||
return resultWithReadLock<float>([&] {
|
||||
return _alpha;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ public:
|
|||
void setShape(const entity::Shape& shape);
|
||||
void setShape(const QString& shape) { setShape(entity::shapeFromString(shape)); }
|
||||
|
||||
float getAlpha() const { return _alpha; };
|
||||
float getAlpha() const;
|
||||
void setAlpha(float alpha);
|
||||
|
||||
glm::u8vec3 getColor() const;
|
||||
|
@ -99,20 +99,15 @@ public:
|
|||
virtual void computeShapeInfo(ShapeInfo& info) override;
|
||||
virtual ShapeType getShapeType() const override;
|
||||
|
||||
std::shared_ptr<graphics::Material> getMaterial() { return _material; }
|
||||
|
||||
protected:
|
||||
|
||||
float _alpha { 1.0f };
|
||||
glm::u8vec3 _color;
|
||||
float _alpha { 1.0f };
|
||||
entity::Shape _shape { entity::Shape::Sphere };
|
||||
|
||||
//! This is SHAPE_TYPE_ELLIPSOID rather than SHAPE_TYPE_NONE to maintain
|
||||
//! prior functionality where new or unsupported shapes are treated as
|
||||
//! ellipsoids.
|
||||
ShapeType _collisionShapeType{ ShapeType::SHAPE_TYPE_ELLIPSOID };
|
||||
|
||||
std::shared_ptr<graphics::Material> _material;
|
||||
};
|
||||
|
||||
#endif // hifi_ShapeEntityItem_h
|
||||
|
|
|
@ -21,7 +21,8 @@
|
|||
#include "EntityTree.h"
|
||||
#include "EntityTreeElement.h"
|
||||
|
||||
const QString WebEntityItem::DEFAULT_SOURCE_URL("http://www.google.com");
|
||||
const QString WebEntityItem::DEFAULT_SOURCE_URL = "http://www.google.com";
|
||||
const uint8_t WebEntityItem::DEFAULT_MAX_FPS = 10;
|
||||
|
||||
EntityItemPointer WebEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
EntityItemPointer entity(new WebEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); });
|
||||
|
@ -31,20 +32,25 @@ EntityItemPointer WebEntityItem::factory(const EntityItemID& entityID, const Ent
|
|||
|
||||
WebEntityItem::WebEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) {
|
||||
_type = EntityTypes::Web;
|
||||
_dpi = ENTITY_ITEM_DEFAULT_DPI;
|
||||
}
|
||||
|
||||
const float WEB_ENTITY_ITEM_FIXED_DEPTH = 0.01f;
|
||||
|
||||
void WebEntityItem::setUnscaledDimensions(const glm::vec3& value) {
|
||||
// NOTE: Web Entities always have a "depth" of 1cm.
|
||||
const float WEB_ENTITY_ITEM_FIXED_DEPTH = 0.01f;
|
||||
EntityItem::setUnscaledDimensions(glm::vec3(value.x, value.y, WEB_ENTITY_ITEM_FIXED_DEPTH));
|
||||
}
|
||||
|
||||
EntityItemProperties WebEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const {
|
||||
EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class
|
||||
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(alpha, getAlpha);
|
||||
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(sourceUrl, getSourceUrl);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(dpi, getDPI);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(scriptURL, getScriptURL);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(maxFPS, getMaxFPS);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(inputMode, getInputMode);
|
||||
return properties;
|
||||
}
|
||||
|
||||
|
@ -52,8 +58,14 @@ bool WebEntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
bool somethingChanged = false;
|
||||
somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class
|
||||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(alpha, setAlpha);
|
||||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(sourceUrl, setSourceUrl);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(dpi, setDPI);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(scriptURL, setScriptURL);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(maxFPS, setMaxFPS);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(inputMode, setInputMode);
|
||||
|
||||
if (somethingChanged) {
|
||||
bool wantDebug = false;
|
||||
|
@ -77,16 +89,28 @@ int WebEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, i
|
|||
int bytesRead = 0;
|
||||
const unsigned char* dataAt = data;
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_COLOR, glm::u8vec3, setColor);
|
||||
READ_ENTITY_PROPERTY(PROP_ALPHA, float, setAlpha);
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_SOURCE_URL, QString, setSourceUrl);
|
||||
READ_ENTITY_PROPERTY(PROP_DPI, uint16_t, setDPI);
|
||||
READ_ENTITY_PROPERTY(PROP_SCRIPT_URL, QString, setScriptURL);
|
||||
READ_ENTITY_PROPERTY(PROP_MAX_FPS, uint8_t, setMaxFPS);
|
||||
READ_ENTITY_PROPERTY(PROP_INPUT_MODE, WebInputMode, setInputMode);
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
EntityPropertyFlags WebEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
||||
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
||||
requestedProperties += PROP_COLOR;
|
||||
requestedProperties += PROP_ALPHA;
|
||||
|
||||
requestedProperties += PROP_SOURCE_URL;
|
||||
requestedProperties += PROP_DPI;
|
||||
requestedProperties += PROP_SCRIPT_URL;
|
||||
requestedProperties += PROP_MAX_FPS;
|
||||
requestedProperties += PROP_INPUT_MODE;
|
||||
return requestedProperties;
|
||||
}
|
||||
|
||||
|
@ -99,8 +123,14 @@ void WebEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitst
|
|||
OctreeElement::AppendState& appendState) const {
|
||||
|
||||
bool successPropertyFits = true;
|
||||
APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, _sourceUrl);
|
||||
APPEND_ENTITY_PROPERTY(PROP_DPI, _dpi);
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha());
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, getSourceUrl());
|
||||
APPEND_ENTITY_PROPERTY(PROP_DPI, getDPI());
|
||||
APPEND_ENTITY_PROPERTY(PROP_SCRIPT_URL, getScriptURL());
|
||||
APPEND_ENTITY_PROPERTY(PROP_MAX_FPS, getMaxFPS());
|
||||
APPEND_ENTITY_PROPERTY(PROP_INPUT_MODE, (uint32_t)getInputMode());
|
||||
}
|
||||
|
||||
bool WebEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
|
@ -157,6 +187,30 @@ bool WebEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin, co
|
|||
}
|
||||
}
|
||||
|
||||
void WebEntityItem::setColor(const glm::u8vec3& value) {
|
||||
withWriteLock([&] {
|
||||
_color = value;
|
||||
});
|
||||
}
|
||||
|
||||
glm::u8vec3 WebEntityItem::getColor() const {
|
||||
return resultWithReadLock<glm::u8vec3>([&] {
|
||||
return _color;
|
||||
});
|
||||
}
|
||||
|
||||
void WebEntityItem::setAlpha(float alpha) {
|
||||
withWriteLock([&] {
|
||||
_alpha = alpha;
|
||||
});
|
||||
}
|
||||
|
||||
float WebEntityItem::getAlpha() const {
|
||||
return resultWithReadLock<float>([&] {
|
||||
return _alpha;
|
||||
});
|
||||
}
|
||||
|
||||
void WebEntityItem::setSourceUrl(const QString& value) {
|
||||
withWriteLock([&] {
|
||||
if (_sourceUrl != value) {
|
||||
|
@ -172,17 +226,63 @@ void WebEntityItem::setSourceUrl(const QString& value) {
|
|||
}
|
||||
|
||||
QString WebEntityItem::getSourceUrl() const {
|
||||
QString result;
|
||||
withReadLock([&] {
|
||||
result = _sourceUrl;
|
||||
return resultWithReadLock<QString>([&] {
|
||||
return _sourceUrl;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
void WebEntityItem::setDPI(uint16_t value) {
|
||||
_dpi = value;
|
||||
withWriteLock([&] {
|
||||
_dpi = value;
|
||||
});
|
||||
}
|
||||
|
||||
uint16_t WebEntityItem::getDPI() const {
|
||||
return _dpi;
|
||||
return resultWithReadLock<uint16_t>([&] {
|
||||
return _dpi;
|
||||
});
|
||||
}
|
||||
|
||||
void WebEntityItem::setScriptURL(const QString& value) {
|
||||
withWriteLock([&] {
|
||||
if (_scriptURL != value) {
|
||||
auto newURL = QUrl::fromUserInput(value);
|
||||
|
||||
if (newURL.isValid()) {
|
||||
_scriptURL = newURL.toDisplayString();
|
||||
} else {
|
||||
qCDebug(entities) << "Clearing web entity source URL since" << value << "cannot be parsed to a valid URL.";
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
QString WebEntityItem::getScriptURL() const {
|
||||
return resultWithReadLock<QString>([&] {
|
||||
return _scriptURL;
|
||||
});
|
||||
}
|
||||
|
||||
void WebEntityItem::setMaxFPS(uint8_t value) {
|
||||
withWriteLock([&] {
|
||||
_maxFPS = value;
|
||||
});
|
||||
}
|
||||
|
||||
uint8_t WebEntityItem::getMaxFPS() const {
|
||||
return resultWithReadLock<uint8_t>([&] {
|
||||
return _maxFPS;
|
||||
});
|
||||
}
|
||||
|
||||
void WebEntityItem::setInputMode(const WebInputMode& value) {
|
||||
withWriteLock([&] {
|
||||
_inputMode = value;
|
||||
});
|
||||
}
|
||||
|
||||
WebInputMode WebEntityItem::getInputMode() const {
|
||||
return resultWithReadLock<WebInputMode>([&] {
|
||||
return _inputMode;
|
||||
});
|
||||
}
|
|
@ -13,8 +13,6 @@
|
|||
|
||||
class WebEntityItem : public EntityItem {
|
||||
public:
|
||||
static const QString DEFAULT_SOURCE_URL;
|
||||
|
||||
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
|
||||
WebEntityItem(const EntityItemID& entityItemID);
|
||||
|
@ -54,15 +52,38 @@ public:
|
|||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const override;
|
||||
|
||||
virtual void setSourceUrl(const QString& value);
|
||||
glm::u8vec3 getColor() const;
|
||||
void setColor(const glm::u8vec3& value);
|
||||
|
||||
float getAlpha() const;
|
||||
void setAlpha(float alpha);
|
||||
|
||||
static const QString DEFAULT_SOURCE_URL;
|
||||
void setSourceUrl(const QString& value);
|
||||
QString getSourceUrl() const;
|
||||
|
||||
void setDPI(uint16_t value);
|
||||
uint16_t getDPI() const;
|
||||
|
||||
void setScriptURL(const QString& value);
|
||||
QString getScriptURL() const;
|
||||
|
||||
static const uint8_t DEFAULT_MAX_FPS;
|
||||
void setMaxFPS(uint8_t value);
|
||||
uint8_t getMaxFPS() const;
|
||||
|
||||
void setInputMode(const WebInputMode& value);
|
||||
WebInputMode getInputMode() const;
|
||||
|
||||
protected:
|
||||
glm::u8vec3 _color;
|
||||
float _alpha { 1.0f };
|
||||
|
||||
QString _sourceUrl;
|
||||
uint16_t _dpi;
|
||||
QString _scriptURL;
|
||||
uint8_t _maxFPS;
|
||||
WebInputMode _inputMode;
|
||||
};
|
||||
|
||||
#endif // hifi_WebEntityItem_h
|
||||
|
|
|
@ -131,6 +131,7 @@ public:
|
|||
glm::vec3 geometricTranslation;
|
||||
glm::quat geometricRotation;
|
||||
glm::vec3 geometricScaling;
|
||||
bool isLimbNode; // is this FBXModel transform is a "LimbNode" i.e. a joint
|
||||
};
|
||||
|
||||
glm::mat4 getGlobalTransform(const QMultiMap<QString, QString>& _connectionParentMap,
|
||||
|
@ -559,9 +560,11 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
|
|||
glm::vec3 geometricRotation;
|
||||
|
||||
glm::vec3 rotationMin, rotationMax;
|
||||
|
||||
bool isLimbNode = object.properties.size() >= 3 && object.properties.at(2) == "LimbNode";
|
||||
FBXModel fbxModel = { name, -1, glm::vec3(), glm::mat4(), glm::quat(), glm::quat(), glm::quat(),
|
||||
glm::mat4(), glm::vec3(), glm::vec3(),
|
||||
false, glm::vec3(), glm::quat(), glm::vec3(1.0f) };
|
||||
glm::mat4(), glm::vec3(), glm::vec3(),
|
||||
false, glm::vec3(), glm::quat(), glm::vec3(1.0f), isLimbNode };
|
||||
ExtractedMesh* mesh = NULL;
|
||||
QVector<ExtractedBlendshape> blendshapes;
|
||||
foreach (const FBXNode& subobject, object.children) {
|
||||
|
@ -752,17 +755,17 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
|
|||
} else if (subobject.name == "Texture_Alpha_Source" && subobject.properties.length() >= TEXTURE_ALPHA_SOURCE_MIN_SIZE) {
|
||||
tex.assign<uint8_t>(tex.alphaSource, subobject.properties.at(0).value<int>());
|
||||
} else if (subobject.name == "ModelUVTranslation" && subobject.properties.length() >= MODEL_UV_TRANSLATION_MIN_SIZE) {
|
||||
tex.assign(tex.UVTranslation, glm::vec2(subobject.properties.at(0).value<double>(),
|
||||
subobject.properties.at(1).value<double>()));
|
||||
auto newTranslation = glm::vec3(subobject.properties.at(0).value<double>(), subobject.properties.at(1).value<double>(), 0.0);
|
||||
tex.assign(tex.translation, tex.translation + newTranslation);
|
||||
} else if (subobject.name == "ModelUVScaling" && subobject.properties.length() >= MODEL_UV_SCALING_MIN_SIZE) {
|
||||
tex.assign(tex.UVScaling, glm::vec2(subobject.properties.at(0).value<double>(),
|
||||
subobject.properties.at(1).value<double>()));
|
||||
if (tex.UVScaling.x == 0.0f) {
|
||||
tex.UVScaling.x = 1.0f;
|
||||
auto newScaling = glm::vec3(subobject.properties.at(0).value<double>(), subobject.properties.at(1).value<double>(), 1.0);
|
||||
if (newScaling.x == 0.0f) {
|
||||
newScaling.x = 1.0f;
|
||||
}
|
||||
if (tex.UVScaling.y == 0.0f) {
|
||||
tex.UVScaling.y = 1.0f;
|
||||
if (newScaling.y == 0.0f) {
|
||||
newScaling.y = 1.0f;
|
||||
}
|
||||
tex.assign(tex.scaling, tex.scaling * newScaling);
|
||||
} else if (subobject.name == "Cropping" && subobject.properties.length() >= CROPPING_MIN_SIZE) {
|
||||
tex.assign(tex.cropping, glm::vec4(subobject.properties.at(0).value<int>(),
|
||||
subobject.properties.at(1).value<int>(),
|
||||
|
@ -790,20 +793,21 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
|
|||
} else if (property.properties.at(0) == USE_MATERIAL) {
|
||||
tex.assign<bool>(tex.useMaterial, property.properties.at(index).value<int>());
|
||||
} else if (property.properties.at(0) == TRANSLATION) {
|
||||
tex.assign(tex.translation, getVec3(property.properties, index));
|
||||
tex.assign(tex.translation, tex.translation + getVec3(property.properties, index));
|
||||
} else if (property.properties.at(0) == ROTATION) {
|
||||
tex.assign(tex.rotation, getVec3(property.properties, index));
|
||||
} else if (property.properties.at(0) == SCALING) {
|
||||
tex.assign(tex.scaling, getVec3(property.properties, index));
|
||||
if (tex.scaling.x == 0.0f) {
|
||||
tex.scaling.x = 1.0f;
|
||||
auto newScaling = getVec3(property.properties, index);
|
||||
if (newScaling.x == 0.0f) {
|
||||
newScaling.x = 1.0f;
|
||||
}
|
||||
if (tex.scaling.y == 0.0f) {
|
||||
tex.scaling.y = 1.0f;
|
||||
if (newScaling.y == 0.0f) {
|
||||
newScaling.y = 1.0f;
|
||||
}
|
||||
if (tex.scaling.z == 0.0f) {
|
||||
tex.scaling.z = 1.0f;
|
||||
if (newScaling.z == 0.0f) {
|
||||
newScaling.z = 1.0f;
|
||||
}
|
||||
tex.assign(tex.scaling, tex.scaling * newScaling);
|
||||
}
|
||||
#if defined(DEBUG_FBXSERIALIZER)
|
||||
else {
|
||||
|
@ -848,6 +852,7 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
|
|||
}
|
||||
} else if (object.name == "Material") {
|
||||
HFMMaterial material;
|
||||
MaterialParam materialParam;
|
||||
material.name = (object.properties.at(1).toString());
|
||||
foreach (const FBXNode& subobject, object.children) {
|
||||
bool properties = false;
|
||||
|
@ -892,6 +897,10 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
|
|||
static const QVariant MAYA_EMISSIVE_INTENSITY = QByteArray("Maya|emissive_intensity");
|
||||
static const QVariant MAYA_USE_EMISSIVE_MAP = QByteArray("Maya|use_emissive_map");
|
||||
static const QVariant MAYA_USE_AO_MAP = QByteArray("Maya|use_ao_map");
|
||||
static const QVariant MAYA_UV_SCALE = QByteArray("Maya|uv_scale");
|
||||
static const QVariant MAYA_UV_OFFSET = QByteArray("Maya|uv_offset");
|
||||
static const int MAYA_UV_OFFSET_PROPERTY_LENGTH = 6;
|
||||
static const int MAYA_UV_SCALE_PROPERTY_LENGTH = 6;
|
||||
|
||||
|
||||
|
||||
|
@ -980,6 +989,27 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
|
|||
material.isPBSMaterial = true;
|
||||
material.useOcclusionMap = (bool)property.properties.at(index).value<double>();
|
||||
|
||||
} else if (property.properties.at(0) == MAYA_UV_SCALE) {
|
||||
if (property.properties.size() == MAYA_UV_SCALE_PROPERTY_LENGTH) {
|
||||
// properties: { "Maya|uv_scale", "Vector2D", "Vector2", nothing, double, double }
|
||||
glm::vec3 scale = glm::vec3(property.properties.at(4).value<double>(), property.properties.at(5).value<double>(), 1.0);
|
||||
if (scale.x == 0.0f) {
|
||||
scale.x = 1.0f;
|
||||
}
|
||||
if (scale.y == 0.0f) {
|
||||
scale.y = 1.0f;
|
||||
}
|
||||
if (scale.z == 0.0f) {
|
||||
scale.z = 1.0f;
|
||||
}
|
||||
materialParam.scaling *= scale;
|
||||
}
|
||||
} else if (property.properties.at(0) == MAYA_UV_OFFSET) {
|
||||
if (property.properties.size() == MAYA_UV_OFFSET_PROPERTY_LENGTH) {
|
||||
// properties: { "Maya|uv_offset", "Vector2D", "Vector2", nothing, double, double }
|
||||
glm::vec3 translation = glm::vec3(property.properties.at(4).value<double>(), property.properties.at(5).value<double>(), 1.0);
|
||||
materialParam.translation += translation;
|
||||
}
|
||||
} else {
|
||||
const QString propname = property.properties.at(0).toString();
|
||||
unknowns.push_back(propname.toStdString());
|
||||
|
@ -1001,6 +1031,7 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
|
|||
}
|
||||
material.materialID = getID(object.properties);
|
||||
_hfmMaterials.insert(material.materialID, material);
|
||||
_materialParams.insert(material.materialID, materialParam);
|
||||
|
||||
|
||||
} else if (object.name == "NodeAttribute") {
|
||||
|
@ -1258,6 +1289,7 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
|
|||
// convert the models to joints
|
||||
QVariantList freeJoints = mapping.values("freeJoint");
|
||||
hfmModel.hasSkeletonJoints = false;
|
||||
|
||||
foreach (const QString& modelID, modelIDs) {
|
||||
const FBXModel& fbxModel = fbxModels[modelID];
|
||||
HFMJoint joint;
|
||||
|
@ -1288,6 +1320,8 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
|
|||
joint.geometricTranslation = fbxModel.geometricTranslation;
|
||||
joint.geometricRotation = fbxModel.geometricRotation;
|
||||
joint.geometricScaling = fbxModel.geometricScaling;
|
||||
joint.isSkeletonJoint = fbxModel.isLimbNode;
|
||||
hfmModel.hasSkeletonJoints = (hfmModel.hasSkeletonJoints || joint.isSkeletonJoint);
|
||||
|
||||
glm::quat combinedRotation = joint.preRotation * joint.rotation * joint.postRotation;
|
||||
|
||||
|
@ -1311,14 +1345,6 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
|
|||
joint.name = hfmModel.hfmToHifiJointNameMapping.key(fbxModel.name);
|
||||
}
|
||||
|
||||
foreach (const QString& childID, _connectionChildMap.values(modelID)) {
|
||||
QString type = typeFlags.value(childID);
|
||||
if (!type.isEmpty()) {
|
||||
hfmModel.hasSkeletonJoints |= (joint.isSkeletonJoint = type.toLower().contains("Skeleton"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
joint.bindTransformFoundInCluster = false;
|
||||
|
||||
hfmModel.joints.append(joint);
|
||||
|
@ -1439,7 +1465,9 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
|
|||
materialIndex++;
|
||||
|
||||
} else if (_textureFilenames.contains(childID)) {
|
||||
HFMTexture texture = getTexture(childID);
|
||||
// NOTE (Sabrina 2019/01/11): getTextures now takes in the materialID as a second parameter, because FBX material nodes can sometimes have uv transform information (ex: "Maya|uv_scale")
|
||||
// I'm leaving the second parameter blank right now as this code may never be used.
|
||||
HFMTexture texture = getTexture(childID, "");
|
||||
for (int j = 0; j < extracted.partMaterialTextures.size(); j++) {
|
||||
int partTexture = extracted.partMaterialTextures.at(j).second;
|
||||
if (partTexture == textureIndex && !(partTexture == 0 && materialsHaveTextures)) {
|
||||
|
|
|
@ -37,8 +37,6 @@ class FBXNode;
|
|||
|
||||
class TextureParam {
|
||||
public:
|
||||
glm::vec2 UVTranslation;
|
||||
glm::vec2 UVScaling;
|
||||
glm::vec4 cropping;
|
||||
QString UVSet;
|
||||
|
||||
|
@ -63,8 +61,6 @@ public:
|
|||
bool isDefault;
|
||||
|
||||
TextureParam() :
|
||||
UVTranslation(0.0f),
|
||||
UVScaling(1.0f),
|
||||
cropping(0.0f),
|
||||
UVSet("map1"),
|
||||
translation(0.0f),
|
||||
|
@ -77,8 +73,6 @@ public:
|
|||
{}
|
||||
|
||||
TextureParam(const TextureParam& src) :
|
||||
UVTranslation(src.UVTranslation),
|
||||
UVScaling(src.UVScaling),
|
||||
cropping(src.cropping),
|
||||
UVSet(src.UVSet),
|
||||
translation(src.translation),
|
||||
|
@ -92,6 +86,22 @@ public:
|
|||
|
||||
};
|
||||
|
||||
class MaterialParam {
|
||||
public:
|
||||
glm::vec3 translation;
|
||||
glm::vec3 scaling;
|
||||
|
||||
MaterialParam() :
|
||||
translation(0.0),
|
||||
scaling(1.0)
|
||||
{}
|
||||
|
||||
MaterialParam(const MaterialParam& src) :
|
||||
translation(src.translation),
|
||||
scaling(src.scaling)
|
||||
{}
|
||||
};
|
||||
|
||||
class ExtractedMesh;
|
||||
|
||||
class FBXSerializer : public HFMSerializer {
|
||||
|
@ -114,7 +124,7 @@ public:
|
|||
static ExtractedMesh extractMesh(const FBXNode& object, unsigned int& meshIndex, bool deduplicate = true);
|
||||
QHash<QString, ExtractedMesh> meshes;
|
||||
|
||||
HFMTexture getTexture(const QString& textureID);
|
||||
HFMTexture getTexture(const QString& textureID, const QString& materialID);
|
||||
|
||||
QHash<QString, QString> _textureNames;
|
||||
// Hashes the original RelativeFilename of textures
|
||||
|
@ -141,6 +151,7 @@ public:
|
|||
QHash<QString, QString> occlusionTextures;
|
||||
|
||||
QHash<QString, HFMMaterial> _hfmMaterials;
|
||||
QHash<QString, MaterialParam> _materialParams;
|
||||
|
||||
void consolidateHFMMaterials(const QVariantHash& mapping);
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
#include <hfm/ModelFormatLogging.h>
|
||||
|
||||
HFMTexture FBXSerializer::getTexture(const QString& textureID) {
|
||||
HFMTexture FBXSerializer::getTexture(const QString& textureID, const QString& materialID) {
|
||||
HFMTexture texture;
|
||||
const QByteArray& filepath = _textureFilepaths.value(textureID);
|
||||
texture.content = _textureContent.value(filepath);
|
||||
|
@ -45,8 +45,8 @@ HFMTexture FBXSerializer::getTexture(const QString& textureID) {
|
|||
if (_textureParams.contains(textureID)) {
|
||||
auto p = _textureParams.value(textureID);
|
||||
|
||||
texture.transform.setTranslation(p.translation);
|
||||
texture.transform.setRotation(glm::quat(glm::radians(p.rotation)));
|
||||
texture.transform.postTranslate(p.translation);
|
||||
texture.transform.postRotate(glm::quat(glm::radians(p.rotation)));
|
||||
|
||||
auto scaling = p.scaling;
|
||||
// Protect from bad scaling which should never happen
|
||||
|
@ -59,13 +59,19 @@ HFMTexture FBXSerializer::getTexture(const QString& textureID) {
|
|||
if (scaling.z == 0.0f) {
|
||||
scaling.z = 1.0f;
|
||||
}
|
||||
texture.transform.setScale(scaling);
|
||||
texture.transform.postScale(scaling);
|
||||
|
||||
if ((p.UVSet != "map1") && (p.UVSet != "UVSet0")) {
|
||||
texture.texcoordSet = 1;
|
||||
}
|
||||
texture.texcoordSetName = p.UVSet;
|
||||
}
|
||||
auto materialParamItr = _materialParams.find(materialID);
|
||||
if (materialParamItr != _materialParams.end()) {
|
||||
auto& materialParam = materialParamItr.value();
|
||||
texture.transform.postTranslate(materialParam.translation);
|
||||
texture.transform.postScale(materialParam.scaling);
|
||||
}
|
||||
return texture;
|
||||
}
|
||||
|
||||
|
@ -102,12 +108,12 @@ void FBXSerializer::consolidateHFMMaterials(const QVariantHash& mapping) {
|
|||
material.diffuseFactor = 1.0;
|
||||
}
|
||||
|
||||
diffuseTexture = getTexture(diffuseTextureID);
|
||||
diffuseTexture = getTexture(diffuseTextureID, material.materialID);
|
||||
|
||||
// FBX files generated by 3DSMax have an intermediate texture parent, apparently
|
||||
foreach(const QString& childTextureID, _connectionChildMap.values(diffuseTextureID)) {
|
||||
if (_textureFilenames.contains(childTextureID)) {
|
||||
diffuseTexture = getTexture(diffuseTextureID);
|
||||
diffuseTexture = getTexture(diffuseTextureID, material.materialID);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,7 +128,7 @@ void FBXSerializer::consolidateHFMMaterials(const QVariantHash& mapping) {
|
|||
transparentTextureID = diffuseTextureID;
|
||||
}
|
||||
if (!transparentTextureID.isNull()) {
|
||||
transparentTexture = getTexture(transparentTextureID);
|
||||
transparentTexture = getTexture(transparentTextureID, material.materialID);
|
||||
material.opacityTexture = transparentTexture;
|
||||
detectDifferentUVs |= (transparentTexture.texcoordSet != 0) || (!transparentTexture.transform.isIdentity());
|
||||
}
|
||||
|
@ -131,13 +137,13 @@ void FBXSerializer::consolidateHFMMaterials(const QVariantHash& mapping) {
|
|||
QString bumpTextureID = bumpTextures.value(material.materialID);
|
||||
QString normalTextureID = normalTextures.value(material.materialID);
|
||||
if (!normalTextureID.isNull()) {
|
||||
normalTexture = getTexture(normalTextureID);
|
||||
normalTexture = getTexture(normalTextureID, material.materialID);
|
||||
normalTexture.isBumpmap = false;
|
||||
|
||||
material.normalTexture = normalTexture;
|
||||
detectDifferentUVs |= (normalTexture.texcoordSet != 0) || (!normalTexture.transform.isIdentity());
|
||||
} else if (!bumpTextureID.isNull()) {
|
||||
normalTexture = getTexture(bumpTextureID);
|
||||
normalTexture = getTexture(bumpTextureID, material.materialID);
|
||||
normalTexture.isBumpmap = true;
|
||||
|
||||
material.normalTexture = normalTexture;
|
||||
|
@ -147,7 +153,7 @@ void FBXSerializer::consolidateHFMMaterials(const QVariantHash& mapping) {
|
|||
HFMTexture specularTexture;
|
||||
QString specularTextureID = specularTextures.value(material.materialID);
|
||||
if (!specularTextureID.isNull()) {
|
||||
specularTexture = getTexture(specularTextureID);
|
||||
specularTexture = getTexture(specularTextureID, material.materialID);
|
||||
detectDifferentUVs |= (specularTexture.texcoordSet != 0) || (!specularTexture.transform.isIdentity());
|
||||
material.specularTexture = specularTexture;
|
||||
}
|
||||
|
@ -155,7 +161,7 @@ void FBXSerializer::consolidateHFMMaterials(const QVariantHash& mapping) {
|
|||
HFMTexture metallicTexture;
|
||||
QString metallicTextureID = metallicTextures.value(material.materialID);
|
||||
if (!metallicTextureID.isNull()) {
|
||||
metallicTexture = getTexture(metallicTextureID);
|
||||
metallicTexture = getTexture(metallicTextureID, material.materialID);
|
||||
detectDifferentUVs |= (metallicTexture.texcoordSet != 0) || (!metallicTexture.transform.isIdentity());
|
||||
material.metallicTexture = metallicTexture;
|
||||
}
|
||||
|
@ -163,7 +169,7 @@ void FBXSerializer::consolidateHFMMaterials(const QVariantHash& mapping) {
|
|||
HFMTexture roughnessTexture;
|
||||
QString roughnessTextureID = roughnessTextures.value(material.materialID);
|
||||
if (!roughnessTextureID.isNull()) {
|
||||
roughnessTexture = getTexture(roughnessTextureID);
|
||||
roughnessTexture = getTexture(roughnessTextureID, material.materialID);
|
||||
material.roughnessTexture = roughnessTexture;
|
||||
detectDifferentUVs |= (roughnessTexture.texcoordSet != 0) || (!roughnessTexture.transform.isIdentity());
|
||||
}
|
||||
|
@ -171,7 +177,7 @@ void FBXSerializer::consolidateHFMMaterials(const QVariantHash& mapping) {
|
|||
HFMTexture shininessTexture;
|
||||
QString shininessTextureID = shininessTextures.value(material.materialID);
|
||||
if (!shininessTextureID.isNull()) {
|
||||
shininessTexture = getTexture(shininessTextureID);
|
||||
shininessTexture = getTexture(shininessTextureID, material.materialID);
|
||||
material.glossTexture = shininessTexture;
|
||||
detectDifferentUVs |= (shininessTexture.texcoordSet != 0) || (!shininessTexture.transform.isIdentity());
|
||||
}
|
||||
|
@ -179,7 +185,7 @@ void FBXSerializer::consolidateHFMMaterials(const QVariantHash& mapping) {
|
|||
HFMTexture emissiveTexture;
|
||||
QString emissiveTextureID = emissiveTextures.value(material.materialID);
|
||||
if (!emissiveTextureID.isNull()) {
|
||||
emissiveTexture = getTexture(emissiveTextureID);
|
||||
emissiveTexture = getTexture(emissiveTextureID, material.materialID);
|
||||
detectDifferentUVs |= (emissiveTexture.texcoordSet != 0) || (!emissiveTexture.transform.isIdentity());
|
||||
material.emissiveTexture = emissiveTexture;
|
||||
|
||||
|
@ -202,7 +208,7 @@ void FBXSerializer::consolidateHFMMaterials(const QVariantHash& mapping) {
|
|||
}
|
||||
|
||||
if (!occlusionTextureID.isNull()) {
|
||||
occlusionTexture = getTexture(occlusionTextureID);
|
||||
occlusionTexture = getTexture(occlusionTextureID, material.materialID);
|
||||
detectDifferentUVs |= (occlusionTexture.texcoordSet != 0) || (!emissiveTexture.transform.isIdentity());
|
||||
material.occlusionTexture = occlusionTexture;
|
||||
}
|
||||
|
@ -222,7 +228,7 @@ void FBXSerializer::consolidateHFMMaterials(const QVariantHash& mapping) {
|
|||
}
|
||||
|
||||
if (_loadLightmaps && !ambientTextureID.isNull()) {
|
||||
ambientTexture = getTexture(ambientTextureID);
|
||||
ambientTexture = getTexture(ambientTextureID, material.materialID);
|
||||
detectDifferentUVs |= (ambientTexture.texcoordSet != 0) || (!ambientTexture.transform.isIdentity());
|
||||
material.lightmapTexture = ambientTexture;
|
||||
material.lightmapParams = lightmapParams;
|
||||
|
|
|
@ -40,13 +40,13 @@ namespace scriptable {
|
|||
* @typedef {object} Graphics.Material
|
||||
* @property {string} name
|
||||
* @property {string} model
|
||||
* @property {number} opacity
|
||||
* @property {number} roughness
|
||||
* @property {number} metallic
|
||||
* @property {number} scattering
|
||||
* @property {boolean} unlit
|
||||
* @propety {Vec3} emissive
|
||||
* @propety {Vec3} albedo
|
||||
* @property {number|string} opacity
|
||||
* @property {number|string} roughness
|
||||
* @property {number|string} metallic
|
||||
* @property {number|string} scattering
|
||||
* @property {boolean|string} unlit
|
||||
* @propety {Vec3|string} emissive
|
||||
* @propety {Vec3|string} albedo
|
||||
* @property {string} emissiveMap
|
||||
* @property {string} albedoMap
|
||||
* @property {string} opacityMap
|
||||
|
@ -59,6 +59,11 @@ namespace scriptable {
|
|||
* @property {string} occlusionMap
|
||||
* @property {string} lightmapMap
|
||||
* @property {string} scatteringMap
|
||||
* @property {string} texCoordTransform0
|
||||
* @property {string} texCoordTransform1
|
||||
* @property {string} lightmapParams
|
||||
* @property {string} materialParams
|
||||
* @property {boolean} defaultFallthrough
|
||||
*/
|
||||
class ScriptableMaterial {
|
||||
public:
|
||||
|
@ -88,6 +93,9 @@ namespace scriptable {
|
|||
QString occlusionMap;
|
||||
QString lightmapMap;
|
||||
QString scatteringMap;
|
||||
|
||||
bool defaultFallthrough;
|
||||
std::unordered_map<uint, bool> propertyFallthroughs; // not actually exposed to script
|
||||
};
|
||||
|
||||
/**jsdoc
|
||||
|
|
|
@ -362,25 +362,64 @@ namespace scriptable {
|
|||
QScriptValue obj = engine->newObject();
|
||||
obj.setProperty("name", material.name);
|
||||
obj.setProperty("model", material.model);
|
||||
obj.setProperty("opacity", material.opacity);
|
||||
obj.setProperty("roughness", material.roughness);
|
||||
obj.setProperty("metallic", material.metallic);
|
||||
obj.setProperty("scattering", material.scattering);
|
||||
obj.setProperty("unlit", material.unlit);
|
||||
obj.setProperty("emissive", vec3ColorToScriptValue(engine, material.emissive));
|
||||
obj.setProperty("albedo", vec3ColorToScriptValue(engine, material.albedo));
|
||||
obj.setProperty("emissiveMap", material.emissiveMap);
|
||||
obj.setProperty("albedoMap", material.albedoMap);
|
||||
|
||||
const QScriptValue FALLTHROUGH("fallthrough");
|
||||
obj.setProperty("opacity", material.propertyFallthroughs.at(graphics::MaterialKey::OPACITY_VAL_BIT) ? FALLTHROUGH : material.opacity);
|
||||
obj.setProperty("roughness", material.propertyFallthroughs.at(graphics::MaterialKey::GLOSSY_VAL_BIT) ? FALLTHROUGH : material.roughness);
|
||||
obj.setProperty("metallic", material.propertyFallthroughs.at(graphics::MaterialKey::METALLIC_VAL_BIT) ? FALLTHROUGH : material.metallic);
|
||||
obj.setProperty("scattering", material.propertyFallthroughs.at(graphics::MaterialKey::SCATTERING_VAL_BIT) ? FALLTHROUGH : material.scattering);
|
||||
obj.setProperty("unlit", material.propertyFallthroughs.at(graphics::MaterialKey::UNLIT_VAL_BIT) ? FALLTHROUGH : material.unlit);
|
||||
obj.setProperty("emissive", material.propertyFallthroughs.at(graphics::MaterialKey::EMISSIVE_VAL_BIT) ? FALLTHROUGH : vec3ColorToScriptValue(engine, material.emissive));
|
||||
obj.setProperty("albedo", material.propertyFallthroughs.at(graphics::MaterialKey::ALBEDO_VAL_BIT) ? FALLTHROUGH : vec3ColorToScriptValue(engine, material.albedo));
|
||||
|
||||
obj.setProperty("emissiveMap", material.propertyFallthroughs.at(graphics::MaterialKey::EMISSIVE_MAP_BIT) ? FALLTHROUGH : material.emissiveMap);
|
||||
obj.setProperty("albedoMap", material.propertyFallthroughs.at(graphics::MaterialKey::ALBEDO_MAP_BIT) ? FALLTHROUGH : material.albedoMap);
|
||||
obj.setProperty("opacityMap", material.opacityMap);
|
||||
obj.setProperty("metallicMap", material.metallicMap);
|
||||
obj.setProperty("specularMap", material.specularMap);
|
||||
obj.setProperty("roughnessMap", material.roughnessMap);
|
||||
obj.setProperty("glossMap", material.glossMap);
|
||||
obj.setProperty("normalMap", material.normalMap);
|
||||
obj.setProperty("bumpMap", material.bumpMap);
|
||||
obj.setProperty("occlusionMap", material.occlusionMap);
|
||||
obj.setProperty("lightmapMap", material.lightmapMap);
|
||||
obj.setProperty("scatteringMap", material.scatteringMap);
|
||||
obj.setProperty("occlusionMap", material.propertyFallthroughs.at(graphics::MaterialKey::OCCLUSION_MAP_BIT) ? FALLTHROUGH : material.occlusionMap);
|
||||
obj.setProperty("lightmapMap", material.propertyFallthroughs.at(graphics::MaterialKey::LIGHTMAP_MAP_BIT) ? FALLTHROUGH : material.lightmapMap);
|
||||
obj.setProperty("scatteringMap", material.propertyFallthroughs.at(graphics::MaterialKey::SCATTERING_MAP_BIT) ? FALLTHROUGH : material.scatteringMap);
|
||||
|
||||
// Only set one of each of these
|
||||
if (material.propertyFallthroughs.at(graphics::MaterialKey::METALLIC_MAP_BIT)) {
|
||||
obj.setProperty("metallicMap", FALLTHROUGH);
|
||||
} else if (!material.metallicMap.isEmpty()) {
|
||||
obj.setProperty("metallicMap", material.metallicMap);
|
||||
} else if (!material.specularMap.isEmpty()) {
|
||||
obj.setProperty("specularMap", material.specularMap);
|
||||
}
|
||||
|
||||
if (material.propertyFallthroughs.at(graphics::MaterialKey::ROUGHNESS_MAP_BIT)) {
|
||||
obj.setProperty("roughnessMap", FALLTHROUGH);
|
||||
} else if (!material.roughnessMap.isEmpty()) {
|
||||
obj.setProperty("roughnessMap", material.roughnessMap);
|
||||
} else if (!material.glossMap.isEmpty()) {
|
||||
obj.setProperty("glossMap", material.glossMap);
|
||||
}
|
||||
|
||||
if (material.propertyFallthroughs.at(graphics::MaterialKey::NORMAL_MAP_BIT)) {
|
||||
obj.setProperty("normalMap", FALLTHROUGH);
|
||||
} else if (!material.normalMap.isEmpty()) {
|
||||
obj.setProperty("normalMap", material.normalMap);
|
||||
} else if (!material.bumpMap.isEmpty()) {
|
||||
obj.setProperty("bumpMap", material.bumpMap);
|
||||
}
|
||||
|
||||
// These need to be implemented, but set the fallthrough for now
|
||||
if (material.propertyFallthroughs.at(graphics::Material::TEXCOORDTRANSFORM0)) {
|
||||
obj.setProperty("texCoordTransform0", FALLTHROUGH);
|
||||
}
|
||||
if (material.propertyFallthroughs.at(graphics::Material::TEXCOORDTRANSFORM1)) {
|
||||
obj.setProperty("texCoordTransform1", FALLTHROUGH);
|
||||
}
|
||||
if (material.propertyFallthroughs.at(graphics::Material::LIGHTMAP_PARAMS)) {
|
||||
obj.setProperty("lightmapParams", FALLTHROUGH);
|
||||
}
|
||||
if (material.propertyFallthroughs.at(graphics::Material::MATERIAL_PARAMS)) {
|
||||
obj.setProperty("materialParams", FALLTHROUGH);
|
||||
}
|
||||
|
||||
obj.setProperty("defaultFallthrough", material.defaultFallthrough);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
|
|
@ -42,6 +42,9 @@ scriptable::ScriptableMaterial& scriptable::ScriptableMaterial::operator=(const
|
|||
lightmapMap = material.lightmapMap;
|
||||
scatteringMap = material.scatteringMap;
|
||||
|
||||
defaultFallthrough = material.defaultFallthrough;
|
||||
propertyFallthroughs = material.propertyFallthroughs;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -54,7 +57,9 @@ scriptable::ScriptableMaterial::ScriptableMaterial(const graphics::MaterialPoint
|
|||
scattering(material->getScattering()),
|
||||
unlit(material->isUnlit()),
|
||||
emissive(material->getEmissive()),
|
||||
albedo(material->getAlbedo())
|
||||
albedo(material->getAlbedo()),
|
||||
defaultFallthrough(material->getDefaultFallthrough()),
|
||||
propertyFallthroughs(material->getPropertyFallthroughs())
|
||||
{
|
||||
auto map = material->getTextureMap(graphics::Material::MapChannel::EMISSIVE_MAP);
|
||||
if (map && map->getTextureSource()) {
|
||||
|
|
|
@ -17,125 +17,125 @@
|
|||
using namespace graphics;
|
||||
using namespace gpu;
|
||||
|
||||
Material::Material() :
|
||||
_key(0),
|
||||
_schemaBuffer(),
|
||||
_textureMaps()
|
||||
{
|
||||
// created from nothing: create the Buffer to store the properties
|
||||
Schema schema;
|
||||
_schemaBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema, sizeof(Schema)));
|
||||
const float Material::DEFAULT_EMISSIVE { 0.0f };
|
||||
const float Material::DEFAULT_OPACITY { 1.0f };
|
||||
const float Material::DEFAULT_ALBEDO { 0.5f };
|
||||
const float Material::DEFAULT_METALLIC { 0.0f };
|
||||
const float Material::DEFAULT_ROUGHNESS { 1.0f };
|
||||
const float Material::DEFAULT_SCATTERING { 0.0f };
|
||||
|
||||
Material::Material() {
|
||||
for (int i = 0; i < NUM_TOTAL_FLAGS; i++) {
|
||||
_propertyFallthroughs[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
Material::Material(const Material& material) :
|
||||
_name(material._name),
|
||||
_model(material._model),
|
||||
_key(material._key),
|
||||
_textureMaps(material._textureMaps)
|
||||
_emissive(material._emissive),
|
||||
_opacity(material._opacity),
|
||||
_albedo(material._albedo),
|
||||
_roughness(material._roughness),
|
||||
_metallic(material._metallic),
|
||||
_scattering(material._scattering),
|
||||
_texcoordTransforms(material._texcoordTransforms),
|
||||
_lightmapParams(material._lightmapParams),
|
||||
_materialParams(material._materialParams),
|
||||
_textureMaps(material._textureMaps),
|
||||
_defaultFallthrough(material._defaultFallthrough),
|
||||
_propertyFallthroughs(material._propertyFallthroughs)
|
||||
{
|
||||
// copied: create the Buffer to store the properties, avoid holding a ref to the old Buffer
|
||||
Schema schema;
|
||||
_schemaBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema, sizeof(Schema)));
|
||||
_schemaBuffer.edit<Schema>() = material._schemaBuffer.get<Schema>();
|
||||
}
|
||||
|
||||
Material& Material::operator= (const Material& material) {
|
||||
Material& Material::operator=(const Material& material) {
|
||||
QMutexLocker locker(&_textureMapsMutex);
|
||||
|
||||
_name = material._name;
|
||||
_model = material._model;
|
||||
_key = material._key;
|
||||
_emissive = material._emissive;
|
||||
_opacity = material._opacity;
|
||||
_albedo = material._albedo;
|
||||
_roughness = material._roughness;
|
||||
_metallic = material._metallic;
|
||||
_scattering = material._scattering;
|
||||
_texcoordTransforms = material._texcoordTransforms;
|
||||
_lightmapParams = material._lightmapParams;
|
||||
_materialParams = material._materialParams;
|
||||
_textureMaps = material._textureMaps;
|
||||
|
||||
_key = (material._key);
|
||||
_textureMaps = (material._textureMaps);
|
||||
_hasCalculatedTextureInfo = false;
|
||||
|
||||
// copied: create the Buffer to store the properties, avoid holding a ref to the old Buffer
|
||||
Schema schema;
|
||||
_schemaBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema, sizeof(Schema)));
|
||||
_schemaBuffer.edit<Schema>() = material._schemaBuffer.get<Schema>();
|
||||
_defaultFallthrough = material._defaultFallthrough;
|
||||
_propertyFallthroughs = material._propertyFallthroughs;
|
||||
|
||||
return (*this);
|
||||
}
|
||||
|
||||
Material::~Material() {
|
||||
}
|
||||
|
||||
void Material::setEmissive(const Color& emissive, bool isSRGB) {
|
||||
_key.setEmissive(glm::any(glm::greaterThan(emissive, Color(0.0f))));
|
||||
_schemaBuffer.edit<Schema>()._key = (uint32) _key._flags.to_ulong();
|
||||
_schemaBuffer.edit<Schema>()._emissive = (isSRGB ? ColorUtils::sRGBToLinearVec3(emissive) : emissive);
|
||||
void Material::setEmissive(const glm::vec3& emissive, bool isSRGB) {
|
||||
_key.setEmissive(glm::any(glm::greaterThan(emissive, glm::vec3(0.0f))));
|
||||
_emissive = (isSRGB ? ColorUtils::sRGBToLinearVec3(emissive) : emissive);
|
||||
}
|
||||
|
||||
void Material::setOpacity(float opacity) {
|
||||
_key.setTranslucentFactor((opacity < 1.0f));
|
||||
_schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong();
|
||||
_schemaBuffer.edit<Schema>()._opacity = opacity;
|
||||
_opacity = opacity;
|
||||
}
|
||||
|
||||
void Material::setUnlit(bool value) {
|
||||
_key.setUnlit(value);
|
||||
_schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong();
|
||||
}
|
||||
|
||||
void Material::setAlbedo(const Color& albedo, bool isSRGB) {
|
||||
_key.setAlbedo(glm::any(glm::greaterThan(albedo, Color(0.0f))));
|
||||
_schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong();
|
||||
_schemaBuffer.edit<Schema>()._albedo = (isSRGB ? ColorUtils::sRGBToLinearVec3(albedo) : albedo);
|
||||
void Material::setAlbedo(const glm::vec3& albedo, bool isSRGB) {
|
||||
_key.setAlbedo(glm::any(glm::greaterThan(albedo, glm::vec3(0.0f))));
|
||||
_albedo = (isSRGB ? ColorUtils::sRGBToLinearVec3(albedo) : albedo);
|
||||
}
|
||||
|
||||
void Material::setRoughness(float roughness) {
|
||||
roughness = std::min(1.0f, std::max(roughness, 0.0f));
|
||||
_key.setGlossy((roughness < 1.0f));
|
||||
_schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong();
|
||||
_schemaBuffer.edit<Schema>()._roughness = roughness;
|
||||
_key.setGlossy(roughness < 1.0f);
|
||||
_roughness = roughness;
|
||||
}
|
||||
|
||||
void Material::setMetallic(float metallic) {
|
||||
metallic = glm::clamp(metallic, 0.0f, 1.0f);
|
||||
_key.setMetallic(metallic > 0.0f);
|
||||
_schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong();
|
||||
_schemaBuffer.edit<Schema>()._metallic = metallic;
|
||||
_metallic = metallic;
|
||||
}
|
||||
|
||||
void Material::setScattering(float scattering) {
|
||||
scattering = glm::clamp(scattering, 0.0f, 1.0f);
|
||||
_key.setMetallic(scattering > 0.0f);
|
||||
_schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong();
|
||||
_schemaBuffer.edit<Schema>()._scattering = scattering;
|
||||
_scattering = scattering;
|
||||
}
|
||||
|
||||
void Material::setTextureMap(MapChannel channel, const TextureMapPointer& textureMap) {
|
||||
QMutexLocker locker(&_textureMapsMutex);
|
||||
|
||||
if (textureMap) {
|
||||
_key.setMapChannel(channel, (true));
|
||||
_key.setMapChannel(channel, true);
|
||||
_textureMaps[channel] = textureMap;
|
||||
} else {
|
||||
_key.setMapChannel(channel, (false));
|
||||
_key.setMapChannel(channel, false);
|
||||
_textureMaps.erase(channel);
|
||||
}
|
||||
_hasCalculatedTextureInfo = false;
|
||||
|
||||
_schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong();
|
||||
|
||||
if (channel == MaterialKey::ALBEDO_MAP) {
|
||||
resetOpacityMap();
|
||||
|
||||
// update the texcoord0 with albedo
|
||||
_schemaBuffer.edit<Schema>()._texcoordTransforms[0] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4());
|
||||
_texcoordTransforms[0] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4());
|
||||
}
|
||||
|
||||
if (channel == MaterialKey::OCCLUSION_MAP) {
|
||||
_schemaBuffer.edit<Schema>()._texcoordTransforms[1] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4());
|
||||
_texcoordTransforms[1] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4());
|
||||
}
|
||||
|
||||
if (channel == MaterialKey::LIGHTMAP_MAP) {
|
||||
// update the texcoord1 with lightmap
|
||||
_schemaBuffer.edit<Schema>()._texcoordTransforms[1] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4());
|
||||
_schemaBuffer.edit<Schema>()._lightmapParams = (textureMap ? glm::vec2(textureMap->getLightmapOffsetScale()) : glm::vec2(0.0, 1.0));
|
||||
_texcoordTransforms[1] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4());
|
||||
_lightmapParams = (textureMap ? glm::vec2(textureMap->getLightmapOffsetScale()) : glm::vec2(0.0, 1.0));
|
||||
}
|
||||
|
||||
_schemaBuffer.edit<Schema>()._materialParams = (textureMap ? glm::vec2(textureMap->getMappingMode(), textureMap->getRepeat()) : glm::vec2(MaterialMappingMode::UV, 1.0));
|
||||
|
||||
_schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong();
|
||||
_materialParams = (textureMap ? glm::vec2(textureMap->getMappingMode(), textureMap->getRepeat()) : glm::vec2(MaterialMappingMode::UV, 1.0));
|
||||
|
||||
}
|
||||
|
||||
|
@ -163,11 +163,8 @@ void Material::resetOpacityMap() const {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
_schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong();
|
||||
}
|
||||
|
||||
|
||||
const TextureMapPointer Material::getTextureMap(MapChannel channel) const {
|
||||
QMutexLocker locker(&_textureMapsMutex);
|
||||
|
||||
|
@ -179,40 +176,6 @@ const TextureMapPointer Material::getTextureMap(MapChannel channel) const {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
bool Material::calculateMaterialInfo() const {
|
||||
if (!_hasCalculatedTextureInfo) {
|
||||
QMutexLocker locker(&_textureMapsMutex);
|
||||
|
||||
bool allTextures = true; // assume we got this...
|
||||
_textureSize = 0;
|
||||
_textureCount = 0;
|
||||
|
||||
for (auto const &textureMapItem : _textureMaps) {
|
||||
auto textureMap = textureMapItem.second;
|
||||
if (textureMap) {
|
||||
auto textureSoure = textureMap->getTextureSource();
|
||||
if (textureSoure) {
|
||||
auto texture = textureSoure->getGPUTexture();
|
||||
if (texture) {
|
||||
auto size = texture->getSize();
|
||||
_textureSize += size;
|
||||
_textureCount++;
|
||||
} else {
|
||||
allTextures = false;
|
||||
}
|
||||
} else {
|
||||
allTextures = false;
|
||||
}
|
||||
} else {
|
||||
allTextures = false;
|
||||
}
|
||||
}
|
||||
_hasCalculatedTextureInfo = allTextures;
|
||||
}
|
||||
return _hasCalculatedTextureInfo;
|
||||
}
|
||||
|
||||
void Material::setTextureTransforms(const Transform& transform, MaterialMappingMode mode, bool repeat) {
|
||||
for (auto &textureMapItem : _textureMaps) {
|
||||
if (textureMapItem.second) {
|
||||
|
@ -222,7 +185,32 @@ void Material::setTextureTransforms(const Transform& transform, MaterialMappingM
|
|||
}
|
||||
}
|
||||
for (int i = 0; i < NUM_TEXCOORD_TRANSFORMS; i++) {
|
||||
_schemaBuffer.edit<Schema>()._texcoordTransforms[i] = transform.getMatrix();
|
||||
_texcoordTransforms[i] = transform.getMatrix();
|
||||
}
|
||||
_schemaBuffer.edit<Schema>()._materialParams = glm::vec2(mode, repeat);
|
||||
_materialParams = glm::vec2(mode, repeat);
|
||||
}
|
||||
|
||||
MultiMaterial::MultiMaterial() {
|
||||
Schema schema;
|
||||
_schemaBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema, sizeof(Schema)));
|
||||
}
|
||||
|
||||
void MultiMaterial::calculateMaterialInfo() const {
|
||||
if (!_hasCalculatedTextureInfo) {
|
||||
bool allTextures = true; // assume we got this...
|
||||
_textureSize = 0;
|
||||
_textureCount = 0;
|
||||
|
||||
auto textures = _textureTable->getTextures();
|
||||
for (auto const &texture : textures) {
|
||||
if (texture && texture->isDefined()) {
|
||||
auto size = texture->getSize();
|
||||
_textureSize += size;
|
||||
_textureCount++;
|
||||
} else {
|
||||
allTextures = false;
|
||||
}
|
||||
}
|
||||
_hasCalculatedTextureInfo = allTextures;
|
||||
}
|
||||
}
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include <bitset>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <queue>
|
||||
|
||||
#include <ColorUtils.h>
|
||||
|
@ -176,7 +177,6 @@ public:
|
|||
bool isTexelOpaque() const { return isOpaque() && isOpacityMaskMap(); }
|
||||
};
|
||||
|
||||
|
||||
class MaterialFilter {
|
||||
public:
|
||||
MaterialKey::Flags _value{ 0 };
|
||||
|
@ -266,84 +266,44 @@ public:
|
|||
|
||||
class Material {
|
||||
public:
|
||||
typedef gpu::BufferView UniformBufferView;
|
||||
|
||||
typedef glm::vec3 Color;
|
||||
|
||||
// Texture Map Array Schema
|
||||
static const int NUM_TEXCOORD_TRANSFORMS{ 2 };
|
||||
|
||||
typedef MaterialKey::MapChannel MapChannel;
|
||||
typedef std::map<MapChannel, TextureMapPointer> TextureMaps;
|
||||
typedef std::bitset<MaterialKey::NUM_MAP_CHANNELS> MapFlags;
|
||||
|
||||
Material();
|
||||
Material(const Material& material);
|
||||
Material& operator= (const Material& material);
|
||||
virtual ~Material();
|
||||
|
||||
const MaterialKey& getKey() const { return _key; }
|
||||
|
||||
void setEmissive(const Color& emissive, bool isSRGB = true);
|
||||
Color getEmissive(bool SRGB = true) const { return (SRGB ? ColorUtils::tosRGBVec3(_schemaBuffer.get<Schema>()._emissive) : _schemaBuffer.get<Schema>()._emissive); }
|
||||
static const float DEFAULT_EMISSIVE;
|
||||
void setEmissive(const glm::vec3& emissive, bool isSRGB = true);
|
||||
glm::vec3 getEmissive(bool SRGB = true) const { return (SRGB ? ColorUtils::tosRGBVec3(_emissive) : _emissive); }
|
||||
|
||||
static const float DEFAULT_OPACITY;
|
||||
void setOpacity(float opacity);
|
||||
float getOpacity() const { return _schemaBuffer.get<Schema>()._opacity; }
|
||||
float getOpacity() const { return _opacity; }
|
||||
|
||||
void setUnlit(bool value);
|
||||
bool isUnlit() const { return _key.isUnlit(); }
|
||||
|
||||
void setAlbedo(const Color& albedo, bool isSRGB = true);
|
||||
Color getAlbedo(bool SRGB = true) const { return (SRGB ? ColorUtils::tosRGBVec3(_schemaBuffer.get<Schema>()._albedo) : _schemaBuffer.get<Schema>()._albedo); }
|
||||
static const float DEFAULT_ALBEDO;
|
||||
void setAlbedo(const glm::vec3& albedo, bool isSRGB = true);
|
||||
glm::vec3 getAlbedo(bool SRGB = true) const { return (SRGB ? ColorUtils::tosRGBVec3(_albedo) : _albedo); }
|
||||
|
||||
static const float DEFAULT_METALLIC;
|
||||
void setMetallic(float metallic);
|
||||
float getMetallic() const { return _schemaBuffer.get<Schema>()._metallic; }
|
||||
float getMetallic() const { return _metallic; }
|
||||
|
||||
static const float DEFAULT_ROUGHNESS;
|
||||
void setRoughness(float roughness);
|
||||
float getRoughness() const { return _schemaBuffer.get<Schema>()._roughness; }
|
||||
float getRoughness() const { return _roughness; }
|
||||
|
||||
static const float DEFAULT_SCATTERING;
|
||||
void setScattering(float scattering);
|
||||
float getScattering() const { return _schemaBuffer.get<Schema>()._scattering; }
|
||||
|
||||
// Schema to access the attribute values of the material
|
||||
class Schema {
|
||||
public:
|
||||
glm::vec3 _emissive { 0.0f }; // No Emissive
|
||||
float _opacity { 1.0f }; // Opacity = 1 => Not Transparent
|
||||
|
||||
glm::vec3 _albedo { 0.5f }; // Grey albedo => isAlbedo
|
||||
float _roughness { 1.0f }; // Roughness = 1 => Not Glossy
|
||||
|
||||
float _metallic { 0.0f }; // Not Metallic
|
||||
float _scattering { 0.0f }; // Scattering info
|
||||
#if defined(__clang__)
|
||||
__attribute__((unused))
|
||||
#endif
|
||||
glm::vec2 _spare { 0.0f }; // Padding
|
||||
|
||||
uint32_t _key { 0 }; // a copy of the materialKey
|
||||
#if defined(__clang__)
|
||||
__attribute__((unused))
|
||||
#endif
|
||||
glm::vec3 _spare2 { 0.0f };
|
||||
|
||||
// for alignment beauty, Material size == Mat4x4
|
||||
|
||||
// Texture Coord Transform Array
|
||||
glm::mat4 _texcoordTransforms[NUM_TEXCOORD_TRANSFORMS];
|
||||
|
||||
glm::vec2 _lightmapParams { 0.0, 1.0 };
|
||||
|
||||
// x: material mode (0 for UV, 1 for PROJECTED)
|
||||
// y: 1 for texture repeat, 0 for discard outside of 0 - 1
|
||||
glm::vec2 _materialParams { 0.0, 1.0 };
|
||||
|
||||
Schema() {}
|
||||
};
|
||||
|
||||
const UniformBufferView& getSchemaBuffer() const { return _schemaBuffer; }
|
||||
float getScattering() const { return _scattering; }
|
||||
|
||||
// The texture map to channel association
|
||||
static const int NUM_TEXCOORD_TRANSFORMS { 2 };
|
||||
void setTextureMap(MapChannel channel, const TextureMapPointer& textureMap);
|
||||
const TextureMaps& getTextureMaps() const { return _textureMaps; } // FIXME - not thread safe...
|
||||
const TextureMapPointer getTextureMap(MapChannel channel) const;
|
||||
|
@ -355,10 +315,6 @@ public:
|
|||
// conversion from legacy material properties to PBR equivalent
|
||||
static float shininessToRoughness(float shininess) { return 1.0f - shininess / 100.0f; }
|
||||
|
||||
int getTextureCount() const { calculateMaterialInfo(); return _textureCount; }
|
||||
size_t getTextureSize() const { calculateMaterialInfo(); return _textureSize; }
|
||||
bool hasTextureInfo() const { return _hasCalculatedTextureInfo; }
|
||||
|
||||
void setTextureTransforms(const Transform& transform, MaterialMappingMode mode, bool repeat);
|
||||
|
||||
const std::string& getName() const { return _name; }
|
||||
|
@ -366,28 +322,50 @@ public:
|
|||
const std::string& getModel() const { return _model; }
|
||||
void setModel(const std::string& model) { _model = model; }
|
||||
|
||||
const gpu::TextureTablePointer& getTextureTable() const { return _textureTable; }
|
||||
glm::mat4 getTexCoordTransform(uint i) const { return _texcoordTransforms[i]; }
|
||||
glm::vec2 getLightmapParams() const { return _lightmapParams; }
|
||||
glm::vec2 getMaterialParams() const { return _materialParams; }
|
||||
|
||||
bool getDefaultFallthrough() const { return _defaultFallthrough; }
|
||||
void setDefaultFallthrough(bool defaultFallthrough) { _defaultFallthrough = defaultFallthrough; }
|
||||
|
||||
enum ExtraFlagBit {
|
||||
TEXCOORDTRANSFORM0 = MaterialKey::NUM_FLAGS,
|
||||
TEXCOORDTRANSFORM1,
|
||||
LIGHTMAP_PARAMS,
|
||||
MATERIAL_PARAMS,
|
||||
|
||||
NUM_TOTAL_FLAGS
|
||||
};
|
||||
std::unordered_map<uint, bool> getPropertyFallthroughs() { return _propertyFallthroughs; }
|
||||
bool getPropertyFallthrough(uint property) { return _propertyFallthroughs[property]; }
|
||||
void setPropertyDoesFallthrough(uint property) { _propertyFallthroughs[property] = true; }
|
||||
|
||||
protected:
|
||||
std::string _name { "" };
|
||||
|
||||
private:
|
||||
mutable MaterialKey _key;
|
||||
mutable UniformBufferView _schemaBuffer;
|
||||
mutable gpu::TextureTablePointer _textureTable{ std::make_shared<gpu::TextureTable>() };
|
||||
std::string _model { "hifi_pbr" };
|
||||
mutable MaterialKey _key { 0 };
|
||||
|
||||
// Material properties
|
||||
glm::vec3 _emissive { DEFAULT_EMISSIVE };
|
||||
float _opacity { DEFAULT_OPACITY };
|
||||
glm::vec3 _albedo { DEFAULT_ALBEDO };
|
||||
float _roughness { DEFAULT_ROUGHNESS };
|
||||
float _metallic { DEFAULT_METALLIC };
|
||||
float _scattering { DEFAULT_SCATTERING };
|
||||
std::array<glm::mat4, NUM_TEXCOORD_TRANSFORMS> _texcoordTransforms;
|
||||
glm::vec2 _lightmapParams { 0.0, 1.0 };
|
||||
glm::vec2 _materialParams { 0.0, 1.0 };
|
||||
TextureMaps _textureMaps;
|
||||
|
||||
bool _defaultFallthrough { false };
|
||||
std::unordered_map<uint, bool> _propertyFallthroughs { NUM_TOTAL_FLAGS };
|
||||
|
||||
mutable QMutex _textureMapsMutex { QMutex::Recursive };
|
||||
mutable size_t _textureSize { 0 };
|
||||
mutable int _textureCount { 0 };
|
||||
mutable bool _hasCalculatedTextureInfo { false };
|
||||
bool calculateMaterialInfo() const;
|
||||
|
||||
std::string _model { "hifi_pbr" };
|
||||
|
||||
};
|
||||
typedef std::shared_ptr< Material > MaterialPointer;
|
||||
typedef std::shared_ptr<Material> MaterialPointer;
|
||||
|
||||
class MaterialLayer {
|
||||
public:
|
||||
|
@ -403,9 +381,18 @@ public:
|
|||
return left.priority < right.priority;
|
||||
}
|
||||
};
|
||||
typedef std::priority_queue<MaterialLayer, std::vector<MaterialLayer>, MaterialLayerCompare> MaterialLayerQueue;
|
||||
|
||||
class MultiMaterial : public std::priority_queue<MaterialLayer, std::vector<MaterialLayer>, MaterialLayerCompare> {
|
||||
class MultiMaterial : public MaterialLayerQueue {
|
||||
public:
|
||||
MultiMaterial();
|
||||
|
||||
void push(const MaterialLayer& value) {
|
||||
MaterialLayerQueue::push(value);
|
||||
_hasCalculatedTextureInfo = false;
|
||||
_needsUpdate = true;
|
||||
}
|
||||
|
||||
bool remove(const MaterialPointer& value) {
|
||||
auto it = c.begin();
|
||||
while (it != c.end()) {
|
||||
|
@ -417,11 +404,78 @@ public:
|
|||
if (it != c.end()) {
|
||||
c.erase(it);
|
||||
std::make_heap(c.begin(), c.end(), comp);
|
||||
_hasCalculatedTextureInfo = false;
|
||||
_needsUpdate = true;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Schema to access the attribute values of the material
|
||||
class Schema {
|
||||
public:
|
||||
glm::vec3 _emissive { Material::DEFAULT_EMISSIVE }; // No Emissive
|
||||
float _opacity { Material::DEFAULT_OPACITY }; // Opacity = 1 => Not Transparent
|
||||
|
||||
glm::vec3 _albedo { Material::DEFAULT_ALBEDO }; // Grey albedo => isAlbedo
|
||||
float _roughness { Material::DEFAULT_ROUGHNESS }; // Roughness = 1 => Not Glossy
|
||||
|
||||
float _metallic { Material::DEFAULT_METALLIC }; // Not Metallic
|
||||
float _scattering { Material::DEFAULT_SCATTERING }; // Scattering info
|
||||
#if defined(__clang__)
|
||||
__attribute__((unused))
|
||||
#endif
|
||||
glm::vec2 _spare { 0.0f }; // Padding
|
||||
|
||||
uint32_t _key { 0 }; // a copy of the materialKey
|
||||
#if defined(__clang__)
|
||||
__attribute__((unused))
|
||||
#endif
|
||||
glm::vec3 _spare2 { 0.0f };
|
||||
|
||||
// for alignment beauty, Material size == Mat4x4
|
||||
|
||||
// Texture Coord Transform Array
|
||||
glm::mat4 _texcoordTransforms[Material::NUM_TEXCOORD_TRANSFORMS];
|
||||
|
||||
glm::vec2 _lightmapParams { 0.0, 1.0 };
|
||||
|
||||
// x: material mode (0 for UV, 1 for PROJECTED)
|
||||
// y: 1 for texture repeat, 0 for discard outside of 0 - 1
|
||||
glm::vec2 _materialParams { 0.0, 1.0 };
|
||||
|
||||
Schema() {
|
||||
for (auto& transform : _texcoordTransforms) {
|
||||
transform = glm::mat4();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
gpu::BufferView& getSchemaBuffer() { return _schemaBuffer; }
|
||||
graphics::MaterialKey getMaterialKey() const { return graphics::MaterialKey(_schemaBuffer.get<graphics::MultiMaterial::Schema>()._key); }
|
||||
const gpu::TextureTablePointer& getTextureTable() const { return _textureTable; }
|
||||
|
||||
bool needsUpdate() const { return _needsUpdate; }
|
||||
void setNeedsUpdate(bool needsUpdate) { _needsUpdate = needsUpdate; }
|
||||
|
||||
void setTexturesLoading(bool value) { _texturesLoading = value; }
|
||||
bool areTexturesLoading() const { return _texturesLoading; }
|
||||
|
||||
int getTextureCount() const { calculateMaterialInfo(); return _textureCount; }
|
||||
size_t getTextureSize() const { calculateMaterialInfo(); return _textureSize; }
|
||||
bool hasTextureInfo() const { return _hasCalculatedTextureInfo; }
|
||||
|
||||
private:
|
||||
gpu::BufferView _schemaBuffer;
|
||||
gpu::TextureTablePointer _textureTable { std::make_shared<gpu::TextureTable>() };
|
||||
bool _needsUpdate { false };
|
||||
bool _texturesLoading { false };
|
||||
|
||||
mutable size_t _textureSize { 0 };
|
||||
mutable int _textureCount { 0 };
|
||||
mutable bool _hasCalculatedTextureInfo { false };
|
||||
void calculateMaterialInfo() const;
|
||||
};
|
||||
|
||||
};
|
||||
|
|
|
@ -205,6 +205,8 @@ bool HFMModel::convexHullContains(const glm::vec3& point) const {
|
|||
|
||||
auto checkEachPrimitive = [=](HFMMesh& mesh, QVector<int> indices, int primitiveSize) -> bool {
|
||||
// Check whether the point is "behind" all the primitives.
|
||||
// But first must transform from model-frame into mesh-frame
|
||||
glm::vec3 transformedPoint = glm::vec3(glm::inverse(mesh.modelTransform) * glm::vec4(point, 1.0f));
|
||||
int verticesSize = mesh.vertices.size();
|
||||
for (int j = 0;
|
||||
j < indices.size() - 2; // -2 in case the vertices aren't the right size -- we access j + 2 below
|
||||
|
@ -212,7 +214,7 @@ bool HFMModel::convexHullContains(const glm::vec3& point) const {
|
|||
if (indices[j] < verticesSize &&
|
||||
indices[j + 1] < verticesSize &&
|
||||
indices[j + 2] < verticesSize &&
|
||||
!isPointBehindTrianglesPlane(point,
|
||||
!isPointBehindTrianglesPlane(transformedPoint,
|
||||
mesh.vertices[indices[j]],
|
||||
mesh.vertices[indices[j + 1]],
|
||||
mesh.vertices[indices[j + 2]])) {
|
||||
|
|
|
@ -111,146 +111,300 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMater
|
|||
/**jsdoc
|
||||
* A material such as may be used by a {@link Entities.EntityType|Material} entity.
|
||||
* @typedef {object} Material
|
||||
* @property {string} name="" - A name for the material.
|
||||
* @property {string} model="hifi_pbr" - <em>Currently not used.</em>
|
||||
* @property {Color|RGBS} emissive - The emissive color, i.e., the color that the material emits. A {@link Color} value
|
||||
* is treated as sRGB. A {@link RGBS} value can be either RGB or sRGB.
|
||||
* @property {number} opacity=1.0 - The opacity, <code>0.0</code> – <code>1.0</code>.
|
||||
* @property {boolean} unlit=false - If <code>true</code>, the material is not lit.
|
||||
* @property {Color|RGBS} albedo - The albedo color. A {@link Color} value is treated as sRGB. A {@link RGBS} value can
|
||||
* be either RGB or sRGB.
|
||||
* @property {number} roughness - The roughness, <code>0.0</code> – <code>1.0</code>.
|
||||
* @property {number} metallic - The metallicness, <code>0.0</code> – <code>1.0</code>.
|
||||
* @property {number} scattering - The scattering, <code>0.0</code> – <code>1.0</code>.
|
||||
* @property {string} emissiveMap - URL of emissive texture image.
|
||||
* @property {string} albedoMap - URL of albedo texture image.
|
||||
* @property {string} model="hifi_pbr" - Different material models support different properties and rendering modes.
|
||||
* Supported models are: "hifi_pbr"
|
||||
* @property {string} name="" - A name for the material. Supported by all material models.
|
||||
* @property {Color|RGBS|string} emissive - The emissive color, i.e., the color that the material emits. A {@link Color} value
|
||||
* is treated as sRGB. A {@link RGBS} value can be either RGB or sRGB. Set to <code>"fallthrough"</code> to fallthrough to
|
||||
* the material below. "hifi_pbr" model only.
|
||||
* @property {number|string} opacity=1.0 - The opacity, <code>0.0</code> – <code>1.0</code>. Set to <code>"fallthrough"</code> to fallthrough to
|
||||
* the material below. "hifi_pbr" model only.
|
||||
* @property {boolean|string} unlit=false - If <code>true</code>, the material is not lit. Set to <code>"fallthrough"</code> to fallthrough to
|
||||
* the material below. "hifi_pbr" model only.
|
||||
* @property {Color|RGBS|string} albedo - The albedo color. A {@link Color} value is treated as sRGB. A {@link RGBS} value can
|
||||
* be either RGB or sRGB. Set to <code>"fallthrough"</code> to fallthrough to the material below. Set to <code>"fallthrough"</code> to fallthrough to
|
||||
* the material below. "hifi_pbr" model only.
|
||||
* @property {number|string} roughness - The roughness, <code>0.0</code> – <code>1.0</code>. Set to <code>"fallthrough"</code> to fallthrough to
|
||||
* the material below. "hifi_pbr" model only.
|
||||
* @property {number|string} metallic - The metallicness, <code>0.0</code> – <code>1.0</code>. Set to <code>"fallthrough"</code> to fallthrough to
|
||||
* the material below. "hifi_pbr" model only.
|
||||
* @property {number|string} scattering - The scattering, <code>0.0</code> – <code>1.0</code>. Set to <code>"fallthrough"</code> to fallthrough to
|
||||
* the material below. "hifi_pbr" model only.
|
||||
* @property {string} emissiveMap - URL of emissive texture image. Set to <code>"fallthrough"</code> to fallthrough to
|
||||
* the material below. "hifi_pbr" model only.
|
||||
* @property {string} albedoMap - URL of albedo texture image. Set to <code>"fallthrough"</code> to fallthrough to
|
||||
* the material below. "hifi_pbr" model only.
|
||||
* @property {string} opacityMap - URL of opacity texture image. Set value the same as the <code>albedoMap</code> value for
|
||||
* transparency.
|
||||
* @property {string} roughnessMap - URL of roughness texture image. Can use this or <code>glossMap</code>, but not both.
|
||||
* @property {string} glossMap - URL of gloss texture image. Can use this or <code>roughnessMap</code>, but not both.
|
||||
* @property {string} metallicMap - URL of metallic texture image. Can use this or <code>specularMap</code>, but not both.
|
||||
* @property {string} specularMap - URL of specular texture image. Can use this or <code>metallicMap</code>, but not both.
|
||||
* @property {string} normalMap - URL of normal texture image. Can use this or <code>bumpMap</code>, but not both.
|
||||
* @property {string} bumpMap - URL of bump texture image. Can use this or <code>normalMap</code>, but not both.
|
||||
* @property {string} occlusionMap - URL of occlusion texture image.
|
||||
* transparency. "hifi_pbr" model only.
|
||||
* @property {string} roughnessMap - URL of roughness texture image. Can use this or <code>glossMap</code>, but not both. Set to <code>"fallthrough"</code>
|
||||
* to fallthrough to the material below. "hifi_pbr" model only.
|
||||
* @property {string} glossMap - URL of gloss texture image. Can use this or <code>roughnessMap</code>, but not both. Set to <code>"fallthrough"</code>
|
||||
* to fallthrough to the material below. "hifi_pbr" model only.
|
||||
* @property {string} metallicMap - URL of metallic texture image. Can use this or <code>specularMap</code>, but not both. Set to <code>"fallthrough"</code>
|
||||
* to fallthrough to the material below. "hifi_pbr" model only.
|
||||
* @property {string} specularMap - URL of specular texture image. Can use this or <code>metallicMap</code>, but not both. Set to <code>"fallthrough"</code>
|
||||
* to fallthrough to the material below. "hifi_pbr" model only.
|
||||
* @property {string} normalMap - URL of normal texture image. Can use this or <code>bumpMap</code>, but not both. Set to <code>"fallthrough"</code>
|
||||
* to fallthrough to the material below. "hifi_pbr" model only.
|
||||
* @property {string} bumpMap - URL of bump texture image. Can use this or <code>normalMap</code>, but not both. Set to <code>"fallthrough"</code>
|
||||
* to fallthrough to the material below. "hifi_pbr" model only.
|
||||
* @property {string} occlusionMap - URL of occlusion texture image. Set to <code>"fallthrough"</code> to fallthrough to the material below. "hifi_pbr" model only.
|
||||
* @property {string} scatteringMap - URL of scattering texture image. Only used if <code>normalMap</code> or
|
||||
* <code>bumpMap</code> is specified.
|
||||
* @property {string} lightMap - URL of light map texture image. <em>Currently not used.</em>
|
||||
* <code>bumpMap</code> is specified. Set to <code>"fallthrough"</code> to fallthrough to the material below. "hifi_pbr" model only.
|
||||
* @property {string} lightMap - URL of light map texture image. <em>Currently not used.</em>. Set to <code>"fallthrough"</code>
|
||||
* to fallthrough to the material below. "hifi_pbr" model only.
|
||||
* @property {string} texCoordTransform0 - The transform to use for all of the maps besides occlusionMap and lightMap. Currently unused. Set to
|
||||
* <code>"fallthrough"</code> to fallthrough to the material below. "hifi_pbr" model only.
|
||||
* @property {string} texCoordTransform1 - The transform to use for occlusionMap and lightMap. Currently unused. Set to <code>"fallthrough"</code>
|
||||
* to fallthrough to the material below. "hifi_pbr" model only.
|
||||
* @property {string} lightmapParams - Parameters for controlling how lightMap is used. Currently unused. Set to <code>"fallthrough"</code>
|
||||
* to fallthrough to the material below. "hifi_pbr" model only.
|
||||
* @property {string} materialParams - Parameters for controlling the material projection and repition. Currently unused. Set to <code>"fallthrough"</code>
|
||||
* to fallthrough to the material below. "hifi_pbr" model only.
|
||||
* @property {bool} defaultFallthrough=false - If <code>true</code>, all properties will fallthrough to the material below unless they are set. If
|
||||
* <code>false</code>, they will respect the individual properties' fallthrough state. "hifi_pbr" model only.
|
||||
*/
|
||||
// Note: See MaterialEntityItem.h for default values used in practice.
|
||||
std::pair<std::string, std::shared_ptr<NetworkMaterial>> NetworkMaterialResource::parseJSONMaterial(const QJsonObject& materialJSON, const QUrl& baseUrl) {
|
||||
std::string name = "";
|
||||
std::shared_ptr<NetworkMaterial> material = std::make_shared<NetworkMaterial>();
|
||||
for (auto& key : materialJSON.keys()) {
|
||||
if (key == "name") {
|
||||
auto nameJSON = materialJSON.value(key);
|
||||
if (nameJSON.isString()) {
|
||||
name = nameJSON.toString().toStdString();
|
||||
}
|
||||
} else if (key == "model") {
|
||||
auto modelJSON = materialJSON.value(key);
|
||||
if (modelJSON.isString()) {
|
||||
material->setModel(modelJSON.toString().toStdString());
|
||||
}
|
||||
} else if (key == "emissive") {
|
||||
glm::vec3 color;
|
||||
bool isSRGB;
|
||||
bool valid = parseJSONColor(materialJSON.value(key), color, isSRGB);
|
||||
if (valid) {
|
||||
material->setEmissive(color, isSRGB);
|
||||
}
|
||||
} else if (key == "opacity") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isDouble()) {
|
||||
material->setOpacity(value.toDouble());
|
||||
}
|
||||
} else if (key == "unlit") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isBool()) {
|
||||
material->setUnlit(value.toBool());
|
||||
}
|
||||
} else if (key == "albedo") {
|
||||
glm::vec3 color;
|
||||
bool isSRGB;
|
||||
bool valid = parseJSONColor(materialJSON.value(key), color, isSRGB);
|
||||
if (valid) {
|
||||
material->setAlbedo(color, isSRGB);
|
||||
}
|
||||
} else if (key == "roughness") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isDouble()) {
|
||||
material->setRoughness(value.toDouble());
|
||||
}
|
||||
} else if (key == "metallic") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isDouble()) {
|
||||
material->setMetallic(value.toDouble());
|
||||
}
|
||||
} else if (key == "scattering") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isDouble()) {
|
||||
material->setScattering(value.toDouble());
|
||||
}
|
||||
} else if (key == "emissiveMap") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isString()) {
|
||||
material->setEmissiveMap(baseUrl.resolved(value.toString()));
|
||||
}
|
||||
} else if (key == "albedoMap") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isString()) {
|
||||
QString urlString = value.toString();
|
||||
bool useAlphaChannel = false;
|
||||
auto opacityMap = materialJSON.find("opacityMap");
|
||||
if (opacityMap != materialJSON.end() && opacityMap->isString() && opacityMap->toString() == urlString) {
|
||||
useAlphaChannel = true;
|
||||
|
||||
const std::string HIFI_PBR = "hifi_pbr";
|
||||
std::string modelString = HIFI_PBR;
|
||||
auto modelJSONIter = materialJSON.find("model");
|
||||
if (modelJSONIter != materialJSON.end() && modelJSONIter.value().isString()) {
|
||||
modelString = modelJSONIter.value().toString().toStdString();
|
||||
material->setModel(modelString);
|
||||
}
|
||||
|
||||
if (modelString == HIFI_PBR) {
|
||||
const QString FALLTHROUGH("fallthrough");
|
||||
for (auto& key : materialJSON.keys()) {
|
||||
if (key == "name") {
|
||||
auto nameJSON = materialJSON.value(key);
|
||||
if (nameJSON.isString()) {
|
||||
name = nameJSON.toString().toStdString();
|
||||
}
|
||||
} else if (key == "model") {
|
||||
auto modelJSON = materialJSON.value(key);
|
||||
if (modelJSON.isString()) {
|
||||
material->setModel(modelJSON.toString().toStdString());
|
||||
}
|
||||
} else if (key == "emissive") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isString() && value.toString() == FALLTHROUGH) {
|
||||
material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::EMISSIVE_VAL_BIT);
|
||||
} else {
|
||||
glm::vec3 color;
|
||||
bool isSRGB;
|
||||
bool valid = parseJSONColor(value, color, isSRGB);
|
||||
if (valid) {
|
||||
material->setEmissive(color, isSRGB);
|
||||
}
|
||||
}
|
||||
} else if (key == "opacity") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isString() && value.toString() == FALLTHROUGH) {
|
||||
material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::OPACITY_VAL_BIT);
|
||||
} else if (value.isDouble()) {
|
||||
material->setOpacity(value.toDouble());
|
||||
}
|
||||
} else if (key == "unlit") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isString() && value.toString() == FALLTHROUGH) {
|
||||
material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::UNLIT_VAL_BIT);
|
||||
} else if (value.isBool()) {
|
||||
material->setUnlit(value.toBool());
|
||||
}
|
||||
} else if (key == "albedo") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isString() && value.toString() == FALLTHROUGH) {
|
||||
material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::ALBEDO_VAL_BIT);
|
||||
} else {
|
||||
glm::vec3 color;
|
||||
bool isSRGB;
|
||||
bool valid = parseJSONColor(value, color, isSRGB);
|
||||
if (valid) {
|
||||
material->setAlbedo(color, isSRGB);
|
||||
}
|
||||
}
|
||||
} else if (key == "roughness") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isString() && value.toString() == FALLTHROUGH) {
|
||||
material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::GLOSSY_VAL_BIT);
|
||||
} else if (value.isDouble()) {
|
||||
material->setRoughness(value.toDouble());
|
||||
}
|
||||
} else if (key == "metallic") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isString() && value.toString() == FALLTHROUGH) {
|
||||
material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::METALLIC_VAL_BIT);
|
||||
} else if (value.isDouble()) {
|
||||
material->setMetallic(value.toDouble());
|
||||
}
|
||||
} else if (key == "scattering") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isString() && value.toString() == FALLTHROUGH) {
|
||||
material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::SCATTERING_VAL_BIT);
|
||||
} else if (value.isDouble()) {
|
||||
material->setScattering(value.toDouble());
|
||||
}
|
||||
} else if (key == "emissiveMap") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isString()) {
|
||||
auto valueString = value.toString();
|
||||
if (valueString == FALLTHROUGH) {
|
||||
material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::EMISSIVE_MAP_BIT);
|
||||
} else {
|
||||
material->setEmissiveMap(baseUrl.resolved(valueString));
|
||||
}
|
||||
}
|
||||
} else if (key == "albedoMap") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isString()) {
|
||||
QString valueString = value.toString();
|
||||
if (valueString == FALLTHROUGH) {
|
||||
material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::ALBEDO_MAP_BIT);
|
||||
} else {
|
||||
bool useAlphaChannel = false;
|
||||
auto opacityMap = materialJSON.find("opacityMap");
|
||||
if (opacityMap != materialJSON.end() && opacityMap->isString() && opacityMap->toString() == valueString) {
|
||||
useAlphaChannel = true;
|
||||
}
|
||||
material->setAlbedoMap(baseUrl.resolved(valueString), useAlphaChannel);
|
||||
}
|
||||
}
|
||||
} else if (key == "roughnessMap") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isString()) {
|
||||
auto valueString = value.toString();
|
||||
if (valueString == FALLTHROUGH) {
|
||||
material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::ROUGHNESS_MAP_BIT);
|
||||
} else {
|
||||
material->setRoughnessMap(baseUrl.resolved(valueString), false);
|
||||
}
|
||||
}
|
||||
} else if (key == "glossMap") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isString()) {
|
||||
auto valueString = value.toString();
|
||||
if (valueString == FALLTHROUGH) {
|
||||
material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::ROUGHNESS_MAP_BIT);
|
||||
} else {
|
||||
material->setRoughnessMap(baseUrl.resolved(valueString), true);
|
||||
}
|
||||
}
|
||||
} else if (key == "metallicMap") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isString()) {
|
||||
auto valueString = value.toString();
|
||||
if (valueString == FALLTHROUGH) {
|
||||
material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::METALLIC_MAP_BIT);
|
||||
} else {
|
||||
material->setMetallicMap(baseUrl.resolved(valueString), false);
|
||||
}
|
||||
}
|
||||
} else if (key == "specularMap") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isString()) {
|
||||
auto valueString = value.toString();
|
||||
if (valueString == FALLTHROUGH) {
|
||||
material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::METALLIC_MAP_BIT);
|
||||
} else {
|
||||
material->setMetallicMap(baseUrl.resolved(valueString), true);
|
||||
}
|
||||
}
|
||||
} else if (key == "normalMap") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isString()) {
|
||||
auto valueString = value.toString();
|
||||
if (valueString == FALLTHROUGH) {
|
||||
material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::NORMAL_MAP_BIT);
|
||||
} else {
|
||||
material->setNormalMap(baseUrl.resolved(valueString), false);
|
||||
}
|
||||
}
|
||||
} else if (key == "bumpMap") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isString()) {
|
||||
auto valueString = value.toString();
|
||||
if (valueString == FALLTHROUGH) {
|
||||
material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::NORMAL_MAP_BIT);
|
||||
} else {
|
||||
material->setNormalMap(baseUrl.resolved(valueString), true);
|
||||
}
|
||||
}
|
||||
} else if (key == "occlusionMap") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isString()) {
|
||||
auto valueString = value.toString();
|
||||
if (valueString == FALLTHROUGH) {
|
||||
material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::OCCLUSION_MAP_BIT);
|
||||
} else {
|
||||
material->setOcclusionMap(baseUrl.resolved(valueString));
|
||||
}
|
||||
}
|
||||
} else if (key == "scatteringMap") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isString()) {
|
||||
auto valueString = value.toString();
|
||||
if (valueString == FALLTHROUGH) {
|
||||
material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::SCATTERING_MAP_BIT);
|
||||
} else {
|
||||
material->setScatteringMap(baseUrl.resolved(valueString));
|
||||
}
|
||||
}
|
||||
} else if (key == "lightMap") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isString()) {
|
||||
auto valueString = value.toString();
|
||||
if (valueString == FALLTHROUGH) {
|
||||
material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::LIGHTMAP_MAP_BIT);
|
||||
} else {
|
||||
material->setLightmapMap(baseUrl.resolved(valueString));
|
||||
}
|
||||
}
|
||||
} else if (key == "texCoordTransform0") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isString()) {
|
||||
auto valueString = value.toString();
|
||||
if (valueString == FALLTHROUGH) {
|
||||
material->setPropertyDoesFallthrough(graphics::Material::ExtraFlagBit::TEXCOORDTRANSFORM0);
|
||||
}
|
||||
}
|
||||
// TODO: implement texCoordTransform0
|
||||
} else if (key == "texCoordTransform1") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isString()) {
|
||||
auto valueString = value.toString();
|
||||
if (valueString == FALLTHROUGH) {
|
||||
material->setPropertyDoesFallthrough(graphics::Material::ExtraFlagBit::TEXCOORDTRANSFORM1);
|
||||
}
|
||||
}
|
||||
// TODO: implement texCoordTransform1
|
||||
} else if (key == "lightmapParams") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isString()) {
|
||||
auto valueString = value.toString();
|
||||
if (valueString == FALLTHROUGH) {
|
||||
material->setPropertyDoesFallthrough(graphics::Material::ExtraFlagBit::LIGHTMAP_PARAMS);
|
||||
}
|
||||
}
|
||||
// TODO: implement lightmapParams
|
||||
} else if (key == "materialParams") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isString()) {
|
||||
auto valueString = value.toString();
|
||||
if (valueString == FALLTHROUGH) {
|
||||
material->setPropertyDoesFallthrough(graphics::Material::ExtraFlagBit::MATERIAL_PARAMS);
|
||||
}
|
||||
}
|
||||
// TODO: implement materialParams
|
||||
} else if (key == "defaultFallthrough") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isBool()) {
|
||||
material->setDefaultFallthrough(value.toBool());
|
||||
}
|
||||
material->setAlbedoMap(baseUrl.resolved(urlString), useAlphaChannel);
|
||||
}
|
||||
} else if (key == "roughnessMap") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isString()) {
|
||||
material->setRoughnessMap(baseUrl.resolved(value.toString()), false);
|
||||
}
|
||||
} else if (key == "glossMap") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isString()) {
|
||||
material->setRoughnessMap(baseUrl.resolved(value.toString()), true);
|
||||
}
|
||||
} else if (key == "metallicMap") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isString()) {
|
||||
material->setMetallicMap(baseUrl.resolved(value.toString()), false);
|
||||
}
|
||||
} else if (key == "specularMap") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isString()) {
|
||||
material->setMetallicMap(baseUrl.resolved(value.toString()), true);
|
||||
}
|
||||
} else if (key == "normalMap") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isString()) {
|
||||
material->setNormalMap(baseUrl.resolved(value.toString()), false);
|
||||
}
|
||||
} else if (key == "bumpMap") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isString()) {
|
||||
material->setNormalMap(baseUrl.resolved(value.toString()), true);
|
||||
}
|
||||
} else if (key == "occlusionMap") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isString()) {
|
||||
material->setOcclusionMap(baseUrl.resolved(value.toString()));
|
||||
}
|
||||
} else if (key == "scatteringMap") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isString()) {
|
||||
material->setScatteringMap(baseUrl.resolved(value.toString()));
|
||||
}
|
||||
} else if (key == "lightMap") {
|
||||
auto value = materialJSON.value(key);
|
||||
if (value.isString()) {
|
||||
material->setLightmapMap(baseUrl.resolved(value.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -588,6 +588,29 @@ void AccountManager::requestAccessTokenWithSteam(QByteArray authSessionTicket) {
|
|||
connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestAccessTokenError(QNetworkReply::NetworkError)));
|
||||
}
|
||||
|
||||
void AccountManager::requestAccessTokenWithOculus(const QString& nonce, const QString &oculusID) {
|
||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
|
||||
QNetworkRequest request;
|
||||
request.setHeader(QNetworkRequest::UserAgentHeader, _userAgentGetter());
|
||||
|
||||
QUrl grantURL = _authURL;
|
||||
grantURL.setPath("/oauth/token");
|
||||
|
||||
QByteArray postData;
|
||||
postData.append("grant_type=password&");
|
||||
postData.append("oculus_nonce=" + nonce + "&");
|
||||
postData.append("oculus_id=" + oculusID + "&");
|
||||
postData.append("scope=" + ACCOUNT_MANAGER_REQUESTED_SCOPE);
|
||||
|
||||
request.setUrl(grantURL);
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
||||
|
||||
QNetworkReply* requestReply = networkAccessManager.post(request, postData);
|
||||
connect(requestReply, &QNetworkReply::finished, this, &AccountManager::requestAccessTokenFinished);
|
||||
connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestAccessTokenError(QNetworkReply::NetworkError)));
|
||||
}
|
||||
|
||||
void AccountManager::refreshAccessToken() {
|
||||
|
||||
// we can't refresh our access token if we don't have a refresh token, so check for that first
|
||||
|
|
|
@ -106,6 +106,7 @@ public:
|
|||
public slots:
|
||||
void requestAccessToken(const QString& login, const QString& password);
|
||||
void requestAccessTokenWithSteam(QByteArray authSessionTicket);
|
||||
void requestAccessTokenWithOculus(const QString& nonce, const QString& oculusID);
|
||||
void requestAccessTokenWithAuthCode(const QString& authCode,
|
||||
const QString& clientId,
|
||||
const QString& clientSecret,
|
||||
|
|
|
@ -33,7 +33,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
case PacketType::EntityEdit:
|
||||
case PacketType::EntityData:
|
||||
case PacketType::EntityPhysics:
|
||||
return static_cast<PacketVersion>(EntityVersion::FixProtocolVersionBumpMismatch);
|
||||
return static_cast<PacketVersion>(EntityVersion::LAST_PACKET_TYPE);
|
||||
case PacketType::EntityQuery:
|
||||
return static_cast<PacketVersion>(EntityQueryPacketVersion::ConicalFrustums);
|
||||
case PacketType::AvatarIdentity:
|
||||
|
|
|
@ -255,7 +255,13 @@ enum class EntityVersion : PacketVersion {
|
|||
MorePropertiesCleanup,
|
||||
FixPropertiesFromCleanup,
|
||||
UpdatedPolyLines,
|
||||
FixProtocolVersionBumpMismatch
|
||||
FixProtocolVersionBumpMismatch,
|
||||
MigrateOverlayRenderProperties,
|
||||
MissingWebEntityProperties,
|
||||
|
||||
// Add new versions above here
|
||||
NUM_PACKET_TYPE,
|
||||
LAST_PACKET_TYPE = NUM_PACKET_TYPE - 1
|
||||
};
|
||||
|
||||
enum class EntityScriptCallMethodVersion : PacketVersion {
|
||||
|
|
43
libraries/octree/src/OctreePacketData.cpp
Normal file → Executable file
43
libraries/octree/src/OctreePacketData.cpp
Normal file → Executable file
|
@ -77,8 +77,8 @@ bool OctreePacketData::append(const unsigned char* data, int length) {
|
|||
_bytesAvailable -= length;
|
||||
success = true;
|
||||
_dirty = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#ifdef WANT_DEBUG
|
||||
if (!success) {
|
||||
qCDebug(octree) << "OctreePacketData::append(const unsigned char* data, int length) FAILING....";
|
||||
|
@ -97,7 +97,7 @@ bool OctreePacketData::append(unsigned char byte) {
|
|||
if (_bytesAvailable > 0) {
|
||||
_uncompressed[_bytesInUse] = byte;
|
||||
_bytesInUse++;
|
||||
_bytesAvailable--;
|
||||
_bytesAvailable--;
|
||||
success = true;
|
||||
_dirty = true;
|
||||
}
|
||||
|
@ -110,13 +110,13 @@ bool OctreePacketData::reserveBitMask() {
|
|||
|
||||
bool OctreePacketData::reserveBytes(int numberOfBytes) {
|
||||
bool success = false;
|
||||
|
||||
|
||||
if (_bytesAvailable >= numberOfBytes) {
|
||||
_bytesReserved += numberOfBytes;
|
||||
_bytesAvailable -= numberOfBytes;
|
||||
success = true;
|
||||
}
|
||||
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
|
@ -188,7 +188,7 @@ bool OctreePacketData::startSubTree(const unsigned char* octcode) {
|
|||
|
||||
const unsigned char* OctreePacketData::getFinalizedData() {
|
||||
if (!_enableCompression) {
|
||||
return &_uncompressed[0];
|
||||
return &_uncompressed[0];
|
||||
}
|
||||
|
||||
if (_dirty) {
|
||||
|
@ -197,7 +197,7 @@ const unsigned char* OctreePacketData::getFinalizedData() {
|
|||
}
|
||||
compressContent();
|
||||
}
|
||||
return &_compressed[0];
|
||||
return &_compressed[0];
|
||||
}
|
||||
|
||||
int OctreePacketData::getFinalizedSize() {
|
||||
|
@ -223,7 +223,7 @@ void OctreePacketData::endSubTree() {
|
|||
void OctreePacketData::discardSubTree() {
|
||||
int bytesInSubTree = _bytesInUse - _subTreeAt;
|
||||
_bytesInUse -= bytesInSubTree;
|
||||
_bytesAvailable += bytesInSubTree;
|
||||
_bytesAvailable += bytesInSubTree;
|
||||
_subTreeAt = _bytesInUse; // should be the same actually...
|
||||
_dirty = true;
|
||||
|
||||
|
@ -231,7 +231,7 @@ void OctreePacketData::discardSubTree() {
|
|||
int reduceBytesOfOctalCodes = _bytesOfOctalCodes - _bytesOfOctalCodesCurrentSubTree;
|
||||
_bytesOfOctalCodes = _bytesOfOctalCodesCurrentSubTree;
|
||||
_totalBytesOfOctalCodes -= reduceBytesOfOctalCodes;
|
||||
|
||||
|
||||
// if we discard the subtree then reset reserved bytes to the value when we started the subtree
|
||||
_bytesReserved = _subTreeBytesReserved;
|
||||
}
|
||||
|
@ -243,7 +243,7 @@ LevelDetails OctreePacketData::startLevel() {
|
|||
|
||||
void OctreePacketData::discardLevel(LevelDetails key) {
|
||||
int bytesInLevel = _bytesInUse - key._startIndex;
|
||||
|
||||
|
||||
// reset statistics...
|
||||
int reduceBytesOfOctalCodes = _bytesOfOctalCodes - key._bytesOfOctalCodes;
|
||||
int reduceBytesOfBitMasks = _bytesOfBitMasks - key._bytesOfBitmasks;
|
||||
|
@ -261,11 +261,11 @@ void OctreePacketData::discardLevel(LevelDetails key) {
|
|||
qCDebug(octree, "discardLevel() BEFORE _dirty=%s bytesInLevel=%d _compressedBytes=%d _bytesInUse=%d",
|
||||
debug::valueOf(_dirty), bytesInLevel, _compressedBytes, _bytesInUse);
|
||||
}
|
||||
|
||||
|
||||
_bytesInUse -= bytesInLevel;
|
||||
_bytesAvailable += bytesInLevel;
|
||||
_bytesAvailable += bytesInLevel;
|
||||
_dirty = true;
|
||||
|
||||
|
||||
// reserved bytes are reset to the value when the level started
|
||||
_bytesReserved = key._bytesReservedAtStart;
|
||||
|
||||
|
@ -333,7 +333,7 @@ bool OctreePacketData::appendValue(uint8_t value) {
|
|||
|
||||
bool OctreePacketData::appendValue(uint16_t value) {
|
||||
const unsigned char* data = (const unsigned char*)&value;
|
||||
|
||||
|
||||
int length = sizeof(value);
|
||||
bool success = append(data, length);
|
||||
if (success) {
|
||||
|
@ -506,10 +506,11 @@ bool OctreePacketData::appendValue(bool value) {
|
|||
|
||||
bool OctreePacketData::appendValue(const QString& string) {
|
||||
// TODO: make this a ByteCountCoded leading byte
|
||||
uint16_t length = string.size() + 1; // include NULL
|
||||
QByteArray utf8Array = string.toUtf8();
|
||||
uint16_t length = utf8Array.length(); // no NULL
|
||||
bool success = appendValue(length);
|
||||
if (success) {
|
||||
success = appendRawData((const unsigned char*)qPrintable(string), length);
|
||||
success = appendRawData((const unsigned char*)utf8Array.constData(), length);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
@ -666,7 +667,7 @@ void OctreePacketData::debugContent() {
|
|||
}
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
|
||||
qCDebug(octree, "OctreePacketData::debugContent()... UNCOMPRESSED DATA.... size=%d",_bytesInUse);
|
||||
perline=0;
|
||||
for (int i = 0; i < _bytesInUse; i++) {
|
||||
|
@ -702,16 +703,16 @@ int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, glm::u
|
|||
return sizeof(result);
|
||||
}
|
||||
|
||||
int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QString& result) {
|
||||
int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QString& result) {
|
||||
uint16_t length;
|
||||
memcpy(&length, dataBytes, sizeof(length));
|
||||
dataBytes += sizeof(length);
|
||||
QString value((const char*)dataBytes);
|
||||
QString value = QString::fromUtf8((const char*)dataBytes, length);
|
||||
result = value;
|
||||
return sizeof(length) + length;
|
||||
}
|
||||
|
||||
int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QUuid& result) {
|
||||
int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QUuid& result) {
|
||||
uint16_t length;
|
||||
memcpy(&length, dataBytes, sizeof(length));
|
||||
dataBytes += sizeof(length);
|
||||
|
@ -819,4 +820,4 @@ int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, AACube
|
|||
int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QRect& result) {
|
||||
memcpy(&result, dataBytes, sizeof(result));
|
||||
return sizeof(result);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,6 +35,9 @@
|
|||
|
||||
#include "MaterialMappingMode.h"
|
||||
#include "BillboardMode.h"
|
||||
#include "RenderLayer.h"
|
||||
#include "PrimitiveMode.h"
|
||||
#include "WebInputMode.h"
|
||||
|
||||
#include "OctreeConstants.h"
|
||||
#include "OctreeElement.h"
|
||||
|
@ -263,6 +266,9 @@ public:
|
|||
static int unpackDataFromBytes(const unsigned char* dataBytes, ShapeType& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }
|
||||
static int unpackDataFromBytes(const unsigned char* dataBytes, MaterialMappingMode& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }
|
||||
static int unpackDataFromBytes(const unsigned char* dataBytes, BillboardMode& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }
|
||||
static int unpackDataFromBytes(const unsigned char* dataBytes, RenderLayer& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }
|
||||
static int unpackDataFromBytes(const unsigned char* dataBytes, PrimitiveMode& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }
|
||||
static int unpackDataFromBytes(const unsigned char* dataBytes, WebInputMode& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }
|
||||
static int unpackDataFromBytes(const unsigned char* dataBytes, glm::vec2& result);
|
||||
static int unpackDataFromBytes(const unsigned char* dataBytes, glm::vec3& result);
|
||||
static int unpackDataFromBytes(const unsigned char* dataBytes, glm::u8vec3& result);
|
||||
|
|
|
@ -109,7 +109,8 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) {
|
|||
}
|
||||
_dynamicsWorld = nullptr;
|
||||
}
|
||||
int32_t collisionGroup = computeCollisionGroup();
|
||||
int32_t collisionMask = computeCollisionMask();
|
||||
int32_t collisionGroup = BULLET_COLLISION_GROUP_MY_AVATAR;
|
||||
if (_rigidBody) {
|
||||
updateMassProperties();
|
||||
}
|
||||
|
@ -117,7 +118,7 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) {
|
|||
// add to new world
|
||||
_dynamicsWorld = world;
|
||||
_pendingFlags &= ~PENDING_FLAG_JUMP;
|
||||
_dynamicsWorld->addRigidBody(_rigidBody, collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR);
|
||||
_dynamicsWorld->addRigidBody(_rigidBody, collisionGroup, collisionMask);
|
||||
_dynamicsWorld->addAction(this);
|
||||
// restore gravity settings because adding an object to the world overwrites its gravity setting
|
||||
_rigidBody->setGravity(_currentGravity * _currentUp);
|
||||
|
@ -127,7 +128,7 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) {
|
|||
assert(shape && shape->getShapeType() == CONVEX_HULL_SHAPE_PROXYTYPE);
|
||||
_ghost.setCharacterShape(static_cast<btConvexHullShape*>(shape));
|
||||
}
|
||||
_ghost.setCollisionGroupAndMask(collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR & (~ collisionGroup));
|
||||
_ghost.setCollisionGroupAndMask(collisionGroup, collisionMask & (~ collisionGroup));
|
||||
_ghost.setCollisionWorld(_dynamicsWorld);
|
||||
_ghost.setRadiusAndHalfHeight(_radius, _halfHeight);
|
||||
if (_rigidBody) {
|
||||
|
@ -384,8 +385,8 @@ static const char* stateToStr(CharacterController::State state) {
|
|||
#endif // #ifdef DEBUG_STATE_CHANGE
|
||||
|
||||
void CharacterController::updateCurrentGravity() {
|
||||
int32_t collisionGroup = computeCollisionGroup();
|
||||
if (_state == State::Hover || collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS) {
|
||||
int32_t collisionMask = computeCollisionMask();
|
||||
if (_state == State::Hover || collisionMask == BULLET_COLLISION_MASK_COLLISIONLESS) {
|
||||
_currentGravity = 0.0f;
|
||||
} else {
|
||||
_currentGravity = _gravity;
|
||||
|
@ -458,28 +459,7 @@ void CharacterController::setLocalBoundingBox(const glm::vec3& minCorner, const
|
|||
void CharacterController::setCollisionless(bool collisionless) {
|
||||
if (collisionless != _collisionless) {
|
||||
_collisionless = collisionless;
|
||||
_pendingFlags |= PENDING_FLAG_UPDATE_COLLISION_GROUP;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t CharacterController::computeCollisionGroup() const {
|
||||
if (_collisionless) {
|
||||
return _collisionlessAllowed ? BULLET_COLLISION_GROUP_COLLISIONLESS : BULLET_COLLISION_GROUP_MY_AVATAR;
|
||||
} else {
|
||||
return BULLET_COLLISION_GROUP_MY_AVATAR;
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterController::handleChangedCollisionGroup() {
|
||||
if (_pendingFlags & PENDING_FLAG_UPDATE_COLLISION_GROUP) {
|
||||
// ATM the easiest way to update collision groups is to remove/re-add the RigidBody
|
||||
if (_dynamicsWorld) {
|
||||
_dynamicsWorld->removeRigidBody(_rigidBody);
|
||||
int32_t collisionGroup = computeCollisionGroup();
|
||||
_dynamicsWorld->addRigidBody(_rigidBody, collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR);
|
||||
}
|
||||
_pendingFlags &= ~PENDING_FLAG_UPDATE_COLLISION_GROUP;
|
||||
updateCurrentGravity();
|
||||
_pendingFlags |= PENDING_FLAG_UPDATE_COLLISION_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -567,8 +547,8 @@ void CharacterController::applyMotor(int index, btScalar dt, btVector3& worldVel
|
|||
btScalar angle = motor.rotation.getAngle();
|
||||
btVector3 velocity = worldVelocity.rotate(axis, -angle);
|
||||
|
||||
int32_t collisionGroup = computeCollisionGroup();
|
||||
if (collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS ||
|
||||
int32_t collisionMask = computeCollisionMask();
|
||||
if (collisionMask == BULLET_COLLISION_MASK_COLLISIONLESS ||
|
||||
_state == State::Hover || motor.hTimescale == motor.vTimescale) {
|
||||
// modify velocity
|
||||
btScalar tau = dt / motor.hTimescale;
|
||||
|
@ -708,11 +688,11 @@ void CharacterController::updateState() {
|
|||
btVector3 rayStart = _position;
|
||||
|
||||
btScalar rayLength = _radius;
|
||||
int32_t collisionGroup = computeCollisionGroup();
|
||||
if (collisionGroup == BULLET_COLLISION_GROUP_MY_AVATAR) {
|
||||
rayLength += _scaleFactor * DEFAULT_AVATAR_FALL_HEIGHT;
|
||||
} else {
|
||||
int32_t collisionMask = computeCollisionMask();
|
||||
if (collisionMask == BULLET_COLLISION_MASK_COLLISIONLESS) {
|
||||
rayLength += MIN_HOVER_HEIGHT;
|
||||
} else {
|
||||
rayLength += _scaleFactor * DEFAULT_AVATAR_FALL_HEIGHT;
|
||||
}
|
||||
btVector3 rayEnd = rayStart - rayLength * _currentUp;
|
||||
|
||||
|
@ -746,69 +726,7 @@ void CharacterController::updateState() {
|
|||
|
||||
// disable normal state transitions while collisionless
|
||||
const btScalar MAX_WALKING_SPEED = 2.65f;
|
||||
if (collisionGroup == BULLET_COLLISION_GROUP_MY_AVATAR) {
|
||||
switch (_state) {
|
||||
case State::Ground:
|
||||
if (!rayHasHit && !_hasSupport) {
|
||||
SET_STATE(State::Hover, "no ground detected");
|
||||
} else if (_pendingFlags & PENDING_FLAG_JUMP && _jumpButtonDownCount != _takeoffJumpButtonID) {
|
||||
_takeoffJumpButtonID = _jumpButtonDownCount;
|
||||
_takeoffToInAirStartTime = now;
|
||||
SET_STATE(State::Takeoff, "jump pressed");
|
||||
} else if (rayHasHit && !_hasSupport && _floorDistance > GROUND_TO_FLY_THRESHOLD) {
|
||||
SET_STATE(State::InAir, "falling");
|
||||
}
|
||||
break;
|
||||
case State::Takeoff:
|
||||
if (!rayHasHit && !_hasSupport) {
|
||||
SET_STATE(State::Hover, "no ground");
|
||||
} else if ((now - _takeoffToInAirStartTime) > TAKE_OFF_TO_IN_AIR_PERIOD) {
|
||||
SET_STATE(State::InAir, "takeoff done");
|
||||
|
||||
// compute jumpSpeed based on the scaled jump height for the default avatar in default gravity.
|
||||
const float jumpHeight = std::max(_scaleFactor * DEFAULT_AVATAR_JUMP_HEIGHT, DEFAULT_AVATAR_MIN_JUMP_HEIGHT);
|
||||
const float jumpSpeed = sqrtf(2.0f * -DEFAULT_AVATAR_GRAVITY * jumpHeight);
|
||||
velocity += jumpSpeed * _currentUp;
|
||||
_rigidBody->setLinearVelocity(velocity);
|
||||
}
|
||||
break;
|
||||
case State::InAir: {
|
||||
const float jumpHeight = std::max(_scaleFactor * DEFAULT_AVATAR_JUMP_HEIGHT, DEFAULT_AVATAR_MIN_JUMP_HEIGHT);
|
||||
const float jumpSpeed = sqrtf(2.0f * -DEFAULT_AVATAR_GRAVITY * jumpHeight);
|
||||
if ((velocity.dot(_currentUp) <= (jumpSpeed / 2.0f)) && ((_floorDistance < FLY_TO_GROUND_THRESHOLD) || _hasSupport)) {
|
||||
SET_STATE(State::Ground, "hit ground");
|
||||
} else if (_flyingAllowed) {
|
||||
btVector3 desiredVelocity = _targetVelocity;
|
||||
if (desiredVelocity.length2() < MIN_TARGET_SPEED_SQUARED) {
|
||||
desiredVelocity = btVector3(0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
bool vertTargetSpeedIsNonZero = desiredVelocity.dot(_currentUp) > MIN_TARGET_SPEED;
|
||||
if ((jumpButtonHeld || vertTargetSpeedIsNonZero) && (_takeoffJumpButtonID != _jumpButtonDownCount)) {
|
||||
SET_STATE(State::Hover, "double jump button");
|
||||
} else if ((jumpButtonHeld || vertTargetSpeedIsNonZero) && (now - _jumpButtonDownStartTime) > JUMP_TO_HOVER_PERIOD) {
|
||||
SET_STATE(State::Hover, "jump button held");
|
||||
} else if (_floorDistance > _scaleFactor * DEFAULT_AVATAR_FALL_HEIGHT) {
|
||||
// Transition to hover if we are above the fall threshold
|
||||
SET_STATE(State::Hover, "above fall threshold");
|
||||
}
|
||||
} else if (!rayHasHit && !_hasSupport) {
|
||||
SET_STATE(State::Hover, "no ground detected");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case State::Hover:
|
||||
btScalar horizontalSpeed = (velocity - velocity.dot(_currentUp) * _currentUp).length();
|
||||
bool flyingFast = horizontalSpeed > (MAX_WALKING_SPEED * 0.75f);
|
||||
if (!_flyingAllowed && rayHasHit) {
|
||||
SET_STATE(State::InAir, "flying not allowed");
|
||||
} else if ((_floorDistance < MIN_HOVER_HEIGHT) && !jumpButtonHeld && !flyingFast) {
|
||||
SET_STATE(State::InAir, "near ground");
|
||||
} else if (((_floorDistance < FLY_TO_GROUND_THRESHOLD) || _hasSupport) && !flyingFast) {
|
||||
SET_STATE(State::Ground, "touching ground");
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (collisionMask == BULLET_COLLISION_MASK_COLLISIONLESS) {
|
||||
// when collisionless: only switch between State::Ground and State::Hover
|
||||
// and bypass state debugging
|
||||
if (rayHasHit) {
|
||||
|
@ -820,6 +738,68 @@ void CharacterController::updateState() {
|
|||
} else {
|
||||
_state = State::Hover;
|
||||
}
|
||||
} else {
|
||||
switch (_state) {
|
||||
case State::Ground:
|
||||
if (!rayHasHit && !_hasSupport) {
|
||||
SET_STATE(State::Hover, "no ground detected");
|
||||
} else if (_pendingFlags & PENDING_FLAG_JUMP && _jumpButtonDownCount != _takeoffJumpButtonID) {
|
||||
_takeoffJumpButtonID = _jumpButtonDownCount;
|
||||
_takeoffToInAirStartTime = now;
|
||||
SET_STATE(State::Takeoff, "jump pressed");
|
||||
} else if (rayHasHit && !_hasSupport && _floorDistance > GROUND_TO_FLY_THRESHOLD) {
|
||||
SET_STATE(State::InAir, "falling");
|
||||
}
|
||||
break;
|
||||
case State::Takeoff:
|
||||
if (!rayHasHit && !_hasSupport) {
|
||||
SET_STATE(State::Hover, "no ground");
|
||||
} else if ((now - _takeoffToInAirStartTime) > TAKE_OFF_TO_IN_AIR_PERIOD) {
|
||||
SET_STATE(State::InAir, "takeoff done");
|
||||
|
||||
// compute jumpSpeed based on the scaled jump height for the default avatar in default gravity.
|
||||
const float jumpHeight = std::max(_scaleFactor * DEFAULT_AVATAR_JUMP_HEIGHT, DEFAULT_AVATAR_MIN_JUMP_HEIGHT);
|
||||
const float jumpSpeed = sqrtf(2.0f * -DEFAULT_AVATAR_GRAVITY * jumpHeight);
|
||||
velocity += jumpSpeed * _currentUp;
|
||||
_rigidBody->setLinearVelocity(velocity);
|
||||
}
|
||||
break;
|
||||
case State::InAir: {
|
||||
const float jumpHeight = std::max(_scaleFactor * DEFAULT_AVATAR_JUMP_HEIGHT, DEFAULT_AVATAR_MIN_JUMP_HEIGHT);
|
||||
const float jumpSpeed = sqrtf(2.0f * -DEFAULT_AVATAR_GRAVITY * jumpHeight);
|
||||
if ((velocity.dot(_currentUp) <= (jumpSpeed / 2.0f)) && ((_floorDistance < FLY_TO_GROUND_THRESHOLD) || _hasSupport)) {
|
||||
SET_STATE(State::Ground, "hit ground");
|
||||
} else if (_flyingAllowed) {
|
||||
btVector3 desiredVelocity = _targetVelocity;
|
||||
if (desiredVelocity.length2() < MIN_TARGET_SPEED_SQUARED) {
|
||||
desiredVelocity = btVector3(0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
bool vertTargetSpeedIsNonZero = desiredVelocity.dot(_currentUp) > MIN_TARGET_SPEED;
|
||||
if ((jumpButtonHeld || vertTargetSpeedIsNonZero) && (_takeoffJumpButtonID != _jumpButtonDownCount)) {
|
||||
SET_STATE(State::Hover, "double jump button");
|
||||
} else if ((jumpButtonHeld || vertTargetSpeedIsNonZero) && (now - _jumpButtonDownStartTime) > JUMP_TO_HOVER_PERIOD) {
|
||||
SET_STATE(State::Hover, "jump button held");
|
||||
} else if (_floorDistance > _scaleFactor * DEFAULT_AVATAR_FALL_HEIGHT) {
|
||||
// Transition to hover if we are above the fall threshold
|
||||
SET_STATE(State::Hover, "above fall threshold");
|
||||
}
|
||||
} else if (!rayHasHit && !_hasSupport) {
|
||||
SET_STATE(State::Hover, "no ground detected");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case State::Hover:
|
||||
btScalar horizontalSpeed = (velocity - velocity.dot(_currentUp) * _currentUp).length();
|
||||
bool flyingFast = horizontalSpeed > (MAX_WALKING_SPEED * 0.75f);
|
||||
if (!_flyingAllowed && rayHasHit) {
|
||||
SET_STATE(State::InAir, "flying not allowed");
|
||||
} else if ((_floorDistance < MIN_HOVER_HEIGHT) && !jumpButtonHeld && !flyingFast) {
|
||||
SET_STATE(State::InAir, "near ground");
|
||||
} else if (((_floorDistance < FLY_TO_GROUND_THRESHOLD) || _hasSupport) && !flyingFast) {
|
||||
SET_STATE(State::Ground, "touching ground");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -866,6 +846,6 @@ void CharacterController::setFlyingAllowed(bool value) {
|
|||
void CharacterController::setCollisionlessAllowed(bool value) {
|
||||
if (value != _collisionlessAllowed) {
|
||||
_collisionlessAllowed = value;
|
||||
_pendingFlags |= PENDING_FLAG_UPDATE_COLLISION_GROUP;
|
||||
_pendingFlags |= PENDING_FLAG_UPDATE_COLLISION_MASK;
|
||||
}
|
||||
}
|
||||
|
|
8
libraries/physics/src/CharacterController.h
Normal file → Executable file
8
libraries/physics/src/CharacterController.h
Normal file → Executable file
|
@ -30,7 +30,7 @@ const uint32_t PENDING_FLAG_ADD_TO_SIMULATION = 1U << 0;
|
|||
const uint32_t PENDING_FLAG_REMOVE_FROM_SIMULATION = 1U << 1;
|
||||
const uint32_t PENDING_FLAG_UPDATE_SHAPE = 1U << 2;
|
||||
const uint32_t PENDING_FLAG_JUMP = 1U << 3;
|
||||
const uint32_t PENDING_FLAG_UPDATE_COLLISION_GROUP = 1U << 4;
|
||||
const uint32_t PENDING_FLAG_UPDATE_COLLISION_MASK = 1U << 4;
|
||||
const uint32_t PENDING_FLAG_RECOMPUTE_FLYING = 1U << 5;
|
||||
const float DEFAULT_MIN_FLOOR_NORMAL_DOT_UP = cosf(PI / 3.0f);
|
||||
|
||||
|
@ -120,14 +120,16 @@ public:
|
|||
bool isStuck() const { return _isStuck; }
|
||||
|
||||
void setCollisionless(bool collisionless);
|
||||
int32_t computeCollisionGroup() const;
|
||||
void handleChangedCollisionGroup();
|
||||
|
||||
virtual int32_t computeCollisionMask() const = 0;
|
||||
virtual void handleChangedCollisionMask() = 0;
|
||||
|
||||
bool getRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation);
|
||||
|
||||
void setFlyingAllowed(bool value);
|
||||
void setCollisionlessAllowed(bool value);
|
||||
|
||||
void setPendingFlagsUpdateCollisionMask(){ _pendingFlags |= PENDING_FLAG_UPDATE_COLLISION_MASK; }
|
||||
|
||||
protected:
|
||||
#ifdef DEBUG_STATE_CHANGE
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue