mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Merge remote-tracking branch 'upstream/master' into android_goto_splash
This commit is contained in:
commit
c15ef56d90
202 changed files with 6466 additions and 1826 deletions
|
@ -19,6 +19,8 @@ To produce an executable installer on Windows, the following are required:
|
|||
- [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)
|
||||
|
||||
Run the `package` target to create an executable installer using the Nullsoft Scriptable Install System.
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ struct AssetMeta {
|
|||
AssetMeta() {
|
||||
}
|
||||
|
||||
BakeVersion bakeVersion;
|
||||
BakeVersion bakeVersion { INITIAL_BAKE_VERSION };
|
||||
bool failedLastBake { false };
|
||||
QString lastBakeErrors;
|
||||
};
|
||||
|
|
|
@ -115,11 +115,7 @@ public:
|
|||
uint64_t getLastOtherAvatarEncodeTime(QUuid otherAvatar) const;
|
||||
void setLastOtherAvatarEncodeTime(const QUuid& otherAvatar, uint64_t time);
|
||||
|
||||
QVector<JointData>& getLastOtherAvatarSentJoints(QUuid otherAvatar) {
|
||||
auto& lastOtherAvatarSentJoints = _lastOtherAvatarSentJoints[otherAvatar];
|
||||
lastOtherAvatarSentJoints.resize(_avatar->getJointCount());
|
||||
return lastOtherAvatarSentJoints;
|
||||
}
|
||||
QVector<JointData>& getLastOtherAvatarSentJoints(QUuid otherAvatar) { return _lastOtherAvatarSentJoints[otherAvatar]; }
|
||||
|
||||
void queuePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer node);
|
||||
int processPackets(); // returns number of packets processed
|
||||
|
|
|
@ -381,6 +381,9 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
bool includeThisAvatar = true;
|
||||
auto lastEncodeForOther = nodeData->getLastOtherAvatarEncodeTime(otherNode->getUUID());
|
||||
QVector<JointData>& lastSentJointsForOther = nodeData->getLastOtherAvatarSentJoints(otherNode->getUUID());
|
||||
|
||||
lastSentJointsForOther.resize(otherAvatar->getJointCount());
|
||||
|
||||
bool distanceAdjust = true;
|
||||
glm::vec3 viewerPosition = myPosition;
|
||||
AvatarDataPacket::HasFlags hasFlagsOut; // the result of the toByteArray
|
||||
|
|
|
@ -380,10 +380,15 @@ void EntityServer::trackSend(const QUuid& dataID, quint64 dataLastEdited, const
|
|||
}
|
||||
|
||||
void EntityServer::trackViewerGone(const QUuid& sessionID) {
|
||||
QWriteLocker locker(&_viewerSendingStatsLock);
|
||||
_viewerSendingStats.remove(sessionID);
|
||||
{
|
||||
QWriteLocker locker(&_viewerSendingStatsLock);
|
||||
_viewerSendingStats.remove(sessionID);
|
||||
}
|
||||
|
||||
if (_entitySimulation) {
|
||||
_entitySimulation->clearOwnership(sessionID);
|
||||
_tree->withReadLock([&] {
|
||||
_entitySimulation->clearOwnership(sessionID);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
3
cmake/externals/quazip/CMakeLists.txt
vendored
3
cmake/externals/quazip/CMakeLists.txt
vendored
|
@ -41,6 +41,9 @@ if (APPLE)
|
|||
elseif (WIN32)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/quazip5.lib CACHE FILEPATH "Location of QuaZip release library")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/quazip5d.lib CACHE FILEPATH "Location of QuaZip release library")
|
||||
elseif (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libquazip5.so CACHE FILEPATH "Location of QuaZip release library")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/libquazip5d.so CACHE FILEPATH "Location of QuaZip release library")
|
||||
else ()
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libquazip5.so CACHE FILEPATH "Location of QuaZip release library")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/libquazip5.so CACHE FILEPATH "Location of QuaZip release library")
|
||||
|
|
|
@ -4,8 +4,8 @@ set(EXTERNAL_NAME serverless-content)
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC66-v4.zip
|
||||
URL_MD5 d4f42f630986c83427ff39e1fe9908c6
|
||||
URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC67-v4.zip
|
||||
URL_MD5 ba32aed18bfeaac4ccaf5ebb8ea3e804
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
|
|
|
@ -87,6 +87,10 @@
|
|||
;--------------------------------
|
||||
;--------------------------------
|
||||
;General
|
||||
|
||||
; hide install details since we show an image slideshow in their place
|
||||
ShowInstDetails nevershow
|
||||
|
||||
; leverage the UAC NSIS plugin to promote uninstaller to elevated privileges
|
||||
!include UAC.nsh
|
||||
|
||||
|
@ -446,6 +450,7 @@ SectionEnd
|
|||
Page custom PostInstallOptionsPage ReadPostInstallOptions
|
||||
|
||||
!define MUI_PAGE_CUSTOMFUNCTION_PRE PageInstallFilesPre
|
||||
!define MUI_PAGE_CUSTOMFUNCTION_SHOW StartInstallSlideshow
|
||||
!insertmacro MUI_PAGE_INSTFILES
|
||||
|
||||
!insertmacro MUI_UNPAGE_CONFIRM
|
||||
|
@ -544,11 +549,33 @@ Var Express
|
|||
${EndIf}
|
||||
!macroend
|
||||
|
||||
!macro DownloadSlideshowImages
|
||||
InitPluginsDir
|
||||
|
||||
Push $0
|
||||
|
||||
; figure out where to download installer slideshow images from
|
||||
StrCpy $0 "http://cdn.highfidelity.com/installer/slideshow"
|
||||
|
||||
${If} $CampaignName == ""
|
||||
StrCpy $0 "$0/default"
|
||||
${Else}
|
||||
StrCpy $0 "$0/$CampaignName"
|
||||
${EndIf}
|
||||
|
||||
NSISdl::download_quiet $0/1.jpg "$PLUGINSDIR\1.jpg"
|
||||
NSISdl::download_quiet $0/2.jpg "$PLUGINSDIR\2.jpg"
|
||||
NSISdl::download_quiet $0/3.jpg "$PLUGINSDIR\3.jpg"
|
||||
|
||||
Pop $0
|
||||
!macroend
|
||||
|
||||
Function OnUserAbort
|
||||
!insertmacro GoogleAnalytics "Installer" "Abort" "User Abort" ""
|
||||
FunctionEnd
|
||||
Function PageWelcomePre
|
||||
!insertmacro GoogleAnalytics "Installer" "Welcome" "" ""
|
||||
!insertmacro DownloadSlideshowImages
|
||||
FunctionEnd
|
||||
Function PageLicensePre
|
||||
!insertmacro GoogleAnalytics "Installer" "License" "" ""
|
||||
|
@ -640,6 +667,56 @@ Function ChangeCustomLabel
|
|||
Pop $R1
|
||||
FunctionEnd
|
||||
|
||||
!macro AddImageToSlideshowFile ImageFilename
|
||||
${If} ${FileExists} "$PLUGINSDIR\${ImageFilename}.jpg"
|
||||
FileWrite $0 "= ${ImageFilename}.jpg,500,5000,$\"$\"$\r$\n"
|
||||
StrCpy $1 "1"
|
||||
${EndIf}
|
||||
!macroend
|
||||
|
||||
Function StartInstallSlideshow
|
||||
; create a slideshow file based on what files we have available
|
||||
|
||||
; stash $0 and $1
|
||||
Push $0
|
||||
Push $1
|
||||
|
||||
; start $1 as 0, indicating we have no images present
|
||||
StrCpy $1 "0"
|
||||
|
||||
FileOpen $0 "$PLUGINSDIR\slideshow.dat" w
|
||||
|
||||
; write the language value to the slideshow file for english
|
||||
FileWrite $0 "[1033]$\r$\n"
|
||||
|
||||
; for each of 1.jpg, 2.jpg, 3.jpg
|
||||
; if the image is present add it to the dat file and set our flag
|
||||
; to show we found at least one image
|
||||
!insertmacro AddImageToSlideshowFile "1"
|
||||
!insertmacro AddImageToSlideshowFile "2"
|
||||
!insertmacro AddImageToSlideshowFile "3"
|
||||
|
||||
FileClose $0
|
||||
|
||||
; NOTE: something inside of nsisSlideshow::show isn't keeping the stack clean
|
||||
; so we need to push things back BEFORE we call it
|
||||
|
||||
${If} $1 == "1"
|
||||
Pop $1
|
||||
Pop $0
|
||||
|
||||
; show the slideshow using the created data file
|
||||
nsisSlideshow::show /NOUNLOAD "/auto=$PLUGINSDIR\slideshow.dat"
|
||||
${Else}
|
||||
Pop $1
|
||||
Pop $0
|
||||
|
||||
; show the install details because we didn't end up with slideshow images to show
|
||||
SetDetailsView show
|
||||
${EndIf}
|
||||
|
||||
FunctionEnd
|
||||
|
||||
Function PostInstallOptionsPage
|
||||
!insertmacro MaybeSkipPage
|
||||
!insertmacro GoogleAnalytics "Installer" "Post Install Options" "" ""
|
||||
|
@ -928,10 +1005,28 @@ Function HandlePostInstallOptions
|
|||
${EndIf}
|
||||
FunctionEnd
|
||||
|
||||
Function OptionallyDownloadCampaignServerless
|
||||
${If} $CampaignName != ""
|
||||
InitPluginsDir
|
||||
|
||||
NSISdl::download_quiet http://cdn.highfidelity.com/installer/serverless/$CampaignName.zip $PLUGINSDIR\$CampaignName.zip
|
||||
|
||||
${If} ${FileExists} $PLUGINSDIR\$CampaignName.zip
|
||||
; replace the installed serverless content with the campaign content
|
||||
|
||||
RMDir /r "$INSTDIR\resources\serverless"
|
||||
CreateDirectory "$INSTDIR\resources\serverless"
|
||||
nsisunz::Unzip "$PLUGINSDIR\$CampaignName.zip" "$INSTDIR\resources\serverless"
|
||||
${EndIf}
|
||||
|
||||
${Endif}
|
||||
FunctionEnd
|
||||
|
||||
;--------------------------------
|
||||
;Installer Sections
|
||||
|
||||
Section "-Core installation"
|
||||
|
||||
;The following delete blocks are temporary and can be removed once users who had the initial installer have updated
|
||||
|
||||
;Delete any server-console files installed before it was placed in sub-folder
|
||||
|
@ -983,11 +1078,13 @@ Section "-Core installation"
|
|||
WriteRegStr HKLM "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "" $INSTDIR
|
||||
|
||||
;Write some information about this install to the installation folder
|
||||
Push $0
|
||||
FileOpen $0 "$INSTDIR\installer.ini" w
|
||||
FileWrite $0 "type=@INSTALLER_TYPE@$\r$\n"
|
||||
FileWrite $0 "campaign=$CampaignName$\r$\n"
|
||||
FileWrite $0 "exepath=$EXEPATH$\r$\n"
|
||||
FileClose $0
|
||||
Pop $0
|
||||
|
||||
;Package the signed uninstaller produced by the inner loop
|
||||
!ifndef INNER
|
||||
|
@ -1078,6 +1175,9 @@ Section "-Core installation"
|
|||
|
||||
@CPACK_NSIS_EXTRA_INSTALL_COMMANDS@
|
||||
|
||||
; see if we have a campaign that we might need to grab special content for
|
||||
Call OptionallyDownloadCampaignServerless
|
||||
|
||||
; Handle whichever post install options were set
|
||||
Call HandlePostInstallOptions
|
||||
|
||||
|
|
|
@ -617,7 +617,7 @@ bool DomainServer::isPacketVerified(const udt::Packet& packet) {
|
|||
}
|
||||
} else {
|
||||
HIFI_FDEBUG("Packet of type" << headerType
|
||||
<< "received from unknown node with UUID" << uuidStringWithoutCurlyBraces(sourceNode->getUUID()));
|
||||
<< "received from unknown node with Local ID" << localSourceID);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ Slider {
|
|||
|
||||
property alias minimumValue: slider.from
|
||||
property alias maximumValue: slider.to
|
||||
property bool tickmarksEnabled: false
|
||||
|
||||
height: hifi.fontSizes.textFieldInput + 14 // Match height of TextField control.
|
||||
y: sliderLabel.visible ? sliderLabel.height + sliderLabel.anchors.bottomMargin : 0
|
||||
|
|
|
@ -20,6 +20,7 @@ SpinBox {
|
|||
property int colorScheme: hifi.colorSchemes.light
|
||||
readonly property bool isLightColorScheme: colorScheme === hifi.colorSchemes.light
|
||||
property string label: ""
|
||||
property string suffix: ""
|
||||
property string labelInside: ""
|
||||
property color colorLabelInside: hifi.colors.white
|
||||
property real controlHeight: height + (spinBoxLabel.visible ? spinBoxLabel.height + spinBoxLabel.anchors.bottomMargin : 0)
|
||||
|
@ -34,8 +35,11 @@ SpinBox {
|
|||
property real realTo: 100.0
|
||||
property real realStepSize: 1.0
|
||||
|
||||
signal editingFinished()
|
||||
|
||||
implicitHeight: height
|
||||
implicitWidth: width
|
||||
editable: true
|
||||
|
||||
padding: 0
|
||||
leftPadding: 0
|
||||
|
@ -68,16 +72,16 @@ SpinBox {
|
|||
}
|
||||
|
||||
validator: DoubleValidator {
|
||||
bottom: Math.min(spinBox.from, spinBox.to)*spinBox.factor
|
||||
top: Math.max(spinBox.from, spinBox.to)*spinBox.factor
|
||||
bottom: Math.min(spinBox.from, spinBox.to)
|
||||
top: Math.max(spinBox.from, spinBox.to)
|
||||
}
|
||||
|
||||
textFromValue: function(value, locale) {
|
||||
return parseFloat(value*1.0/factor).toFixed(decimals);
|
||||
return parseFloat(value/factor).toFixed(decimals);
|
||||
}
|
||||
|
||||
valueFromText: function(text, locale) {
|
||||
return Number.fromLocaleString(locale, text);
|
||||
return Number.fromLocaleString(locale, text)*factor;
|
||||
}
|
||||
|
||||
|
||||
|
@ -88,12 +92,14 @@ SpinBox {
|
|||
: (spinBox.activeFocus ? hifi.colors.white : hifi.colors.lightGrayText)
|
||||
selectedTextColor: hifi.colors.black
|
||||
selectionColor: hifi.colors.primaryHighlight
|
||||
text: spinBox.textFromValue(spinBox.value, spinBox.locale)
|
||||
text: spinBox.textFromValue(spinBox.value, spinBox.locale) + suffix
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
leftPadding: spinBoxLabelInside.visible ? 30 : hifi.dimensions.textPadding
|
||||
//rightPadding: hifi.dimensions.spinnerSize
|
||||
width: spinBox.width - hifi.dimensions.spinnerSize
|
||||
onEditingFinished: spinBox.editingFinished()
|
||||
}
|
||||
|
||||
up.indicator: Item {
|
||||
x: spinBox.width - implicitWidth - 5
|
||||
y: 1
|
||||
|
|
|
@ -21,6 +21,7 @@ Item {
|
|||
signal newViewRequestedCallback(var request)
|
||||
signal loadingChangedCallback(var loadRequest)
|
||||
|
||||
|
||||
width: parent.width
|
||||
|
||||
property bool interactive: false
|
||||
|
@ -29,6 +30,10 @@ Item {
|
|||
id: hifi
|
||||
}
|
||||
|
||||
function stop() {
|
||||
webViewCore.stop();
|
||||
}
|
||||
|
||||
function unfocus() {
|
||||
webViewCore.runJavaScript("if (document.activeElement) document.activeElement.blur();", function(result) {
|
||||
console.log('unfocus completed: ', result);
|
||||
|
|
|
@ -21,6 +21,10 @@ Item {
|
|||
property bool passwordField: false
|
||||
property alias flickable: webroot.interactive
|
||||
|
||||
function stop() {
|
||||
webroot.stop();
|
||||
}
|
||||
|
||||
// FIXME - Keyboard HMD only: Make Interface either set keyboardRaised property directly in OffscreenQmlSurface
|
||||
// or provide HMDinfo object to QML in RenderableWebEntityItem and do the following.
|
||||
/*
|
||||
|
|
|
@ -254,7 +254,7 @@ ModalWindow {
|
|||
text: root.warning;
|
||||
wrapMode: Text.WordWrap;
|
||||
font.italic: true;
|
||||
maximumLineCount: 2;
|
||||
maximumLineCount: 3;
|
||||
}
|
||||
|
||||
HiFiGlyphs {
|
||||
|
|
|
@ -254,7 +254,7 @@ ModalWindow {
|
|||
text: root.warning;
|
||||
wrapMode: Text.WordWrap;
|
||||
font.italic: true;
|
||||
maximumLineCount: 2;
|
||||
maximumLineCount: 3;
|
||||
}
|
||||
|
||||
HiFiGlyphs {
|
||||
|
|
|
@ -282,7 +282,7 @@ TabletModalWindow {
|
|||
text: root.warning;
|
||||
wrapMode: Text.WordWrap;
|
||||
font.italic: true;
|
||||
maximumLineCount: 2;
|
||||
maximumLineCount: 3;
|
||||
}
|
||||
|
||||
HiFiGlyphs {
|
||||
|
|
|
@ -18,11 +18,11 @@ Preference {
|
|||
height: control.height + hifi.dimensions.controlInterlineHeight
|
||||
|
||||
Component.onCompleted: {
|
||||
spinner.value = preference.value;
|
||||
spinner.realValue = preference.value;
|
||||
}
|
||||
|
||||
function save() {
|
||||
preference.value = spinner.value;
|
||||
preference.value = spinner.realValue;
|
||||
preference.save();
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ Preference {
|
|||
|
||||
Component.onCompleted: {
|
||||
slider.value = preference.value;
|
||||
spinner.value = preference.value;
|
||||
spinner.realValue = preference.value;
|
||||
}
|
||||
|
||||
function save() {
|
||||
|
@ -60,7 +60,7 @@ Preference {
|
|||
maximumValue: MyAvatar.getDomainMaxScale()
|
||||
stepSize: preference.step
|
||||
onValueChanged: {
|
||||
spinner.value = value
|
||||
spinner.realValue = value
|
||||
}
|
||||
anchors {
|
||||
right: spinner.left
|
||||
|
@ -73,12 +73,12 @@ Preference {
|
|||
SpinBox {
|
||||
id: spinner
|
||||
decimals: preference.decimals
|
||||
value: preference.value
|
||||
realValue: preference.value
|
||||
minimumValue: MyAvatar.getDomainMinScale()
|
||||
maximumValue: MyAvatar.getDomainMaxScale()
|
||||
width: 100
|
||||
onValueChanged: {
|
||||
slider.value = value;
|
||||
slider.value = realValue;
|
||||
}
|
||||
anchors {
|
||||
right: button.left
|
||||
|
@ -92,10 +92,10 @@ Preference {
|
|||
id: button
|
||||
onClicked: {
|
||||
if (spinner.maximumValue >= 1) {
|
||||
spinner.value = 1
|
||||
spinner.realValue = 1
|
||||
slider.value = 1
|
||||
} else {
|
||||
spinner.value = spinner.maximumValue
|
||||
spinner.realValue = spinner.maximumValue
|
||||
slider.value = spinner.maximumValue
|
||||
}
|
||||
}
|
||||
|
@ -108,4 +108,4 @@ Preference {
|
|||
colorScheme: hifi.colorSchemes.dark
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -258,7 +258,9 @@ Item {
|
|||
anchors.topMargin: 26;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 20;
|
||||
width: paintedWidth;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 20;
|
||||
elide: Text.ElideRight;
|
||||
height: 30;
|
||||
// Text size
|
||||
size: 22;
|
||||
|
@ -844,7 +846,7 @@ Item {
|
|||
property string selectedRecipientUserName;
|
||||
property string selectedRecipientProfilePic;
|
||||
|
||||
visible: root.currentActiveView === "sendAssetStep";
|
||||
visible: root.currentActiveView === "sendAssetStep" || paymentSuccess.visible || paymentFailure.visible;
|
||||
anchors.fill: parent;
|
||||
anchors.topMargin: root.parentAppTitleBarHeight;
|
||||
|
||||
|
@ -856,7 +858,9 @@ Item {
|
|||
anchors.topMargin: 26;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 20;
|
||||
width: paintedWidth;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 20;
|
||||
elide: Text.ElideRight;
|
||||
height: 30;
|
||||
// Text size
|
||||
size: 22;
|
||||
|
@ -907,7 +911,7 @@ Item {
|
|||
// "CHANGE" button
|
||||
HifiControlsUit.Button {
|
||||
id: changeButton;
|
||||
color: root.assetName === "" ? hifi.buttons.none : hifi.buttons.noneBorderlessGray;
|
||||
color: root.assetName === "" ? hifi.buttons.none : hifi.buttons.white;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.right: parent.right;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
|
@ -1238,7 +1242,7 @@ Item {
|
|||
// Sending Asset Overlay START
|
||||
Rectangle {
|
||||
id: sendingAssetOverlay;
|
||||
z: 998;
|
||||
z: 999;
|
||||
|
||||
visible: root.isCurrentlySendingAsset;
|
||||
anchors.fill: parent;
|
||||
|
@ -1281,26 +1285,43 @@ Item {
|
|||
// Payment Success BEGIN
|
||||
Rectangle {
|
||||
id: paymentSuccess;
|
||||
z: 998;
|
||||
|
||||
visible: root.currentActiveView === "paymentSuccess";
|
||||
anchors.fill: parent;
|
||||
color: Qt.rgba(0.0, 0.0, 0.0, 0.8);
|
||||
|
||||
// This object is always used in a popup or full-screen Wallet section.
|
||||
// This MouseArea is used to prevent a user from being
|
||||
// able to click on a button/mouseArea underneath the popup/section.
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
propagateComposedEvents: false;
|
||||
hoverEnabled: true;
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.centerIn: parent;
|
||||
width: parent.width - 30;
|
||||
height: parent.height - 30;
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: root.assetName === "" ? 15 : 150;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: root.assetName === "" ? 15 : 50;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: root.assetName === "" ? 15 : 50;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: root.assetName === "" ? 15 : 240;
|
||||
color: "#FFFFFF";
|
||||
|
||||
RalewaySemiBold {
|
||||
id: paymentSentText;
|
||||
text: root.assetName === "" ? "Payment Sent" : '"' + root.assetName + '"';
|
||||
text: root.assetName === "" ? "Payment Sent" : "Gift Sent";
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 26;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 20;
|
||||
width: paintedWidth;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 20;
|
||||
elide: Text.ElideRight;
|
||||
height: 30;
|
||||
// Text size
|
||||
size: 22;
|
||||
|
@ -1310,6 +1331,7 @@ Item {
|
|||
|
||||
HiFiGlyphs {
|
||||
id: closeGlyphButton_paymentSuccess;
|
||||
visible: root.assetName === "";
|
||||
text: hifi.glyphs.close;
|
||||
color: hifi.colors.lightGrayText;
|
||||
size: 26;
|
||||
|
@ -1375,6 +1397,49 @@ Item {
|
|||
isDisplayingNearby: sendAssetStep.referrer === "nearby";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Item {
|
||||
id: giftContainer_paymentSuccess;
|
||||
visible: root.assetName !== "";
|
||||
anchors.top: sendToContainer_paymentSuccess.bottom;
|
||||
anchors.topMargin: 8;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 20;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 20;
|
||||
height: 30;
|
||||
|
||||
RalewaySemiBold {
|
||||
id: gift_paymentSuccess;
|
||||
text: "Gift:";
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.bottom: parent.bottom;
|
||||
width: 90;
|
||||
// Text size
|
||||
size: 18;
|
||||
// Style
|
||||
color: hifi.colors.baseGray;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
RalewaySemiBold {
|
||||
text: root.assetName;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: gift_paymentSuccess.right;
|
||||
anchors.right: parent.right;
|
||||
height: parent.height;
|
||||
// Text size
|
||||
size: 18;
|
||||
// Style
|
||||
elide: Text.ElideRight;
|
||||
color: hifi.colors.baseGray;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: amountContainer_paymentSuccess;
|
||||
|
@ -1433,6 +1498,7 @@ Item {
|
|||
|
||||
RalewaySemiBold {
|
||||
id: optionalMessage_paymentSuccess;
|
||||
visible: root.assetName === "";
|
||||
text: optionalMessage.text;
|
||||
// Anchors
|
||||
anchors.top: amountContainer_paymentSuccess.visible ? amountContainer_paymentSuccess.bottom : sendToContainer_paymentSuccess.bottom;
|
||||
|
@ -1457,7 +1523,7 @@ Item {
|
|||
colorScheme: root.assetName === "" ? hifi.colorSchemes.dark : hifi.colorSchemes.light;
|
||||
anchors.horizontalCenter: parent.horizontalCenter;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 80;
|
||||
anchors.bottomMargin: root.assetName === "" ? 80 : 30;
|
||||
height: 50;
|
||||
width: 120;
|
||||
text: "Close";
|
||||
|
@ -1476,26 +1542,43 @@ Item {
|
|||
// Payment Failure BEGIN
|
||||
Rectangle {
|
||||
id: paymentFailure;
|
||||
z: 998;
|
||||
|
||||
visible: root.currentActiveView === "paymentFailure";
|
||||
anchors.fill: parent;
|
||||
color: Qt.rgba(0.0, 0.0, 0.0, 0.8);
|
||||
|
||||
// This object is always used in a popup or full-screen Wallet section.
|
||||
// This MouseArea is used to prevent a user from being
|
||||
// able to click on a button/mouseArea underneath the popup/section.
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
propagateComposedEvents: false;
|
||||
hoverEnabled: true;
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.centerIn: parent;
|
||||
width: parent.width - 30;
|
||||
height: parent.height - 30;
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: root.assetName === "" ? 15 : 150;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: root.assetName === "" ? 15 : 50;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: root.assetName === "" ? 15 : 50;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: root.assetName === "" ? 15 : 300;
|
||||
color: "#FFFFFF";
|
||||
|
||||
RalewaySemiBold {
|
||||
id: paymentFailureText;
|
||||
text: root.assetName === "" ? "Payment Failed" : '"' + root.assetName + '"';
|
||||
text: root.assetName === "" ? "Payment Failed" : "Failed";
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 26;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 20;
|
||||
width: paintedWidth;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 20;
|
||||
elide: Text.ElideRight;
|
||||
height: 30;
|
||||
// Text size
|
||||
size: 22;
|
||||
|
@ -1505,6 +1588,7 @@ Item {
|
|||
|
||||
HiFiGlyphs {
|
||||
id: closeGlyphButton_paymentFailure;
|
||||
visible: root.assetName === "";
|
||||
text: hifi.glyphs.close;
|
||||
color: hifi.colors.lightGrayText;
|
||||
size: 26;
|
||||
|
@ -1551,6 +1635,7 @@ Item {
|
|||
|
||||
Item {
|
||||
id: sendToContainer_paymentFailure;
|
||||
visible: root.assetName === "";
|
||||
anchors.top: paymentFailureDetailText.bottom;
|
||||
anchors.topMargin: 8;
|
||||
anchors.left: parent.left;
|
||||
|
@ -1645,7 +1730,8 @@ Item {
|
|||
}
|
||||
|
||||
RalewaySemiBold {
|
||||
id: optionalMessage_paymentFailuire;
|
||||
id: optionalMessage_paymentFailure;
|
||||
visible: root.assetName === "";
|
||||
text: optionalMessage.text;
|
||||
// Anchors
|
||||
anchors.top: amountContainer_paymentFailure.visible ? amountContainer_paymentFailure.bottom : sendToContainer_paymentFailure.bottom;
|
||||
|
@ -1663,14 +1749,15 @@ Item {
|
|||
verticalAlignment: Text.AlignTop;
|
||||
}
|
||||
|
||||
// "Close" button
|
||||
// "Cancel" button
|
||||
HifiControlsUit.Button {
|
||||
id: closeButton_paymentFailure;
|
||||
color: hifi.buttons.noneBorderless;
|
||||
colorScheme: root.assetName === "" ? hifi.colorSchemes.dark : hifi.colorSchemes.light;
|
||||
anchors.horizontalCenter: parent.horizontalCenter;
|
||||
anchors.right: retryButton_paymentFailure.left;
|
||||
anchors.rightMargin: 12;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 80;
|
||||
anchors.bottomMargin: root.assetName === "" ? 80 : 30;
|
||||
height: 50;
|
||||
width: 120;
|
||||
text: "Cancel";
|
||||
|
@ -1691,7 +1778,7 @@ Item {
|
|||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 12;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 80;
|
||||
anchors.bottomMargin: root.assetName === "" ? 80 : 30;
|
||||
height: 50;
|
||||
width: 120;
|
||||
text: "Retry";
|
||||
|
@ -1768,7 +1855,7 @@ Item {
|
|||
switch (message.method) {
|
||||
case 'selectRecipient':
|
||||
if (message.isSelected) {
|
||||
chooseRecipientNearby.selectedRecipient = message.id[0];
|
||||
chooseRecipientNearby.selectedRecipient = message.id;
|
||||
sendAssetStep.selectedRecipientDisplayName = message.displayName;
|
||||
sendAssetStep.selectedRecipientUserName = message.userName;
|
||||
} else {
|
||||
|
|
|
@ -239,7 +239,6 @@ Item {
|
|||
width: 62;
|
||||
|
||||
onLoaded: {
|
||||
item.enabled = (root.purchaseStatus === "confirmed");
|
||||
item.buttonGlyphText = hifi.glyphs.gift;
|
||||
item.buttonText = "Gift";
|
||||
item.buttonClicked = function() {
|
||||
|
|
|
@ -124,6 +124,14 @@ Rectangle {
|
|||
root.numUpdatesAvailable = result.data.updates.length;
|
||||
}
|
||||
}
|
||||
|
||||
onAppInstalled: {
|
||||
root.installedApps = Commerce.getInstalledApps();
|
||||
}
|
||||
|
||||
onAppUninstalled: {
|
||||
root.installedApps = Commerce.getInstalledApps();
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
|
@ -249,6 +257,145 @@ Rectangle {
|
|||
Commerce.getWalletStatus();
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: installedAppsContainer;
|
||||
z: 998;
|
||||
visible: false;
|
||||
anchors.top: titleBarContainer.bottom;
|
||||
anchors.topMargin: -titleBarContainer.additionalDropdownHeight;
|
||||
anchors.left: parent.left;
|
||||
anchors.bottom: parent.bottom;
|
||||
width: parent.width;
|
||||
|
||||
RalewayRegular {
|
||||
id: installedAppsHeader;
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 10;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 12;
|
||||
height: 80;
|
||||
width: paintedWidth;
|
||||
text: "All Installed Marketplace Apps";
|
||||
color: hifi.colors.black;
|
||||
size: 22;
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: installedAppsList;
|
||||
clip: true;
|
||||
model: installedAppsModel;
|
||||
snapMode: ListView.SnapToItem;
|
||||
// Anchors
|
||||
anchors.top: installedAppsHeader.bottom;
|
||||
anchors.left: parent.left;
|
||||
anchors.bottom: sideloadAppButton.top;
|
||||
width: parent.width;
|
||||
delegate: Item {
|
||||
width: parent.width;
|
||||
height: 40;
|
||||
|
||||
RalewayRegular {
|
||||
text: model.appUrl;
|
||||
// Text size
|
||||
size: 16;
|
||||
// Anchors
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 12;
|
||||
height: parent.height;
|
||||
anchors.right: sideloadAppOpenButton.left;
|
||||
anchors.rightMargin: 8;
|
||||
elide: Text.ElideRight;
|
||||
// Style
|
||||
color: hifi.colors.black;
|
||||
// Alignment
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
onClicked: {
|
||||
Window.copyToClipboard((model.appUrl).slice(0, -9));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.Button {
|
||||
id: sideloadAppOpenButton;
|
||||
text: "OPEN";
|
||||
color: hifi.buttons.blue;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 2;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 2;
|
||||
anchors.right: uninstallGlyph.left;
|
||||
anchors.rightMargin: 8;
|
||||
width: 80;
|
||||
onClicked: {
|
||||
Commerce.openApp(model.appUrl);
|
||||
}
|
||||
}
|
||||
|
||||
HiFiGlyphs {
|
||||
id: uninstallGlyph;
|
||||
text: hifi.glyphs.close;
|
||||
color: hifi.colors.black;
|
||||
size: 22;
|
||||
anchors.top: parent.top;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 6;
|
||||
width: 35;
|
||||
height: parent.height;
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
hoverEnabled: true;
|
||||
onEntered: {
|
||||
parent.text = hifi.glyphs.closeInverted;
|
||||
}
|
||||
onExited: {
|
||||
parent.text = hifi.glyphs.close;
|
||||
}
|
||||
onClicked: {
|
||||
Commerce.uninstallApp(model.appUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
HifiControlsUit.Button {
|
||||
id: sideloadAppButton;
|
||||
color: hifi.buttons.blue;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 8;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 8;
|
||||
anchors.right: closeAppListButton.left;
|
||||
anchors.rightMargin: 8;
|
||||
height: 40;
|
||||
text: "SIDELOAD APP FROM LOCAL DISK";
|
||||
onClicked: {
|
||||
Window.browseChanged.connect(onFileOpenChanged);
|
||||
Window.browseAsync("Locate your app's .app.json file", "", "*.app.json");
|
||||
}
|
||||
}
|
||||
HifiControlsUit.Button {
|
||||
id: closeAppListButton;
|
||||
color: hifi.buttons.white;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 8;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 8;
|
||||
width: 100;
|
||||
height: 40;
|
||||
text: "BACK";
|
||||
onClicked: {
|
||||
installedAppsContainer.visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HifiWallet.NeedsLogIn {
|
||||
id: needsLogIn;
|
||||
|
@ -317,7 +464,7 @@ Rectangle {
|
|||
//
|
||||
Item {
|
||||
id: purchasesContentsContainer;
|
||||
visible: root.activeView === "purchasesMain";
|
||||
visible: root.activeView === "purchasesMain" && !installedAppsList.visible;
|
||||
// Anchors
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
|
@ -959,6 +1106,39 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
if ((event.key == Qt.Key_F) && (event.modifiers & Qt.ControlModifier)) {
|
||||
installedAppsContainer.visible = !installedAppsContainer.visible;
|
||||
console.log("User changed visibility of installedAppsContainer to " + installedAppsContainer.visible);
|
||||
}
|
||||
}
|
||||
function onFileOpenChanged(filename) {
|
||||
// disconnect the event, otherwise the requests will stack up
|
||||
try { // Not all calls to onFileOpenChanged() connect an event.
|
||||
Window.browseChanged.disconnect(onFileOpenChanged);
|
||||
} catch (e) {
|
||||
console.log('Purchases.qml ignoring', e);
|
||||
}
|
||||
if (filename) {
|
||||
Commerce.installApp(filename);
|
||||
}
|
||||
}
|
||||
ListModel {
|
||||
id: installedAppsModel;
|
||||
}
|
||||
onInstalledAppsChanged: {
|
||||
installedAppsModel.clear();
|
||||
var installedAppsArray = root.installedApps.split(",");
|
||||
var installedAppsObject = [];
|
||||
// "- 1" because the last app string ends with ","
|
||||
for (var i = 0; i < installedAppsArray.length - 1; i++) {
|
||||
installedAppsObject[i] = {
|
||||
"appUrl": installedAppsArray[i]
|
||||
}
|
||||
}
|
||||
installedAppsModel.append(installedAppsObject);
|
||||
}
|
||||
|
||||
//
|
||||
// Function Name: fromScript()
|
||||
//
|
||||
|
|
|
@ -181,11 +181,11 @@ Item {
|
|||
minimumValue: 0.01
|
||||
maximumValue: 10
|
||||
realStepSize: 0.05;
|
||||
value: attachment ? attachment.scale : 1.0
|
||||
realValue: attachment ? attachment.scale : 1.0
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
onValueChanged: {
|
||||
if (completed && attachment && attachment.scale !== value) {
|
||||
attachment.scale = value;
|
||||
onRealValueChanged: {
|
||||
if (completed && attachment && attachment.scale !== realValue) {
|
||||
attachment.scale = realValue;
|
||||
updateAttachment();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ Item {
|
|||
id: xspinner
|
||||
width: root.spinboxWidth
|
||||
anchors { left: parent.left }
|
||||
value: root.vector.x
|
||||
realValue: root.vector.x
|
||||
labelInside: "X:"
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
colorLabelInside: hifi.colors.redHighlight
|
||||
|
@ -72,17 +72,17 @@ Item {
|
|||
id: yspinner
|
||||
width: root.spinboxWidth
|
||||
anchors { horizontalCenter: parent.horizontalCenter }
|
||||
value: root.vector.y
|
||||
realValue: root.vector.y
|
||||
labelInside: "Y:"
|
||||
colorLabelInside: hifi.colors.greenHighlight
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
decimals: root.decimals
|
||||
stepSize: root.stepSize
|
||||
realStepSize: root.stepSize
|
||||
maximumValue: root.maximumValue
|
||||
minimumValue: root.minimumValue
|
||||
onValueChanged: {
|
||||
if (value !== vector.y) {
|
||||
vector.y = value
|
||||
onRealValueChanged: {
|
||||
if (realValue !== vector.y) {
|
||||
vector.y = realValue
|
||||
root.valueChanged();
|
||||
}
|
||||
}
|
||||
|
@ -93,17 +93,17 @@ Item {
|
|||
id: zspinner
|
||||
width: root.spinboxWidth
|
||||
anchors { right: parent.right; }
|
||||
value: root.vector.z
|
||||
realValue: root.vector.z
|
||||
labelInside: "Z:"
|
||||
colorLabelInside: hifi.colors.primaryHighlight
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
decimals: root.decimals
|
||||
stepSize: root.stepSize
|
||||
realStepSize: root.stepSize
|
||||
maximumValue: root.maximumValue
|
||||
minimumValue: root.minimumValue
|
||||
onValueChanged: {
|
||||
if (value !== vector.z) {
|
||||
vector.z = value
|
||||
onRealValueChanged: {
|
||||
if (realValue !== vector.z) {
|
||||
vector.z = realValue
|
||||
root.valueChanged();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ StackView {
|
|||
id: stack
|
||||
initialItem: inputConfiguration
|
||||
property alias messageVisible: imageMessageBox.visible
|
||||
property alias selectedPlugin: box.currentText
|
||||
Rectangle {
|
||||
id: inputConfiguration
|
||||
anchors.fill: parent
|
||||
|
|
|
@ -34,7 +34,7 @@ Rectangle {
|
|||
readonly property bool hmdHead: headBox.checked
|
||||
readonly property bool headPuck: headPuckBox.checked
|
||||
readonly property bool handController: handBox.checked
|
||||
|
||||
|
||||
readonly property bool handPuck: handPuckBox.checked
|
||||
readonly property bool hmdDesktop: hmdInDesktop.checked
|
||||
|
||||
|
@ -105,7 +105,7 @@ Rectangle {
|
|||
|
||||
RalewayBold {
|
||||
size: 12
|
||||
text: "Vive HMD"
|
||||
text: stack.selectedPlugin + " HMD"
|
||||
color: hifi.colors.lightGrayText
|
||||
}
|
||||
|
||||
|
@ -143,7 +143,7 @@ Rectangle {
|
|||
anchors.topMargin: 5
|
||||
anchors.left: openVrConfiguration.left
|
||||
anchors.leftMargin: leftMargin + 10
|
||||
|
||||
|
||||
onClicked: {
|
||||
if (checked) {
|
||||
headBox.checked = false;
|
||||
|
@ -178,8 +178,8 @@ Rectangle {
|
|||
label: "Y Offset"
|
||||
suffix: " cm"
|
||||
minimumValue: -10
|
||||
stepSize: 1
|
||||
value: -5
|
||||
realStepSize: 1
|
||||
realValue: -5
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
|
||||
onEditingFinished: {
|
||||
|
@ -193,10 +193,10 @@ Rectangle {
|
|||
width: 112
|
||||
label: "Z Offset"
|
||||
minimumValue: -10
|
||||
stepSize: 1
|
||||
realStepSize: 1
|
||||
decimals: 1
|
||||
suffix: " cm"
|
||||
value: -5
|
||||
realValue: -5
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
|
||||
onEditingFinished: {
|
||||
|
@ -288,7 +288,7 @@ Rectangle {
|
|||
suffix: " cm"
|
||||
label: "Y Offset"
|
||||
minimumValue: -10
|
||||
stepSize: 1
|
||||
realStepSize: 1
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
|
||||
onEditingFinished: {
|
||||
|
@ -303,7 +303,7 @@ Rectangle {
|
|||
label: "Z Offset"
|
||||
suffix: " cm"
|
||||
minimumValue: -10
|
||||
stepSize: 1
|
||||
realStepSize: 1
|
||||
decimals: 1
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
|
||||
|
@ -535,9 +535,9 @@ Rectangle {
|
|||
suffix: " cm"
|
||||
label: "Arm Circumference"
|
||||
minimumValue: 0
|
||||
stepSize: 1.0
|
||||
realStepSize: 1.0
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
value: 33.0
|
||||
realValue: 33.0
|
||||
|
||||
onEditingFinished: {
|
||||
sendConfigurationSettings();
|
||||
|
@ -550,10 +550,10 @@ Rectangle {
|
|||
label: "Shoulder Width"
|
||||
suffix: " cm"
|
||||
minimumValue: 0
|
||||
stepSize: 1.0
|
||||
realStepSize: 1.0
|
||||
decimals: 1
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
value: 48
|
||||
realValue: 48
|
||||
|
||||
onEditingFinished: {
|
||||
sendConfigurationSettings();
|
||||
|
@ -659,13 +659,13 @@ Rectangle {
|
|||
InputConfiguration.uncalibratePlugin(pluginName);
|
||||
updateCalibrationButton();
|
||||
} else {
|
||||
calibrationTimer.interval = timeToCalibrate.value * 1000
|
||||
openVrConfiguration.countDown = timeToCalibrate.value;
|
||||
calibrationTimer.interval = timeToCalibrate.realValue * 1000
|
||||
openVrConfiguration.countDown = timeToCalibrate.realValue;
|
||||
var calibratingScreen = screen.createObject();
|
||||
stack.push(calibratingScreen);
|
||||
calibratingScreen.canceled.connect(cancelCalibration);
|
||||
calibratingScreen.restart.connect(restartCalibration);
|
||||
calibratingScreen.start(calibrationTimer.interval, timeToCalibrate.value);
|
||||
calibratingScreen.start(calibrationTimer.interval, timeToCalibrate.realValue);
|
||||
calibrationTimer.start();
|
||||
}
|
||||
}
|
||||
|
@ -728,12 +728,12 @@ Rectangle {
|
|||
anchors.leftMargin: leftMargin
|
||||
|
||||
minimumValue: 5
|
||||
value: 5
|
||||
realValue: 5
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
|
||||
onEditingFinished: {
|
||||
calibrationTimer.interval = value * 1000;
|
||||
openVrConfiguration.countDown = value;
|
||||
calibrationTimer.interval = realValue * 1000;
|
||||
openVrConfiguration.countDown = realValue;
|
||||
numberAnimation.duration = calibrationTimer.interval;
|
||||
}
|
||||
}
|
||||
|
@ -772,12 +772,12 @@ Rectangle {
|
|||
|
||||
RalewayBold {
|
||||
id: advanceSettings
|
||||
|
||||
|
||||
text: "Advanced Settings"
|
||||
size: 12
|
||||
|
||||
|
||||
color: hifi.colors.white
|
||||
|
||||
|
||||
anchors.top: advanceSeperator.bottom
|
||||
anchors.topMargin: 10
|
||||
anchors.left: parent.left
|
||||
|
@ -795,7 +795,7 @@ Rectangle {
|
|||
anchors.topMargin: 5
|
||||
anchors.left: openVrConfiguration.left
|
||||
anchors.leftMargin: leftMargin + 10
|
||||
|
||||
|
||||
onClicked: {
|
||||
if (!checked & hmdInDesktop.checked) {
|
||||
headBox.checked = true;
|
||||
|
@ -809,9 +809,9 @@ Rectangle {
|
|||
RalewayBold {
|
||||
id: viveDesktopText
|
||||
size: 10
|
||||
text: "Use Vive devices in desktop mode"
|
||||
text: "Use " + stack.selectedPlugin + " devices in desktop mode"
|
||||
color: hifi.colors.white
|
||||
|
||||
|
||||
anchors {
|
||||
left: viveInDesktop.right
|
||||
leftMargin: 5
|
||||
|
@ -819,7 +819,7 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
NumberAnimation {
|
||||
id: numberAnimation
|
||||
target: openVrConfiguration
|
||||
|
@ -910,8 +910,8 @@ Rectangle {
|
|||
var desktopMode = settings["desktopMode"];
|
||||
var hmdDesktopPosition = settings["hmdDesktopTracking"];
|
||||
|
||||
armCircumference.value = settings.armCircumference;
|
||||
shoulderWidth.value = settings.shoulderWidth;
|
||||
armCircumference.realValue = settings.armCircumference;
|
||||
shoulderWidth.realValue = settings.shoulderWidth;
|
||||
|
||||
if (HmdHead) {
|
||||
headBox.checked = true;
|
||||
|
@ -1075,22 +1075,22 @@ Rectangle {
|
|||
|
||||
var headObject = {
|
||||
"override": overrideHead,
|
||||
"Y": headYOffset.value,
|
||||
"Z": headZOffset.value
|
||||
"Y": headYOffset.realValue,
|
||||
"Z": headZOffset.realValue
|
||||
}
|
||||
|
||||
var handObject = {
|
||||
"override": overrideHandController,
|
||||
"Y": handYOffset.value,
|
||||
"Z": handZOffset.value
|
||||
"Y": handYOffset.realValue,
|
||||
"Z": handZOffset.realValue
|
||||
}
|
||||
|
||||
var settingsObject = {
|
||||
"bodyConfiguration": trackerConfiguration,
|
||||
"headConfiguration": headObject,
|
||||
"handConfiguration": handObject,
|
||||
"armCircumference": armCircumference.value,
|
||||
"shoulderWidth": shoulderWidth.value,
|
||||
"armCircumference": armCircumference.realValue,
|
||||
"shoulderWidth": shoulderWidth.realValue,
|
||||
"desktopMode": viveInDesktop.checked,
|
||||
"hmdDesktopTracking": hmdInDesktop.checked
|
||||
}
|
||||
|
|
|
@ -145,6 +145,16 @@
|
|||
#include <avatars-renderer/ScriptAvatar.h>
|
||||
#include <RenderableEntityItem.h>
|
||||
|
||||
#include <AnimationLogging.h>
|
||||
#include <AvatarLogging.h>
|
||||
#include <ScriptEngineLogging.h>
|
||||
#include <ModelFormatLogging.h>
|
||||
#include <controllers/Logging.h>
|
||||
#include <NetworkLogging.h>
|
||||
#include <shared/StorageLogging.h>
|
||||
#include <ScriptEngineLogging.h>
|
||||
#include <ui/Logging.h>
|
||||
|
||||
#include "AudioClient.h"
|
||||
#include "audio/AudioScope.h"
|
||||
#include "avatar/AvatarManager.h"
|
||||
|
@ -1061,6 +1071,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
if (steamClient) {
|
||||
qCDebug(interfaceapp) << "[VERSION] SteamVR buildID:" << steamClient->getSteamVRBuildID();
|
||||
}
|
||||
setCrashAnnotation("steam", property(hifi::properties::STEAM).toBool() ? "1" : "0");
|
||||
|
||||
qCDebug(interfaceapp) << "[VERSION] Build sequence:" << qPrintable(applicationVersion());
|
||||
qCDebug(interfaceapp) << "[VERSION] MODIFIED_ORGANIZATION:" << BuildInfo::MODIFIED_ORGANIZATION;
|
||||
qCDebug(interfaceapp) << "[VERSION] VERSION:" << BuildInfo::VERSION;
|
||||
|
@ -1146,6 +1158,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
const DomainHandler& domainHandler = nodeList->getDomainHandler();
|
||||
|
||||
connect(&domainHandler, SIGNAL(domainURLChanged(QUrl)), SLOT(domainURLChanged(QUrl)));
|
||||
connect(&domainHandler, &DomainHandler::domainURLChanged, [](QUrl domainURL){
|
||||
setCrashAnnotation("domain", domainURL.toString().toStdString());
|
||||
});
|
||||
connect(&domainHandler, SIGNAL(resetting()), SLOT(resettingDomain()));
|
||||
connect(&domainHandler, SIGNAL(connectedToDomain(QUrl)), SLOT(updateWindowTitle()));
|
||||
connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(updateWindowTitle()));
|
||||
|
@ -1191,6 +1206,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
auto dialogsManager = DependencyManager::get<DialogsManager>();
|
||||
connect(accountManager.data(), &AccountManager::authRequired, dialogsManager.data(), &DialogsManager::showLoginDialog);
|
||||
connect(accountManager.data(), &AccountManager::usernameChanged, this, &Application::updateWindowTitle);
|
||||
connect(accountManager.data(), &AccountManager::usernameChanged, [](QString username){
|
||||
setCrashAnnotation("username", username.toStdString());
|
||||
});
|
||||
|
||||
// set the account manager's root URL and trigger a login request if we don't have the access token
|
||||
accountManager->setIsAgent(true);
|
||||
|
@ -1208,6 +1226,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
connect(this, &Application::activeDisplayPluginChanged, this, &Application::updateThreadPoolCount);
|
||||
connect(this, &Application::activeDisplayPluginChanged, this, [](){
|
||||
qApp->setProperty(hifi::properties::HMD, qApp->isHMDMode());
|
||||
auto displayPlugin = qApp->getActiveDisplayPlugin();
|
||||
setCrashAnnotation("display_plugin", displayPlugin->getName().toStdString());
|
||||
setCrashAnnotation("hmd", displayPlugin->isHmd() ? "1" : "0");
|
||||
});
|
||||
connect(this, &Application::activeDisplayPluginChanged, this, &Application::updateSystemTabletMode);
|
||||
|
||||
|
@ -1215,6 +1236,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
connect(myAvatar.get(), &MyAvatar::positionGoneTo,
|
||||
DependencyManager::get<AddressManager>().data(), &AddressManager::storeCurrentAddress);
|
||||
|
||||
connect(myAvatar.get(), &MyAvatar::skeletonModelURLChanged, [](){
|
||||
QUrl avatarURL = qApp->getMyAvatar()->getSkeletonModelURL();
|
||||
setCrashAnnotation("avatar", avatarURL.toString().toStdString());
|
||||
});
|
||||
|
||||
|
||||
// Inititalize sample before registering
|
||||
_sampleSound = DependencyManager::get<SoundCache>()->getSound(PathUtils::resourcesUrl("sounds/sample.wav"));
|
||||
|
||||
|
@ -1307,6 +1334,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
// Needs to happen AFTER the render engine initialization to access its configuration
|
||||
initializeUi();
|
||||
|
||||
updateVerboseLogging();
|
||||
|
||||
init();
|
||||
qCDebug(interfaceapp, "init() complete.");
|
||||
|
||||
|
@ -1323,49 +1352,48 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
// Make sure we don't time out during slow operations at startup
|
||||
updateHeartbeat();
|
||||
|
||||
// sessionRunTime will be reset soon by loadSettings. Grab it now to get previous session value.
|
||||
// The value will be 0 if the user blew away settings this session, which is both a feature and a bug.
|
||||
static const QString TESTER = "HIFI_TESTER";
|
||||
auto gpuIdent = GPUIdent::getInstance();
|
||||
auto glContextData = getGLContextData();
|
||||
QJsonObject properties = {
|
||||
{ "version", applicationVersion() },
|
||||
{ "tester", QProcessEnvironment::systemEnvironment().contains(TESTER) },
|
||||
{ "previousSessionCrashed", _previousSessionCrashed },
|
||||
{ "previousSessionRuntime", sessionRunTime.get() },
|
||||
{ "cpu_architecture", QSysInfo::currentCpuArchitecture() },
|
||||
{ "kernel_type", QSysInfo::kernelType() },
|
||||
{ "kernel_version", QSysInfo::kernelVersion() },
|
||||
{ "os_type", QSysInfo::productType() },
|
||||
{ "os_version", QSysInfo::productVersion() },
|
||||
{ "gpu_name", gpuIdent->getName() },
|
||||
{ "gpu_driver", gpuIdent->getDriver() },
|
||||
{ "gpu_memory", static_cast<qint64>(gpuIdent->getMemory()) },
|
||||
{ "gl_version_int", glVersionToInteger(glContextData.value("version").toString()) },
|
||||
{ "gl_version", glContextData["version"] },
|
||||
{ "gl_vender", glContextData["vendor"] },
|
||||
{ "gl_sl_version", glContextData["sl_version"] },
|
||||
{ "gl_renderer", glContextData["renderer"] },
|
||||
{ "ideal_thread_count", QThread::idealThreadCount() }
|
||||
};
|
||||
auto macVersion = QSysInfo::macVersion();
|
||||
if (macVersion != QSysInfo::MV_None) {
|
||||
properties["os_osx_version"] = QSysInfo::macVersion();
|
||||
}
|
||||
auto windowsVersion = QSysInfo::windowsVersion();
|
||||
if (windowsVersion != QSysInfo::WV_None) {
|
||||
properties["os_win_version"] = QSysInfo::windowsVersion();
|
||||
constexpr auto INSTALLER_INI_NAME = "installer.ini";
|
||||
auto iniPath = QDir(applicationDirPath()).filePath(INSTALLER_INI_NAME);
|
||||
QFile installerFile { iniPath };
|
||||
std::unordered_map<QString, QString> installerKeyValues;
|
||||
if (installerFile.open(QIODevice::ReadOnly)) {
|
||||
while (!installerFile.atEnd()) {
|
||||
auto line = installerFile.readLine();
|
||||
if (!line.isEmpty()) {
|
||||
auto index = line.indexOf("=");
|
||||
if (index >= 0) {
|
||||
installerKeyValues[line.mid(0, index).trimmed()] = line.mid(index + 1).trimmed();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ProcessorInfo procInfo;
|
||||
if (getProcessorInfo(procInfo)) {
|
||||
properties["processor_core_count"] = procInfo.numProcessorCores;
|
||||
properties["logical_processor_count"] = procInfo.numLogicalProcessors;
|
||||
properties["processor_l1_cache_count"] = procInfo.numProcessorCachesL1;
|
||||
properties["processor_l2_cache_count"] = procInfo.numProcessorCachesL2;
|
||||
properties["processor_l3_cache_count"] = procInfo.numProcessorCachesL3;
|
||||
// In practice we shouldn't run across installs that don't have a known installer type.
|
||||
// Client or Client+Server installs should always have the installer.ini next to their
|
||||
// respective interface.exe, and Steam installs will be detected as such. If a user were
|
||||
// to delete the installer.ini, though, and as an example, we won't know the context of the
|
||||
// original install.
|
||||
constexpr auto INSTALLER_KEY_TYPE = "type";
|
||||
constexpr auto INSTALLER_KEY_CAMPAIGN = "campaign";
|
||||
constexpr auto INSTALLER_TYPE_UNKNOWN = "unknown";
|
||||
constexpr auto INSTALLER_TYPE_STEAM = "steam";
|
||||
|
||||
auto typeIt = installerKeyValues.find(INSTALLER_KEY_TYPE);
|
||||
QString installerType = INSTALLER_TYPE_UNKNOWN;
|
||||
if (typeIt == installerKeyValues.end()) {
|
||||
if (property(hifi::properties::STEAM).toBool()) {
|
||||
installerType = INSTALLER_TYPE_STEAM;
|
||||
}
|
||||
} else {
|
||||
installerType = typeIt->second;
|
||||
}
|
||||
|
||||
auto campaignIt = installerKeyValues.find(INSTALLER_KEY_CAMPAIGN);
|
||||
QString installerCampaign = campaignIt != installerKeyValues.end() ? campaignIt->second : "";
|
||||
|
||||
qDebug() << "Detected installer type:" << installerType;
|
||||
qDebug() << "Detected installer campaign:" << installerCampaign;
|
||||
|
||||
// add firstRun flag from settings to launch event
|
||||
Setting::Handle<bool> firstRun { Settings::firstRun, true };
|
||||
|
||||
|
@ -1378,6 +1406,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
userActivityLogger.disable(false);
|
||||
}
|
||||
|
||||
QString machineFingerPrint = uuidStringWithoutCurlyBraces(FingerprintUtils::getMachineFingerprint());
|
||||
|
||||
if (userActivityLogger.isEnabled()) {
|
||||
// sessionRunTime will be reset soon by loadSettings. Grab it now to get previous session value.
|
||||
// The value will be 0 if the user blew away settings this session, which is both a feature and a bug.
|
||||
|
@ -1387,6 +1417,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
QJsonObject properties = {
|
||||
{ "version", applicationVersion() },
|
||||
{ "tester", QProcessEnvironment::systemEnvironment().contains(TESTER) },
|
||||
{ "installer_campaign", installerCampaign },
|
||||
{ "installer_type", installerType },
|
||||
{ "previousSessionCrashed", _previousSessionCrashed },
|
||||
{ "previousSessionRuntime", sessionRunTime.get() },
|
||||
{ "cpu_architecture", QSysInfo::currentCpuArchitecture() },
|
||||
|
@ -1425,11 +1457,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
properties["first_run"] = firstRun.get();
|
||||
|
||||
// add the user's machine ID to the launch event
|
||||
properties["machine_fingerprint"] = uuidStringWithoutCurlyBraces(FingerprintUtils::getMachineFingerprint());
|
||||
properties["machine_fingerprint"] = machineFingerPrint;
|
||||
|
||||
userActivityLogger.logAction("launch", properties);
|
||||
}
|
||||
|
||||
setCrashAnnotation("machine_fingerprint", machineFingerPrint.toStdString());
|
||||
|
||||
_entityEditSender.setMyAvatar(myAvatar.get());
|
||||
|
||||
// The entity octree will have to know about MyAvatar for the parentJointName import
|
||||
|
@ -1706,7 +1740,15 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
const QString HIFI_NO_UPDATER_COMMAND_LINE_KEY = "--no-updater";
|
||||
bool noUpdater = arguments().indexOf(HIFI_NO_UPDATER_COMMAND_LINE_KEY) != -1;
|
||||
if (!noUpdater) {
|
||||
constexpr auto INSTALLER_TYPE_CLIENT_ONLY = "client_only";
|
||||
|
||||
auto applicationUpdater = DependencyManager::get<AutoUpdater>();
|
||||
|
||||
AutoUpdater::InstallerType type = installerType == INSTALLER_TYPE_CLIENT_ONLY
|
||||
? AutoUpdater::InstallerType::CLIENT_ONLY : AutoUpdater::InstallerType::FULL;
|
||||
|
||||
applicationUpdater->setInstallerType(type);
|
||||
applicationUpdater->setInstallerCampaign(installerCampaign);
|
||||
connect(applicationUpdater.data(), &AutoUpdater::newVersionIsAvailable, dialogsManager.data(), &DialogsManager::showUpdateDialog);
|
||||
applicationUpdater->checkForUpdate();
|
||||
}
|
||||
|
@ -2166,6 +2208,46 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
qCDebug(interfaceapp) << "Metaverse session ID is" << uuidStringWithoutCurlyBraces(accountManager->getSessionID());
|
||||
}
|
||||
|
||||
void Application::updateVerboseLogging() {
|
||||
bool enable = Menu::getInstance()->isOptionChecked(MenuOption::VerboseLogging);
|
||||
|
||||
const_cast<QLoggingCategory*>(&animation())->setEnabled(QtDebugMsg, enable);
|
||||
const_cast<QLoggingCategory*>(&animation())->setEnabled(QtInfoMsg, enable);
|
||||
|
||||
const_cast<QLoggingCategory*>(&avatars())->setEnabled(QtDebugMsg, enable);
|
||||
const_cast<QLoggingCategory*>(&avatars())->setEnabled(QtInfoMsg, enable);
|
||||
|
||||
const_cast<QLoggingCategory*>(&scriptengine())->setEnabled(QtDebugMsg, enable);
|
||||
const_cast<QLoggingCategory*>(&scriptengine())->setEnabled(QtInfoMsg, enable);
|
||||
|
||||
const_cast<QLoggingCategory*>(&modelformat())->setEnabled(QtDebugMsg, enable);
|
||||
const_cast<QLoggingCategory*>(&modelformat())->setEnabled(QtInfoMsg, enable);
|
||||
|
||||
const_cast<QLoggingCategory*>(&controllers())->setEnabled(QtDebugMsg, enable);
|
||||
const_cast<QLoggingCategory*>(&controllers())->setEnabled(QtInfoMsg, enable);
|
||||
|
||||
const_cast<QLoggingCategory*>(&resourceLog())->setEnabled(QtDebugMsg, enable);
|
||||
const_cast<QLoggingCategory*>(&resourceLog())->setEnabled(QtInfoMsg, enable);
|
||||
|
||||
const_cast<QLoggingCategory*>(&networking())->setEnabled(QtDebugMsg, enable);
|
||||
const_cast<QLoggingCategory*>(&networking())->setEnabled(QtInfoMsg, enable);
|
||||
|
||||
const_cast<QLoggingCategory*>(&asset_client())->setEnabled(QtDebugMsg, enable);
|
||||
const_cast<QLoggingCategory*>(&asset_client())->setEnabled(QtInfoMsg, enable);
|
||||
|
||||
const_cast<QLoggingCategory*>(&messages_client())->setEnabled(QtDebugMsg, enable);
|
||||
const_cast<QLoggingCategory*>(&messages_client())->setEnabled(QtInfoMsg, enable);
|
||||
|
||||
const_cast<QLoggingCategory*>(&storagelogging())->setEnabled(QtDebugMsg, enable);
|
||||
const_cast<QLoggingCategory*>(&storagelogging())->setEnabled(QtInfoMsg, enable);
|
||||
|
||||
const_cast<QLoggingCategory*>(&uiLogging())->setEnabled(QtDebugMsg, enable);
|
||||
const_cast<QLoggingCategory*>(&uiLogging())->setEnabled(QtInfoMsg, enable);
|
||||
|
||||
const_cast<QLoggingCategory*>(&glLogging())->setEnabled(QtDebugMsg, enable);
|
||||
const_cast<QLoggingCategory*>(&glLogging())->setEnabled(QtInfoMsg, enable);
|
||||
}
|
||||
|
||||
void Application::domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo) {
|
||||
DomainHandler::ConnectionRefusedReason reasonCode = static_cast<DomainHandler::ConnectionRefusedReason>(reasonCodeInt);
|
||||
|
||||
|
@ -3037,7 +3119,6 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
|
|||
PROFILE_RANGE(render, __FUNCTION__);
|
||||
|
||||
bool sandboxIsRunning = SandboxUtils::readStatus(reply->readAll());
|
||||
qDebug() << "HandleSandboxStatus" << sandboxIsRunning;
|
||||
|
||||
enum HandControllerType {
|
||||
Vive,
|
||||
|
@ -4754,7 +4835,7 @@ void Application::updateLOD(float deltaTime) const {
|
|||
}
|
||||
}
|
||||
|
||||
void Application::pushPostUpdateLambda(void* key, std::function<void()> func) {
|
||||
void Application::pushPostUpdateLambda(void* key, const std::function<void()>& func) {
|
||||
std::unique_lock<std::mutex> guard(_postUpdateLambdasLock);
|
||||
_postUpdateLambdas[key] = func;
|
||||
}
|
||||
|
@ -7364,7 +7445,7 @@ void Application::windowMinimizedChanged(bool minimized) {
|
|||
}
|
||||
}
|
||||
|
||||
void Application::postLambdaEvent(std::function<void()> f) {
|
||||
void Application::postLambdaEvent(const std::function<void()>& f) {
|
||||
if (this->thread() == QThread::currentThread()) {
|
||||
f();
|
||||
} else {
|
||||
|
@ -7372,6 +7453,15 @@ void Application::postLambdaEvent(std::function<void()> f) {
|
|||
}
|
||||
}
|
||||
|
||||
void Application::sendLambdaEvent(const std::function<void()>& f) {
|
||||
if (this->thread() == QThread::currentThread()) {
|
||||
f();
|
||||
} else {
|
||||
LambdaEvent event(f);
|
||||
QCoreApplication::sendEvent(this, &event);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::initPlugins(const QStringList& arguments) {
|
||||
QCommandLineOption display("display", "Preferred displays", "displays");
|
||||
QCommandLineOption disableDisplays("disable-displays", "Displays to disable", "displays");
|
||||
|
|
|
@ -136,7 +136,8 @@ public:
|
|||
Application(int& argc, char** argv, QElapsedTimer& startup_time, bool runningMarkerExisted);
|
||||
~Application();
|
||||
|
||||
void postLambdaEvent(std::function<void()> f) override;
|
||||
void postLambdaEvent(const std::function<void()>& f) override;
|
||||
void sendLambdaEvent(const std::function<void()>& f) override;
|
||||
|
||||
QString getPreviousScriptLocation();
|
||||
void setPreviousScriptLocation(const QString& previousScriptLocation);
|
||||
|
@ -240,7 +241,7 @@ public:
|
|||
|
||||
qint64 getCurrentSessionRuntime() const { return _sessionRunTimer.elapsed(); }
|
||||
|
||||
bool isAboutToQuit() const override { return _aboutToQuit; }
|
||||
bool isAboutToQuit() const { return _aboutToQuit; }
|
||||
bool isPhysicsEnabled() const { return _physicsEnabled; }
|
||||
|
||||
// the isHMDMode is true whenever we use the interface from an HMD and not a standard flat display
|
||||
|
@ -264,7 +265,7 @@ public:
|
|||
render::EnginePointer getRenderEngine() override { return _renderEngine; }
|
||||
gpu::ContextPointer getGPUContext() const { return _gpuContext; }
|
||||
|
||||
virtual void pushPostUpdateLambda(void* key, std::function<void()> func) override;
|
||||
virtual void pushPostUpdateLambda(void* key, const std::function<void()>& func) override;
|
||||
|
||||
void updateMyAvatarLookAtPosition();
|
||||
|
||||
|
@ -403,8 +404,10 @@ public slots:
|
|||
|
||||
Q_INVOKABLE bool askBeforeSetAvatarUrl(const QString& avatarUrl) { return askToSetAvatarUrl(avatarUrl); }
|
||||
|
||||
void updateVerboseLogging();
|
||||
Q_INVOKABLE void openAndroidActivity(const QString& activityName);
|
||||
|
||||
|
||||
private slots:
|
||||
void onDesktopRootItemCreated(QQuickItem* qmlContext);
|
||||
void onDesktopRootContextCreated(QQmlContext* qmlContext);
|
||||
|
|
|
@ -15,6 +15,11 @@
|
|||
#include <DependencyManager.h>
|
||||
#include "Bookmarks.h"
|
||||
|
||||
/**jsdoc
|
||||
* This API helps manage adding and deleting avatar bookmarks.
|
||||
* @namespace AvatarBookmarks
|
||||
*/
|
||||
|
||||
class AvatarBookmarks: public Bookmarks, public Dependency {
|
||||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
@ -23,7 +28,12 @@ public:
|
|||
AvatarBookmarks();
|
||||
void setupMenus(Menu* menubar, MenuWrapper* menu) override;
|
||||
|
||||
|
||||
public slots:
|
||||
/**jsdoc
|
||||
* Add the current Avatar to your avatar bookmarks.
|
||||
* @function AvatarBookmarks.addBookMark
|
||||
*/
|
||||
void addBookmark();
|
||||
|
||||
protected:
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//
|
||||
//
|
||||
// Bookmarks.h
|
||||
// interface/src
|
||||
//
|
||||
|
@ -48,6 +48,9 @@ protected:
|
|||
bool _isMenuSorted;
|
||||
|
||||
protected slots:
|
||||
/**jsdoc
|
||||
* @function AvatarBookmarks.deleteBookmark
|
||||
*/
|
||||
void deleteBookmark();
|
||||
|
||||
private:
|
||||
|
|
|
@ -43,12 +43,10 @@ void ConnectionMonitor::init() {
|
|||
}
|
||||
|
||||
void ConnectionMonitor::startTimer() {
|
||||
qDebug() << "ConnectionMonitor: Starting timer";
|
||||
_timer.start(DISPLAY_AFTER_DISCONNECTED_FOR_X_MS);
|
||||
}
|
||||
|
||||
void ConnectionMonitor::stopTimer() {
|
||||
qDebug() << "ConnectionMonitor: Stopping timer";
|
||||
_timer.stop();
|
||||
DependencyManager::get<DialogsManager>()->setDomainConnectionFailureVisibility(false);
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
|
||||
#if HAS_CRASHPAD
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include <QStandardPaths>
|
||||
#include <QDir>
|
||||
|
||||
|
@ -23,8 +25,8 @@
|
|||
#include <client/crashpad_client.h>
|
||||
#include <client/crash_report_database.h>
|
||||
#include <client/settings.h>
|
||||
// #include <client/annotation_list.h>
|
||||
// #include <client/crashpad_info.h>
|
||||
#include <client/annotation_list.h>
|
||||
#include <client/crashpad_info.h>
|
||||
|
||||
using namespace crashpad;
|
||||
|
||||
|
@ -35,32 +37,19 @@ static std::wstring gIPCPipe;
|
|||
|
||||
extern QString qAppFileName();
|
||||
|
||||
// crashpad::AnnotationList* crashpadAnnotations { nullptr };
|
||||
std::mutex annotationMutex;
|
||||
crashpad::SimpleStringDictionary* crashpadAnnotations { nullptr };
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
LONG WINAPI vectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo) {
|
||||
static const DWORD EXTERNAL_EXCEPTION_CODE{ 0xe06d7363 };
|
||||
static const DWORD HEAP_CORRUPTION_CODE{ 0xc0000374 };
|
||||
|
||||
auto exceptionCode = pExceptionInfo->ExceptionRecord->ExceptionCode;
|
||||
if (exceptionCode == EXTERNAL_EXCEPTION_CODE) {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
if (exceptionCode == HEAP_CORRUPTION_CODE) {
|
||||
qCritical() << "VectoredExceptionHandler: Heap corruption:" << QString::number(exceptionCode, 16);
|
||||
|
||||
if (pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_HEAP_CORRUPTION ||
|
||||
pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_STACK_BUFFER_OVERRUN) {
|
||||
CrashpadClient client;
|
||||
if (gIPCPipe.length()) {
|
||||
bool rc = client.SetHandlerIPCPipe(gIPCPipe);
|
||||
qCritical() << "SetHandlerIPCPipe = " << rc;
|
||||
} else {
|
||||
qCritical() << "No IPC Pipe was previously defined for crash handler.";
|
||||
client.SetHandlerIPCPipe(gIPCPipe);
|
||||
}
|
||||
qCritical() << "Calling DumpAndCrash()";
|
||||
client.DumpAndCrash(pExceptionInfo);
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
|
@ -116,12 +105,14 @@ bool startCrashHandler() {
|
|||
}
|
||||
|
||||
void setCrashAnnotation(std::string name, std::string value) {
|
||||
// if (!crashpadAnnotations) {
|
||||
// crashpadAnnotations = new crashpad::AnnotationList(); // don't free this, let it leak
|
||||
// crashpad::CrashpadInfo* crashpad_info = crashpad::GetCrashpadInfo();
|
||||
// crashpad_info->set_simple_annotations(crashpadAnnotations);
|
||||
// }
|
||||
// crashpadAnnotations->SetKeyValue(name, value);
|
||||
std::lock_guard<std::mutex> guard(annotationMutex);
|
||||
if (!crashpadAnnotations) {
|
||||
crashpadAnnotations = new crashpad::SimpleStringDictionary(); // don't free this, let it leak
|
||||
crashpad::CrashpadInfo* crashpad_info = crashpad::CrashpadInfo::GetCrashpadInfo();
|
||||
crashpad_info->set_simple_annotations(crashpadAnnotations);
|
||||
}
|
||||
std::replace(value.begin(), value.end(), ',', ';');
|
||||
crashpadAnnotations->SetKeyValue(name, value);
|
||||
}
|
||||
|
||||
#else
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <UserActivityLogger.h>
|
||||
#include <UUID.h>
|
||||
|
||||
#include "Crashpad.h"
|
||||
#include "DiscoverabilityManager.h"
|
||||
#include "Menu.h"
|
||||
|
||||
|
@ -127,10 +128,12 @@ void DiscoverabilityManager::updateLocation() {
|
|||
QNetworkAccessManager::PutOperation, callbackParameters);
|
||||
}
|
||||
|
||||
// Update Steam
|
||||
// Update Steam and crash logger
|
||||
QUrl currentAddress = addressManager->currentFacingPublicAddress();
|
||||
if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
|
||||
steamClient->updateLocation(domainHandler.getHostname(), addressManager->currentFacingPublicAddress());
|
||||
steamClient->updateLocation(domainHandler.getHostname(), currentAddress);
|
||||
}
|
||||
setCrashAnnotation("address", currentAddress.toString().toStdString());
|
||||
}
|
||||
|
||||
void DiscoverabilityManager::handleHeartbeatResponse(QNetworkReply& requestReply) {
|
||||
|
|
|
@ -34,43 +34,127 @@ const float ADJUST_LOD_MIN_SIZE_SCALE = DEFAULT_OCTREE_SIZE_SCALE * 0.04f;
|
|||
|
||||
class AABox;
|
||||
|
||||
/**jsdoc
|
||||
* The LOD class manages your Level of Detail functions within Interface.
|
||||
* @namespace LODManager
|
||||
* @property {number} presentTime <em>Read-only.</em>
|
||||
* @property {number} engineRunTime <em>Read-only.</em>
|
||||
* @property {number} gpuTime <em>Read-only.</em>
|
||||
* @property {number} avgRenderTime <em>Read-only.</em>
|
||||
* @property {number} fps <em>Read-only.</em>
|
||||
* @property {number} lodLevel <em>Read-only.</em>
|
||||
* @property {number} lodDecreaseFPS <em>Read-only.</em>
|
||||
* @property {number} lodIncreaseFPS <em>Read-only.</em>
|
||||
*/
|
||||
|
||||
class LODManager : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
||||
public:
|
||||
Q_INVOKABLE void setAutomaticLODAdjust(bool value) { _automaticLODAdjust = value; }
|
||||
Q_INVOKABLE bool getAutomaticLODAdjust() const { return _automaticLODAdjust; }
|
||||
|
||||
Q_INVOKABLE void setDesktopLODDecreaseFPS(float value);
|
||||
Q_INVOKABLE float getDesktopLODDecreaseFPS() const;
|
||||
Q_INVOKABLE float getDesktopLODIncreaseFPS() const;
|
||||
|
||||
Q_INVOKABLE void setHMDLODDecreaseFPS(float value);
|
||||
Q_INVOKABLE float getHMDLODDecreaseFPS() const;
|
||||
Q_INVOKABLE float getHMDLODIncreaseFPS() const;
|
||||
|
||||
// User Tweakable LOD Items
|
||||
Q_INVOKABLE QString getLODFeedbackText();
|
||||
Q_INVOKABLE void setOctreeSizeScale(float sizeScale);
|
||||
Q_INVOKABLE float getOctreeSizeScale() const { return _octreeSizeScale; }
|
||||
|
||||
Q_INVOKABLE void setBoundaryLevelAdjust(int boundaryLevelAdjust);
|
||||
Q_INVOKABLE int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; }
|
||||
|
||||
Q_INVOKABLE float getLODDecreaseFPS() const;
|
||||
Q_INVOKABLE float getLODIncreaseFPS() const;
|
||||
|
||||
Q_PROPERTY(float presentTime READ getPresentTime)
|
||||
Q_PROPERTY(float engineRunTime READ getEngineRunTime)
|
||||
Q_PROPERTY(float gpuTime READ getGPUTime)
|
||||
Q_PROPERTY(float avgRenderTime READ getAverageRenderTime)
|
||||
Q_PROPERTY(float fps READ getMaxTheoreticalFPS)
|
||||
Q_PROPERTY(float lodLevel READ getLODLevel)
|
||||
|
||||
Q_PROPERTY(float lodDecreaseFPS READ getLODDecreaseFPS)
|
||||
Q_PROPERTY(float lodIncreaseFPS READ getLODIncreaseFPS)
|
||||
|
||||
public:
|
||||
|
||||
/**jsdoc
|
||||
* @function LODManager.setAutomaticLODAdjust
|
||||
* @param {boolean} value
|
||||
*/
|
||||
Q_INVOKABLE void setAutomaticLODAdjust(bool value) { _automaticLODAdjust = value; }
|
||||
|
||||
/**jsdoc
|
||||
* @function LODManager.getAutomaticLODAdjust
|
||||
* @returns {boolean}
|
||||
*/
|
||||
Q_INVOKABLE bool getAutomaticLODAdjust() const { return _automaticLODAdjust; }
|
||||
|
||||
/**jsdoc
|
||||
* @function LODManager.setDesktopLODDecreaseFPS
|
||||
* @param {number} value
|
||||
*/
|
||||
Q_INVOKABLE void setDesktopLODDecreaseFPS(float value);
|
||||
|
||||
/**jsdoc
|
||||
* @function LODManager.getDesktopLODDecreaseFPS
|
||||
* @returns {number}
|
||||
*/
|
||||
|
||||
Q_INVOKABLE float getDesktopLODDecreaseFPS() const;
|
||||
|
||||
/**jsdoc
|
||||
* @function LODManager.getDesktopLODIncreaseFPS
|
||||
* @returns {number}
|
||||
*/
|
||||
Q_INVOKABLE float getDesktopLODIncreaseFPS() const;
|
||||
|
||||
/**jsdoc
|
||||
* @function LODManager.setHMDLODDecreaseFPS
|
||||
* @param {number} value
|
||||
*/
|
||||
|
||||
Q_INVOKABLE void setHMDLODDecreaseFPS(float value);
|
||||
|
||||
/**jsdoc
|
||||
* @function LODManager.getHMDLODDecreaseFPS
|
||||
* @returns {number}
|
||||
*/
|
||||
Q_INVOKABLE float getHMDLODDecreaseFPS() const;
|
||||
|
||||
/**jsdoc
|
||||
* @function LODManager.getHMDLODIncreaseFPS
|
||||
* @returns {number}
|
||||
*/
|
||||
Q_INVOKABLE float getHMDLODIncreaseFPS() const;
|
||||
|
||||
// User Tweakable LOD Items
|
||||
/**jsdoc
|
||||
* @function LODManager.getLODFeedbackText
|
||||
* @returns {string}
|
||||
*/
|
||||
Q_INVOKABLE QString getLODFeedbackText();
|
||||
|
||||
/**jsdoc
|
||||
* @function LODManager.setOctreeSizeScale
|
||||
* @param {number} sizeScale
|
||||
*/
|
||||
Q_INVOKABLE void setOctreeSizeScale(float sizeScale);
|
||||
|
||||
/**jsdoc
|
||||
* @function LODManager.getOctreeSizeScale
|
||||
* @returns {number}
|
||||
*/
|
||||
Q_INVOKABLE float getOctreeSizeScale() const { return _octreeSizeScale; }
|
||||
|
||||
/**jsdoc
|
||||
* @function LODManager.setBoundaryLevelAdjust
|
||||
* @param {number} boundaryLevelAdjust
|
||||
*/
|
||||
Q_INVOKABLE void setBoundaryLevelAdjust(int boundaryLevelAdjust);
|
||||
|
||||
/**jsdoc
|
||||
* @function LODManager.getBoundaryLevelAdjust
|
||||
* @returns {number}
|
||||
*/
|
||||
Q_INVOKABLE int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; }
|
||||
|
||||
/**jsdoc
|
||||
* @function LODManager.getLODDecreaseFPS
|
||||
* @returns {number}
|
||||
*/
|
||||
Q_INVOKABLE float getLODDecreaseFPS() const;
|
||||
|
||||
/**jsdoc
|
||||
* @function LODManager.getLODIncreaseFPS
|
||||
* @returns {number}
|
||||
*/
|
||||
Q_INVOKABLE float getLODIncreaseFPS() const;
|
||||
|
||||
float getPresentTime() const { return _presentTime; }
|
||||
float getEngineRunTime() const { return _engineRunTime; }
|
||||
float getGPUTime() const { return _gpuTime; }
|
||||
|
@ -88,7 +172,17 @@ public:
|
|||
float getLODLevel() const;
|
||||
|
||||
signals:
|
||||
|
||||
/**jsdoc
|
||||
* @function LODManager.LODIncreased
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void LODIncreased();
|
||||
|
||||
/**jsdoc
|
||||
* @function LODManager.LODDecreased
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void LODDecreased();
|
||||
|
||||
private:
|
||||
|
|
|
@ -810,6 +810,9 @@ Menu::Menu() {
|
|||
scriptEngines->loadScript(defaultScriptsLoc.toString());
|
||||
});
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::VerboseLogging, 0, false,
|
||||
qApp, SLOT(updateVerboseLogging()));
|
||||
|
||||
#if 0 /// -------------- REMOVED FOR NOW --------------
|
||||
addDisabledActionAndSeparator(navigateMenu, "History");
|
||||
QAction* backAction = addActionToQMenuAndActionHash(navigateMenu, MenuOption::Back, 0, addressManager.data(), SLOT(goBack()));
|
||||
|
|
|
@ -142,6 +142,7 @@ namespace MenuOption {
|
|||
const QString Pair = "Pair";
|
||||
const QString PhysicsShowHulls = "Draw Collision Shapes";
|
||||
const QString PhysicsShowOwned = "Highlight Simulation Ownership";
|
||||
const QString VerboseLogging = "Verbose Logging";
|
||||
const QString PipelineWarnings = "Log Render Pipeline Warnings";
|
||||
const QString Preferences = "General...";
|
||||
const QString Quit = "Quit";
|
||||
|
|
|
@ -22,6 +22,9 @@
|
|||
|
||||
#include <DependencyManager.h>
|
||||
|
||||
/**jsdoc
|
||||
* @namespace SpeechRecognizer
|
||||
*/
|
||||
class SpeechRecognizer : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
@ -31,12 +34,39 @@ public:
|
|||
bool getEnabled() const { return _enabled; }
|
||||
|
||||
public slots:
|
||||
|
||||
/**jsdoc
|
||||
* @function SpeechRecognizer.setEnabled
|
||||
* @param {boolean} enabled
|
||||
*/
|
||||
void setEnabled(bool enabled);
|
||||
|
||||
/**jsdoc
|
||||
* @function SpeechRecognizer.addCommand
|
||||
* @param {string} command
|
||||
*/
|
||||
void addCommand(const QString& command);
|
||||
|
||||
/**jsdoc
|
||||
* @function SpeechRecognizer.removeCommand
|
||||
* @param {string} command
|
||||
*/
|
||||
void removeCommand(const QString& command);
|
||||
|
||||
signals:
|
||||
|
||||
/**jsdoc
|
||||
* @function SpeechRecognizer.commandRecognized
|
||||
* @param {string} command
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void commandRecognized(const QString& command);
|
||||
|
||||
/**jsdoc
|
||||
* @function SpeechRecognizer.enabledUpdated
|
||||
* @param {boolean} enabled
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void enabledUpdated(bool enabled);
|
||||
|
||||
protected:
|
||||
|
|
|
@ -25,6 +25,17 @@ class AudioScope : public QObject, public Dependency {
|
|||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
||||
/**jsdoc
|
||||
* The AudioScope API helps control the Audio Scope features in Interface
|
||||
* @namespace AudioScope
|
||||
* @property {number} scopeInput <em>Read-only.</em>
|
||||
* @property {number} scopeOutputLeft <em>Read-only.</em>
|
||||
* @property {number} scopeOutputRight <em>Read-only.</em>
|
||||
* @property {number} triggerInput <em>Read-only.</em>
|
||||
* @property {number} triggerOutputLeft <em>Read-only.</em>
|
||||
* @property {number} triggerOutputRight <em>Read-only.</em>
|
||||
*/
|
||||
|
||||
Q_PROPERTY(QVector<int> scopeInput READ getScopeInput)
|
||||
Q_PROPERTY(QVector<int> scopeOutputLeft READ getScopeOutputLeft)
|
||||
Q_PROPERTY(QVector<int> scopeOutputRight READ getScopeOutputRight)
|
||||
|
@ -40,42 +51,164 @@ public:
|
|||
void reallocateScope(int frames);
|
||||
|
||||
public slots:
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioScope.toggle
|
||||
*/
|
||||
void toggle() { setVisible(!_isEnabled); }
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioScope.setVisible
|
||||
* @param {boolean} visible
|
||||
*/
|
||||
void setVisible(bool visible);
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioScope.getVisible
|
||||
* @returns {boolean}
|
||||
*/
|
||||
bool getVisible() const { return _isEnabled; }
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioScope.togglePause
|
||||
*/
|
||||
void togglePause() { setPause(!_isPaused); }
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioScope.setPause
|
||||
* @param {boolean} paused
|
||||
*/
|
||||
void setPause(bool paused) { _isPaused = paused; emit pauseChanged(); }
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioScope.getPause
|
||||
* @returns {boolean}
|
||||
*/
|
||||
bool getPause() { return _isPaused; }
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioScope.toggleTrigger
|
||||
*/
|
||||
void toggleTrigger() { _autoTrigger = !_autoTrigger; }
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioScope.getAutoTrigger
|
||||
* @returns {boolean}
|
||||
*/
|
||||
bool getAutoTrigger() { return _autoTrigger; }
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioScope.setAutoTrigger
|
||||
* @param {boolean} autoTrigger
|
||||
*/
|
||||
void setAutoTrigger(bool autoTrigger) { _isTriggered = false; _autoTrigger = autoTrigger; }
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioScope.setTriggerValues
|
||||
* @param {number} x
|
||||
* @param {number} y
|
||||
*/
|
||||
void setTriggerValues(int x, int y) { _triggerValues.x = x; _triggerValues.y = y; }
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioScope.setTriggered
|
||||
* @param {boolean} triggered
|
||||
*/
|
||||
void setTriggered(bool triggered) { _isTriggered = triggered; }
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioScope.getTriggered
|
||||
* @returns {boolean}
|
||||
*/
|
||||
bool getTriggered() { return _isTriggered; }
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioScope.getFramesPerSecond
|
||||
* @returns {number}
|
||||
*/
|
||||
float getFramesPerSecond();
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioScope.getFramesPerScope
|
||||
* @returns {number}
|
||||
*/
|
||||
int getFramesPerScope() { return _framesPerScope; }
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioScope.selectAudioScopeFiveFrames
|
||||
*/
|
||||
void selectAudioScopeFiveFrames();
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioScope.selectAudioScopeTwentyFrames
|
||||
*/
|
||||
void selectAudioScopeTwentyFrames();
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioScope.selectAudioScopeFiftyFrames
|
||||
*/
|
||||
void selectAudioScopeFiftyFrames();
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioScope.getScopeInput
|
||||
* @returns {number[]}
|
||||
*/
|
||||
QVector<int> getScopeInput() { return _scopeInputData; };
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioScope.getScopeOutputLeft
|
||||
* @returns {number[]}
|
||||
*/
|
||||
QVector<int> getScopeOutputLeft() { return _scopeOutputLeftData; };
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioScope.getScopeOutputRight
|
||||
* @returns {number[]}
|
||||
*/
|
||||
QVector<int> getScopeOutputRight() { return _scopeOutputRightData; };
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioScope.getTriggerInput
|
||||
* @returns {number[]}
|
||||
*/
|
||||
QVector<int> getTriggerInput() { return _triggerInputData; };
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioScope.getTriggerOutputLeft
|
||||
* @returns {number[]}
|
||||
*/
|
||||
QVector<int> getTriggerOutputLeft() { return _triggerOutputLeftData; };
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioScope.getTriggerOutputRight
|
||||
* @returns {number[]}
|
||||
*/
|
||||
QVector<int> getTriggerOutputRight() { return _triggerOutputRightData; };
|
||||
|
||||
void setLocalEcho(bool serverEcho);
|
||||
/**jsdoc
|
||||
* @function AudioScope.setLocalEcho
|
||||
* @parm {boolean} localEcho
|
||||
*/
|
||||
void setLocalEcho(bool localEcho);
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioScope.setServerEcho
|
||||
* @parm {boolean} serverEcho
|
||||
*/
|
||||
void setServerEcho(bool serverEcho);
|
||||
|
||||
signals:
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioScope.pauseChanged
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void pauseChanged();
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioScope.triggered
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void triggered();
|
||||
|
||||
protected:
|
||||
|
|
|
@ -27,12 +27,17 @@
|
|||
#include "AvatarMotionState.h"
|
||||
#include "MyAvatar.h"
|
||||
|
||||
/**jsdoc
|
||||
* The AvatarManager API has properties and methods which manage Avatars within the same domain.
|
||||
* @namespace AvatarManager
|
||||
*/
|
||||
|
||||
class AvatarManager : public AvatarHashMap {
|
||||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
||||
public:
|
||||
|
||||
/// Registers the script types associated with the avatar manager.
|
||||
static void registerMetaTypes(QScriptEngine* engine);
|
||||
|
||||
|
@ -43,6 +48,11 @@ public:
|
|||
std::shared_ptr<MyAvatar> getMyAvatar() { return _myAvatar; }
|
||||
glm::vec3 getMyAvatarPosition() const { return _myAvatar->getWorldPosition(); }
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarManager.getAvatar
|
||||
* @param {Uuid} avatarID
|
||||
* @returns {AvatarData}
|
||||
*/
|
||||
// Null/Default-constructed QUuids will return MyAvatar
|
||||
Q_INVOKABLE virtual ScriptAvatarData* getAvatar(QUuid avatarID) override { return new ScriptAvatar(getAvatarBySessionID(avatarID)); }
|
||||
|
||||
|
@ -66,24 +76,76 @@ public:
|
|||
void handleChangedMotionStates(const VectorOfMotionStates& motionStates);
|
||||
void handleCollisionEvents(const CollisionEvents& collisionEvents);
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarManager.getAvatarDataRate
|
||||
* @param {Uuid} sessionID
|
||||
* @param {string} [rateName=""]
|
||||
* @returns {number}
|
||||
*/
|
||||
Q_INVOKABLE float getAvatarDataRate(const QUuid& sessionID, const QString& rateName = QString("")) const;
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarManager.getAvatarUpdateRate
|
||||
* @param {Uuid} sessionID
|
||||
* @param {string} [rateName=""]
|
||||
* @returns {number}
|
||||
*/
|
||||
|
||||
Q_INVOKABLE float getAvatarUpdateRate(const QUuid& sessionID, const QString& rateName = QString("")) const;
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarManager.getAvatarSimulationRate
|
||||
* @param {Uuid} sessionID
|
||||
* @param {string} [rateName=""]
|
||||
* @returns {number}
|
||||
*/
|
||||
|
||||
Q_INVOKABLE float getAvatarSimulationRate(const QUuid& sessionID, const QString& rateName = QString("")) const;
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarManager.findRayIntersection
|
||||
* @param {PickRay} ray
|
||||
* @param {Uuid[]} [avatarsToInclude=[]]
|
||||
* @param {Uuid[]} [avatarsToDiscard=[]]
|
||||
* @returns {RayToAvatarIntersectionResult}
|
||||
*/
|
||||
Q_INVOKABLE RayToAvatarIntersectionResult findRayIntersection(const PickRay& ray,
|
||||
const QScriptValue& avatarIdsToInclude = QScriptValue(),
|
||||
const QScriptValue& avatarIdsToDiscard = QScriptValue());
|
||||
/**jsdoc
|
||||
* @function AvatarManager.findRayIntersectionVector
|
||||
* @param {PickRay} ray
|
||||
* @param {Uuid[]} avatarsToInclude
|
||||
* @param {Uuid[]} avatarsToDiscard
|
||||
* @returns {RayToAvatarIntersectionResult}
|
||||
*/
|
||||
Q_INVOKABLE RayToAvatarIntersectionResult findRayIntersectionVector(const PickRay& ray,
|
||||
const QVector<EntityItemID>& avatarsToInclude,
|
||||
const QVector<EntityItemID>& avatarsToDiscard);
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarManager.getAvatarSortCoefficient
|
||||
* @param {string} name
|
||||
* @returns {number}
|
||||
*/
|
||||
// TODO: remove this HACK once we settle on optimal default sort coefficients
|
||||
Q_INVOKABLE float getAvatarSortCoefficient(const QString& name);
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarManager.setAvatarSortCoefficient
|
||||
* @param {string} name
|
||||
* @param {number} value
|
||||
*/
|
||||
Q_INVOKABLE void setAvatarSortCoefficient(const QString& name, const QScriptValue& value);
|
||||
|
||||
float getMyAvatarSendRate() const { return _myAvatarSendRate.rate(); }
|
||||
|
||||
public slots:
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarManager.updateAvatarRenderStatus
|
||||
* @param {boolean} shouldRenderAvatars
|
||||
*/
|
||||
void updateAvatarRenderStatus(bool shouldRenderAvatars);
|
||||
|
||||
private:
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -615,9 +615,15 @@ void Wallet::updateImageProvider() {
|
|||
securityImageProvider->setSecurityImage(_securityImage);
|
||||
|
||||
// inform tablet security image provider
|
||||
QQmlEngine* tabletEngine = DependencyManager::get<TabletScriptingInterface>()->getTablet("com.highfidelity.interface.tablet.system")->getTabletSurface()->getSurfaceContext()->engine();
|
||||
securityImageProvider = reinterpret_cast<SecurityImageProvider*>(tabletEngine->imageProvider(SecurityImageProvider::PROVIDER_NAME));
|
||||
securityImageProvider->setSecurityImage(_securityImage);
|
||||
TabletProxy* tablet = DependencyManager::get<TabletScriptingInterface>()->getTablet("com.highfidelity.interface.tablet.system");
|
||||
if (tablet) {
|
||||
OffscreenQmlSurface* tabletSurface = tablet->getTabletSurface();
|
||||
if (tabletSurface) {
|
||||
QQmlEngine* tabletEngine = tabletSurface->getSurfaceContext()->engine();
|
||||
securityImageProvider = reinterpret_cast<SecurityImageProvider*>(tabletEngine->imageProvider(SecurityImageProvider::PROVIDER_NAME));
|
||||
securityImageProvider->setSecurityImage(_securityImage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Wallet::chooseSecurityImage(const QString& filename) {
|
||||
|
|
|
@ -26,6 +26,11 @@
|
|||
|
||||
#include <trackers/FaceTracker.h>
|
||||
|
||||
/**jsdoc
|
||||
* The FaceTracker API helps manage facial tracking hardware.
|
||||
* @namespace FaceTracker
|
||||
*/
|
||||
|
||||
class DdeFaceTracker : public FaceTracker, public Dependency {
|
||||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
@ -57,7 +62,16 @@ public:
|
|||
void setEyeClosingThreshold(float eyeClosingThreshold);
|
||||
|
||||
public slots:
|
||||
|
||||
/**jsdoc
|
||||
* @function FaceTracker.setEnabled
|
||||
* @param {boolean} enabled
|
||||
*/
|
||||
void setEnabled(bool enabled) override;
|
||||
|
||||
/**jsdoc
|
||||
* @function FaceTracker.calibrate
|
||||
*/
|
||||
void calibrate();
|
||||
|
||||
private slots:
|
||||
|
|
|
@ -109,7 +109,7 @@ public:
|
|||
*
|
||||
* @typedef {Object} Picks.RayPickResult
|
||||
* @property {number} type The intersection type.
|
||||
* @property {bool} intersects If there was a valid intersection (type != INTERSECTED_NONE)
|
||||
* @property {boolean} intersects If there was a valid intersection (type != INTERSECTED_NONE)
|
||||
* @property {Uuid} objectID The ID of the intersected object. Uuid.NULL for the HUD or invalid intersections.
|
||||
* @property {float} distance The distance to the intersection point from the origin of the ray.
|
||||
* @property {Vec3} intersection The intersection point in world-space.
|
||||
|
@ -123,7 +123,7 @@ public:
|
|||
*
|
||||
* @typedef {Object} Picks.StylusPickResult
|
||||
* @property {number} type The intersection type.
|
||||
* @property {bool} intersects If there was a valid intersection (type != INTERSECTED_NONE)
|
||||
* @property {boolean} intersects If there was a valid intersection (type != INTERSECTED_NONE)
|
||||
* @property {Uuid} objectID The ID of the intersected object. Uuid.NULL for the HUD or invalid intersections.
|
||||
* @property {float} distance The distance to the intersection point from the origin of the ray.
|
||||
* @property {Vec3} intersection The intersection point in world-space.
|
||||
|
|
|
@ -34,7 +34,17 @@ void DownloadInfoResultFromScriptValue(const QScriptValue& object, DownloadInfoR
|
|||
|
||||
class AccountServicesScriptingInterface : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* The AccountServices API contains helper functions related to user connectivity
|
||||
*
|
||||
* @namespace AccountServices
|
||||
* @property {string} username <em>Read-only.</em>
|
||||
* @property {boolean} loggedIn <em>Read-only.</em>
|
||||
* @property {string} findableBy
|
||||
* @property {string} metaverseServerURL <em>Read-only.</em>
|
||||
*/
|
||||
|
||||
Q_PROPERTY(QString username READ getUsername NOTIFY myUsernameChanged)
|
||||
Q_PROPERTY(bool loggedIn READ loggedIn NOTIFY loggedInChanged)
|
||||
Q_PROPERTY(QString findableBy READ getFindableBy WRITE setFindableBy NOTIFY findableByChanged)
|
||||
|
@ -48,11 +58,33 @@ public:
|
|||
QUrl getMetaverseServerURL() { return DependencyManager::get<AccountManager>()->getMetaverseServerURL(); }
|
||||
|
||||
public slots:
|
||||
|
||||
/**jsdoc
|
||||
* @function AccountServices.getDownloadInfo
|
||||
* @returns {DownloadInfoResult}
|
||||
*/
|
||||
DownloadInfoResult getDownloadInfo();
|
||||
|
||||
/**jsdoc
|
||||
* @function AccountServices.updateDownloadInfo
|
||||
*/
|
||||
void updateDownloadInfo();
|
||||
|
||||
/**jsdoc
|
||||
* @function AccountServices.isLoggedIn
|
||||
* @returns {boolean}
|
||||
*/
|
||||
bool isLoggedIn();
|
||||
|
||||
/**jsdoc
|
||||
* @function AccountServices.checkAndSignalForAccessToken
|
||||
* @returns {boolean}
|
||||
*/
|
||||
bool checkAndSignalForAccessToken();
|
||||
|
||||
/**jsdoc
|
||||
* @function AccountServices.logOut
|
||||
*/
|
||||
void logOut();
|
||||
|
||||
private slots:
|
||||
|
@ -66,11 +98,46 @@ private slots:
|
|||
void onUsernameChanged(const QString& username);
|
||||
|
||||
signals:
|
||||
|
||||
/**jsdoc
|
||||
* @function AccountServices.connected
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void connected();
|
||||
|
||||
/**jsdoc
|
||||
* @function AccountServices.disconnected
|
||||
* @param {string} reason
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void disconnected(const QString& reason);
|
||||
|
||||
/**jsdoc
|
||||
* @function AccountServices.myUsernameChanged
|
||||
* @param {string} username
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void myUsernameChanged(const QString& username);
|
||||
|
||||
/**jsdoc
|
||||
* @function AccountServices.downloadInfoChanged
|
||||
* @param {} info
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void downloadInfoChanged(DownloadInfoResult info);
|
||||
|
||||
/**jsdoc
|
||||
* @function AccountServices.findableByChanged
|
||||
* @param {string} discoverabilityMode
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void findableByChanged(const QString& discoverabilityMode);
|
||||
|
||||
/**jsdoc
|
||||
* @function AccountServices.loggedInChanged
|
||||
* @param {boolean} loggedIn
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void loggedInChanged(bool loggedIn);
|
||||
|
||||
private:
|
||||
|
|
|
@ -147,7 +147,7 @@ void Audio::setInputVolume(float volume) {
|
|||
}
|
||||
|
||||
float Audio::getInputLevel() const {
|
||||
return resultWithReadLock<bool>([&] {
|
||||
return resultWithReadLock<float>([&] {
|
||||
return _inputLevel;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -25,6 +25,18 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
|
|||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
||||
/**jsdoc
|
||||
* The Audio API features tools to help control audio contexts and settings.
|
||||
*
|
||||
* @namespace Audio
|
||||
* @property {boolean} muted
|
||||
* @property {boolean} noiseReduction
|
||||
* @property {number} inputVolume
|
||||
* @property {number} inputLevel <em>Read-only.</em>
|
||||
* @property {string} context <em>Read-only.</em>
|
||||
* @property {} devices <em>Read-only.</em>
|
||||
*/
|
||||
|
||||
Q_PROPERTY(bool muted READ isMuted WRITE setMuted NOTIFY mutedChanged)
|
||||
Q_PROPERTY(bool noiseReduction READ noiseReductionEnabled WRITE enableNoiseReduction NOTIFY noiseReductionChanged)
|
||||
Q_PROPERTY(float inputVolume READ getInputVolume WRITE setInputVolume NOTIFY inputVolumeChanged)
|
||||
|
@ -49,24 +61,99 @@ public:
|
|||
|
||||
void showMicMeter(bool show);
|
||||
|
||||
/**jsdoc
|
||||
* @function Audio.setInputDevice
|
||||
* @param {} device
|
||||
* @param {boolean} isHMD
|
||||
*/
|
||||
Q_INVOKABLE void setInputDevice(const QAudioDeviceInfo& device, bool isHMD);
|
||||
Q_INVOKABLE void setOutputDevice(const QAudioDeviceInfo& device, bool isHMD);
|
||||
Q_INVOKABLE void setReverb(bool enable);
|
||||
Q_INVOKABLE void setReverbOptions(const AudioEffectOptions* options);
|
||||
|
||||
/**jsdoc
|
||||
* @function Audio.setOutputDevice
|
||||
* @param {} device
|
||||
* @param {boolean} isHMD
|
||||
*/
|
||||
Q_INVOKABLE void setOutputDevice(const QAudioDeviceInfo& device, bool isHMD);
|
||||
|
||||
/**jsdoc
|
||||
* @function Audio.setReverb
|
||||
* @param {boolean} enable
|
||||
*/
|
||||
Q_INVOKABLE void setReverb(bool enable);
|
||||
|
||||
/**jsdoc
|
||||
* @function Audio.setReverbOptions
|
||||
* @param {} options
|
||||
*/
|
||||
Q_INVOKABLE void setReverbOptions(const AudioEffectOptions* options);
|
||||
|
||||
/**jsdoc
|
||||
* @function Audio.startRecording
|
||||
* @param {string} filename
|
||||
* @returns {boolean}
|
||||
*/
|
||||
Q_INVOKABLE bool startRecording(const QString& filename);
|
||||
|
||||
/**jsdoc
|
||||
* @function Audio.stopRecording
|
||||
*/
|
||||
Q_INVOKABLE void stopRecording();
|
||||
|
||||
/**jsdoc
|
||||
* @function Audio.getRecording
|
||||
* @returns {boolean}
|
||||
*/
|
||||
Q_INVOKABLE bool getRecording();
|
||||
|
||||
signals:
|
||||
|
||||
/**jsdoc
|
||||
* @function Audio.nop
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void nop();
|
||||
|
||||
/**jsdoc
|
||||
* @function Audio.mutedChanged
|
||||
* @param {boolean} isMuted
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void mutedChanged(bool isMuted);
|
||||
|
||||
/**jsdoc
|
||||
* @function Audio.noiseReductionChanged
|
||||
* @param {boolean} isEnabled
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void noiseReductionChanged(bool isEnabled);
|
||||
|
||||
/**jsdoc
|
||||
* @function Audio.inputVolumeChanged
|
||||
* @param {number} volume
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void inputVolumeChanged(float volume);
|
||||
|
||||
/**jsdoc
|
||||
* @function Audio.inputLevelChanged
|
||||
* @param {number} level
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void inputLevelChanged(float level);
|
||||
|
||||
/**jsdoc
|
||||
* @function Audio.contextChanged
|
||||
* @param {string} context
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void contextChanged(const QString& context);
|
||||
|
||||
public slots:
|
||||
|
||||
/**jsdoc
|
||||
* @function Audio.onContextChanged
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void onContextChanged();
|
||||
|
||||
private slots:
|
||||
|
|
|
@ -108,11 +108,9 @@ AudioDeviceList::~AudioDeviceList() {
|
|||
// store the selected device
|
||||
foreach(std::shared_ptr<AudioDevice> adevice, _devices) {
|
||||
if (adevice->selectedDesktop) {
|
||||
qDebug() << "Saving Desktop for" << _mode << "name" << adevice->info.deviceName();
|
||||
settingDesktop.set(adevice->info.deviceName());
|
||||
}
|
||||
if (adevice->selectedHMD) {
|
||||
qDebug() << "Saving HMD for" << _mode << "name" << adevice->info.deviceName();
|
||||
settingHMD.set(adevice->info.deviceName());
|
||||
}
|
||||
}
|
||||
|
@ -311,7 +309,6 @@ void AudioDeviceList::onDevicesChanged(const QList<QAudioDeviceInfo>& devices) {
|
|||
}
|
||||
}
|
||||
|
||||
qDebug() << "adding audio device:" << device.display << device.selectedDesktop << device.selectedHMD << _mode;
|
||||
newDevices.push_back(newDevice(device));
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ class ScriptEngine;
|
|||
/**jsdoc
|
||||
* The Controller API provides facilities to interact with computer and controller hardware.
|
||||
*
|
||||
* <h5>Functions:</h5>
|
||||
* <h5>Functions</h5>
|
||||
*
|
||||
* <p>Properties</p>
|
||||
* <ul>
|
||||
|
@ -143,6 +143,61 @@ class ScriptEngine;
|
|||
* <li>{@link Controller.stopInputPlayback|stopInputPlayback}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h5>Entity Methods:</h5>
|
||||
*
|
||||
* <p>The default scripts implement hand controller actions that use {@link Entities.callEntityMethod} to call entity script
|
||||
* methods, if present in the entity being interacted with.</p>
|
||||
*
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Method Name</th><th>Description</th><th>Example</th></tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr>
|
||||
* <td><code>startFarTrigger</code><br /><code>continueFarTrigger</code><br /><code>stopFarTrigger</code></td>
|
||||
* <td>These methods are called when a user is more than 0.3m away from the entity, the entity is triggerable, and the
|
||||
* user starts, continues, or stops squeezing the trigger.</td>
|
||||
* </td>
|
||||
* <td>A light switch that can be toggled on and off from a distance.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><code>startNearTrigger</code><br /><code>continueNearTrigger</code><br /><code>stopNearTrigger</code></td>
|
||||
* <td>These methods are called when a user is less than 0.3m away from the entity, the entity is triggerable, and the
|
||||
* user starts, continues, or stops squeezing the trigger.</td>
|
||||
* <td>A doorbell that can be rung when a user is near.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><code>startDistanceGrab</code><br /><code>continueDistanceGrab</code><br /></td>
|
||||
* <td>These methods are called when a user is more than 0.3m away from the entity, the entity is either cloneable, or
|
||||
* grabbable and not locked, and the user starts or continues to squeeze the trigger.</td>
|
||||
* <td>A comet that emits icy particle trails when a user is dragging it through the sky.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><code>startNearGrab</code><br /><code>continueNearGrab</code><br /></td>
|
||||
* <td>These methods are called when a user is less than 0.3m away from the entity, the entity is either cloneable, or
|
||||
* grabbable and not locked, and the user starts or continues to squeeze the trigger.</td>
|
||||
* <td>A ball that glows when it's being held close.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><code>releaseGrab</code></td>
|
||||
* <td>This method is called when a user releases the trigger when having been either distance or near grabbing an
|
||||
* entity.</td>
|
||||
* <td>Turn off the ball glow or comet trail with the user finishes grabbing it.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><code>startEquip</code><br /><code>continueEquip</code><br /><code>releaseEquip</code></td>
|
||||
* <td>These methods are called when a user starts, continues, or stops equipping an entity.</td>
|
||||
* <td>A glass that stays in the user's hand after the trigger is clicked.</td>
|
||||
* </tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* <p>All the entity methods are called with the following two arguments:</p>
|
||||
* <ul>
|
||||
* <li>The entity ID.</li>
|
||||
* <li>A string, <code>"hand,userID"</code> — where "hand" is <code>"left"</code> or <code>"right"</code>, and "userID"
|
||||
* is the user's {@link MyAvatar|MyAvatar.sessionUUID}.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @namespace Controller
|
||||
*
|
||||
* @property {Controller.Actions} Actions - Predefined actions on Interface and the user's avatar. These can be used as end
|
||||
|
|
|
@ -15,6 +15,11 @@
|
|||
#include <QObject>
|
||||
#include <DependencyManager.h>
|
||||
|
||||
/**jsdoc
|
||||
* The GooglePoly API allows you to interact with Google Poly models direct from inside High Fidelity.
|
||||
* @namespace GooglePoly
|
||||
*/
|
||||
|
||||
class GooglePolyScriptingInterface : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -22,15 +27,75 @@ public:
|
|||
GooglePolyScriptingInterface();
|
||||
|
||||
public slots:
|
||||
|
||||
/**jsdoc
|
||||
* @function GooglePoly.setAPIKey
|
||||
* @param {string} key
|
||||
*/
|
||||
void setAPIKey(const QString& key);
|
||||
|
||||
/**jsdoc
|
||||
* @function GooglePoly.getAssetList
|
||||
* @param {string} keyword
|
||||
* @param {string} category
|
||||
* @param {string} format
|
||||
* @returns {string}
|
||||
*/
|
||||
QString getAssetList(const QString& keyword, const QString& category, const QString& format);
|
||||
|
||||
/**jsdoc
|
||||
* @function GooglePoly.getFBX
|
||||
* @param {string} keyword
|
||||
* @param {string} category
|
||||
* @returns {string}
|
||||
*/
|
||||
QString getFBX(const QString& keyword, const QString& category);
|
||||
|
||||
/**jsdoc
|
||||
* @function GooglePoly.getOBJ
|
||||
* @param {string} keyword
|
||||
* @param {string} category
|
||||
* @returns {string}
|
||||
*/
|
||||
QString getOBJ(const QString& keyword, const QString& category);
|
||||
QString getBlocks(const QString& keyword, const QString& categoryy);
|
||||
|
||||
/**jsdoc
|
||||
* @function GooglePoly.getBlocks
|
||||
* @param {string} keyword
|
||||
* @param {string} category
|
||||
* @returns {string}
|
||||
*/
|
||||
QString getBlocks(const QString& keyword, const QString& category);
|
||||
|
||||
/**jsdoc
|
||||
* @function GooglePoly.getGLTF
|
||||
* @param {string} keyword
|
||||
* @param {string} category
|
||||
* @returns {string}
|
||||
*/
|
||||
QString getGLTF(const QString& keyword, const QString& category);
|
||||
|
||||
/**jsdoc
|
||||
* @function GooglePoly.getGLTF2
|
||||
* @param {string} keyword
|
||||
* @param {string} category
|
||||
* @returns {string}
|
||||
*/
|
||||
QString getGLTF2(const QString& keyword, const QString& category);
|
||||
|
||||
/**jsdoc
|
||||
* @function GooglePoly.getTilt
|
||||
* @param {string} keyword
|
||||
* @param {string} category
|
||||
* @returns {string}
|
||||
*/
|
||||
QString getTilt(const QString& keyword, const QString& category);
|
||||
|
||||
/**jsdoc
|
||||
* @function GooglePoly.getModelInfo
|
||||
* @param {string} input
|
||||
* @returns {string}
|
||||
*/
|
||||
QString getModelInfo(const QString& input);
|
||||
|
||||
private:
|
||||
|
|
|
@ -56,6 +56,19 @@ bool GameplayObjects::removeFromGameplayObjects(const OverlayID& overlayID) {
|
|||
SelectionScriptingInterface::SelectionScriptingInterface() {
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Value</th><th>Description</th></tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td><code>"avatar"</code></td><td></td></tr>
|
||||
* <tr><td><code>"entity"</code></td><td></td></tr>
|
||||
* <tr><td><code>"overlay"</code></td><td></td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef {string} Selection.ItemType
|
||||
*/
|
||||
bool SelectionScriptingInterface::addToSelectedItemsList(const QString& listName, const QString& itemType, const QUuid& id) {
|
||||
if (itemType == "avatar") {
|
||||
return addToGameplayObjects(listName, (QUuid)id);
|
||||
|
@ -255,6 +268,12 @@ void SelectionScriptingInterface::printList(const QString& listName) {
|
|||
}
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* @typedef {object} Selection.SelectedItemsList
|
||||
* @property {Uuid[]} avatars - The IDs of the avatars in the selection.
|
||||
* @property {Uuid[]} entities - The IDs of the entities in the selection.
|
||||
* @property {Uuid[]} overlays - The IDs of the overlays in the selection.
|
||||
*/
|
||||
QVariantMap SelectionScriptingInterface::getSelectedItemsList(const QString& listName) const {
|
||||
QReadLocker lock(&_selectionListsLock);
|
||||
QVariantMap list;
|
||||
|
@ -461,6 +480,20 @@ bool SelectionHighlightStyle::fromVariantMap(const QVariantMap& properties) {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* @typedef {object} Selection.HighlightStyle
|
||||
* @property {Color} outlineUnoccludedColor - Color of the specified highlight region.
|
||||
* @property {Color} outlineOccludedColor - ""
|
||||
* @property {Color} fillUnoccludedColor- ""
|
||||
* @property {Color} fillOccludedColor- ""
|
||||
* @property {number} outlineUnoccludedAlpha - Alpha value ranging from <code>0.0</code> (not visible) to <code>1.0</code>
|
||||
* (fully opaque) for the specified highlight region.
|
||||
* @property {number} outlineOccludedAlpha - ""
|
||||
* @property {number} fillUnoccludedAlpha - ""
|
||||
* @property {number} fillOccludedAlpha - ""
|
||||
* @property {number} outlineWidth - Width of the outline, in pixels.
|
||||
* @property {boolean} isOutlineSmooth - <code>true</code> to enable outline smooth fall-off.
|
||||
*/
|
||||
QVariantMap SelectionHighlightStyle::toVariantMap() const {
|
||||
QVariantMap properties;
|
||||
|
||||
|
|
|
@ -82,6 +82,46 @@ protected:
|
|||
render::HighlightStyle _style;
|
||||
};
|
||||
|
||||
/**jsdoc
|
||||
* The <code>Selection</code> API provides a means of grouping together avatars, entities, and overlays in named lists.
|
||||
* @namespace Selection
|
||||
*
|
||||
* @example <caption>Outline an entity when it is grabbed by a controller.</caption>
|
||||
* // Create a box and copy the following text into the entity's "Script URL" field.
|
||||
* (function () {
|
||||
* print("Starting highlight script...............");
|
||||
* var _this = this;
|
||||
* var prevID = 0;
|
||||
* var listName = "contextOverlayHighlightList";
|
||||
* var listType = "entity";
|
||||
*
|
||||
* _this.startNearGrab = function(entityID){
|
||||
* if (prevID !== entityID) {
|
||||
* Selection.addToSelectedItemsList(listName, listType, entityID);
|
||||
* prevID = entityID;
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* _this.releaseGrab = function(entityID){
|
||||
* if (prevID !== 0) {
|
||||
* Selection.removeFromSelectedItemsList("contextOverlayHighlightList", listType, prevID);
|
||||
* prevID = 0;
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* var cleanup = function(){
|
||||
* Entities.findEntities(MyAvatar.position, 1000).forEach(function(entity) {
|
||||
* try {
|
||||
* Selection.removeListFromMap(listName);
|
||||
* } catch (e) {
|
||||
* print("Error cleaning up.");
|
||||
* }
|
||||
* });
|
||||
* };
|
||||
*
|
||||
* Script.scriptEnding.connect(cleanup);
|
||||
* });
|
||||
*/
|
||||
class SelectionScriptingInterface : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -89,138 +129,120 @@ public:
|
|||
SelectionScriptingInterface();
|
||||
|
||||
/**jsdoc
|
||||
* Query the names of all the selection lists
|
||||
* Get the names of all the selection lists.
|
||||
* @function Selection.getListNames
|
||||
* @return An array of names of all the selection lists
|
||||
* @return {list[]} An array of names of all the selection lists.
|
||||
*/
|
||||
Q_INVOKABLE QStringList getListNames() const;
|
||||
|
||||
/**jsdoc
|
||||
* Removes a named selection from the list of selections.
|
||||
* Delete a named selection list.
|
||||
* @function Selection.removeListFromMap
|
||||
* @param listName {string} name of the selection
|
||||
* @returns {bool} true if the selection existed and was successfully removed.
|
||||
* @param {string} listName - The name of the selection list.
|
||||
* @returns {boolean} <code>true</code> if the selection existed and was successfully removed, otherwise <code>false</code>.
|
||||
*/
|
||||
Q_INVOKABLE bool removeListFromMap(const QString& listName);
|
||||
|
||||
/**jsdoc
|
||||
* Add an item in a selection.
|
||||
* Add an item to a selection list.
|
||||
* @function Selection.addToSelectedItemsList
|
||||
* @param listName {string} name of the selection
|
||||
* @param itemType {string} the type of the item (one of "avatar", "entity" or "overlay")
|
||||
* @param id {EntityID} the Id of the item to add to the selection
|
||||
* @returns {bool} true if the item was successfully added.
|
||||
* @param {string} listName - The name of the selection list to add the item to.
|
||||
* @param {Selection.ItemType} itemType - The type of the item being added.
|
||||
* @param {Uuid} id - The ID of the item to add to the selection.
|
||||
* @returns {boolean} <code>true</code> if the item was successfully added, otherwise <code>false</code>.
|
||||
*/
|
||||
Q_INVOKABLE bool addToSelectedItemsList(const QString& listName, const QString& itemType, const QUuid& id);
|
||||
/**jsdoc
|
||||
* Remove an item from a selection.
|
||||
* Remove an item from a selection list.
|
||||
* @function Selection.removeFromSelectedItemsList
|
||||
* @param listName {string} name of the selection
|
||||
* @param itemType {string} the type of the item (one of "avatar", "entity" or "overlay")
|
||||
* @param id {EntityID} the Id of the item to remove
|
||||
* @returns {bool} true if the item was successfully removed.
|
||||
* @param {string} listName - The name of the selection list to remove the item from.
|
||||
* @param {Selection.ItemType} itemType - The type of the item being removed.
|
||||
* @param {Uuid} id - The ID of the item to remove.
|
||||
* @returns {boolean} <code>true</code> if the item was successfully removed, otherwise <code>false</code>.
|
||||
* <codefalse</code> is returned if the list doesn't contain any data.
|
||||
*/
|
||||
Q_INVOKABLE bool removeFromSelectedItemsList(const QString& listName, const QString& itemType, const QUuid& id);
|
||||
/**jsdoc
|
||||
* Remove all items from a selection.
|
||||
* @function Selection.clearSelectedItemsList
|
||||
* @param listName {string} name of the selection
|
||||
* @returns {bool} true if the item was successfully cleared.
|
||||
* @param {string} listName - The name of the selection list.
|
||||
* @returns {boolean} <code>true</code> if the item was successfully cleared, otherwise <code>false</code>.
|
||||
*/
|
||||
Q_INVOKABLE bool clearSelectedItemsList(const QString& listName);
|
||||
|
||||
/**jsdoc
|
||||
* Prints out the list of avatars, entities and overlays stored in a particular selection.
|
||||
* Print out the list of avatars, entities, and overlays in a selection to the <em>debug log</em> (not the script log).
|
||||
* @function Selection.printList
|
||||
* @param listName {string} name of the selection
|
||||
* @param {string} listName - The name of the selection list.
|
||||
*/
|
||||
Q_INVOKABLE void printList(const QString& listName);
|
||||
|
||||
/**jsdoc
|
||||
* Query the list of avatars, entities and overlays stored in a particular selection.
|
||||
* Get the list of avatars, entities, and overlays stored in a selection list.
|
||||
* @function Selection.getList
|
||||
* @param listName {string} name of the selection
|
||||
* @return a js object describing the content of a selection list with the following properties:
|
||||
* - "entities": [ and array of the entityID of the entities in the selection]
|
||||
* - "avatars": [ and array of the avatarID of the avatars in the selection]
|
||||
* - "overlays": [ and array of the overlayID of the overlays in the selection]
|
||||
* If the list name doesn't exist, the function returns an empty js object with no properties.
|
||||
* @param {string} listName - The name of the selection list.
|
||||
* @return {Selection.SelectedItemsList} The content of a selection list. If the list name doesn't exist, the function
|
||||
* returns an empty object with no properties.
|
||||
*/
|
||||
Q_INVOKABLE QVariantMap getSelectedItemsList(const QString& listName) const;
|
||||
|
||||
/**jsdoc
|
||||
* Query the names of the highlighted selection lists
|
||||
* Get the names of the highlighted selection lists.
|
||||
* @function Selection.getHighlightedListNames
|
||||
* @return An array of names of the selection list currently highlight enabled
|
||||
* @return {string[]} An array of names of the selection list currently highlight enabled.
|
||||
*/
|
||||
Q_INVOKABLE QStringList getHighlightedListNames() const;
|
||||
|
||||
/**jsdoc
|
||||
* Enable highlighting for the named selection.
|
||||
* If the Selection doesn't exist, it will be created.
|
||||
* All objects in the list will be displayed with the highlight effect as specified from the highlightStyle.
|
||||
* The function can be called several times with different values in the style to modify it.
|
||||
*
|
||||
* Enable highlighting for a selection list.
|
||||
* If the selection list doesn't exist, it will be created.
|
||||
* All objects in the list will be displayed with the highlight effect specified.
|
||||
* The function can be called several times with different values in the style to modify it.<br />
|
||||
* Note: This function implicitly calls {@link Selection.enableListToScene}.
|
||||
* @function Selection.enableListHighlight
|
||||
* @param listName {string} name of the selection
|
||||
* @param highlightStyle {jsObject} highlight style fields (see Selection.getListHighlightStyle for a detailed description of the highlightStyle).
|
||||
* @returns {bool} true if the selection was successfully enabled for highlight.
|
||||
*
|
||||
* Note: This function will implicitly call Selection.enableListToScene
|
||||
* @param {string} listName - The name of the selection list.
|
||||
* @param {Selection.HighlightStyle} highlightStyle - The highlight style.
|
||||
* @returns {boolean} true if the selection was successfully enabled for highlight.
|
||||
*/
|
||||
Q_INVOKABLE bool enableListHighlight(const QString& listName, const QVariantMap& highlightStyle);
|
||||
|
||||
/**jsdoc
|
||||
* Disable highlighting for the named selection.
|
||||
* If the Selection doesn't exist or wasn't enabled for highliting then nothing happens simply returning false.
|
||||
*
|
||||
* Disable highlighting for the selection list.
|
||||
* If the selection list doesn't exist or wasn't enabled for highlighting then nothing happens and <code>false</code> is
|
||||
* returned.<br />
|
||||
* Note: This function implicitly calls {@link Selection.disableListToScene}.
|
||||
* @function Selection.disableListHighlight
|
||||
* @param listName {string} name of the selection
|
||||
* @returns {bool} true if the selection was successfully disabled for highlight, false otherwise.
|
||||
*
|
||||
* Note: This function will implicitly call Selection.disableListToScene
|
||||
* @param {string} listName - The name of the selection list.
|
||||
* @returns {boolean} <code>true</code> if the selection was successfully disabled for highlight, otherwise
|
||||
* <code>false</code>.
|
||||
*/
|
||||
Q_INVOKABLE bool disableListHighlight(const QString& listName);
|
||||
/**jsdoc
|
||||
* Enable scene selection for the named selection.
|
||||
* Enable scene selection for the selection list.
|
||||
* If the Selection doesn't exist, it will be created.
|
||||
* All objects in the list will be sent to a scene selection.
|
||||
*
|
||||
* @function Selection.enableListToScene
|
||||
* @param listName {string} name of the selection
|
||||
* @returns {bool} true if the selection was successfully enabled on the scene.
|
||||
* @param {string} listName - The name of the selection list.
|
||||
* @returns {boolean} <code>true</code> if the selection was successfully enabled on the scene, otherwise <code>false</code>.
|
||||
*/
|
||||
Q_INVOKABLE bool enableListToScene(const QString& listName);
|
||||
|
||||
/**jsdoc
|
||||
* Disable scene selection for the named selection.
|
||||
* If the Selection doesn't exist or wasn't enabled on the scene then nothing happens simply returning false.
|
||||
*
|
||||
* If the selection list doesn't exist or wasn't enabled on the scene then nothing happens and <code>false</code> is
|
||||
* returned.
|
||||
* @function Selection.disableListToScene
|
||||
* @param listName {string} name of the selection
|
||||
* @returns {bool} true if the selection was successfully disabled on the scene, false otherwise.
|
||||
* @param {string} listName - The name of the selection list.
|
||||
* @returns {boolean} true if the selection was successfully disabled on the scene, false otherwise.
|
||||
*/
|
||||
Q_INVOKABLE bool disableListToScene(const QString& listName);
|
||||
|
||||
/**jsdoc
|
||||
* Query the highlight style values for the named selection.
|
||||
* If the Selection doesn't exist or hasn't been highlight enabled yet, it will return an empty object.
|
||||
* Otherwise, the jsObject describes the highlight style properties:
|
||||
* - outlineUnoccludedColor: {xColor} Color of the specified highlight region
|
||||
* - outlineOccludedColor: {xColor} "
|
||||
* - fillUnoccludedColor: {xColor} "
|
||||
* - fillOccludedColor: {xColor} "
|
||||
*
|
||||
* - outlineUnoccludedAlpha: {float} Alpha value ranging from 0.0 (not visible) to 1.0 (fully opaque) for the specified highlight region
|
||||
* - outlineOccludedAlpha: {float} "
|
||||
* - fillUnoccludedAlpha: {float} "
|
||||
* - fillOccludedAlpha: {float} "
|
||||
*
|
||||
* - outlineWidth: {float} width of the outline expressed in pixels
|
||||
* - isOutlineSmooth: {bool} true to enable oultine smooth falloff
|
||||
*
|
||||
* Get the highlight style values for the a selection list.
|
||||
* If the selection doesn't exist or hasn't been highlight enabled yet, an empty object is returned.
|
||||
* @function Selection.getListHighlightStyle
|
||||
* @param listName {string} name of the selection
|
||||
* @returns {jsObject} highlight style as described above
|
||||
* @param {string} listName - The name of the selection list.
|
||||
* @returns {Selection.HighlightStyle} highlight style
|
||||
*/
|
||||
Q_INVOKABLE QVariantMap getListHighlightStyle(const QString& listName) const;
|
||||
|
||||
|
@ -232,6 +254,12 @@ public:
|
|||
void onSelectedItemsListChanged(const QString& listName);
|
||||
|
||||
signals:
|
||||
/**jsoc
|
||||
* Triggered when a list's content changes.
|
||||
* @function Selection.selectedItemsListChanged
|
||||
* @param {string} listName - The name of the selection list that changed.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void selectedItemsListChanged(const QString& listName);
|
||||
|
||||
private:
|
||||
|
|
|
@ -87,7 +87,7 @@ public slots:
|
|||
* Display a dialog with the specified message and an "OK" button. The dialog is non-modal; the script continues without
|
||||
* waiting for a user response.
|
||||
* @function Window.alert
|
||||
* @param {string} message="" - The message to display.
|
||||
* @param {string} [message=""] - The message to display.
|
||||
* @example <caption>Display a friendly greeting.</caption>
|
||||
* Window.alert("Welcome!");
|
||||
* print("Script continues without waiting");
|
||||
|
@ -98,7 +98,7 @@ public slots:
|
|||
* Prompt the user to confirm something. Displays a modal dialog with a message plus "Yes" and "No" buttons.
|
||||
* responds.
|
||||
* @function Window.confirm
|
||||
* @param {string} message="" - The question to display.
|
||||
* @param {string} [message=""] - The question to display.
|
||||
* @returns {boolean} <code>true</code> if the user selects "Yes", otherwise <code>false</code>.
|
||||
* @example <caption>Ask the user a question requiring a yes/no answer.</caption>
|
||||
* var answer = Window.confirm("Are you sure?");
|
||||
|
@ -128,8 +128,8 @@ public slots:
|
|||
* buttons. A {@link Window.promptTextChanged|promptTextChanged} signal is emitted when the user OKs the dialog; no signal
|
||||
* is emitted if the user cancels the dialog.
|
||||
* @function Window.promptAsync
|
||||
* @param {string} message - The question to display.
|
||||
* @param {string} defaultText - The default answer text.
|
||||
* @param {string} [message=""] - The question to display.
|
||||
* @param {string} [defaultText=""] - The default answer text.
|
||||
* @example <caption>Ask the user a question requiring a text answer without waiting for the answer.</caption>
|
||||
* function onPromptTextChanged(text) {
|
||||
* print("User answer: " + text);
|
||||
|
@ -144,8 +144,8 @@ public slots:
|
|||
/**jsdoc
|
||||
* Prompt the user to choose a directory. Displays a modal dialog that navigates the directory tree.
|
||||
* @function Window.browseDir
|
||||
* @param {string} title="" - The title to display at the top of the dialog.
|
||||
* @param {string} directory="" - The initial directory to start browsing at.
|
||||
* @param {string} [title=""] - The title to display at the top of the dialog.
|
||||
* @param {string} [directory=""] - The initial directory to start browsing at.
|
||||
* @returns {string} The path of the directory if one is chosen, otherwise <code>null</code>.
|
||||
* @example <caption>Ask the user to choose a directory.</caption>
|
||||
* var directory = Window.browseDir("Select Directory", Paths.resources);
|
||||
|
@ -158,8 +158,8 @@ public slots:
|
|||
* {@link Window.browseDirChanged|browseDirChanged} signal is emitted when a directory is chosen; no signal is emitted if
|
||||
* the user cancels the dialog.
|
||||
* @function Window.browseDirAsync
|
||||
* @param {string} title="" - The title to display at the top of the dialog.
|
||||
* @param {string} directory="" - The initial directory to start browsing at.
|
||||
* @param {string} [title=""] - The title to display at the top of the dialog.
|
||||
* @param {string} [directory=""] - The initial directory to start browsing at.
|
||||
* @example <caption>Ask the user to choose a directory without waiting for the answer.</caption>
|
||||
* function onBrowseDirChanged(directory) {
|
||||
* print("Directory: " + directory);
|
||||
|
@ -174,9 +174,9 @@ public slots:
|
|||
/**jsdoc
|
||||
* Prompt the user to choose a file. Displays a modal dialog that navigates the directory tree.
|
||||
* @function Window.browse
|
||||
* @param {string} title="" - The title to display at the top of the dialog.
|
||||
* @param {string} directory="" - The initial directory to start browsing at.
|
||||
* @param {string} nameFilter="" - The types of files to display. Examples: <code>"*.json"</code> and
|
||||
* @param {string} [title=""] - The title to display at the top of the dialog.
|
||||
* @param {string} [directory=""] - The initial directory to start browsing at.
|
||||
* @param {string} [nameFilter=""] - The types of files to display. Examples: <code>"*.json"</code> and
|
||||
* <code>"Images (*.png *.jpg *.svg)"</code>. All files are displayed if a filter isn't specified.
|
||||
* @returns {string} The path and name of the file if one is chosen, otherwise <code>null</code>.
|
||||
* @example <caption>Ask the user to choose an image file.</caption>
|
||||
|
@ -190,9 +190,9 @@ public slots:
|
|||
* {@link Window.browseChanged|browseChanged} signal is emitted when a file is chosen; no signal is emitted if the user
|
||||
* cancels the dialog.
|
||||
* @function Window.browseAsync
|
||||
* @param {string} title="" - The title to display at the top of the dialog.
|
||||
* @param {string} directory="" - The initial directory to start browsing at.
|
||||
* @param {string} nameFilter="" - The types of files to display. Examples: <code>"*.json"</code> and
|
||||
* @param {string} [title=""] - The title to display at the top of the dialog.
|
||||
* @param {string} [directory=""] - The initial directory to start browsing at.
|
||||
* @param {string} [nameFilter=""] - The types of files to display. Examples: <code>"*.json"</code> and
|
||||
* <code>"Images (*.png *.jpg *.svg)"</code>. All files are displayed if a filter isn't specified.
|
||||
* @example <caption>Ask the user to choose an image file without waiting for the answer.</caption>
|
||||
* function onBrowseChanged(filename) {
|
||||
|
@ -209,9 +209,9 @@ public slots:
|
|||
* Prompt the user to specify the path and name of a file to save to. Displays a model dialog that navigates the directory
|
||||
* tree and allows the user to type in a file name.
|
||||
* @function Window.save
|
||||
* @param {string} title="" - The title to display at the top of the dialog.
|
||||
* @param {string} directory="" - The initial directory to start browsing at.
|
||||
* @param {string} nameFilter="" - The types of files to display. Examples: <code>"*.json"</code> and
|
||||
* @param {string} [title=""] - The title to display at the top of the dialog.
|
||||
* @param {string} [directory=""] - The initial directory to start browsing at.
|
||||
* @param {string} [nameFilter=""] - The types of files to display. Examples: <code>"*.json"</code> and
|
||||
* <code>"Images (*.png *.jpg *.svg)"</code>. All files are displayed if a filter isn't specified.
|
||||
* @returns {string} The path and name of the file if one is specified, otherwise <code>null</code>. If a single file type
|
||||
* is specified in the nameFilter, that file type extension is automatically appended to the result when appropriate.
|
||||
|
@ -226,9 +226,9 @@ public slots:
|
|||
* directory tree and allows the user to type in a file name. A {@link Window.saveFileChanged|saveFileChanged} signal is
|
||||
* emitted when a file is specified; no signal is emitted if the user cancels the dialog.
|
||||
* @function Window.saveAsync
|
||||
* @param {string} title="" - The title to display at the top of the dialog.
|
||||
* @param {string} directory="" - The initial directory to start browsing at.
|
||||
* @param {string} nameFilter="" - The types of files to display. Examples: <code>"*.json"</code> and
|
||||
* @param {string} [title=""] - The title to display at the top of the dialog.
|
||||
* @param {string} [directory=""] - The initial directory to start browsing at.
|
||||
* @param {string} [nameFilter=""] - The types of files to display. Examples: <code>"*.json"</code> and
|
||||
* <code>"Images (*.png *.jpg *.svg)"</code>. All files are displayed if a filter isn't specified.
|
||||
* @example <caption>Ask the user to specify a file to save to without waiting for an answer.</caption>
|
||||
* function onSaveFileChanged(filename) {
|
||||
|
@ -245,9 +245,9 @@ public slots:
|
|||
* Prompt the user to choose an Asset Server item. Displays a modal dialog that navigates the tree of assets on the Asset
|
||||
* Server.
|
||||
* @function Window.browseAssets
|
||||
* @param {string} title="" - The title to display at the top of the dialog.
|
||||
* @param {string} directory="" - The initial directory to start browsing at.
|
||||
* @param {string} nameFilter="" - The types of files to display. Examples: <code>"*.json"</code> and
|
||||
* @param {string} [title=""] - The title to display at the top of the dialog.
|
||||
* @param {string} [directory=""] - The initial directory to start browsing at.
|
||||
* @param {string} [nameFilter=""] - The types of files to display. Examples: <code>"*.json"</code> and
|
||||
* <code>"Images (*.png *.jpg *.svg)"</code>. All files are displayed if a filter isn't specified.
|
||||
* @returns {string} The path and name of the asset if one is chosen, otherwise <code>null</code>.
|
||||
* @example <caption>Ask the user to select an FBX asset.</caption>
|
||||
|
@ -261,9 +261,9 @@ public slots:
|
|||
* Asset Server. A {@link Window.assetsDirChanged|assetsDirChanged} signal is emitted when an asset is chosen; no signal is
|
||||
* emitted if the user cancels the dialog.
|
||||
* @function Window.browseAssetsAsync
|
||||
* @param {string} title="" - The title to display at the top of the dialog.
|
||||
* @param {string} directory="" - The initial directory to start browsing at.
|
||||
* @param {string} nameFilter="" - The types of files to display. Examples: <code>"*.json"</code> and
|
||||
* @param {string} [title=""] - The title to display at the top of the dialog.
|
||||
* @param {string} [directory=""] - The initial directory to start browsing at.
|
||||
* @param {string} [nameFilter=""] - The types of files to display. Examples: <code>"*.json"</code> and
|
||||
* <code>"Images (*.png *.jpg *.svg)"</code>. All files are displayed if a filter isn't specified.
|
||||
* @example
|
||||
* function onAssetsDirChanged(asset) {
|
||||
|
@ -280,7 +280,7 @@ public slots:
|
|||
* Open the Asset Browser dialog. If a file to upload is specified, the user is prompted to enter the folder and name to
|
||||
* map the file to on the asset server.
|
||||
* @function Window.showAssetServer
|
||||
* @param {string} uploadFile="" - The path and name of a file to upload to the asset server.
|
||||
* @param {string} [uploadFile=""] - The path and name of a file to upload to the asset server.
|
||||
* @example <caption>Upload a file to the asset server.</caption>
|
||||
* var filename = Window.browse("Select File to Add to Asset Server", Paths.resources);
|
||||
* print("File: " + filename);
|
||||
|
@ -317,14 +317,14 @@ public slots:
|
|||
* NOTE: to provide a non-default value - all previous parameters must be provided.
|
||||
* General > Snapshots.
|
||||
* @function Window.takeSnapshot
|
||||
* @param {boolean} notify=true - This value is passed on through the {@link Window.stillSnapshotTaken|stillSnapshotTaken}
|
||||
* @param {boolean} [notify=true] - This value is passed on through the {@link Window.stillSnapshotTaken|stillSnapshotTaken}
|
||||
* signal.
|
||||
* @param {boolean} includeAnimated=false - If <code>true</code>, a moving image is captured as an animated GIF in addition
|
||||
* @param {boolean} [includeAnimated=false] - If <code>true</code>, a moving image is captured as an animated GIF in addition
|
||||
* to a still image.
|
||||
* @param {number} aspectRatio=0 - The width/height ratio of the snapshot required. If the value is <code>0</code> the
|
||||
* @param {number} [aspectRatio=0] - The width/height ratio of the snapshot required. If the value is <code>0</code> the
|
||||
* full resolution is used (window dimensions in desktop mode; HMD display dimensions in HMD mode), otherwise one of the
|
||||
* dimensions is adjusted in order to match the aspect ratio.
|
||||
* @param {string} filename="" - If this parameter is not given, the image will be saved as 'hifi-snap-by-<user name>-YYYY-MM-DD_HH-MM-SS'.
|
||||
* @param {string} [filename=""] - If this parameter is not given, the image will be saved as 'hifi-snap-by-<user name>-YYYY-MM-DD_HH-MM-SS'.
|
||||
* If this parameter is <code>""</code> then the image will be saved as ".jpg".
|
||||
* Otherwise, the image will be saved to this filename, with an appended ".jpg".
|
||||
*
|
||||
|
@ -358,7 +358,7 @@ public slots:
|
|||
* Takes a still snapshot of the current view from the secondary camera that can be set up through the {@link Render} API.
|
||||
* NOTE: to provide a non-default value - all previous parameters must be provided.
|
||||
* @function Window.takeSecondaryCameraSnapshot
|
||||
* @param {string} filename="" - If this parameter is not given, the image will be saved as 'hifi-snap-by-<user name>-YYYY-MM-DD_HH-MM-SS'.
|
||||
* @param {string} [filename=""] - If this parameter is not given, the image will be saved as 'hifi-snap-by-<user name>-YYYY-MM-DD_HH-MM-SS'.
|
||||
* If this parameter is <code>""</code> then the image will be saved as ".jpg".
|
||||
* Otherwise, the image will be saved to this filename, with an appended ".jpg".
|
||||
*
|
||||
|
@ -397,7 +397,7 @@ public slots:
|
|||
* has been prepared.
|
||||
* @function Window.shareSnapshot
|
||||
* @param {string} path - The path and name of the image file to share.
|
||||
* @param {string} href="" - The metaverse location where the snapshot was taken.
|
||||
* @param {string} [href=""] - The metaverse location where the snapshot was taken.
|
||||
*/
|
||||
void shareSnapshot(const QString& path, const QUrl& href = QUrl(""));
|
||||
|
||||
|
|
|
@ -58,9 +58,8 @@ void AddressBarDialog::loadHome() {
|
|||
qDebug() << "Called LoadHome";
|
||||
auto locationBookmarks = DependencyManager::get<LocationBookmarks>();
|
||||
QString homeLocation = locationBookmarks->addressForBookmark(LocationBookmarks::HOME_BOOKMARK);
|
||||
const QString DEFAULT_HOME_LOCATION = "localhost";
|
||||
if (homeLocation == "") {
|
||||
homeLocation = DEFAULT_HOME_LOCATION;
|
||||
homeLocation = DEFAULT_HIFI_ADDRESS;
|
||||
}
|
||||
DependencyManager::get<AddressManager>()->handleLookupString(homeLocation);
|
||||
}
|
||||
|
|
|
@ -23,6 +23,15 @@ class AvatarInputs : public QObject {
|
|||
Q_OBJECT
|
||||
HIFI_QML_DECL
|
||||
|
||||
/**jsdoc
|
||||
* API to help manage your Avatar's input
|
||||
* @namespace AvatarInputs
|
||||
* @property {boolean} cameraEnabled <em>Read-only.</em>
|
||||
* @property {boolean} cameraMuted <em>Read-only.</em>
|
||||
* @property {boolean} isHMD <em>Read-only.</em>
|
||||
* @property {boolean} showAudioTools
|
||||
*/
|
||||
|
||||
AI_PROPERTY(bool, cameraEnabled, false)
|
||||
AI_PROPERTY(bool, cameraMuted, false)
|
||||
AI_PROPERTY(bool, isHMD, false)
|
||||
|
@ -31,22 +40,64 @@ class AvatarInputs : public QObject {
|
|||
|
||||
public:
|
||||
static AvatarInputs* getInstance();
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarInputs.loudnessToAudioLevel
|
||||
* @param {number} loudness
|
||||
* @returns {number}
|
||||
*/
|
||||
Q_INVOKABLE float loudnessToAudioLevel(float loudness);
|
||||
|
||||
AvatarInputs(QObject* parent = nullptr);
|
||||
void update();
|
||||
bool showAudioTools() const { return _showAudioTools; }
|
||||
|
||||
public slots:
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarInputs.setShowAudioTools
|
||||
* @param {boolean} showAudioTools
|
||||
*/
|
||||
void setShowAudioTools(bool showAudioTools);
|
||||
|
||||
signals:
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarInputs.cameraEnabledChanged
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void cameraEnabledChanged();
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarInputs.cameraMutedChanged
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void cameraMutedChanged();
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarInputs.isHMDChanged
|
||||
* @returns {Signal}
|
||||
*/
|
||||
|
||||
void isHMDChanged();
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarInputs.showAudioToolsChanged
|
||||
* @param {boolean} show
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void showAudioToolsChanged(bool show);
|
||||
|
||||
protected:
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarInputs.resetSensors
|
||||
*/
|
||||
Q_INVOKABLE void resetSensors();
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarInputs.toggleCameraMute
|
||||
*/
|
||||
Q_INVOKABLE void toggleCameraMute();
|
||||
|
||||
private:
|
||||
|
|
|
@ -80,7 +80,6 @@ void DialogsManager::showFeed() {
|
|||
}
|
||||
|
||||
void DialogsManager::setDomainConnectionFailureVisibility(bool visible) {
|
||||
qDebug() << "DialogsManager::setDomainConnectionFailureVisibility: visible" << visible;
|
||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||
auto tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
|
||||
|
||||
|
|
|
@ -65,14 +65,10 @@ const QString Web3DOverlay::TYPE = "web3d";
|
|||
const QString Web3DOverlay::QML = "Web3DOverlay.qml";
|
||||
|
||||
static auto qmlSurfaceDeleter = [](OffscreenQmlSurface* surface) {
|
||||
AbstractViewStateInterface::instance()->postLambdaEvent([surface] {
|
||||
if (AbstractViewStateInterface::instance()->isAboutToQuit()) {
|
||||
// WebEngineView may run other threads (wasapi), so they must be deleted for a clean shutdown
|
||||
// if the application has already stopped its event loop, delete must be explicit
|
||||
delete surface;
|
||||
} else {
|
||||
surface->deleteLater();
|
||||
}
|
||||
AbstractViewStateInterface::instance()->sendLambdaEvent([surface] {
|
||||
// WebEngineView may run other threads (wasapi), so they must be deleted for a clean shutdown
|
||||
// if the application has already stopped its event loop, delete must be explicit
|
||||
delete surface;
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -334,16 +330,20 @@ void Web3DOverlay::render(RenderArgs* args) {
|
|||
renderTransform.setScale(1.0f);
|
||||
batch.setModelTransform(renderTransform);
|
||||
|
||||
// Turn off jitter for these entities
|
||||
batch.pushProjectionJitter();
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
if (color.a < OPAQUE_ALPHA_THRESHOLD) {
|
||||
geometryCache->bindWebBrowserProgram(batch, true);
|
||||
} else {
|
||||
geometryCache->bindWebBrowserProgram(batch);
|
||||
}
|
||||
|
||||
vec2 halfSize = vec2(size.x, size.y) / 2.0f;
|
||||
geometryCache->renderQuad(batch, halfSize * -1.0f, halfSize, vec2(0), vec2(1), color, _geometryId);
|
||||
batch.popProjectionJitter(); // Restore jitter
|
||||
batch.setResourceTexture(0, nullptr); // restore default white color after me
|
||||
|
||||
}
|
||||
|
||||
Transform Web3DOverlay::evalRenderTransform() {
|
||||
|
|
|
@ -588,6 +588,7 @@ void AnimExpression::evalUnaryMinus(const AnimVariantMap& map, std::stack<OpCode
|
|||
PUSH(false);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpCode::Int:
|
||||
PUSH(-rhs.intVal);
|
||||
|
|
|
@ -29,18 +29,67 @@ class AnimationCache : public ResourceCache, public Dependency {
|
|||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
||||
/**jsdoc
|
||||
* @namespace AnimationCache
|
||||
* @augments ResourceCache
|
||||
*/
|
||||
|
||||
public:
|
||||
|
||||
// Properties are copied over from ResourceCache (see ResourceCache.h for reason).
|
||||
|
||||
/**jsdoc
|
||||
* Returns animation resource for particular animation
|
||||
* API to manage animation cache resources.
|
||||
* @namespace AnimationCache
|
||||
*
|
||||
* @property {number} numTotal - Total number of total resources. <em>Read-only.</em>
|
||||
* @property {number} numCached - Total number of cached resource. <em>Read-only.</em>
|
||||
* @property {number} sizeTotal - Size in bytes of all resources. <em>Read-only.</em>
|
||||
* @property {number} sizeCached - Size in bytes of all cached resources. <em>Read-only.</em>
|
||||
*/
|
||||
|
||||
// Functions are copied over from ResourceCache (see ResourceCache.h for reason).
|
||||
|
||||
/**jsdoc
|
||||
* Get the list of all resource URLs.
|
||||
* @function AnimationCache.getResourceList
|
||||
* @return {string[]}
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* @function AnimationCache.dirty
|
||||
* @returns {Signal}
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* @function AnimationCache.updateTotalSize
|
||||
* @param {number} deltaSize
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* @function AnimationCache.prefetch
|
||||
* @param {string} url
|
||||
* @param {object} extra
|
||||
* @returns {object}
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* Asynchronously loads a resource from the specified URL and returns it.
|
||||
* @function AnimationCache.getResource
|
||||
* @param {string} url - URL of the resource to load.
|
||||
* @param {string} [fallback=""] - Fallback URL if load of the desired URL fails.
|
||||
* @param {} [extra=null]
|
||||
* @return {Resource}
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* Prefetches a resource.
|
||||
* @function AnimationCache.prefetch
|
||||
* @param {string} url - URL of the resource to prefetch.
|
||||
* @return {Resource}
|
||||
*/
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* Returns animation resource for particular animation.
|
||||
* @function AnimationCache.getAnimation
|
||||
* @param url {string} url to load
|
||||
* @return {Resource} animation
|
||||
* @param {string} url - URL to load.
|
||||
* @returns {Resource} animation
|
||||
*/
|
||||
Q_INVOKABLE AnimationPointer getAnimation(const QString& url) { return getAnimation(QUrl(url)); }
|
||||
Q_INVOKABLE AnimationPointer getAnimation(const QUrl& url);
|
||||
|
|
|
@ -38,24 +38,137 @@ class MixedProcessedAudioStream;
|
|||
|
||||
class AudioStreamStatsInterface : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
/**jsdoc
|
||||
* @class AudioStats.AudioStreamStats
|
||||
* @property {number} lossRate <em>Read-only.</em>
|
||||
* @property {number} lossCount <em>Read-only.</em>
|
||||
* @property {number} lossRateWindow <em>Read-only.</em>
|
||||
* @property {number} lossCountWindow <em>Read-only.</em>
|
||||
* @property {number} framesDesired <em>Read-only.</em>
|
||||
* @property {number} framesAvailable <em>Read-only.</em>
|
||||
* @property {number} framesAvailableAvg <em>Read-only.</em>
|
||||
* @property {number} unplayedMsMax <em>Read-only.</em>
|
||||
* @property {number} starveCount <em>Read-only.</em>
|
||||
* @property {number} lastStarveDurationCount <em>Read-only.</em>
|
||||
* @property {number} dropCount <em>Read-only.</em>
|
||||
* @property {number} overflowCount <em>Read-only.</em>
|
||||
* @property {number} timegapMsMax <em>Read-only.</em>
|
||||
* @property {number} timegapMsAvg <em>Read-only.</em>
|
||||
* @property {number} timegapMsMaxWindow <em>Read-only.</em>
|
||||
* @property {number} timegapMsAvgWindow <em>Read-only.</em>
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioStats.AudioStreamStats.lossRateChanged
|
||||
* @param {number} lossRate
|
||||
* @returns {Signal}
|
||||
*/
|
||||
AUDIO_PROPERTY(float, lossRate)
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioStats.AudioStreamStats.lossCountChanged
|
||||
* @param {number} lossCount
|
||||
* @returns {Signal}
|
||||
*/
|
||||
AUDIO_PROPERTY(float, lossCount)
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioStats.AudioStreamStats.lossRateWindowChanged
|
||||
* @param {number} lossRateWindow
|
||||
* @returns {Signal}
|
||||
*/
|
||||
AUDIO_PROPERTY(float, lossRateWindow)
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioStats.AudioStreamStats.lossCountWindowChanged
|
||||
* @param {number} lossCountWindow
|
||||
* @returns {Signal}
|
||||
*/
|
||||
AUDIO_PROPERTY(float, lossCountWindow)
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioStats.AudioStreamStats.framesDesiredChanged
|
||||
* @param {number} framesDesired
|
||||
* @returns {Signal}
|
||||
*/
|
||||
AUDIO_PROPERTY(int, framesDesired)
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioStats.AudioStreamStats.framesAvailableChanged
|
||||
* @param {number} framesAvailable
|
||||
* @returns {Signal}
|
||||
*/
|
||||
AUDIO_PROPERTY(int, framesAvailable)
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioStats.AudioStreamStats.framesAvailableAvgChanged
|
||||
* @param {number} framesAvailableAvg
|
||||
* @returns {Signal}
|
||||
*/
|
||||
AUDIO_PROPERTY(int, framesAvailableAvg)
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioStats.AudioStreamStats.unplayedMsMaxChanged
|
||||
* @param {number} unplayedMsMax
|
||||
* @returns {Signal}
|
||||
*/
|
||||
AUDIO_PROPERTY(float, unplayedMsMax)
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioStats.AudioStreamStats.starveCountChanged
|
||||
* @param {number} starveCount
|
||||
* @returns {Signal}
|
||||
*/
|
||||
AUDIO_PROPERTY(int, starveCount)
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioStats.AudioStreamStats.lastStarveDurationCountChanged
|
||||
* @param {number} lastStarveDurationCount
|
||||
* @returns {Signal}
|
||||
*/
|
||||
AUDIO_PROPERTY(int, lastStarveDurationCount)
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioStats.AudioStreamStats.dropCountChanged
|
||||
* @param {number} dropCount
|
||||
* @returns {Signal}
|
||||
*/
|
||||
AUDIO_PROPERTY(int, dropCount)
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioStats.AudioStreamStats.overflowCountChanged
|
||||
* @param {number} overflowCount
|
||||
* @returns {Signal}
|
||||
*/
|
||||
AUDIO_PROPERTY(int, overflowCount)
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioStats.AudioStreamStats.timegapMsMaxChanged
|
||||
* @param {number} timegapMsMax
|
||||
* @returns {Signal}
|
||||
*/
|
||||
AUDIO_PROPERTY(quint64, timegapMsMax)
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioStats.AudioStreamStats.timegapMsAvgChanged
|
||||
* @param {number} timegapMsAvg
|
||||
* @returns {Signal}
|
||||
*/
|
||||
AUDIO_PROPERTY(quint64, timegapMsAvg)
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioStats.AudioStreamStats.timegapMsMaxWindowChanged
|
||||
* @param {number} timegapMsMaxWindow
|
||||
* @returns {Signal}
|
||||
*/
|
||||
AUDIO_PROPERTY(quint64, timegapMsMaxWindow)
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioStats.AudioStreamStats.timegapMsAvgWindowChanged
|
||||
* @param {number} timegapMsAvgWindow
|
||||
* @returns {Signal}
|
||||
*/
|
||||
AUDIO_PROPERTY(quint64, timegapMsAvgWindow)
|
||||
|
||||
public:
|
||||
|
@ -68,19 +181,84 @@ private:
|
|||
|
||||
class AudioStatsInterface : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
/**jsdoc
|
||||
* Audio stats from the client.
|
||||
* @namespace AudioStats
|
||||
* @property {number} pingMs <em>Read-only.</em>
|
||||
* @property {number} inputReadMsMax <em>Read-only.</em>
|
||||
* @property {number} inputUnplayedMsMax <em>Read-only.</em>
|
||||
* @property {number} outputUnplayedMsMax <em>Read-only.</em>
|
||||
* @property {number} sentTimegapMsMax <em>Read-only.</em>
|
||||
* @property {number} sentTimegapMsAvg <em>Read-only.</em>
|
||||
* @property {number} sentTimegapMsMaxWindow <em>Read-only.</em>
|
||||
* @property {number} sentTimegapMsAvgWindow <em>Read-only.</em>
|
||||
* @property {AudioStats.AudioStreamStats} clientStream <em>Read-only.</em>
|
||||
* @property {AudioStats.AudioStreamStats} mixerStream <em>Read-only.</em>
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioStats.pingMsChanged
|
||||
* @param {number} pingMs
|
||||
* @returns {Signal}
|
||||
*/
|
||||
AUDIO_PROPERTY(float, pingMs);
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioStats.inputReadMsMaxChanged
|
||||
* @param {number} inputReadMsMax
|
||||
* @returns {Signal}
|
||||
*/
|
||||
AUDIO_PROPERTY(float, inputReadMsMax);
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioStats.inputUnplayedMsMaxChanged
|
||||
* @param {number} inputUnplayedMsMax
|
||||
* @returns {Signal}
|
||||
*/
|
||||
AUDIO_PROPERTY(float, inputUnplayedMsMax);
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioStats.outputUnplayedMsMaxChanged
|
||||
* @param {number} outputUnplayedMsMax
|
||||
* @returns {Signal}
|
||||
*/
|
||||
AUDIO_PROPERTY(float, outputUnplayedMsMax);
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioStats.sentTimegapMsMaxChanged
|
||||
* @param {number} sentTimegapMsMax
|
||||
* @returns {Signal}
|
||||
*/
|
||||
AUDIO_PROPERTY(quint64, sentTimegapMsMax);
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioStats.sentTimegapMsAvgChanged
|
||||
* @param {number} sentTimegapMsAvg
|
||||
* @returns {Signal}
|
||||
*/
|
||||
AUDIO_PROPERTY(quint64, sentTimegapMsAvg);
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioStats.sentTimegapMsMaxWindowChanged
|
||||
* @param {number} sentTimegapMsMaxWindow
|
||||
* @returns {Signal}
|
||||
*/
|
||||
AUDIO_PROPERTY(quint64, sentTimegapMsMaxWindow);
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioStats.sentTimegapMsAvgWindowChanged
|
||||
* @param {number} sentTimegapMsAvgWindow
|
||||
* @returns {Signal}
|
||||
*/
|
||||
AUDIO_PROPERTY(quint64, sentTimegapMsAvgWindow);
|
||||
|
||||
Q_PROPERTY(AudioStreamStatsInterface* mixerStream READ getMixerStream NOTIFY mixerStreamChanged);
|
||||
Q_PROPERTY(AudioStreamStatsInterface* clientStream READ getClientStream NOTIFY clientStreamChanged);
|
||||
|
||||
// FIXME: The injectorStreams property isn't available in JavaScript but the notification signal is.
|
||||
Q_PROPERTY(QObject* injectorStreams READ getInjectorStreams NOTIFY injectorStreamsChanged);
|
||||
|
||||
public:
|
||||
|
@ -97,8 +275,23 @@ public:
|
|||
void updateInjectorStreams(const QHash<QUuid, AudioStreamStats>& stats);
|
||||
|
||||
signals:
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioStats.mixerStreamChanged
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void mixerStreamChanged();
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioStats.clientStreamChanged
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void clientStreamChanged();
|
||||
|
||||
/**jsdoc
|
||||
* @function AudioStats.injectorStreamsChanged
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void injectorStreamsChanged();
|
||||
|
||||
private:
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
// Inline functions to implement audio dynamics processing
|
||||
//
|
||||
|
||||
#include <stddef.h>
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
|
|
@ -150,6 +150,7 @@ int InboundAudioStream::parseData(ReceivedMessage& message) {
|
|||
|
||||
// fall through to OnTime case
|
||||
}
|
||||
// FALLTHRU
|
||||
case SequenceNumberStats::OnTime: {
|
||||
// Packet is on time; parse its data to the ringbuffer
|
||||
if (message.getType() == PacketType::SilentAudioFrame
|
||||
|
|
|
@ -22,6 +22,67 @@ class SoundCache : public ResourceCache, public Dependency {
|
|||
SINGLETON_DEPENDENCY
|
||||
|
||||
public:
|
||||
|
||||
// Properties are copied over from ResourceCache (see ResourceCache.h for reason).
|
||||
|
||||
/**jsdoc
|
||||
* API to manage sound cache resources.
|
||||
* @namespace SoundCache
|
||||
*
|
||||
* @property {number} numTotal - Total number of total resources. <em>Read-only.</em>
|
||||
* @property {number} numCached - Total number of cached resource. <em>Read-only.</em>
|
||||
* @property {number} sizeTotal - Size in bytes of all resources. <em>Read-only.</em>
|
||||
* @property {number} sizeCached - Size in bytes of all cached resources. <em>Read-only.</em>
|
||||
*/
|
||||
|
||||
|
||||
// Functions are copied over from ResourceCache (see ResourceCache.h for reason).
|
||||
|
||||
/**jsdoc
|
||||
* Get the list of all resource URLs.
|
||||
* @function SoundCache.getResourceList
|
||||
* @return {string[]}
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* @function SoundCache.dirty
|
||||
* @returns {Signal}
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* @function SoundCache.updateTotalSize
|
||||
* @param {number} deltaSize
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* @function SoundCache.prefetch
|
||||
* @param {string} url
|
||||
* @param {object} extra
|
||||
* @returns {object}
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* Asynchronously loads a resource from the specified URL and returns it.
|
||||
* @function SoundCache.getResource
|
||||
* @param {string} url - URL of the resource to load.
|
||||
* @param {string} [fallback=""] - Fallback URL if load of the desired URL fails.
|
||||
* @param {} [extra=null]
|
||||
* @return {Resource}
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* Prefetches a resource.
|
||||
* @function SoundCache.prefetch
|
||||
* @param {string} url - URL of the resource to prefetch.
|
||||
* @return {Resource}
|
||||
*/
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* @function SoundCache.getSound
|
||||
* @param {string} url
|
||||
* @returns {object}
|
||||
*/
|
||||
Q_INVOKABLE SharedSoundPointer getSound(const QUrl& url);
|
||||
protected:
|
||||
virtual QSharedPointer<Resource> createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <unordered_map>
|
||||
|
||||
AutoUpdater::AutoUpdater() {
|
||||
#if defined Q_OS_WIN32
|
||||
|
@ -43,63 +44,114 @@ void AutoUpdater::parseLatestVersionData() {
|
|||
QNetworkReply* sender = qobject_cast<QNetworkReply*>(QObject::sender());
|
||||
|
||||
QXmlStreamReader xml(sender);
|
||||
|
||||
struct InstallerURLs {
|
||||
QString full;
|
||||
QString clientOnly;
|
||||
};
|
||||
|
||||
int version;
|
||||
int version { 0 };
|
||||
QString downloadUrl;
|
||||
QString releaseTime;
|
||||
QString releaseNotes;
|
||||
QString commitSha;
|
||||
QString pullRequestNumber;
|
||||
|
||||
while (!xml.atEnd() && !xml.hasError()) {
|
||||
if (xml.name().toString() == "project" &&
|
||||
xml.attributes().hasAttribute("name") &&
|
||||
xml.attributes().value("name").toString() == "interface") {
|
||||
xml.readNext();
|
||||
|
||||
while (!xml.atEnd() && !xml.hasError() && xml.name().toString() != "project") {
|
||||
if (xml.name().toString() == "platform" &&
|
||||
while (xml.readNextStartElement()) {
|
||||
if (xml.name() == "projects") {
|
||||
while (xml.readNextStartElement()) {
|
||||
if (xml.name().toString() == "project" &&
|
||||
xml.attributes().hasAttribute("name") &&
|
||||
xml.attributes().value("name").toString() == _operatingSystem) {
|
||||
xml.readNext();
|
||||
while (!xml.atEnd() && !xml.hasError() &&
|
||||
xml.name().toString() != "platform") {
|
||||
|
||||
if (xml.name().toString() == "build" && xml.tokenType() != QXmlStreamReader::EndElement) {
|
||||
xml.readNext();
|
||||
version = xml.readElementText().toInt();
|
||||
xml.readNext();
|
||||
downloadUrl = xml.readElementText();
|
||||
xml.readNext();
|
||||
releaseTime = xml.readElementText();
|
||||
xml.readNext();
|
||||
if (xml.name().toString() == "notes" && xml.tokenType() != QXmlStreamReader::EndElement) {
|
||||
xml.readNext();
|
||||
while (!xml.atEnd() && !xml.hasError() && xml.name().toString() != "notes") {
|
||||
if (xml.name().toString() == "note" && xml.tokenType() != QXmlStreamReader::EndElement) {
|
||||
releaseNotes = releaseNotes + "\n" + xml.readElementText();
|
||||
xml.attributes().value("name").toString() == "interface") {
|
||||
|
||||
while (xml.readNextStartElement()) {
|
||||
|
||||
if (xml.name().toString() == "platform" &&
|
||||
xml.attributes().hasAttribute("name") &&
|
||||
xml.attributes().value("name").toString() == _operatingSystem) {
|
||||
|
||||
while (xml.readNextStartElement()) {
|
||||
if (xml.name() == "build") {
|
||||
QHash<QString, InstallerURLs> campaignInstallers;
|
||||
|
||||
while (xml.readNextStartElement()) {
|
||||
if (xml.name() == "version") {
|
||||
version = xml.readElementText().toInt();
|
||||
} else if (xml.name() == "url") {
|
||||
downloadUrl = xml.readElementText();
|
||||
} else if (xml.name() == "installers") {
|
||||
while (xml.readNextStartElement()) {
|
||||
QString campaign = xml.name().toString();
|
||||
QString full;
|
||||
QString clientOnly;
|
||||
while (xml.readNextStartElement()) {
|
||||
if (xml.name() == "full") {
|
||||
full = xml.readElementText();
|
||||
} else if (xml.name() == "client_only") {
|
||||
clientOnly = xml.readElementText();
|
||||
} else {
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
campaignInstallers[campaign] = { full, clientOnly };
|
||||
}
|
||||
} else if (xml.name() == "timestamp") {
|
||||
releaseTime = xml.readElementText();
|
||||
} else if (xml.name() == "notes") {
|
||||
while (xml.readNextStartElement()) {
|
||||
if (xml.name() == "note") {
|
||||
releaseNotes = releaseNotes + "\n" + xml.readElementText();
|
||||
} else {
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else if (xml.name() == "sha") {
|
||||
commitSha = xml.readElementText();
|
||||
} else if (xml.name() == "pull_request") {
|
||||
pullRequestNumber = xml.readElementText();
|
||||
} else {
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
xml.readNext();
|
||||
|
||||
static const QString DEFAULT_INSTALLER_CAMPAIGN_NAME = "standard";
|
||||
for (auto& campaign : { _installerCampaign, DEFAULT_INSTALLER_CAMPAIGN_NAME }) {
|
||||
auto it = campaignInstallers.find(campaign);
|
||||
if (it != campaignInstallers.end()) {
|
||||
auto& urls = *it;
|
||||
if (_installerType == InstallerType::CLIENT_ONLY) {
|
||||
if (!urls.clientOnly.isEmpty()) {
|
||||
downloadUrl = urls.clientOnly;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (!urls.full.isEmpty()) {
|
||||
downloadUrl = urls.full;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
appendBuildData(version, downloadUrl, releaseTime, releaseNotes, pullRequestNumber);
|
||||
releaseNotes = "";
|
||||
} else {
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
xml.readNext();
|
||||
commitSha = xml.readElementText();
|
||||
xml.readNext();
|
||||
pullRequestNumber = xml.readElementText();
|
||||
appendBuildData(version, downloadUrl, releaseTime, releaseNotes, pullRequestNumber);
|
||||
releaseNotes = "";
|
||||
} else {
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
|
||||
xml.readNext();
|
||||
}
|
||||
} else {
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
xml.readNext();
|
||||
}
|
||||
|
||||
} else {
|
||||
xml.readNext();
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
sender->deleteLater();
|
||||
emit latestVersionDataParsed();
|
||||
}
|
||||
|
|
|
@ -36,10 +36,17 @@ class AutoUpdater : public QObject, public Dependency {
|
|||
|
||||
public:
|
||||
AutoUpdater();
|
||||
|
||||
enum class InstallerType {
|
||||
CLIENT_ONLY = 0,
|
||||
FULL
|
||||
};
|
||||
|
||||
void checkForUpdate();
|
||||
const QMap<int, QMap<QString, QString>>& getBuildData() { return _builds; }
|
||||
void performAutoUpdate(int version);
|
||||
void setInstallerType(InstallerType type) { _installerType = type; }
|
||||
void setInstallerCampaign(QString campaign) { _installerCampaign = campaign; }
|
||||
|
||||
signals:
|
||||
void latestVersionDataParsed();
|
||||
|
@ -49,6 +56,8 @@ signals:
|
|||
private:
|
||||
QMap<int, QMap<QString, QString>> _builds;
|
||||
QString _operatingSystem;
|
||||
InstallerType _installerType { InstallerType::FULL };
|
||||
QString _installerCampaign { "" };
|
||||
|
||||
void getLatestVersionData();
|
||||
void downloadUpdateVersion(int version);
|
||||
|
|
|
@ -57,15 +57,7 @@ using AvatarPhysicsCallback = std::function<void(uint32_t)>;
|
|||
class Avatar : public AvatarData, public scriptable::ModelProvider {
|
||||
Q_OBJECT
|
||||
|
||||
/**jsdoc
|
||||
* An avatar is representation of yourself or another user. The Avatar API can be used to query or manipulate the avatar of a user.
|
||||
* NOTE: Avatar extends AvatarData, see those namespace for more properties/methods.
|
||||
*
|
||||
* @namespace Avatar
|
||||
* @augments AvatarData
|
||||
*
|
||||
* @property skeletonOffset {Vec3} can be used to apply a translation offset between the avatar's position and the registration point of the 3d model.
|
||||
*/
|
||||
// This property has JSDoc in MyAvatar.h.
|
||||
Q_PROPERTY(glm::vec3 skeletonOffset READ getSkeletonOffset WRITE setSkeletonOffset)
|
||||
|
||||
public:
|
||||
|
@ -128,14 +120,25 @@ public:
|
|||
virtual int getJointIndex(const QString& name) const override;
|
||||
virtual QStringList getJointNames() const override;
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.getDefaultJointRotation
|
||||
* @param {number} index
|
||||
* @returns {Quat}
|
||||
*/
|
||||
Q_INVOKABLE virtual glm::quat getDefaultJointRotation(int index) const;
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.getDefaultJointTranslation
|
||||
* @param {number} index
|
||||
* @returns {Vec3}
|
||||
*/
|
||||
Q_INVOKABLE virtual glm::vec3 getDefaultJointTranslation(int index) const;
|
||||
|
||||
/**jsdoc
|
||||
* Provides read only access to the default joint rotations in avatar coordinates.
|
||||
* The default pose of the avatar is defined by the position and orientation of all bones
|
||||
* in the avatar's model file. Typically this is a t-pose.
|
||||
* @function Avatar.getAbsoluteDefaultJointRotationInObjectFrame
|
||||
* in the avatar's model file. Typically this is a T-pose.
|
||||
* @function MyAvatar.getAbsoluteDefaultJointRotationInObjectFrame
|
||||
* @param index {number} index number
|
||||
* @returns {Quat} The rotation of this joint in avatar coordinates.
|
||||
*/
|
||||
|
@ -144,8 +147,8 @@ public:
|
|||
/**jsdoc
|
||||
* Provides read only access to the default joint translations in avatar coordinates.
|
||||
* The default pose of the avatar is defined by the position and orientation of all bones
|
||||
* in the avatar's model file. Typically this is a t-pose.
|
||||
* @function Avatar.getAbsoluteDefaultJointTranslationInObjectFrame
|
||||
* in the avatar's model file. Typically this is a T-pose.
|
||||
* @function MyAvatar.getAbsoluteDefaultJointTranslationInObjectFrame
|
||||
* @param index {number} index number
|
||||
* @returns {Vec3} The position of this joint in avatar coordinates.
|
||||
*/
|
||||
|
@ -170,14 +173,65 @@ public:
|
|||
|
||||
virtual void applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetration) { }
|
||||
|
||||
/**jsdoc
|
||||
* Set the offset applied to the current avatar. The offset adjusts the position that the avatar is rendered. For example,
|
||||
* with an offset of <code>{ x: 0, y: 0.1, z: 0 }</code>, your avatar will appear to be raised off the ground slightly.
|
||||
* @function MyAvatar.setSkeletonOffset
|
||||
* @param {Vec3} offset - The skeleton offset to set.
|
||||
* @example <caption>Raise your avatar off the ground a little.</caption>
|
||||
* // Raise your avatar off the ground a little.
|
||||
* MyAvatar.setSkeletonOffset({ x: 0, y: 0.1: z: 0 });
|
||||
*
|
||||
* // Restore its offset after 5s.
|
||||
* Script.setTimeout(function () {
|
||||
* MyAvatar.setSkeletonOffset(Vec3.ZERO);
|
||||
* }, 5000);
|
||||
*/
|
||||
Q_INVOKABLE void setSkeletonOffset(const glm::vec3& offset);
|
||||
|
||||
/**jsdoc
|
||||
* Get the offset applied to the current avatar. The offset adjusts the position that the avatar is rendered. For example,
|
||||
* with an offset of <code>{ x: 0, y: 0.1, z: 0 }</code>, your avatar will appear to be raised off the ground slightly.
|
||||
* @function MyAvatar.getSkeletonOffset
|
||||
* @returns {Vec3} The current skeleton offset.
|
||||
* @example <caption>Report your avatar's current skeleton offset.</caption>
|
||||
* print(JSON.stringify(MyAvatar.getSkeletonOffset());
|
||||
*/
|
||||
Q_INVOKABLE glm::vec3 getSkeletonOffset() { return _skeletonOffset; }
|
||||
|
||||
virtual glm::vec3 getSkeletonPosition() const;
|
||||
|
||||
/**jsdoc
|
||||
* Get the position of a joint in the current avatar.
|
||||
* @function MyAvatar.getJointPosition
|
||||
* @param {number} index - The index of the joint.
|
||||
* @returns {Vec3} The position of the joint in world coordinates.
|
||||
*/
|
||||
Q_INVOKABLE glm::vec3 getJointPosition(int index) const;
|
||||
|
||||
/**jsdoc
|
||||
* Get the position of a joint in the current avatar.
|
||||
* @function MyAvatar.getJointPosition
|
||||
* @param {string} name - The name of the joint.
|
||||
* @returns {Vec3} The position of the joint in world coordinates.
|
||||
* @example <caption>Report the position of your avatar's hips.</caption>
|
||||
* print(JSON.stringify(MyAvatar.getJointPosition("Hips")));
|
||||
*/
|
||||
Q_INVOKABLE glm::vec3 getJointPosition(const QString& name) const;
|
||||
|
||||
/**jsdoc
|
||||
* Get the position of the current avatar's neck in world coordinates.
|
||||
* @function MyAvatar.getNeckPosition
|
||||
* @returns {Vec3} The position of the neck in world coordinates.
|
||||
* @example <caption>Report the position of your avatar's neck.</caption>
|
||||
* print(JSON.stringify(MyAvatar.getNeckPosition()));
|
||||
*/
|
||||
Q_INVOKABLE glm::vec3 getNeckPosition() const;
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.getAcceleration
|
||||
* @returns {Vec3}
|
||||
*/
|
||||
Q_INVOKABLE glm::vec3 getAcceleration() const { return _acceleration; }
|
||||
|
||||
/// Scales a world space position vector relative to the avatar position and scale
|
||||
|
@ -201,24 +255,47 @@ public:
|
|||
void setPositionViaScript(const glm::vec3& position) override;
|
||||
void setOrientationViaScript(const glm::quat& orientation) override;
|
||||
|
||||
// these call through to the SpatiallyNestable versions, but they are here to expose these to javascript.
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.getParentID
|
||||
* @returns {Uuid}
|
||||
*/
|
||||
// This calls through to the SpatiallyNestable versions, but is here to expose these to JavaScript.
|
||||
Q_INVOKABLE virtual const QUuid getParentID() const override { return SpatiallyNestable::getParentID(); }
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.setParentID
|
||||
* @param {Uuid} parentID
|
||||
*/
|
||||
// This calls through to the SpatiallyNestable versions, but is here to expose these to JavaScript.
|
||||
Q_INVOKABLE virtual void setParentID(const QUuid& parentID) override;
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.getParentJointIndex
|
||||
* @returns {number}
|
||||
*/
|
||||
// This calls through to the SpatiallyNestable versions, but is here to expose these to JavaScript.
|
||||
Q_INVOKABLE virtual quint16 getParentJointIndex() const override { return SpatiallyNestable::getParentJointIndex(); }
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.setParentJointIndex
|
||||
* @param {number} parentJointIndex
|
||||
*/
|
||||
// This calls through to the SpatiallyNestable versions, but is here to expose these to JavaScript.
|
||||
Q_INVOKABLE virtual void setParentJointIndex(quint16 parentJointIndex) override;
|
||||
|
||||
/**jsdoc
|
||||
* Information about a single joint in an Avatar's skeleton hierarchy.
|
||||
* @typedef Avatar.SkeletonJoint
|
||||
* @property {string} name - name of joint
|
||||
* @property {number} index - joint index
|
||||
* @property {number} parentIndex - index of this joint's parent (-1 if no parent)
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* Returns an array of joints, where each joint is an object containing name, index and parentIndex fields.
|
||||
* @function Avatar.getSkeleton
|
||||
* @returns {Avatar.SkeletonJoint[]} returns a list of information about each joint in this avatar's skeleton.
|
||||
* Returns an array of joints, where each joint is an object containing name, index, and parentIndex fields.
|
||||
* @function MyAvatar.getSkeleton
|
||||
* @returns {MyAvatar.SkeletonJoint[]} A list of information about each joint in this avatar's skeleton.
|
||||
*/
|
||||
/**jsdoc
|
||||
* Information about a single joint in an Avatar's skeleton hierarchy.
|
||||
* @typedef MyAvatar.SkeletonJoint
|
||||
* @property {string} name - Joint name.
|
||||
* @property {number} index - Joint index.
|
||||
* @property {number} parentIndex - Index of this joint's parent (-1 if no parent).
|
||||
*/
|
||||
Q_INVOKABLE QList<QVariant> getSkeleton();
|
||||
|
||||
|
@ -235,6 +312,11 @@ public:
|
|||
void setTargetScale(float targetScale) override;
|
||||
float getTargetScale() const { return _targetScale; }
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.getSimulationRate
|
||||
* @param {string} [rateName=""]
|
||||
* @returns {number}
|
||||
*/
|
||||
Q_INVOKABLE float getSimulationRate(const QString& rateName = QString("")) const;
|
||||
|
||||
bool hasNewJointData() const { return _hasNewJointData; }
|
||||
|
@ -256,6 +338,7 @@ public:
|
|||
bool isFading() const { return _isFading; }
|
||||
void updateFadingStatus(render::ScenePointer scene);
|
||||
|
||||
// JSDoc is in AvatarData.h.
|
||||
Q_INVOKABLE virtual float getEyeHeight() const override;
|
||||
|
||||
// returns eye height of avatar in meters, ignoring avatar scale.
|
||||
|
@ -282,16 +365,57 @@ public slots:
|
|||
|
||||
// FIXME - these should be migrated to use Pose data instead
|
||||
// thread safe, will return last valid palm from cache
|
||||
|
||||
/**jsdoc
|
||||
* Get the position of the left palm in world coordinates.
|
||||
* @function MyAvatar.getLeftPalmPosition
|
||||
* @returns {Vec3} The position of the left palm in world coordinates.
|
||||
* @example <caption>Report the position of your avatar's left palm.</caption>
|
||||
* print(JSON.stringify(MyAvatar.getLeftPalmPosition()));
|
||||
*/
|
||||
glm::vec3 getLeftPalmPosition() const;
|
||||
|
||||
/**jsdoc
|
||||
* Get the rotation of the left palm in world coordinates.
|
||||
* @function MyAvatar.getLeftPalmRotation
|
||||
* @returns {Vec3} The rotation of the left palm in world coordinates.
|
||||
* @example <caption>Report the rotation of your avatar's left palm.</caption>
|
||||
* print(JSON.stringify(MyAvatar.getLeftPalmRotation()));
|
||||
*/
|
||||
glm::quat getLeftPalmRotation() const;
|
||||
/**jsdoc
|
||||
* Get the position of the right palm in world coordinates.
|
||||
* @function MyAvatar.getRightPalmPosition
|
||||
* @returns {Vec3} The position of the right palm in world coordinates.
|
||||
* @example <caption>Report the position of your avatar's right palm.</caption>
|
||||
* print(JSON.stringify(MyAvatar.getRightPalmPosition()));
|
||||
*/
|
||||
glm::vec3 getRightPalmPosition() const;
|
||||
|
||||
/**jsdoc
|
||||
* Get the rotation of the right palm in world coordinates.
|
||||
* @function MyAvatar.getRightPalmRotation
|
||||
* @returns {Vec3} The rotation of the right palm in world coordinates.
|
||||
* @example <caption>Report the rotation of your avatar's right palm.</caption>
|
||||
* print(JSON.stringify(MyAvatar.getRightPalmRotation()));
|
||||
*/
|
||||
glm::quat getRightPalmRotation() const;
|
||||
|
||||
// hooked up to Model::setURLFinished signal
|
||||
void setModelURLFinished(bool success);
|
||||
|
||||
// hooked up to Model::rigReady & rigReset signals
|
||||
/**jsdoc
|
||||
* @function MyAvatar.rigReady
|
||||
* @returns {Signal}
|
||||
*/
|
||||
// Hooked up to Model::rigReady signal
|
||||
void rigReady();
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.rigReset
|
||||
* @returns {Signal}
|
||||
*/
|
||||
// Jooked up to Model::rigReset signal
|
||||
void rigReset();
|
||||
|
||||
protected:
|
||||
|
|
|
@ -2362,6 +2362,15 @@ glm::vec3 AvatarData::getAbsoluteJointTranslationInObjectFrame(int index) const
|
|||
return glm::vec3();
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* @typedef MyAvatar.AttachmentData
|
||||
* @property {string} modelUrl
|
||||
* @property {string} jointName
|
||||
* @property {Vec3} translation
|
||||
* @property {Vec3} rotation
|
||||
* @property {number} scale
|
||||
* @property {boolean} soft
|
||||
*/
|
||||
QVariant AttachmentData::toVariant() const {
|
||||
QVariantMap result;
|
||||
result["modelUrl"] = modelURL;
|
||||
|
|
|
@ -353,6 +353,7 @@ public:
|
|||
class AvatarData : public QObject, public SpatiallyNestable {
|
||||
Q_OBJECT
|
||||
|
||||
// The following properties have JSDoc in MyAvatar.h.
|
||||
Q_PROPERTY(glm::vec3 position READ getWorldPosition WRITE setPositionViaScript)
|
||||
Q_PROPERTY(float scale READ getTargetScale WRITE setTargetScale)
|
||||
Q_PROPERTY(float density READ getDensity)
|
||||
|
@ -505,7 +506,7 @@ public:
|
|||
/**jsdoc
|
||||
* returns the minimum scale allowed for this avatar in the current domain.
|
||||
* This value can change as the user changes avatars or when changing domains.
|
||||
* @function AvatarData.getDomainMinScale
|
||||
* @function MyAvatar.getDomainMinScale
|
||||
* @returns {number} minimum scale allowed for this avatar in the current domain.
|
||||
*/
|
||||
Q_INVOKABLE float getDomainMinScale() const;
|
||||
|
@ -513,7 +514,7 @@ public:
|
|||
/**jsdoc
|
||||
* returns the maximum scale allowed for this avatar in the current domain.
|
||||
* This value can change as the user changes avatars or when changing domains.
|
||||
* @function AvatarData.getDomainMaxScale
|
||||
* @function MyAvatar.getDomainMaxScale
|
||||
* @returns {number} maximum scale allowed for this avatar in the current domain.
|
||||
*/
|
||||
Q_INVOKABLE float getDomainMaxScale() const;
|
||||
|
@ -529,16 +530,16 @@ public:
|
|||
/**jsdoc
|
||||
* Provides read only access to the current eye height of the avatar.
|
||||
* This height is only an estimate and might be incorrect for avatars that are missing standard joints.
|
||||
* @function AvatarData.getEyeHeight
|
||||
* @returns {number} eye height of avatar in meters
|
||||
* @function MyAvatar.getEyeHeight
|
||||
* @returns {number} Eye height of avatar in meters.
|
||||
*/
|
||||
Q_INVOKABLE virtual float getEyeHeight() const { return _targetScale * getUnscaledEyeHeight(); }
|
||||
|
||||
/**jsdoc
|
||||
* Provides read only access to the current height of the avatar.
|
||||
* This height is only an estimate and might be incorrect for avatars that are missing standard joints.
|
||||
* @function AvatarData.getHeight
|
||||
* @returns {number} height of avatar in meters
|
||||
* @function MyAvatar.getHeight
|
||||
* @returns {number} Height of avatar in meters.
|
||||
*/
|
||||
Q_INVOKABLE virtual float getHeight() const;
|
||||
|
||||
|
@ -547,49 +548,372 @@ public:
|
|||
void setDomainMinimumHeight(float domainMinimumHeight);
|
||||
void setDomainMaximumHeight(float domainMaximumHeight);
|
||||
|
||||
// Hand State
|
||||
/**jsdoc
|
||||
* @function MyAvatar.setHandState
|
||||
* @param {string} state
|
||||
*/
|
||||
Q_INVOKABLE void setHandState(char s) { _handState = s; }
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.getHandState
|
||||
* @returns {string}
|
||||
*/
|
||||
Q_INVOKABLE char getHandState() const { return _handState; }
|
||||
|
||||
const QVector<JointData>& getRawJointData() const { return _jointData; }
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.setRawJointData
|
||||
* @param {JointData[]} data
|
||||
*/
|
||||
Q_INVOKABLE void setRawJointData(QVector<JointData> data);
|
||||
|
||||
/**jsdoc
|
||||
* Set a specific joint's rotation and position relative to its parent.
|
||||
* <p>Setting joint data completely overrides/replaces all motion from the default animation system including inverse
|
||||
* kinematics, but just for the specified joint. So for example, if you were to procedurally manipulate the finger joints,
|
||||
* the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate
|
||||
* joints in the inverse kinematics chain, the inverse kinematics might not function as you expect. For example, if you set
|
||||
* the rotation of the elbow, the hand inverse kinematics position won't end up in the right place.</p>
|
||||
* @function MyAvatar.setJointData
|
||||
* @param {number} index - The index of the joint.
|
||||
* @param {Quat} rotation - The rotation of the joint relative to its parent.
|
||||
* @param {Vec3} translation - The translation of the joint relative to its parent.
|
||||
* @example <caption>Set your avatar to it's default T-pose for a while.<br />
|
||||
* <img alt="Avatar in T-pose" src="https://docs.highfidelity.com/user/pages/06.api-reference/25.myavatar/t-pose.png" />
|
||||
* </caption>
|
||||
* // Set all joint translations and rotations to defaults.
|
||||
* var i, length, rotation, translation;
|
||||
* for (i = 0, length = MyAvatar.getJointNames().length; i < length; i++) {
|
||||
* rotation = MyAvatar.getDefaultJointRotation(i);
|
||||
* translation = MyAvatar.getDefaultJointTranslation(i);
|
||||
* MyAvatar.setJointData(i, rotation, translation);
|
||||
* }
|
||||
*
|
||||
* // Restore your avatar's motion after 5s.
|
||||
* Script.setTimeout(function () {
|
||||
* MyAvatar.clearJointsData();
|
||||
* }, 5000);
|
||||
*/
|
||||
Q_INVOKABLE virtual void setJointData(int index, const glm::quat& rotation, const glm::vec3& translation);
|
||||
|
||||
/**jsdoc
|
||||
* Set a specific joint's rotation relative to its parent.
|
||||
* <p>Setting joint data completely overrides/replaces all motion from the default animation system including inverse
|
||||
* kinematics, but just for the specified joint. So for example, if you were to procedurally manipulate the finger joints,
|
||||
* the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate
|
||||
* joints in the inverse kinematics chain, the inverse kinematics might not function as you expect. For example, if you set
|
||||
* the rotation of the elbow, the hand inverse kinematics position won't end up in the right place.</p>
|
||||
* @function MyAvatar.setJointRotation
|
||||
* @param {number} index - The index of the joint.
|
||||
* @param {Quat} rotation - The rotation of the joint relative to its parent.
|
||||
*/
|
||||
Q_INVOKABLE virtual void setJointRotation(int index, const glm::quat& rotation);
|
||||
|
||||
/**jsdoc
|
||||
* Set a specific joint's translation relative to its parent.
|
||||
* <p>Setting joint data completely overrides/replaces all motion from the default animation system including inverse
|
||||
* kinematics, but just for the specified joint. So for example, if you were to procedurally manipulate the finger joints,
|
||||
* the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate
|
||||
* joints in the inverse kinematics chain, the inverse kinematics might not function as you expect. For example, if you set
|
||||
* the rotation of the elbow, the hand inverse kinematics position won't end up in the right place.</p>
|
||||
* @function MyAvatar.setJointTranslation
|
||||
* @param {number} index - The index of the joint.
|
||||
* @param {Vec3} translation - The translation of the joint relative to its parent.
|
||||
*/
|
||||
Q_INVOKABLE virtual void setJointTranslation(int index, const glm::vec3& translation);
|
||||
|
||||
/**jsdoc
|
||||
* Clear joint translations and rotations set by script for a specific joint. This restores all motion from the default
|
||||
* animation system including inverse kinematics for that joint.
|
||||
* <p>Note: This is slightly faster than the function variation that specifies the joint name.</p>
|
||||
* @function MyAvatar.clearJointData
|
||||
* @param {number} index - The index of the joint.
|
||||
*/
|
||||
Q_INVOKABLE virtual void clearJointData(int index);
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.isJointDataValid
|
||||
* @param {number} index
|
||||
* @returns {boolean}
|
||||
*/
|
||||
Q_INVOKABLE bool isJointDataValid(int index) const;
|
||||
|
||||
/**jsdoc
|
||||
* Get the rotation of a joint relative to its parent. For information on the joint hierarchy used, see
|
||||
* <a href="https://docs.highfidelity.com/create-and-explore/avatars/avatar-standards">Avatar Standards</a>.
|
||||
* @function MyAvatar.getJointRotation
|
||||
* @param {number} index - The index of the joint.
|
||||
* @returns {Quat} The rotation of the joint relative to its parent.
|
||||
*/
|
||||
Q_INVOKABLE virtual glm::quat getJointRotation(int index) const;
|
||||
|
||||
/**jsdoc
|
||||
* Get the translation of a joint relative to its parent. For information on the joint hierarchy used, see
|
||||
* <a href="https://docs.highfidelity.com/create-and-explore/avatars/avatar-standards">Avatar Standards</a>.
|
||||
* @function MyAvatar.getJointTranslation
|
||||
* @param {number} index - The index of the joint.
|
||||
* @returns {Vec3} The translation of the joint relative to its parent.
|
||||
*/
|
||||
Q_INVOKABLE virtual glm::vec3 getJointTranslation(int index) const;
|
||||
|
||||
/**jsdoc
|
||||
* Set a specific joint's rotation and position relative to its parent.
|
||||
* <p>Setting joint data completely overrides/replaces all motion from the default animation system including inverse
|
||||
* kinematics, but just for the specified joint. So for example, if you were to procedurally manipulate the finger joints,
|
||||
* the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate
|
||||
* joints in the inverse kinematics chain, the inverse kinematics might not function as you expect. For example, if you set
|
||||
* the rotation of the elbow, the hand inverse kinematics position won't end up in the right place.</p>
|
||||
* @function MyAvatar.setJointData
|
||||
* @param {string} name - The name of the joint.
|
||||
* @param {Quat} rotation - The rotation of the joint relative to its parent.
|
||||
* @param {Vec3} translation - The translation of the joint relative to its parent.
|
||||
*/
|
||||
Q_INVOKABLE virtual void setJointData(const QString& name, const glm::quat& rotation, const glm::vec3& translation);
|
||||
|
||||
/**jsdoc
|
||||
* Set a specific joint's rotation relative to its parent.
|
||||
* <p>Setting joint data completely overrides/replaces all motion from the default animation system including inverse
|
||||
* kinematics, but just for the specified joint. So for example, if you were to procedurally manipulate the finger joints,
|
||||
* the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate
|
||||
* joints in the inverse kinematics chain, the inverse kinematics might not function as you expect. For example, if you set
|
||||
* the rotation of the elbow, the hand inverse kinematics position won't end up in the right place.</p>
|
||||
* @function MyAvatar.setJointRotation
|
||||
* @param {string} name - The name of the joint.
|
||||
* @param {Quat} rotation - The rotation of the joint relative to its parent.
|
||||
* @example <caption>Set your avatar to its default T-pose then rotate its right arm.<br />
|
||||
* <img alt="Avatar in T-pose with arm rotated"
|
||||
* src="https://docs.highfidelity.com/user/pages/06.api-reference/25.myavatar/armpose.png" /></caption>
|
||||
* // Set all joint translations and rotations to defaults.
|
||||
* var i, length, rotation, translation;
|
||||
* for (i = 0, length = MyAvatar.getJointNames().length; i < length; i++) {
|
||||
* rotation = MyAvatar.getDefaultJointRotation(i);
|
||||
* translation = MyAvatar.getDefaultJointTranslation(i);
|
||||
* MyAvatar.setJointData(i, rotation, translation);
|
||||
* }
|
||||
*
|
||||
* // Rotate the right arm.
|
||||
* var newArmRotation = { x: 0.47, y: 0.22, z: -0.02, w: 0.87 };
|
||||
* MyAvatar.setJointRotation("RightArm", newArmRotation);
|
||||
*
|
||||
* // Restore your avatar's motion after 5s.
|
||||
* Script.setTimeout(function () {
|
||||
* MyAvatar.clearJointsData();
|
||||
* }, 5000);
|
||||
*/
|
||||
Q_INVOKABLE virtual void setJointRotation(const QString& name, const glm::quat& rotation);
|
||||
|
||||
/**jsdoc
|
||||
* Set a specific joint's translation relative to its parent.
|
||||
* <p>Setting joint data completely overrides/replaces all motion from the default animation system including inverse
|
||||
* kinematics, but just for the specified joint. So for example, if you were to procedurally manipulate the finger joints,
|
||||
* the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate
|
||||
* joints in the inverse kinematics chain, the inverse kinematics might not function as you expect. For example, if you set
|
||||
* the rotation of the elbow, the hand inverse kinematics position won't end up in the right place.</p>
|
||||
* @function MyAvatar.setJointTranslation
|
||||
* @param {string} name - The name of the joint.
|
||||
* @param {Vec3} translation - The translation of the joint relative to its parent.
|
||||
* @example <caption>Stretch your avatar's neck. Depending on the avatar you are using, you will either see a gap between
|
||||
* the head and body or you will see the neck stretched.<br />
|
||||
* <img alt="Avatar with neck stretched"
|
||||
* src="https://docs.highfidelity.com/user/pages/06.api-reference/25.myavatar/stretched-neck.png" /></caption>
|
||||
* // Stretch your avatar's neck.
|
||||
* MyAvatar.setJointTranslation("Neck", { x: 0, y: 25, z: 0 });
|
||||
*
|
||||
* // Restore your avatar's neck after 5s.
|
||||
* Script.setTimeout(function () {
|
||||
* MyAvatar.clearJointData("Neck");
|
||||
* }, 5000);
|
||||
*/
|
||||
Q_INVOKABLE virtual void setJointTranslation(const QString& name, const glm::vec3& translation);
|
||||
|
||||
/**jsdoc
|
||||
* Clear joint translations and rotations set by script for a specific joint. This restores all motion from the default
|
||||
* animation system including inverse kinematics for that joint.
|
||||
* <p>Note: This is slightly slower than the function variation that specifies the joint index.</p>
|
||||
* @function MyAvatar.clearJointData
|
||||
* @param {string} name - The name of the joint.
|
||||
* @example <caption>Offset and restore the position of your avatar's head.</caption>
|
||||
* // Move your avatar's head up by 25cm from where it should be.
|
||||
* MyAvatar.setJointTranslation("Neck", { x: 0, y: 0.25, z: 0 });
|
||||
*
|
||||
* // Restore your avatar's head to its default position after 5s.
|
||||
* Script.setTimeout(function () {
|
||||
* MyAvatar.clearJointData("Neck");
|
||||
* }, 5000);
|
||||
*/
|
||||
Q_INVOKABLE virtual void clearJointData(const QString& name);
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.isJointDataValid
|
||||
* @param {string} name
|
||||
* @returns {boolean}
|
||||
*/
|
||||
Q_INVOKABLE virtual bool isJointDataValid(const QString& name) const;
|
||||
|
||||
/**jsdoc
|
||||
* Get the rotation of a joint relative to its parent. For information on the joint hierarchy used, see
|
||||
* <a href="https://docs.highfidelity.com/create-and-explore/avatars/avatar-standards">Avatar Standards</a>.
|
||||
* @function MyAvatar.getJointRotation
|
||||
* @param {string} name - The name of the joint.
|
||||
* @returns {Quat} The rotation of the joint relative to its parent.
|
||||
* @example <caption>Report the rotation of your avatar's hips joint.</caption>
|
||||
* print(JSON.stringify(MyAvatar.getJointRotation("Hips")));
|
||||
*/
|
||||
Q_INVOKABLE virtual glm::quat getJointRotation(const QString& name) const;
|
||||
|
||||
/**jsdoc
|
||||
* Get the translation of a joint relative to its parent. For information on the joint hierarchy used, see
|
||||
* <a href="https://docs.highfidelity.com/create-and-explore/avatars/avatar-standards">Avatar Standards</a>.
|
||||
* @function MyAvatar.getJointTranslation
|
||||
* @param {number} name - The name of the joint.
|
||||
* @returns {Vec3} The translation of the joint relative to its parent.
|
||||
* @example <caption>Report the translation of your avatar's hips joint.</caption>
|
||||
* print(JSON.stringify(MyAvatar.getJointRotation("Hips")));
|
||||
*/
|
||||
Q_INVOKABLE virtual glm::vec3 getJointTranslation(const QString& name) const;
|
||||
|
||||
/**jsdoc
|
||||
* Get the rotations of all joints in the current avatar. Each joint's rotation is relative to its parent joint.
|
||||
* @function MyAvatar.getJointRotations
|
||||
* @returns {Quat[]} The rotations of all joints relative to each's parent. The values are in the same order as the array
|
||||
* returned by {@link MyAvatar.getJointNames}.
|
||||
* @example <caption>Report the rotations of all your avatar's joints.</caption>
|
||||
* print(JSON.stringify(MyAvatar.getJointRotations()));
|
||||
*/
|
||||
Q_INVOKABLE virtual QVector<glm::quat> getJointRotations() const;
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.getJointTranslations
|
||||
* @returns {Vec3[]}
|
||||
*/
|
||||
Q_INVOKABLE virtual QVector<glm::vec3> getJointTranslations() const;
|
||||
|
||||
/**jsdoc
|
||||
* Set the rotations of all joints in the current avatar. Each joint's rotation is relative to its parent joint.
|
||||
* <p>Setting joint data completely overrides/replaces all motion from the default animation system including inverse
|
||||
* kinematics, but just for the specified joint. So for example, if you were to procedurally manipulate the finger joints,
|
||||
* the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate
|
||||
* joints in the inverse kinematics chain, the inverse kinematics might not function as you expect. For example, if you set
|
||||
* the rotation of the elbow, the hand inverse kinematics position won't end up in the right place.</p>
|
||||
* @function MyAvatar.setJointRotations
|
||||
* @param {Quat[]} jointRotations - The rotations for all joints in the avatar. The values are in the same order as the
|
||||
* array returned by {@link MyAvatar.getJointNames}.
|
||||
* @example <caption>Set your avatar to its default T-pose then rotate its right arm.<br />
|
||||
* <img alt="Avatar in T-pose" src="https://docs.highfidelity.com/user/pages/06.api-reference/25.myavatar/armpose.png" />
|
||||
* </caption>
|
||||
* // Set all joint translations and rotations to defaults.
|
||||
* var i, length, rotation, translation;
|
||||
* for (i = 0, length = MyAvatar.getJointNames().length; i < length; i++) {
|
||||
* rotation = MyAvatar.getDefaultJointRotation(i);
|
||||
* translation = MyAvatar.getDefaultJointTranslation(i);
|
||||
* MyAvatar.setJointData(i, rotation, translation);
|
||||
* }
|
||||
*
|
||||
* // Get all join rotations.
|
||||
* var jointRotations = MyAvatar.getJointRotations();
|
||||
*
|
||||
* // Update the rotation of the right arm in the array.
|
||||
* jointRotations[MyAvatar.getJointIndex("RightArm")] = { x: 0.47, y: 0.22, z: -0.02, w: 0.87 };
|
||||
*
|
||||
* // Update all joint rotations.
|
||||
* MyAvatar.setJointRotations(jointRotations);
|
||||
*
|
||||
* // Restore your avatar's motion after 5s.
|
||||
* Script.setTimeout(function () {
|
||||
* MyAvatar.clearJointsData();
|
||||
* }, 5000);
|
||||
*/
|
||||
Q_INVOKABLE virtual void setJointRotations(const QVector<glm::quat>& jointRotations);
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.setJointTranslations
|
||||
* @param {Vec3[]} translations
|
||||
*/
|
||||
Q_INVOKABLE virtual void setJointTranslations(const QVector<glm::vec3>& jointTranslations);
|
||||
|
||||
/**jsdoc
|
||||
* Clear all joint translations and rotations that have been set by script. This restores all motion from the default
|
||||
* animation system including inverse kinematics for all joints.
|
||||
* @function MyAvatar.clearJointsData
|
||||
* @example <caption>Set your avatar to it's default T-pose for a while.</caption>
|
||||
* // Set all joint translations and rotations to defaults.
|
||||
* var i, length, rotation, translation;
|
||||
* for (i = 0, length = MyAvatar.getJointNames().length; i < length; i++) {
|
||||
* rotation = MyAvatar.getDefaultJointRotation(i);
|
||||
* translation = MyAvatar.getDefaultJointTranslation(i);
|
||||
* MyAvatar.setJointData(i, rotation, translation);
|
||||
* }
|
||||
*
|
||||
* // Restore your avatar's motion after 5s.
|
||||
* Script.setTimeout(function () {
|
||||
* MyAvatar.clearJointsData();
|
||||
* }, 5000);
|
||||
*/
|
||||
Q_INVOKABLE virtual void clearJointsData();
|
||||
|
||||
/**jsdoc
|
||||
* Get the joint index for a named joint. The joint index value is the position of the joint in the array returned by
|
||||
* {@link MyAvatar.getJointNames}.
|
||||
* @function MyAvatar.getJointIndex
|
||||
* @param {string} name - The name of the joint.
|
||||
* @returns {number} The index of the joint.
|
||||
* @example <caption>Report the index of your avatar's left arm joint.</caption>
|
||||
* print(JSON.stringify(MyAvatar.getJointIndex("LeftArm"));
|
||||
*/
|
||||
/// Returns the index of the joint with the specified name, or -1 if not found/unknown.
|
||||
Q_INVOKABLE virtual int getJointIndex(const QString& name) const;
|
||||
|
||||
/**jsdoc
|
||||
* Get the names of all the joints in the current avatar.
|
||||
* @function MyAvatar.getJointNames
|
||||
* @returns {string[]} The joint names.
|
||||
* @example <caption>Report the names of all the joints in your current avatar.</caption>
|
||||
* print(JSON.stringify(MyAvatar.getJointNames()));
|
||||
*/
|
||||
Q_INVOKABLE virtual QStringList getJointNames() const;
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.setBlendshape
|
||||
* @param {string} name
|
||||
* @param {number} value
|
||||
*/
|
||||
Q_INVOKABLE void setBlendshape(QString name, float val) { _headData->setBlendshape(name, val); }
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.getAttachmentsVariant
|
||||
* @returns {object}
|
||||
*/
|
||||
// FIXME: Can this name be improved? Can it be deprecated?
|
||||
Q_INVOKABLE QVariantList getAttachmentsVariant() const;
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.setAttachmentsVariant
|
||||
* @param {object} variant
|
||||
*/
|
||||
// FIXME: Can this name be improved? Can it be deprecated?
|
||||
Q_INVOKABLE void setAttachmentsVariant(const QVariantList& variant);
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.updateAvatarEntity
|
||||
* @param {Uuid} entityID
|
||||
* @param {string} entityData
|
||||
*/
|
||||
Q_INVOKABLE void updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData);
|
||||
/**jsdoc
|
||||
* @function MyAvatar.clearAvatarEntity
|
||||
* @param {Uuid} entityID
|
||||
*/
|
||||
Q_INVOKABLE void clearAvatarEntity(const QUuid& entityID);
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.setForceFaceTrackerConnected
|
||||
* @param {boolean} connected
|
||||
*/
|
||||
Q_INVOKABLE void setForceFaceTrackerConnected(bool connected) { _forceFaceTrackerConnected = connected; }
|
||||
|
||||
// key state
|
||||
|
@ -627,15 +951,96 @@ public:
|
|||
markIdentityDataChanged();
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* Get information about all models currently attached to your avatar.
|
||||
* @function MyAvatar.getAttachmentData
|
||||
* @returns {MyAvatar.AttachmentData[]} Information about all models attached to your avatar.
|
||||
* @example <caption>Report the URLs of all current attachments.</caption>
|
||||
* var attachments = MyAvatar.getaAttachmentData();
|
||||
* for (var i = 0; i < attachments.length; i++) {
|
||||
* print (attachments[i].modelURL);
|
||||
* }
|
||||
*/
|
||||
Q_INVOKABLE QVector<AttachmentData> getAttachmentData() const;
|
||||
|
||||
/**jsdoc
|
||||
* Set all models currently attached to your avatar. For example, if you retrieve attachment data using
|
||||
* {@link MyAvatar.getAttachmentData}, make changes to it, and then want to update your avatar's attachments per the
|
||||
* changed data. You can also remove all attachments by using setting <code>attachmentData</code> to <code>null</code>.
|
||||
* @function MyAvatar.setAttachmentData
|
||||
* @param {MyAvatar.AttachmentData[]} attachmentData - The attachment data defining the models to have attached to your avatar. Use
|
||||
* <code>null</code> to remove all attachments.
|
||||
* @example <caption>Remove a hat attachment if your avatar is wearing it.</caption>
|
||||
* var hatURL = "https://s3.amazonaws.com/hifi-public/tony/cowboy-hat.fbx";
|
||||
* var attachments = MyAvatar.getAttachmentData();
|
||||
*
|
||||
* for (var i = 0; i < attachments.length; i++) {
|
||||
* if (attachments[i].modelURL === hatURL) {
|
||||
* attachments.splice(i, 1);
|
||||
* MyAvatar.setAttachmentData(attachments);
|
||||
* break;
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
Q_INVOKABLE virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData);
|
||||
|
||||
/**jsdoc
|
||||
* Attach a model to your avatar. For example, you can give your avatar a hat to wear, a guitar to hold, or a surfboard to
|
||||
* stand on.
|
||||
* <p>Note: Attached models are models only; they are not entities and can not be manipulated using the {@link Entities} API.
|
||||
* Nor can you use this function to attach an entity (such as a sphere or a box) to your avatar.</p>
|
||||
* @function MyAvatar.attach
|
||||
* @param {string} modelURL - The URL of the model to attach. Models can be .FBX or .OBJ format.
|
||||
* @param {string} [jointName=""] - The name of the avatar joint (see {@link MyAvatar.getJointNames}) to attach the model
|
||||
* to.
|
||||
* @param {Vec3} [translation=Vec3.ZERO] - The offset to apply to the model relative to the joint position.
|
||||
* @param {Quat} [rotation=Quat.IDENTITY] - The rotation to apply to the model relative to the joint orientation.
|
||||
* @param {number} [scale=1.0] - The scale to apply to the model.
|
||||
* @param {boolean} [isSoft=false] - If the model has a skeleton, set this to <code>true</code> so that the bones of the
|
||||
* attached model's skeleton are be rotated to fit the avatar's current pose. <code>isSoft</code> is used, for example,
|
||||
* to have clothing that moves with the avatar.<br />
|
||||
* If <code>true</code>, the <code>translation</code>, <code>rotation</code>, and <code>scale</code> parameters are
|
||||
* ignored.
|
||||
* @param {boolean} [allowDuplicates=false]
|
||||
* @param {boolean} [useSaved=true]
|
||||
* @example <caption>Attach a cowboy hat to your avatar's head.</caption>
|
||||
* var attachment = {
|
||||
* modelURL: "https://s3.amazonaws.com/hifi-public/tony/cowboy-hat.fbx",
|
||||
* jointName: "Head",
|
||||
* translation: {"x": 0, "y": 0.25, "z": 0},
|
||||
* rotation: {"x": 0, "y": 0, "z": 0, "w": 1},
|
||||
* scale: 1,
|
||||
* isSoft: false
|
||||
* };
|
||||
*
|
||||
* MyAvatar.attach(attachment.modelURL,
|
||||
* attachment.jointName,
|
||||
* attachment.translation,
|
||||
* attachment.rotation,
|
||||
* attachment.scale,
|
||||
* attachment.isSoft);
|
||||
*/
|
||||
Q_INVOKABLE virtual void attach(const QString& modelURL, const QString& jointName = QString(),
|
||||
const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(),
|
||||
float scale = 1.0f, bool isSoft = false,
|
||||
bool allowDuplicates = false, bool useSaved = true);
|
||||
|
||||
/**jsdoc
|
||||
* Detach the most recently attached instance of a particular model from either a specific joint or any joint.
|
||||
* @function MyAvatar.detachOne
|
||||
* @param {string} modelURL - The URL of the model to detach.
|
||||
* @param {string} [jointName=""] - The name of the joint to detach the model from. If <code>""</code>, then the most
|
||||
* recently attached model is removed from which ever joint it was attached to.
|
||||
*/
|
||||
Q_INVOKABLE void detachOne(const QString& modelURL, const QString& jointName = QString());
|
||||
|
||||
/**jsdoc
|
||||
* Detach all instances of a particular model from either a specific joint or all joints.
|
||||
* @function MyAvatar.detachAll
|
||||
* @param {string} modelURL - The URL of the model to detach.
|
||||
* @param {string} [jointName=""] - The name of the joint to detach the model from. If <code>""</code>, then the model is
|
||||
* detached from all joints.
|
||||
*/
|
||||
Q_INVOKABLE void detachAll(const QString& modelURL, const QString& jointName = QString());
|
||||
|
||||
QString getSkeletonModelURLFromScript() const { return _skeletonModelURL.toString(); }
|
||||
|
@ -657,19 +1062,63 @@ public:
|
|||
glm::vec3 getClientGlobalPosition() const { return _globalPosition; }
|
||||
glm::vec3 getGlobalBoundingBoxCorner() const { return _globalPosition + _globalBoundingBoxOffset - _globalBoundingBoxDimensions; }
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.getAvatarEntityData
|
||||
* @returns {object}
|
||||
*/
|
||||
Q_INVOKABLE AvatarEntityMap getAvatarEntityData() const;
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.setAvatarEntityData
|
||||
* @param {object} avatarEntityData
|
||||
*/
|
||||
Q_INVOKABLE void setAvatarEntityData(const AvatarEntityMap& avatarEntityData);
|
||||
|
||||
virtual void setAvatarEntityDataChanged(bool value) { _avatarEntityDataChanged = value; }
|
||||
void insertDetachedEntityID(const QUuid entityID);
|
||||
AvatarEntityIDs getAndClearRecentlyDetachedIDs();
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.getSensorToWorldMatrix
|
||||
* @returns {Mat4}
|
||||
*/
|
||||
// thread safe
|
||||
Q_INVOKABLE glm::mat4 getSensorToWorldMatrix() const;
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.getSensorToWorldScale
|
||||
* @returns {number}
|
||||
*/
|
||||
// thread safe
|
||||
Q_INVOKABLE float getSensorToWorldScale() const;
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.getControllerLeftHandMatrix
|
||||
* @returns {Mat4}
|
||||
*/
|
||||
// thread safe
|
||||
Q_INVOKABLE glm::mat4 getControllerLeftHandMatrix() const;
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.getControllerRightHandMatrix
|
||||
* @returns {Mat4}
|
||||
*/
|
||||
// thread safe
|
||||
Q_INVOKABLE glm::mat4 getControllerRightHandMatrix() const;
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.getDataRate
|
||||
* @param {string} [rateName=""]
|
||||
* @returns {number}
|
||||
*/
|
||||
Q_INVOKABLE float getDataRate(const QString& rateName = QString("")) const;
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.getUpdateRate
|
||||
* @param {string} [rateName=""]
|
||||
* @returns {number}
|
||||
*/
|
||||
Q_INVOKABLE float getUpdateRate(const QString& rateName = QString("")) const;
|
||||
|
||||
int getJointCount() const { return _jointData.size(); }
|
||||
|
@ -705,17 +1154,60 @@ public:
|
|||
virtual void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) {}
|
||||
|
||||
signals:
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.displayNameChanged
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void displayNameChanged();
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.sessionDisplayNameChanged
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void sessionDisplayNameChanged();
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.skeletonModelURLChanged
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void skeletonModelURLChanged();
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.lookAtSnappingChanged
|
||||
* @param {boolean} enabled
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void lookAtSnappingChanged(bool enabled);
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.sessionUUIDChanged
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void sessionUUIDChanged();
|
||||
|
||||
public slots:
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.sendAvatarDataPacket
|
||||
* @param {boolean} [sendAll=false]
|
||||
*/
|
||||
void sendAvatarDataPacket(bool sendAll = false);
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.sendIdentityPacket
|
||||
*/
|
||||
void sendIdentityPacket();
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.setJointMappingsFromNetworkReply
|
||||
*/
|
||||
void setJointMappingsFromNetworkReply();
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.setSessionUUID
|
||||
* @param {Uuid} sessionUUID
|
||||
*/
|
||||
virtual void setSessionUUID(const QUuid& sessionUUID) {
|
||||
if (sessionUUID != getID()) {
|
||||
if (sessionUUID == QUuid()) {
|
||||
|
@ -727,13 +1219,45 @@ public slots:
|
|||
}
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.getAbsoluteJointRotationInObjectFrame
|
||||
* @param {number} index
|
||||
* @returns {Quat}
|
||||
*/
|
||||
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override;
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.getAbsoluteJointTranslationInObjectFrame
|
||||
* @param {number} index
|
||||
* @returns {Vec3}
|
||||
*/
|
||||
virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override;
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.setAbsoluteJointRotationInObjectFrame
|
||||
* @param {number} index
|
||||
* @param {Quat} rotation
|
||||
* @returns {boolean}
|
||||
*/
|
||||
virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) override { return false; }
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.setAbsoluteJointTranslationInObjectFrame
|
||||
* @param {number} index
|
||||
* @param {Vec3} translation
|
||||
* @returns {boolean}
|
||||
*/
|
||||
virtual bool setAbsoluteJointTranslationInObjectFrame(int index, const glm::vec3& translation) override { return false; }
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.getTargetScale
|
||||
* @returns {number}
|
||||
*/
|
||||
float getTargetScale() const { return _targetScale; } // why is this a slot?
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.resetLastSent
|
||||
*/
|
||||
void resetLastSent() { _lastToByteArray = 0; }
|
||||
|
||||
protected:
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
#ifndef hifi_AvatarHashMap_h
|
||||
#define hifi_AvatarHashMap_h
|
||||
|
||||
|
@ -39,9 +40,22 @@ public:
|
|||
int size() { return _avatarHash.size(); }
|
||||
|
||||
// Currently, your own avatar will be included as the null avatar id.
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarManager.getAvatarIdentifiers
|
||||
* @returns {Uuid[]}
|
||||
*/
|
||||
Q_INVOKABLE QVector<QUuid> getAvatarIdentifiers();
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarManager.getAvatarsInRange
|
||||
* @param {Vec3} position
|
||||
* @param {number} range
|
||||
* @returns {Uuid[]}
|
||||
*/
|
||||
Q_INVOKABLE QVector<QUuid> getAvatarsInRange(const glm::vec3& position, float rangeMeters) const;
|
||||
|
||||
// No JSDod because it's documwented in AvatarManager.
|
||||
// Null/Default-constructed QUuids will return MyAvatar
|
||||
Q_INVOKABLE virtual ScriptAvatarData* getAvatar(QUuid avatarID) { return new ScriptAvatarData(getAvatarBySessionID(avatarID)); }
|
||||
|
||||
|
@ -49,18 +63,67 @@ public:
|
|||
int numberOfAvatarsInRange(const glm::vec3& position, float rangeMeters);
|
||||
|
||||
signals:
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarManager.avatarAddedEvent
|
||||
* @param {Uuid} sessionUUID
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void avatarAddedEvent(const QUuid& sessionUUID);
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarManager.avatarRemovedEvent
|
||||
* @param {Uuid} sessionUUID
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void avatarRemovedEvent(const QUuid& sessionUUID);
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarManager.avatarSessionChangedEvent
|
||||
* @param {Uuid} sessionUUID
|
||||
* @param {Uuid} oldSessionUUID
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void avatarSessionChangedEvent(const QUuid& sessionUUID,const QUuid& oldUUID);
|
||||
|
||||
public slots:
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarManager.isAvatarInRange
|
||||
* @param {string} position
|
||||
* @param {string} range
|
||||
* @returns {boolean}
|
||||
*/
|
||||
bool isAvatarInRange(const glm::vec3 & position, const float range);
|
||||
|
||||
protected slots:
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarManager.sessionUUIDChanged
|
||||
* @param {Uuid} sessionUUID
|
||||
* @param {Uuid} oldSessionUUID
|
||||
*/
|
||||
void sessionUUIDChanged(const QUuid& sessionUUID, const QUuid& oldUUID);
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarManager.processAvatarDataPacket
|
||||
* @param {} message
|
||||
* @param {} sendingNode
|
||||
*/
|
||||
void processAvatarDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarManager.processAvatarIdentityPacket
|
||||
* @param {} message
|
||||
* @param {} sendingNode
|
||||
*/
|
||||
void processAvatarIdentityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarManager.processKillAvatar
|
||||
* @param {} message
|
||||
* @param {} sendingNode
|
||||
*/
|
||||
void processKillAvatar(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
|
||||
|
||||
protected:
|
||||
|
|
|
@ -39,7 +39,9 @@ class UserInputMapper;
|
|||
* methods.</li>
|
||||
* <li>Use {@link Controller.parseMapping} or {@link Controller.loadMapping} to load a {@link Controller.MappingJSON}.</li>
|
||||
* </ul>
|
||||
* <p>Enable the mapping using {@link MappingObject#enable|enable} or {@link Controller.enableMapping} for it to take effect.
|
||||
*
|
||||
* <p>Enable the mapping using {@link MappingObject#enable|enable} or {@link Controller.enableMapping} for it to take
|
||||
* effect.</p>
|
||||
*
|
||||
* <p>Mappings and their routes are applied according to the following rules:</p>
|
||||
* <ul>
|
||||
|
@ -49,7 +51,7 @@ class UserInputMapper;
|
|||
* output that already has a route the new route is ignored.</li>
|
||||
* <li>New mappings override previous mappings: each output is processed using the route in the most recently enabled
|
||||
* mapping that contains that output.</li>
|
||||
* </p>
|
||||
* </ul>
|
||||
*
|
||||
* @class MappingObject
|
||||
*/
|
||||
|
|
|
@ -29,7 +29,8 @@ class ScriptingInterface;
|
|||
* <p>A route in a {@link MappingObject} used by the {@link Controller} API.</p>
|
||||
*
|
||||
* <p>Create a route using {@link MappingObject} methods and apply this object's methods to process it, terminating with
|
||||
* {@link RouteObject#to} to apply it to a <code>Standard</code> control, action, or script function.</p>
|
||||
* {@link RouteObject#to} to apply it to a <code>Standard</code> control, action, or script function. Note: Loops are not
|
||||
* permitted.</p>
|
||||
*
|
||||
* <p>Some methods apply to routes with number data, some apply routes with {@link Pose} data, and some apply to both route
|
||||
* types.<p>
|
||||
|
|
|
@ -172,6 +172,17 @@ private:
|
|||
ReticleInterface* _reticleInterface { nullptr };
|
||||
};
|
||||
|
||||
/**jsdoc
|
||||
* @namespace Reticle
|
||||
* @property {boolean} allowMouseCapture
|
||||
* @property {number} depth
|
||||
* @property {Vec2} maximumPosition
|
||||
* @property {boolean} mouseCaptured
|
||||
* @property {boolean} pointingAtSystemOverlay
|
||||
* @property {Vec2} position
|
||||
* @property {number} scale
|
||||
* @property {boolean} visible
|
||||
*/
|
||||
// Scripting interface available to control the Reticle
|
||||
class ReticleInterface : public QObject {
|
||||
Q_OBJECT
|
||||
|
@ -187,25 +198,82 @@ class ReticleInterface : public QObject {
|
|||
public:
|
||||
ReticleInterface(CompositorHelper* outer) : QObject(outer), _compositor(outer) {}
|
||||
|
||||
/**jsdoc
|
||||
* @function Reticle.isMouseCaptured
|
||||
* @returns {boolean}
|
||||
*/
|
||||
Q_INVOKABLE bool isMouseCaptured() { return _compositor->shouldCaptureMouse(); }
|
||||
|
||||
/**jsdoc
|
||||
* @function Reticle.getAllowMouseCapture
|
||||
* @returns {boolean}
|
||||
*/
|
||||
Q_INVOKABLE bool getAllowMouseCapture() { return _compositor->getAllowMouseCapture(); }
|
||||
|
||||
/**jsdoc
|
||||
* @function Reticle.setAllowMouseCapture
|
||||
* @param {boolean} allowMouseCaptured
|
||||
*/
|
||||
Q_INVOKABLE void setAllowMouseCapture(bool value) { return _compositor->setAllowMouseCapture(value); }
|
||||
|
||||
/**jsdoc
|
||||
* @function Reticle.isPointingAtSystemOverlay
|
||||
* @returns {boolean}
|
||||
*/
|
||||
Q_INVOKABLE bool isPointingAtSystemOverlay() { return !_compositor->getReticleOverDesktop(); }
|
||||
|
||||
/**jsdoc
|
||||
* @function Reticle.getVisible
|
||||
* @returns {boolean}
|
||||
*/
|
||||
Q_INVOKABLE bool getVisible() { return _compositor->getReticleVisible(); }
|
||||
|
||||
/**jsdoc
|
||||
* @function Reticle.setVisible
|
||||
* @param {boolean} visible
|
||||
*/
|
||||
Q_INVOKABLE void setVisible(bool visible) { _compositor->setReticleVisible(visible); }
|
||||
|
||||
/**jsdoc
|
||||
* @function Reticle.getDepth
|
||||
* @returns {number}
|
||||
*/
|
||||
Q_INVOKABLE float getDepth() { return _compositor->getReticleDepth(); }
|
||||
|
||||
/**jsdoc
|
||||
* @function Reticle.setDepth
|
||||
* @param {number} depth
|
||||
*/
|
||||
Q_INVOKABLE void setDepth(float depth) { _compositor->setReticleDepth(depth); }
|
||||
|
||||
/**jsdoc
|
||||
* @function Reticle.getScale
|
||||
* @returns {number}
|
||||
*/
|
||||
Q_INVOKABLE float getScale() const;
|
||||
|
||||
/**jsdoc
|
||||
* @function Reticle.setScale
|
||||
* @param {number} scale
|
||||
*/
|
||||
Q_INVOKABLE void setScale(float scale);
|
||||
|
||||
/**jsdoc
|
||||
* @function Reticle.getPosition
|
||||
* @returns {Vec2}
|
||||
*/
|
||||
Q_INVOKABLE QVariant getPosition() const;
|
||||
|
||||
/**jsdoc
|
||||
* @function Reticle.setPosition
|
||||
* @param {Vec2} position
|
||||
*/
|
||||
Q_INVOKABLE void setPosition(QVariant position);
|
||||
|
||||
/**jsdoc
|
||||
* @function Reticle.getMaximumPosition
|
||||
* @returns {Vec2}
|
||||
*/
|
||||
Q_INVOKABLE glm::vec2 getMaximumPosition() { return _compositor->getReticleMaximumPosition(); }
|
||||
|
||||
private:
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#include <EntityScriptingInterface.h>
|
||||
|
||||
#include "EntitiesRendererLogging.h"
|
||||
|
||||
#include <NetworkingConstants.h>
|
||||
|
||||
using namespace render;
|
||||
using namespace render::entities;
|
||||
|
@ -45,6 +45,7 @@ static int DEFAULT_MAX_FPS = 10;
|
|||
static int YOUTUBE_MAX_FPS = 30;
|
||||
|
||||
static QTouchDevice _touchDevice;
|
||||
static const char* URL_PROPERTY = "url";
|
||||
|
||||
WebEntityRenderer::ContentType WebEntityRenderer::getContentType(const QString& urlString) {
|
||||
if (urlString.isEmpty()) {
|
||||
|
@ -52,7 +53,7 @@ WebEntityRenderer::ContentType WebEntityRenderer::getContentType(const QString&
|
|||
}
|
||||
|
||||
const QUrl url(urlString);
|
||||
if (url.scheme() == "http" || url.scheme() == "https" ||
|
||||
if (url.scheme() == URL_SCHEME_HTTP || url.scheme() == URL_SCHEME_HTTPS ||
|
||||
urlString.toLower().endsWith(".htm") || urlString.toLower().endsWith(".html")) {
|
||||
return ContentType::HtmlContent;
|
||||
}
|
||||
|
@ -164,6 +165,8 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene
|
|||
if (urlChanged) {
|
||||
if (newContentType != ContentType::HtmlContent || currentContentType != ContentType::HtmlContent) {
|
||||
destroyWebSurface();
|
||||
// If we destroyed the surface, the URL change will be implicitly handled by the re-creation
|
||||
urlChanged = false;
|
||||
}
|
||||
|
||||
withWriteLock([&] {
|
||||
|
@ -185,8 +188,8 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene
|
|||
return;
|
||||
}
|
||||
|
||||
if (urlChanged) {
|
||||
_webSurface->getRootItem()->setProperty("url", _lastSourceUrl);
|
||||
if (urlChanged && _contentType == ContentType::HtmlContent) {
|
||||
_webSurface->getRootItem()->setProperty(URL_PROPERTY, _lastSourceUrl);
|
||||
}
|
||||
|
||||
if (_contextPosition != entity->getWorldPosition()) {
|
||||
|
@ -246,14 +249,25 @@ void WebEntityRenderer::doRender(RenderArgs* args) {
|
|||
batch.setResourceTexture(0, _texture);
|
||||
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
|
||||
|
||||
// Turn off jitter for these entities
|
||||
batch.pushProjectionJitter();
|
||||
DependencyManager::get<GeometryCache>()->bindWebBrowserProgram(batch, fadeRatio < OPAQUE_ALPHA_THRESHOLD);
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f, 1.0f, 1.0f, fadeRatio), _geometryId);
|
||||
batch.popProjectionJitter();
|
||||
}
|
||||
|
||||
bool WebEntityRenderer::hasWebSurface() {
|
||||
return (bool)_webSurface && _webSurface->getRootItem();
|
||||
}
|
||||
|
||||
static const auto WebSurfaceDeleter = [](OffscreenQmlSurface* webSurface) {
|
||||
AbstractViewStateInterface::instance()->sendLambdaEvent([webSurface] {
|
||||
// WebEngineView may run other threads (wasapi), so they must be deleted for a clean shutdown
|
||||
// if the application has already stopped its event loop, delete must be explicit
|
||||
delete webSurface;
|
||||
});
|
||||
};
|
||||
|
||||
bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) {
|
||||
if (_currentWebCount >= MAX_CONCURRENT_WEB_VIEWS) {
|
||||
qWarning() << "Too many concurrent web views to create new view";
|
||||
|
@ -261,20 +275,9 @@ bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) {
|
|||
}
|
||||
|
||||
++_currentWebCount;
|
||||
auto deleter = [](OffscreenQmlSurface* webSurface) {
|
||||
AbstractViewStateInterface::instance()->postLambdaEvent([webSurface] {
|
||||
if (AbstractViewStateInterface::instance()->isAboutToQuit()) {
|
||||
// WebEngineView may run other threads (wasapi), so they must be deleted for a clean shutdown
|
||||
// if the application has already stopped its event loop, delete must be explicit
|
||||
delete webSurface;
|
||||
} else {
|
||||
webSurface->deleteLater();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// FIXME use the surface cache instead of explicit creation
|
||||
_webSurface = QSharedPointer<OffscreenQmlSurface>(new OffscreenQmlSurface(), deleter);
|
||||
_webSurface = QSharedPointer<OffscreenQmlSurface>(new OffscreenQmlSurface(), WebSurfaceDeleter);
|
||||
// FIXME, the max FPS could be better managed by being dynamic (based on the number of current surfaces
|
||||
// and the current rendering load)
|
||||
_webSurface->setMaxFps(DEFAULT_MAX_FPS);
|
||||
|
@ -302,15 +305,10 @@ bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) {
|
|||
_webSurface->setMaxFps(DEFAULT_MAX_FPS);
|
||||
}
|
||||
_webSurface->load("controls/WebEntityView.qml", [this](QQmlContext* context, QObject* item) {
|
||||
item->setProperty("url", _lastSourceUrl);
|
||||
item->setProperty(URL_PROPERTY, _lastSourceUrl);
|
||||
});
|
||||
} else if (_contentType == ContentType::QmlContent) {
|
||||
_webSurface->load(_lastSourceUrl, [this](QQmlContext* context, QObject* item) {
|
||||
if (item && item->objectName() == "tabletRoot") {
|
||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||
tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface.data());
|
||||
}
|
||||
});
|
||||
_webSurface->load(_lastSourceUrl);
|
||||
}
|
||||
_fadeStartTime = usecTimestampNow();
|
||||
_webSurface->resume();
|
||||
|
@ -320,27 +318,21 @@ bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) {
|
|||
|
||||
void WebEntityRenderer::destroyWebSurface() {
|
||||
QSharedPointer<OffscreenQmlSurface> webSurface;
|
||||
ContentType contentType{ ContentType::NoContent };
|
||||
withWriteLock([&] {
|
||||
webSurface.swap(_webSurface);
|
||||
std::swap(contentType, _contentType);
|
||||
});
|
||||
|
||||
if (webSurface) {
|
||||
--_currentWebCount;
|
||||
QQuickItem* rootItem = webSurface->getRootItem();
|
||||
|
||||
if (rootItem && rootItem->objectName() == "tabletRoot") {
|
||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||
tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", nullptr);
|
||||
}
|
||||
|
||||
// Fix for crash in QtWebEngineCore when rapidly switching domains
|
||||
// Call stop on the QWebEngineView before destroying OffscreenQMLSurface.
|
||||
if (rootItem) {
|
||||
QObject* obj = rootItem->findChild<QObject*>("webEngineView");
|
||||
if (obj) {
|
||||
// stop loading
|
||||
QMetaObject::invokeMethod(obj, "stop");
|
||||
}
|
||||
if (rootItem && contentType == ContentType::HtmlContent) {
|
||||
// stop loading
|
||||
QMetaObject::invokeMethod(rootItem, "stop");
|
||||
}
|
||||
|
||||
webSurface->pause();
|
||||
|
|
|
@ -69,7 +69,6 @@ private:
|
|||
graphics::SkyboxPointer editSkybox() { return editBackground()->getSkybox(); }
|
||||
graphics::HazePointer editHaze() { _needHazeUpdate = true; return _haze; }
|
||||
|
||||
bool _needsInitialSimulation{ true };
|
||||
glm::vec3 _lastPosition;
|
||||
glm::vec3 _lastDimensions;
|
||||
glm::quat _lastRotation;
|
||||
|
|
|
@ -216,7 +216,6 @@ bool AnimationPropertyGroup::appendToEditPacket(OctreePacketData* packetData,
|
|||
|
||||
|
||||
bool AnimationPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) {
|
||||
|
||||
int bytesRead = 0;
|
||||
bool overwriteLocalData = true;
|
||||
bool somethingChanged = false;
|
||||
|
@ -360,3 +359,21 @@ int AnimationPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char
|
|||
READ_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, bool, setHold);
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
float AnimationPropertyGroup::getNumFrames() const {
|
||||
return _lastFrame - _firstFrame + 1.0f;
|
||||
}
|
||||
|
||||
float AnimationPropertyGroup::computeLoopedFrame(float frame) const {
|
||||
float numFrames = getNumFrames();
|
||||
if (numFrames > 1.0f) {
|
||||
frame = getFirstFrame() + fmodf(frame - getFirstFrame(), numFrames);
|
||||
} else {
|
||||
frame = getFirstFrame();
|
||||
}
|
||||
return frame;
|
||||
}
|
||||
|
||||
bool AnimationPropertyGroup::isValidAndRunning() const {
|
||||
return getRunning() && (getFPS() > 0.0f) && (getNumFrames() > 1.0f) && !(getURL().isEmpty());
|
||||
}
|
||||
|
|
|
@ -77,6 +77,10 @@ public:
|
|||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
|
||||
bool& somethingChanged) override;
|
||||
|
||||
float getNumFrames() const;
|
||||
float computeLoopedFrame(float frame) const;
|
||||
bool isValidAndRunning() const;
|
||||
|
||||
DEFINE_PROPERTY_REF(PROP_ANIMATION_URL, URL, url, QString, "");
|
||||
DEFINE_PROPERTY(PROP_ANIMATION_FPS, FPS, fps, float, 30.0f);
|
||||
DEFINE_PROPERTY(PROP_ANIMATION_FRAME_INDEX, CurrentFrame, currentFrame, float, 0.0f);
|
||||
|
|
|
@ -942,11 +942,10 @@ void EntityItem::setMass(float mass) {
|
|||
float volume = _volumeMultiplier * dimensions.x * dimensions.y * dimensions.z;
|
||||
|
||||
// compute new density
|
||||
const float MIN_VOLUME = 1.0e-6f; // 0.001mm^3
|
||||
float newDensity = 1.0f;
|
||||
if (volume < 1.0e-6f) {
|
||||
if (volume < ENTITY_ITEM_MIN_VOLUME) {
|
||||
// avoid divide by zero
|
||||
newDensity = glm::min(mass / MIN_VOLUME, ENTITY_ITEM_MAX_DENSITY);
|
||||
newDensity = glm::min(mass / ENTITY_ITEM_MIN_VOLUME, ENTITY_ITEM_MAX_DENSITY);
|
||||
} else {
|
||||
newDensity = glm::max(glm::min(mass / volume, ENTITY_ITEM_MAX_DENSITY), ENTITY_ITEM_MIN_DENSITY);
|
||||
}
|
||||
|
@ -1688,7 +1687,7 @@ void EntityItem::setScaledDimensions(const glm::vec3& value) {
|
|||
}
|
||||
|
||||
void EntityItem::setUnscaledDimensions(const glm::vec3& value) {
|
||||
glm::vec3 newDimensions = glm::max(value, glm::vec3(0.0f)); // can never have negative dimensions
|
||||
glm::vec3 newDimensions = glm::max(value, glm::vec3(ENTITY_ITEM_MIN_DIMENSION));
|
||||
if (getUnscaledDimensions() != newDimensions) {
|
||||
withWriteLock([&] {
|
||||
_unscaledDimensions = newDimensions;
|
||||
|
|
|
@ -597,7 +597,7 @@ protected:
|
|||
//
|
||||
|
||||
// DirtyFlags are set whenever a property changes that the EntitySimulation needs to know about.
|
||||
uint32_t _flags { 0 }; // things that have changed from EXTERNAL changes (via script or packet) but NOT from simulation
|
||||
std::atomic_uint _flags { 0 }; // things that have changed from EXTERNAL changes (via script or packet) but NOT from simulation
|
||||
|
||||
// these backpointers are only ever set/cleared by friends:
|
||||
EntityTreeElementPointer _element; // set by EntityTreeElement
|
||||
|
|
|
@ -60,8 +60,10 @@ const float ENTITY_ITEM_DEFAULT_LIFETIME = ENTITY_ITEM_IMMORTAL_LIFETIME;
|
|||
const glm::vec3 ENTITY_ITEM_DEFAULT_POSITION = ENTITY_ITEM_ZERO_VEC3;
|
||||
const glm::quat ENTITY_ITEM_DEFAULT_ROTATION;
|
||||
const float ENTITY_ITEM_DEFAULT_WIDTH = 0.1f;
|
||||
const float ENTITY_ITEM_MIN_DIMENSION = 0.001f;
|
||||
const glm::vec3 ENTITY_ITEM_DEFAULT_DIMENSIONS = glm::vec3(ENTITY_ITEM_DEFAULT_WIDTH);
|
||||
const float ENTITY_ITEM_DEFAULT_VOLUME = ENTITY_ITEM_DEFAULT_WIDTH * ENTITY_ITEM_DEFAULT_WIDTH * ENTITY_ITEM_DEFAULT_WIDTH;
|
||||
const float ENTITY_ITEM_MIN_VOLUME = ENTITY_ITEM_MIN_DIMENSION * ENTITY_ITEM_MIN_DIMENSION * ENTITY_ITEM_MIN_DIMENSION;
|
||||
|
||||
const float ENTITY_ITEM_MAX_DENSITY = 10000.0f; // kg/m^3 density of silver
|
||||
const float ENTITY_ITEM_MIN_DENSITY = 100.0f; // kg/m^3 density of balsa wood
|
||||
|
|
|
@ -766,6 +766,36 @@ QVector<QUuid> EntityScriptingInterface::findEntitiesByType(const QString entity
|
|||
return result;
|
||||
}
|
||||
|
||||
QVector<QUuid> EntityScriptingInterface::findEntitiesByName(const QString entityName, const glm::vec3& center, float radius, bool caseSensitiveSearch) const {
|
||||
|
||||
QVector<QUuid> result;
|
||||
if (_entityTree) {
|
||||
QVector<EntityItemPointer> entities;
|
||||
_entityTree->withReadLock([&] {
|
||||
_entityTree->findEntities(center, radius, entities);
|
||||
});
|
||||
|
||||
if (caseSensitiveSearch) {
|
||||
foreach(EntityItemPointer entity, entities) {
|
||||
if (entity->getName() == entityName) {
|
||||
result << entity->getEntityItemID();
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
QString entityNameLowerCase = entityName.toLower();
|
||||
|
||||
foreach(EntityItemPointer entity, entities) {
|
||||
QString entityItemLowerCase = entity->getName().toLower();
|
||||
if (entityItemLowerCase == entityNameLowerCase) {
|
||||
result << entity->getEntityItemID();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking,
|
||||
const QScriptValue& entityIdsToInclude, const QScriptValue& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) {
|
||||
QVector<EntityItemID> entitiesToInclude = qVectorEntityItemIDFromScriptValue(entityIdsToInclude);
|
||||
|
|
|
@ -387,6 +387,22 @@ public slots:
|
|||
/// this function will not find any entities in script engine contexts which don't have access to entities
|
||||
Q_INVOKABLE QVector<QUuid> findEntitiesByType(const QString entityType, const glm::vec3& center, float radius) const;
|
||||
|
||||
/**jsdoc
|
||||
* Find all entities of a particular name that intersect a sphere defined by a center point and radius.
|
||||
* @function Entities.findEntitiesByName
|
||||
* @param {string} entityName - The name of the entity to search for.
|
||||
* @param {Vec3} center - The point about which to search.
|
||||
* @param {number} radius - The radius within which to search.
|
||||
* @param {boolean} [caseSensitive=false] - If <code>true</code> then the search is case-sensitive.
|
||||
* @returns {Uuid[]} An array of entity IDs that have the specified name and intersect the search sphere. The array is empty
|
||||
* if no entities could be found.
|
||||
* @example <caption>Report the number of entities with the name, "Light-Target".</caption>
|
||||
* var entityIDs = Entities.findEntitiesByName("Light-Target", MyAvatar.position, 10, false);
|
||||
* print("Number of entities with the name "Light-Target": " + entityIDs.length);
|
||||
*/
|
||||
Q_INVOKABLE QVector<QUuid> findEntitiesByName(const QString entityName, const glm::vec3& center, float radius,
|
||||
bool caseSensitiveSearch = false ) const;
|
||||
|
||||
/**jsdoc
|
||||
* Find the first entity intersected by a {@link PickRay}. <code>Light</code> and <code>Zone</code> entities are not
|
||||
* intersected unless they've been configured as pickable using {@link Entities.setLightsArePickable|setLightsArePickable}
|
||||
|
|
|
@ -1172,16 +1172,6 @@ void EntityTree::startChallengeOwnershipTimer(const EntityItemID& entityItemID)
|
|||
_challengeOwnershipTimeoutTimer->start(5000);
|
||||
}
|
||||
|
||||
void EntityTree::startPendingTransferStatusTimer(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode) {
|
||||
qCDebug(entities) << "'transfer_status' is 'pending', checking again in 90 seconds..." << entityItemID;
|
||||
QTimer* transferStatusRetryTimer = new QTimer(this);
|
||||
connect(transferStatusRetryTimer, &QTimer::timeout, this, [=]() {
|
||||
validatePop(certID, entityItemID, senderNode, true);
|
||||
});
|
||||
transferStatusRetryTimer->setSingleShot(true);
|
||||
transferStatusRetryTimer->start(90000);
|
||||
}
|
||||
|
||||
QByteArray EntityTree::computeNonce(const QString& certID, const QString ownerKey) {
|
||||
QUuid nonce = QUuid::createUuid(); //random, 5-hex value, separated by "-"
|
||||
QByteArray nonceBytes = nonce.toByteArray();
|
||||
|
@ -1321,7 +1311,7 @@ void EntityTree::sendChallengeOwnershipRequestPacket(const QByteArray& certID, c
|
|||
nodeList->sendPacket(std::move(challengeOwnershipPacket), *(nodeList->nodeWithUUID(QUuid::fromRfc4122(nodeToChallenge))));
|
||||
}
|
||||
|
||||
void EntityTree::validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode, bool isRetryingValidation) {
|
||||
void EntityTree::validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode) {
|
||||
// Start owner verification.
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
// First, asynchronously hit "proof_of_purchase_status?transaction_type=transfer" endpoint.
|
||||
|
@ -1352,30 +1342,13 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt
|
|||
withWriteLock([&] {
|
||||
deleteEntity(entityItemID, true);
|
||||
});
|
||||
} else if (jsonObject["transfer_status"].toArray().first().toString() == "pending") {
|
||||
if (isRetryingValidation) {
|
||||
qCDebug(entities) << "'transfer_status' is 'pending' after retry, deleting entity" << entityItemID;
|
||||
withWriteLock([&] {
|
||||
deleteEntity(entityItemID, true);
|
||||
});
|
||||
} else {
|
||||
if (thread() != QThread::currentThread()) {
|
||||
QMetaObject::invokeMethod(this, "startPendingTransferStatusTimer",
|
||||
Q_ARG(const QString&, certID),
|
||||
Q_ARG(const EntityItemID&, entityItemID),
|
||||
Q_ARG(const SharedNodePointer&, senderNode));
|
||||
return;
|
||||
} else {
|
||||
startPendingTransferStatusTimer(certID, entityItemID, senderNode);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Second, challenge ownership of the PoP cert
|
||||
// (ignore pending status; a failure will be cleaned up during DDV)
|
||||
sendChallengeOwnershipPacket(certID,
|
||||
jsonObject["transfer_recipient_key"].toString(),
|
||||
entityItemID,
|
||||
senderNode);
|
||||
|
||||
}
|
||||
} else {
|
||||
qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; deleting entity" << entityItemID
|
||||
|
@ -1429,6 +1402,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
|
|||
|
||||
case PacketType::EntityAdd:
|
||||
isAdd = true; // fall through to next case
|
||||
// FALLTHRU
|
||||
case PacketType::EntityPhysics:
|
||||
case PacketType::EntityEdit: {
|
||||
quint64 startDecode = 0, endDecode = 0;
|
||||
|
@ -1619,7 +1593,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
|
|||
// Delete the entity we just added if it doesn't pass static certificate verification
|
||||
deleteEntity(entityItemID, true);
|
||||
} else {
|
||||
validatePop(properties.getCertificateID(), entityItemID, senderNode, false);
|
||||
validatePop(properties.getCertificateID(), entityItemID, senderNode);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -397,12 +397,11 @@ protected:
|
|||
QHash<EntityItemID, EntityItemPointer> _entitiesToAdd;
|
||||
|
||||
Q_INVOKABLE void startChallengeOwnershipTimer(const EntityItemID& entityItemID);
|
||||
Q_INVOKABLE void startPendingTransferStatusTimer(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode);
|
||||
|
||||
private:
|
||||
void sendChallengeOwnershipPacket(const QString& certID, const QString& ownerKey, const EntityItemID& entityItemID, const SharedNodePointer& senderNode);
|
||||
void sendChallengeOwnershipRequestPacket(const QByteArray& certID, const QByteArray& text, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode);
|
||||
void validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode, bool isRetryingValidation);
|
||||
void validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode);
|
||||
|
||||
std::shared_ptr<AvatarData> _myAvatar{ nullptr };
|
||||
|
||||
|
|
|
@ -82,12 +82,12 @@ bool ModelEntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(jointTranslations, setJointTranslations);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(relayParentJoints, setRelayParentJoints);
|
||||
|
||||
bool somethingChangedInAnimations = _animationProperties.setProperties(properties);
|
||||
|
||||
if (somethingChangedInAnimations) {
|
||||
_flags |= Simulation::DIRTY_UPDATEABLE;
|
||||
}
|
||||
somethingChanged = somethingChanged || somethingChangedInAnimations;
|
||||
withWriteLock([&] {
|
||||
AnimationPropertyGroup animationProperties = _animationProperties;
|
||||
animationProperties.setProperties(properties);
|
||||
bool somethingChangedInAnimations = applyNewAnimationProperties(animationProperties);
|
||||
somethingChanged = somethingChanged || somethingChangedInAnimations;
|
||||
});
|
||||
|
||||
if (somethingChanged) {
|
||||
bool wantDebug = false;
|
||||
|
@ -118,12 +118,16 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
|||
READ_ENTITY_PROPERTY(PROP_TEXTURES, QString, setTextures);
|
||||
READ_ENTITY_PROPERTY(PROP_RELAY_PARENT_JOINTS, bool, setRelayParentJoints);
|
||||
|
||||
// grab a local copy of _animationProperties to avoid multiple locks
|
||||
int bytesFromAnimation;
|
||||
withWriteLock([&] {
|
||||
// Note: since we've associated our _animationProperties with our _animationLoop, the readEntitySubclassDataFromBuffer()
|
||||
// will automatically read into the animation loop
|
||||
bytesFromAnimation = _animationProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
|
||||
withReadLock([&] {
|
||||
AnimationPropertyGroup animationProperties = _animationProperties;
|
||||
bytesFromAnimation = animationProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
|
||||
propertyFlags, overwriteLocalData, animationPropertiesChanged);
|
||||
if (animationPropertiesChanged) {
|
||||
applyNewAnimationProperties(animationProperties);
|
||||
somethingChanged = true;
|
||||
}
|
||||
});
|
||||
|
||||
bytesRead += bytesFromAnimation;
|
||||
|
@ -131,11 +135,6 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
|||
|
||||
READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, setShapeType);
|
||||
|
||||
if (animationPropertiesChanged) {
|
||||
_flags |= Simulation::DIRTY_UPDATEABLE;
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_JOINT_ROTATIONS_SET, QVector<bool>, setJointRotationsSet);
|
||||
READ_ENTITY_PROPERTY(PROP_JOINT_ROTATIONS, QVector<glm::quat>, setJointRotations);
|
||||
READ_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS_SET, QVector<bool>, setJointTranslationsSet);
|
||||
|
@ -194,98 +193,38 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit
|
|||
|
||||
// added update function back for property fix
|
||||
void ModelEntityItem::update(const quint64& now) {
|
||||
assert(_lastAnimated > 0);
|
||||
|
||||
{
|
||||
auto currentAnimationProperties = this->getAnimationProperties();
|
||||
|
||||
if (_previousAnimationProperties != currentAnimationProperties) {
|
||||
withWriteLock([&] {
|
||||
// if we hit start animation or change the first or last frame then restart the animation
|
||||
if ((currentAnimationProperties.getFirstFrame() != _previousAnimationProperties.getFirstFrame()) ||
|
||||
(currentAnimationProperties.getLastFrame() != _previousAnimationProperties.getLastFrame()) ||
|
||||
(currentAnimationProperties.getRunning() && !_previousAnimationProperties.getRunning())) {
|
||||
|
||||
// when we start interface and the property is are set then the current frame is initialized to -1
|
||||
if (_currentFrame < 0) {
|
||||
// don't reset _lastAnimated here because we need the timestamp from the ModelEntityItem constructor for when the properties were set
|
||||
_currentFrame = currentAnimationProperties.getCurrentFrame();
|
||||
setAnimationCurrentFrame(_currentFrame);
|
||||
} else {
|
||||
_lastAnimated = usecTimestampNow();
|
||||
_currentFrame = currentAnimationProperties.getFirstFrame();
|
||||
setAnimationCurrentFrame(currentAnimationProperties.getFirstFrame());
|
||||
}
|
||||
} else if (!currentAnimationProperties.getRunning() && _previousAnimationProperties.getRunning()) {
|
||||
_currentFrame = currentAnimationProperties.getFirstFrame();
|
||||
setAnimationCurrentFrame(_currentFrame);
|
||||
} else if (currentAnimationProperties.getCurrentFrame() != _previousAnimationProperties.getCurrentFrame()) {
|
||||
// don't reset _lastAnimated here because the currentFrame was set with the previous setting of _lastAnimated
|
||||
_currentFrame = currentAnimationProperties.getCurrentFrame();
|
||||
}
|
||||
|
||||
});
|
||||
_previousAnimationProperties = this->getAnimationProperties();
|
||||
|
||||
}
|
||||
|
||||
if (isAnimatingSomething()) {
|
||||
if (!(getAnimationFirstFrame() < 0) && !(getAnimationFirstFrame() > getAnimationLastFrame())) {
|
||||
updateFrameCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EntityItem::update(now);
|
||||
}
|
||||
|
||||
bool ModelEntityItem::needsToCallUpdate() const {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ModelEntityItem::updateFrameCount() {
|
||||
|
||||
if (_currentFrame < 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_lastAnimated) {
|
||||
_lastAnimated = usecTimestampNow();
|
||||
return;
|
||||
}
|
||||
|
||||
auto now = usecTimestampNow();
|
||||
|
||||
// update the interval since the last animation.
|
||||
// increment timestamp before checking "hold"
|
||||
auto interval = now - _lastAnimated;
|
||||
_lastAnimated = now;
|
||||
|
||||
// if fps is negative then increment timestamp and return.
|
||||
if (getAnimationFPS() < 0.0f) {
|
||||
// grab a local copy of _animationProperties to avoid multiple locks
|
||||
auto animationProperties = getAnimationProperties();
|
||||
|
||||
// bail on "hold"
|
||||
if (animationProperties.getHold()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int updatedFrameCount = getAnimationLastFrame() - getAnimationFirstFrame() + 1;
|
||||
|
||||
if (!getAnimationHold() && getAnimationIsPlaying()) {
|
||||
float deltaTime = (float)interval / (float)USECS_PER_SECOND;
|
||||
_currentFrame += (deltaTime * getAnimationFPS());
|
||||
if (_currentFrame > getAnimationLastFrame() + 1) {
|
||||
if (getAnimationLoop() && getAnimationFirstFrame() != getAnimationLastFrame()) {
|
||||
_currentFrame = getAnimationFirstFrame() + (int)(_currentFrame - getAnimationFirstFrame()) % updatedFrameCount;
|
||||
} else {
|
||||
_currentFrame = getAnimationLastFrame();
|
||||
}
|
||||
} else if (_currentFrame < getAnimationFirstFrame()) {
|
||||
if (getAnimationFirstFrame() < 0) {
|
||||
_currentFrame = 0;
|
||||
} else {
|
||||
_currentFrame = getAnimationFirstFrame();
|
||||
}
|
||||
// increment animation frame
|
||||
_currentFrame += (animationProperties.getFPS() * ((float)interval) / (float)USECS_PER_SECOND);
|
||||
if (_currentFrame > animationProperties.getLastFrame() + 1.0f) {
|
||||
if (animationProperties.getLoop()) {
|
||||
_currentFrame = animationProperties.computeLoopedFrame(_currentFrame);
|
||||
} else {
|
||||
_currentFrame = animationProperties.getLastFrame();
|
||||
}
|
||||
} else if (_currentFrame < animationProperties.getFirstFrame()) {
|
||||
if (animationProperties.getFirstFrame() < 0.0f) {
|
||||
_currentFrame = 0.0f;
|
||||
} else {
|
||||
_currentFrame = animationProperties.getFirstFrame();
|
||||
}
|
||||
// qCDebug(entities) << "in update frame " << _currentFrame;
|
||||
setAnimationCurrentFrame(_currentFrame);
|
||||
}
|
||||
setAnimationCurrentFrame(_currentFrame);
|
||||
|
||||
EntityItem::update(now);
|
||||
}
|
||||
|
||||
void ModelEntityItem::debugDump() const {
|
||||
|
@ -361,67 +300,61 @@ void ModelEntityItem::setAnimationURL(const QString& url) {
|
|||
}
|
||||
|
||||
void ModelEntityItem::setAnimationSettings(const QString& value) {
|
||||
// the animations setting is a JSON string that may contain various animation settings.
|
||||
// if it includes fps, currentFrame, or running, those values will be parsed out and
|
||||
// will over ride the regular animation settings
|
||||
// NOTE: this method only called for old bitstream format
|
||||
|
||||
QJsonDocument settingsAsJson = QJsonDocument::fromJson(value.toUtf8());
|
||||
QJsonObject settingsAsJsonObject = settingsAsJson.object();
|
||||
QVariantMap settingsMap = settingsAsJsonObject.toVariantMap();
|
||||
if (settingsMap.contains("fps")) {
|
||||
float fps = settingsMap["fps"].toFloat();
|
||||
setAnimationFPS(fps);
|
||||
}
|
||||
withWriteLock([&] {
|
||||
auto animationProperties = _animationProperties;
|
||||
|
||||
// old settings used frameIndex
|
||||
if (settingsMap.contains("frameIndex")) {
|
||||
float currentFrame = settingsMap["frameIndex"].toFloat();
|
||||
#ifdef WANT_DEBUG
|
||||
if (!getAnimationURL().isEmpty()) {
|
||||
qCDebug(entities) << "ModelEntityItem::setAnimationSettings() calling setAnimationFrameIndex()...";
|
||||
qCDebug(entities) << " model URL:" << getModelURL();
|
||||
qCDebug(entities) << " animation URL:" << getAnimationURL();
|
||||
qCDebug(entities) << " settings:" << value;
|
||||
qCDebug(entities) << " settingsMap[frameIndex]:" << settingsMap["frameIndex"];
|
||||
qCDebug(entities" currentFrame: %20.5f", currentFrame);
|
||||
// the animations setting is a JSON string that may contain various animation settings.
|
||||
// if it includes fps, currentFrame, or running, those values will be parsed out and
|
||||
// will over ride the regular animation settings
|
||||
QJsonDocument settingsAsJson = QJsonDocument::fromJson(value.toUtf8());
|
||||
QJsonObject settingsAsJsonObject = settingsAsJson.object();
|
||||
QVariantMap settingsMap = settingsAsJsonObject.toVariantMap();
|
||||
if (settingsMap.contains("fps")) {
|
||||
float fps = settingsMap["fps"].toFloat();
|
||||
animationProperties.setFPS(fps);
|
||||
}
|
||||
#endif
|
||||
|
||||
setAnimationCurrentFrame(currentFrame);
|
||||
}
|
||||
|
||||
if (settingsMap.contains("running")) {
|
||||
bool running = settingsMap["running"].toBool();
|
||||
if (running != getAnimationIsPlaying()) {
|
||||
setAnimationIsPlaying(running);
|
||||
// old settings used frameIndex
|
||||
if (settingsMap.contains("frameIndex")) {
|
||||
float currentFrame = settingsMap["frameIndex"].toFloat();
|
||||
animationProperties.setCurrentFrame(currentFrame);
|
||||
}
|
||||
}
|
||||
|
||||
if (settingsMap.contains("firstFrame")) {
|
||||
float firstFrame = settingsMap["firstFrame"].toFloat();
|
||||
setAnimationFirstFrame(firstFrame);
|
||||
}
|
||||
if (settingsMap.contains("running")) {
|
||||
bool running = settingsMap["running"].toBool();
|
||||
if (running != animationProperties.getRunning()) {
|
||||
animationProperties.setRunning(running);
|
||||
}
|
||||
}
|
||||
|
||||
if (settingsMap.contains("lastFrame")) {
|
||||
float lastFrame = settingsMap["lastFrame"].toFloat();
|
||||
setAnimationLastFrame(lastFrame);
|
||||
}
|
||||
if (settingsMap.contains("firstFrame")) {
|
||||
float firstFrame = settingsMap["firstFrame"].toFloat();
|
||||
animationProperties.setFirstFrame(firstFrame);
|
||||
}
|
||||
|
||||
if (settingsMap.contains("loop")) {
|
||||
bool loop = settingsMap["loop"].toBool();
|
||||
setAnimationLoop(loop);
|
||||
}
|
||||
if (settingsMap.contains("lastFrame")) {
|
||||
float lastFrame = settingsMap["lastFrame"].toFloat();
|
||||
animationProperties.setLastFrame(lastFrame);
|
||||
}
|
||||
|
||||
if (settingsMap.contains("hold")) {
|
||||
bool hold = settingsMap["hold"].toBool();
|
||||
setAnimationHold(hold);
|
||||
}
|
||||
if (settingsMap.contains("loop")) {
|
||||
bool loop = settingsMap["loop"].toBool();
|
||||
animationProperties.setLoop(loop);
|
||||
}
|
||||
|
||||
if (settingsMap.contains("allowTranslation")) {
|
||||
bool allowTranslation = settingsMap["allowTranslation"].toBool();
|
||||
setAnimationAllowTranslation(allowTranslation);
|
||||
}
|
||||
_flags |= Simulation::DIRTY_UPDATEABLE;
|
||||
if (settingsMap.contains("hold")) {
|
||||
bool hold = settingsMap["hold"].toBool();
|
||||
animationProperties.setHold(hold);
|
||||
}
|
||||
|
||||
if (settingsMap.contains("allowTranslation")) {
|
||||
bool allowTranslation = settingsMap["allowTranslation"].toBool();
|
||||
animationProperties.setAllowTranslation(allowTranslation);
|
||||
}
|
||||
applyNewAnimationProperties(animationProperties);
|
||||
});
|
||||
}
|
||||
|
||||
void ModelEntityItem::setAnimationIsPlaying(bool value) {
|
||||
|
@ -713,11 +646,45 @@ float ModelEntityItem::getAnimationFPS() const {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
bool ModelEntityItem::isAnimatingSomething() const {
|
||||
return resultWithReadLock<bool>([&] {
|
||||
return !_animationProperties.getURL().isEmpty() &&
|
||||
_animationProperties.getRunning() &&
|
||||
(_animationProperties.getFPS() != 0.0f);
|
||||
});
|
||||
return _animationProperties.isValidAndRunning();
|
||||
});
|
||||
}
|
||||
|
||||
bool ModelEntityItem::applyNewAnimationProperties(AnimationPropertyGroup newProperties) {
|
||||
// call applyNewAnimationProperties() whenever trying to update _animationProperties
|
||||
// because there is some reset logic we need to do whenever the animation "config" properties change
|
||||
// NOTE: this private method is always called inside withWriteLock()
|
||||
|
||||
// if we hit start animation or change the first or last frame then restart the animation
|
||||
if ((newProperties.getFirstFrame() != _animationProperties.getFirstFrame()) ||
|
||||
(newProperties.getLastFrame() != _animationProperties.getLastFrame()) ||
|
||||
(newProperties.getRunning() && !_animationProperties.getRunning())) {
|
||||
|
||||
// when we start interface and the property is are set then the current frame is initialized to -1
|
||||
if (_currentFrame < 0.0f) {
|
||||
// don't reset _lastAnimated here because we need the timestamp from the ModelEntityItem constructor for when the properties were set
|
||||
_currentFrame = newProperties.getCurrentFrame();
|
||||
newProperties.setCurrentFrame(_currentFrame);
|
||||
} else {
|
||||
_lastAnimated = usecTimestampNow();
|
||||
_currentFrame = newProperties.getFirstFrame();
|
||||
newProperties.setCurrentFrame(newProperties.getFirstFrame());
|
||||
}
|
||||
} else if (!newProperties.getRunning() && _animationProperties.getRunning()) {
|
||||
_currentFrame = newProperties.getFirstFrame();
|
||||
newProperties.setCurrentFrame(_currentFrame);
|
||||
} else if (newProperties.getCurrentFrame() != _animationProperties.getCurrentFrame()) {
|
||||
// don't reset _lastAnimated here because the currentFrame was set with the previous setting of _lastAnimated
|
||||
_currentFrame = newProperties.getCurrentFrame();
|
||||
}
|
||||
|
||||
// finally apply the changes
|
||||
bool somethingChanged = newProperties != _animationProperties;
|
||||
if (somethingChanged) {
|
||||
_animationProperties = newProperties;
|
||||
_flags |= Simulation::DIRTY_UPDATEABLE;
|
||||
}
|
||||
return somethingChanged;
|
||||
}
|
||||
|
|
|
@ -46,10 +46,9 @@ public:
|
|||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
|
||||
bool& somethingChanged) override;
|
||||
|
||||
// update() and needstocallupdate() added back for the entity property fix
|
||||
|
||||
virtual void update(const quint64& now) override;
|
||||
virtual bool needsToCallUpdate() const override;
|
||||
void updateFrameCount();
|
||||
bool needsToCallUpdate() const override { return isAnimatingSomething(); }
|
||||
|
||||
virtual void debugDump() const override;
|
||||
|
||||
|
@ -132,6 +131,7 @@ public:
|
|||
|
||||
private:
|
||||
void setAnimationSettings(const QString& value); // only called for old bitstream format
|
||||
bool applyNewAnimationProperties(AnimationPropertyGroup newProperties);
|
||||
ShapeType computeTrueShapeType() const;
|
||||
|
||||
protected:
|
||||
|
@ -172,7 +172,6 @@ protected:
|
|||
|
||||
private:
|
||||
uint64_t _lastAnimated{ 0 };
|
||||
AnimationPropertyGroup _previousAnimationProperties;
|
||||
float _currentFrame{ -1.0f };
|
||||
};
|
||||
|
||||
|
|
|
@ -77,8 +77,6 @@ class PolyLineEntityItem : public EntityItem {
|
|||
QString getTextures() const;
|
||||
void setTextures(const QString& textures);
|
||||
|
||||
virtual bool needsToCallUpdate() const override { return true; }
|
||||
|
||||
virtual ShapeType getShapeType() const override { return SHAPE_TYPE_NONE; }
|
||||
|
||||
bool pointsChanged() const { return _pointsChanged; }
|
||||
|
|
|
@ -38,11 +38,8 @@ void SimpleEntitySimulation::clearOwnership(const QUuid& ownerID) {
|
|||
entity->clearSimulationOwnership();
|
||||
entity->markAsChangedOnServer();
|
||||
if (auto element = entity->getElement()) {
|
||||
auto tree = getEntityTree();
|
||||
tree->withReadLock([&] {
|
||||
DirtyOctreeElementOperator op(element);
|
||||
tree->recurseTreeWithOperator(&op);
|
||||
});
|
||||
DirtyOctreeElementOperator op(element);
|
||||
getEntityTree()->recurseTreeWithOperator(&op);
|
||||
}
|
||||
} else {
|
||||
++itemItr;
|
||||
|
|
|
@ -996,14 +996,12 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
|
|||
QByteArray filename = subobject.properties.at(0).toByteArray();
|
||||
QByteArray filepath = filename.replace('\\', '/');
|
||||
filename = fileOnUrl(filepath, url);
|
||||
qDebug() << "Filename" << filepath << filename;
|
||||
_textureFilepaths.insert(getID(object.properties), filepath);
|
||||
_textureFilenames.insert(getID(object.properties), filename);
|
||||
} else if (subobject.name == "TextureName" && subobject.properties.length() >= TEXTURE_NAME_MIN_SIZE) {
|
||||
// trim the name from the timestamp
|
||||
QString name = QString(subobject.properties.at(0).toByteArray());
|
||||
name = name.left(name.indexOf('['));
|
||||
qDebug() << "Filename" << name;
|
||||
_textureNames.insert(getID(object.properties), name);
|
||||
} 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>());
|
||||
|
|
|
@ -249,7 +249,7 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn
|
|||
indexToDirect = true;
|
||||
}
|
||||
}
|
||||
if (indexToDirect && data.normalIndices.isEmpty()) {
|
||||
if (indexToDirect && data.colorIndices.isEmpty()) {
|
||||
// hack to work around wacky Makehuman exports
|
||||
data.colorsByVertex = true;
|
||||
}
|
||||
|
|
|
@ -142,7 +142,6 @@ void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) {
|
|||
out << prop.toInt();
|
||||
break;
|
||||
|
||||
encodeNode(out, FBXNode());
|
||||
case QMetaType::Float:
|
||||
out.device()->write("F", 1);
|
||||
out << prop.toFloat();
|
||||
|
|
|
@ -44,8 +44,9 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] =
|
|||
|
||||
(&::gpu::gl::GLBackend::do_setModelTransform),
|
||||
(&::gpu::gl::GLBackend::do_setViewTransform),
|
||||
(&::gpu::gl::GLBackend::do_setProjectionTransform),
|
||||
(&::gpu::gl::GLBackend::do_setViewportTransform),
|
||||
(&::gpu::gl::GLBackend::do_setProjectionTransform),
|
||||
(&::gpu::gl::GLBackend::do_setProjectionJitter),
|
||||
(&::gpu::gl::GLBackend::do_setViewportTransform),
|
||||
(&::gpu::gl::GLBackend::do_setDepthRangeTransform),
|
||||
|
||||
(&::gpu::gl::GLBackend::do_setPipeline),
|
||||
|
@ -166,7 +167,18 @@ void GLBackend::renderPassTransfer(const Batch& batch) {
|
|||
case Batch::COMMAND_drawIndexedInstanced:
|
||||
case Batch::COMMAND_multiDrawIndirect:
|
||||
case Batch::COMMAND_multiDrawIndexedIndirect:
|
||||
_transform.preUpdate(_commandIndex, _stereo);
|
||||
{
|
||||
Vec2u outputSize{ 1,1 };
|
||||
|
||||
if (_output._framebuffer) {
|
||||
outputSize.x = _output._framebuffer->getWidth();
|
||||
outputSize.y = _output._framebuffer->getHeight();
|
||||
} else if (glm::dot(_transform._projectionJitter, _transform._projectionJitter)>0.0f) {
|
||||
qCWarning(gpugllogging) << "Jittering needs to have a frame buffer to be set";
|
||||
}
|
||||
|
||||
_transform.preUpdate(_commandIndex, _stereo, outputSize);
|
||||
}
|
||||
break;
|
||||
|
||||
case Batch::COMMAND_disableContextStereo:
|
||||
|
@ -179,8 +191,10 @@ void GLBackend::renderPassTransfer(const Batch& batch) {
|
|||
|
||||
case Batch::COMMAND_setViewportTransform:
|
||||
case Batch::COMMAND_setViewTransform:
|
||||
case Batch::COMMAND_setProjectionTransform: {
|
||||
CommandCall call = _commandCalls[(*command)];
|
||||
case Batch::COMMAND_setProjectionTransform:
|
||||
case Batch::COMMAND_setProjectionJitter:
|
||||
{
|
||||
CommandCall call = _commandCalls[(*command)];
|
||||
(this->*(call))(batch, *offset);
|
||||
break;
|
||||
}
|
||||
|
@ -254,6 +268,8 @@ void GLBackend::render(const Batch& batch) {
|
|||
if (!batch.isStereoEnabled()) {
|
||||
_stereo._enable = false;
|
||||
}
|
||||
// Reset jitter
|
||||
_transform._projectionJitter = Vec2(0.0f, 0.0f);
|
||||
|
||||
{
|
||||
PROFILE_RANGE(render_gpu_gl_detail, "Transfer");
|
||||
|
|
|
@ -126,6 +126,7 @@ public:
|
|||
virtual void do_setModelTransform(const Batch& batch, size_t paramOffset) final;
|
||||
virtual void do_setViewTransform(const Batch& batch, size_t paramOffset) final;
|
||||
virtual void do_setProjectionTransform(const Batch& batch, size_t paramOffset) final;
|
||||
virtual void do_setProjectionJitter(const Batch& batch, size_t paramOffset) final;
|
||||
virtual void do_setViewportTransform(const Batch& batch, size_t paramOffset) final;
|
||||
virtual void do_setDepthRangeTransform(const Batch& batch, size_t paramOffset) final;
|
||||
|
||||
|
@ -367,6 +368,7 @@ protected:
|
|||
Mat4 _projection;
|
||||
Vec4i _viewport { 0, 0, 1, 1 };
|
||||
Vec2 _depthRange { 0.0f, 1.0f };
|
||||
Vec2 _projectionJitter{ 0.0f, 0.0f };
|
||||
bool _invalidView { false };
|
||||
bool _invalidProj { false };
|
||||
bool _invalidViewport { false };
|
||||
|
@ -379,7 +381,7 @@ protected:
|
|||
mutable List::const_iterator _camerasItr;
|
||||
mutable size_t _currentCameraOffset{ INVALID_OFFSET };
|
||||
|
||||
void preUpdate(size_t commandIndex, const StereoState& stereo);
|
||||
void preUpdate(size_t commandIndex, const StereoState& stereo, Vec2u framebufferSize);
|
||||
void update(size_t commandIndex, const StereoState& stereo) const;
|
||||
void bindCurrentCamera(int stereoSide) const;
|
||||
} _transform;
|
||||
|
|
|
@ -28,6 +28,12 @@ void GLBackend::do_setProjectionTransform(const Batch& batch, size_t paramOffset
|
|||
_transform._invalidProj = true;
|
||||
}
|
||||
|
||||
void GLBackend::do_setProjectionJitter(const Batch& batch, size_t paramOffset) {
|
||||
_transform._projectionJitter.x = batch._params[paramOffset]._float;
|
||||
_transform._projectionJitter.y = batch._params[paramOffset+1]._float;
|
||||
_transform._invalidProj = true;
|
||||
}
|
||||
|
||||
void GLBackend::do_setViewportTransform(const Batch& batch, size_t paramOffset) {
|
||||
memcpy(&_transform._viewport, batch.readData(batch._params[paramOffset]._uint), sizeof(Vec4i));
|
||||
|
||||
|
@ -90,7 +96,7 @@ void GLBackend::syncTransformStateCache() {
|
|||
_transform._enabledDrawcallInfoBuffer = false;
|
||||
}
|
||||
|
||||
void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const StereoState& stereo) {
|
||||
void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const StereoState& stereo, Vec2u framebufferSize) {
|
||||
// Check all the dirty flags and update the state accordingly
|
||||
if (_invalidViewport) {
|
||||
_camera._viewport = glm::vec4(_viewport);
|
||||
|
@ -117,20 +123,21 @@ void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const Stereo
|
|||
|
||||
if (_invalidView || _invalidProj || _invalidViewport) {
|
||||
size_t offset = _cameraUboSize * _cameras.size();
|
||||
Vec2 finalJitter = _projectionJitter / Vec2(framebufferSize);
|
||||
_cameraOffsets.push_back(TransformStageState::Pair(commandIndex, offset));
|
||||
|
||||
if (stereo.isStereo()) {
|
||||
#ifdef GPU_STEREO_CAMERA_BUFFER
|
||||
_cameras.push_back(CameraBufferElement(_camera.getEyeCamera(0, stereo, _view), _camera.getEyeCamera(1, stereo, _view)));
|
||||
_cameras.push_back(CameraBufferElement(_camera.getEyeCamera(0, stereo, _view, finalJitter), _camera.getEyeCamera(1, stereo, _view, finalJitter)));
|
||||
#else
|
||||
_cameras.push_back((_camera.getEyeCamera(0, stereo, _view)));
|
||||
_cameras.push_back((_camera.getEyeCamera(1, stereo, _view)));
|
||||
_cameras.push_back((_camera.getEyeCamera(0, stereo, _view, finalJitter)));
|
||||
_cameras.push_back((_camera.getEyeCamera(1, stereo, _view, finalJitter)));
|
||||
#endif
|
||||
} else {
|
||||
#ifdef GPU_STEREO_CAMERA_BUFFER
|
||||
_cameras.push_back(CameraBufferElement(_camera.recomputeDerived(_view)));
|
||||
_cameras.push_back(CameraBufferElement(_camera.getMonoCamera(_view, finalJitter)));
|
||||
#else
|
||||
_cameras.push_back((_camera.recomputeDerived(_view)));
|
||||
_cameras.push_back((_camera.getMonoCamera(_view, finalJitter)));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
@ -265,6 +265,22 @@ void Batch::setProjectionTransform(const Mat4& proj) {
|
|||
_params.emplace_back(cacheData(sizeof(Mat4), &proj));
|
||||
}
|
||||
|
||||
void Batch::setProjectionJitter(float jx, float jy) {
|
||||
_projectionJitter.x = jx;
|
||||
_projectionJitter.y = jy;
|
||||
pushProjectionJitter(jx, jy);
|
||||
}
|
||||
|
||||
void Batch::pushProjectionJitter(float jx, float jy) {
|
||||
ADD_COMMAND(setProjectionJitter);
|
||||
_params.emplace_back(jx);
|
||||
_params.emplace_back(jy);
|
||||
}
|
||||
|
||||
void Batch::popProjectionJitter() {
|
||||
pushProjectionJitter(_projectionJitter.x, _projectionJitter.y);
|
||||
}
|
||||
|
||||
void Batch::setViewportTransform(const Vec4i& viewport) {
|
||||
ADD_COMMAND(setViewportTransform);
|
||||
|
||||
|
|
|
@ -167,6 +167,10 @@ public:
|
|||
void resetViewTransform() { setViewTransform(Transform(), false); }
|
||||
void setViewTransform(const Transform& view, bool camera = true);
|
||||
void setProjectionTransform(const Mat4& proj);
|
||||
void setProjectionJitter(float jx = 0.0f, float jy = 0.0f);
|
||||
// Very simple 1 level stack management of jitter.
|
||||
void pushProjectionJitter(float jx = 0.0f, float jy = 0.0f);
|
||||
void popProjectionJitter();
|
||||
// Viewport is xy = low left corner in framebuffer, zw = width height of the viewport, expressed in pixels
|
||||
void setViewportTransform(const Vec4i& viewport);
|
||||
void setDepthRangeTransform(float nearDepth, float farDepth);
|
||||
|
@ -292,8 +296,9 @@ public:
|
|||
|
||||
COMMAND_setModelTransform,
|
||||
COMMAND_setViewTransform,
|
||||
COMMAND_setProjectionTransform,
|
||||
COMMAND_setViewportTransform,
|
||||
COMMAND_setProjectionTransform,
|
||||
COMMAND_setProjectionJitter,
|
||||
COMMAND_setViewportTransform,
|
||||
COMMAND_setDepthRangeTransform,
|
||||
|
||||
COMMAND_setPipeline,
|
||||
|
@ -496,6 +501,7 @@ public:
|
|||
|
||||
NamedBatchDataMap _namedData;
|
||||
|
||||
glm::vec2 _projectionJitter{ 0.0f, 0.0f };
|
||||
bool _enableStereo{ true };
|
||||
bool _enableSkybox { false };
|
||||
|
||||
|
|
|
@ -41,15 +41,19 @@ vec3 color_LinearToYCoCg(vec3 rgb) {
|
|||
);
|
||||
}
|
||||
|
||||
vec3 color_YCoCgToLinear(vec3 ycocg) {
|
||||
vec3 color_YCoCgToUnclampedLinear(vec3 ycocg) {
|
||||
// R = Y + Co - Cg
|
||||
// G = Y + Cg
|
||||
// B = Y - Co - Cg
|
||||
return clamp(vec3(
|
||||
return vec3(
|
||||
ycocg.x + ycocg.y - ycocg.z,
|
||||
ycocg.x + ycocg.z,
|
||||
ycocg.x - ycocg.y - ycocg.z
|
||||
), vec3(0.0), vec3(1.0));
|
||||
);
|
||||
}
|
||||
|
||||
vec3 color_YCoCgToLinear(vec3 ycocg) {
|
||||
return clamp(color_YCoCgToUnclampedLinear(ycocg), vec3(0.0), vec3(1.0));
|
||||
}
|
||||
|
||||
<@func declareColorWheel()@>
|
||||
|
|
|
@ -222,7 +222,7 @@ const Backend::TransformCamera& Backend::TransformCamera::recomputeDerived(const
|
|||
return *this;
|
||||
}
|
||||
|
||||
Backend::TransformCamera Backend::TransformCamera::getEyeCamera(int eye, const StereoState& _stereo, const Transform& xformView) const {
|
||||
Backend::TransformCamera Backend::TransformCamera::getEyeCamera(int eye, const StereoState& _stereo, const Transform& xformView, Vec2 normalizedJitter) const {
|
||||
TransformCamera result = *this;
|
||||
Transform offsetTransform = xformView;
|
||||
if (!_stereo._skybox) {
|
||||
|
@ -231,6 +231,9 @@ Backend::TransformCamera Backend::TransformCamera::getEyeCamera(int eye, const S
|
|||
// FIXME: If "skybox" the ipd is set to 0 for now, let s try to propose a better solution for this in the future
|
||||
}
|
||||
result._projection = _stereo._eyeProjections[eye];
|
||||
normalizedJitter.x *= 2.0f;
|
||||
result._projection[2][0] += normalizedJitter.x;
|
||||
result._projection[2][1] += normalizedJitter.y;
|
||||
result.recomputeDerived(offsetTransform);
|
||||
|
||||
result._stereoInfo = Vec4(1.0f, (float)eye, 0.0f, 0.0f);
|
||||
|
@ -238,6 +241,14 @@ Backend::TransformCamera Backend::TransformCamera::getEyeCamera(int eye, const S
|
|||
return result;
|
||||
}
|
||||
|
||||
Backend::TransformCamera Backend::TransformCamera::getMonoCamera(const Transform& xformView, Vec2 normalizedJitter) const {
|
||||
TransformCamera result = *this;
|
||||
result._projection[2][0] += normalizedJitter.x;
|
||||
result._projection[2][1] += normalizedJitter.y;
|
||||
result.recomputeDerived(xformView);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Counters for Buffer and Texture usage in GPU/Context
|
||||
|
||||
ContextMetricSize Backend::freeGPUMemSize;
|
||||
|
|
|
@ -64,19 +64,16 @@ public:
|
|||
virtual void recycle() const = 0;
|
||||
virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) = 0;
|
||||
|
||||
// UBO class... layout MUST match the layout in Transform.slh
|
||||
class TransformCamera {
|
||||
public:
|
||||
mutable Mat4 _view;
|
||||
mutable Mat4 _viewInverse;
|
||||
mutable Mat4 _projectionViewUntranslated;
|
||||
Mat4 _projection;
|
||||
mutable Mat4 _projectionInverse;
|
||||
Vec4 _viewport; // Public value is int but float in the shader to stay in floats for all the transform computations.
|
||||
mutable Vec4 _stereoInfo;
|
||||
// Shared header between C++ and GLSL
|
||||
#include "TransformCamera_shared.slh"
|
||||
|
||||
class TransformCamera : public _TransformCamera {
|
||||
public:
|
||||
const Backend::TransformCamera& recomputeDerived(const Transform& xformView) const;
|
||||
TransformCamera getEyeCamera(int eye, const StereoState& stereo, const Transform& xformView) const;
|
||||
// Jitter should be divided by framebuffer size
|
||||
TransformCamera getMonoCamera(const Transform& xformView, Vec2 normalizedJitter) const;
|
||||
// Jitter should be divided by framebuffer size
|
||||
TransformCamera getEyeCamera(int eye, const StereoState& stereo, const Transform& xformView, Vec2 normalizedJitter) const;
|
||||
};
|
||||
|
||||
|
||||
|
@ -136,7 +133,6 @@ protected:
|
|||
friend class Context;
|
||||
mutable ContextStats _stats;
|
||||
StereoState _stereo;
|
||||
|
||||
};
|
||||
|
||||
class Context {
|
||||
|
|
|
@ -11,20 +11,14 @@
|
|||
<@def GPU_TRANSFORM_STATE_SLH@>
|
||||
|
||||
<@func declareStandardCameraTransform()@>
|
||||
struct TransformCamera {
|
||||
mat4 _view;
|
||||
mat4 _viewInverse;
|
||||
mat4 _projectionViewUntranslated;
|
||||
mat4 _projection;
|
||||
mat4 _projectionInverse;
|
||||
vec4 _viewport;
|
||||
vec4 _stereoInfo;
|
||||
};
|
||||
<@include gpu/TransformCamera_shared.slh@>
|
||||
|
||||
#define TransformCamera _TransformCamera
|
||||
|
||||
layout(std140) uniform transformCameraBuffer {
|
||||
#ifdef GPU_TRANSFORM_IS_STEREO
|
||||
#ifdef GPU_TRANSFORM_STEREO_CAMERA
|
||||
TransformCamera _camera[2];
|
||||
TransformCamera _camera[2];
|
||||
#else
|
||||
TransformCamera _camera;
|
||||
#endif
|
||||
|
|
26
libraries/gpu/src/gpu/TransformCamera_shared.slh
Normal file
26
libraries/gpu/src/gpu/TransformCamera_shared.slh
Normal file
|
@ -0,0 +1,26 @@
|
|||
// glsl / C++ compatible source as interface for FadeEffect
|
||||
#ifdef __cplusplus
|
||||
# define _MAT4 Mat4
|
||||
# define _VEC4 Vec4
|
||||
# define _MUTABLE mutable
|
||||
#else
|
||||
# define _MAT4 mat4
|
||||
# define _VEC4 vec4
|
||||
# define _MUTABLE
|
||||
#endif
|
||||
|
||||
struct _TransformCamera {
|
||||
_MUTABLE _MAT4 _view;
|
||||
_MUTABLE _MAT4 _viewInverse;
|
||||
_MUTABLE _MAT4 _projectionViewUntranslated;
|
||||
_MAT4 _projection;
|
||||
_MUTABLE _MAT4 _projectionInverse;
|
||||
_VEC4 _viewport; // Public value is int but float in the shader to stay in floats for all the transform computations.
|
||||
_MUTABLE _VEC4 _stereoInfo;
|
||||
};
|
||||
|
||||
// <@if 1@>
|
||||
// Trigger Scribe include
|
||||
// <@endif@> <!def that !>
|
||||
//
|
||||
|
|
@ -137,6 +137,62 @@ class ModelCache : public ResourceCache, public Dependency {
|
|||
SINGLETON_DEPENDENCY
|
||||
|
||||
public:
|
||||
|
||||
// Properties are copied over from ResourceCache (see ResourceCache.h for reason).
|
||||
|
||||
/**jsdoc
|
||||
* API to manage model cache resources.
|
||||
* @namespace ModelCache
|
||||
*
|
||||
* @property {number} numTotal - Total number of total resources. <em>Read-only.</em>
|
||||
* @property {number} numCached - Total number of cached resource. <em>Read-only.</em>
|
||||
* @property {number} sizeTotal - Size in bytes of all resources. <em>Read-only.</em>
|
||||
* @property {number} sizeCached - Size in bytes of all cached resources. <em>Read-only.</em>
|
||||
*/
|
||||
|
||||
|
||||
// Functions are copied over from ResourceCache (see ResourceCache.h for reason).
|
||||
|
||||
/**jsdoc
|
||||
* Get the list of all resource URLs.
|
||||
* @function ModelCache.getResourceList
|
||||
* @return {string[]}
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* @function ModelCache.dirty
|
||||
* @returns {Signal}
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* @function ModelCache.updateTotalSize
|
||||
* @param {number} deltaSize
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* @function ModelCache.prefetch
|
||||
* @param {string} url
|
||||
* @param {object} extra
|
||||
* @returns {object}
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* Asynchronously loads a resource from the specified URL and returns it.
|
||||
* @function ModelCache.getResource
|
||||
* @param {string} url - URL of the resource to load.
|
||||
* @param {string} [fallback=""] - Fallback URL if load of the desired URL fails.
|
||||
* @param {} [extra=null]
|
||||
* @return {Resource}
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* Prefetches a resource.
|
||||
* @function ModelCache.prefetch
|
||||
* @param {string} url - URL of the resource to prefetch.
|
||||
* @return {Resource}
|
||||
*/
|
||||
|
||||
|
||||
GeometryResource::Pointer getGeometryResource(const QUrl& url,
|
||||
const QVariantHash& mapping = QVariantHash(),
|
||||
const QUrl& textureBaseUrl = QUrl());
|
||||
|
|
|
@ -137,12 +137,69 @@ using NetworkTexturePointer = QSharedPointer<NetworkTexture>;
|
|||
|
||||
Q_DECLARE_METATYPE(QWeakPointer<NetworkTexture>)
|
||||
|
||||
|
||||
/// Stores cached textures, including render-to-texture targets.
|
||||
class TextureCache : public ResourceCache, public Dependency {
|
||||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
||||
public:
|
||||
|
||||
// Properties are copied over from ResourceCache (see ResourceCache.h for reason).
|
||||
|
||||
/**jsdoc
|
||||
* API to manage texture cache resources.
|
||||
* @namespace TextureCache
|
||||
*
|
||||
* @property {number} numTotal - Total number of total resources. <em>Read-only.</em>
|
||||
* @property {number} numCached - Total number of cached resource. <em>Read-only.</em>
|
||||
* @property {number} sizeTotal - Size in bytes of all resources. <em>Read-only.</em>
|
||||
* @property {number} sizeCached - Size in bytes of all cached resources. <em>Read-only.</em>
|
||||
*/
|
||||
|
||||
|
||||
// Functions are copied over from ResourceCache (see ResourceCache.h for reason).
|
||||
|
||||
/**jsdoc
|
||||
* Get the list of all resource URLs.
|
||||
* @function TextureCache.getResourceList
|
||||
* @return {string[]}
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* @function TextureCache.dirty
|
||||
* @returns {Signal}
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* @function TextureCache.updateTotalSize
|
||||
* @param {number} deltaSize
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* @function TextureCache.prefetch
|
||||
* @param {string} url
|
||||
* @param {object} extra
|
||||
* @returns {object}
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* Asynchronously loads a resource from the specified URL and returns it.
|
||||
* @function TextureCache.getResource
|
||||
* @param {string} url - URL of the resource to load.
|
||||
* @param {string} [fallback=""] - Fallback URL if load of the desired URL fails.
|
||||
* @param {} [extra=null]
|
||||
* @return {Resource}
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* Prefetches a resource.
|
||||
* @function TextureCache.prefetch
|
||||
* @param {string} url - URL of the resource to prefetch.
|
||||
* @return {Resource}
|
||||
*/
|
||||
|
||||
|
||||
/// Returns the ID of the permutation/normal texture used for Perlin noise shader programs. This texture
|
||||
/// has two lines: the first, a set of random numbers in [0, 255] to be used as permutation offsets, and
|
||||
/// the second, a set of random unit vectors to be used as noise gradients.
|
||||
|
@ -180,9 +237,20 @@ public:
|
|||
static const int DEFAULT_SPECTATOR_CAM_HEIGHT { 1024 };
|
||||
|
||||
signals:
|
||||
/**jsdoc
|
||||
* @function TextureCache.spectatorCameraFramebufferReset
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void spectatorCameraFramebufferReset();
|
||||
|
||||
protected:
|
||||
|
||||
/**jsdoc
|
||||
* @function TextureCache.prefect
|
||||
* @param {string} url
|
||||
* @param {number} type
|
||||
* @param {number} [maxNumPixels=67108864]
|
||||
*/
|
||||
// Overload ResourceCache::prefetch to allow specifying texture type for loads
|
||||
Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url, int type, int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS);
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue