Merging with master

This commit is contained in:
samcake 2018-04-24 11:49:34 -07:00
commit 33bbbcef83
31 changed files with 361 additions and 141 deletions

View file

@ -19,6 +19,7 @@ To produce an executable installer on Windows, the following are required:
- [nsProcess Plug-in for Nullsoft](http://nsis.sourceforge.net/NsProcess_plugin) - 1.6 - [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 - [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 - [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
Run the `package` target to create an executable installer using the Nullsoft Scriptable Install System. Run the `package` target to create an executable installer using the Nullsoft Scriptable Install System.

View file

@ -87,6 +87,10 @@
;-------------------------------- ;--------------------------------
;-------------------------------- ;--------------------------------
;General ;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 ; leverage the UAC NSIS plugin to promote uninstaller to elevated privileges
!include UAC.nsh !include UAC.nsh
@ -446,6 +450,7 @@ SectionEnd
Page custom PostInstallOptionsPage ReadPostInstallOptions Page custom PostInstallOptionsPage ReadPostInstallOptions
!define MUI_PAGE_CUSTOMFUNCTION_PRE PageInstallFilesPre !define MUI_PAGE_CUSTOMFUNCTION_PRE PageInstallFilesPre
!define MUI_PAGE_CUSTOMFUNCTION_SHOW StartInstallSlideshow
!insertmacro MUI_PAGE_INSTFILES !insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_UNPAGE_CONFIRM
@ -544,11 +549,33 @@ Var Express
${EndIf} ${EndIf}
!macroend !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 Function OnUserAbort
!insertmacro GoogleAnalytics "Installer" "Abort" "User Abort" "" !insertmacro GoogleAnalytics "Installer" "Abort" "User Abort" ""
FunctionEnd FunctionEnd
Function PageWelcomePre Function PageWelcomePre
!insertmacro GoogleAnalytics "Installer" "Welcome" "" "" !insertmacro GoogleAnalytics "Installer" "Welcome" "" ""
!insertmacro DownloadSlideshowImages
FunctionEnd FunctionEnd
Function PageLicensePre Function PageLicensePre
!insertmacro GoogleAnalytics "Installer" "License" "" "" !insertmacro GoogleAnalytics "Installer" "License" "" ""
@ -640,6 +667,56 @@ Function ChangeCustomLabel
Pop $R1 Pop $R1
FunctionEnd 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 Function PostInstallOptionsPage
!insertmacro MaybeSkipPage !insertmacro MaybeSkipPage
!insertmacro GoogleAnalytics "Installer" "Post Install Options" "" "" !insertmacro GoogleAnalytics "Installer" "Post Install Options" "" ""
@ -932,6 +1009,7 @@ FunctionEnd
;Installer Sections ;Installer Sections
Section "-Core installation" Section "-Core installation"
;The following delete blocks are temporary and can be removed once users who had the initial installer have updated ;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 ;Delete any server-console files installed before it was placed in sub-folder
@ -983,11 +1061,13 @@ Section "-Core installation"
WriteRegStr HKLM "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "" $INSTDIR WriteRegStr HKLM "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "" $INSTDIR
;Write some information about this install to the installation folder ;Write some information about this install to the installation folder
Push $0
FileOpen $0 "$INSTDIR\installer.ini" w FileOpen $0 "$INSTDIR\installer.ini" w
FileWrite $0 "type=@INSTALLER_TYPE@$\r$\n" FileWrite $0 "type=@INSTALLER_TYPE@$\r$\n"
FileWrite $0 "campaign=$CampaignName$\r$\n" FileWrite $0 "campaign=$CampaignName$\r$\n"
FileWrite $0 "exepath=$EXEPATH$\r$\n" FileWrite $0 "exepath=$EXEPATH$\r$\n"
FileClose $0 FileClose $0
Pop $0
;Package the signed uninstaller produced by the inner loop ;Package the signed uninstaller produced by the inner loop
!ifndef INNER !ifndef INNER

View file

@ -617,7 +617,7 @@ bool DomainServer::isPacketVerified(const udt::Packet& packet) {
} }
} else { } else {
HIFI_FDEBUG("Packet of type" << headerType 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; return false;
} }
} }

View file

@ -24,6 +24,7 @@ Slider {
property alias minimumValue: slider.from property alias minimumValue: slider.from
property alias maximumValue: slider.to property alias maximumValue: slider.to
property bool tickmarksEnabled: false
height: hifi.fontSizes.textFieldInput + 14 // Match height of TextField control. height: hifi.fontSizes.textFieldInput + 14 // Match height of TextField control.
y: sliderLabel.visible ? sliderLabel.height + sliderLabel.anchors.bottomMargin : 0 y: sliderLabel.visible ? sliderLabel.height + sliderLabel.anchors.bottomMargin : 0

View file

@ -20,6 +20,7 @@ SpinBox {
property int colorScheme: hifi.colorSchemes.light property int colorScheme: hifi.colorSchemes.light
readonly property bool isLightColorScheme: colorScheme === hifi.colorSchemes.light readonly property bool isLightColorScheme: colorScheme === hifi.colorSchemes.light
property string label: "" property string label: ""
property string suffix: ""
property string labelInside: "" property string labelInside: ""
property color colorLabelInside: hifi.colors.white property color colorLabelInside: hifi.colors.white
property real controlHeight: height + (spinBoxLabel.visible ? spinBoxLabel.height + spinBoxLabel.anchors.bottomMargin : 0) property real controlHeight: height + (spinBoxLabel.visible ? spinBoxLabel.height + spinBoxLabel.anchors.bottomMargin : 0)
@ -34,8 +35,11 @@ SpinBox {
property real realTo: 100.0 property real realTo: 100.0
property real realStepSize: 1.0 property real realStepSize: 1.0
signal editingFinished()
implicitHeight: height implicitHeight: height
implicitWidth: width implicitWidth: width
editable: true
padding: 0 padding: 0
leftPadding: 0 leftPadding: 0
@ -68,16 +72,16 @@ SpinBox {
} }
validator: DoubleValidator { validator: DoubleValidator {
bottom: Math.min(spinBox.from, spinBox.to)*spinBox.factor bottom: Math.min(spinBox.from, spinBox.to)
top: Math.max(spinBox.from, spinBox.to)*spinBox.factor top: Math.max(spinBox.from, spinBox.to)
} }
textFromValue: function(value, locale) { textFromValue: function(value, locale) {
return parseFloat(value*1.0/factor).toFixed(decimals); return parseFloat(value/factor).toFixed(decimals);
} }
valueFromText: function(text, locale) { 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) : (spinBox.activeFocus ? hifi.colors.white : hifi.colors.lightGrayText)
selectedTextColor: hifi.colors.black selectedTextColor: hifi.colors.black
selectionColor: hifi.colors.primaryHighlight selectionColor: hifi.colors.primaryHighlight
text: spinBox.textFromValue(spinBox.value, spinBox.locale) text: spinBox.textFromValue(spinBox.value, spinBox.locale) + suffix
verticalAlignment: Qt.AlignVCenter verticalAlignment: Qt.AlignVCenter
leftPadding: spinBoxLabelInside.visible ? 30 : hifi.dimensions.textPadding leftPadding: spinBoxLabelInside.visible ? 30 : hifi.dimensions.textPadding
//rightPadding: hifi.dimensions.spinnerSize //rightPadding: hifi.dimensions.spinnerSize
width: spinBox.width - hifi.dimensions.spinnerSize width: spinBox.width - hifi.dimensions.spinnerSize
onEditingFinished: spinBox.editingFinished()
} }
up.indicator: Item { up.indicator: Item {
x: spinBox.width - implicitWidth - 5 x: spinBox.width - implicitWidth - 5
y: 1 y: 1

View file

@ -18,11 +18,11 @@ Preference {
height: control.height + hifi.dimensions.controlInterlineHeight height: control.height + hifi.dimensions.controlInterlineHeight
Component.onCompleted: { Component.onCompleted: {
spinner.value = preference.value; spinner.realValue = preference.value;
} }
function save() { function save() {
preference.value = spinner.value; preference.value = spinner.realValue;
preference.save(); preference.save();
} }

View file

@ -21,7 +21,7 @@ Preference {
Component.onCompleted: { Component.onCompleted: {
slider.value = preference.value; slider.value = preference.value;
spinner.value = preference.value; spinner.realValue = preference.value;
} }
function save() { function save() {
@ -60,7 +60,7 @@ Preference {
maximumValue: MyAvatar.getDomainMaxScale() maximumValue: MyAvatar.getDomainMaxScale()
stepSize: preference.step stepSize: preference.step
onValueChanged: { onValueChanged: {
spinner.value = value spinner.realValue = value
} }
anchors { anchors {
right: spinner.left right: spinner.left
@ -73,12 +73,12 @@ Preference {
SpinBox { SpinBox {
id: spinner id: spinner
decimals: preference.decimals decimals: preference.decimals
value: preference.value realValue: preference.value
minimumValue: MyAvatar.getDomainMinScale() minimumValue: MyAvatar.getDomainMinScale()
maximumValue: MyAvatar.getDomainMaxScale() maximumValue: MyAvatar.getDomainMaxScale()
width: 100 width: 100
onValueChanged: { onValueChanged: {
slider.value = value; slider.value = realValue;
} }
anchors { anchors {
right: button.left right: button.left
@ -92,10 +92,10 @@ Preference {
id: button id: button
onClicked: { onClicked: {
if (spinner.maximumValue >= 1) { if (spinner.maximumValue >= 1) {
spinner.value = 1 spinner.realValue = 1
slider.value = 1 slider.value = 1
} else { } else {
spinner.value = spinner.maximumValue spinner.realValue = spinner.maximumValue
slider.value = spinner.maximumValue slider.value = spinner.maximumValue
} }
} }
@ -108,4 +108,4 @@ Preference {
colorScheme: hifi.colorSchemes.dark colorScheme: hifi.colorSchemes.dark
} }
} }
} }

View file

@ -181,11 +181,11 @@ Item {
minimumValue: 0.01 minimumValue: 0.01
maximumValue: 10 maximumValue: 10
realStepSize: 0.05; realStepSize: 0.05;
value: attachment ? attachment.scale : 1.0 realValue: attachment ? attachment.scale : 1.0
colorScheme: hifi.colorSchemes.dark colorScheme: hifi.colorSchemes.dark
onValueChanged: { onRealValueChanged: {
if (completed && attachment && attachment.scale !== value) { if (completed && attachment && attachment.scale !== realValue) {
attachment.scale = value; attachment.scale = realValue;
updateAttachment(); updateAttachment();
} }
} }

View file

@ -51,7 +51,7 @@ Item {
id: xspinner id: xspinner
width: root.spinboxWidth width: root.spinboxWidth
anchors { left: parent.left } anchors { left: parent.left }
value: root.vector.x realValue: root.vector.x
labelInside: "X:" labelInside: "X:"
colorScheme: hifi.colorSchemes.dark colorScheme: hifi.colorSchemes.dark
colorLabelInside: hifi.colors.redHighlight colorLabelInside: hifi.colors.redHighlight
@ -72,17 +72,17 @@ Item {
id: yspinner id: yspinner
width: root.spinboxWidth width: root.spinboxWidth
anchors { horizontalCenter: parent.horizontalCenter } anchors { horizontalCenter: parent.horizontalCenter }
value: root.vector.y realValue: root.vector.y
labelInside: "Y:" labelInside: "Y:"
colorLabelInside: hifi.colors.greenHighlight colorLabelInside: hifi.colors.greenHighlight
colorScheme: hifi.colorSchemes.dark colorScheme: hifi.colorSchemes.dark
decimals: root.decimals decimals: root.decimals
stepSize: root.stepSize realStepSize: root.stepSize
maximumValue: root.maximumValue maximumValue: root.maximumValue
minimumValue: root.minimumValue minimumValue: root.minimumValue
onValueChanged: { onRealValueChanged: {
if (value !== vector.y) { if (realValue !== vector.y) {
vector.y = value vector.y = realValue
root.valueChanged(); root.valueChanged();
} }
} }
@ -93,17 +93,17 @@ Item {
id: zspinner id: zspinner
width: root.spinboxWidth width: root.spinboxWidth
anchors { right: parent.right; } anchors { right: parent.right; }
value: root.vector.z realValue: root.vector.z
labelInside: "Z:" labelInside: "Z:"
colorLabelInside: hifi.colors.primaryHighlight colorLabelInside: hifi.colors.primaryHighlight
colorScheme: hifi.colorSchemes.dark colorScheme: hifi.colorSchemes.dark
decimals: root.decimals decimals: root.decimals
stepSize: root.stepSize realStepSize: root.stepSize
maximumValue: root.maximumValue maximumValue: root.maximumValue
minimumValue: root.minimumValue minimumValue: root.minimumValue
onValueChanged: { onRealValueChanged: {
if (value !== vector.z) { if (realValue !== vector.z) {
vector.z = value vector.z = realValue
root.valueChanged(); root.valueChanged();
} }
} }

View file

@ -178,8 +178,8 @@ Rectangle {
label: "Y Offset" label: "Y Offset"
suffix: " cm" suffix: " cm"
minimumValue: -10 minimumValue: -10
stepSize: 1 realStepSize: 1
value: -5 realValue: -5
colorScheme: hifi.colorSchemes.dark colorScheme: hifi.colorSchemes.dark
onEditingFinished: { onEditingFinished: {
@ -193,10 +193,10 @@ Rectangle {
width: 112 width: 112
label: "Z Offset" label: "Z Offset"
minimumValue: -10 minimumValue: -10
stepSize: 1 realStepSize: 1
decimals: 1 decimals: 1
suffix: " cm" suffix: " cm"
value: -5 realValue: -5
colorScheme: hifi.colorSchemes.dark colorScheme: hifi.colorSchemes.dark
onEditingFinished: { onEditingFinished: {
@ -288,7 +288,7 @@ Rectangle {
suffix: " cm" suffix: " cm"
label: "Y Offset" label: "Y Offset"
minimumValue: -10 minimumValue: -10
stepSize: 1 realStepSize: 1
colorScheme: hifi.colorSchemes.dark colorScheme: hifi.colorSchemes.dark
onEditingFinished: { onEditingFinished: {
@ -303,7 +303,7 @@ Rectangle {
label: "Z Offset" label: "Z Offset"
suffix: " cm" suffix: " cm"
minimumValue: -10 minimumValue: -10
stepSize: 1 realStepSize: 1
decimals: 1 decimals: 1
colorScheme: hifi.colorSchemes.dark colorScheme: hifi.colorSchemes.dark
@ -535,9 +535,9 @@ Rectangle {
suffix: " cm" suffix: " cm"
label: "Arm Circumference" label: "Arm Circumference"
minimumValue: 0 minimumValue: 0
stepSize: 1.0 realStepSize: 1.0
colorScheme: hifi.colorSchemes.dark colorScheme: hifi.colorSchemes.dark
value: 33.0 realValue: 33.0
onEditingFinished: { onEditingFinished: {
sendConfigurationSettings(); sendConfigurationSettings();
@ -550,10 +550,10 @@ Rectangle {
label: "Shoulder Width" label: "Shoulder Width"
suffix: " cm" suffix: " cm"
minimumValue: 0 minimumValue: 0
stepSize: 1.0 realStepSize: 1.0
decimals: 1 decimals: 1
colorScheme: hifi.colorSchemes.dark colorScheme: hifi.colorSchemes.dark
value: 48 realValue: 48
onEditingFinished: { onEditingFinished: {
sendConfigurationSettings(); sendConfigurationSettings();
@ -659,13 +659,13 @@ Rectangle {
InputConfiguration.uncalibratePlugin(pluginName); InputConfiguration.uncalibratePlugin(pluginName);
updateCalibrationButton(); updateCalibrationButton();
} else { } else {
calibrationTimer.interval = timeToCalibrate.value * 1000 calibrationTimer.interval = timeToCalibrate.realValue * 1000
openVrConfiguration.countDown = timeToCalibrate.value; openVrConfiguration.countDown = timeToCalibrate.realValue;
var calibratingScreen = screen.createObject(); var calibratingScreen = screen.createObject();
stack.push(calibratingScreen); stack.push(calibratingScreen);
calibratingScreen.canceled.connect(cancelCalibration); calibratingScreen.canceled.connect(cancelCalibration);
calibratingScreen.restart.connect(restartCalibration); calibratingScreen.restart.connect(restartCalibration);
calibratingScreen.start(calibrationTimer.interval, timeToCalibrate.value); calibratingScreen.start(calibrationTimer.interval, timeToCalibrate.realValue);
calibrationTimer.start(); calibrationTimer.start();
} }
} }
@ -728,12 +728,12 @@ Rectangle {
anchors.leftMargin: leftMargin anchors.leftMargin: leftMargin
minimumValue: 5 minimumValue: 5
value: 5 realValue: 5
colorScheme: hifi.colorSchemes.dark colorScheme: hifi.colorSchemes.dark
onEditingFinished: { onEditingFinished: {
calibrationTimer.interval = value * 1000; calibrationTimer.interval = realValue * 1000;
openVrConfiguration.countDown = value; openVrConfiguration.countDown = realValue;
numberAnimation.duration = calibrationTimer.interval; numberAnimation.duration = calibrationTimer.interval;
} }
} }
@ -910,8 +910,8 @@ Rectangle {
var desktopMode = settings["desktopMode"]; var desktopMode = settings["desktopMode"];
var hmdDesktopPosition = settings["hmdDesktopTracking"]; var hmdDesktopPosition = settings["hmdDesktopTracking"];
armCircumference.value = settings.armCircumference; armCircumference.realValue = settings.armCircumference;
shoulderWidth.value = settings.shoulderWidth; shoulderWidth.realValue = settings.shoulderWidth;
if (HmdHead) { if (HmdHead) {
headBox.checked = true; headBox.checked = true;
@ -1075,22 +1075,22 @@ Rectangle {
var headObject = { var headObject = {
"override": overrideHead, "override": overrideHead,
"Y": headYOffset.value, "Y": headYOffset.realValue,
"Z": headZOffset.value "Z": headZOffset.realValue
} }
var handObject = { var handObject = {
"override": overrideHandController, "override": overrideHandController,
"Y": handYOffset.value, "Y": handYOffset.realValue,
"Z": handZOffset.value "Z": handZOffset.realValue
} }
var settingsObject = { var settingsObject = {
"bodyConfiguration": trackerConfiguration, "bodyConfiguration": trackerConfiguration,
"headConfiguration": headObject, "headConfiguration": headObject,
"handConfiguration": handObject, "handConfiguration": handObject,
"armCircumference": armCircumference.value, "armCircumference": armCircumference.realValue,
"shoulderWidth": shoulderWidth.value, "shoulderWidth": shoulderWidth.realValue,
"desktopMode": viveInDesktop.checked, "desktopMode": viveInDesktop.checked,
"hmdDesktopTracking": hmdInDesktop.checked "hmdDesktopTracking": hmdInDesktop.checked
} }

View file

@ -4753,7 +4753,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); std::unique_lock<std::mutex> guard(_postUpdateLambdasLock);
_postUpdateLambdas[key] = func; _postUpdateLambdas[key] = func;
} }
@ -7377,7 +7377,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()) { if (this->thread() == QThread::currentThread()) {
f(); f();
} else { } else {
@ -7385,6 +7385,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) { void Application::initPlugins(const QStringList& arguments) {
QCommandLineOption display("display", "Preferred displays", "displays"); QCommandLineOption display("display", "Preferred displays", "displays");
QCommandLineOption disableDisplays("disable-displays", "Displays to disable", "displays"); QCommandLineOption disableDisplays("disable-displays", "Displays to disable", "displays");

View file

@ -138,7 +138,8 @@ public:
Application(int& argc, char** argv, QElapsedTimer& startup_time, bool runningMarkerExisted); Application(int& argc, char** argv, QElapsedTimer& startup_time, bool runningMarkerExisted);
~Application(); ~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(); QString getPreviousScriptLocation();
void setPreviousScriptLocation(const QString& previousScriptLocation); void setPreviousScriptLocation(const QString& previousScriptLocation);
@ -242,7 +243,7 @@ public:
qint64 getCurrentSessionRuntime() const { return _sessionRunTimer.elapsed(); } qint64 getCurrentSessionRuntime() const { return _sessionRunTimer.elapsed(); }
bool isAboutToQuit() const override { return _aboutToQuit; } bool isAboutToQuit() const { return _aboutToQuit; }
bool isPhysicsEnabled() const { return _physicsEnabled; } bool isPhysicsEnabled() const { return _physicsEnabled; }
// the isHMDMode is true whenever we use the interface from an HMD and not a standard flat display // the isHMDMode is true whenever we use the interface from an HMD and not a standard flat display
@ -266,10 +267,9 @@ public:
render::EnginePointer getRenderEngine() override { return _renderEngine; } render::EnginePointer getRenderEngine() override { return _renderEngine; }
gpu::ContextPointer getGPUContext() const { return _gpuContext; } gpu::ContextPointer getGPUContext() const { return _gpuContext; }
const GameWorkload& getGameWorkload() const { return _gameWorkload; } const GameWorkload& getGameWorkload() const { return _gameWorkload; }
virtual void pushPostUpdateLambda(void* key, std::function<void()> func) override; virtual void pushPostUpdateLambda(void* key, const std::function<void()>& func) override;
void updateMyAvatarLookAtPosition(); void updateMyAvatarLookAtPosition();

View file

@ -147,7 +147,7 @@ void Audio::setInputVolume(float volume) {
} }
float Audio::getInputLevel() const { float Audio::getInputLevel() const {
return resultWithReadLock<bool>([&] { return resultWithReadLock<float>([&] {
return _inputLevel; return _inputLevel;
}); });
} }

View file

@ -65,14 +65,10 @@ const QString Web3DOverlay::TYPE = "web3d";
const QString Web3DOverlay::QML = "Web3DOverlay.qml"; const QString Web3DOverlay::QML = "Web3DOverlay.qml";
static auto qmlSurfaceDeleter = [](OffscreenQmlSurface* surface) { static auto qmlSurfaceDeleter = [](OffscreenQmlSurface* surface) {
AbstractViewStateInterface::instance()->postLambdaEvent([surface] { AbstractViewStateInterface::instance()->sendLambdaEvent([surface] {
if (AbstractViewStateInterface::instance()->isAboutToQuit()) { // WebEngineView may run other threads (wasapi), so they must be deleted for a clean shutdown
// 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
// if the application has already stopped its event loop, delete must be explicit delete surface;
delete surface;
} else {
surface->deleteLater();
}
}); });
}; };

View file

@ -25,7 +25,7 @@
#include <EntityScriptingInterface.h> #include <EntityScriptingInterface.h>
#include "EntitiesRendererLogging.h" #include "EntitiesRendererLogging.h"
#include <NetworkingConstants.h>
using namespace render; using namespace render;
using namespace render::entities; using namespace render::entities;
@ -45,6 +45,7 @@ static int DEFAULT_MAX_FPS = 10;
static int YOUTUBE_MAX_FPS = 30; static int YOUTUBE_MAX_FPS = 30;
static QTouchDevice _touchDevice; static QTouchDevice _touchDevice;
static const char* URL_PROPERTY = "url";
WebEntityRenderer::ContentType WebEntityRenderer::getContentType(const QString& urlString) { WebEntityRenderer::ContentType WebEntityRenderer::getContentType(const QString& urlString) {
if (urlString.isEmpty()) { if (urlString.isEmpty()) {
@ -52,7 +53,7 @@ WebEntityRenderer::ContentType WebEntityRenderer::getContentType(const QString&
} }
const QUrl url(urlString); 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")) { urlString.toLower().endsWith(".htm") || urlString.toLower().endsWith(".html")) {
return ContentType::HtmlContent; return ContentType::HtmlContent;
} }
@ -164,6 +165,8 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene
if (urlChanged) { if (urlChanged) {
if (newContentType != ContentType::HtmlContent || currentContentType != ContentType::HtmlContent) { if (newContentType != ContentType::HtmlContent || currentContentType != ContentType::HtmlContent) {
destroyWebSurface(); destroyWebSurface();
// If we destroyed the surface, the URL change will be implicitly handled by the re-creation
urlChanged = false;
} }
withWriteLock([&] { withWriteLock([&] {
@ -185,8 +188,8 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene
return; return;
} }
if (urlChanged) { if (urlChanged && _contentType == ContentType::HtmlContent) {
_webSurface->getRootItem()->setProperty("url", _lastSourceUrl); _webSurface->getRootItem()->setProperty(URL_PROPERTY, _lastSourceUrl);
} }
if (_contextPosition != entity->getWorldPosition()) { if (_contextPosition != entity->getWorldPosition()) {
@ -254,6 +257,14 @@ bool WebEntityRenderer::hasWebSurface() {
return (bool)_webSurface && _webSurface->getRootItem(); 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) { bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) {
if (_currentWebCount >= MAX_CONCURRENT_WEB_VIEWS) { if (_currentWebCount >= MAX_CONCURRENT_WEB_VIEWS) {
qWarning() << "Too many concurrent web views to create new view"; qWarning() << "Too many concurrent web views to create new view";
@ -261,20 +272,9 @@ bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) {
} }
++_currentWebCount; ++_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 // 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 // FIXME, the max FPS could be better managed by being dynamic (based on the number of current surfaces
// and the current rendering load) // and the current rendering load)
_webSurface->setMaxFps(DEFAULT_MAX_FPS); _webSurface->setMaxFps(DEFAULT_MAX_FPS);
@ -302,7 +302,7 @@ bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) {
_webSurface->setMaxFps(DEFAULT_MAX_FPS); _webSurface->setMaxFps(DEFAULT_MAX_FPS);
} }
_webSurface->load("controls/WebEntityView.qml", [this](QQmlContext* context, QObject* item) { _webSurface->load("controls/WebEntityView.qml", [this](QQmlContext* context, QObject* item) {
item->setProperty("url", _lastSourceUrl); item->setProperty(URL_PROPERTY, _lastSourceUrl);
}); });
} else if (_contentType == ContentType::QmlContent) { } else if (_contentType == ContentType::QmlContent) {
_webSurface->load(_lastSourceUrl, [this](QQmlContext* context, QObject* item) { _webSurface->load(_lastSourceUrl, [this](QQmlContext* context, QObject* item) {
@ -327,6 +327,11 @@ void WebEntityRenderer::destroyWebSurface() {
if (webSurface) { if (webSurface) {
--_currentWebCount; --_currentWebCount;
QQuickItem* rootItem = webSurface->getRootItem(); QQuickItem* rootItem = webSurface->getRootItem();
// Explicitly set the web URL to an empty string, in an effort to get a
// faster shutdown of any chromium processes interacting with audio
if (rootItem && _contentType == ContentType::HtmlContent) {
rootItem->setProperty(URL_PROPERTY, "");
}
if (rootItem && rootItem->objectName() == "tabletRoot") { if (rootItem && rootItem->objectName() == "tabletRoot") {
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>(); auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();

View file

@ -942,11 +942,10 @@ void EntityItem::setMass(float mass) {
float volume = _volumeMultiplier * dimensions.x * dimensions.y * dimensions.z; float volume = _volumeMultiplier * dimensions.x * dimensions.y * dimensions.z;
// compute new density // compute new density
const float MIN_VOLUME = 1.0e-6f; // 0.001mm^3
float newDensity = 1.0f; float newDensity = 1.0f;
if (volume < 1.0e-6f) { if (volume < ENTITY_ITEM_MIN_VOLUME) {
// avoid divide by zero // 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 { } else {
newDensity = glm::max(glm::min(mass / volume, ENTITY_ITEM_MAX_DENSITY), ENTITY_ITEM_MIN_DENSITY); 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) { 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) { if (getUnscaledDimensions() != newDimensions) {
withWriteLock([&] { withWriteLock([&] {
_unscaledDimensions = newDimensions; _unscaledDimensions = newDimensions;

View file

@ -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::vec3 ENTITY_ITEM_DEFAULT_POSITION = ENTITY_ITEM_ZERO_VEC3;
const glm::quat ENTITY_ITEM_DEFAULT_ROTATION; const glm::quat ENTITY_ITEM_DEFAULT_ROTATION;
const float ENTITY_ITEM_DEFAULT_WIDTH = 0.1f; 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 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_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_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 const float ENTITY_ITEM_MIN_DENSITY = 100.0f; // kg/m^3 density of balsa wood

View file

@ -358,7 +358,7 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe
} else { } else {
HIFI_FCDEBUG(networking(), HIFI_FCDEBUG(networking(),
"Packet of type" << headerType << "received from unknown node with UUID" << uuidStringWithoutCurlyBraces(sourceID)); "Packet of type" << headerType << "received from unknown node with Local ID" << sourceLocalID);
} }
} }

View file

@ -638,7 +638,7 @@ void NodeList::processDomainServerList(QSharedPointer<ReceivedMessage> message)
// if this was the first domain-server list from this domain, we've now connected // if this was the first domain-server list from this domain, we've now connected
if (!_domainHandler.isConnected()) { if (!_domainHandler.isConnected()) {
_domainHandler.setLocalID(newLocalID); _domainHandler.setLocalID(domainLocalID);
_domainHandler.setUUID(domainUUID); _domainHandler.setUUID(domainUUID);
_domainHandler.setIsConnected(true); _domainHandler.setIsConnected(true);

View file

@ -66,14 +66,10 @@ OffscreenSurface::OffscreenSurface()
} }
OffscreenSurface::~OffscreenSurface() { OffscreenSurface::~OffscreenSurface() {
disconnect(qApp); delete _sharedObject;
_sharedObject->destroy();
} }
bool OffscreenSurface::fetchTexture(TextureAndFence& textureAndFence) { bool OffscreenSurface::fetchTexture(TextureAndFence& textureAndFence) {
if (!_sharedObject) {
return false;
}
hifi::qml::impl::TextureAndFence typedTextureAndFence; hifi::qml::impl::TextureAndFence typedTextureAndFence;
bool result = _sharedObject->fetchTexture(typedTextureAndFence); bool result = _sharedObject->fetchTexture(typedTextureAndFence);
textureAndFence = typedTextureAndFence; textureAndFence = typedTextureAndFence;

View file

@ -49,8 +49,8 @@ RenderEventHandler::RenderEventHandler(SharedObject* shared, QThread* targetThre
qFatal("Unable to create new offscreen GL context"); qFatal("Unable to create new offscreen GL context");
} }
moveToThread(targetThread);
_canvas.moveToThreadWithContext(targetThread); _canvas.moveToThreadWithContext(targetThread);
moveToThread(targetThread);
} }
void RenderEventHandler::onInitalize() { void RenderEventHandler::onInitalize() {
@ -160,11 +160,8 @@ void RenderEventHandler::onQuit() {
} }
_shared->shutdownRendering(_canvas, _currentSize); _shared->shutdownRendering(_canvas, _currentSize);
// Release the reference to the shared object. This will allow it to _canvas.doneCurrent();
// be destroyed (should happen on it's own thread). _canvas.moveToThreadWithContext(qApp->thread());
_shared->deleteLater(); moveToThread(qApp->thread());
deleteLater();
QThread::currentThread()->quit(); QThread::currentThread()->quit();
} }

View file

@ -72,26 +72,35 @@ SharedObject::SharedObject() {
QObject::connect(qApp, &QCoreApplication::aboutToQuit, this, &SharedObject::onAboutToQuit); QObject::connect(qApp, &QCoreApplication::aboutToQuit, this, &SharedObject::onAboutToQuit);
} }
SharedObject::~SharedObject() { SharedObject::~SharedObject() {
if (_quickWindow) { // After destroy returns, the rendering thread should be gone
_quickWindow->destroy(); destroy();
_quickWindow = nullptr;
// _renderTimer is created with `this` as the parent, so need no explicit destruction
// Destroy the event hand
if (_renderObject) {
delete _renderObject;
_renderObject = nullptr;
} }
if (_renderControl) { if (_renderControl) {
_renderControl->deleteLater(); delete _renderControl;
_renderControl = nullptr; _renderControl = nullptr;
} }
if (_renderThread) { if (_quickWindow) {
_renderThread->quit(); _quickWindow->destroy();
_renderThread->deleteLater(); delete _quickWindow;
_quickWindow = nullptr;
} }
if (_rootItem) { // _rootItem is parented to the quickWindow, so needs no explicit destruction
_rootItem->deleteLater(); //if (_rootItem) {
_rootItem = nullptr; // delete _rootItem;
} // _rootItem = nullptr;
//}
releaseEngine(_qmlContext->engine()); releaseEngine(_qmlContext->engine());
} }
@ -119,6 +128,10 @@ void SharedObject::create(OffscreenSurface* surface) {
} }
void SharedObject::setRootItem(QQuickItem* rootItem) { void SharedObject::setRootItem(QQuickItem* rootItem) {
if (_quit) {
return;
}
_rootItem = rootItem; _rootItem = rootItem;
_rootItem->setSize(_quickWindow->size()); _rootItem->setSize(_quickWindow->size());
@ -127,7 +140,6 @@ void SharedObject::setRootItem(QQuickItem* rootItem) {
_renderThread->setObjectName(objectName()); _renderThread->setObjectName(objectName());
_renderThread->start(); _renderThread->start();
// Create event handler for the render thread // Create event handler for the render thread
_renderObject = new RenderEventHandler(this, _renderThread); _renderObject = new RenderEventHandler(this, _renderThread);
QCoreApplication::postEvent(this, new OffscreenEvent(OffscreenEvent::Initialize)); QCoreApplication::postEvent(this, new OffscreenEvent(OffscreenEvent::Initialize));
@ -137,35 +149,43 @@ void SharedObject::setRootItem(QQuickItem* rootItem) {
} }
void SharedObject::destroy() { void SharedObject::destroy() {
// `destroy` is idempotent, it can be called multiple times without issues
if (_quit) { if (_quit) {
return; return;
} }
if (!_rootItem) { if (!_rootItem) {
deleteLater();
return; return;
} }
_paused = true; _paused = true;
if (_renderTimer) { if (_renderTimer) {
_renderTimer->stop();
QObject::disconnect(_renderTimer); QObject::disconnect(_renderTimer);
_renderTimer->deleteLater();
} }
QObject::disconnect(_renderControl); if (_renderControl) {
QObject::disconnect(_renderControl);
}
QObject::disconnect(qApp); QObject::disconnect(qApp);
{ {
QMutexLocker lock(&_mutex); QMutexLocker lock(&_mutex);
_quit = true; _quit = true;
QCoreApplication::postEvent(_renderObject, new OffscreenEvent(OffscreenEvent::Quit), Qt::HighEventPriority); if (_renderObject) {
QCoreApplication::postEvent(_renderObject, new OffscreenEvent(OffscreenEvent::Quit), Qt::HighEventPriority);
}
} }
// Block until the rendering thread has stopped // Block until the rendering thread has stopped
// FIXME this is undesirable because this is blocking the main thread, // FIXME this is undesirable because this is blocking the main thread,
// but I haven't found a reliable way to do this only at application // but I haven't found a reliable way to do this only at application
// shutdown // shutdown
_renderThread->wait(); if (_renderThread) {
_renderThread->wait();
delete _renderThread;
_renderThread = nullptr;
}
} }

View file

@ -37,15 +37,25 @@ public:
virtual glm::vec3 getAvatarPosition() const = 0; virtual glm::vec3 getAvatarPosition() const = 0;
virtual bool isAboutToQuit() const = 0; // Unfortunately, having this here is a bad idea. Lots of objects connect to
virtual void postLambdaEvent(std::function<void()> f) = 0; // the aboutToQuit signal, and it's impossible to know the order in which
// the receivers will be called, so this might return false negatives
//virtual bool isAboutToQuit() const = 0;
// Queue code to execute on the main thread.
// If called from the main thread, the lambda will execute synchronously
virtual void postLambdaEvent(const std::function<void()>& f) = 0;
// Synchronously execute code on the main thread. This function will
// not return until the code is executed, regardles of which thread it
// is called from
virtual void sendLambdaEvent(const std::function<void()>& f) = 0;
virtual qreal getDevicePixelRatio() = 0; virtual qreal getDevicePixelRatio() = 0;
virtual render::ScenePointer getMain3DScene() = 0; virtual render::ScenePointer getMain3DScene() = 0;
virtual render::EnginePointer getRenderEngine() = 0; virtual render::EnginePointer getRenderEngine() = 0;
virtual void pushPostUpdateLambda(void* key, std::function<void()> func) = 0; virtual void pushPostUpdateLambda(void* key, const std::function<void()>& func) = 0;
virtual bool isHMDMode() const = 0; virtual bool isHMDMode() const = 0;
@ -54,5 +64,4 @@ public:
static void setInstance(AbstractViewStateInterface* instance); static void setInstance(AbstractViewStateInterface* instance);
}; };
#endif // hifi_AbstractViewStateInterface_h
#endif // hifi_AbstractViewStateInterface_h

View file

@ -182,9 +182,7 @@ const float SCALE_CHANGE_EPSILON = 0.0000001f;
void Model::setScaleInternal(const glm::vec3& scale) { void Model::setScaleInternal(const glm::vec3& scale) {
if (glm::distance(_scale, scale) > SCALE_CHANGE_EPSILON) { if (glm::distance(_scale, scale) > SCALE_CHANGE_EPSILON) {
_scale = scale; _scale = scale;
if (_scale.x == 0.0f || _scale.y == 0.0f || _scale.z == 0.0f) { assert(_scale.x != 0.0f && scale.y != 0.0f && scale.z != 0.0f);
assert(false);
}
simulate(0.0f, true); simulate(0.0f, true);
} }
} }

View file

@ -102,7 +102,7 @@ inline float ColorUtils::tosRGBFloat(const float &linear) {
} else if (0 < linear && linear < SRGB_ELBOW_INV) { } else if (0 < linear && linear < SRGB_ELBOW_INV) {
sRGBValue = 12.92f * linear; sRGBValue = 12.92f * linear;
} else if (SRGB_ELBOW_INV <= linear && linear < 1) { } else if (SRGB_ELBOW_INV <= linear && linear < 1) {
sRGBValue = 1.055f * powf(linear, 0.41666f - 0.055f); sRGBValue = 1.055f * powf(linear, 0.41666f) - 0.055f;
} else { } else {
sRGBValue = 1.0f; sRGBValue = 1.0f;
} }

View file

@ -198,6 +198,7 @@ class IntPreference : public TypedPreference<int> {
Q_PROPERTY(float min READ getMin CONSTANT) Q_PROPERTY(float min READ getMin CONSTANT)
Q_PROPERTY(float max READ getMax CONSTANT) Q_PROPERTY(float max READ getMax CONSTANT)
Q_PROPERTY(float step READ getStep CONSTANT) Q_PROPERTY(float step READ getStep CONSTANT)
Q_PROPERTY(int decimals READ getDecimals CONSTANT)
public: public:
IntPreference(const QString& category, const QString& name, Getter getter, Setter setter) IntPreference(const QString& category, const QString& name, Getter getter, Setter setter)
@ -212,6 +213,9 @@ public:
float getStep() const { return _step; } float getStep() const { return _step; }
void setStep(float step) { _step = step; }; void setStep(float step) { _step = step; };
int getDecimals() const { return _decimals; }
void setDecimals(int decimals) { _decimals = decimals; };
signals: signals:
void valueChanged(); void valueChanged();
@ -221,6 +225,7 @@ protected:
int _min { std::numeric_limits<int>::min() }; int _min { std::numeric_limits<int>::min() };
int _max { std::numeric_limits<int>::max() }; int _max { std::numeric_limits<int>::max() };
int _step { 1 }; int _step { 1 };
int _decimals { 0 };
}; };
class StringPreference : public TypedPreference<QString> { class StringPreference : public TypedPreference<QString> {

View file

@ -176,7 +176,10 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
var TRIGGER_OFF_VALUE = 0.1; var TRIGGER_OFF_VALUE = 0.1;
var TRIGGER_ON_VALUE = TRIGGER_OFF_VALUE + 0.05; // Squeezed just enough to activate search or near grab var TRIGGER_ON_VALUE = TRIGGER_OFF_VALUE + 0.05; // Squeezed just enough to activate search or near grab
var BUMPER_ON_VALUE = 0.5; var BUMPER_ON_VALUE = 0.5;
var EMPTY_PARENT_ID = "{00000000-0000-0000-0000-000000000000}";
var UNEQUIP_KEY = "u";
function getWearableData(props) { function getWearableData(props) {
var wearable = {}; var wearable = {};
@ -270,6 +273,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
this.shouldSendStart = false; this.shouldSendStart = false;
this.equipedWithSecondary = false; this.equipedWithSecondary = false;
this.handHasBeenRightsideUp = false; this.handHasBeenRightsideUp = false;
this.mouseEquip = false;
this.parameters = makeDispatcherModuleParameters( this.parameters = makeDispatcherModuleParameters(
300, 300,
@ -279,10 +283,11 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
var equipHotspotBuddy = new EquipHotspotBuddy(); var equipHotspotBuddy = new EquipHotspotBuddy();
this.setMessageGrabData = function(entityProperties) { this.setMessageGrabData = function(entityProperties, mouseEquip) {
if (entityProperties) { if (entityProperties) {
this.messageGrabEntity = true; this.messageGrabEntity = true;
this.grabEntityProps = entityProperties; this.grabEntityProps = entityProperties;
this.mouseEquip = mouseEquip;
} }
}; };
@ -580,6 +585,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
this.targetEntityID = null; this.targetEntityID = null;
this.messageGrabEntity = false; this.messageGrabEntity = false;
this.grabEntityProps = null; this.grabEntityProps = null;
this.mouseEquip = false;
}; };
this.updateInputs = function (controllerData) { this.updateInputs = function (controllerData) {
@ -656,7 +662,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
var timestamp = Date.now(); var timestamp = Date.now();
this.updateInputs(controllerData); this.updateInputs(controllerData);
if (!this.isTargetIDValid(controllerData)) { if (!this.mouseEquip && !this.isTargetIDValid(controllerData)) {
this.endEquipEntity(); this.endEquipEntity();
return makeRunningValues(false, [], []); return makeRunningValues(false, [], []);
} }
@ -757,7 +763,8 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
var equipModule = (data.hand === "left") ? leftEquipEntity : rightEquipEntity; var equipModule = (data.hand === "left") ? leftEquipEntity : rightEquipEntity;
var entityProperties = Entities.getEntityProperties(data.entityID, DISPATCHER_PROPERTIES); var entityProperties = Entities.getEntityProperties(data.entityID, DISPATCHER_PROPERTIES);
entityProperties.id = data.entityID; entityProperties.id = data.entityID;
equipModule.setMessageGrabData(entityProperties); var mouseEquip = false;
equipModule.setMessageGrabData(entityProperties, mouseEquip);
} catch (e) { } catch (e) {
print("WARNING: equipEntity.js -- error parsing Hifi-Hand-Grab message: " + message); print("WARNING: equipEntity.js -- error parsing Hifi-Hand-Grab message: " + message);
@ -774,10 +781,68 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
} }
} }
}; };
var clearGrabActions = function(entityID) {
var actionIDs = Entities.getActionIDs(entityID);
var myGrabTag = "grab-" + MyAvatar.sessionUUID;
for (var actionIndex = 0; actionIndex < actionIDs.length; actionIndex++) {
var actionID = actionIDs[actionIndex];
var actionArguments = Entities.getActionArguments(entityID, actionID);
var tag = actionArguments.tag;
if (tag === myGrabTag) {
Entities.deleteAction(entityID, actionID);
}
}
};
var onMousePress = function(event) {
if (isInEditMode()) { // don't consider any mouse clicks on the entity while in edit
return;
}
var pickRay = Camera.computePickRay(event.x, event.y);
var intersection = Entities.findRayIntersection(pickRay, true);
if (intersection.intersects) {
var entityID = intersection.entityID;
var entityProperties = Entities.getEntityProperties(entityID, DISPATCHER_PROPERTIES);
var hasEquipData = getWearableData(entityProperties).joints || getEquipHotspotsData(entityProperties).length > 0;
if (hasEquipData && entityProperties.parentID === EMPTY_PARENT_ID && !entityIsFarGrabbedByOther(entityID)) {
entityProperties.id = entityID;
var rightHandPosition = MyAvatar.getJointPosition("RightHand");
var leftHandPosition = MyAvatar.getJointPosition("LeftHand");
var distanceToRightHand = Vec3.distance(entityProperties.position, rightHandPosition);
var distanceToLeftHand = Vec3.distance(entityProperties.position, leftHandPosition);
var leftHandAvailable = leftEquipEntity.targetEntityID === null;
var rightHandAvailable = rightEquipEntity.targetEntityID === null;
var mouseEquip = true;
if (rightHandAvailable && (distanceToRightHand < distanceToLeftHand || !leftHandAvailable)) {
// clear any existing grab actions on the entity now (their later removal could affect bootstrapping flags)
clearGrabActions(entityID);
rightEquipEntity.setMessageGrabData(entityProperties, mouseEquip);
} else if (leftHandAvailable && (distanceToLeftHand < distanceToRightHand || !rightHandAvailable)) {
// clear any existing grab actions on the entity now (their later removal could affect bootstrapping flags)
clearGrabActions(entityID);
leftEquipEntity.setMessageGrabData(entityProperties, mouseEquip);
}
}
}
};
var onKeyPress = function(event) {
if (event.text === UNEQUIP_KEY) {
if (rightEquipEntity.targetEntityID) {
rightEquipEntity.endEquipEntity();
}
if (leftEquipEntity.targetEntityID) {
leftEquipEntity.endEquipEntity();
}
}
};
Messages.subscribe('Hifi-Hand-Grab'); Messages.subscribe('Hifi-Hand-Grab');
Messages.subscribe('Hifi-Hand-Drop'); Messages.subscribe('Hifi-Hand-Drop');
Messages.messageReceived.connect(handleMessage); Messages.messageReceived.connect(handleMessage);
Controller.mousePressEvent.connect(onMousePress);
Controller.keyPressEvent.connect(onKeyPress);
var leftEquipEntity = new EquipEntity(LEFT_HAND); var leftEquipEntity = new EquipEntity(LEFT_HAND);
var rightEquipEntity = new EquipEntity(RIGHT_HAND); var rightEquipEntity = new EquipEntity(RIGHT_HAND);
@ -791,6 +856,9 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
disableDispatcherModule("LeftEquipEntity"); disableDispatcherModule("LeftEquipEntity");
disableDispatcherModule("RightEquipEntity"); disableDispatcherModule("RightEquipEntity");
clearAttachPoints(); clearAttachPoints();
Messages.messageReceived.disconnect(handleMessage);
Controller.mousePressEvent.disconnect(onMousePress);
Controller.keyPressEvent.disconnect(onKeyPress);
} }
Script.scriptEnding.connect(cleanup); Script.scriptEnding.connect(cleanup);
}()); }());

View file

@ -569,7 +569,7 @@ Grabber.prototype.moveEventProcess = function() {
} }
if (!this.actionID) { if (!this.actionID) {
if (!entityIsGrabbedByOther(this.entityID)) { if (!entityIsGrabbedByOther(this.entityID) && !entityIsEquipped(this.entityID)) {
this.actionID = Entities.addAction("far-grab", this.entityID, actionArgs); this.actionID = Entities.addAction("far-grab", this.entityID, actionArgs);
} }
} else { } else {

View file

@ -415,6 +415,34 @@ distanceBetweenPointAndEntityBoundingBox = function(point, entityProps) {
return Vec3.distance(v, localPoint); return Vec3.distance(v, localPoint);
}; };
entityIsEquipped = function(entityID) {
var rightEquipEntity = getEnabledModuleByName("RightEquipEntity");
var leftEquipEntity = getEnabledModuleByName("LeftEquipEntity");
var equippedInRightHand = rightEquipEntity ? rightEquipEntity.targetEntityID === entityID : false;
var equippedInLeftHand = leftEquipEntity ? leftEquipEntity.targetEntityID === entityID : false;
return equippedInRightHand || equippedInLeftHand;
};
entityIsFarGrabbedByOther = function(entityID) {
// by convention, a far grab sets the tag of its action to be far-grab-*owner-session-id*.
var actionIDs = Entities.getActionIDs(entityID);
var myFarGrabTag = "far-grab-" + MyAvatar.sessionUUID;
for (var actionIndex = 0; actionIndex < actionIDs.length; actionIndex++) {
var actionID = actionIDs[actionIndex];
var actionArguments = Entities.getActionArguments(entityID, actionID);
var tag = actionArguments.tag;
if (tag == myFarGrabTag) {
// we see a far-grab-*uuid* shaped tag, but it's our tag, so that's okay.
continue;
}
if (tag.slice(0, 9) == "far-grab-") {
// we see a far-grab-*uuid* shaped tag and it's not ours, so someone else is grabbing it.
return true;
}
}
return false;
};
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {
module.exports = { module.exports = {
makeDispatcherModuleParameters: makeDispatcherModuleParameters, makeDispatcherModuleParameters: makeDispatcherModuleParameters,

View file

@ -715,7 +715,6 @@ function onTabletScreenChanged(type, url) {
ContextOverlay.enabled = false; ContextOverlay.enabled = false;
Users.requestsDomainListData = true; Users.requestsDomainListData = true;
populateNearbyUserList();
audioTimer = createAudioInterval(conserveResources ? AUDIO_LEVEL_CONSERVED_UPDATE_INTERVAL_MS : AUDIO_LEVEL_UPDATE_INTERVAL_MS); audioTimer = createAudioInterval(conserveResources ? AUDIO_LEVEL_CONSERVED_UPDATE_INTERVAL_MS : AUDIO_LEVEL_UPDATE_INTERVAL_MS);
@ -726,6 +725,7 @@ function onTabletScreenChanged(type, url) {
Users.usernameFromIDReply.connect(usernameFromIDReply); Users.usernameFromIDReply.connect(usernameFromIDReply);
triggerMapping.enable(); triggerMapping.enable();
triggerPressMapping.enable(); triggerPressMapping.enable();
populateNearbyUserList();
} else { } else {
off(); off();
ContextOverlay.enabled = true; ContextOverlay.enabled = true;

View file

@ -453,8 +453,8 @@ protected:
return vec3(); return vec3();
} }
bool isAboutToQuit() const override { return false; } void postLambdaEvent(const std::function<void()>& f) override {}
void postLambdaEvent(std::function<void()> f) override {} void sendLambdaEvent(const std::function<void()>& f) override {}
qreal getDevicePixelRatio() override { qreal getDevicePixelRatio() override {
return 1.0f; return 1.0f;
@ -469,7 +469,7 @@ protected:
} }
std::map<void*, std::function<void()>> _postUpdateLambdas; std::map<void*, std::function<void()>> _postUpdateLambdas;
void pushPostUpdateLambda(void* key, std::function<void()> func) override { void pushPostUpdateLambda(void* key, const std::function<void()>& func) override {
_postUpdateLambdas[key] = func; _postUpdateLambdas[key] = func;
} }