mirror of
https://github.com/lubosz/overte.git
synced 2025-04-27 01:15:36 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into QmlMarketplace
This commit is contained in:
commit
d3cffa50d6
99 changed files with 3202 additions and 3194 deletions
.gitignoreINSTALL.md
domain-server/src
interface
libraries
controllers/src/controllers
display-plugins/src/display-plugins
entities-renderer/src
RenderableEntityItem.cppRenderableEntityItem.hRenderableGridEntityItem.cppRenderableGridEntityItem.hRenderableImageEntityItem.cppRenderableImageEntityItem.hRenderableParticleEffectEntityItem.cppRenderableParticleEffectEntityItem.hRenderableShapeEntityItem.cppRenderableShapeEntityItem.hRenderableTextEntityItem.cppRenderableTextEntityItem.hRenderableWebEntityItem.cppRenderableWebEntityItem.h
entities/src
EntityItem.cppEntityItemProperties.cppEntityItemProperties.hEntityItemPropertiesMacros.hEntityPropertyFlags.hGridEntityItem.cppGridEntityItem.hImageEntityItem.cppImageEntityItem.hParticleEffectEntityItem.cppParticleEffectEntityItem.hPulsePropertyGroup.cppPulsePropertyGroup.hShapeEntityItem.cppShapeEntityItem.hTextEntityItem.cppTextEntityItem.hWebEntityItem.cppWebEntityItem.h
fbx/src
hfm/src/hfm
networking/src
octree/src
plugins/src/plugins
shared/src
plugins
oculus/src
OculusBaseDisplayPlugin.cppOculusBaseDisplayPlugin.hOculusControllerManager.cppOculusHelpers.cppOculusHelpers.hOculusPlatformPlugin.cppOculusPlatformPlugin.hOculusProvider.cpp
openvr/src
scripts
developer/utilities/render
system
server-console
tools/unity-avatar-exporter
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -104,4 +104,6 @@ tools/unity-avatar-exporter/Logs
|
|||
tools/unity-avatar-exporter/Packages
|
||||
tools/unity-avatar-exporter/ProjectSettings
|
||||
tools/unity-avatar-exporter/Temp
|
||||
|
||||
server-console/package-lock.json
|
||||
vcpkg/
|
||||
/tools/nitpick/compiledResources
|
||||
|
|
75
INSTALL.md
75
INSTALL.md
|
@ -4,7 +4,8 @@ During generation, CMake should produce an `install` target and a `package` targ
|
|||
|
||||
### Install
|
||||
|
||||
The `install` target will copy the High Fidelity targets and their dependencies to your `CMAKE_INSTALL_PREFIX`.
|
||||
The `install` target will copy the High Fidelity targets and their dependencies to your `CMAKE_INSTALL_PREFIX`.
|
||||
This variable is set by the `project(hifi)` command in `CMakeLists.txt` to `C:/Program Files/hifi` and stored in `build/CMakeCache.txt`
|
||||
|
||||
### Packaging
|
||||
|
||||
|
@ -14,17 +15,67 @@ To produce an installer, run the `package` target.
|
|||
|
||||
To produce an executable installer on Windows, the following are required:
|
||||
|
||||
- [Nullsoft Scriptable Install System](http://nsis.sourceforge.net/Download) - 3.0b3
|
||||
- [UAC Plug-in for Nullsoft](http://nsis.sourceforge.net/UAC_plug-in) - 0.2.4c
|
||||
- [nsProcess Plug-in for Nullsoft](http://nsis.sourceforge.net/NsProcess_plugin) - 1.6
|
||||
- [Inetc Plug-in for Nullsoft](http://nsis.sourceforge.net/Inetc_plug-in) - 1.0
|
||||
- [NSISpcre Plug-in for Nullsoft](http://nsis.sourceforge.net/NSISpcre_plug-in) - 1.0
|
||||
- [nsisSlideshow Plug-in for Nullsoft](http://nsis.sourceforge.net/NsisSlideshow_plug-in) - 1.7
|
||||
- [Nsisunz plug-in for Nullsoft](http://nsis.sourceforge.net/Nsisunz_plug-in)
|
||||
- [ApplicationID plug-in for Nullsoft](http://nsis.sourceforge.net/ApplicationID_plug-in) - 1.0
|
||||
1. [7-zip](<https://www.7-zip.org/download.html>)
|
||||
|
||||
Run the `package` target to create an executable installer using the Nullsoft Scriptable Install System.
|
||||
1. [Nullsoft Scriptable Install System](http://nsis.sourceforge.net/Download) - 3.04
|
||||
Install using defaults (will install to `C:\Program Files (x86)\NSIS`)
|
||||
1. [UAC Plug-in for Nullsoft](http://nsis.sourceforge.net/UAC_plug-in) - 0.2.4c
|
||||
1. Extract Zip
|
||||
1. Copy `UAC.nsh` to `C:\Program Files (x86)\NSIS\Include\`
|
||||
1. Copy `Plugins\x86-ansi\UAC.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-ansi\`
|
||||
1. Copy `Plugins\x86-unicode\UAC.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-unicode\`
|
||||
1. [nsProcess Plug-in for Nullsoft](http://nsis.sourceforge.net/NsProcess_plugin) - 1.6 (use the link marked **nsProcess_1_6.7z**)
|
||||
1. Extract Zip
|
||||
1. Copy `Include\nsProcess.nsh` to `C:\Program Files (x86)\NSIS\Include\`
|
||||
1. Copy `Plugins\nsProcess.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-ansi\`
|
||||
1. Copy `Plugins\nsProcessW.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-unicode\`
|
||||
|
||||
1. [InetC Plug-in for Nullsoft](http://nsis.sourceforge.net/Inetc_plug-in) - 1.0
|
||||
1. Extract Zip
|
||||
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
|
||||
1. Copy `NSISpre.nsh` to `C:\Program Files (x86)\NSIS\Include\`
|
||||
1. Copy `NSISpre.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-ansi\`
|
||||
|
||||
1. [nsisSlideshow Plug-in for Nullsoft](<http://wiz0u.free.fr/prog/nsisSlideshow/>) - 1.7
|
||||
1. Extract Zip
|
||||
1. Copy `bin\nsisSlideshow.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-ansi\`
|
||||
1. Copy `bin\nsisSlideshowW.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-unicode\`
|
||||
|
||||
1. [Nsisunz plug-in for Nullsoft](http://nsis.sourceforge.net/Nsisunz_plug-in)
|
||||
1. Download both Zips and unzip
|
||||
1. Copy `nsisunz\Release\nsisunz.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-ansi\`
|
||||
1. Copy `NSISunzU\Plugin unicode\nsisunz.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-unicode\`
|
||||
|
||||
1. [ApplicationID plug-in for Nullsoft]() - 1.0
|
||||
1. Download [`Pre-built DLLs`](<https://github.com/connectiblutz/NSIS-ApplicationID/releases/download/1.1/NSIS-ApplicationID.zip>)
|
||||
1. Extract Zip
|
||||
1. Copy `Release\ApplicationID.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-ansi\`
|
||||
1. Copy `ReleaseUnicode\ApplicationID.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-unicode\`
|
||||
|
||||
1. [npm](<https://www.npmjs.com/get-npm>)
|
||||
1. Install version 10.15.0 LTS
|
||||
|
||||
1. Perform a clean cmake from a new terminal.
|
||||
1. Open the `hifi.sln` Solution and select the Release configuration.
|
||||
1. Build the Solution.
|
||||
1. Build `packaged-server-console` (found under **Server Console**)
|
||||
This will add 2 folders to `build\server-console\` -
|
||||
`server-console-win32-x64` and `x64`
|
||||
1. Build CMakeTargets->PACKAGE
|
||||
Installer is now available in `build\_CPack_Packages\win64\NSIS`
|
||||
|
||||
#### OS X
|
||||
|
||||
Run the `package` target to create an Apple Disk Image (.dmg).
|
||||
1. [npm](<https://www.npmjs.com/get-npm>)
|
||||
Install version 10.15.0 LTS
|
||||
|
||||
1. Perform a clean cmake.
|
||||
1. Perform a Release build of ALL_BUILD
|
||||
1. Perform a Release build of `packaged-server-console`
|
||||
This will add a folder to `build\server-console\` -
|
||||
Sandbox-darwin-x64
|
||||
1. Perform a Release build of `package`
|
||||
Installer is now available in `build/_CPack_Packages/Darwin/DragNDrop
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -120,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)
|
||||
|
|
|
@ -150,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 });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -169,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 });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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>
|
||||
|
@ -801,7 +802,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)
|
||||
|
@ -2418,7 +2418,6 @@ void Application::updateVerboseLogging() {
|
|||
bool enable = menu->isOptionChecked(MenuOption::VerboseLogging);
|
||||
|
||||
QString rules =
|
||||
"hifi.*.debug=%1\n"
|
||||
"hifi.*.info=%1\n"
|
||||
"hifi.audio-stream.debug=false\n"
|
||||
"hifi.audio-stream.info=false";
|
||||
|
@ -2738,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
|
||||
|
@ -4883,6 +4883,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();
|
||||
|
||||
|
@ -6067,6 +6071,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()),
|
||||
|
@ -6080,7 +6091,8 @@ void Application::update(float deltaTime) {
|
|||
myAvatar->getRightArmCalibrationMat(),
|
||||
myAvatar->getLeftArmCalibrationMat(),
|
||||
myAvatar->getRightHandCalibrationMat(),
|
||||
myAvatar->getLeftHandCalibrationMat()
|
||||
myAvatar->getLeftHandCalibrationMat(),
|
||||
hmdAvatarAlignmentType
|
||||
};
|
||||
|
||||
InputPluginPointer keyboardMousePlugin;
|
||||
|
@ -8243,7 +8255,18 @@ void Application::toggleLogDialog() {
|
|||
return;
|
||||
}
|
||||
if (! _logDialog) {
|
||||
|
||||
bool keepOnTop =_keepLogWindowOnTop.get();
|
||||
#ifdef Q_OS_WIN
|
||||
_logDialog = new LogDialog(keepOnTop ? qApp->getWindow() : nullptr, getLogger());
|
||||
#else
|
||||
_logDialog = new LogDialog(nullptr, getLogger());
|
||||
|
||||
if (keepOnTop) {
|
||||
Qt::WindowFlags flags = _logDialog->windowFlags() | Qt::Tool;
|
||||
_logDialog->setWindowFlags(flags);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (_logDialog->isVisible()) {
|
||||
|
@ -8253,6 +8276,19 @@ void Application::toggleLogDialog() {
|
|||
}
|
||||
}
|
||||
|
||||
void Application::recreateLogWindow(int keepOnTop) {
|
||||
_keepLogWindowOnTop.set(keepOnTop != 0);
|
||||
if (_logDialog) {
|
||||
bool toggle = _logDialog->isVisible();
|
||||
_logDialog->close();
|
||||
_logDialog = nullptr;
|
||||
|
||||
if (toggle) {
|
||||
toggleLogDialog();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Application::toggleEntityScriptServerLogDialog() {
|
||||
if (! _entityScriptServerLogDialog) {
|
||||
_entityScriptServerLogDialog = new EntityScriptServerLogDialog(nullptr);
|
||||
|
|
|
@ -217,6 +217,8 @@ public:
|
|||
void setDesktopTabletScale(float desktopTabletScale);
|
||||
|
||||
bool getDesktopTabletBecomesToolbarSetting() { return _desktopTabletBecomesToolbarSetting.get(); }
|
||||
bool getLogWindowOnTopSetting() { return _keepLogWindowOnTop.get(); }
|
||||
void setLogWindowOnTopSetting(bool keepOnTop) { _keepLogWindowOnTop.set(keepOnTop); }
|
||||
void setDesktopTabletBecomesToolbarSetting(bool value);
|
||||
bool getHmdTabletBecomesToolbarSetting() { return _hmdTabletBecomesToolbarSetting.get(); }
|
||||
void setHmdTabletBecomesToolbarSetting(bool value);
|
||||
|
@ -365,6 +367,7 @@ public slots:
|
|||
Q_INVOKABLE void loadDialog();
|
||||
Q_INVOKABLE void loadScriptURLDialog() const;
|
||||
void toggleLogDialog();
|
||||
void recreateLogWindow(int);
|
||||
void toggleEntityScriptServerLogDialog();
|
||||
Q_INVOKABLE void showAssetServerWidget(QString filePath = "");
|
||||
Q_INVOKABLE void loadAddAvatarBookmarkDialog() const;
|
||||
|
@ -656,6 +659,7 @@ private:
|
|||
Setting::Handle<bool> _constrainToolbarPosition;
|
||||
Setting::Handle<QString> _preferredCursor;
|
||||
Setting::Handle<bool> _miniTabletEnabledSetting;
|
||||
Setting::Handle<bool> _keepLogWindowOnTop { "keepLogWindowOnTop", false };
|
||||
|
||||
float _scaleMirror;
|
||||
float _mirrorYawOffset;
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
@ -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));
|
||||
|
@ -4761,7 +4815,7 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) cons
|
|||
}
|
||||
|
||||
bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
|
||||
const float CYLINDER_TOP = 0.1f;
|
||||
const float CYLINDER_TOP = 2.0f;
|
||||
const float CYLINDER_BOTTOM = -1.5f;
|
||||
const float SITTING_BOTTOM = -0.02f;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
@ -519,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
|
||||
|
@ -1585,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
|
||||
|
@ -1773,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
|
||||
|
@ -1946,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;
|
||||
|
@ -1962,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
|
||||
|
|
|
@ -20,9 +20,9 @@
|
|||
#include <PathUtils.h>
|
||||
|
||||
const int TOP_BAR_HEIGHT = 124;
|
||||
const int INITIAL_WIDTH = 720;
|
||||
const int INITIAL_WIDTH = 800;
|
||||
const int INITIAL_HEIGHT = 480;
|
||||
const int MINIMAL_WIDTH = 700;
|
||||
const int MINIMAL_WIDTH = 780;
|
||||
const int SEARCH_BUTTON_LEFT = 25;
|
||||
const int SEARCH_BUTTON_WIDTH = 20;
|
||||
const int SEARCH_TOGGLE_BUTTON_WIDTH = 50;
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
|
||||
#include <shared/AbstractLoggerInterface.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "MainWindow.h"
|
||||
|
||||
const int REVEAL_BUTTON_WIDTH = 122;
|
||||
const int ALL_LOGS_BUTTON_WIDTH = 90;
|
||||
const int MARGIN_LEFT = 25;
|
||||
|
@ -148,6 +151,16 @@ LogDialog::LogDialog(QWidget* parent, AbstractLoggerInterface* logger) : BaseLog
|
|||
_messageCount->setObjectName("messageCount");
|
||||
_messageCount->show();
|
||||
|
||||
_keepOnTopBox = new QCheckBox(" Keep window on top", this);
|
||||
bool isOnTop = qApp-> getLogWindowOnTopSetting();
|
||||
_keepOnTopBox->setCheckState(isOnTop ? Qt::Checked : Qt::Unchecked);
|
||||
#ifdef Q_OS_WIN
|
||||
connect(_keepOnTopBox, &QCheckBox::stateChanged, qApp, &Application::recreateLogWindow);
|
||||
#else
|
||||
connect(_keepOnTopBox, &QCheckBox::stateChanged, this, &LogDialog::handleKeepWindowOnTop);
|
||||
#endif
|
||||
_keepOnTopBox->show();
|
||||
|
||||
_extraDebuggingBox = new QCheckBox("Extra debugging", this);
|
||||
if (_logger->extraDebugging()) {
|
||||
_extraDebuggingBox->setCheckState(Qt::Checked);
|
||||
|
@ -183,6 +196,11 @@ void LogDialog::resizeEvent(QResizeEvent* event) {
|
|||
THIRD_ROW,
|
||||
COMBOBOX_WIDTH,
|
||||
ELEMENT_HEIGHT);
|
||||
|
||||
_keepOnTopBox->setGeometry(width() - ELEMENT_MARGIN - COMBOBOX_WIDTH - ELEMENT_MARGIN - ALL_LOGS_BUTTON_WIDTH - ELEMENT_MARGIN - COMBOBOX_WIDTH - ELEMENT_MARGIN,
|
||||
THIRD_ROW,
|
||||
COMBOBOX_WIDTH,
|
||||
ELEMENT_HEIGHT);
|
||||
_messageCount->setGeometry(_leftPad,
|
||||
THIRD_ROW,
|
||||
COMBOBOX_WIDTH,
|
||||
|
@ -234,6 +252,23 @@ void LogDialog::handleInfoPrintBox(int state) {
|
|||
printLogFile();
|
||||
}
|
||||
|
||||
void LogDialog::handleKeepWindowOnTop(int state) {
|
||||
bool keepOnTop = (state != 0);
|
||||
|
||||
Qt::WindowFlags flags = windowFlags();
|
||||
|
||||
if (keepOnTop) {
|
||||
flags |= Qt::Tool;
|
||||
} else {
|
||||
flags &= ~Qt::Tool;
|
||||
}
|
||||
|
||||
setWindowFlags(flags);
|
||||
qApp->setLogWindowOnTopSetting(keepOnTop);
|
||||
|
||||
show();
|
||||
}
|
||||
|
||||
void LogDialog::handleCriticalPrintBox(int state) {
|
||||
_logger->setCriticalPrint(state != 0);
|
||||
printLogFile();
|
||||
|
|
|
@ -34,6 +34,7 @@ public slots:
|
|||
private slots:
|
||||
void handleRevealButton();
|
||||
void handleExtraDebuggingCheckbox(int);
|
||||
void handleKeepWindowOnTop(int);
|
||||
void handleDebugPrintBox(int);
|
||||
void handleInfoPrintBox(int);
|
||||
void handleCriticalPrintBox(int);
|
||||
|
@ -55,6 +56,7 @@ protected:
|
|||
|
||||
private:
|
||||
QCheckBox* _extraDebuggingBox;
|
||||
QCheckBox* _keepOnTopBox;
|
||||
QPushButton* _revealLogButton;
|
||||
QPushButton* _allLogsButton;
|
||||
QCheckBox* _debugPrintBox;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -141,7 +141,7 @@ std::shared_ptr<T> make_renderer(const EntityItemPointer& entity) {
|
|||
return std::shared_ptr<T>(new T(entity), [](T* ptr) { ptr->deleteLater(); });
|
||||
}
|
||||
|
||||
EntityRenderer::EntityRenderer(const EntityItemPointer& entity) : _entity(entity) {
|
||||
EntityRenderer::EntityRenderer(const EntityItemPointer& entity) : _created(entity->getCreated()), _entity(entity) {
|
||||
connect(entity.get(), &EntityItem::requestRenderUpdate, this, [&] {
|
||||
_needsRenderUpdate = true;
|
||||
emit requestRenderUpdate();
|
||||
|
@ -468,3 +468,32 @@ void EntityRenderer::removeMaterial(graphics::MaterialPointer material, const st
|
|||
std::lock_guard<std::mutex> lock(_materialsLock);
|
||||
_materials[parentMaterialName].remove(material);
|
||||
}
|
||||
|
||||
glm::vec4 EntityRenderer::calculatePulseColor(const glm::vec4& color, const PulsePropertyGroup& pulseProperties, quint64 start) {
|
||||
if (pulseProperties.getPeriod() == 0.0f || (pulseProperties.getColorMode() == PulseMode::NONE && pulseProperties.getAlphaMode() == PulseMode::NONE)) {
|
||||
return color;
|
||||
}
|
||||
|
||||
float t = ((float)(usecTimestampNow() - start)) / ((float)USECS_PER_SECOND);
|
||||
float pulse = 0.5f * (cosf(t * (2.0f * (float)M_PI) / pulseProperties.getPeriod()) + 1.0f) * (pulseProperties.getMax() - pulseProperties.getMin()) + pulseProperties.getMin();
|
||||
float outPulse = (1.0f - pulse);
|
||||
|
||||
glm::vec4 result = color;
|
||||
if (pulseProperties.getColorMode() == PulseMode::IN_PHASE) {
|
||||
result.r *= pulse;
|
||||
result.g *= pulse;
|
||||
result.b *= pulse;
|
||||
} else if (pulseProperties.getColorMode() == PulseMode::OUT_PHASE) {
|
||||
result.r *= outPulse;
|
||||
result.g *= outPulse;
|
||||
result.b *= outPulse;
|
||||
}
|
||||
|
||||
if (pulseProperties.getAlphaMode() == PulseMode::IN_PHASE) {
|
||||
result.a *= pulse;
|
||||
} else if (pulseProperties.getAlphaMode() == PulseMode::OUT_PHASE) {
|
||||
result.a *= outPulse;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
|
@ -62,6 +62,8 @@ public:
|
|||
|
||||
virtual scriptable::ScriptableModelBase getScriptableModel() override { return scriptable::ScriptableModelBase(); }
|
||||
|
||||
static glm::vec4 calculatePulseColor(const glm::vec4& color, const PulsePropertyGroup& pulseProperties, quint64 start);
|
||||
|
||||
protected:
|
||||
virtual bool needsRenderUpdateFromEntity() const final { return needsRenderUpdateFromEntity(_entity); }
|
||||
virtual void onAddToScene(const EntityItemPointer& entity);
|
||||
|
@ -151,6 +153,8 @@ protected:
|
|||
std::unordered_map<std::string, graphics::MultiMaterial> _materials;
|
||||
std::mutex _materialsLock;
|
||||
|
||||
quint64 _created;
|
||||
|
||||
private:
|
||||
// The base class relies on comparing the model transform to the entity transform in order
|
||||
// to trigger an update, so the member must not be visible to derived classes as a modifiable
|
||||
|
|
|
@ -26,7 +26,7 @@ GridEntityRenderer::~GridEntityRenderer() {
|
|||
}
|
||||
|
||||
bool GridEntityRenderer::isTransparent() const {
|
||||
return Parent::isTransparent() || _alpha < 1.0f;
|
||||
return Parent::isTransparent() || _alpha < 1.0f || _pulseProperties.getAlphaMode() != PulseMode::NONE;
|
||||
}
|
||||
|
||||
bool GridEntityRenderer::needsRenderUpdate() const {
|
||||
|
@ -55,6 +55,10 @@ bool GridEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint
|
|||
return true;
|
||||
}
|
||||
|
||||
if (_pulseProperties != entity->getPulseProperties()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
|
@ -65,6 +69,7 @@ void GridEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen
|
|||
withWriteLock([&] {
|
||||
_color = entity->getColor();
|
||||
_alpha = entity->getAlpha();
|
||||
_pulseProperties = entity->getPulseProperties();
|
||||
|
||||
_followCamera = entity->getFollowCamera();
|
||||
_majorGridEvery = entity->getMajorGridEvery();
|
||||
|
@ -105,11 +110,12 @@ ShapeKey GridEntityRenderer::getShapeKey() {
|
|||
}
|
||||
|
||||
void GridEntityRenderer::doRender(RenderArgs* args) {
|
||||
glm::u8vec3 color;
|
||||
glm::vec4 color;
|
||||
glm::vec3 dimensions;
|
||||
Transform renderTransform;
|
||||
withReadLock([&] {
|
||||
color = _color;
|
||||
color = glm::vec4(toGlm(_color), _alpha);
|
||||
color = EntityRenderer::calculatePulseColor(color, _pulseProperties, _created);
|
||||
dimensions = _dimensions;
|
||||
renderTransform = _renderTransform;
|
||||
});
|
||||
|
@ -141,12 +147,11 @@ void GridEntityRenderer::doRender(RenderArgs* args) {
|
|||
float majorGridColDivisions = dimensions.y / _majorGridEvery;
|
||||
float minorGridRowDivisions = dimensions.x / _minorGridEvery;
|
||||
float minorGridColDivisions = dimensions.y / _minorGridEvery;
|
||||
glm::vec4 gridColor(toGlm(color), _alpha);
|
||||
|
||||
const float MINOR_GRID_EDGE = 0.0025f;
|
||||
const float MAJOR_GRID_EDGE = 0.005f;
|
||||
DependencyManager::get<GeometryCache>()->renderGrid(*batch, minCorner, maxCorner,
|
||||
minorGridRowDivisions, minorGridColDivisions, MINOR_GRID_EDGE,
|
||||
majorGridRowDivisions, majorGridColDivisions, MAJOR_GRID_EDGE,
|
||||
gridColor, _geometryId);
|
||||
color, _geometryId);
|
||||
}
|
|
@ -36,6 +36,7 @@ private:
|
|||
|
||||
glm::u8vec3 _color;
|
||||
float _alpha;
|
||||
PulsePropertyGroup _pulseProperties;
|
||||
|
||||
bool _followCamera;
|
||||
uint32_t _majorGridEvery;
|
||||
|
|
|
@ -26,7 +26,7 @@ ImageEntityRenderer::~ImageEntityRenderer() {
|
|||
}
|
||||
|
||||
bool ImageEntityRenderer::isTransparent() const {
|
||||
return Parent::isTransparent() || (_textureIsLoaded && _texture->getGPUTexture() && _texture->getGPUTexture()->getUsage().isAlpha()) || _alpha < 1.0f;
|
||||
return Parent::isTransparent() || (_textureIsLoaded && _texture->getGPUTexture() && _texture->getGPUTexture()->getUsage().isAlpha()) || _alpha < 1.0f || _pulseProperties.getAlphaMode() != PulseMode::NONE;
|
||||
}
|
||||
|
||||
bool ImageEntityRenderer::needsRenderUpdate() const {
|
||||
|
@ -71,6 +71,10 @@ bool ImageEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoin
|
|||
return true;
|
||||
}
|
||||
|
||||
if (_pulseProperties != entity->getPulseProperties()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
|
@ -97,6 +101,7 @@ void ImageEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
|
||||
_color = entity->getColor();
|
||||
_alpha = entity->getAlpha();
|
||||
_pulseProperties = entity->getPulseProperties();
|
||||
|
||||
if (!_textureIsLoaded && _texture && _texture->isLoaded()) {
|
||||
_textureIsLoaded = true;
|
||||
|
@ -135,13 +140,14 @@ ShapeKey ImageEntityRenderer::getShapeKey() {
|
|||
void ImageEntityRenderer::doRender(RenderArgs* args) {
|
||||
NetworkTexturePointer texture;
|
||||
QRect subImage;
|
||||
glm::u8vec3 color;
|
||||
glm::vec4 color;
|
||||
glm::vec3 dimensions;
|
||||
Transform transform;
|
||||
withReadLock([&] {
|
||||
texture = _texture;
|
||||
subImage = _subImage;
|
||||
color = _color;
|
||||
color = glm::vec4(toGlm(_color), _alpha);
|
||||
color = EntityRenderer::calculatePulseColor(color, _pulseProperties, _created);
|
||||
dimensions = _dimensions;
|
||||
transform = _renderTransform;
|
||||
});
|
||||
|
@ -211,11 +217,9 @@ void ImageEntityRenderer::doRender(RenderArgs* args) {
|
|||
glm::vec2 texCoordBottomRight((fromImage.x() + fromImage.width() - 0.5f) / imageWidth,
|
||||
(fromImage.y() + fromImage.height() - 0.5f) / imageHeight);
|
||||
|
||||
glm::vec4 imageColor(toGlm(color), _alpha);
|
||||
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(
|
||||
*batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
|
||||
imageColor, _geometryId
|
||||
color, _geometryId
|
||||
);
|
||||
|
||||
batch->setResourceTexture(0, nullptr);
|
||||
|
|
|
@ -44,6 +44,7 @@ private:
|
|||
|
||||
glm::u8vec3 _color;
|
||||
float _alpha;
|
||||
PulsePropertyGroup _pulseProperties;
|
||||
|
||||
glm::vec3 _dimensions;
|
||||
|
||||
|
|
|
@ -71,8 +71,11 @@ bool ParticleEffectEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedE
|
|||
return true;
|
||||
}
|
||||
|
||||
auto particleProperties = entity->getParticleProperties();
|
||||
if (particleProperties != _particleProperties) {
|
||||
if (_particleProperties != entity->getParticleProperties()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_pulseProperties != entity->getPulseProperties()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -95,6 +98,10 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
withWriteLock([&] {
|
||||
_pulseProperties = entity->getPulseProperties();
|
||||
});
|
||||
_emitting = entity->getIsEmitting();
|
||||
|
||||
bool hasTexture = resultWithReadLock<bool>([&]{ return _particleProperties.textures.isEmpty(); });
|
||||
|
@ -142,10 +149,6 @@ void ParticleEffectEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEn
|
|||
particleUniforms.radius.middle = _particleProperties.radius.gradient.target;
|
||||
particleUniforms.radius.finish = _particleProperties.radius.range.finish;
|
||||
particleUniforms.radius.spread = _particleProperties.radius.gradient.spread;
|
||||
particleUniforms.color.start = _particleProperties.getColorStart();
|
||||
particleUniforms.color.middle = _particleProperties.getColorMiddle();
|
||||
particleUniforms.color.finish = _particleProperties.getColorFinish();
|
||||
particleUniforms.color.spread = _particleProperties.getColorSpread();
|
||||
particleUniforms.spin.start = _particleProperties.spin.range.start;
|
||||
particleUniforms.spin.middle = _particleProperties.spin.gradient.target;
|
||||
particleUniforms.spin.finish = _particleProperties.spin.range.finish;
|
||||
|
@ -158,6 +161,7 @@ void ParticleEffectEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEn
|
|||
}
|
||||
|
||||
ItemKey ParticleEffectEntityRenderer::getKey() {
|
||||
// FIXME: implement isTransparent() for particles and an opaque pipeline
|
||||
if (_visible) {
|
||||
return ItemKey::Builder::transparentShape().withTagBits(getTagMask()).withLayer(getHifiRenderLayer());
|
||||
} else {
|
||||
|
@ -334,12 +338,18 @@ void ParticleEffectEntityRenderer::doRender(RenderArgs* args) {
|
|||
gpu::Batch& batch = *args->_batch;
|
||||
batch.setResourceTexture(0, _networkTexture->getGPUTexture());
|
||||
|
||||
Transform transform;
|
||||
Transform transform;
|
||||
// The particles are in world space, so the transform is unused, except for the rotation, which we use
|
||||
// if the particles are marked rotateWithEntity
|
||||
withReadLock([&] {
|
||||
transform.setRotation(_renderTransform.getRotation());
|
||||
auto& color = _uniformBuffer.edit<ParticleUniforms>().color;
|
||||
color.start = EntityRenderer::calculatePulseColor(_particleProperties.getColorStart(), _pulseProperties, _created);
|
||||
color.middle = EntityRenderer::calculatePulseColor(_particleProperties.getColorMiddle(), _pulseProperties, _created);
|
||||
color.finish = EntityRenderer::calculatePulseColor(_particleProperties.getColorFinish(), _pulseProperties, _created);
|
||||
color.spread = EntityRenderer::calculatePulseColor(_particleProperties.getColorSpread(), _pulseProperties, _created);
|
||||
});
|
||||
|
||||
batch.setModelTransform(transform);
|
||||
batch.setUniformBuffer(0, _uniformBuffer);
|
||||
batch.setInputFormat(_vertexFormat);
|
||||
|
|
|
@ -94,6 +94,8 @@ private:
|
|||
BufferView _uniformBuffer;
|
||||
quint64 _lastSimulated { 0 };
|
||||
|
||||
PulsePropertyGroup _pulseProperties;
|
||||
|
||||
NetworkTexturePointer _networkTexture;
|
||||
ScenePointer _scene;
|
||||
};
|
||||
|
|
|
@ -85,6 +85,10 @@ bool ShapeEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoin
|
|||
return true;
|
||||
}
|
||||
|
||||
if (_pulseProperties != entity->getPulseProperties()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -97,6 +101,7 @@ void ShapeEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
}
|
||||
|
||||
_shape = entity->getShape();
|
||||
_pulseProperties = entity->getPulseProperties();
|
||||
});
|
||||
|
||||
void* key = (void*)this;
|
||||
|
@ -141,6 +146,10 @@ void ShapeEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint
|
|||
}
|
||||
|
||||
bool ShapeEntityRenderer::isTransparent() const {
|
||||
if (_pulseProperties.getAlphaMode() != PulseMode::NONE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_procedural.isEnabled() && _procedural.isFading()) {
|
||||
return Interpolate::calculateFadeRatio(_procedural.getFadeStartTime()) < 1.0f;
|
||||
}
|
||||
|
@ -248,6 +257,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
|
|||
materials = _materials["0"];
|
||||
auto& schema = materials.getSchemaBuffer().get<graphics::MultiMaterial::Schema>();
|
||||
outColor = glm::vec4(schema._albedo, schema._opacity);
|
||||
outColor = EntityRenderer::calculatePulseColor(outColor, _pulseProperties, _created);
|
||||
if (_procedural.isReady()) {
|
||||
outColor = _procedural.getColor(outColor);
|
||||
outColor.a *= _procedural.isFading() ? Interpolate::calculateFadeRatio(_procedural.getFadeStartTime()) : 1.0f;
|
||||
|
|
|
@ -40,9 +40,12 @@ private:
|
|||
Procedural _procedural;
|
||||
QString _lastUserData;
|
||||
entity::Shape _shape { entity::Sphere };
|
||||
|
||||
PulsePropertyGroup _pulseProperties;
|
||||
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;
|
||||
|
|
|
@ -41,7 +41,7 @@ TextEntityRenderer::~TextEntityRenderer() {
|
|||
}
|
||||
|
||||
bool TextEntityRenderer::isTransparent() const {
|
||||
return Parent::isTransparent() || _textAlpha < 1.0f || _backgroundAlpha < 1.0f;
|
||||
return Parent::isTransparent() || _textAlpha < 1.0f || _backgroundAlpha < 1.0f || _pulseProperties.getAlphaMode() != PulseMode::NONE;
|
||||
}
|
||||
|
||||
ShapeKey TextEntityRenderer::getShapeKey() {
|
||||
|
@ -104,6 +104,10 @@ bool TextEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint
|
|||
return true;
|
||||
}
|
||||
|
||||
if (_pulseProperties != entity->getPulseProperties()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -119,32 +123,39 @@ void TextEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen
|
|||
}
|
||||
|
||||
void TextEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
|
||||
_text = entity->getText();
|
||||
_lineHeight = entity->getLineHeight();
|
||||
_textColor = toGlm(entity->getTextColor());
|
||||
_textAlpha = entity->getTextAlpha();
|
||||
_backgroundColor = toGlm(entity->getBackgroundColor());
|
||||
_backgroundAlpha = entity->getBackgroundAlpha();
|
||||
_billboardMode = entity->getBillboardMode();
|
||||
_leftMargin = entity->getLeftMargin();
|
||||
_rightMargin = entity->getRightMargin();
|
||||
_topMargin = entity->getTopMargin();
|
||||
_bottomMargin = entity->getBottomMargin();
|
||||
withWriteLock([&] {
|
||||
_pulseProperties = entity->getPulseProperties();
|
||||
_text = entity->getText();
|
||||
_lineHeight = entity->getLineHeight();
|
||||
_textColor = toGlm(entity->getTextColor());
|
||||
_textAlpha = entity->getTextAlpha();
|
||||
_backgroundColor = toGlm(entity->getBackgroundColor());
|
||||
_backgroundAlpha = entity->getBackgroundAlpha();
|
||||
_billboardMode = entity->getBillboardMode();
|
||||
_leftMargin = entity->getLeftMargin();
|
||||
_rightMargin = entity->getRightMargin();
|
||||
_topMargin = entity->getTopMargin();
|
||||
_bottomMargin = entity->getBottomMargin();
|
||||
});
|
||||
}
|
||||
|
||||
void TextEntityRenderer::doRender(RenderArgs* args) {
|
||||
PerformanceTimer perfTimer("RenderableTextEntityItem::render");
|
||||
|
||||
glm::vec4 textColor;
|
||||
glm::vec4 backgroundColor;
|
||||
Transform modelTransform;
|
||||
glm::vec3 dimensions;
|
||||
withReadLock([&] {
|
||||
modelTransform = _renderTransform;
|
||||
dimensions = _dimensions;
|
||||
});
|
||||
|
||||
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
|
||||
glm::vec4 textColor = glm::vec4(_textColor, fadeRatio * _textAlpha);
|
||||
glm::vec4 backgroundColor = glm::vec4(_backgroundColor, fadeRatio * _backgroundAlpha);
|
||||
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
|
||||
textColor = glm::vec4(_textColor, fadeRatio * _textAlpha);
|
||||
textColor = EntityRenderer::calculatePulseColor(textColor, _pulseProperties, _created);
|
||||
backgroundColor = glm::vec4(_backgroundColor, fadeRatio * _backgroundAlpha);
|
||||
backgroundColor = EntityRenderer::calculatePulseColor(backgroundColor, _pulseProperties, _created);
|
||||
});
|
||||
|
||||
// Render background
|
||||
static const float SLIGHTLY_BEHIND = -0.005f;
|
||||
|
|
|
@ -38,6 +38,8 @@ private:
|
|||
int _geometryID{ 0 };
|
||||
std::shared_ptr<TextRenderer3D> _textRenderer;
|
||||
|
||||
PulsePropertyGroup _pulseProperties;
|
||||
|
||||
QString _text;
|
||||
float _lineHeight;
|
||||
glm::vec3 _textColor;
|
||||
|
|
|
@ -97,7 +97,7 @@ WebEntityRenderer::~WebEntityRenderer() {
|
|||
|
||||
bool WebEntityRenderer::isTransparent() const {
|
||||
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
|
||||
return fadeRatio < OPAQUE_ALPHA_THRESHOLD || _alpha < 1.0f;
|
||||
return fadeRatio < OPAQUE_ALPHA_THRESHOLD || _alpha < 1.0f || _pulseProperties.getAlphaMode() != PulseMode::NONE;
|
||||
}
|
||||
|
||||
bool WebEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const {
|
||||
|
@ -143,6 +143,10 @@ bool WebEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointe
|
|||
return true;
|
||||
}
|
||||
|
||||
if (_pulseProperties != entity->getPulseProperties()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -201,6 +205,7 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene
|
|||
_dpi = entity->getDPI();
|
||||
_color = entity->getColor();
|
||||
_alpha = entity->getAlpha();
|
||||
_pulseProperties = entity->getPulseProperties();
|
||||
|
||||
if (_contentType == ContentType::NoContent) {
|
||||
return;
|
||||
|
@ -293,6 +298,7 @@ void WebEntityRenderer::doRender(RenderArgs* args) {
|
|||
withReadLock([&] {
|
||||
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
|
||||
color = glm::vec4(toGlm(_color), _alpha * fadeRatio);
|
||||
color = EntityRenderer::calculatePulseColor(color, _pulseProperties, _created);
|
||||
batch.setModelTransform(_renderTransform);
|
||||
});
|
||||
batch.setResourceTexture(0, _texture);
|
||||
|
|
|
@ -84,6 +84,7 @@ private:
|
|||
|
||||
glm::u8vec3 _color;
|
||||
float _alpha { 1.0f };
|
||||
PulsePropertyGroup _pulseProperties;
|
||||
|
||||
QString _sourceURL;
|
||||
uint16_t _dpi;
|
||||
|
|
|
@ -98,9 +98,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
|
|||
requestedProperties += PROP_RENDER_LAYER;
|
||||
requestedProperties += PROP_PRIMITIVE_MODE;
|
||||
requestedProperties += PROP_IGNORE_PICK_INTERSECTION;
|
||||
withReadLock([&] {
|
||||
requestedProperties += _grabProperties.getEntityProperties(params);
|
||||
});
|
||||
requestedProperties += _grabProperties.getEntityProperties(params);
|
||||
|
||||
// Physics
|
||||
requestedProperties += PROP_DENSITY;
|
||||
|
|
|
@ -42,6 +42,7 @@ BloomPropertyGroup EntityItemProperties::_staticBloom;
|
|||
KeyLightPropertyGroup EntityItemProperties::_staticKeyLight;
|
||||
AmbientLightPropertyGroup EntityItemProperties::_staticAmbientLight;
|
||||
GrabPropertyGroup EntityItemProperties::_staticGrab;
|
||||
PulsePropertyGroup EntityItemProperties::_staticPulse;
|
||||
|
||||
EntityPropertyList PROP_LAST_ITEM = (EntityPropertyList)(PROP_AFTER_LAST_ITEM - 1);
|
||||
|
||||
|
@ -514,6 +515,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
CHECK_PROPERTY_CHANGE(PROP_COMPOUND_SHAPE_URL, compoundShapeURL);
|
||||
CHECK_PROPERTY_CHANGE(PROP_COLOR, color);
|
||||
CHECK_PROPERTY_CHANGE(PROP_ALPHA, alpha);
|
||||
changedProperties += _pulse.getChangedProperties();
|
||||
CHECK_PROPERTY_CHANGE(PROP_TEXTURES, textures);
|
||||
|
||||
// Particles
|
||||
|
@ -1115,6 +1117,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* and <code>spinSpread == PI/2</code>, each particle will have a spin in the range <code>PI/2</code> – <code>3*PI/2</code>.
|
||||
* @property {boolean} rotateWithEntity=false - Whether or not the particles' spin will rotate with the entity. If false, when <code>particleSpin == 0</code>, the particles will point
|
||||
* up in the world. If true, they will point towards the entity's up vector, based on its orientation.
|
||||
* @property {Entities.Pulse} pulse - The pulse-related properties. Deprecated.
|
||||
*
|
||||
* @property {ShapeType} shapeType="none" - <em>Currently not used.</em> <em>Read-only.</em>
|
||||
*
|
||||
|
@ -1242,6 +1245,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* @property {Entities.Shape} shape="Sphere" - The shape of the entity.
|
||||
* @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity.
|
||||
* @property {Color} color=255,255,255 - The color of the entity.
|
||||
* @property {number} alpha=1 - The alpha of the shape.
|
||||
* @property {Entities.Pulse} pulse - The pulse-related properties. Deprecated.
|
||||
* @example <caption>Create a cylinder.</caption>
|
||||
* var shape = Entities.addEntity({
|
||||
* type: "Shape",
|
||||
|
@ -1281,6 +1286,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* @property {number} rightMargin=0.0 - The right margin, in meters.
|
||||
* @property {number} topMargin=0.0 - The top margin, in meters.
|
||||
* @property {number} bottomMargin=0.0 - The bottom margin, in meters.
|
||||
* @property {Entities.Pulse} pulse - The pulse-related properties. Deprecated.
|
||||
* @example <caption>Create a text entity.</caption>
|
||||
* var text = Entities.addEntity({
|
||||
* type: "Text",
|
||||
|
@ -1310,6 +1316,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* @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.
|
||||
* @property {Entities.Pulse} pulse - The pulse-related properties. Deprecated.
|
||||
* @example <caption>Create a Web entity displaying at 1920 x 1080 resolution.</caption>
|
||||
* var METERS_TO_INCHES = 39.3701;
|
||||
* var entity = Entities.addEntity({
|
||||
|
@ -1419,6 +1426,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* the full image in that dimension.
|
||||
* @property {Color} color=255,255,255 - The color of the image.
|
||||
* @property {number} alpha=1 - The alpha of the image.
|
||||
* @property {Entities.Pulse} pulse - The pulse-related properties. Deprecated.
|
||||
* @example <caption>Create a image entity.</caption>
|
||||
* var image = Entities.addEntity({
|
||||
* type: "Image",
|
||||
|
@ -1442,6 +1450,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* line. Minimum value = <code>1</code>.
|
||||
* @property {number} minorGridEvery=1 - Real number of meters at which to draw thin grid lines. Minimum value =
|
||||
* <code>0.001</code>.
|
||||
* @property {Entities.Pulse} pulse - The pulse-related properties. Deprecated.
|
||||
* @example <caption>Create a grid entity.</caption>
|
||||
* var grid = Entities.addEntity({
|
||||
* type: "Grid",
|
||||
|
@ -1579,6 +1588,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SHAPE_TYPE, shapeType, getShapeTypeAsString());
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha);
|
||||
_pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXTURES, textures);
|
||||
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MAX_PARTICLES, maxParticles);
|
||||
|
@ -1652,6 +1662,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
if (_type == EntityTypes::Box || _type == EntityTypes::Sphere || _type == EntityTypes::Shape) {
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha);
|
||||
_pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SHAPE, shape);
|
||||
}
|
||||
|
||||
|
@ -1667,6 +1678,8 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
|
||||
// Text only
|
||||
if (_type == EntityTypes::Text) {
|
||||
_pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties);
|
||||
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXT, text);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LINE_HEIGHT, lineHeight);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_TYPED(PROP_TEXT_COLOR, textColor, getTextColor(), u8vec3Color);
|
||||
|
@ -1708,6 +1721,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
if (_type == EntityTypes::Web) {
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha);
|
||||
_pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties);
|
||||
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOURCE_URL, sourceUrl);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_DPI, dpi);
|
||||
|
@ -1772,6 +1786,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
if (_type == EntityTypes::Image) {
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha);
|
||||
_pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties);
|
||||
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_IMAGE_URL, imageURL);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMISSIVE, emissive);
|
||||
|
@ -1792,6 +1807,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
if (_type == EntityTypes::Grid) {
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha);
|
||||
_pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties);
|
||||
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_GRID_FOLLOW_CAMERA, followCamera);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MAJOR_GRID_EVERY, majorGridEvery);
|
||||
|
@ -1972,6 +1988,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
|
|||
COPY_PROPERTY_FROM_QSCRIPTVALUE(compoundShapeURL, QString, setCompoundShapeURL);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(color, u8vec3Color, setColor);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(alpha, float, setAlpha);
|
||||
_pulse.copyFromScriptValue(object, _defaultSettings);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(textures, QString, setTextures);
|
||||
|
||||
// Particles
|
||||
|
@ -2245,6 +2262,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) {
|
|||
COPY_PROPERTY_IF_CHANGED(compoundShapeURL);
|
||||
COPY_PROPERTY_IF_CHANGED(color);
|
||||
COPY_PROPERTY_IF_CHANGED(alpha);
|
||||
_pulse.merge(other._pulse);
|
||||
COPY_PROPERTY_IF_CHANGED(textures);
|
||||
|
||||
// Particles
|
||||
|
@ -2552,6 +2570,13 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr
|
|||
ADD_PROPERTY_TO_MAP(PROP_COMPOUND_SHAPE_URL, CompoundShapeURL, compoundShapeURL, QString);
|
||||
ADD_PROPERTY_TO_MAP(PROP_COLOR, Color, color, u8vec3Color);
|
||||
ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_ALPHA, Alpha, alpha, float, particle::MINIMUM_ALPHA, particle::MAXIMUM_ALPHA);
|
||||
{ // Pulse
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_PULSE_MIN, Pulse, pulse, Min, min);
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_PULSE_MAX, Pulse, pulse, Max, max);
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_PULSE_PERIOD, Pulse, pulse, Period, period);
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_PULSE_COLOR_MODE, Pulse, pulse, ColorMode, colorMode);
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_PULSE_ALPHA_MODE, Pulse, pulse, AlphaMode, alphaMode);
|
||||
}
|
||||
ADD_PROPERTY_TO_MAP(PROP_TEXTURES, Textures, textures, QString);
|
||||
|
||||
// Particles
|
||||
|
@ -2952,6 +2977,9 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
|
|||
APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)(properties.getShapeType()));
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ALPHA, properties.getAlpha());
|
||||
_staticPulse.setProperties(properties);
|
||||
_staticPulse.appendToEditPacket(packetData, requestedProperties, propertyFlags,
|
||||
propertiesDidntFit, propertyCount, appendState);
|
||||
APPEND_ENTITY_PROPERTY(PROP_TEXTURES, properties.getTextures());
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_MAX_PARTICLES, properties.getMaxParticles());
|
||||
|
@ -3023,6 +3051,10 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
|
|||
}
|
||||
|
||||
if (properties.getType() == EntityTypes::Text) {
|
||||
_staticPulse.setProperties(properties);
|
||||
_staticPulse.appendToEditPacket(packetData, requestedProperties, propertyFlags,
|
||||
propertiesDidntFit, propertyCount, appendState);
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_TEXT, properties.getText());
|
||||
APPEND_ENTITY_PROPERTY(PROP_LINE_HEIGHT, properties.getLineHeight());
|
||||
APPEND_ENTITY_PROPERTY(PROP_TEXT_COLOR, properties.getTextColor());
|
||||
|
@ -3084,6 +3116,9 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
|
|||
if (properties.getType() == EntityTypes::Web) {
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ALPHA, properties.getAlpha());
|
||||
_staticPulse.setProperties(properties);
|
||||
_staticPulse.appendToEditPacket(packetData, requestedProperties, propertyFlags,
|
||||
propertiesDidntFit, propertyCount, appendState);
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, properties.getSourceUrl());
|
||||
APPEND_ENTITY_PROPERTY(PROP_DPI, properties.getDPI());
|
||||
|
@ -3118,6 +3153,9 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
|
|||
properties.getType() == EntityTypes::Sphere) {
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ALPHA, properties.getAlpha());
|
||||
_staticPulse.setProperties(properties);
|
||||
_staticPulse.appendToEditPacket(packetData, requestedProperties, propertyFlags,
|
||||
propertiesDidntFit, propertyCount, appendState);
|
||||
APPEND_ENTITY_PROPERTY(PROP_SHAPE, properties.getShape());
|
||||
}
|
||||
|
||||
|
@ -3138,6 +3176,9 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
|
|||
if (properties.getType() == EntityTypes::Image) {
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ALPHA, properties.getAlpha());
|
||||
_staticPulse.setProperties(properties);
|
||||
_staticPulse.appendToEditPacket(packetData, requestedProperties, propertyFlags,
|
||||
propertiesDidntFit, propertyCount, appendState);
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_IMAGE_URL, properties.getImageURL());
|
||||
APPEND_ENTITY_PROPERTY(PROP_EMISSIVE, properties.getEmissive());
|
||||
|
@ -3150,6 +3191,9 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
|
|||
if (properties.getType() == EntityTypes::Grid) {
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ALPHA, properties.getAlpha());
|
||||
_staticPulse.setProperties(properties);
|
||||
_staticPulse.appendToEditPacket(packetData, requestedProperties, propertyFlags,
|
||||
propertiesDidntFit, propertyCount, appendState);
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_GRID_FOLLOW_CAMERA, properties.getFollowCamera());
|
||||
APPEND_ENTITY_PROPERTY(PROP_MAJOR_GRID_EVERY, properties.getMajorGridEvery());
|
||||
|
@ -3401,6 +3445,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE_TYPE, ShapeType, setShapeType);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA, float, setAlpha);
|
||||
properties.getPulse().decodeFromEditPacket(propertyFlags, dataAt, processedBytes);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXTURES, QString, setTextures);
|
||||
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MAX_PARTICLES, quint32, setMaxParticles);
|
||||
|
@ -3472,6 +3517,8 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
}
|
||||
|
||||
if (properties.getType() == EntityTypes::Text) {
|
||||
properties.getPulse().decodeFromEditPacket(propertyFlags, dataAt, processedBytes);
|
||||
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT, QString, setText);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_HEIGHT, float, setLineHeight);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT_COLOR, u8vec3Color, setTextColor);
|
||||
|
@ -3524,6 +3571,7 @@ 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);
|
||||
properties.getPulse().decodeFromEditPacket(propertyFlags, dataAt, processedBytes);
|
||||
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOURCE_URL, QString, setSourceUrl);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DPI, uint16_t, setDPI);
|
||||
|
@ -3558,6 +3606,8 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
properties.getType() == EntityTypes::Sphere) {
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA, float, setAlpha);
|
||||
properties.getPulse().decodeFromEditPacket(propertyFlags, dataAt, processedBytes);
|
||||
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE, QString, setShape);
|
||||
}
|
||||
|
||||
|
@ -3578,6 +3628,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
if (properties.getType() == EntityTypes::Image) {
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA, float, setAlpha);
|
||||
properties.getPulse().decodeFromEditPacket(propertyFlags, dataAt, processedBytes);
|
||||
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_IMAGE_URL, QString, setImageURL);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMISSIVE, bool, setEmissive);
|
||||
|
@ -3590,6 +3641,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
if (properties.getType() == EntityTypes::Grid) {
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA, float, setAlpha);
|
||||
properties.getPulse().decodeFromEditPacket(propertyFlags, dataAt, processedBytes);
|
||||
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GRID_FOLLOW_CAMERA, bool, setFollowCamera);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MAJOR_GRID_EVERY, uint32_t, setMajorGridEvery);
|
||||
|
@ -3793,6 +3845,7 @@ void EntityItemProperties::markAllChanged() {
|
|||
_shapeTypeChanged = true;
|
||||
_colorChanged = true;
|
||||
_alphaChanged = true;
|
||||
_pulse.markAllChanged();
|
||||
_texturesChanged = true;
|
||||
_compoundShapeURLChanged = true;
|
||||
|
||||
|
@ -4257,6 +4310,7 @@ QList<QString> EntityItemProperties::listChangedProperties() {
|
|||
if (alphaChanged()) {
|
||||
out += "alpha";
|
||||
}
|
||||
getPulse().listChangedProperties(out);
|
||||
if (texturesChanged()) {
|
||||
out += "textures";
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
#include "SkyboxPropertyGroup.h"
|
||||
#include "HazePropertyGroup.h"
|
||||
#include "BloomPropertyGroup.h"
|
||||
#include "PulsePropertyGroup.h"
|
||||
|
||||
#include "MaterialMappingMode.h"
|
||||
#include "BillboardMode.h"
|
||||
|
@ -235,6 +236,7 @@ public:
|
|||
DEFINE_PROPERTY_REF(PROP_COMPOUND_SHAPE_URL, CompoundShapeURL, compoundShapeURL, QString, "");
|
||||
DEFINE_PROPERTY_REF(PROP_COLOR, Color, color, u8vec3Color, particle::DEFAULT_COLOR);
|
||||
DEFINE_PROPERTY(PROP_ALPHA, Alpha, alpha, float, particle::DEFAULT_ALPHA);
|
||||
DEFINE_PROPERTY_GROUP(Pulse, pulse, PulsePropertyGroup);
|
||||
DEFINE_PROPERTY_REF(PROP_TEXTURES, Textures, textures, QString, "");
|
||||
|
||||
// Particles
|
||||
|
|
|
@ -383,13 +383,29 @@ inline QRect QRect_convertFromScriptValue(const QScriptValue& v, bool& isValid)
|
|||
} \
|
||||
}
|
||||
|
||||
#define COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(P, S) \
|
||||
QScriptValue P = object.property(#P); \
|
||||
if (P.isValid()) { \
|
||||
QString newValue = P.toVariant().toString(); \
|
||||
if (_defaultSettings || newValue != get##S##AsString()) { \
|
||||
set##S##FromString(newValue); \
|
||||
} \
|
||||
#define COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(P, S) \
|
||||
{ \
|
||||
QScriptValue P = object.property(#P); \
|
||||
if (P.isValid()) { \
|
||||
QString newValue = P.toVariant().toString(); \
|
||||
if (_defaultSettings || newValue != get##S##AsString()) { \
|
||||
set##S##FromString(newValue); \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
#define COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE_ENUM(G, P, S) \
|
||||
{ \
|
||||
QScriptValue G = object.property(#G); \
|
||||
if (G.isValid()) { \
|
||||
QScriptValue P = G.property(#P); \
|
||||
if (P.isValid()) { \
|
||||
QString newValue = P.toVariant().toString(); \
|
||||
if (_defaultSettings || newValue != get##S##AsString()) { \
|
||||
set##S##FromString(newValue); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
#define DEFINE_PROPERTY_GROUP(N, n, T) \
|
||||
|
|
|
@ -106,12 +106,17 @@ enum EntityPropertyList {
|
|||
PROP_LOCAL_VELOCITY,
|
||||
PROP_LOCAL_ANGULAR_VELOCITY,
|
||||
PROP_LOCAL_DIMENSIONS,
|
||||
|
||||
|
||||
// These properties are used by multiple subtypes but aren't in the base EntityItem
|
||||
PROP_SHAPE_TYPE,
|
||||
PROP_COMPOUND_SHAPE_URL,
|
||||
PROP_COLOR,
|
||||
PROP_ALPHA,
|
||||
PROP_PULSE_MIN,
|
||||
PROP_PULSE_MAX,
|
||||
PROP_PULSE_PERIOD,
|
||||
PROP_PULSE_COLOR_MODE,
|
||||
PROP_PULSE_ALPHA_MODE,
|
||||
PROP_TEXTURES,
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -35,6 +35,9 @@ EntityItemProperties GridEntityItem::getProperties(const EntityPropertyFlags& de
|
|||
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(alpha, getAlpha);
|
||||
withReadLock([&] {
|
||||
_pulseProperties.getProperties(properties);
|
||||
});
|
||||
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(followCamera, getFollowCamera);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(majorGridEvery, getMajorGridEvery);
|
||||
|
@ -48,6 +51,10 @@ bool GridEntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(alpha, setAlpha);
|
||||
withWriteLock([&] {
|
||||
bool pulsePropertiesChanged = _pulseProperties.setProperties(properties);
|
||||
somethingChanged |= pulsePropertiesChanged;
|
||||
});
|
||||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(followCamera, setFollowCamera);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(majorGridEvery, setMajorGridEvery);
|
||||
|
@ -76,6 +83,13 @@ int GridEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
|||
|
||||
READ_ENTITY_PROPERTY(PROP_COLOR, u8vec3Color, setColor);
|
||||
READ_ENTITY_PROPERTY(PROP_ALPHA, float, setAlpha);
|
||||
withWriteLock([&] {
|
||||
int bytesFromPulse = _pulseProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
|
||||
propertyFlags, overwriteLocalData,
|
||||
somethingChanged);
|
||||
bytesRead += bytesFromPulse;
|
||||
dataAt += bytesFromPulse;
|
||||
});
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_GRID_FOLLOW_CAMERA, bool, setFollowCamera);
|
||||
READ_ENTITY_PROPERTY(PROP_MAJOR_GRID_EVERY, uint32_t, setMajorGridEvery);
|
||||
|
@ -89,6 +103,7 @@ EntityPropertyFlags GridEntityItem::getEntityProperties(EncodeBitstreamParams& p
|
|||
|
||||
requestedProperties += PROP_COLOR;
|
||||
requestedProperties += PROP_ALPHA;
|
||||
requestedProperties += _pulseProperties.getEntityProperties(params);
|
||||
|
||||
requestedProperties += PROP_GRID_FOLLOW_CAMERA;
|
||||
requestedProperties += PROP_MAJOR_GRID_EVERY;
|
||||
|
@ -98,7 +113,7 @@ EntityPropertyFlags GridEntityItem::getEntityProperties(EncodeBitstreamParams& p
|
|||
}
|
||||
|
||||
void GridEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData,
|
||||
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
|
@ -109,6 +124,10 @@ void GridEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits
|
|||
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha());
|
||||
withReadLock([&] {
|
||||
_pulseProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties,
|
||||
propertyFlags, propertiesDidntFit, propertyCount, appendState);
|
||||
});
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_GRID_FOLLOW_CAMERA, getFollowCamera());
|
||||
APPEND_ENTITY_PROPERTY(PROP_MAJOR_GRID_EVERY, getMajorGridEvery());
|
||||
|
@ -175,4 +194,10 @@ float GridEntityItem::getMinorGridEvery() const {
|
|||
return resultWithReadLock<float>([&] {
|
||||
return _minorGridEvery;
|
||||
});
|
||||
}
|
||||
|
||||
PulsePropertyGroup GridEntityItem::getPulseProperties() const {
|
||||
return resultWithReadLock<PulsePropertyGroup>([&] {
|
||||
return _pulseProperties;
|
||||
});
|
||||
}
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include "EntityItem.h"
|
||||
|
||||
#include "PulsePropertyGroup.h"
|
||||
|
||||
class GridEntityItem : public EntityItem {
|
||||
using Pointer = std::shared_ptr<GridEntityItem>;
|
||||
public:
|
||||
|
@ -29,7 +31,7 @@ public:
|
|||
EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
||||
|
||||
void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData,
|
||||
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
|
@ -59,9 +61,13 @@ public:
|
|||
void setMinorGridEvery(float minorGridEvery);
|
||||
float getMinorGridEvery() const;
|
||||
|
||||
PulsePropertyGroup getPulseProperties() const;
|
||||
|
||||
protected:
|
||||
glm::u8vec3 _color;
|
||||
float _alpha;
|
||||
PulsePropertyGroup _pulseProperties;
|
||||
|
||||
bool _followCamera { true };
|
||||
uint32_t _majorGridEvery { DEFAULT_MAJOR_GRID_EVERY };
|
||||
float _minorGridEvery { DEFAULT_MINOR_GRID_EVERY };
|
||||
|
|
|
@ -32,6 +32,9 @@ EntityItemProperties ImageEntityItem::getProperties(const EntityPropertyFlags& d
|
|||
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(alpha, getAlpha);
|
||||
withReadLock([&] {
|
||||
_pulseProperties.getProperties(properties);
|
||||
});
|
||||
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(imageURL, getImageURL);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(emissive, getEmissive);
|
||||
|
@ -47,6 +50,10 @@ bool ImageEntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(alpha, setAlpha);
|
||||
withWriteLock([&] {
|
||||
bool pulsePropertiesChanged = _pulseProperties.setProperties(properties);
|
||||
somethingChanged |= pulsePropertiesChanged;
|
||||
});
|
||||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(imageURL, setImageURL);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(emissive, setEmissive);
|
||||
|
@ -77,6 +84,13 @@ int ImageEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
|||
|
||||
READ_ENTITY_PROPERTY(PROP_COLOR, u8vec3Color, setColor);
|
||||
READ_ENTITY_PROPERTY(PROP_ALPHA, float, setAlpha);
|
||||
withWriteLock([&] {
|
||||
int bytesFromPulse = _pulseProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
|
||||
propertyFlags, overwriteLocalData,
|
||||
somethingChanged);
|
||||
bytesRead += bytesFromPulse;
|
||||
dataAt += bytesFromPulse;
|
||||
});
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_IMAGE_URL, QString, setImageURL);
|
||||
READ_ENTITY_PROPERTY(PROP_EMISSIVE, bool, setEmissive);
|
||||
|
@ -92,6 +106,7 @@ EntityPropertyFlags ImageEntityItem::getEntityProperties(EncodeBitstreamParams&
|
|||
|
||||
requestedProperties += PROP_COLOR;
|
||||
requestedProperties += PROP_ALPHA;
|
||||
requestedProperties += _pulseProperties.getEntityProperties(params);
|
||||
|
||||
requestedProperties += PROP_IMAGE_URL;
|
||||
requestedProperties += PROP_EMISSIVE;
|
||||
|
@ -103,7 +118,7 @@ EntityPropertyFlags ImageEntityItem::getEntityProperties(EncodeBitstreamParams&
|
|||
}
|
||||
|
||||
void ImageEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData,
|
||||
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
|
@ -114,6 +129,10 @@ void ImageEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit
|
|||
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha());
|
||||
withReadLock([&] {
|
||||
_pulseProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties,
|
||||
propertyFlags, propertiesDidntFit, propertyCount, appendState);
|
||||
});
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_IMAGE_URL, getImageURL());
|
||||
APPEND_ENTITY_PROPERTY(PROP_EMISSIVE, getEmissive());
|
||||
|
@ -266,4 +285,10 @@ float ImageEntityItem::getAlpha() const {
|
|||
return resultWithReadLock<float>([&] {
|
||||
return _alpha;
|
||||
});
|
||||
}
|
||||
|
||||
PulsePropertyGroup ImageEntityItem::getPulseProperties() const {
|
||||
return resultWithReadLock<PulsePropertyGroup>([&] {
|
||||
return _pulseProperties;
|
||||
});
|
||||
}
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include "EntityItem.h"
|
||||
|
||||
#include "PulsePropertyGroup.h"
|
||||
|
||||
class ImageEntityItem : public EntityItem {
|
||||
using Pointer = std::shared_ptr<ImageEntityItem>;
|
||||
public:
|
||||
|
@ -29,7 +31,7 @@ public:
|
|||
EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
||||
|
||||
void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData,
|
||||
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
|
@ -72,6 +74,8 @@ public:
|
|||
void setAlpha(float alpha);
|
||||
float getAlpha() const;
|
||||
|
||||
PulsePropertyGroup getPulseProperties() const;
|
||||
|
||||
protected:
|
||||
QString _imageURL;
|
||||
bool _emissive { false };
|
||||
|
@ -81,6 +85,7 @@ protected:
|
|||
|
||||
glm::u8vec3 _color;
|
||||
float _alpha;
|
||||
PulsePropertyGroup _pulseProperties;
|
||||
};
|
||||
|
||||
#endif // hifi_ImageEntityItem_h
|
||||
|
|
|
@ -412,6 +412,9 @@ EntityItemProperties ParticleEffectEntityItem::getProperties(const EntityPropert
|
|||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(shapeType, getShapeType);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(alpha, getAlpha);
|
||||
withReadLock([&] {
|
||||
_pulseProperties.getProperties(properties);
|
||||
});
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(textures, getTextures);
|
||||
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(maxParticles, getMaxParticles);
|
||||
|
@ -463,6 +466,10 @@ bool ParticleEffectEntityItem::setProperties(const EntityItemProperties& propert
|
|||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeType, setShapeType);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(alpha, setAlpha);
|
||||
withWriteLock([&] {
|
||||
bool pulsePropertiesChanged = _pulseProperties.setProperties(properties);
|
||||
somethingChanged |= pulsePropertiesChanged;
|
||||
});
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(textures, setTextures);
|
||||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(maxParticles, setMaxParticles);
|
||||
|
@ -535,6 +542,13 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch
|
|||
READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, setShapeType);
|
||||
READ_ENTITY_PROPERTY(PROP_COLOR, u8vec3Color, setColor);
|
||||
READ_ENTITY_PROPERTY(PROP_ALPHA, float, setAlpha);
|
||||
withWriteLock([&] {
|
||||
int bytesFromPulse = _pulseProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
|
||||
propertyFlags, overwriteLocalData,
|
||||
somethingChanged);
|
||||
bytesRead += bytesFromPulse;
|
||||
dataAt += bytesFromPulse;
|
||||
});
|
||||
READ_ENTITY_PROPERTY(PROP_TEXTURES, QString, setTextures);
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_MAX_PARTICLES, quint32, setMaxParticles);
|
||||
|
@ -586,6 +600,7 @@ EntityPropertyFlags ParticleEffectEntityItem::getEntityProperties(EncodeBitstrea
|
|||
requestedProperties += PROP_SHAPE_TYPE;
|
||||
requestedProperties += PROP_COLOR;
|
||||
requestedProperties += PROP_ALPHA;
|
||||
requestedProperties += _pulseProperties.getEntityProperties(params);
|
||||
requestedProperties += PROP_TEXTURES;
|
||||
|
||||
requestedProperties += PROP_MAX_PARTICLES;
|
||||
|
@ -643,6 +658,10 @@ void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData,
|
|||
APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)getShapeType());
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha());
|
||||
withReadLock([&] {
|
||||
_pulseProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties,
|
||||
propertyFlags, propertiesDidntFit, propertyCount, appendState);
|
||||
});
|
||||
APPEND_ENTITY_PROPERTY(PROP_TEXTURES, getTextures());
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_MAX_PARTICLES, getMaxParticles());
|
||||
|
@ -786,4 +805,10 @@ particle::Properties ParticleEffectEntityItem::getParticleProperties() const {
|
|||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
PulsePropertyGroup ParticleEffectEntityItem::getPulseProperties() const {
|
||||
return resultWithReadLock<PulsePropertyGroup>([&] {
|
||||
return _pulseProperties;
|
||||
});
|
||||
}
|
|
@ -16,6 +16,7 @@
|
|||
#include "EntityItem.h"
|
||||
|
||||
#include "ColorUtils.h"
|
||||
#include "PulsePropertyGroup.h"
|
||||
|
||||
namespace particle {
|
||||
static const float SCRIPT_MAXIMUM_PI = 3.1416f; // Round up so that reasonable property values work
|
||||
|
@ -341,9 +342,11 @@ public:
|
|||
virtual bool supportsDetailedIntersection() const override { return false; }
|
||||
|
||||
particle::Properties getParticleProperties() const;
|
||||
PulsePropertyGroup getPulseProperties() const;
|
||||
|
||||
protected:
|
||||
particle::Properties _particleProperties;
|
||||
PulsePropertyGroup _pulseProperties;
|
||||
bool _isEmitting { true };
|
||||
|
||||
ShapeType _shapeType { SHAPE_TYPE_NONE };
|
||||
|
|
249
libraries/entities/src/PulsePropertyGroup.cpp
Normal file
249
libraries/entities/src/PulsePropertyGroup.cpp
Normal file
|
@ -0,0 +1,249 @@
|
|||
//
|
||||
// PulsePropertyGroup.cpp
|
||||
//
|
||||
// Created by Sam Gondelman on 1/15/19
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "PulsePropertyGroup.h"
|
||||
|
||||
#include <OctreePacketData.h>
|
||||
|
||||
#include "EntityItemProperties.h"
|
||||
#include "EntityItemPropertiesMacros.h"
|
||||
|
||||
QHash<QString, PulseMode> stringToPulseModeLookup;
|
||||
|
||||
void addPulseMode(PulseMode mode) {
|
||||
stringToPulseModeLookup[PulseModeHelpers::getNameForPulseMode(mode)] = mode;
|
||||
}
|
||||
|
||||
void buildStringToPulseModeLookup() {
|
||||
addPulseMode(PulseMode::NONE);
|
||||
addPulseMode(PulseMode::IN_PHASE);
|
||||
addPulseMode(PulseMode::OUT_PHASE);
|
||||
}
|
||||
|
||||
QString PulsePropertyGroup::getColorModeAsString() const {
|
||||
return PulseModeHelpers::getNameForPulseMode(_colorMode);
|
||||
}
|
||||
|
||||
void PulsePropertyGroup::setColorModeFromString(const QString& pulseMode) {
|
||||
if (stringToPulseModeLookup.empty()) {
|
||||
buildStringToPulseModeLookup();
|
||||
}
|
||||
auto pulseModeItr = stringToPulseModeLookup.find(pulseMode.toLower());
|
||||
if (pulseModeItr != stringToPulseModeLookup.end()) {
|
||||
_colorMode = pulseModeItr.value();
|
||||
_colorModeChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
QString PulsePropertyGroup::getAlphaModeAsString() const {
|
||||
return PulseModeHelpers::getNameForPulseMode(_alphaMode);
|
||||
}
|
||||
|
||||
void PulsePropertyGroup::setAlphaModeFromString(const QString& pulseMode) {
|
||||
if (stringToPulseModeLookup.empty()) {
|
||||
buildStringToPulseModeLookup();
|
||||
}
|
||||
auto pulseModeItr = stringToPulseModeLookup.find(pulseMode.toLower());
|
||||
if (pulseModeItr != stringToPulseModeLookup.end()) {
|
||||
_alphaMode = pulseModeItr.value();
|
||||
_alphaModeChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
void PulsePropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties,
|
||||
QScriptEngine* engine, bool skipDefaults,
|
||||
EntityItemProperties& defaultEntityProperties) const {
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_PULSE_MIN, Pulse, pulse, Min, min);
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_PULSE_MAX, Pulse, pulse, Max, max);
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_PULSE_PERIOD, Pulse, pulse, Period, period);
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_PULSE_COLOR_MODE, Pulse, pulse, ColorMode, colorMode, getColorModeAsString);
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_PULSE_ALPHA_MODE, Pulse, pulse, AlphaMode, alphaMode, getAlphaModeAsString);
|
||||
}
|
||||
|
||||
void PulsePropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) {
|
||||
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(pulse, min, float, setMin);
|
||||
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(pulse, max, float, setMax);
|
||||
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(pulse, period, float, setPeriod);
|
||||
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE_ENUM(pulse, colorMode, ColorMode);
|
||||
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE_ENUM(pulse, alphaMode, AlphaMode);
|
||||
}
|
||||
|
||||
void PulsePropertyGroup::merge(const PulsePropertyGroup& other) {
|
||||
COPY_PROPERTY_IF_CHANGED(min);
|
||||
COPY_PROPERTY_IF_CHANGED(max);
|
||||
COPY_PROPERTY_IF_CHANGED(period);
|
||||
COPY_PROPERTY_IF_CHANGED(colorMode);
|
||||
COPY_PROPERTY_IF_CHANGED(alphaMode);
|
||||
}
|
||||
|
||||
void PulsePropertyGroup::debugDump() const {
|
||||
qCDebug(entities) << " PulsePropertyGroup: ---------------------------------------------";
|
||||
qCDebug(entities) << " _min:" << _min;
|
||||
qCDebug(entities) << " _max:" << _max;
|
||||
qCDebug(entities) << " _period:" << _period;
|
||||
qCDebug(entities) << " _colorMode:" << getColorModeAsString();
|
||||
qCDebug(entities) << " _alphaMode:" << getAlphaModeAsString();
|
||||
}
|
||||
|
||||
void PulsePropertyGroup::listChangedProperties(QList<QString>& out) {
|
||||
if (minChanged()) {
|
||||
out << "pulse-min";
|
||||
}
|
||||
if (maxChanged()) {
|
||||
out << "pulse-max";
|
||||
}
|
||||
if (periodChanged()) {
|
||||
out << "pulse-period";
|
||||
}
|
||||
if (colorModeChanged()) {
|
||||
out << "pulse-colorMode";
|
||||
}
|
||||
if (alphaModeChanged()) {
|
||||
out << "pulse-alphaMode";
|
||||
}
|
||||
}
|
||||
|
||||
bool PulsePropertyGroup::appendToEditPacket(OctreePacketData* packetData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
int& propertyCount,
|
||||
OctreeElement::AppendState& appendState) const {
|
||||
|
||||
bool successPropertyFits = true;
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_PULSE_MIN, getMin());
|
||||
APPEND_ENTITY_PROPERTY(PROP_PULSE_MAX, getMax());
|
||||
APPEND_ENTITY_PROPERTY(PROP_PULSE_PERIOD, getPeriod());
|
||||
APPEND_ENTITY_PROPERTY(PROP_PULSE_COLOR_MODE, (uint32_t)getColorMode());
|
||||
APPEND_ENTITY_PROPERTY(PROP_PULSE_ALPHA_MODE, (uint32_t)getAlphaMode());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PulsePropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags,
|
||||
const unsigned char*& dataAt , int& processedBytes) {
|
||||
|
||||
int bytesRead = 0;
|
||||
bool overwriteLocalData = true;
|
||||
bool somethingChanged = false;
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_PULSE_MIN, float, setMin);
|
||||
READ_ENTITY_PROPERTY(PROP_PULSE_MAX, float, setMax);
|
||||
READ_ENTITY_PROPERTY(PROP_PULSE_PERIOD, float, setPeriod);
|
||||
READ_ENTITY_PROPERTY(PROP_PULSE_COLOR_MODE, PulseMode, setColorMode);
|
||||
READ_ENTITY_PROPERTY(PROP_PULSE_ALPHA_MODE, PulseMode, setAlphaMode);
|
||||
|
||||
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_PULSE_MIN, Min);
|
||||
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_PULSE_MAX, Max);
|
||||
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_PULSE_PERIOD, Period);
|
||||
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_PULSE_COLOR_MODE, ColorMode);
|
||||
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_PULSE_ALPHA_MODE, AlphaMode);
|
||||
|
||||
processedBytes += bytesRead;
|
||||
|
||||
Q_UNUSED(somethingChanged);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PulsePropertyGroup::markAllChanged() {
|
||||
_minChanged = true;
|
||||
_maxChanged = true;
|
||||
_periodChanged = true;
|
||||
_colorModeChanged = true;
|
||||
_alphaModeChanged = true;
|
||||
}
|
||||
|
||||
EntityPropertyFlags PulsePropertyGroup::getChangedProperties() const {
|
||||
EntityPropertyFlags changedProperties;
|
||||
|
||||
CHECK_PROPERTY_CHANGE(PROP_PULSE_MIN, min);
|
||||
CHECK_PROPERTY_CHANGE(PROP_PULSE_MAX, max);
|
||||
CHECK_PROPERTY_CHANGE(PROP_PULSE_PERIOD, period);
|
||||
CHECK_PROPERTY_CHANGE(PROP_PULSE_COLOR_MODE, colorMode);
|
||||
CHECK_PROPERTY_CHANGE(PROP_PULSE_ALPHA_MODE, alphaMode);
|
||||
|
||||
return changedProperties;
|
||||
}
|
||||
|
||||
void PulsePropertyGroup::getProperties(EntityItemProperties& properties) const {
|
||||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Pulse, Min, getMin);
|
||||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Pulse, Max, getMax);
|
||||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Pulse, Period, getPeriod);
|
||||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Pulse, ColorMode, getColorMode);
|
||||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Pulse, AlphaMode, getAlphaMode);
|
||||
}
|
||||
|
||||
bool PulsePropertyGroup::setProperties(const EntityItemProperties& properties) {
|
||||
bool somethingChanged = false;
|
||||
|
||||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Pulse, Min, min, setMin);
|
||||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Pulse, Max, max, setMax);
|
||||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Pulse, Period, period, setPeriod);
|
||||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Pulse, ColorMode, colorMode, setColorMode);
|
||||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Pulse, AlphaMode, alphaMode, setAlphaMode);
|
||||
|
||||
return somethingChanged;
|
||||
}
|
||||
|
||||
EntityPropertyFlags PulsePropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const {
|
||||
EntityPropertyFlags requestedProperties;
|
||||
|
||||
requestedProperties += PROP_PULSE_MIN;
|
||||
requestedProperties += PROP_PULSE_MAX;
|
||||
requestedProperties += PROP_PULSE_PERIOD;
|
||||
requestedProperties += PROP_PULSE_COLOR_MODE;
|
||||
requestedProperties += PROP_PULSE_ALPHA_MODE;
|
||||
|
||||
return requestedProperties;
|
||||
}
|
||||
|
||||
void PulsePropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
int& propertyCount,
|
||||
OctreeElement::AppendState& appendState) const {
|
||||
|
||||
bool successPropertyFits = true;
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_PULSE_MIN, getMin());
|
||||
APPEND_ENTITY_PROPERTY(PROP_PULSE_MAX, getMax());
|
||||
APPEND_ENTITY_PROPERTY(PROP_PULSE_PERIOD, getPeriod());
|
||||
APPEND_ENTITY_PROPERTY(PROP_PULSE_COLOR_MODE, (uint32_t)getColorMode());
|
||||
APPEND_ENTITY_PROPERTY(PROP_PULSE_ALPHA_MODE, (uint32_t)getAlphaMode());
|
||||
}
|
||||
|
||||
int PulsePropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
|
||||
ReadBitstreamToTreeParams& args,
|
||||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
|
||||
bool& somethingChanged) {
|
||||
|
||||
int bytesRead = 0;
|
||||
const unsigned char* dataAt = data;
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_PULSE_MIN, float, setMin);
|
||||
READ_ENTITY_PROPERTY(PROP_PULSE_MAX, float, setMax);
|
||||
READ_ENTITY_PROPERTY(PROP_PULSE_PERIOD, float, setPeriod);
|
||||
READ_ENTITY_PROPERTY(PROP_PULSE_COLOR_MODE, PulseMode, setColorMode);
|
||||
READ_ENTITY_PROPERTY(PROP_PULSE_ALPHA_MODE, PulseMode, setAlphaMode);
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
bool PulsePropertyGroup::operator==(const PulsePropertyGroup& a) const {
|
||||
return (a._min == _min) &&
|
||||
(a._max == _max) &&
|
||||
(a._period == _period) &&
|
||||
(a._colorMode == _colorMode) &&
|
||||
(a._alphaMode == _alphaMode);
|
||||
}
|
99
libraries/entities/src/PulsePropertyGroup.h
Normal file
99
libraries/entities/src/PulsePropertyGroup.h
Normal file
|
@ -0,0 +1,99 @@
|
|||
//
|
||||
// PulsePropertyGroup.h
|
||||
//
|
||||
// Created by Sam Gondelman on 1/15/19
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_PulsePropertyGroup_h
|
||||
#define hifi_PulsePropertyGroup_h
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <QtScript/QScriptEngine>
|
||||
|
||||
#include <PulseMode.h>
|
||||
|
||||
#include "PropertyGroup.h"
|
||||
#include "EntityItemPropertiesMacros.h"
|
||||
|
||||
class EntityItemProperties;
|
||||
class EncodeBitstreamParams;
|
||||
class OctreePacketData;
|
||||
class ReadBitstreamToTreeParams;
|
||||
|
||||
/**jsdoc
|
||||
* Pulse is defined by the following properties.
|
||||
* @typedef {object} Entities.Pulse
|
||||
*
|
||||
* @property {number} min=0 - The minimum value of the pulse multiplier.
|
||||
* @property {number} max=1 - The maximum value of the pulse multiplier.
|
||||
* @property {number} period=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from
|
||||
* <code>min</code> to <code>max</code>, then <code>max</code> to <code>min</code> in one period.
|
||||
* @property {PulseMode} colorMode="none" - If "in", the color is pulsed in phase with the pulse period; if "out"
|
||||
* the color is pulsed out of phase with the pulse period.
|
||||
* @property {PulseMode} alphaMode="none" - If "in", the alpha is pulsed in phase with the pulse period; if "out"
|
||||
* the alpha is pulsed out of phase with the pulse period.
|
||||
*/
|
||||
|
||||
class PulsePropertyGroup : public PropertyGroup {
|
||||
public:
|
||||
// EntityItemProperty related helpers
|
||||
virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties,
|
||||
QScriptEngine* engine, bool skipDefaults,
|
||||
EntityItemProperties& defaultEntityProperties) const override;
|
||||
virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) override;
|
||||
|
||||
void merge(const PulsePropertyGroup& other);
|
||||
|
||||
virtual void debugDump() const override;
|
||||
virtual void listChangedProperties(QList<QString>& out) override;
|
||||
|
||||
virtual bool appendToEditPacket(OctreePacketData* packetData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
int& propertyCount,
|
||||
OctreeElement::AppendState& appendState) const override;
|
||||
|
||||
virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags,
|
||||
const unsigned char*& dataAt, int& processedBytes) override;
|
||||
virtual void markAllChanged() override;
|
||||
virtual EntityPropertyFlags getChangedProperties() const override;
|
||||
|
||||
// EntityItem related helpers
|
||||
// methods for getting/setting all properties of an entity
|
||||
virtual void getProperties(EntityItemProperties& propertiesOut) const override;
|
||||
|
||||
// returns true if something changed
|
||||
virtual bool setProperties(const EntityItemProperties& properties) override;
|
||||
|
||||
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
||||
|
||||
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
int& propertyCount,
|
||||
OctreeElement::AppendState& appendState) const override;
|
||||
|
||||
virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
|
||||
ReadBitstreamToTreeParams& args,
|
||||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
|
||||
bool& somethingChanged) override;
|
||||
|
||||
bool operator==(const PulsePropertyGroup& a) const;
|
||||
bool operator!=(const PulsePropertyGroup& a) const { return !(*this == a); }
|
||||
|
||||
DEFINE_PROPERTY(PROP_PULSE_MIN, Min, min, float, 0.0f);
|
||||
DEFINE_PROPERTY(PROP_PULSE_MAX, Max, max, float, 1.0f);
|
||||
DEFINE_PROPERTY(PROP_PULSE_PERIOD, Period, period, float, 1.0f);
|
||||
DEFINE_PROPERTY_REF_ENUM(PROP_PULSE_COLOR_MODE, ColorMode, colorMode, PulseMode, PulseMode::NONE);
|
||||
DEFINE_PROPERTY_REF_ENUM(PROP_PULSE_ALPHA_MODE, AlphaMode, alphaMode, PulseMode, PulseMode::NONE);
|
||||
};
|
||||
|
||||
#endif // hifi_PulsePropertyGroup_h
|
|
@ -119,6 +119,9 @@ EntityItemProperties ShapeEntityItem::getProperties(const EntityPropertyFlags& d
|
|||
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(alpha, getAlpha);
|
||||
withReadLock([&] {
|
||||
_pulseProperties.getProperties(properties);
|
||||
});
|
||||
properties.setShape(entity::stringFromShape(getShape()));
|
||||
properties._shapeChanged = false;
|
||||
|
||||
|
@ -159,6 +162,10 @@ bool ShapeEntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(alpha, setAlpha);
|
||||
withWriteLock([&] {
|
||||
bool pulsePropertiesChanged = _pulseProperties.setProperties(properties);
|
||||
somethingChanged |= pulsePropertiesChanged;
|
||||
});
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(shape, setShape);
|
||||
|
||||
if (somethingChanged) {
|
||||
|
@ -184,6 +191,13 @@ int ShapeEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
|||
|
||||
READ_ENTITY_PROPERTY(PROP_COLOR, glm::u8vec3, setColor);
|
||||
READ_ENTITY_PROPERTY(PROP_ALPHA, float, setAlpha);
|
||||
withWriteLock([&] {
|
||||
int bytesFromPulse = _pulseProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
|
||||
propertyFlags, overwriteLocalData,
|
||||
somethingChanged);
|
||||
bytesRead += bytesFromPulse;
|
||||
dataAt += bytesFromPulse;
|
||||
});
|
||||
READ_ENTITY_PROPERTY(PROP_SHAPE, QString, setShape);
|
||||
|
||||
return bytesRead;
|
||||
|
@ -193,12 +207,13 @@ EntityPropertyFlags ShapeEntityItem::getEntityProperties(EncodeBitstreamParams&
|
|||
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
||||
requestedProperties += PROP_COLOR;
|
||||
requestedProperties += PROP_ALPHA;
|
||||
requestedProperties += _pulseProperties.getEntityProperties(params);
|
||||
requestedProperties += PROP_SHAPE;
|
||||
return requestedProperties;
|
||||
}
|
||||
|
||||
void ShapeEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData,
|
||||
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
|
@ -208,6 +223,10 @@ void ShapeEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit
|
|||
bool successPropertyFits = true;
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha());
|
||||
withReadLock([&] {
|
||||
_pulseProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties,
|
||||
propertyFlags, propertiesDidntFit, propertyCount, appendState);
|
||||
});
|
||||
APPEND_ENTITY_PROPERTY(PROP_SHAPE, entity::stringFromShape(getShape()));
|
||||
}
|
||||
|
||||
|
@ -416,4 +435,10 @@ void ShapeEntityItem::computeShapeInfo(ShapeInfo& info) {
|
|||
// This value specifies how the shape should be treated by physics calculations.
|
||||
ShapeType ShapeEntityItem::getShapeType() const {
|
||||
return _collisionShapeType;
|
||||
}
|
||||
|
||||
PulsePropertyGroup ShapeEntityItem::getPulseProperties() const {
|
||||
return resultWithReadLock<PulsePropertyGroup>([&] {
|
||||
return _pulseProperties;
|
||||
});
|
||||
}
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include "EntityItem.h"
|
||||
|
||||
#include "PulsePropertyGroup.h"
|
||||
|
||||
namespace entity {
|
||||
enum Shape {
|
||||
Triangle,
|
||||
|
@ -58,7 +60,7 @@ public:
|
|||
EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
||||
|
||||
void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData,
|
||||
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
|
@ -99,9 +101,12 @@ public:
|
|||
virtual void computeShapeInfo(ShapeInfo& info) override;
|
||||
virtual ShapeType getShapeType() const override;
|
||||
|
||||
PulsePropertyGroup getPulseProperties() const;
|
||||
|
||||
protected:
|
||||
glm::u8vec3 _color;
|
||||
float _alpha { 1.0f };
|
||||
PulsePropertyGroup _pulseProperties;
|
||||
entity::Shape _shape { entity::Shape::Sphere };
|
||||
|
||||
//! This is SHAPE_TYPE_ELLIPSOID rather than SHAPE_TYPE_NONE to maintain
|
||||
|
|
|
@ -49,6 +49,10 @@ void TextEntityItem::setUnscaledDimensions(const glm::vec3& value) {
|
|||
EntityItemProperties TextEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const {
|
||||
EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class
|
||||
|
||||
withReadLock([&] {
|
||||
_pulseProperties.getProperties(properties);
|
||||
});
|
||||
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(text, getText);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(lineHeight, getLineHeight);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(textColor, getTextColor);
|
||||
|
@ -67,6 +71,11 @@ bool TextEntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
bool somethingChanged = false;
|
||||
somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class
|
||||
|
||||
withWriteLock([&] {
|
||||
bool pulsePropertiesChanged = _pulseProperties.setProperties(properties);
|
||||
somethingChanged |= pulsePropertiesChanged;
|
||||
});
|
||||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(text, setText);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(lineHeight, setLineHeight);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(textColor, setTextColor);
|
||||
|
@ -101,6 +110,14 @@ int TextEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
|||
int bytesRead = 0;
|
||||
const unsigned char* dataAt = data;
|
||||
|
||||
withWriteLock([&] {
|
||||
int bytesFromPulse = _pulseProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
|
||||
propertyFlags, overwriteLocalData,
|
||||
somethingChanged);
|
||||
bytesRead += bytesFromPulse;
|
||||
dataAt += bytesFromPulse;
|
||||
});
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_TEXT, QString, setText);
|
||||
READ_ENTITY_PROPERTY(PROP_LINE_HEIGHT, float, setLineHeight);
|
||||
READ_ENTITY_PROPERTY(PROP_TEXT_COLOR, glm::u8vec3, setTextColor);
|
||||
|
@ -118,6 +135,8 @@ int TextEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
|||
|
||||
EntityPropertyFlags TextEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
||||
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
||||
|
||||
requestedProperties += _pulseProperties.getEntityProperties(params);
|
||||
requestedProperties += PROP_TEXT;
|
||||
requestedProperties += PROP_LINE_HEIGHT;
|
||||
requestedProperties += PROP_TEXT_COLOR;
|
||||
|
@ -129,11 +148,12 @@ EntityPropertyFlags TextEntityItem::getEntityProperties(EncodeBitstreamParams& p
|
|||
requestedProperties += PROP_RIGHT_MARGIN;
|
||||
requestedProperties += PROP_TOP_MARGIN;
|
||||
requestedProperties += PROP_BOTTOM_MARGIN;
|
||||
|
||||
return requestedProperties;
|
||||
}
|
||||
|
||||
void TextEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData,
|
||||
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
|
@ -142,6 +162,11 @@ void TextEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits
|
|||
|
||||
bool successPropertyFits = true;
|
||||
|
||||
withReadLock([&] {
|
||||
_pulseProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties,
|
||||
propertyFlags, propertiesDidntFit, propertyCount, appendState);
|
||||
});
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_TEXT, getText());
|
||||
APPEND_ENTITY_PROPERTY(PROP_LINE_HEIGHT, getLineHeight());
|
||||
APPEND_ENTITY_PROPERTY(PROP_TEXT_COLOR, getTextColor());
|
||||
|
@ -345,3 +370,9 @@ float TextEntityItem::getBottomMargin() const {
|
|||
return _bottomMargin;
|
||||
});
|
||||
}
|
||||
|
||||
PulsePropertyGroup TextEntityItem::getPulseProperties() const {
|
||||
return resultWithReadLock<PulsePropertyGroup>([&] {
|
||||
return _pulseProperties;
|
||||
});
|
||||
}
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
#include "EntityItem.h"
|
||||
|
||||
#include "PulsePropertyGroup.h"
|
||||
|
||||
class TextEntityItem : public EntityItem {
|
||||
public:
|
||||
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
|
@ -33,7 +35,7 @@ public:
|
|||
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
||||
|
||||
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData,
|
||||
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
|
@ -94,6 +96,8 @@ public:
|
|||
float getBottomMargin() const;
|
||||
void setBottomMargin(float value);
|
||||
|
||||
PulsePropertyGroup getPulseProperties() const;
|
||||
|
||||
private:
|
||||
QString _text;
|
||||
float _lineHeight;
|
||||
|
@ -101,6 +105,7 @@ private:
|
|||
float _textAlpha;
|
||||
glm::u8vec3 _backgroundColor;
|
||||
float _backgroundAlpha;
|
||||
PulsePropertyGroup _pulseProperties;
|
||||
BillboardMode _billboardMode;
|
||||
float _leftMargin;
|
||||
float _rightMargin;
|
||||
|
|
|
@ -45,6 +45,9 @@ EntityItemProperties WebEntityItem::getProperties(const EntityPropertyFlags& des
|
|||
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(alpha, getAlpha);
|
||||
withReadLock([&] {
|
||||
_pulseProperties.getProperties(properties);
|
||||
});
|
||||
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(sourceUrl, getSourceUrl);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(dpi, getDPI);
|
||||
|
@ -60,6 +63,10 @@ bool WebEntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(alpha, setAlpha);
|
||||
withWriteLock([&] {
|
||||
bool pulsePropertiesChanged = _pulseProperties.setProperties(properties);
|
||||
somethingChanged |= pulsePropertiesChanged;
|
||||
});
|
||||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(sourceUrl, setSourceUrl);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(dpi, setDPI);
|
||||
|
@ -91,6 +98,13 @@ int WebEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, i
|
|||
|
||||
READ_ENTITY_PROPERTY(PROP_COLOR, glm::u8vec3, setColor);
|
||||
READ_ENTITY_PROPERTY(PROP_ALPHA, float, setAlpha);
|
||||
withWriteLock([&] {
|
||||
int bytesFromPulse = _pulseProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
|
||||
propertyFlags, overwriteLocalData,
|
||||
somethingChanged);
|
||||
bytesRead += bytesFromPulse;
|
||||
dataAt += bytesFromPulse;
|
||||
});
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_SOURCE_URL, QString, setSourceUrl);
|
||||
READ_ENTITY_PROPERTY(PROP_DPI, uint16_t, setDPI);
|
||||
|
@ -105,6 +119,7 @@ EntityPropertyFlags WebEntityItem::getEntityProperties(EncodeBitstreamParams& pa
|
|||
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
||||
requestedProperties += PROP_COLOR;
|
||||
requestedProperties += PROP_ALPHA;
|
||||
requestedProperties += _pulseProperties.getEntityProperties(params);
|
||||
|
||||
requestedProperties += PROP_SOURCE_URL;
|
||||
requestedProperties += PROP_DPI;
|
||||
|
@ -115,7 +130,7 @@ EntityPropertyFlags WebEntityItem::getEntityProperties(EncodeBitstreamParams& pa
|
|||
}
|
||||
|
||||
void WebEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData,
|
||||
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
|
@ -125,6 +140,10 @@ void WebEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitst
|
|||
bool successPropertyFits = true;
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha());
|
||||
withReadLock([&] {
|
||||
_pulseProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties,
|
||||
propertyFlags, propertiesDidntFit, propertyCount, appendState);
|
||||
});
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, getSourceUrl());
|
||||
APPEND_ENTITY_PROPERTY(PROP_DPI, getDPI());
|
||||
|
@ -285,4 +304,10 @@ WebInputMode WebEntityItem::getInputMode() const {
|
|||
return resultWithReadLock<WebInputMode>([&] {
|
||||
return _inputMode;
|
||||
});
|
||||
}
|
||||
|
||||
PulsePropertyGroup WebEntityItem::getPulseProperties() const {
|
||||
return resultWithReadLock<PulsePropertyGroup>([&] {
|
||||
return _pulseProperties;
|
||||
});
|
||||
}
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include "EntityItem.h"
|
||||
|
||||
#include "PulsePropertyGroup.h"
|
||||
|
||||
class WebEntityItem : public EntityItem {
|
||||
public:
|
||||
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
|
@ -30,7 +32,7 @@ public:
|
|||
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
||||
|
||||
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData,
|
||||
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
|
@ -75,9 +77,12 @@ public:
|
|||
void setInputMode(const WebInputMode& value);
|
||||
WebInputMode getInputMode() const;
|
||||
|
||||
PulsePropertyGroup getPulseProperties() const;
|
||||
|
||||
protected:
|
||||
glm::u8vec3 _color;
|
||||
float _alpha { 1.0f };
|
||||
PulsePropertyGroup _pulseProperties;
|
||||
|
||||
QString _sourceUrl;
|
||||
uint16_t _dpi;
|
||||
|
|
|
@ -755,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>(),
|
||||
|
@ -793,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 {
|
||||
|
@ -851,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;
|
||||
|
@ -895,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;
|
||||
|
||||
|
||||
|
||||
|
@ -983,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());
|
||||
|
@ -1004,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") {
|
||||
|
@ -1437,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;
|
||||
|
|
|
@ -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]])) {
|
||||
|
|
|
@ -590,6 +590,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,
|
||||
|
|
|
@ -258,6 +258,7 @@ enum class EntityVersion : PacketVersion {
|
|||
FixProtocolVersionBumpMismatch,
|
||||
MigrateOverlayRenderProperties,
|
||||
MissingWebEntityProperties,
|
||||
PulseProperties,
|
||||
|
||||
// Add new versions above here
|
||||
NUM_PACKET_TYPE,
|
||||
|
|
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include "RenderLayer.h"
|
||||
#include "PrimitiveMode.h"
|
||||
#include "WebInputMode.h"
|
||||
#include "PulseMode.h"
|
||||
|
||||
#include "OctreeConstants.h"
|
||||
#include "OctreeElement.h"
|
||||
|
@ -269,6 +270,7 @@ public:
|
|||
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, PulseMode& 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);
|
||||
|
|
|
@ -217,6 +217,9 @@ public:
|
|||
|
||||
static const QString& MENU_PATH();
|
||||
|
||||
// for updating plugin-related commands. Mimics the input plugin.
|
||||
virtual void pluginUpdate() = 0;
|
||||
|
||||
signals:
|
||||
void recommendedFramebufferSizeChanged(const QSize& size);
|
||||
void resetSensorsRequested();
|
||||
|
|
|
@ -21,6 +21,7 @@ class DisplayPlugin;
|
|||
class InputPlugin;
|
||||
class CodecPlugin;
|
||||
class SteamClientPlugin;
|
||||
class OculusPlatformPlugin;
|
||||
class Plugin;
|
||||
class PluginContainer;
|
||||
class PluginManager;
|
||||
|
@ -35,4 +36,5 @@ using CodecPluginPointer = std::shared_ptr<CodecPlugin>;
|
|||
using CodecPluginList = std::vector<CodecPluginPointer>;
|
||||
using CodecPluginProvider = std::function<CodecPluginList()>;
|
||||
using SteamClientPluginPointer = std::shared_ptr<SteamClientPlugin>;
|
||||
using OculusPlatformPluginPointer = std::shared_ptr<OculusPlatformPlugin>;
|
||||
using InputPluginSettingsPersister = std::function<void(const InputPluginList&)>;
|
||||
|
|
28
libraries/plugins/src/plugins/OculusPlatformPlugin.h
Normal file
28
libraries/plugins/src/plugins/OculusPlatformPlugin.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// Created by Wayne Chen on 2018/12/20
|
||||
// Copyright 2018 High Fidelity Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2-0.html
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QString>
|
||||
|
||||
#include <functional>
|
||||
|
||||
using NonceUserIDCallback = std::function<void(QString, QString)>;
|
||||
|
||||
class OculusPlatformPlugin {
|
||||
public:
|
||||
virtual ~OculusPlatformPlugin() = default;
|
||||
|
||||
virtual QString getName() const = 0;
|
||||
virtual QString getOculusUserID() const = 0;
|
||||
|
||||
virtual bool isRunning() const = 0;
|
||||
|
||||
virtual void requestNonceAndUserID(NonceUserIDCallback callback) = 0;
|
||||
|
||||
virtual void handleOVREvents() = 0;
|
||||
};
|
|
@ -188,6 +188,22 @@ const SteamClientPluginPointer PluginManager::getSteamClientPlugin() {
|
|||
return steamClientPlugin;
|
||||
}
|
||||
|
||||
const OculusPlatformPluginPointer PluginManager::getOculusPlatformPlugin() {
|
||||
static OculusPlatformPluginPointer oculusPlatformPlugin;
|
||||
static std::once_flag once;
|
||||
std::call_once(once, [&] {
|
||||
// Now grab the dynamic plugins
|
||||
for (auto loader : getLoadedPlugins()) {
|
||||
OculusPlatformProvider* oculusPlatformProvider = qobject_cast<OculusPlatformProvider*>(loader->instance());
|
||||
if (oculusPlatformProvider) {
|
||||
oculusPlatformPlugin = oculusPlatformProvider->getOculusPlatformPlugin();
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
return oculusPlatformPlugin;
|
||||
}
|
||||
|
||||
const DisplayPluginList& PluginManager::getDisplayPlugins() {
|
||||
static std::once_flag once;
|
||||
static auto deviceAddedCallback = [](QString deviceName) {
|
||||
|
|
|
@ -27,6 +27,7 @@ public:
|
|||
const InputPluginList& getInputPlugins();
|
||||
const CodecPluginList& getCodecPlugins();
|
||||
const SteamClientPluginPointer getSteamClientPlugin();
|
||||
const OculusPlatformPluginPointer getOculusPlatformPlugin();
|
||||
|
||||
DisplayPluginList getPreferredDisplayPlugins();
|
||||
void setPreferredDisplayPlugins(const QStringList& displays);
|
||||
|
|
|
@ -51,5 +51,13 @@ public:
|
|||
virtual SteamClientPluginPointer getSteamClientPlugin() = 0;
|
||||
};
|
||||
|
||||
class OculusPlatformProvider {
|
||||
public:
|
||||
virtual OculusPlatformPluginPointer getOculusPlatformPlugin() = 0;
|
||||
};
|
||||
|
||||
#define SteamClientProvider_iid "com.highfidelity.plugins.steamclient"
|
||||
Q_DECLARE_INTERFACE(SteamClientProvider, SteamClientProvider_iid)
|
||||
|
||||
#define OculusPlatformProvider_iid "com.highfidelity.plugins.oculusplatform"
|
||||
Q_DECLARE_INTERFACE(OculusPlatformProvider, OculusPlatformProvider_iid)
|
|
@ -44,7 +44,7 @@ const float DEFAULT_AVATAR_RIGHTHAND_MASS = 2.0f;
|
|||
|
||||
// Used when avatar is missing joints... (avatar space)
|
||||
const glm::quat DEFAULT_AVATAR_MIDDLE_EYE_ROT { Quaternions::Y_180 };
|
||||
const glm::vec3 DEFAULT_AVATAR_HEAD_TO_MIDDLE_EYE_OFFSET = { 0.0f, 0.06f, -0.09f };
|
||||
const glm::vec3 DEFAULT_AVATAR_HEAD_TO_MIDDLE_EYE_OFFSET = { 0.0f, 0.064f, 0.084f };
|
||||
const glm::vec3 DEFAULT_AVATAR_HEAD_POS { 0.0f, 0.53f, 0.0f };
|
||||
const glm::quat DEFAULT_AVATAR_HEAD_ROT { Quaternions::Y_180 };
|
||||
const glm::vec3 DEFAULT_AVATAR_RIGHTARM_POS { -0.134824f, 0.396348f, -0.0515777f };
|
||||
|
|
25
libraries/shared/src/PulseMode.cpp
Normal file
25
libraries/shared/src/PulseMode.cpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// Created by Sam Gondelman on 1/15/19
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "PulseMode.h"
|
||||
|
||||
const char* pulseModeNames[] = {
|
||||
"none",
|
||||
"in",
|
||||
"out"
|
||||
};
|
||||
|
||||
static const size_t PULSE_MODE_NAMES = (sizeof(pulseModeNames) / sizeof(pulseModeNames[0]));
|
||||
|
||||
QString PulseModeHelpers::getNameForPulseMode(PulseMode mode) {
|
||||
if (((int)mode <= 0) || ((int)mode >= (int)PULSE_MODE_NAMES)) {
|
||||
mode = (PulseMode)0;
|
||||
}
|
||||
|
||||
return pulseModeNames[(int)mode];
|
||||
}
|
41
libraries/shared/src/PulseMode.h
Normal file
41
libraries/shared/src/PulseMode.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
//
|
||||
// Created by Sam Gondelman on 1/15/19.
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_PulseMode_h
|
||||
#define hifi_PulseMode_h
|
||||
|
||||
#include "QString"
|
||||
|
||||
/**jsdoc
|
||||
* <p>Pulse modes for color and alpha pulsing.</p>
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Value</th><th>Description</th></tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td><code>none</code></td><td>No pulsing.</td></tr>
|
||||
* <tr><td><code>in</code></td><td>Pulse in phase with the pulse period.</td></tr>
|
||||
* <tr><td><code>out</code></td><td>Pulse out of phase with the pulse period.</td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef {string} PulseMode
|
||||
*/
|
||||
|
||||
enum class PulseMode {
|
||||
NONE = 0,
|
||||
IN_PHASE,
|
||||
OUT_PHASE
|
||||
};
|
||||
|
||||
class PulseModeHelpers {
|
||||
public:
|
||||
static QString getNameForPulseMode(PulseMode mode);
|
||||
};
|
||||
|
||||
#endif // hifi_PulseMode_h
|
||||
|
|
@ -12,6 +12,7 @@
|
|||
#include <display-plugins/CompositorHelper.h>
|
||||
#include <gpu/Frame.h>
|
||||
#include <gl/Config.h>
|
||||
#include <shared/GlobalAppProperties.h>
|
||||
|
||||
#include "OculusHelpers.h"
|
||||
|
||||
|
@ -30,7 +31,7 @@ bool OculusBaseDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (ovr::quitRequested(status) || ovr::displayLost(status) || !ovr::handleOVREvents()) {
|
||||
if (ovr::quitRequested(status) || ovr::displayLost(status)) {
|
||||
QMetaObject::invokeMethod(qApp, "quit");
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -13,6 +13,9 @@
|
|||
|
||||
#include <OVR_CAPI_GL.h>
|
||||
|
||||
#define OVRPL_DISABLED
|
||||
#include <OVR_Platform.h>
|
||||
|
||||
class OculusBaseDisplayPlugin : public HmdDisplayPlugin {
|
||||
using Parent = HmdDisplayPlugin;
|
||||
public:
|
||||
|
@ -30,7 +33,7 @@ public:
|
|||
|
||||
QRectF getPlayAreaRect() override;
|
||||
QVector<glm::vec3> getSensorPositions() override;
|
||||
|
||||
|
||||
protected:
|
||||
void customizeContext() override;
|
||||
void uncustomizeContext() override;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <controllers/UserInputMapper.h>
|
||||
#include <controllers/StandardControls.h>
|
||||
|
||||
#include <AvatarConstants.h>
|
||||
#include <PerfStat.h>
|
||||
#include <PathUtils.h>
|
||||
#include <NumericalConstants.h>
|
||||
|
@ -327,8 +328,14 @@ void OculusControllerManager::TouchDevice::handleHeadPose(float deltaTime,
|
|||
ovr::toGlm(headPose.AngularVelocity));
|
||||
|
||||
glm::mat4 sensorToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat;
|
||||
glm::mat4 defaultHeadOffset = glm::inverse(inputCalibrationData.defaultCenterEyeMat) *
|
||||
inputCalibrationData.defaultHeadMat;
|
||||
glm::mat4 defaultHeadOffset;
|
||||
if (inputCalibrationData.hmdAvatarAlignmentType == controller::HmdAvatarAlignmentType::Eyes) {
|
||||
// align the eyes of the user with the eyes of the avatar
|
||||
defaultHeadOffset = glm::inverse(inputCalibrationData.defaultCenterEyeMat) * inputCalibrationData.defaultHeadMat;
|
||||
} else {
|
||||
// align the head of the user with the head of the avatar
|
||||
defaultHeadOffset = createMatFromQuatAndPos(Quaternions::IDENTITY, -DEFAULT_AVATAR_HEAD_TO_MIDDLE_EYE_OFFSET);
|
||||
}
|
||||
|
||||
pose.valid = true;
|
||||
_poseStateMap[controller::HEAD] = pose.postTransform(defaultHeadOffset).transform(sensorToAvatar);
|
||||
|
|
|
@ -296,34 +296,3 @@ controller::Pose hifi::ovr::toControllerPose(ovrHandType hand,
|
|||
pose.valid = true;
|
||||
return pose;
|
||||
}
|
||||
|
||||
bool hifi::ovr::handleOVREvents() {
|
||||
#ifdef OCULUS_APP_ID
|
||||
if (qApp->property(hifi::properties::OCULUS_STORE).toBool()) {
|
||||
// pop messages to see if we got a return for an entitlement check
|
||||
ovrMessageHandle message = ovr_PopMessage();
|
||||
|
||||
while (message) {
|
||||
switch (ovr_Message_GetType(message)) {
|
||||
case ovrMessage_Entitlement_GetIsViewerEntitled: {
|
||||
if (!ovr_Message_IsError(message)) {
|
||||
// this viewer is entitled, no need to flag anything
|
||||
qCDebug(oculusLog) << "Oculus Platform entitlement check succeeded, proceeding normally";
|
||||
} else {
|
||||
// we failed the entitlement check, quit
|
||||
qCDebug(oculusLog) << "Oculus Platform entitlement check failed, app will now quit" << OCULUS_APP_ID;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// free the message handle to cleanup and not leak
|
||||
ovr_FreeMessage(message);
|
||||
|
||||
// pop the next message to check, if there is one
|
||||
message = ovr_PopMessage();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -30,7 +30,6 @@ struct ovr {
|
|||
static ovrSessionStatus getStatus(ovrResult& result);
|
||||
static ovrTrackingState getTrackingState(double absTime = 0.0, ovrBool latencyMarker = ovrFalse);
|
||||
static QString getError();
|
||||
static bool handleOVREvents();
|
||||
|
||||
static inline bool quitRequested() { return quitRequested(getStatus()); }
|
||||
static inline bool reorientRequested() { return reorientRequested(getStatus()); }
|
||||
|
|
109
plugins/oculus/src/OculusPlatformPlugin.cpp
Normal file
109
plugins/oculus/src/OculusPlatformPlugin.cpp
Normal file
|
@ -0,0 +1,109 @@
|
|||
//
|
||||
// Created by Wayne Chen on 2019/01/08
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "OculusPlatformPlugin.h"
|
||||
|
||||
#include <shared/GlobalAppProperties.h>
|
||||
|
||||
#include <QMetaObject>
|
||||
|
||||
#include "OculusHelpers.h"
|
||||
|
||||
QString OculusAPIPlugin::NAME { "Oculus Rift" };
|
||||
|
||||
OculusAPIPlugin::OculusAPIPlugin() {
|
||||
_session = hifi::ovr::acquireRenderSession();
|
||||
}
|
||||
|
||||
OculusAPIPlugin::~OculusAPIPlugin() {
|
||||
hifi::ovr::releaseRenderSession(_session);
|
||||
}
|
||||
|
||||
bool OculusAPIPlugin::isRunning() const {
|
||||
return (qApp->property(hifi::properties::OCULUS_STORE).toBool());
|
||||
}
|
||||
|
||||
void OculusAPIPlugin::requestNonceAndUserID(NonceUserIDCallback callback) {
|
||||
#ifdef OCULUS_APP_ID
|
||||
_nonceUserIDCallback = callback;
|
||||
ovr_User_GetUserProof();
|
||||
ovr_User_GetLoggedInUser();
|
||||
#endif
|
||||
}
|
||||
|
||||
void OculusAPIPlugin::handleOVREvents() {
|
||||
#ifdef OCULUS_APP_ID
|
||||
if (qApp->property(hifi::properties::OCULUS_STORE).toBool()) {
|
||||
// pop messages to see if we got a return for an entitlement check
|
||||
ovrMessageHandle message { nullptr };
|
||||
|
||||
// pop the next message to check, if there is one
|
||||
while ((message = ovr_PopMessage())) {
|
||||
switch (ovr_Message_GetType(message)) {
|
||||
case ovrMessage_Entitlement_GetIsViewerEntitled: {
|
||||
if (!ovr_Message_IsError(message)) {
|
||||
// this viewer is entitled, no need to flag anything
|
||||
qCDebug(oculusLog) << "Oculus Platform entitlement check succeeded, proceeding normally";
|
||||
} else {
|
||||
// we failed the entitlement check, quit
|
||||
qCDebug(oculusLog) << "Oculus Platform entitlement check failed, app will now quit" << OCULUS_APP_ID;
|
||||
QMetaObject::invokeMethod(qApp, "quit");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ovrMessage_User_Get: {
|
||||
if (!ovr_Message_IsError(message)) {
|
||||
qCDebug(oculusLog) << "Oculus Platform user retrieval succeeded";
|
||||
ovrUserHandle user = ovr_Message_GetUser(message);
|
||||
_user = ovr_User_GetOculusID(user);
|
||||
// went all the way through the `requestNonceAndUserID()` pipeline successfully.
|
||||
} else {
|
||||
qCDebug(oculusLog) << "Oculus Platform user retrieval failed" << QString(ovr_Error_GetMessage(ovr_Message_GetError(message)));
|
||||
// emit the signal so we don't hang for it anywhere else.
|
||||
_user = "";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ovrMessage_User_GetLoggedInUser: {
|
||||
if (!ovr_Message_IsError(message)) {
|
||||
ovrUserHandle user = ovr_Message_GetUser(message);
|
||||
_userID = ovr_User_GetID(user);
|
||||
ovr_User_Get(_userID);
|
||||
} else {
|
||||
qCDebug(oculusLog) << "Oculus Platform user ID retrieval failed" << QString(ovr_Error_GetMessage(ovr_Message_GetError(message)));
|
||||
// emit the signal so we don't hang for it anywhere else.
|
||||
}
|
||||
_userIDChanged = true;
|
||||
break;
|
||||
}
|
||||
case ovrMessage_User_GetUserProof: {
|
||||
if (!ovr_Message_IsError(message)) {
|
||||
ovrUserProofHandle userProof = ovr_Message_GetUserProof(message);
|
||||
_nonce = ovr_UserProof_GetNonce(userProof);
|
||||
qCDebug(oculusLog) << "Oculus Platform nonce retrieval succeeded: " << _nonce;
|
||||
} else {
|
||||
qCDebug(oculusLog) << "Oculus Platform nonce retrieval failed" << QString(ovr_Error_GetMessage(ovr_Message_GetError(message)));
|
||||
_nonce = "";
|
||||
// emit the signal so we don't hang for it anywhere else.
|
||||
}
|
||||
_nonceChanged = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_nonceChanged && _userIDChanged) {
|
||||
_nonceUserIDCallback(_nonce, QString::number(_userID));
|
||||
_nonceChanged = _userIDChanged = false;
|
||||
}
|
||||
|
||||
// free the message handle to cleanup and not leak
|
||||
ovr_FreeMessage(message);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
39
plugins/oculus/src/OculusPlatformPlugin.h
Normal file
39
plugins/oculus/src/OculusPlatformPlugin.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// Created by Wayne Chen on 2019/01/08
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <plugins/OculusPlatformPlugin.h>
|
||||
|
||||
#include <OVR_CAPI_GL.h>
|
||||
|
||||
#define OVRPL_DISABLED
|
||||
#include <OVR_Platform.h>
|
||||
|
||||
class OculusAPIPlugin : public OculusPlatformPlugin {
|
||||
public:
|
||||
OculusAPIPlugin();
|
||||
virtual ~OculusAPIPlugin();
|
||||
QString getName() const { return NAME; }
|
||||
QString getOculusUserID() const { return _user; };
|
||||
|
||||
bool isRunning() const;
|
||||
|
||||
virtual void requestNonceAndUserID(NonceUserIDCallback callback);
|
||||
|
||||
virtual void handleOVREvents();
|
||||
|
||||
private:
|
||||
static QString NAME;
|
||||
NonceUserIDCallback _nonceUserIDCallback;
|
||||
QString _nonce;
|
||||
bool _nonceChanged{ false };
|
||||
bool _userIDChanged{ false };
|
||||
QString _user;
|
||||
ovrID _userID;
|
||||
ovrSession _session;
|
||||
};
|
|
@ -18,15 +18,18 @@
|
|||
|
||||
#include "OculusDisplayPlugin.h"
|
||||
#include "OculusDebugDisplayPlugin.h"
|
||||
#include "OculusPlatformPlugin.h"
|
||||
#include "OculusControllerManager.h"
|
||||
|
||||
class OculusProvider : public QObject, public DisplayProvider, InputProvider
|
||||
class OculusProvider : public QObject, public DisplayProvider, InputProvider, OculusPlatformProvider
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PLUGIN_METADATA(IID DisplayProvider_iid FILE "oculus.json")
|
||||
Q_INTERFACES(DisplayProvider)
|
||||
Q_PLUGIN_METADATA(IID InputProvider_iid FILE "oculus.json")
|
||||
Q_INTERFACES(InputProvider)
|
||||
Q_PLUGIN_METADATA(IID OculusPlatformProvider_iid FILE "oculus.json")
|
||||
Q_INTERFACES(OculusPlatformProvider)
|
||||
|
||||
public:
|
||||
OculusProvider(QObject* parent = nullptr) : QObject(parent) {}
|
||||
|
@ -62,6 +65,15 @@ public:
|
|||
return _inputPlugins;
|
||||
}
|
||||
|
||||
virtual OculusPlatformPluginPointer getOculusPlatformPlugin() override {
|
||||
static std::once_flag once;
|
||||
std::call_once(once, [&] {
|
||||
_oculusPlatformPlugin = std::make_shared<OculusAPIPlugin>();
|
||||
|
||||
});
|
||||
return _oculusPlatformPlugin;
|
||||
}
|
||||
|
||||
virtual void destroyInputPlugins() override {
|
||||
_inputPlugins.clear();
|
||||
}
|
||||
|
@ -73,6 +85,7 @@ public:
|
|||
private:
|
||||
DisplayPluginList _displayPlugins;
|
||||
InputPluginList _inputPlugins;
|
||||
OculusPlatformPluginPointer _oculusPlatformPlugin;
|
||||
};
|
||||
|
||||
#include "OculusProvider.moc"
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <SettingHandle.h>
|
||||
#include <OffscreenUi.h>
|
||||
#include <GLMHelpers.h>
|
||||
#include <AvatarConstants.h>
|
||||
#include <glm/ext.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
#include <ui-plugins/PluginContainer.h>
|
||||
|
@ -1024,7 +1025,16 @@ void ViveControllerManager::InputDevice::handleHeadPoseEvent(const controller::I
|
|||
//perform a 180 flip to make the HMD face the +z instead of -z, beacuse the head faces +z
|
||||
glm::mat4 matYFlip = mat * Matrices::Y_180;
|
||||
controller::Pose pose(extractTranslation(matYFlip), glmExtractRotation(matYFlip), linearVelocity, angularVelocity);
|
||||
glm::mat4 defaultHeadOffset = glm::inverse(inputCalibrationData.defaultCenterEyeMat) * inputCalibrationData.defaultHeadMat;
|
||||
|
||||
glm::mat4 defaultHeadOffset;
|
||||
if (inputCalibrationData.hmdAvatarAlignmentType == controller::HmdAvatarAlignmentType::Eyes) {
|
||||
// align the eyes of the user with the eyes of the avatar
|
||||
defaultHeadOffset = glm::inverse(inputCalibrationData.defaultCenterEyeMat) * inputCalibrationData.defaultHeadMat;
|
||||
} else {
|
||||
// align the head of the user with the head of the avatar
|
||||
defaultHeadOffset = createMatFromQuatAndPos(Quaternions::IDENTITY, -DEFAULT_AVATAR_HEAD_TO_MIDDLE_EYE_OFFSET);
|
||||
}
|
||||
|
||||
glm::mat4 sensorToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat;
|
||||
_poseStateMap[controller::HEAD] = pose.postTransform(defaultHeadOffset).transform(sensorToAvatar);
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ import QtQuick.Layouts 1.3
|
|||
import stylesUit 1.0
|
||||
import controlsUit 1.0 as HifiControls
|
||||
import "configSlider"
|
||||
import "../lib/jet/qml" as Jet
|
||||
|
||||
Rectangle {
|
||||
HifiConstants { id: hifi;}
|
||||
|
@ -249,12 +248,6 @@ Rectangle {
|
|||
checked: render.mainViewTask.getConfig("DrawOverlayHUDOpaqueBounds")["enabled"]
|
||||
onCheckedChanged: { render.mainViewTask.getConfig("DrawOverlayHUDOpaqueBounds")["enabled"] = checked }
|
||||
}
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: "Transparents in HUD"
|
||||
checked: render.mainViewTask.getConfig("DrawOverlayHUDTransparentBounds")["enabled"]
|
||||
onCheckedChanged: { render.mainViewTask.getConfig("DrawOverlayHUDTransparentBounds")["enabled"] = checked }
|
||||
}
|
||||
|
||||
}
|
||||
Column {
|
||||
|
@ -277,6 +270,12 @@ Rectangle {
|
|||
checked: render.mainViewTask.getConfig("DrawZones")["enabled"]
|
||||
onCheckedChanged: { render.mainViewTask.getConfig("ZoneRenderer")["enabled"] = checked; render.mainViewTask.getConfig("DrawZones")["enabled"] = checked; }
|
||||
}
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: "Transparents in HUD"
|
||||
checked: render.mainViewTask.getConfig("DrawOverlayHUDTransparentBounds")["enabled"]
|
||||
onCheckedChanged: { render.mainViewTask.getConfig("DrawOverlayHUDTransparentBounds")["enabled"] = checked }
|
||||
}
|
||||
}
|
||||
}
|
||||
Separator {}
|
||||
|
@ -303,5 +302,13 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
}
|
||||
Row {
|
||||
HifiControls.Button {
|
||||
text: "Material"
|
||||
onClicked: {
|
||||
sendToScript({method: "openMaterialInspectorView"});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
|
||||
(function() {
|
||||
var AppUi = Script.require('appUi');
|
||||
|
||||
var MaterialInspector = Script.require('./materialInspector.js');
|
||||
|
||||
var moveDebugCursor = false;
|
||||
var onMousePressEvent = function (e) {
|
||||
|
@ -41,11 +43,12 @@
|
|||
Render.getConfig("RenderMainView").getConfig("DebugDeferredBuffer").size = { x: nx, y: ny, z: 1.0, w: 1.0 };
|
||||
}
|
||||
|
||||
function Page(title, qmlurl, width, height) {
|
||||
function Page(title, qmlurl, width, height, handleWindowFunc) {
|
||||
this.title = title;
|
||||
this.qml = qmlurl;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.handleWindowFunc = handleWindowFunc;
|
||||
|
||||
this.window;
|
||||
|
||||
|
@ -73,8 +76,10 @@
|
|||
presentationMode: Desktop.PresentationMode.NATIVE,
|
||||
size: {x: this.width, y: this.height}
|
||||
});
|
||||
this.handleWindowFunc(this.window);
|
||||
this.window.closed.connect(function () {
|
||||
that.killView();
|
||||
this.handleWindowFunc(undefined);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -84,8 +89,12 @@
|
|||
this._pages = {};
|
||||
};
|
||||
|
||||
Pages.prototype.addPage = function (command, title, qmlurl, width, height) {
|
||||
this._pages[command] = new Page(title, qmlurl, width, height);
|
||||
Pages.prototype.addPage = function (command, title, qmlurl, width, height, handleWindowFunc) {
|
||||
if (handleWindowFunc === undefined) {
|
||||
// Workaround for bad linter
|
||||
handleWindowFunc = function(window){};
|
||||
}
|
||||
this._pages[command] = new Page(title, qmlurl, width, height, handleWindowFunc);
|
||||
};
|
||||
|
||||
Pages.prototype.open = function (command) {
|
||||
|
@ -110,6 +119,7 @@
|
|||
pages.addPage('openEngineView', 'Render Engine', 'engineInspector.qml', 300, 400);
|
||||
pages.addPage('openEngineLODView', 'Render LOD', 'lod.qml', 300, 400);
|
||||
pages.addPage('openCullInspectorView', 'Cull Inspector', 'culling.qml', 300, 400);
|
||||
pages.addPage('openMaterialInspectorView', 'Material Inspector', 'materialInspector.qml', 300, 400, MaterialInspector.setWindow);
|
||||
|
||||
function fromQml(message) {
|
||||
if (pages.open(message.method)) {
|
||||
|
|
165
scripts/developer/utilities/render/materialInspector.js
Normal file
165
scripts/developer/utilities/render/materialInspector.js
Normal file
|
@ -0,0 +1,165 @@
|
|||
//
|
||||
// materialInspector.js
|
||||
//
|
||||
// Created by Sabrina Shanman on 2019-01-17
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
"use strict";
|
||||
|
||||
var activeWindow;
|
||||
|
||||
// Adapted from Samuel G's material painting script
|
||||
function getTopMaterial(multiMaterial) {
|
||||
// For non-models: multiMaterial[0] will be the top material
|
||||
// For models, multiMaterial[0] is the base material, and multiMaterial[1] is the highest priority applied material
|
||||
if (multiMaterial.length > 1) {
|
||||
if (multiMaterial[1].priority > multiMaterial[0].priority) {
|
||||
return multiMaterial[1];
|
||||
}
|
||||
}
|
||||
|
||||
return multiMaterial[0];
|
||||
}
|
||||
|
||||
function updateMaterial(type, id, meshPart) {
|
||||
var mesh = Graphics.getModel(id);
|
||||
var meshPartString = meshPart.toString();
|
||||
if (!mesh) {
|
||||
return;
|
||||
}
|
||||
var materials = mesh.materialLayers;
|
||||
if (!materials[meshPartString] || materials[meshPartString].length <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var topMaterial = getTopMaterial(materials[meshPartString]);
|
||||
var materialJSONText = JSON.stringify({
|
||||
materialVersion: 1,
|
||||
materials: topMaterial.material
|
||||
}, null, 2);
|
||||
|
||||
toQml({method: "setObjectInfo", params: {id: id, type: type, meshPart: meshPart}});
|
||||
toQml({method: "setMaterialJSON", params: {materialJSONText: materialJSONText}});
|
||||
}
|
||||
|
||||
// Adapted from Samuel G's material painting script
|
||||
function getHoveredMaterialLocation(event) {
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
var closest;
|
||||
var id;
|
||||
var type = "Entity";
|
||||
var avatar = AvatarManager.findRayIntersection(pickRay);
|
||||
var entity = Entities.findRayIntersection(pickRay, true);
|
||||
var overlay = Overlays.findRayIntersection(pickRay, true);
|
||||
|
||||
closest = entity;
|
||||
id = entity.entityID;
|
||||
|
||||
if (avatar.intersects && avatar.distance < closest.distance) {
|
||||
closest = avatar;
|
||||
id = avatar.avatarID;
|
||||
type = "Avatar";
|
||||
} else if (overlay.intersects && overlay.distance < closest.distance) {
|
||||
closest = overlay;
|
||||
id = overlay.overlayID;
|
||||
type = "Overlay";
|
||||
}
|
||||
|
||||
if (closest.intersects) {
|
||||
return {
|
||||
type: type,
|
||||
id: id,
|
||||
meshPart: (closest.extraInfo.shapeID ? closest.extraInfo.shapeID : 0)
|
||||
};
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
var pressedID;
|
||||
var pressedMeshPart;
|
||||
|
||||
function mousePressEvent(event) {
|
||||
if (!event.isLeftButton) {
|
||||
return;
|
||||
}
|
||||
|
||||
var result = getHoveredMaterialLocation(event);
|
||||
|
||||
if (result !== undefined) {
|
||||
pressedID = result.id;
|
||||
pressedMeshPart = result.meshPart;
|
||||
}
|
||||
}
|
||||
|
||||
function mouseReleaseEvent(event) {
|
||||
if (!event.isLeftButton) {
|
||||
return;
|
||||
}
|
||||
|
||||
var result = getHoveredMaterialLocation(event);
|
||||
|
||||
if (result !== undefined && result.id === pressedID && result.meshPart === pressedMeshPart) {
|
||||
updateMaterial(result.type, result.id, result.meshPart);
|
||||
setSelectedObject(result.id, result.type);
|
||||
}
|
||||
}
|
||||
|
||||
function killWindow() {
|
||||
setWindow(undefined);
|
||||
}
|
||||
|
||||
function toQml(message) {
|
||||
if (activeWindow === undefined) {
|
||||
return; // Shouldn't happen
|
||||
}
|
||||
|
||||
activeWindow.sendToQml(message);
|
||||
}
|
||||
|
||||
function fromQml(message) {
|
||||
// No cases currently
|
||||
}
|
||||
|
||||
var SELECT_LIST = "luci_materialInspector_SelectionList";
|
||||
Selection.enableListHighlight(SELECT_LIST, {
|
||||
outlineUnoccludedColor: { red: 255, green: 255, blue: 255 }
|
||||
});
|
||||
function setSelectedObject(id, type) {
|
||||
Selection.clearSelectedItemsList(SELECT_LIST);
|
||||
if (id !== undefined && !Uuid.isNull(id)) {
|
||||
Selection.addToSelectedItemsList(SELECT_LIST, type.toLowerCase(), id);
|
||||
}
|
||||
}
|
||||
|
||||
function setWindow(window) {
|
||||
if (activeWindow !== undefined) {
|
||||
setSelectedObject(Uuid.NULL, "");
|
||||
activeWindow.closed.disconnect(killWindow);
|
||||
activeWindow.fromQml.disconnect(fromQml);
|
||||
Controller.mousePressEvent.disconnect(mousePressEvent);
|
||||
Controller.mouseReleaseEvent.disconnect(mouseReleaseEvent);
|
||||
activeWindow.close();
|
||||
}
|
||||
if (window !== undefined) {
|
||||
window.closed.connect(killWindow);
|
||||
window.fromQml.connect(fromQml);
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
|
||||
}
|
||||
activeWindow = window;
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
setWindow(undefined);
|
||||
Selection.disableListHighlight(SELECT_LIST);
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
||||
|
||||
module.exports = {
|
||||
setWindow: setWindow
|
||||
};
|
65
scripts/developer/utilities/render/materialInspector.qml
Normal file
65
scripts/developer/utilities/render/materialInspector.qml
Normal file
|
@ -0,0 +1,65 @@
|
|||
//
|
||||
// materialInspector.qml
|
||||
//
|
||||
// Created by Sabrina Shanman on 2019-01-16
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.3 as Original
|
||||
import QtQuick.Layouts 1.3
|
||||
|
||||
import stylesUit 1.0
|
||||
import controlsUit 1.0 as HifiControls
|
||||
|
||||
Rectangle {
|
||||
HifiConstants { id: hifi;}
|
||||
color: Qt.rgba(hifi.colors.baseGray.r, hifi.colors.baseGray.g, hifi.colors.baseGray.b, 0.8);
|
||||
id: root;
|
||||
|
||||
function fromScript(message) {
|
||||
switch (message.method) {
|
||||
case "setObjectInfo":
|
||||
entityIDInfo.text = "Type: " + message.params.type + "\nID: " + message.params.id + "\nMesh Part: " + message.params.meshPart;
|
||||
break;
|
||||
case "setMaterialJSON":
|
||||
materialJSONText.text = message.params.materialJSONText;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: entityIDContainer
|
||||
height: 52
|
||||
width: root.width
|
||||
color: Qt.rgba(root.color.r * 0.7, root.color.g * 0.7, root.color.b * 0.7, 0.8);
|
||||
TextEdit {
|
||||
id: entityIDInfo
|
||||
text: "Type: Unknown\nID: None\nMesh Part: Unknown"
|
||||
font.pointSize: 9
|
||||
color: "#FFFFFF"
|
||||
readOnly: true
|
||||
selectByMouse: true
|
||||
}
|
||||
}
|
||||
|
||||
Original.ScrollView {
|
||||
anchors.top: entityIDContainer.bottom
|
||||
height: root.height - entityIDContainer.height
|
||||
width: root.width
|
||||
clip: true
|
||||
Original.ScrollBar.horizontal.policy: Original.ScrollBar.AlwaysOff
|
||||
TextEdit {
|
||||
id: materialJSONText
|
||||
text: "Click an object to get material JSON"
|
||||
width: root.width
|
||||
font.pointSize: 10
|
||||
color: "#FFFFFF"
|
||||
readOnly: true
|
||||
selectByMouse: true
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
}
|
|
@ -62,6 +62,7 @@ function getMyAvatar() {
|
|||
function getMyAvatarSettings() {
|
||||
return {
|
||||
dominantHand: MyAvatar.getDominantHand(),
|
||||
hmdAvatarAlignmentType: MyAvatar.getHmdAvatarAlignmentType(),
|
||||
collisionsEnabled: MyAvatar.getCollisionsEnabled(),
|
||||
otherAvatarsCollisionsEnabled: MyAvatar.getOtherAvatarsCollisionsEnabled(),
|
||||
collisionSoundUrl : MyAvatar.collisionSoundURL,
|
||||
|
@ -129,6 +130,13 @@ function onDominantHandChanged(dominantHand) {
|
|||
}
|
||||
}
|
||||
|
||||
function onHmdAvatarAlignmentTypeChanged(type) {
|
||||
if (currentAvatarSettings.hmdAvatarAlignmentType !== type) {
|
||||
currentAvatarSettings.hmdAvatarAlignmentType = type;
|
||||
sendToQml({'method' : 'settingChanged', 'name' : 'hmdAvatarAlignmentType', 'value' : type});
|
||||
}
|
||||
}
|
||||
|
||||
function onCollisionsEnabledChanged(enabled) {
|
||||
if(currentAvatarSettings.collisionsEnabled !== enabled) {
|
||||
currentAvatarSettings.collisionsEnabled = enabled;
|
||||
|
@ -331,6 +339,7 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See
|
|||
currentAvatar.avatarScale = message.avatarScale;
|
||||
|
||||
MyAvatar.setDominantHand(message.settings.dominantHand);
|
||||
MyAvatar.setHmdAvatarAlignmentType(message.settings.hmdAvatarAlignmentType);
|
||||
MyAvatar.setOtherAvatarsCollisionsEnabled(message.settings.otherAvatarsCollisionsEnabled);
|
||||
MyAvatar.setCollisionsEnabled(message.settings.collisionsEnabled);
|
||||
MyAvatar.collisionSoundURL = message.settings.collisionSoundUrl;
|
||||
|
@ -521,6 +530,7 @@ function off() {
|
|||
Entities.deletingWearable.disconnect(onDeletingWearable);
|
||||
MyAvatar.skeletonModelURLChanged.disconnect(onSkeletonModelURLChanged);
|
||||
MyAvatar.dominantHandChanged.disconnect(onDominantHandChanged);
|
||||
MyAvatar.hmdAvatarAlignmentTypeChanged.disconnect(onHmdAvatarAlignmentTypeChanged);
|
||||
MyAvatar.collisionsEnabledChanged.disconnect(onCollisionsEnabledChanged);
|
||||
MyAvatar.otherAvatarsCollisionsEnabledChanged.disconnect(onOtherAvatarsCollisionsEnabledChanged);
|
||||
MyAvatar.newCollisionSoundURL.disconnect(onNewCollisionSoundUrl);
|
||||
|
@ -542,6 +552,7 @@ function on() {
|
|||
Entities.deletingWearable.connect(onDeletingWearable);
|
||||
MyAvatar.skeletonModelURLChanged.connect(onSkeletonModelURLChanged);
|
||||
MyAvatar.dominantHandChanged.connect(onDominantHandChanged);
|
||||
MyAvatar.hmdAvatarAlignmentTypeChanged.connect(onHmdAvatarAlignmentTypeChanged);
|
||||
MyAvatar.collisionsEnabledChanged.connect(onCollisionsEnabledChanged);
|
||||
MyAvatar.otherAvatarsCollisionsEnabledChanged.connect(onOtherAvatarsCollisionsEnabledChanged);
|
||||
MyAvatar.newCollisionSoundURL.connect(onNewCollisionSoundUrl);
|
||||
|
|
|
@ -44,6 +44,7 @@ const COLUMNS = {
|
|||
initialWidth: 0.16,
|
||||
initiallyShown: true,
|
||||
alwaysShown: true,
|
||||
defaultSortOrder: ASCENDING_SORT,
|
||||
},
|
||||
name: {
|
||||
columnHeader: "Name",
|
||||
|
@ -51,6 +52,7 @@ const COLUMNS = {
|
|||
initialWidth: 0.34,
|
||||
initiallyShown: true,
|
||||
alwaysShown: true,
|
||||
defaultSortOrder: ASCENDING_SORT,
|
||||
},
|
||||
url: {
|
||||
columnHeader: "File",
|
||||
|
@ -58,6 +60,7 @@ const COLUMNS = {
|
|||
propertyID: "url",
|
||||
initialWidth: 0.34,
|
||||
initiallyShown: true,
|
||||
defaultSortOrder: ASCENDING_SORT,
|
||||
},
|
||||
locked: {
|
||||
columnHeader: "",
|
||||
|
@ -66,6 +69,7 @@ const COLUMNS = {
|
|||
initialWidth: 0.08,
|
||||
initiallyShown: true,
|
||||
alwaysShown: true,
|
||||
defaultSortOrder: DESCENDING_SORT,
|
||||
},
|
||||
visible: {
|
||||
columnHeader: "",
|
||||
|
@ -74,25 +78,29 @@ const COLUMNS = {
|
|||
initialWidth: 0.08,
|
||||
initiallyShown: true,
|
||||
alwaysShown: true,
|
||||
defaultSortOrder: DESCENDING_SORT,
|
||||
},
|
||||
verticesCount: {
|
||||
columnHeader: "Verts",
|
||||
dropdownLabel: "Vertices",
|
||||
propertyID: "verticesCount",
|
||||
initialWidth: 0.08,
|
||||
defaultSortOrder: DESCENDING_SORT,
|
||||
},
|
||||
texturesCount: {
|
||||
columnHeader: "Texts",
|
||||
dropdownLabel: "Textures",
|
||||
propertyID: "texturesCount",
|
||||
initialWidth: 0.08,
|
||||
defaultSortOrder: DESCENDING_SORT,
|
||||
},
|
||||
texturesSize: {
|
||||
columnHeader: "Text MB",
|
||||
dropdownLabel: "Texture Size",
|
||||
propertyID: "texturesSize",
|
||||
initialWidth: 0.10,
|
||||
format: decimalMegabytes
|
||||
format: decimalMegabytes,
|
||||
defaultSortOrder: DESCENDING_SORT,
|
||||
},
|
||||
hasTransparent: {
|
||||
columnHeader: "",
|
||||
|
@ -100,6 +108,7 @@ const COLUMNS = {
|
|||
dropdownLabel: "Transparency",
|
||||
propertyID: "hasTransparent",
|
||||
initialWidth: 0.04,
|
||||
defaultSortOrder: DESCENDING_SORT,
|
||||
},
|
||||
isBaked: {
|
||||
columnHeader: "",
|
||||
|
@ -107,12 +116,14 @@ const COLUMNS = {
|
|||
dropdownLabel: "Baked",
|
||||
propertyID: "isBaked",
|
||||
initialWidth: 0.08,
|
||||
defaultSortOrder: DESCENDING_SORT,
|
||||
},
|
||||
drawCalls: {
|
||||
columnHeader: "Draws",
|
||||
dropdownLabel: "Draws",
|
||||
propertyID: "drawCalls",
|
||||
initialWidth: 0.08,
|
||||
defaultSortOrder: DESCENDING_SORT,
|
||||
},
|
||||
hasScript: {
|
||||
columnHeader: "k",
|
||||
|
@ -120,27 +131,10 @@ const COLUMNS = {
|
|||
dropdownLabel: "Script",
|
||||
propertyID: "hasScript",
|
||||
initialWidth: 0.06,
|
||||
defaultSortOrder: DESCENDING_SORT,
|
||||
},
|
||||
};
|
||||
|
||||
const COMPARE_ASCENDING = function(a, b) {
|
||||
let va = a[currentSortColumnID];
|
||||
let vb = b[currentSortColumnID];
|
||||
|
||||
if (va < vb) {
|
||||
return -1;
|
||||
} else if (va > vb) {
|
||||
return 1;
|
||||
} else if (a.id < b.id) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
};
|
||||
const COMPARE_DESCENDING = function(a, b) {
|
||||
return COMPARE_ASCENDING(b, a);
|
||||
};
|
||||
|
||||
const FILTER_TYPES = [
|
||||
"Shape",
|
||||
"Model",
|
||||
|
@ -643,6 +637,10 @@ function loaded() {
|
|||
|
||||
refreshEntityList();
|
||||
}
|
||||
|
||||
const isNullOrEmpty = function(value) {
|
||||
return value === undefined || value === null || value === "";
|
||||
};
|
||||
|
||||
function refreshEntityList() {
|
||||
PROFILE("refresh-entity-list", function() {
|
||||
|
@ -660,8 +658,31 @@ function loaded() {
|
|||
});
|
||||
|
||||
PROFILE("sort", function() {
|
||||
let cmp = currentSortOrder === ASCENDING_SORT ? COMPARE_ASCENDING : COMPARE_DESCENDING;
|
||||
visibleEntities.sort(cmp);
|
||||
let isAscendingSort = currentSortOrder === ASCENDING_SORT;
|
||||
let isDefaultSort = currentSortOrder === COLUMNS[currentSortColumnID].defaultSortOrder;
|
||||
visibleEntities.sort((entityA, entityB) => {
|
||||
/**
|
||||
* If the default sort is ascending, empty should be considered largest.
|
||||
* If the default sort is descending, empty should be considered smallest.
|
||||
*/
|
||||
if (!isAscendingSort) {
|
||||
[entityA, entityB] = [entityB, entityA];
|
||||
}
|
||||
let valueA = entityA[currentSortColumnID];
|
||||
let valueB = entityB[currentSortColumnID];
|
||||
|
||||
if (valueA === valueB) {
|
||||
return entityA.id < entityB.id ? -1 : 1;
|
||||
}
|
||||
|
||||
if (isNullOrEmpty(valueA)) {
|
||||
return (isDefaultSort ? 1 : -1) * (isAscendingSort ? 1 : -1);
|
||||
}
|
||||
if (isNullOrEmpty(valueB)) {
|
||||
return (isDefaultSort ? -1 : 1) * (isAscendingSort ? 1 : -1);
|
||||
}
|
||||
return valueA < valueB ? -1 : 1;
|
||||
});
|
||||
});
|
||||
|
||||
PROFILE("update-dom", function() {
|
||||
|
@ -757,7 +778,7 @@ function loaded() {
|
|||
} else {
|
||||
elSortOrders[currentSortColumnID].innerHTML = "";
|
||||
currentSortColumnID = columnID;
|
||||
currentSortOrder = ASCENDING_SORT;
|
||||
currentSortOrder = COLUMNS[currentSortColumnID].defaultSortOrder;
|
||||
}
|
||||
refreshSortOrder();
|
||||
refreshEntityList();
|
||||
|
|
2562
server-console/package-lock.json
generated
2562
server-console/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -12,7 +12,43 @@ using System;
|
|||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
|
||||
class AvatarExporter : MonoBehaviour {
|
||||
class AvatarExporter : MonoBehaviour {
|
||||
// update version number for every PR that changes this file, also set updated version in README file
|
||||
static readonly string AVATAR_EXPORTER_VERSION = "0.1";
|
||||
|
||||
static readonly float HIPS_GROUND_MIN_Y = 0.01f;
|
||||
static readonly float HIPS_SPINE_CHEST_MIN_SEPARATION = 0.001f;
|
||||
static readonly int MAXIMUM_USER_BONE_COUNT = 256;
|
||||
static readonly string EMPTY_WARNING_TEXT = "None";
|
||||
|
||||
static readonly string[] RECOMMENDED_UNITY_VERSIONS = new string[] {
|
||||
"2018.2.12f1",
|
||||
"2018.2.11f1",
|
||||
"2018.2.10f1",
|
||||
"2018.2.9f1",
|
||||
"2018.2.8f1",
|
||||
"2018.2.7f1",
|
||||
"2018.2.6f1",
|
||||
"2018.2.5f1",
|
||||
"2018.2.4f1",
|
||||
"2018.2.3f1",
|
||||
"2018.2.2f1",
|
||||
"2018.2.1f1",
|
||||
"2018.2.0f2",
|
||||
"2018.1.9f2",
|
||||
"2018.1.8f1",
|
||||
"2018.1.7f1",
|
||||
"2018.1.6f1",
|
||||
"2018.1.5f1",
|
||||
"2018.1.4f1",
|
||||
"2018.1.3f1",
|
||||
"2018.1.2f1",
|
||||
"2018.1.1f1",
|
||||
"2018.1.0f2",
|
||||
"2017.4.18f1",
|
||||
"2017.4.17f1",
|
||||
};
|
||||
|
||||
static readonly Dictionary<string, string> HUMANOID_TO_HIFI_JOINT_NAME = new Dictionary<string, string> {
|
||||
{"Chest", "Spine1"},
|
||||
{"Head", "Head"},
|
||||
|
@ -70,70 +106,163 @@ class AvatarExporter : MonoBehaviour {
|
|||
{"UpperChest", "Spine2"},
|
||||
};
|
||||
|
||||
static readonly Dictionary<string, Quaternion> referenceAbsoluteRotations = new Dictionary<string, Quaternion> {
|
||||
// absolute reference rotations for each Humanoid bone using Artemis fbx in Unity 2018.2.12f1
|
||||
static readonly Dictionary<string, Quaternion> REFERENCE_ROTATIONS = new Dictionary<string, Quaternion> {
|
||||
{"Chest", new Quaternion(-0.0824653f, 1.25274e-7f, -6.75759e-6f, 0.996594f)},
|
||||
{"Head", new Quaternion(-2.509889e-9f, -3.379446e-12f, 2.306033e-13f, 1f)},
|
||||
{"Hips", new Quaternion(-3.043941e-10f, -1.573706e-7f, 5.112975e-6f, 1f)},
|
||||
{"LeftHandIndex3", new Quaternion(-0.5086057f, 0.4908088f, -0.4912299f, -0.5090388f)},
|
||||
{"LeftHandIndex2", new Quaternion(-0.4934928f, 0.5062312f, -0.5064303f, -0.4936835f)},
|
||||
{"LeftHandIndex1", new Quaternion(-0.4986293f, 0.5017503f, -0.5013659f, -0.4982448f)},
|
||||
{"LeftHandPinky3", new Quaternion(-0.490056f, 0.5143053f, -0.5095307f, -0.4855038f)},
|
||||
{"LeftHandPinky2", new Quaternion(-0.5083722f, 0.4954255f, -0.4915887f, -0.5044324f)},
|
||||
{"LeftHandPinky1", new Quaternion(-0.5062528f, 0.497324f, -0.4937346f, -0.5025966f)},
|
||||
{"LeftHandMiddle3", new Quaternion(-0.4871885f, 0.5123404f, -0.5125002f, -0.4873383f)},
|
||||
{"LeftHandMiddle2", new Quaternion(-0.5171652f, 0.4827828f, -0.4822642f, -0.5166069f)},
|
||||
{"LeftHandMiddle1", new Quaternion(-0.4955998f, 0.5041052f, -0.5043675f, -0.4958555f)},
|
||||
{"LeftHandRing3", new Quaternion(-0.4936301f, 0.5097645f, -0.5061787f, -0.4901562f)},
|
||||
{"LeftHandRing2", new Quaternion(-0.5089865f, 0.4943658f, -0.4909532f, -0.5054707f)},
|
||||
{"LeftHandRing1", new Quaternion(-0.5020972f, 0.5005084f, -0.4979034f, -0.4994819f)},
|
||||
{"LeftHandThumb3", new Quaternion(-0.6617184f, 0.2884935f, -0.3604706f, -0.5907297f)},
|
||||
{"LeftHandThumb2", new Quaternion(-0.6935627f, 0.1995147f, -0.2805665f, -0.6328092f)},
|
||||
{"LeftHandThumb1", new Quaternion(-0.6663674f, 0.278572f, -0.3507071f, -0.5961183f)},
|
||||
{"Left Index Distal", new Quaternion(-0.5086057f, 0.4908088f, -0.4912299f, -0.5090388f)},
|
||||
{"Left Index Intermediate", new Quaternion(-0.4934928f, 0.5062312f, -0.5064303f, -0.4936835f)},
|
||||
{"Left Index Proximal", new Quaternion(-0.4986293f, 0.5017503f, -0.5013659f, -0.4982448f)},
|
||||
{"Left Little Distal", new Quaternion(-0.490056f, 0.5143053f, -0.5095307f, -0.4855038f)},
|
||||
{"Left Little Intermediate", new Quaternion(-0.5083722f, 0.4954255f, -0.4915887f, -0.5044324f)},
|
||||
{"Left Little Proximal", new Quaternion(-0.5062528f, 0.497324f, -0.4937346f, -0.5025966f)},
|
||||
{"Left Middle Distal", new Quaternion(-0.4871885f, 0.5123404f, -0.5125002f, -0.4873383f)},
|
||||
{"Left Middle Intermediate", new Quaternion(-0.5171652f, 0.4827828f, -0.4822642f, -0.5166069f)},
|
||||
{"Left Middle Proximal", new Quaternion(-0.4955998f, 0.5041052f, -0.5043675f, -0.4958555f)},
|
||||
{"Left Ring Distal", new Quaternion(-0.4936301f, 0.5097645f, -0.5061787f, -0.4901562f)},
|
||||
{"Left Ring Intermediate", new Quaternion(-0.5089865f, 0.4943658f, -0.4909532f, -0.5054707f)},
|
||||
{"Left Ring Proximal", new Quaternion(-0.5020972f, 0.5005084f, -0.4979034f, -0.4994819f)},
|
||||
{"Left Thumb Distal", new Quaternion(-0.6617184f, 0.2884935f, -0.3604706f, -0.5907297f)},
|
||||
{"Left Thumb Intermediate", new Quaternion(-0.6935627f, 0.1995147f, -0.2805665f, -0.6328092f)},
|
||||
{"Left Thumb Proximal", new Quaternion(-0.6663674f, 0.278572f, -0.3507071f, -0.5961183f)},
|
||||
{"LeftEye", new Quaternion(-2.509889e-9f, -3.379446e-12f, 2.306033e-13f, 1f)},
|
||||
{"LeftFoot", new Quaternion(0.009215056f, 0.3612514f, 0.9323555f, -0.01121602f)},
|
||||
{"LeftHand", new Quaternion(-0.4797408f, 0.5195366f, -0.5279632f, -0.4703038f)},
|
||||
{"LeftForeArm", new Quaternion(-0.4594738f, 0.4594729f, -0.5374805f, -0.5374788f)},
|
||||
{"LeftLeg", new Quaternion(-0.0005380471f, -0.03154583f, 0.9994993f, 0.002378627f)},
|
||||
{"LeftLowerArm", new Quaternion(-0.4594738f, 0.4594729f, -0.5374805f, -0.5374788f)},
|
||||
{"LeftLowerLeg", new Quaternion(-0.0005380471f, -0.03154583f, 0.9994993f, 0.002378627f)},
|
||||
{"LeftShoulder", new Quaternion(-0.3840606f, 0.525857f, -0.5957767f, -0.47013f)},
|
||||
{"LeftToeBase", new Quaternion(-0.0002536641f, 0.7113448f, 0.7027079f, -0.01379319f)},
|
||||
{"LeftArm", new Quaternion(-0.4591927f, 0.4591916f, -0.5377204f, -0.5377193f)},
|
||||
{"LeftUpLeg", new Quaternion(-0.0006682819f, 0.0006864658f, 0.9999968f, -0.002333928f)},
|
||||
{"LeftToes", new Quaternion(-0.0002536641f, 0.7113448f, 0.7027079f, -0.01379319f)},
|
||||
{"LeftUpperArm", new Quaternion(-0.4591927f, 0.4591916f, -0.5377204f, -0.5377193f)},
|
||||
{"LeftUpperLeg", new Quaternion(-0.0006682819f, 0.0006864658f, 0.9999968f, -0.002333928f)},
|
||||
{"Neck", new Quaternion(-2.509889e-9f, -3.379446e-12f, 2.306033e-13f, 1f)},
|
||||
{"RightHandIndex3", new Quaternion(0.5083892f, 0.4911618f, -0.4914584f, 0.5086939f)},
|
||||
{"RightHandIndex2", new Quaternion(0.4931984f, 0.5065879f, -0.5067145f, 0.4933202f)},
|
||||
{"RightHandIndex1", new Quaternion(0.4991491f, 0.5012957f, -0.5008481f, 0.4987026f)},
|
||||
{"RightHandPinky3", new Quaternion(0.4890696f, 0.5154139f, -0.5104482f, 0.4843578f)},
|
||||
{"RightHandPinky2", new Quaternion(0.5084175f, 0.495413f, -0.4915423f, 0.5044444f)},
|
||||
{"RightHandPinky1", new Quaternion(0.5069782f, 0.4965974f, -0.4930001f, 0.5033045f)},
|
||||
{"RightHandMiddle3", new Quaternion(0.4867662f, 0.5129694f, -0.5128888f, 0.4866894f)},
|
||||
{"RightHandMiddle2", new Quaternion(0.5167004f, 0.4833596f, -0.4827653f, 0.5160643f)},
|
||||
{"RightHandMiddle1", new Quaternion(0.4965845f, 0.5031784f, -0.5033959f, 0.4967981f)},
|
||||
{"RightHandRing3", new Quaternion(0.4933217f, 0.5102056f, -0.5064691f, 0.4897075f)},
|
||||
{"RightHandRing2", new Quaternion(0.5085972f, 0.494844f, -0.4913519f, 0.505007f)},
|
||||
{"RightHandRing1", new Quaternion(0.502959f, 0.4996676f, -0.4970418f, 0.5003144f)},
|
||||
{"RightHandThumb3", new Quaternion(0.6611374f, 0.2896575f, -0.3616535f, 0.5900872f)},
|
||||
{"RightHandThumb2", new Quaternion(0.6937408f, 0.1986776f, -0.279922f, 0.6331626f)},
|
||||
{"RightHandThumb1", new Quaternion(0.6664271f, 0.2783172f, -0.3505667f, 0.596253f)},
|
||||
{"Right Index Distal", new Quaternion(0.5083892f, 0.4911618f, -0.4914584f, 0.5086939f)},
|
||||
{"Right Index Intermediate", new Quaternion(0.4931984f, 0.5065879f, -0.5067145f, 0.4933202f)},
|
||||
{"Right Index Proximal", new Quaternion(0.4991491f, 0.5012957f, -0.5008481f, 0.4987026f)},
|
||||
{"Right Little Distal", new Quaternion(0.4890696f, 0.5154139f, -0.5104482f, 0.4843578f)},
|
||||
{"Right Little Intermediate", new Quaternion(0.5084175f, 0.495413f, -0.4915423f, 0.5044444f)},
|
||||
{"Right Little Proximal", new Quaternion(0.5069782f, 0.4965974f, -0.4930001f, 0.5033045f)},
|
||||
{"Right Middle Distal", new Quaternion(0.4867662f, 0.5129694f, -0.5128888f, 0.4866894f)},
|
||||
{"Right Middle Intermediate", new Quaternion(0.5167004f, 0.4833596f, -0.4827653f, 0.5160643f)},
|
||||
{"Right Middle Proximal", new Quaternion(0.4965845f, 0.5031784f, -0.5033959f, 0.4967981f)},
|
||||
{"Right Ring Distal", new Quaternion(0.4933217f, 0.5102056f, -0.5064691f, 0.4897075f)},
|
||||
{"Right Ring Intermediate", new Quaternion(0.5085972f, 0.494844f, -0.4913519f, 0.505007f)},
|
||||
{"Right Ring Proximal", new Quaternion(0.502959f, 0.4996676f, -0.4970418f, 0.5003144f)},
|
||||
{"Right Thumb Distal", new Quaternion(0.6611374f, 0.2896575f, -0.3616535f, 0.5900872f)},
|
||||
{"Right Thumb Intermediate", new Quaternion(0.6937408f, 0.1986776f, -0.279922f, 0.6331626f)},
|
||||
{"Right Thumb Proximal", new Quaternion(0.6664271f, 0.2783172f, -0.3505667f, 0.596253f)},
|
||||
{"RightEye", new Quaternion(-2.509889e-9f, -3.379446e-12f, 2.306033e-13f, 1f)},
|
||||
{"RightFoot", new Quaternion(-0.009482829f, 0.3612484f, 0.9323512f, 0.01144584f)},
|
||||
{"RightHand", new Quaternion(0.4797273f, 0.5195542f, -0.5279628f, 0.4702987f)},
|
||||
{"RightForeArm", new Quaternion(0.4594217f, 0.4594215f, -0.5375242f, 0.5375237f)},
|
||||
{"RightLeg", new Quaternion(0.0005446263f, -0.03177159f, 0.9994922f, -0.002395923f)},
|
||||
{"RightLowerArm", new Quaternion(0.4594217f, 0.4594215f, -0.5375242f, 0.5375237f)},
|
||||
{"RightLowerLeg", new Quaternion(0.0005446263f, -0.03177159f, 0.9994922f, -0.002395923f)},
|
||||
{"RightShoulder", new Quaternion(0.3841222f, 0.5257177f, -0.5957286f, 0.4702966f)},
|
||||
{"RightToeBase", new Quaternion(0.0001034f, 0.7113398f, 0.7027067f, 0.01411251f)},
|
||||
{"RightArm", new Quaternion(0.4591419f, 0.4591423f, -0.537763f, 0.5377624f)},
|
||||
{"RightUpLeg", new Quaternion(0.0006750703f, 0.0008973633f, 0.9999966f, 0.002352045f)},
|
||||
{"RightToes", new Quaternion(0.0001034f, 0.7113398f, 0.7027067f, 0.01411251f)},
|
||||
{"RightUpperArm", new Quaternion(0.4591419f, 0.4591423f, -0.537763f, 0.5377624f)},
|
||||
{"RightUpperLeg", new Quaternion(0.0006750703f, 0.0008973633f, 0.9999966f, 0.002352045f)},
|
||||
{"Spine", new Quaternion(-0.05427956f, 1.508558e-7f, -2.775203e-6f, 0.9985258f)},
|
||||
{"Spine1", new Quaternion(-0.0824653f, 1.25274e-7f, -6.75759e-6f, 0.996594f)},
|
||||
{"Spine2", new Quaternion(-0.0824653f, 1.25274e-7f, -6.75759e-6f, 0.996594f)},
|
||||
{"UpperChest", new Quaternion(-0.0824653f, 1.25274e-7f, -6.75759e-6f, 0.996594f)},
|
||||
};
|
||||
|
||||
// Humanoid mapping name suffixes for each set of appendages
|
||||
static readonly string[] LEG_MAPPING_SUFFIXES = new string[] {
|
||||
"UpperLeg",
|
||||
"LowerLeg",
|
||||
"Foot",
|
||||
"Toes",
|
||||
};
|
||||
static readonly string[] ARM_MAPPING_SUFFIXES = new string[] {
|
||||
"Shoulder",
|
||||
"UpperArm",
|
||||
"LowerArm",
|
||||
"Hand",
|
||||
};
|
||||
static readonly string[] HAND_MAPPING_SUFFIXES = new string[] {
|
||||
" Index Distal",
|
||||
" Index Intermediate",
|
||||
" Index Proximal",
|
||||
" Little Distal",
|
||||
" Little Intermediate",
|
||||
" Little Proximal",
|
||||
" Middle Distal",
|
||||
" Middle Intermediate",
|
||||
" Middle Proximal",
|
||||
" Ring Distal",
|
||||
" Ring Intermediate",
|
||||
" Ring Proximal",
|
||||
" Thumb Distal",
|
||||
" Thumb Intermediate",
|
||||
" Thumb Proximal",
|
||||
};
|
||||
|
||||
static Dictionary<string, string> userBoneToHumanoidMappings = new Dictionary<string, string>();
|
||||
static Dictionary<string, string> userParentNames = new Dictionary<string, string>();
|
||||
static Dictionary<string, Quaternion> userAbsoluteRotations = new Dictionary<string, Quaternion>();
|
||||
enum BoneRule {
|
||||
RecommendedUnityVersion,
|
||||
SingleRoot,
|
||||
NoDuplicateMapping,
|
||||
NoAsymmetricalLegMapping,
|
||||
NoAsymmetricalArmMapping,
|
||||
NoAsymmetricalHandMapping,
|
||||
HipsMapped,
|
||||
SpineMapped,
|
||||
SpineDescendantOfHips,
|
||||
ChestMapped,
|
||||
ChestDescendantOfSpine,
|
||||
NeckMapped,
|
||||
HeadMapped,
|
||||
HeadDescendantOfChest,
|
||||
EyesMapped,
|
||||
HipsNotOnGround,
|
||||
HipsSpineChestNotCoincident,
|
||||
TotalBoneCountUnderLimit,
|
||||
BoneRuleEnd,
|
||||
};
|
||||
// rules that are treated as errors and prevent exporting, otherwise rules will show as warnings
|
||||
static readonly BoneRule[] EXPORT_BLOCKING_BONE_RULES = new BoneRule[] {
|
||||
BoneRule.HipsMapped,
|
||||
BoneRule.SpineMapped,
|
||||
BoneRule.ChestMapped,
|
||||
BoneRule.HeadMapped,
|
||||
};
|
||||
|
||||
class UserBoneInformation {
|
||||
public string humanName; // bone name in Humanoid if it is mapped, otherwise ""
|
||||
public string parentName; // parent user bone name
|
||||
public int mappingCount; // number of times this bone is mapped in Humanoid
|
||||
public Vector3 position; // absolute position
|
||||
public Quaternion rotation; // absolute rotation
|
||||
public BoneTreeNode boneTreeNode;
|
||||
|
||||
public UserBoneInformation() {
|
||||
humanName = "";
|
||||
parentName = "";
|
||||
mappingCount = 0;
|
||||
position = new Vector3();
|
||||
rotation = new Quaternion();
|
||||
boneTreeNode = new BoneTreeNode();
|
||||
}
|
||||
|
||||
public bool HasHumanMapping() { return !string.IsNullOrEmpty(humanName); }
|
||||
}
|
||||
|
||||
class BoneTreeNode {
|
||||
public string boneName;
|
||||
public List<BoneTreeNode> children = new List<BoneTreeNode>();
|
||||
|
||||
public BoneTreeNode() {}
|
||||
public BoneTreeNode(string name) {
|
||||
boneName = name;
|
||||
}
|
||||
}
|
||||
|
||||
static Dictionary<string, UserBoneInformation> userBoneInfos = new Dictionary<string, UserBoneInformation>();
|
||||
static Dictionary<string, string> humanoidToUserBoneMappings = new Dictionary<string, string>();
|
||||
static BoneTreeNode userBoneTree = new BoneTreeNode();
|
||||
static Dictionary<BoneRule, string> failedBoneRules = new Dictionary<BoneRule, string>();
|
||||
|
||||
static string assetPath = "";
|
||||
static string assetName = "";
|
||||
static HumanDescription humanDescription;
|
||||
|
||||
|
||||
[MenuItem("High Fidelity/Export New Avatar")]
|
||||
static void ExportNewAvatar() {
|
||||
|
@ -144,6 +273,11 @@ class AvatarExporter : MonoBehaviour {
|
|||
static void UpdateAvatar() {
|
||||
ExportSelectedAvatar(true);
|
||||
}
|
||||
|
||||
[MenuItem("High Fidelity/About")]
|
||||
static void About() {
|
||||
EditorUtility.DisplayDialog("About", "High Fidelity, Inc.\nAvatar Exporter\nVersion " + AVATAR_EXPORTER_VERSION, "Ok");
|
||||
}
|
||||
|
||||
static void ExportSelectedAvatar(bool updateAvatar) {
|
||||
string[] guids = Selection.assetGUIDs;
|
||||
|
@ -163,14 +297,58 @@ class AvatarExporter : MonoBehaviour {
|
|||
return;
|
||||
}
|
||||
if (modelImporter.animationType != ModelImporterAnimationType.Human) {
|
||||
EditorUtility.DisplayDialog("Error", "Please set model's Animation Type to Humanoid in the Rig section of it's Inspector window.", "Ok");
|
||||
EditorUtility.DisplayDialog("Error", "Please set model's Animation Type to Humanoid in " +
|
||||
" the Rig section of it's Inspector window.", "Ok");
|
||||
return;
|
||||
}
|
||||
|
||||
humanDescription = modelImporter.humanDescription;
|
||||
if (!SetJointMappingsAndParentNames()) {
|
||||
|
||||
humanDescription = modelImporter.humanDescription;
|
||||
SetUserBoneInformation();
|
||||
|
||||
// format resulting bone rule failure strings
|
||||
// consider export-blocking bone rules to be errors and show them in an error dialog,
|
||||
// and also include any other bone rule failures as warnings in the dialog
|
||||
string boneErrors = "";
|
||||
string boneWarnings = "";
|
||||
foreach (var failedBoneRule in failedBoneRules) {
|
||||
if (Array.IndexOf(EXPORT_BLOCKING_BONE_RULES, failedBoneRule.Key) >= 0) {
|
||||
boneErrors += failedBoneRule.Value + "\n\n";
|
||||
} else {
|
||||
boneWarnings += failedBoneRule.Value + "\n\n";
|
||||
}
|
||||
}
|
||||
if (!string.IsNullOrEmpty(boneErrors)) {
|
||||
// if there are both errors and warnings then warnings will be displayed with errors in the error dialog
|
||||
if (!string.IsNullOrEmpty(boneWarnings)) {
|
||||
boneErrors = "Errors:\n\n" + boneErrors;
|
||||
boneErrors += "Warnings:\n\n" + boneWarnings;
|
||||
}
|
||||
// remove ending newlines from the last rule failure string that was added above
|
||||
boneErrors = boneErrors.Substring(0, boneErrors.LastIndexOf("\n\n"));
|
||||
EditorUtility.DisplayDialog("Error", boneErrors, "Ok");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!humanoidToUserBoneMappings.ContainsKey("UpperChest")) {
|
||||
// if parent of Neck is not Chest then map the parent to UpperChest
|
||||
string neckUserBone;
|
||||
if (humanoidToUserBoneMappings.TryGetValue("Neck", out neckUserBone)) {
|
||||
UserBoneInformation neckParentBoneInfo;
|
||||
string neckParentUserBone = userBoneInfos[neckUserBone].parentName;
|
||||
if (userBoneInfos.TryGetValue(neckParentUserBone, out neckParentBoneInfo) && !neckParentBoneInfo.HasHumanMapping()) {
|
||||
neckParentBoneInfo.humanName = "UpperChest";
|
||||
humanoidToUserBoneMappings.Add("UpperChest", neckParentUserBone);
|
||||
}
|
||||
}
|
||||
// if there is still no UpperChest bone but there is a Chest bone then we remap Chest to UpperChest
|
||||
string chestUserBone;
|
||||
if (!humanoidToUserBoneMappings.ContainsKey("UpperChest") &&
|
||||
humanoidToUserBoneMappings.TryGetValue("Chest", out chestUserBone)) {
|
||||
userBoneInfos[chestUserBone].humanName = "UpperChest";
|
||||
humanoidToUserBoneMappings.Remove("Chest");
|
||||
humanoidToUserBoneMappings.Add("UpperChest", chestUserBone);
|
||||
}
|
||||
}
|
||||
|
||||
string documentsFolder = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments);
|
||||
string hifiFolder = documentsFolder + "\\High Fidelity Projects";
|
||||
|
@ -236,11 +414,12 @@ class AvatarExporter : MonoBehaviour {
|
|||
modelImporter = ModelImporter.GetAtPath(assetPath) as ModelImporter;
|
||||
modelImporter.animationType = ModelImporterAnimationType.Human;
|
||||
EditorUtility.SetDirty(modelImporter);
|
||||
modelImporter.SaveAndReimport();
|
||||
humanDescription = modelImporter.humanDescription;
|
||||
modelImporter.SaveAndReimport();
|
||||
|
||||
// redo joint mappings and parent names due to the fbx change
|
||||
SetJointMappingsAndParentNames();
|
||||
// redo parent names, joint mappings, and user bone positions due to the fbx change
|
||||
// as well as re-check the bone rules for failures
|
||||
humanDescription = modelImporter.humanDescription;
|
||||
SetUserBoneInformation();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -277,19 +456,30 @@ class AvatarExporter : MonoBehaviour {
|
|||
|
||||
// write out a new fst file in place of the old file
|
||||
WriteFST(exportFstPath, projectName);
|
||||
|
||||
// display success dialog with any bone rule warnings
|
||||
string successDialog = "Avatar successfully updated!";
|
||||
if (!string.IsNullOrEmpty(boneWarnings)) {
|
||||
successDialog += "\n\nWarnings:\n" + boneWarnings;
|
||||
}
|
||||
EditorUtility.DisplayDialog("Success!", successDialog, "Ok");
|
||||
} else { // Export New Avatar menu option
|
||||
// create High Fidelity Projects folder in user documents folder if it doesn't exist
|
||||
if (!Directory.Exists(hifiFolder)) {
|
||||
Directory.CreateDirectory(hifiFolder);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(boneWarnings)) {
|
||||
boneWarnings = EMPTY_WARNING_TEXT;
|
||||
}
|
||||
|
||||
// open a popup window to enter new export project name and project location
|
||||
ExportProjectWindow window = ScriptableObject.CreateInstance<ExportProjectWindow>();
|
||||
window.Init(hifiFolder, OnExportProjectWindowClose);
|
||||
window.Init(hifiFolder, boneWarnings, OnExportProjectWindowClose);
|
||||
}
|
||||
}
|
||||
|
||||
static void OnExportProjectWindowClose(string projectDirectory, string projectName) {
|
||||
static void OnExportProjectWindowClose(string projectDirectory, string projectName, string warnings) {
|
||||
// copy the fbx from the Unity Assets folder to the project directory
|
||||
string exportModelPath = projectDirectory + assetName + ".fbx";
|
||||
File.Copy(assetPath, exportModelPath);
|
||||
|
@ -304,94 +494,19 @@ class AvatarExporter : MonoBehaviour {
|
|||
string exportFstPath = projectDirectory + "avatar.fst";
|
||||
WriteFST(exportFstPath, projectName);
|
||||
|
||||
// remove any double slashes in texture directory path and warn user to copy external textures over
|
||||
// remove any double slashes in texture directory path, display success dialog with any
|
||||
// bone warnings previously mentioned, and suggest user to copy external textures over
|
||||
texturesDirectory = texturesDirectory.Replace("\\\\", "\\");
|
||||
EditorUtility.DisplayDialog("Warning", "If you are using any external textures with your model, " +
|
||||
"please copy those textures to " + texturesDirectory, "Ok");
|
||||
string successDialog = "Avatar successfully exported!\n\n";
|
||||
if (warnings != EMPTY_WARNING_TEXT) {
|
||||
successDialog += "Warnings:\n" + warnings;
|
||||
}
|
||||
successDialog += "Note: If you are using any external textures with your model, " +
|
||||
"please copy those textures to " + texturesDirectory;
|
||||
EditorUtility.DisplayDialog("Success!", successDialog, "Ok");
|
||||
}
|
||||
|
||||
static bool SetJointMappingsAndParentNames() {
|
||||
userParentNames.Clear();
|
||||
userBoneToHumanoidMappings.Clear();
|
||||
|
||||
// instantiate a game object of the user avatar to save out bone parents then destroy it
|
||||
UnityEngine.Object avatarResource = AssetDatabase.LoadAssetAtPath(assetPath, typeof(UnityEngine.Object));
|
||||
GameObject assetGameObject = (GameObject)Instantiate(avatarResource);
|
||||
SetParentNames(assetGameObject.transform, userParentNames);
|
||||
DestroyImmediate(assetGameObject);
|
||||
|
||||
// store joint mappings only for joints that exist in hifi and verify missing required joints
|
||||
HumanBone[] boneMap = humanDescription.human;
|
||||
string chestUserBone = "";
|
||||
string neckUserBone = "";
|
||||
foreach (HumanBone bone in boneMap) {
|
||||
string humanName = bone.humanName;
|
||||
string boneName = bone.boneName;
|
||||
string hifiJointName;
|
||||
if (HUMANOID_TO_HIFI_JOINT_NAME.TryGetValue(humanName, out hifiJointName)) {
|
||||
userBoneToHumanoidMappings.Add(boneName, humanName);
|
||||
if (humanName == "Chest") {
|
||||
chestUserBone = boneName;
|
||||
} else if (humanName == "Neck") {
|
||||
neckUserBone = boneName;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (!userBoneToHumanoidMappings.ContainsValue("Hips")) {
|
||||
EditorUtility.DisplayDialog("Error", "There is no Hips bone in selected avatar", "Ok");
|
||||
return false;
|
||||
}
|
||||
if (!userBoneToHumanoidMappings.ContainsValue("Spine")) {
|
||||
EditorUtility.DisplayDialog("Error", "There is no Spine bone in selected avatar", "Ok");
|
||||
return false;
|
||||
}
|
||||
if (!userBoneToHumanoidMappings.ContainsValue("Chest")) {
|
||||
// check to see if there is a child of Spine that could be mapped to Chest
|
||||
string spineChild = "";
|
||||
foreach (var parentRelation in userParentNames) {
|
||||
string humanName;
|
||||
if (userBoneToHumanoidMappings.TryGetValue(parentRelation.Value, out humanName) && humanName == "Spine") {
|
||||
if (spineChild == "") {
|
||||
spineChild = parentRelation.Key;
|
||||
} else {
|
||||
// found more than one Spine child so we can't choose one to remap
|
||||
spineChild = "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (spineChild != "" && !userBoneToHumanoidMappings.ContainsKey(spineChild)) {
|
||||
// use child of Spine as Chest
|
||||
userBoneToHumanoidMappings.Add(spineChild, "Chest");
|
||||
chestUserBone = spineChild;
|
||||
} else {
|
||||
EditorUtility.DisplayDialog("Error", "There is no Chest bone in selected avatar", "Ok");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!userBoneToHumanoidMappings.ContainsValue("UpperChest")) {
|
||||
//if parent of Neck is not Chest then map the parent to UpperChest
|
||||
if (neckUserBone != "") {
|
||||
string neckParentUserBone, neckParentHuman;
|
||||
userParentNames.TryGetValue(neckUserBone, out neckParentUserBone);
|
||||
userBoneToHumanoidMappings.TryGetValue(neckParentUserBone, out neckParentHuman);
|
||||
if (neckParentHuman != "Chest" && !userBoneToHumanoidMappings.ContainsKey(neckParentUserBone)) {
|
||||
userBoneToHumanoidMappings.Add(neckParentUserBone, "UpperChest");
|
||||
}
|
||||
}
|
||||
// if there is still no UpperChest bone but there is a Chest bone then we remap Chest to UpperChest
|
||||
if (!userBoneToHumanoidMappings.ContainsValue("UpperChest") && chestUserBone != "") {
|
||||
userBoneToHumanoidMappings[chestUserBone] = "UpperChest";
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void WriteFST(string exportFstPath, string projectName) {
|
||||
userAbsoluteRotations.Clear();
|
||||
|
||||
static void WriteFST(string exportFstPath, string projectName) {
|
||||
// write out core fields to top of fst file
|
||||
try {
|
||||
File.WriteAllText(exportFstPath, "name = " + projectName + "\ntype = body+head\nscale = 1\nfilename = " +
|
||||
|
@ -403,49 +518,53 @@ class AvatarExporter : MonoBehaviour {
|
|||
}
|
||||
|
||||
// write out joint mappings to fst file
|
||||
foreach (var jointMapping in userBoneToHumanoidMappings) {
|
||||
string hifiJointName = HUMANOID_TO_HIFI_JOINT_NAME[jointMapping.Value];
|
||||
File.AppendAllText(exportFstPath, "jointMap = " + hifiJointName + " = " + jointMapping.Key + "\n");
|
||||
foreach (var userBoneInfo in userBoneInfos) {
|
||||
if (userBoneInfo.Value.HasHumanMapping()) {
|
||||
string hifiJointName = HUMANOID_TO_HIFI_JOINT_NAME[userBoneInfo.Value.humanName];
|
||||
File.AppendAllText(exportFstPath, "jointMap = " + hifiJointName + " = " + userBoneInfo.Key + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
// calculate and write out joint rotation offsets to fst file
|
||||
SkeletonBone[] skeletonMap = humanDescription.skeleton;
|
||||
foreach (SkeletonBone userBone in skeletonMap) {
|
||||
string userBoneName = userBone.name;
|
||||
Quaternion userBoneRotation = userBone.rotation;
|
||||
|
||||
string parentName;
|
||||
userParentNames.TryGetValue(userBoneName, out parentName);
|
||||
if (parentName == "root") {
|
||||
// if the parent is root then use bone's rotation
|
||||
userAbsoluteRotations.Add(userBoneName, userBoneRotation);
|
||||
} else {
|
||||
// otherwise multiply bone's rotation by parent bone's absolute rotation
|
||||
userAbsoluteRotations.Add(userBoneName, userAbsoluteRotations[parentName] * userBoneRotation);
|
||||
UserBoneInformation userBoneInfo;
|
||||
if (!userBoneInfos.TryGetValue(userBoneName, out userBoneInfo)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// generate joint rotation offsets for both humanoid-mapped bones as well as extra unmapped bones in user avatar
|
||||
Quaternion userBoneRotation = userBone.rotation;
|
||||
string parentName = userBoneInfo.parentName;
|
||||
if (parentName == "root") {
|
||||
// if the parent is root then use bone's rotation
|
||||
userBoneInfo.rotation = userBoneRotation;
|
||||
} else {
|
||||
// otherwise multiply bone's rotation by parent bone's absolute rotation
|
||||
userBoneInfo.rotation = userBoneInfos[parentName].rotation * userBoneRotation;
|
||||
}
|
||||
|
||||
// generate joint rotation offsets for both humanoid-mapped bones as well as extra unmapped bones
|
||||
Quaternion jointOffset = new Quaternion();
|
||||
string humanName, outputJointName = "";
|
||||
if (userBoneToHumanoidMappings.TryGetValue(userBoneName, out humanName)) {
|
||||
outputJointName = HUMANOID_TO_HIFI_JOINT_NAME[humanName];
|
||||
Quaternion rotation = referenceAbsoluteRotations[outputJointName];
|
||||
jointOffset = Quaternion.Inverse(userAbsoluteRotations[userBoneName]) * rotation;
|
||||
} else if (userAbsoluteRotations.ContainsKey(userBoneName)) {
|
||||
string outputJointName = "";
|
||||
if (userBoneInfo.HasHumanMapping()) {
|
||||
outputJointName = HUMANOID_TO_HIFI_JOINT_NAME[userBoneInfo.humanName];
|
||||
Quaternion rotation = REFERENCE_ROTATIONS[userBoneInfo.humanName];
|
||||
jointOffset = Quaternion.Inverse(userBoneInfo.rotation) * rotation;
|
||||
} else {
|
||||
outputJointName = userBoneName;
|
||||
string lastRequiredParent = FindLastRequiredParentBone(userBoneName);
|
||||
if (lastRequiredParent == "root") {
|
||||
jointOffset = Quaternion.Inverse(userAbsoluteRotations[userBoneName]);
|
||||
} else {
|
||||
jointOffset = Quaternion.Inverse(userBoneInfo.rotation);
|
||||
string lastRequiredParent = FindLastRequiredAncestorBone(userBoneName);
|
||||
if (lastRequiredParent != "root") {
|
||||
// take the previous offset and multiply it by the current local when we have an extra joint
|
||||
string lastRequiredParentHifiName = HUMANOID_TO_HIFI_JOINT_NAME[userBoneToHumanoidMappings[lastRequiredParent]];
|
||||
Quaternion lastRequiredParentRotation = referenceAbsoluteRotations[lastRequiredParentHifiName];
|
||||
jointOffset = Quaternion.Inverse(userAbsoluteRotations[userBoneName]) * lastRequiredParentRotation;
|
||||
string lastRequiredParentHumanName = userBoneInfos[lastRequiredParent].humanName;
|
||||
Quaternion lastRequiredParentRotation = REFERENCE_ROTATIONS[lastRequiredParentHumanName];
|
||||
jointOffset *= lastRequiredParentRotation;
|
||||
}
|
||||
}
|
||||
|
||||
// swap from left-handed (Unity) to right-handed (HiFi) coordinates and write out joint rotation offset to fst
|
||||
if (outputJointName != "") {
|
||||
if (!string.IsNullOrEmpty(outputJointName)) {
|
||||
jointOffset = new Quaternion(-jointOffset.x, jointOffset.y, jointOffset.z, -jointOffset.w);
|
||||
File.AppendAllText(exportFstPath, "jointRotationOffset = " + outputJointName + " = (" + jointOffset.x + ", " +
|
||||
jointOffset.y + ", " + jointOffset.z + ", " + jointOffset.w + ")\n");
|
||||
|
@ -455,48 +574,326 @@ class AvatarExporter : MonoBehaviour {
|
|||
// open File Explorer to the project directory once finished
|
||||
System.Diagnostics.Process.Start("explorer.exe", "/select," + exportFstPath);
|
||||
}
|
||||
|
||||
static void SetParentNames(Transform modelBone, Dictionary<string, string> parentNames) {
|
||||
for (int i = 0; i < modelBone.childCount; i++) {
|
||||
SetParentNames(modelBone.GetChild(i), parentNames);
|
||||
|
||||
static void SetUserBoneInformation() {
|
||||
userBoneInfos.Clear();
|
||||
humanoidToUserBoneMappings.Clear();
|
||||
userBoneTree = new BoneTreeNode();
|
||||
|
||||
// instantiate a game object of the user avatar to traverse the bone tree to gather
|
||||
// bone parents and positions as well as build a bone tree, then destroy it
|
||||
UnityEngine.Object avatarResource = AssetDatabase.LoadAssetAtPath(assetPath, typeof(UnityEngine.Object));
|
||||
GameObject assetGameObject = (GameObject)Instantiate(avatarResource);
|
||||
TraverseUserBoneTree(assetGameObject.transform);
|
||||
DestroyImmediate(assetGameObject);
|
||||
|
||||
// iterate over Humanoid bones and update user bone info to increase human mapping counts for each bone
|
||||
// as well as set their Humanoid name and build a Humanoid to user bone mapping
|
||||
HumanBone[] boneMap = humanDescription.human;
|
||||
foreach (HumanBone bone in boneMap) {
|
||||
string humanName = bone.humanName;
|
||||
string userBoneName = bone.boneName;
|
||||
string hifiJointName;
|
||||
if (userBoneInfos.ContainsKey(userBoneName)) {
|
||||
++userBoneInfos[userBoneName].mappingCount;
|
||||
if (HUMANOID_TO_HIFI_JOINT_NAME.TryGetValue(humanName, out hifiJointName)) {
|
||||
userBoneInfos[userBoneName].humanName = humanName;
|
||||
humanoidToUserBoneMappings.Add(humanName, userBoneName);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (modelBone.parent != null) {
|
||||
parentNames.Add(modelBone.name, modelBone.parent.name);
|
||||
} else {
|
||||
parentNames.Add(modelBone.name, "root");
|
||||
|
||||
// generate the list of bone rule failure strings for any bone rules that are not satisfied by this avatar
|
||||
SetFailedBoneRules();
|
||||
}
|
||||
|
||||
static void TraverseUserBoneTree(Transform modelBone) {
|
||||
GameObject gameObject = modelBone.gameObject;
|
||||
|
||||
// check if this transform is a node containing mesh, light, or camera instead of a bone
|
||||
bool mesh = gameObject.GetComponent<MeshRenderer>() != null || gameObject.GetComponent<SkinnedMeshRenderer>() != null;
|
||||
bool light = gameObject.GetComponent<Light>() != null;
|
||||
bool camera = gameObject.GetComponent<Camera>() != null;
|
||||
|
||||
// if it is in fact a bone, add it to the bone tree as well as user bone infos list with position and parent name
|
||||
if (!mesh && !light && !camera) {
|
||||
UserBoneInformation userBoneInfo = new UserBoneInformation();
|
||||
userBoneInfo.position = modelBone.position; // bone's absolute position
|
||||
|
||||
string boneName = modelBone.name;
|
||||
if (modelBone.parent == null) {
|
||||
// if no parent then this is actual root bone node of the user avatar, so consider it's parent as "root"
|
||||
userBoneTree = new BoneTreeNode(boneName); // initialize root of tree
|
||||
userBoneInfo.parentName = "root";
|
||||
userBoneInfo.boneTreeNode = userBoneTree;
|
||||
} else {
|
||||
// otherwise add this bone node as a child to it's parent's children list
|
||||
string parentName = modelBone.parent.name;
|
||||
BoneTreeNode boneTreeNode = new BoneTreeNode(boneName);
|
||||
userBoneInfos[parentName].boneTreeNode.children.Add(boneTreeNode);
|
||||
userBoneInfo.parentName = parentName;
|
||||
}
|
||||
|
||||
userBoneInfos.Add(boneName, userBoneInfo);
|
||||
}
|
||||
|
||||
// recurse over transform node's children
|
||||
for (int i = 0; i < modelBone.childCount; ++i) {
|
||||
TraverseUserBoneTree(modelBone.GetChild(i));
|
||||
}
|
||||
}
|
||||
|
||||
static string FindLastRequiredParentBone(string currentBone) {
|
||||
static string FindLastRequiredAncestorBone(string currentBone) {
|
||||
string result = currentBone;
|
||||
while (result != "root" && !userBoneToHumanoidMappings.ContainsKey(result)) {
|
||||
result = userParentNames[result];
|
||||
// iterating upward through user bone info parent names, find the first ancestor bone that is mapped in Humanoid
|
||||
while (result != "root" && userBoneInfos.ContainsKey(result) && !userBoneInfos[result].HasHumanMapping()) {
|
||||
result = userBoneInfos[result].parentName;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void SetFailedBoneRules() {
|
||||
failedBoneRules.Clear();
|
||||
|
||||
string hipsUserBone = "";
|
||||
string spineUserBone = "";
|
||||
string chestUserBone = "";
|
||||
string headUserBone = "";
|
||||
|
||||
Vector3 hipsPosition = new Vector3();
|
||||
|
||||
// iterate over all bone rules in order and add any rules that fail
|
||||
// to the failed bone rules map with appropriate error or warning text
|
||||
for (BoneRule boneRule = 0; boneRule < BoneRule.BoneRuleEnd; ++boneRule) {
|
||||
switch (boneRule) {
|
||||
case BoneRule.RecommendedUnityVersion:
|
||||
if (Array.IndexOf(RECOMMENDED_UNITY_VERSIONS, Application.unityVersion) == -1) {
|
||||
failedBoneRules.Add(boneRule, "The current version of Unity is not one of the recommended Unity " +
|
||||
"versions. If you are using a version of Unity later than 2018.2.12f1, " +
|
||||
"it is recommended to apply Enforce T-Pose under the Pose dropdown " +
|
||||
"in Humanoid configuration.");
|
||||
}
|
||||
break;
|
||||
case BoneRule.SingleRoot:
|
||||
// bone rule fails if the root bone node has more than one child bone
|
||||
if (userBoneTree.children.Count > 1) {
|
||||
failedBoneRules.Add(boneRule, "There is more than one bone at the top level of the selected avatar's " +
|
||||
"bone hierarchy. Please ensure all bones for Humanoid mappings are " +
|
||||
"under the same bone hierarchy.");
|
||||
}
|
||||
break;
|
||||
case BoneRule.NoDuplicateMapping:
|
||||
// bone rule fails if any user bone is mapped to more than one Humanoid bone
|
||||
foreach (var userBoneInfo in userBoneInfos) {
|
||||
string boneName = userBoneInfo.Key;
|
||||
int mappingCount = userBoneInfo.Value.mappingCount;
|
||||
if (mappingCount > 1) {
|
||||
string text = "The " + boneName + " bone is mapped to more than one bone in Humanoid.";
|
||||
if (failedBoneRules.ContainsKey(boneRule)) {
|
||||
failedBoneRules[boneRule] += "\n" + text;
|
||||
} else {
|
||||
failedBoneRules.Add(boneRule, text);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case BoneRule.NoAsymmetricalLegMapping:
|
||||
CheckAsymmetricalMappingRule(boneRule, LEG_MAPPING_SUFFIXES, "leg");
|
||||
break;
|
||||
case BoneRule.NoAsymmetricalArmMapping:
|
||||
CheckAsymmetricalMappingRule(boneRule, ARM_MAPPING_SUFFIXES, "arm");
|
||||
break;
|
||||
case BoneRule.NoAsymmetricalHandMapping:
|
||||
CheckAsymmetricalMappingRule(boneRule, HAND_MAPPING_SUFFIXES, "hand");
|
||||
break;
|
||||
case BoneRule.HipsMapped:
|
||||
hipsUserBone = CheckHumanBoneMappingRule(boneRule, "Hips");
|
||||
break;
|
||||
case BoneRule.SpineMapped:
|
||||
spineUserBone = CheckHumanBoneMappingRule(boneRule, "Spine");
|
||||
break;
|
||||
case BoneRule.SpineDescendantOfHips:
|
||||
CheckUserBoneDescendantOfHumanRule(boneRule, spineUserBone, "Hips");
|
||||
break;
|
||||
case BoneRule.ChestMapped:
|
||||
if (!humanoidToUserBoneMappings.TryGetValue("Chest", out chestUserBone)) {
|
||||
// check to see if there is a child of Spine that we can suggest to be mapped to Chest
|
||||
string spineChild = "";
|
||||
if (!string.IsNullOrEmpty(spineUserBone)) {
|
||||
BoneTreeNode spineTreeNode = userBoneInfos[spineUserBone].boneTreeNode;
|
||||
if (spineTreeNode.children.Count == 1) {
|
||||
spineChild = spineTreeNode.children[0].boneName;
|
||||
}
|
||||
}
|
||||
failedBoneRules.Add(boneRule, "There is no Chest bone mapped in Humanoid for the selected avatar.");
|
||||
// if the only found child of Spine is not yet mapped then add it as a suggestion for Chest mapping
|
||||
if (!string.IsNullOrEmpty(spineChild) && !userBoneInfos[spineChild].HasHumanMapping()) {
|
||||
failedBoneRules[boneRule] += " It is suggested that you map bone " + spineChild +
|
||||
" to Chest in Humanoid.";
|
||||
}
|
||||
}
|
||||
break;
|
||||
case BoneRule.ChestDescendantOfSpine:
|
||||
CheckUserBoneDescendantOfHumanRule(boneRule, chestUserBone, "Spine");
|
||||
break;
|
||||
case BoneRule.NeckMapped:
|
||||
CheckHumanBoneMappingRule(boneRule, "Neck");
|
||||
break;
|
||||
case BoneRule.HeadMapped:
|
||||
headUserBone = CheckHumanBoneMappingRule(boneRule, "Head");
|
||||
break;
|
||||
case BoneRule.HeadDescendantOfChest:
|
||||
CheckUserBoneDescendantOfHumanRule(boneRule, headUserBone, "Chest");
|
||||
break;
|
||||
case BoneRule.EyesMapped:
|
||||
bool leftEyeMapped = humanoidToUserBoneMappings.ContainsKey("LeftEye");
|
||||
bool rightEyeMapped = humanoidToUserBoneMappings.ContainsKey("RightEye");
|
||||
if (!leftEyeMapped || !rightEyeMapped) {
|
||||
if (leftEyeMapped && !rightEyeMapped) {
|
||||
failedBoneRules.Add(boneRule, "There is no RightEye bone mapped in Humanoid " +
|
||||
"for the selected avatar.");
|
||||
} else if (!leftEyeMapped && rightEyeMapped) {
|
||||
failedBoneRules.Add(boneRule, "There is no LeftEye bone mapped in Humanoid " +
|
||||
"for the selected avatar.");
|
||||
} else {
|
||||
failedBoneRules.Add(boneRule, "There is no LeftEye or RightEye bone mapped in Humanoid " +
|
||||
"for the selected avatar.");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case BoneRule.HipsNotOnGround:
|
||||
// ensure the absolute Y position for the bone mapped to Hips (if its mapped) is at least HIPS_GROUND_MIN_Y
|
||||
if (!string.IsNullOrEmpty(hipsUserBone)) {
|
||||
UserBoneInformation hipsBoneInfo = userBoneInfos[hipsUserBone];
|
||||
hipsPosition = hipsBoneInfo.position;
|
||||
if (hipsPosition.y < HIPS_GROUND_MIN_Y) {
|
||||
failedBoneRules.Add(boneRule, "The bone mapped to Hips in Humanoid (" + hipsUserBone +
|
||||
") should not be at ground level.");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case BoneRule.HipsSpineChestNotCoincident:
|
||||
// ensure the bones mapped to Hips, Spine, and Chest are all not in the same position,
|
||||
// check Hips to Spine and Spine to Chest lengths are within HIPS_SPINE_CHEST_MIN_SEPARATION
|
||||
if (!string.IsNullOrEmpty(spineUserBone) && !string.IsNullOrEmpty(chestUserBone) &&
|
||||
!string.IsNullOrEmpty(hipsUserBone)) {
|
||||
UserBoneInformation spineBoneInfo = userBoneInfos[spineUserBone];
|
||||
UserBoneInformation chestBoneInfo = userBoneInfos[chestUserBone];
|
||||
Vector3 hipsToSpine = hipsPosition - spineBoneInfo.position;
|
||||
Vector3 spineToChest = spineBoneInfo.position - chestBoneInfo.position;
|
||||
if (hipsToSpine.magnitude < HIPS_SPINE_CHEST_MIN_SEPARATION &&
|
||||
spineToChest.magnitude < HIPS_SPINE_CHEST_MIN_SEPARATION) {
|
||||
failedBoneRules.Add(boneRule, "The bone mapped to Hips in Humanoid (" + hipsUserBone +
|
||||
"), the bone mapped to Spine in Humanoid (" + spineUserBone +
|
||||
"), and the bone mapped to Chest in Humanoid (" + chestUserBone +
|
||||
") should not be coincidental.");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case BoneRule.TotalBoneCountUnderLimit:
|
||||
int userBoneCount = userBoneInfos.Count;
|
||||
if (userBoneCount > MAXIMUM_USER_BONE_COUNT) {
|
||||
failedBoneRules.Add(boneRule, "The total number of bones in the avatar (" + userBoneCount +
|
||||
") exceeds the maximum bone limit (" + MAXIMUM_USER_BONE_COUNT + ").");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static string CheckHumanBoneMappingRule(BoneRule boneRule, string humanBoneName) {
|
||||
string userBoneName = "";
|
||||
// bone rule fails if bone is not mapped in Humanoid
|
||||
if (!humanoidToUserBoneMappings.TryGetValue(humanBoneName, out userBoneName)) {
|
||||
failedBoneRules.Add(boneRule, "There is no " + humanBoneName + " bone mapped in Humanoid for the selected avatar.");
|
||||
}
|
||||
return userBoneName;
|
||||
}
|
||||
|
||||
static void CheckUserBoneDescendantOfHumanRule(BoneRule boneRule, string userBoneName, string descendantOfHumanName) {
|
||||
if (string.IsNullOrEmpty(userBoneName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
string descendantOfUserBoneName = "";
|
||||
if (!humanoidToUserBoneMappings.TryGetValue(descendantOfHumanName, out descendantOfUserBoneName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
string userBone = userBoneName;
|
||||
string ancestorUserBone = "";
|
||||
UserBoneInformation userBoneInfo = new UserBoneInformation();
|
||||
// iterate upward from user bone through user bone info parent names until root
|
||||
// is reached or the ancestor bone name matches the target descendant of name
|
||||
while (ancestorUserBone != "root") {
|
||||
if (userBoneInfos.TryGetValue(userBone, out userBoneInfo)) {
|
||||
ancestorUserBone = userBoneInfo.parentName;
|
||||
if (ancestorUserBone == descendantOfUserBoneName) {
|
||||
return;
|
||||
}
|
||||
userBone = ancestorUserBone;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// bone rule fails if no ancestor of given user bone matched the descendant of name (no early return)
|
||||
failedBoneRules.Add(boneRule, "The bone mapped to " + userBoneInfo.humanName + " in Humanoid (" + userBoneName +
|
||||
") is not a child of the bone mapped to " + descendantOfHumanName + " in Humanoid (" +
|
||||
descendantOfUserBoneName + ").");
|
||||
}
|
||||
|
||||
static void CheckAsymmetricalMappingRule(BoneRule boneRule, string[] mappingSuffixes, string appendage) {
|
||||
int leftCount = 0;
|
||||
int rightCount = 0;
|
||||
// add Left/Right to each mapping suffix to make Humanoid mapping names,
|
||||
// and count the number of bones mapped in Humanoid on each side
|
||||
foreach (string mappingSuffix in mappingSuffixes) {
|
||||
string leftMapping = "Left" + mappingSuffix;
|
||||
string rightMapping = "Right" + mappingSuffix;
|
||||
if (humanoidToUserBoneMappings.ContainsKey(leftMapping)) {
|
||||
++leftCount;
|
||||
}
|
||||
if (humanoidToUserBoneMappings.ContainsKey(rightMapping)) {
|
||||
++rightCount;
|
||||
}
|
||||
}
|
||||
// bone rule fails if number of left appendage mappings doesn't match number of right appendage mappings
|
||||
if (leftCount != rightCount) {
|
||||
failedBoneRules.Add(boneRule, "The number of bones mapped in Humanoid for the left " + appendage + " (" +
|
||||
leftCount + ") does not match the number of bones mapped in Humanoid for the right " +
|
||||
appendage + " (" + rightCount + ").");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ExportProjectWindow : EditorWindow {
|
||||
const int MIN_WIDTH = 450;
|
||||
const int MIN_HEIGHT = 250;
|
||||
const int WINDOW_WIDTH = 500;
|
||||
const int WINDOW_HEIGHT = 460;
|
||||
const int BUTTON_FONT_SIZE = 16;
|
||||
const int LABEL_FONT_SIZE = 16;
|
||||
const int TEXT_FIELD_FONT_SIZE = 14;
|
||||
const int TEXT_FIELD_HEIGHT = 20;
|
||||
const int ERROR_FONT_SIZE = 12;
|
||||
const int WARNING_SCROLL_HEIGHT = 170;
|
||||
const string EMPTY_ERROR_TEXT = "None\n";
|
||||
|
||||
string projectName = "";
|
||||
string projectLocation = "";
|
||||
string projectDirectory = "";
|
||||
string errorLabel = "\n";
|
||||
string errorText = EMPTY_ERROR_TEXT;
|
||||
string warningText = "";
|
||||
Vector2 warningScrollPosition = new Vector2(0, 0);
|
||||
|
||||
public delegate void OnCloseDelegate(string projectDirectory, string projectName);
|
||||
public delegate void OnCloseDelegate(string projectDirectory, string projectName, string warnings);
|
||||
OnCloseDelegate onCloseCallback;
|
||||
|
||||
public void Init(string initialPath, OnCloseDelegate closeCallback) {
|
||||
minSize = new Vector2(MIN_WIDTH, MIN_HEIGHT);
|
||||
public void Init(string initialPath, string warnings, OnCloseDelegate closeCallback) {
|
||||
minSize = new Vector2(WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||
maxSize = new Vector2(WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||
titleContent.text = "Export New Avatar";
|
||||
projectLocation = initialPath;
|
||||
warningText = warnings;
|
||||
onCloseCallback = closeCallback;
|
||||
ShowUtility();
|
||||
}
|
||||
|
@ -513,6 +910,9 @@ class ExportProjectWindow : EditorWindow {
|
|||
GUIStyle errorStyle = new GUIStyle(GUI.skin.label);
|
||||
errorStyle.fontSize = ERROR_FONT_SIZE;
|
||||
errorStyle.normal.textColor = Color.red;
|
||||
errorStyle.wordWrap = true;
|
||||
GUIStyle warningStyle = new GUIStyle(errorStyle);
|
||||
warningStyle.normal.textColor = Color.yellow;
|
||||
|
||||
GUILayout.Space(10);
|
||||
|
||||
|
@ -534,10 +934,20 @@ class ExportProjectWindow : EditorWindow {
|
|||
}
|
||||
}
|
||||
|
||||
// Red error label text to display any issues under text fields and Browse button
|
||||
GUILayout.Label(errorLabel, errorStyle);
|
||||
// Red error label text to display any file-related errors
|
||||
GUILayout.Label("Error:", errorStyle);
|
||||
GUILayout.Label(errorText, errorStyle);
|
||||
|
||||
GUILayout.Space(20);
|
||||
GUILayout.Space(10);
|
||||
|
||||
// Yellow warning label text to display scrollable list of any bone-related warnings
|
||||
GUILayout.Label("Warnings:", warningStyle);
|
||||
warningScrollPosition = GUILayout.BeginScrollView(warningScrollPosition, GUILayout.Width(WINDOW_WIDTH),
|
||||
GUILayout.Height(WARNING_SCROLL_HEIGHT));
|
||||
GUILayout.Label(warningText, warningStyle);
|
||||
GUILayout.EndScrollView();
|
||||
|
||||
GUILayout.Space(10);
|
||||
|
||||
// Export button which will verify project folder can actually be created
|
||||
// before closing popup window and calling back to initiate the export
|
||||
|
@ -546,7 +956,7 @@ class ExportProjectWindow : EditorWindow {
|
|||
export = true;
|
||||
if (!CheckForErrors(true)) {
|
||||
Close();
|
||||
onCloseCallback(projectDirectory, projectName);
|
||||
onCloseCallback(projectDirectory, projectName, warningText);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -562,12 +972,12 @@ class ExportProjectWindow : EditorWindow {
|
|||
}
|
||||
|
||||
bool CheckForErrors(bool exporting) {
|
||||
errorLabel = "\n"; // default to no error
|
||||
errorText = EMPTY_ERROR_TEXT; // default to None if no errors found
|
||||
projectDirectory = projectLocation + "\\" + projectName + "\\";
|
||||
if (projectName.Length > 0) {
|
||||
// new project must have a unique folder name since the folder will be created for it
|
||||
if (Directory.Exists(projectDirectory)) {
|
||||
errorLabel = "A folder with the name " + projectName +
|
||||
errorText = "A folder with the name " + projectName +
|
||||
" already exists at that location.\nPlease choose a different project name or location.";
|
||||
return true;
|
||||
}
|
||||
|
@ -575,7 +985,7 @@ class ExportProjectWindow : EditorWindow {
|
|||
if (projectLocation.Length > 0) {
|
||||
// before clicking Export we can verify that the project location at least starts with a drive
|
||||
if (!Char.IsLetter(projectLocation[0]) || projectLocation.Length == 1 || projectLocation[1] != ':') {
|
||||
errorLabel = "Project location is invalid. Please choose a different project location.\n";
|
||||
errorText = "Project location is invalid. Please choose a different project location.\n";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -583,16 +993,16 @@ class ExportProjectWindow : EditorWindow {
|
|||
// when exporting, project name and location must both be defined, and project location must
|
||||
// be valid and accessible (we attempt to create the project folder at this time to verify this)
|
||||
if (projectName.Length == 0) {
|
||||
errorLabel = "Please define a project name.\n";
|
||||
errorText = "Please define a project name.\n";
|
||||
return true;
|
||||
} else if (projectLocation.Length == 0) {
|
||||
errorLabel = "Please define a project location.\n";
|
||||
errorText = "Please define a project location.\n";
|
||||
return true;
|
||||
} else {
|
||||
try {
|
||||
Directory.CreateDirectory(projectDirectory);
|
||||
} catch {
|
||||
errorLabel = "Project location is invalid. Please choose a different project location.\n";
|
||||
errorText = "Project location is invalid. Please choose a different project location.\n";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
High Fidelity, Inc.
|
||||
Avatar Exporter
|
||||
Version 0.1
|
||||
|
||||
Note: It is recommended to use Unity versions between 2017.4.17f1 and 2018.2.12f1 for this Avatar Exporter.
|
||||
|
||||
To create a new avatar project:
|
||||
|
|
Binary file not shown.
Loading…
Reference in a new issue