Merge branch 'master' into proceduralMesh

This commit is contained in:
Sam Gondelman 2019-05-23 09:35:14 -07:00 committed by GitHub
commit 3d05a954bc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
55 changed files with 1457 additions and 509 deletions

View file

@ -138,7 +138,7 @@ public:
/// 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 override;
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override;
Q_INVOKABLE virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override;
/**jsdoc
* @comment Uses the base class's JSDoc.

View file

@ -358,7 +358,7 @@ SharedNodePointer DomainGatekeeper::processAssignmentConnectRequest(const NodeCo
nodeData->setNodeVersion(it->second.getNodeVersion());
nodeData->setHardwareAddress(nodeConnection.hardwareAddress);
nodeData->setMachineFingerprint(nodeConnection.machineFingerprint);
nodeData->setLastDomainCheckinTimestamp(nodeConnection.lastPingTimestamp);
nodeData->setWasAssigned(true);
// cleanup the PendingAssignedNodeData for this assignment now that it's connecting
@ -499,6 +499,9 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
// set the machine fingerprint passed in the connect request
nodeData->setMachineFingerprint(nodeConnection.machineFingerprint);
// set the last ping timestamp passed in the connect request
nodeData->setLastDomainCheckinTimestamp(nodeConnection.lastPingTimestamp);
// also add an interpolation to DomainServerNodeData so that servers can get username in stats
nodeData->addOverrideForKey(USERNAME_UUID_REPLACEMENT_STATS_KEY,
uuidStringWithoutCurlyBraces(newNode->getUUID()), username);

View file

@ -1068,6 +1068,8 @@ void DomainServer::processListRequestPacket(QSharedPointer<ReceivedMessage> mess
// update the connecting hostname in case it has changed
nodeData->setPlaceName(nodeRequestData.placeName);
nodeData->setLastDomainCheckinTimestamp(nodeRequestData.lastPingTimestamp);
sendDomainListToNode(sendingNode, message->getSenderSockAddr());
}
@ -1174,6 +1176,10 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
DomainServerNodeData* nodeData = static_cast<DomainServerNodeData*>(node->getLinkedData());
domainListStream << nodeData->getLastDomainCheckinTimestamp();
domainListStream << usecTimestampNow();
// store the nodeInterestSet on this DomainServerNodeData, in case it has changed
auto& nodeInterestSet = nodeData->getNodeInterestSet();

View file

@ -61,6 +61,9 @@ public:
void setMachineFingerprint(const QUuid& machineFingerprint) { _machineFingerprint = machineFingerprint; }
const QUuid& getMachineFingerprint() { return _machineFingerprint; }
void setLastDomainCheckinTimestamp(quint64 lastDomainCheckinTimestamp) { _lastDomainCheckinTimestamp = lastDomainCheckinTimestamp; }
quint64 getLastDomainCheckinTimestamp() { return _lastDomainCheckinTimestamp; }
void addOverrideForKey(const QString& key, const QString& value, const QString& overrideValue);
void removeOverrideForKey(const QString& key, const QString& value);
@ -93,7 +96,7 @@ private:
QString _nodeVersion;
QString _hardwareAddress;
QUuid _machineFingerprint;
quint64 _lastDomainCheckinTimestamp;
QString _placeName;
bool _wasAssigned { false };

View file

@ -36,6 +36,8 @@ NodeConnectionData NodeConnectionData::fromDataStream(QDataStream& dataStream, c
// now the machine fingerprint
dataStream >> newHeader.machineFingerprint;
}
dataStream >> newHeader.lastPingTimestamp;
dataStream >> newHeader.nodeType
>> newHeader.publicSockAddr >> newHeader.localSockAddr

View file

@ -22,6 +22,7 @@ public:
bool isConnectRequest = true);
QUuid connectUUID;
quint64 lastPingTimestamp{ 0 };
NodeType_t nodeType;
HifiSockAddr publicSockAddr;
HifiSockAddr localSockAddr;

View file

@ -58,7 +58,8 @@ Rectangle {
if (isLoggedIn) {
Commerce.getWalletStatus();
} else {
// Show some error to the user
errorText.text = "There was a problem while retrieving your inventory. " +
"Please try closing and re-opening the Avatar app.\n\nLogin status result: " + isLoggedIn;
}
}
@ -66,11 +67,19 @@ Rectangle {
if (walletStatus === 5) {
getInventory();
} else {
// Show some error to the user
errorText.text = "There was a problem while retrieving your inventory. " +
"Please try closing and re-opening the Avatar app.\n\nWallet status result: " + walletStatus;
}
}
onInventoryResult: {
if (result.status !== "success") {
errorText.text = "There was a problem while retrieving your inventory. " +
"Please try closing and re-opening the Avatar app.\n\nInventory status: " + result.status + "\nMessage: " + result.message;
} else if (result.data && result.data.assets && result.data.assets.length === 0) {
errorText.text = "You have not created any avatars yet! Create an avatar with the Avatar Creator, then close and re-open the Avatar App."
}
avatarAppInventoryModel.handlePage(result.status !== "success" && result.message, result);
root.updatePreviewUrl();
}
@ -172,7 +181,7 @@ Rectangle {
anchors.bottom: parent.bottom
AnimatedImage {
visible: !inventoryContentsList.visible
visible: !inventoryContentsList.visible && !errorText.visible
anchors.centerIn: parent
width: 72
height: width
@ -181,7 +190,7 @@ Rectangle {
ListView {
id: inventoryContentsList
visible: avatarAppInventoryModel.count !== 0
visible: avatarAppInventoryModel.count !== 0 && !errorText.visible
interactive: contentItem.height > height
clip: true
model: avatarAppInventoryModel
@ -196,6 +205,18 @@ Rectangle {
standaloneIncompatible: model.standalone_incompatible
}
}
HifiStylesUit.GraphikRegular {
id: errorText
text: ""
visible: text !== ""
anchors.fill: parent
size: 22
color: simplifiedUI.colors.text.white
wrapMode: Text.Wrap
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
}

View file

@ -19,8 +19,8 @@ Flickable {
id: root
contentWidth: parent.width
contentHeight: audioColumnLayout.height
topMargin: 16
bottomMargin: 16
topMargin: 24
bottomMargin: 24
clip: true
function changePeakValuesEnabled(enabled) {
@ -73,14 +73,23 @@ Flickable {
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
height: 30
labelText: "People Volume"
from: -60
to: 10
from: simplifiedUI.numericConstants.mutedValue
to: 20.0
defaultValue: 0.0
value: AudioScriptingInterface.getAvatarGain()
stepSize: 5.0
value: AudioScriptingInterface.avatarGain
live: true
function updatePeopleGain(sliderValue) {
if (AudioScriptingInterface.avatarGain !== sliderValue) {
AudioScriptingInterface.avatarGain = sliderValue;
}
}
onValueChanged: {
if (AudioScriptingInterface.getAvatarGain() != peopleVolume.value) {
AudioScriptingInterface.setAvatarGain(peopleVolume.value);
updatePeopleGain(value);
}
onPressedChanged: {
if (!pressed) {
updatePeopleGain(value);
}
}
}
@ -92,14 +101,24 @@ Flickable {
Layout.topMargin: 2
height: 30
labelText: "Environment Volume"
from: -60
to: 10
from: simplifiedUI.numericConstants.mutedValue
to: 20.0
defaultValue: 0.0
value: AudioScriptingInterface.getInjectorGain()
stepSize: 5.0
value: AudioScriptingInterface.serverInjectorGain
live: true
function updateEnvironmentGain(sliderValue) {
if (AudioScriptingInterface.serverInjectorGain !== sliderValue) {
AudioScriptingInterface.serverInjectorGain = sliderValue;
AudioScriptingInterface.localInjectorGain = sliderValue;
}
}
onValueChanged: {
if (AudioScriptingInterface.getInjectorGain() != environmentVolume.value) {
AudioScriptingInterface.setInjectorGain(environmentVolume.value);
updateEnvironmentGain(value);
}
onPressedChanged: {
if (!pressed) {
updateEnvironmentGain(value);
}
}
}
@ -111,14 +130,23 @@ Flickable {
Layout.topMargin: 2
height: 30
labelText: "System Sound Volume"
from: -60
to: 10
from: simplifiedUI.numericConstants.mutedValue
to: 20.0
defaultValue: 0.0
value: AudioScriptingInterface.getSystemInjectorGain()
stepSize: 5.0
value: AudioScriptingInterface.systemInjectorGain
live: true
function updateSystemGain(sliderValue) {
if (AudioScriptingInterface.systemInjectorGain !== sliderValue) {
AudioScriptingInterface.systemInjectorGain = sliderValue;
}
}
onValueChanged: {
if (AudioScriptingInterface.getSystemInjectorGain() != systemSoundVolume.value) {
AudioScriptingInterface.setSystemInjectorGain(systemSoundVolume.value);
updateSystemGain(value);
}
onPressedChanged: {
if (!pressed) {
updateSystemGain(value);
}
}
}
@ -189,7 +217,7 @@ Flickable {
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
interactive: false
height: contentItem.height
spacing: 4
spacing: simplifiedUI.margins.settings.spacingBetweenRadiobuttons
clip: true
model: AudioScriptingInterface.devices.input
delegate: Item {
@ -200,6 +228,8 @@ Flickable {
id: inputDeviceCheckbox
anchors.left: parent.left
width: parent.width - inputLevel.width
height: paintedHeight
wrapLabel: false
checked: selectedDesktop
text: model.devicename
ButtonGroup.group: inputDeviceButtonGroup
@ -288,7 +318,7 @@ Flickable {
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
interactive: false
height: contentItem.height
spacing: 4
spacing: simplifiedUI.margins.settings.spacingBetweenRadiobuttons
clip: true
model: AudioScriptingInterface.devices.output
delegate: Item {
@ -299,8 +329,10 @@ Flickable {
id: outputDeviceCheckbox
anchors.left: parent.left
width: parent.width
height: paintedHeight
checked: selectedDesktop
text: model.devicename
wrapLabel: false
ButtonGroup.group: outputDeviceButtonGroup
onClicked: {
AudioScriptingInterface.setOutputDevice(model.info, false); // `false` argument for Desktop mode setting

View file

@ -20,8 +20,8 @@ Flickable {
id: root
contentWidth: parent.width
contentHeight: generalColumnLayout.height
topMargin: 16
bottomMargin: 16
topMargin: 24
bottomMargin: 24
clip: true
onAvatarNametagModeChanged: {
@ -63,6 +63,7 @@ Flickable {
ColumnLayout {
id: avatarNameTagsRadioButtonGroup
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
spacing: simplifiedUI.margins.settings.spacingBetweenRadiobuttons
SimplifiedControls.RadioButton {
id: avatarNameTagsOff
@ -110,6 +111,7 @@ Flickable {
ColumnLayout {
id: performanceRadioButtonGroup
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
spacing: simplifiedUI.margins.settings.spacingBetweenRadiobuttons
SimplifiedControls.RadioButton {
id: performanceLow
@ -157,6 +159,7 @@ Flickable {
ColumnLayout {
id: cameraRadioButtonGroup
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
spacing: simplifiedUI.margins.settings.spacingBetweenRadiobuttons
SimplifiedControls.RadioButton {
id: firstPerson

View file

@ -19,8 +19,8 @@ Flickable {
id: root
contentWidth: parent.width
contentHeight: vrColumnLayout.height
topMargin: 16
bottomMargin: 16
topMargin: 24
bottomMargin: 24
clip: true
function changePeakValuesEnabled(enabled) {
@ -70,6 +70,7 @@ Flickable {
id: controlsRadioButtonGroup
width: parent.width
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
spacing: simplifiedUI.margins.settings.spacingBetweenRadiobuttons
ButtonGroup { id: controlsButtonGroup }
@ -202,7 +203,7 @@ Flickable {
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
interactive: false
height: contentItem.height
spacing: 4
spacing: simplifiedUI.margins.settings.spacingBetweenRadiobuttons
clip: true
model: AudioScriptingInterface.devices.input
delegate: Item {
@ -301,7 +302,7 @@ Flickable {
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
interactive: false
height: contentItem.height
spacing: 4
spacing: simplifiedUI.margins.settings.spacingBetweenRadiobuttons
clip: true
model: AudioScriptingInterface.devices.output
delegate: Item {

View file

@ -147,7 +147,7 @@ QtObject {
}
readonly property color darkSeparator: "#595959"
readonly property color darkBackground: "#1A1A1A"
readonly property color darkBackground: "#000000"
readonly property color darkBackgroundHighlight: "#575757"
readonly property color highlightOnDark: Qt.rgba(1, 1, 1, 0.2)
readonly property color white: "#FFFFFF"
@ -182,9 +182,10 @@ QtObject {
}
readonly property QtObject settings: QtObject {
property real subtitleTopMargin: 2
property real settingsGroupTopMargin: 10
property real spacingBetweenSettings: 48
property int subtitleTopMargin: 2
property int settingsGroupTopMargin: 24
property int spacingBetweenSettings: 48
property int spacingBetweenRadiobuttons: 14
}
}
@ -220,4 +221,8 @@ QtObject {
}
}
}
readonly property QtObject numericConstants: QtObject {
readonly property real mutedValue: -60.0
}
}

View file

@ -87,7 +87,6 @@ RadioButton {
contentItem: Text {
id: radioButtonLabel
height: root.radioButtonRadius
font.pixelSize: 14
font.family: "Graphik"
font.weight: Font.Normal
@ -99,5 +98,6 @@ RadioButton {
enabled: root.enabled
verticalAlignment: Text.AlignVCenter
leftPadding: radioButtonIndicator.width + root.labelLeftMargin
topPadding: -3 // For perfect alignment when using Graphik
}
}

View file

@ -30,6 +30,7 @@ Item {
property alias live: sliderControl.live
property alias stepSize: sliderControl.stepSize
property alias snapMode: sliderControl.snapMode
property alias pressed: sliderControl.pressed
property real defaultValue: 0.0
HifiStylesUit.GraphikRegular {

View file

@ -203,7 +203,10 @@ Rectangle {
Image {
id: outputDeviceButton
property bool outputMuted: false
property bool outputMuted: AudioScriptingInterface.avatarGain === simplifiedUI.numericConstants.mutedValue &&
AudioScriptingInterface.serverInjectorGain === simplifiedUI.numericConstants.mutedValue &&
AudioScriptingInterface.localInjectorGain === simplifiedUI.numericConstants.mutedValue &&
AudioScriptingInterface.systemInjectorGain === simplifiedUI.numericConstants.mutedValue
source: outputDeviceButton.outputMuted ? "./images/outputDeviceMuted.svg" : "./images/outputDeviceLoud.svg"
anchors.centerIn: parent
width: 20
@ -228,13 +231,16 @@ Rectangle {
}
onClicked: {
Tablet.playSound(TabletEnums.ButtonClick);
outputDeviceButton.outputMuted = !outputDeviceButton.outputMuted;
if (!outputDeviceButton.outputMuted && !AudioScriptingInterface.muted) {
AudioScriptingInterface.muted = true;
}
sendToScript({
"source": "SimplifiedTopBar.qml",
"method": "setOutputMuted",
"data": {
"outputMuted": outputDeviceButton.outputMuted
"outputMuted": !outputDeviceButton.outputMuted
}
});
}
@ -242,6 +248,69 @@ Rectangle {
}
Item {
id: statusButtonContainer
anchors.verticalCenter: parent.verticalCenter
anchors.left: outputDeviceButtonContainer.right
anchors.leftMargin: 8
width: 36
height: width
Rectangle {
id: statusButton
property string currentStatus
anchors.centerIn: parent
anchors.horizontalCenterOffset: 1
anchors.verticalCenterOffset: 2
width: 13
height: width
radius: width/2
visible: false
}
ColorOverlay {
anchors.fill: statusButton
opacity: statusButton.currentStatus ? 1 : 0
source: statusButton
color: if (statusButton.currentStatus === "busy") {
"#ff001a"
} else if (statusButton.currentStatus === "available") {
"#009036"
} else if (statusButton.currentStatus) {
"#ffed00"
}
}
Image {
id: focusIcon
source: "./images/focus.svg"
opacity: statusButtonMouseArea.containsMouse ? 1.0 : (statusButton.currentStatus === "busy" ? 0.7 : 0.3)
anchors.centerIn: parent
width: 36
height: 20
fillMode: Image.PreserveAspectFit
}
MouseArea {
id: statusButtonMouseArea
anchors.fill: parent
enabled: statusButton.currentStatus
hoverEnabled: true
onEntered: {
Tablet.playSound(TabletEnums.ButtonHover);
}
onClicked: {
Tablet.playSound(TabletEnums.ButtonClick);
sendToScript({
"source": "SimplifiedTopBar.qml",
"method": "toggleStatus"
});
}
}
}
Item {
id: hmdButtonContainer
@ -280,11 +349,6 @@ Rectangle {
Tablet.playSound(TabletEnums.ButtonClick);
var displayPluginCount = Window.getDisplayPluginCount();
if (HMD.active) {
// This next line seems backwards and shouldn't be necessary - the NOTIFY handler should
// result in `displayModeImage.source` changing automatically - but that's not working.
// This is working. So, I'm keeping it.
displayModeImage.source = "./images/vrMode.svg";
// Switch to desktop mode - selects first VR display plugin
for (var i = 0; i < displayPluginCount; i++) {
if (!Window.isDisplayPluginHmd(i)) {
@ -293,11 +357,6 @@ Rectangle {
}
}
} else {
// This next line seems backwards and shouldn't be necessary - the NOTIFY handler should
// result in `displayModeImage.source` changing automatically - but that's not working.
// This is working. So, I'm keeping it.
displayModeImage.source = "./images/desktopMode.svg";
// Switch to VR mode - selects first HMD display plugin
for (var i = 0; i < displayPluginCount; i++) {
if (Window.isDisplayPluginHmd(i)) {
@ -397,8 +456,8 @@ Rectangle {
}
break;
case "updateOutputMuted":
outputDeviceButton.outputMuted = message.data.outputMuted;
case "updateStatusButton":
statusButton.currentStatus = message.data.currentStatus;
break;
default:

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="32" height="19.001" fill="none" version="1.1" viewBox="0 0 32 19.001" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<metadata>
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
<dc:title/>
</cc:Work>
</rdf:RDF>
</metadata>
<path d="m7 19h-3.542c-0.46115 0.0089-0.91905-0.0192-1.3443-0.2001-0.42525-0.1808-0.80846-0.4496-1.125-0.7892-0.3234-0.3355-0.57737-0.7329-0.74698-1.1691-0.16962-0.4362-0.25146-0.9023-0.24076-1.3709v-5.47c-0.0066251-0.46326 0.078429-0.92318 0.25019-1.3526 0.17176-0.42944 0.42674-0.81971 0.74988-1.1478 0.32313-0.32808 0.70786-0.58732 1.1316-0.76244 0.42368-0.17511 0.87772-0.26255 1.3353-0.25716h1.4718c0.03732-0.87492 0.24704-1.7335 0.61682-2.5251s0.89216-1.5005 1.5364-2.0849c1.2696-1.2214 3.1558-1.8924 4.9071-1.87l8 2.2471e-5c1.7402-0.025877 3.6023 0.64637 4.8546 1.87 1.2793 1.2015 2.0626 2.8482 2.1927 4.61h1.3178c0.946-0.01712 1.8609 0.3419 2.5484 1 0.3479 0.31321 0.625 0.69864 0.8125 1.13 0.1875 0.43142 0.2809 0.89867 0.274 1.37v5.47c0.0037 0.4767-0.091 0.9488-0.2781 1.3863-0.1871 0.4374-0.376 0.8397-0.7219 1.163-0.3346 0.3376-0.5167 0.5951-0.9542 0.7753-0.4376 0.1803-1.2083 0.2298-1.6807 0.2247h-3.3624v-12.049c0.0051-0.65734-0.1179-1.3092-0.362-1.9184-0.2441-0.60915-0.6044-1.1636-1.0603-1.6316-0.4213-0.48067-0.9384-0.86553-1.5173-1.1292-0.5788-0.26371-1.4283-0.27101-2.0631-0.27147h-8c-0.6475-0.00706-1.4451-0.00352-2.0378 0.26031-0.59274 0.26384-1.1231 0.65262-1.5556 1.1404-0.45594 0.46802-0.81626 1.0225-1.0603 1.6316-0.24406 0.60916-0.35129 1.261-0.34623 1.9184zm-3.4717-2h1.4717v-8.4793h-1.4717c-0.92847 0-1.5283 0.49-1.5283 1.48v5.47c0.01975 0.99 0.59981 1.5293 1.5283 1.5293zm26.472-6.9993c8e-3 -0.84434-0.6554-1.4883-1.5049-1.48h-1.4478v8.4793h1.4478c0.8319 0.0109 1.5175-0.722 1.5049-1.5293z" fill="#fff"/>
</svg>

After

Width:  |  Height:  |  Size: 2 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.2 KiB

View file

@ -49,17 +49,13 @@
#include "DeferredLightingEffect.h"
#include "PickManager.h"
#include "LightingModel.h"
#include "AmbientOcclusionEffect.h"
#include "RenderShadowTask.h"
#include "AntialiasingEffect.h"
#include "scripting/SettingsScriptingInterface.h"
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
#include "SpeechRecognizer.h"
#endif
#include "MeshPartPayload.h"
#include "scripting/RenderScriptingInterface.h"
extern bool DEV_DECIMATE_TEXTURES;
@ -369,45 +365,14 @@ Menu::Menu() {
// Developer > Render >>>
MenuWrapper* renderOptionsMenu = developerMenu->addMenu("Render");
action = addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::AntiAliasing, 0, true);
connect(action, &QAction::triggered, [action] {
auto renderConfig = qApp->getRenderEngine()->getConfiguration();
if (renderConfig) {
auto mainViewJitterCamConfig = renderConfig->getConfig<JitterSample>("RenderMainView.JitterCam");
auto mainViewAntialiasingConfig = renderConfig->getConfig<Antialiasing>("RenderMainView.Antialiasing");
if (mainViewJitterCamConfig && mainViewAntialiasingConfig) {
if (action->isChecked()) {
mainViewJitterCamConfig->play();
mainViewAntialiasingConfig->setDebugFXAA(false);
} else {
mainViewJitterCamConfig->none();
mainViewAntialiasingConfig->setDebugFXAA(true);
}
}
}
});
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::AntiAliasing, 0, RenderScriptingInterface::getInstance()->getAntialiasingEnabled(),
RenderScriptingInterface::getInstance(), SLOT(setAntialiasingEnabled(bool)));
action = addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Shadows, 0, true);
connect(action, &QAction::triggered, [action] {
auto renderConfig = qApp->getRenderEngine()->getConfiguration();
if (renderConfig) {
auto lightingModelConfig = renderConfig->getConfig<MakeLightingModel>("RenderMainView.LightingModel");
if (lightingModelConfig) {
lightingModelConfig->setShadow(action->isChecked());
}
}
});
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Shadows, 0, RenderScriptingInterface::getInstance()->getShadowsEnabled(),
RenderScriptingInterface::getInstance(), SLOT(setShadowsEnabled(bool)));
action = addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::AmbientOcclusion, 0, false);
connect(action, &QAction::triggered, [action] {
auto renderConfig = qApp->getRenderEngine()->getConfiguration();
if (renderConfig) {
auto lightingModelConfig = renderConfig->getConfig<MakeLightingModel>("RenderMainView.LightingModel");
if (lightingModelConfig) {
lightingModelConfig->setAmbientOcclusion(action->isChecked());
}
}
});
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::AmbientOcclusion, 0, RenderScriptingInterface::getInstance()->getAmbientOcclusionEnabled(),
RenderScriptingInterface::getInstance(), SLOT(setAmbientOcclusionEnabled(bool)));
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::WorldAxes);
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DefaultSkybox, 0, true);

View file

@ -2367,6 +2367,11 @@ void MyAvatar::clearJointsData() {
}
void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setSkeletonModelURL", Q_ARG(const QUrl&, skeletonModelURL));
return;
}
_skeletonModelChangeCount++;
int skeletonModelChangeCount = _skeletonModelChangeCount;

View file

@ -2409,7 +2409,7 @@ private:
void updateEyeContactTarget(float deltaTime);
// These are made private for MyAvatar so that you will use the "use" methods instead
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override;
Q_INVOKABLE virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override;
virtual void updatePalms() override {}
void lateUpdatePalms();

View file

@ -75,6 +75,7 @@ int main(int argc, const char* argv[]) {
QCommandLineOption helpOption = parser.addHelpOption();
QCommandLineOption urlOption("url", "", "value");
QCommandLineOption noLauncherOption("no-launcher", "Do not execute the launcher");
QCommandLineOption noUpdaterOption("no-updater", "Do not show auto-updater");
QCommandLineOption checkMinSpecOption("checkMinSpec", "Check if machine meets minimum specifications");
QCommandLineOption runServerOption("runServer", "Whether to run the server");
@ -84,6 +85,7 @@ int main(int argc, const char* argv[]) {
QCommandLineOption overrideScriptsPathOption(SCRIPTS_SWITCH, "set scripts <path>", "path");
parser.addOption(urlOption);
parser.addOption(noLauncherOption);
parser.addOption(noUpdaterOption);
parser.addOption(checkMinSpecOption);
parser.addOption(runServerOption);
@ -106,6 +108,49 @@ int main(int argc, const char* argv[]) {
Q_UNREACHABLE();
}
QString applicationPath;
{
// A temporary application instance is needed to get the location of the running executable
// Tests using high_resolution_clock show that this takes about 30-50 microseconds (on my machine, YMMV)
// If we wanted to avoid the QCoreApplication, we would need to write our own
// cross-platform implementation.
QCoreApplication tempApp(argc, const_cast<char**>(argv));
applicationPath = QCoreApplication::applicationDirPath();
}
static const QString APPLICATION_CONFIG_FILENAME = "config.json";
QDir applicationDir(applicationPath);
QFile configFile(applicationDir.filePath(APPLICATION_CONFIG_FILENAME));
if (configFile.exists()) {
if (!configFile.open(QIODevice::ReadOnly)) {
qWarning() << "Found application config, but could not open it";
} else {
auto contents = configFile.readAll();
QJsonParseError error;
auto doc = QJsonDocument::fromJson(contents, &error);
if (error.error) {
qWarning() << "Found application config, but could not parse it: " << error.errorString();
} else {
static const QString LAUNCHER_PATH_KEY = "launcherPath";
QString launcherPath = doc.object()[LAUNCHER_PATH_KEY].toString();
if (!launcherPath.isEmpty()) {
if (!parser.isSet(noLauncherOption)) {
qDebug() << "Found a launcherPath in application config. Starting launcher.";
QProcess launcher;
launcher.setProgram(launcherPath);
launcher.startDetached();
return 0;
} else {
qDebug() << "Found a launcherPath in application config, but the launcher"
" has been suppressed. Continuing normal execution.";
}
}
}
}
}
// Early check for --traceFile argument
auto tracer = DependencyManager::set<tracing::Tracer>();
const char * traceFile = nullptr;

View file

@ -10,7 +10,6 @@
//
#include "SafeLanding.h"
#include <SharedUtil.h>
#include "EntityTreeRenderer.h"
@ -35,24 +34,26 @@ bool SafeLanding::SequenceLessThan::operator()(const int& a, const int& b) const
}
void SafeLanding::startEntitySequence(QSharedPointer<EntityTreeRenderer> entityTreeRenderer) {
auto entityTree = entityTreeRenderer->getTree();
if (entityTree) {
Locker lock(_lock);
_entityTree = entityTree;
_trackedEntities.clear();
_trackingEntities = true;
_maxTrackedEntityCount = 0;
connect(std::const_pointer_cast<EntityTree>(_entityTree).get(),
&EntityTree::addingEntity, this, &SafeLanding::addTrackedEntity);
connect(std::const_pointer_cast<EntityTree>(_entityTree).get(),
&EntityTree::deletingEntity, this, &SafeLanding::deleteTrackedEntity);
if (!entityTreeRenderer.isNull()) {
auto entityTree = entityTreeRenderer->getTree();
if (entityTree) {
Locker lock(_lock);
_entityTreeRenderer = entityTreeRenderer;
_trackedEntities.clear();
_trackingEntities = true;
_maxTrackedEntityCount = 0;
connect(std::const_pointer_cast<EntityTree>(entityTree).get(),
&EntityTree::addingEntity, this, &SafeLanding::addTrackedEntity, Qt::DirectConnection);
connect(std::const_pointer_cast<EntityTree>(entityTree).get(),
&EntityTree::deletingEntity, this, &SafeLanding::deleteTrackedEntity);
_sequenceNumbers.clear();
_initialStart = INVALID_SEQUENCE;
_initialEnd = INVALID_SEQUENCE;
_startTime = usecTimestampNow();
EntityTreeRenderer::setEntityLoadingPriorityFunction(&ElevatedPriority);
_sequenceNumbers.clear();
_initialStart = INVALID_SEQUENCE;
_initialEnd = INVALID_SEQUENCE;
_startTime = usecTimestampNow();
EntityTreeRenderer::setEntityLoadingPriorityFunction(&ElevatedPriority);
}
}
}
@ -70,7 +71,12 @@ void SafeLanding::stopEntitySequence() {
void SafeLanding::addTrackedEntity(const EntityItemID& entityID) {
if (_trackingEntities) {
Locker lock(_lock);
EntityItemPointer entity = _entityTree->findEntityByID(entityID);
if (_entityTreeRenderer.isNull() || _entityTreeRenderer->getTree() == nullptr) {
return;
}
EntityItemPointer entity = _entityTreeRenderer->getTree()->findEntityByID(entityID);
if (entity && !entity->isLocalEntity() && entity->getCreated() < _startTime) {
@ -111,7 +117,7 @@ bool SafeLanding::isLoadSequenceComplete() {
Locker lock(_lock);
_initialStart = INVALID_SEQUENCE;
_initialEnd = INVALID_SEQUENCE;
_entityTree = nullptr;
_entityTreeRenderer.clear();
_trackingEntities = false; // Don't track anything else that comes in.
EntityTreeRenderer::setEntityLoadingPriorityFunction(StandardPriority);
}
@ -158,7 +164,7 @@ bool SafeLanding::isSequenceNumbersComplete() {
return false;
}
bool isEntityPhysicsReady(const EntityItemPointer& entity) {
bool SafeLanding::isEntityPhysicsReady(const EntityItemPointer& entity) {
if (entity && !entity->getCollisionless()) {
const auto& entityType = entity->getType();
if (entityType == EntityTypes::Model) {
@ -168,7 +174,10 @@ bool isEntityPhysicsReady(const EntityItemPointer& entity) {
bool hasAABox;
entity->getAABox(hasAABox);
if (hasAABox && downloadedCollisionTypes.count(modelEntity->getShapeType()) != 0) {
return (!entity->shouldBePhysical() || entity->isInPhysicsSimulation() || modelEntity->computeShapeFailedToLoad());
auto space = _entityTreeRenderer->getWorkloadSpace();
uint8_t region = space ? space->getRegion(entity->getSpaceIndex()) : (uint8_t)workload::Region::INVALID;
bool shouldBePhysical = region < workload::Region::R3 && entity->shouldBePhysical();
return (!shouldBePhysical || entity->isInPhysicsSimulation() || modelEntity->computeShapeFailedToLoad());
}
}
}

View file

@ -38,13 +38,14 @@ private slots:
private:
bool isSequenceNumbersComplete();
bool isEntityPhysicsReady(const EntityItemPointer& entity);
void debugDumpSequenceIDs() const;
bool isEntityLoadingComplete();
std::mutex _lock;
using Locker = std::lock_guard<std::mutex>;
bool _trackingEntities { false };
EntityTreePointer _entityTree;
QSharedPointer<EntityTreeRenderer> _entityTreeRenderer;
using EntityMap = std::map<EntityItemID, EntityItemPointer>;
EntityMap _trackedEntities;

View file

@ -400,10 +400,19 @@ void Audio::setReverbOptions(const AudioEffectOptions* options) {
}
void Audio::setAvatarGain(float gain) {
bool changed = false;
if (getAvatarGain() != gain) {
changed = true;
}
withWriteLock([&] {
// ask the NodeList to set the master avatar gain
DependencyManager::get<NodeList>()->setAvatarGain(QUuid(), gain);
});
if (changed) {
emit avatarGainChanged(gain);
}
}
float Audio::getAvatarGain() {
@ -413,10 +422,19 @@ float Audio::getAvatarGain() {
}
void Audio::setInjectorGain(float gain) {
bool changed = false;
if (getInjectorGain() != gain) {
changed = true;
}
withWriteLock([&] {
// ask the NodeList to set the audio injector gain
DependencyManager::get<NodeList>()->setInjectorGain(gain);
});
if (changed) {
emit serverInjectorGainChanged(gain);
}
}
float Audio::getInjectorGain() {
@ -426,6 +444,11 @@ float Audio::getInjectorGain() {
}
void Audio::setLocalInjectorGain(float gain) {
bool changed = false;
if (getLocalInjectorGain() != gain) {
changed = true;
}
withWriteLock([&] {
if (_localInjectorGain != gain) {
_localInjectorGain = gain;
@ -436,6 +459,11 @@ void Audio::setLocalInjectorGain(float gain) {
DependencyManager::get<AudioClient>()->setLocalInjectorGain(gain);
}
});
if (changed) {
emit localInjectorGainChanged(gain);
}
}
float Audio::getLocalInjectorGain() {
@ -445,6 +473,11 @@ float Audio::getLocalInjectorGain() {
}
void Audio::setSystemInjectorGain(float gain) {
bool changed = false;
if (getSystemInjectorGain() != gain) {
changed = true;
}
withWriteLock([&] {
if (_systemInjectorGain != gain) {
_systemInjectorGain = gain;
@ -455,6 +488,10 @@ void Audio::setSystemInjectorGain(float gain) {
DependencyManager::get<AudioClient>()->setSystemInjectorGain(gain);
}
});
if (changed) {
emit systemInjectorGainChanged(gain);
}
}
float Audio::getSystemInjectorGain() {

View file

@ -66,6 +66,10 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
* @property {boolean} pushToTalkHMD - <code>true</code> if HMD push-to-talk is enabled, otherwise <code>false</code>.
* @property {boolean} pushingToTalk - <code>true</code> if the user is currently pushing-to-talk, otherwise
* <code>false</code>.
* @property {float} avatarGain - The gain (relative volume) that avatars' voices are played at. This gain is used at the server.
* @property {float} localInjectorGain - The gain (relative volume) that local injectors (local environment sounds) are played at.
* @property {float} serverInjectorGain - The gain (relative volume) that server injectors (server environment sounds) are played at. This gain is used at the server.
* @property {float} systemInjectorGain - The gain (relative volume) that system sounds are played at.
*
* @comment The following properties are from AudioScriptingInterface.h.
* @property {boolean} isStereoInput - <code>true</code> if the input audio is being used in stereo, otherwise
@ -90,6 +94,10 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
Q_PROPERTY(bool pushToTalkDesktop READ getPTTDesktop WRITE setPTTDesktop NOTIFY pushToTalkDesktopChanged)
Q_PROPERTY(bool pushToTalkHMD READ getPTTHMD WRITE setPTTHMD NOTIFY pushToTalkHMDChanged)
Q_PROPERTY(bool pushingToTalk READ getPushingToTalk WRITE setPushingToTalk NOTIFY pushingToTalkChanged)
Q_PROPERTY(float avatarGain READ getAvatarGain WRITE setAvatarGain NOTIFY avatarGainChanged)
Q_PROPERTY(float localInjectorGain READ getLocalInjectorGain WRITE setLocalInjectorGain NOTIFY localInjectorGainChanged)
Q_PROPERTY(float serverInjectorGain READ getInjectorGain WRITE setInjectorGain NOTIFY serverInjectorGainChanged)
Q_PROPERTY(float systemInjectorGain READ getSystemInjectorGain WRITE setSystemInjectorGain NOTIFY systemInjectorGainChanged)
public:
static QString AUDIO;
@ -412,6 +420,38 @@ signals:
*/
void pushingToTalkChanged(bool talking);
/**jsdoc
* Triggered when the avatar gain changes.
* @function Audio.avatarGainChanged
* @param {float} gain - The new avatar gain value.
* @returns {Signal}
*/
void avatarGainChanged(float gain);
/**jsdoc
* Triggered when the local injector gain changes.
* @function Audio.localInjectorGainChanged
* @param {float} gain - The new local injector gain value.
* @returns {Signal}
*/
void localInjectorGainChanged(float gain);
/**jsdoc
* Triggered when the server injector gain changes.
* @function Audio.serverInjectorGainChanged
* @param {float} gain - The new server injector gain value.
* @returns {Signal}
*/
void serverInjectorGainChanged(float gain);
/**jsdoc
* Triggered when the system injector gain changes.
* @function Audio.systemInjectorGainChanged
* @param {float} gain - The new system injector gain value.
* @returns {Signal}
*/
void systemInjectorGainChanged(float gain);
public slots:
/**jsdoc

View file

@ -7,6 +7,9 @@
//
#include "RenderScriptingInterface.h"
#include "LightingModel.h"
#include "AntialiasingEffect.h"
const QString DEFERRED = "deferred";
const QString FORWARD = "forward";
@ -17,6 +20,9 @@ RenderScriptingInterface* RenderScriptingInterface::getInstance() {
RenderScriptingInterface::RenderScriptingInterface() {
setRenderMethod((render::Args::RenderMethod)_renderMethodSetting.get() == render::Args::RenderMethod::DEFERRED ? DEFERRED : FORWARD);
setShadowsEnabled(_shadowsEnabledSetting.get());
setAmbientOcclusionEnabled(_ambientOcclusionEnabledSetting.get());
setAntialiasingEnabled(_antialiasingEnabledSetting.get());
}
QString RenderScriptingInterface::getRenderMethod() {
@ -24,6 +30,11 @@ QString RenderScriptingInterface::getRenderMethod() {
}
void RenderScriptingInterface::setRenderMethod(const QString& renderMethod) {
render::Args::RenderMethod newMethod = renderMethod == FORWARD ? render::Args::RenderMethod::FORWARD : render::Args::RenderMethod::DEFERRED;
if (_renderMethodSetting.get() == newMethod) {
return;
}
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setRenderMethod", Q_ARG(const QString&, renderMethod));
return;
@ -31,14 +42,81 @@ void RenderScriptingInterface::setRenderMethod(const QString& renderMethod) {
auto config = dynamic_cast<task::SwitchConfig*>(qApp->getRenderEngine()->getConfiguration()->getConfig("RenderMainView.DeferredForwardSwitch"));
if (config) {
if (renderMethod == DEFERRED) {
_renderMethodSetting.set(render::Args::RenderMethod::DEFERRED);
config->setBranch(render::Args::RenderMethod::DEFERRED);
emit config->dirtyEnabled();
} else if (renderMethod == FORWARD) {
_renderMethodSetting.set(render::Args::RenderMethod::FORWARD);
config->setBranch(render::Args::RenderMethod::FORWARD);
emit config->dirtyEnabled();
_renderMethodSetting.set(newMethod);
config->setBranch(newMethod);
emit config->dirtyEnabled();
}
}
bool RenderScriptingInterface::getShadowsEnabled() {
return _shadowsEnabledSetting.get();
}
void RenderScriptingInterface::setShadowsEnabled(bool enabled) {
if (_shadowsEnabledSetting.get() == enabled) {
return;
}
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setShadowsEnabled", Q_ARG(bool, enabled));
return;
}
auto lightingModelConfig = qApp->getRenderEngine()->getConfiguration()->getConfig<MakeLightingModel>("RenderMainView.LightingModel");
if (lightingModelConfig) {
Menu::getInstance()->setIsOptionChecked(MenuOption::Shadows, enabled);
_shadowsEnabledSetting.set(enabled);
lightingModelConfig->setShadow(enabled);
}
}
bool RenderScriptingInterface::getAmbientOcclusionEnabled() {
return _ambientOcclusionEnabledSetting.get();
}
void RenderScriptingInterface::setAmbientOcclusionEnabled(bool enabled) {
if (_ambientOcclusionEnabledSetting.get() == enabled) {
return;
}
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setAmbientOcclusionEnabled", Q_ARG(bool, enabled));
return;
}
auto lightingModelConfig = qApp->getRenderEngine()->getConfiguration()->getConfig<MakeLightingModel>("RenderMainView.LightingModel");
if (lightingModelConfig) {
Menu::getInstance()->setIsOptionChecked(MenuOption::AmbientOcclusion, enabled);
_ambientOcclusionEnabledSetting.set(enabled);
lightingModelConfig->setAmbientOcclusion(enabled);
}
}
bool RenderScriptingInterface::getAntialiasingEnabled() {
return _antialiasingEnabledSetting.get();
}
void RenderScriptingInterface::setAntialiasingEnabled(bool enabled) {
if (_antialiasingEnabledSetting.get() == enabled) {
return;
}
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setAntialiasingEnabled", Q_ARG(bool, enabled));
return;
}
auto mainViewJitterCamConfig = qApp->getRenderEngine()->getConfiguration()->getConfig<JitterSample>("RenderMainView.JitterCam");
auto mainViewAntialiasingConfig = qApp->getRenderEngine()->getConfiguration()->getConfig<Antialiasing>("RenderMainView.Antialiasing");
if (mainViewJitterCamConfig && mainViewAntialiasingConfig) {
Menu::getInstance()->setIsOptionChecked(MenuOption::AntiAliasing, enabled);
_antialiasingEnabledSetting.set(enabled);
if (enabled) {
mainViewJitterCamConfig->play();
mainViewAntialiasingConfig->setDebugFXAA(false);
} else {
mainViewJitterCamConfig->none();
mainViewAntialiasingConfig->setDebugFXAA(true);
}
}
}

View file

@ -26,6 +26,9 @@
class RenderScriptingInterface : public QObject {
Q_OBJECT
Q_PROPERTY(QString renderMethod READ getRenderMethod WRITE setRenderMethod)
Q_PROPERTY(bool shadowsEnabled READ getShadowsEnabled WRITE setShadowsEnabled)
Q_PROPERTY(bool ambientOcclusionEnabled READ getAmbientOcclusionEnabled WRITE setAmbientOcclusionEnabled)
Q_PROPERTY(bool antialiasingEnabled READ getAntialiasingEnabled WRITE setAntialiasingEnabled)
public:
RenderScriptingInterface();
@ -37,8 +40,8 @@ public slots:
* Get a config for a job by name
* @function Render.getConfig
* @param {string} name - Can be:
* - <job_name>. Search for the first job named job_name traversing the the sub graph of task and jobs (from this task as root)
* - <parent_name>.[<sub_parent_names>.]<job_name>. Allows you to first look for the parent_name job (from this task as root) and then search from there for the
* - <job_name>: Search for the first job named job_name traversing the the sub graph of task and jobs (from this task as root)
* - <parent_name>.[<sub_parent_names>.]<job_name>: Allows you to first look for the parent_name job (from this task as root) and then search from there for the
* optional sub_parent_names and finally from there looking for the job_name (assuming every job in the path is found)
* @returns {object} The sub job config.
*/
@ -58,8 +61,53 @@ public slots:
*/
void setRenderMethod(const QString& renderMethod);
/**jsdoc
* Whether or not shadows are enabled
* @function Render.getShadowsEnabled
* @returns {bool} <code>true</code> if shadows are enabled, otherwise <code>false</code>
*/
bool getShadowsEnabled();
/**jsdoc
* Enables or disables shadows
* @function Render.setShadowsEnabled
* @param {bool} enabled - <code>true</code> to enable shadows, <code>false</code> to disable them
*/
void setShadowsEnabled(bool enabled);
/**jsdoc
* Whether or not ambient occlusion is enabled
* @function Render.getAmbientOcclusionEnabled
* @returns {bool} <code>true</code> if ambient occlusion is enabled, otherwise <code>false</code>
*/
bool getAmbientOcclusionEnabled();
/**jsdoc
* Enables or disables ambient occlusion
* @function Render.setAmbientOcclusionEnabled
* @param {bool} enabled - <code>true</code> to enable ambient occlusion, <code>false</code> to disable it
*/
void setAmbientOcclusionEnabled(bool enabled);
/**jsdoc
* Whether or not anti-aliasing is enabled
* @function Render.getAntialiasingEnabled
* @returns {bool} <code>true</code> if anti-aliasing is enabled, otherwise <code>false</code>
*/
bool getAntialiasingEnabled();
/**jsdoc
* Enables or disables anti-aliasing
* @function Render.setAntialiasingEnabled
* @param {bool} enabled - <code>true</code> to enable anti-aliasing, <code>false</code> to disable it
*/
void setAntialiasingEnabled(bool enabled);
private:
Setting::Handle<int> _renderMethodSetting { "renderMethod", RENDER_FORWARD ? render::Args::RenderMethod::FORWARD : render::Args::RenderMethod::DEFERRED };
Setting::Handle<bool> _shadowsEnabledSetting { "shadowsEnabled", true };
Setting::Handle<bool> _ambientOcclusionEnabledSetting { "ambientOcclusionEnabled", false };
Setting::Handle<bool> _antialiasingEnabledSetting { "antialiasingEnabled", true };
};
#endif // hifi_RenderScriptingInterface_h

View file

@ -44,13 +44,14 @@ SelectionScriptingInterface::SelectionScriptingInterface() {
}
/**jsdoc
* The type of a specific item in a selection list.
* <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>"avatar"</code></td><td>The item is an avatar.</td></tr>
* <tr><td><code>"entity"</code></td><td>The item is an entity.</td></tr>
* </tbody>
* </table>
* @typedef {string} Selection.ItemType
@ -245,9 +246,10 @@ void SelectionScriptingInterface::printList(const QString& listName) {
}
/**jsdoc
* A selection list.
* @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[]} avatars - The IDs of the avatars in the selection list.
* @property {Uuid[]} entities - The IDs of the entities in the selection list.
*/
QVariantMap SelectionScriptingInterface::getSelectedItemsList(const QString& listName) const {
QReadLocker lock(&_selectionListsLock);
@ -438,18 +440,19 @@ bool SelectionHighlightStyle::fromVariantMap(const QVariantMap& properties) {
}
/**jsdoc
* The highlighting style of a selection list.
* @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.
* @property {Color} outlineUnoccludedColor=255,178,51 - Unoccluded outline color.
* @property {Color} outlineOccludedColor=255,178,51 - Occluded outline color.
* @property {Color} fillUnoccludedColor=51,178,255 - Unoccluded fill color.
* @property {Color} fillOccludedColor=51,178,255 - Occluded fill color.
* @property {number} outlineUnoccludedAlpha=0.9 - Unoccluded outline alpha, range <code>0.0</code> &ndash; <code>1.0</code>.
* @property {number} outlineOccludedAlpha=0.9 - Occluded outline alpha, range <code>0.0</code> &ndash; <code>1.0</code>.
* @property {number} fillUnoccludedAlpha=0.0 - Unoccluded fill alpha, range <code>0.0</code> &ndash; <code>1.0</code>.
* @property {number} fillOccludedAlpha=0.0 - Occluded fill alpha, range <code>0.0</code> &ndash; <code>1.0</code>.
* @property {number} outlineWidth=2 - Width of the outline, in pixels.
* @property {boolean} isOutlineSmooth=false - <code>true</code> to fade the outside edge of the outline, <code>false</code>
* to have a sharp edge.
*/
QVariantMap SelectionHighlightStyle::toVariantMap() const {
QVariantMap properties;

View file

@ -77,47 +77,45 @@ protected:
};
/**jsdoc
* The <code>Selection</code> API provides a means of grouping together avatars and entities in named lists.
* The <code>Selection</code> API provides a means of grouping together and highlighting avatars and entities in named lists.
*
* @namespace Selection
*
* @hifi-interface
* @hifi-client-entity
* @hifi-avatar
*
* @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.
* @example <caption>Outline an entity when it is grabbed by the mouse or a controller.</caption>
* // Create an entity and copy the following script into the entity's "Script URL" field.
* // Move the entity behind another entity to see the occluded outline.
* (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;
* }
* var LIST_NAME = "SelectionExample",
* ITEM_TYPE = "entity",
* HIGHLIGHT_STYLE = {
* outlineUnoccludedColor: { red: 0, green: 180, blue: 239 },
* outlineUnoccludedAlpha: 0.5,
* outlineOccludedColor: { red: 239, green: 180, blue: 0 },
* outlineOccludedAlpha: 0.5,
* outlineWidth: 4
* };
*
* Selection.enableListHighlight(LIST_NAME, HIGHLIGHT_STYLE);
*
* this.startNearGrab = function (entityID) {
* Selection.addToSelectedItemsList(LIST_NAME, ITEM_TYPE, entityID);
* };
*
* _this.releaseGrab = function(entityID){
* if (prevID !== 0) {
* Selection.removeFromSelectedItemsList("contextOverlayHighlightList", listType, prevID);
* prevID = 0;
* }
*
* this.startDistanceGrab = function (entityID) {
* Selection.addToSelectedItemsList(LIST_NAME, ITEM_TYPE, entityID);
* };
*
* var cleanup = function(){
* Entities.findEntities(MyAvatar.position, 1000).forEach(function(entity) {
* try {
* Selection.removeListFromMap(listName);
* } catch (e) {
* print("Error cleaning up.");
* }
* });
*
* this.releaseGrab = function (entityID) {
* Selection.removeFromSelectedItemsList(LIST_NAME, ITEM_TYPE, entityID);
* };
*
* Script.scriptEnding.connect(cleanup);
*
* Script.scriptEnding.connect(function () {
* Selection.removeListFromMap(LIST_NAME);
* });
* });
*/
class SelectionScriptingInterface : public QObject, public Dependency {
@ -127,121 +125,119 @@ public:
SelectionScriptingInterface();
/**jsdoc
* Get the names of all the selection lists.
* @function Selection.getListNames
* @returns {list[]} An array of names of all the selection lists.
*/
* Gets the names of all current selection lists.
* @function Selection.getListNames
* @returns {string[]} The names of all current selection lists.
* @example <caption>List all the current selection lists.</caption>
* print("Selection lists: " + Selection.getListNames());
*/
Q_INVOKABLE QStringList getListNames() const;
/**jsdoc
* Delete a named selection list.
* @function Selection.removeListFromMap
* @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>.
*/
* Deletes a selection list.
* @function Selection.removeListFromMap
* @param {string} listName - The name of the selection list to delete.
* @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 to a selection list.
* @function Selection.addToSelectedItemsList
* @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>.
*/
* Adds an item to a selection list. The list is created if it doesn't exist.
* @function Selection.addToSelectedItemsList
* @param {string} listName - The name of the selection list to add the item to.
* @param {Selection.ItemType} itemType - The type of item being added.
* @param {Uuid} itemID - The ID of the item to add.
* @returns {boolean} <code>true</code> if the item was successfully added or already existed in the list, otherwise
* <code>false</code>.
*/
Q_INVOKABLE bool addToSelectedItemsList(const QString& listName, const QString& itemType, const QUuid& id);
/**jsdoc
* Remove an item from a selection list.
* @function Selection.removeFromSelectedItemsList
* @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.
*/
* Removes an item from a selection list.
* @function Selection.removeFromSelectedItemsList
* @param {string} listName - The name of the selection list to remove the item from.
* @param {Selection.ItemType} itemType - The type of item being removed.
* @param {Uuid} itemID - The ID of the item to remove.
* @returns {boolean} <code>true</code> if the item was successfully removed or was not in the list, otherwise
* <code>false</code>.
*/
Q_INVOKABLE bool removeFromSelectedItemsList(const QString& listName, const QString& itemType, const QUuid& id);
/**jsdoc
* Remove all items from a selection.
* @function Selection.clearSelectedItemsList
* @param {string} listName - The name of the selection list.
* @returns {boolean} <code>true</code> if the item was successfully cleared, otherwise <code>false</code>.
*/
* Removes all items from a selection list.
* @function Selection.clearSelectedItemsList
* @param {string} listName - The name of the selection list.
* @returns {boolean} <code>true</code> always.
*/
Q_INVOKABLE bool clearSelectedItemsList(const QString& listName);
/**jsdoc
* Print out the list of avatars and entities in a selection to the <em>debug log</em> (not the script log).
* @function Selection.printList
* @param {string} listName - The name of the selection list.
*/
* Prints the list of avatars and entities in a selection to the program log (but not the Script Log window).
* @function Selection.printList
* @param {string} listName - The name of the selection list.
*/
Q_INVOKABLE void printList(const QString& listName);
/**jsdoc
* Get the list of avatars and entities stored in a selection list.
* @function Selection.getSelectedItemsList
* @param {string} listName - The name of the selection list.
* @returns {Selection.SelectedItemsList} The content of a selection list. If the list name doesn't exist, the function
* returns an empty object with no properties.
*/
* Gets the list of avatars and entities in a selection list.
* @function Selection.getSelectedItemsList
* @param {string} listName - The name of the selection list.
* @returns {Selection.SelectedItemsList} The content of the selection list if the list exists, otherwise an empty object.
*/
Q_INVOKABLE QVariantMap getSelectedItemsList(const QString& listName) const;
/**jsdoc
* Get the names of the highlighted selection lists.
* @function Selection.getHighlightedListNames
* @returns {string[]} An array of names of the selection list currently highlight enabled.
*/
* Gets the names of all current selection lists that have highlighting enabled.
* @function Selection.getHighlightedListNames
* @returns {string[]} The names of the selection lists that currently have highlighting enabled.
*/
Q_INVOKABLE QStringList getHighlightedListNames() const;
/**jsdoc
* 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 {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.
*/
* Enables highlighting for a selection list. All items in or subsequently added to the list are displayed with the
* highlight effect specified. The method can be called multiple times with different values in the style to modify the
* highlighting.
* <p>Note: This function implicitly calls {@link Selection.enableListToScene|enableListToScene}.</p>
* @function Selection.enableListHighlight
* @param {string} listName - The name of the selection list.
* @param {Selection.HighlightStyle} highlightStyle - The highlight style.
* @returns {boolean} <code>true</code> always.
*/
Q_INVOKABLE bool enableListHighlight(const QString& listName, const QVariantMap& highlightStyle);
/**jsdoc
* 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 {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>.
*/
* Disables highlighting for a selection list.
* <p>Note: This function implicitly calls {@link Selection.disableListToScene|disableListToScene}.</p>
* @function Selection.disableListHighlight
* @param {string} listName - The name of the selection list.
* @returns {boolean} <code>true</code> always.
*/
Q_INVOKABLE bool disableListHighlight(const QString& listName);
/**jsdoc
* 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 {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>.
*/
* Enables scene selection for a selection list. All items in or subsequently added to the list are sent to a scene
* selection in the rendering engine for debugging purposes.
* @function Selection.enableListToScene
* @param {string} listName - The name of the selection list.
* @returns {boolean} <code>true</code> always.
*/
Q_INVOKABLE bool enableListToScene(const QString& listName);
/**jsdoc
* Disable scene selection for the named selection.
* 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 {string} listName - The name of the selection list.
* @returns {boolean} true if the selection was successfully disabled on the scene, false otherwise.
*/
* Disables scene selection for a selection list.
* @function Selection.disableListToScene
* @param {string} listName - The name of the selection list.
* @returns {boolean} <code>true</code> always.
*/
Q_INVOKABLE bool disableListToScene(const QString& listName);
/**jsdoc
* 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 {string} listName - The name of the selection list.
* @returns {Selection.HighlightStyle} highlight style
*/
* Gets the current highlighting style for a selection list.
* @function Selection.getListHighlightStyle
* @param {string} listName - The name of the selection list.
* @returns {Selection.HighlightStyle} The highlight style of the selection list if the list exists and highlighting is
* enabled, otherwise an empty object.
*/
Q_INVOKABLE QVariantMap getListHighlightStyle(const QString& listName) const;
@ -253,7 +249,7 @@ public:
signals:
/**jsdoc
* Triggered when a list's content changes.
* Triggered when a selection list's content changes or the list is deleted.
* @function Selection.selectedItemsListChanged
* @param {string} listName - The name of the selection list that changed.
* @returns {Signal}
@ -276,7 +272,6 @@ private:
void setupHandler(const QString& selectionName);
void removeHandler(const QString& selectionName);
};
#endif // hifi_SelectionScriptingInterface_h

View file

@ -16,7 +16,8 @@
#include <QString>
/**jsdoc
* The Settings API provides a facility to store and retrieve values that persist between Interface runs.
* The <code>Settings</code> API provides a facility to store and retrieve values that persist between Interface runs.
*
* @namespace Settings
*
* @hifi-interface
@ -33,7 +34,7 @@ public:
public slots:
/**jsdoc
* Retrieve the value from a named setting.
* Retrieves the value from a named setting.
* @function Settings.getValue
* @param {string} key - The name of the setting.
* @param {string|number|boolean|object} [defaultValue=""] - The value to return if the setting doesn't exist.
@ -50,8 +51,8 @@ public slots:
QVariant getValue(const QString& setting, const QVariant& defaultValue);
/**jsdoc
* Store a value in a named setting. If the setting already exists its value is overwritten, otherwise a new setting is
* created. If the value is set to <code>null</code> or <code>undefined</code>, the setting is deleted.
* Stores a value in a named setting. If the setting already exists, its value is overwritten. If the value is
* <code>null</code> or <code>undefined</code>, the setting is deleted.
* @function Settings.setValue
* @param {string} key - The name of the setting. Be sure to use a unique name if creating a new setting.
* @param {string|number|boolean|object|undefined} value - The value to store in the setting. If <code>null</code> or

View file

@ -313,8 +313,9 @@ public slots:
* Takes a snapshot of the current Interface view from the primary camera. When a still image only is captured,
* {@link Window.stillSnapshotTaken|stillSnapshotTaken} is emitted; when a still image plus moving images are captured,
* {@link Window.processingGifStarted|processingGifStarted} and {@link Window.processingGifCompleted|processingGifCompleted}
* are emitted. The path to store the snapshots and the length of the animated GIF to capture are specified in Settings >
* General > Snapshots.
* are emitted.
* <p>Snapshots are saved to the path specified in Settings &gt; General &gt; Snapshots, which can be accessed via the
* {@link Snapshot} API.</p>
*
* @function Window.takeSnapshot
* @param {boolean} [notify=true] - This value is passed on through the {@link Window.stillSnapshotTaken|stillSnapshotTaken}
@ -351,13 +352,15 @@ public slots:
* var notify = true;
* var animated = true;
* var aspect = 1920 / 1080;
* var filename = "";
* var filename = "example-snapshot";
* Window.takeSnapshot(notify, animated, aspect, filename);
*/
void takeSnapshot(bool notify = true, bool includeAnimated = false, float aspectRatio = 0.0f, const QString& filename = QString());
/**jsdoc
* Takes a still snapshot of the current view from the secondary camera that can be set up through the {@link Render} API.
* <p>Snapshots are saved to the path specified in Settings &gt; General &gt; Snapshots, which can be accessed via the
* {@link Snapshot} API.</p>
* @function Window.takeSecondaryCameraSnapshot
* @param {boolean} [notify=true] - This value is passed on through the {@link Window.stillSnapshotTaken|stillSnapshotTaken}
* signal.
@ -372,6 +375,8 @@ public slots:
/**jsdoc
* Takes a 360&deg; snapshot at a given position for the secondary camera. The secondary camera does not need to have been
* set up.
* <p>Snapshots are saved to the path specified in Settings &gt; General &gt; Snapshots, which can be accessed via the
* {@link Snapshot} API.</p>
* @function Window.takeSecondaryCamera360Snapshot
* @param {Vec3} cameraPosition - The position of the camera for the snapshot.
* @param {boolean} [cubemapOutputFormat=false] - If <code>true</code> then the snapshot is saved as a cube map image,

View file

@ -38,6 +38,10 @@ private:
/**jsdoc
* The <code>Snapshot</code> API provides access to the path that snapshots are saved to. This path is that provided in
* Settings &gt; General &gt; Snapshots. Snapshots may be taken using <code>Window</code> API functions such as
* {@link Window.takeSnapshot}.
*
* @namespace Snapshot
*
* @hifi-interface
@ -64,23 +68,31 @@ public:
signals:
/**jsdoc
* Triggered when the path that snapshots are saved to is changed.
* @function Snapshot.snapshotLocationSet
* @param {string} location
* @param {string} location - The new snapshots location.
* @returns {Signal}
* @example <caption>Report when the snapshots location is changed.</caption>
* // Run this script then change the snapshots location in Settings > General > Snapshots.
* Snapshot.snapshotLocationSet.connect(function (path) {
* print("New snapshot location: " + path);
* });
*/
void snapshotLocationSet(const QString& value);
public slots:
/**jsdoc
* Gets the path that snapshots are saved to.
* @function Snapshot.getSnapshotsLocation
* @returns {string}
* @returns {string} The path to save snapshots to.
*/
Q_INVOKABLE QString getSnapshotsLocation();
/**jsdoc
* Sets the path that snapshots are saved to.
* @function Snapshot.setSnapshotsLocation
* @param {String} location
* @param {String} location - The path to save snapshots to.
*/
Q_INVOKABLE void setSnapshotsLocation(const QString& location);

View file

@ -314,10 +314,11 @@ class Stats : public QQuickItem {
STATS_PROPERTY(QVector3D, parabolaPicksUpdated, QVector3D(0, 0, 0))
STATS_PROPERTY(QVector3D, collisionPicksUpdated, QVector3D(0, 0, 0))
#ifdef DEBUG_EVENT_QUEUE
STATS_PROPERTY(bool, eventQueueDebuggingOn, true)
STATS_PROPERTY(int, mainThreadQueueDepth, -1);
STATS_PROPERTY(int, nodeListThreadQueueDepth, -1);
#ifdef DEBUG_EVENT_QUEUE
STATS_PROPERTY(bool, eventQueueDebuggingOn, true)
#else
STATS_PROPERTY(bool, eventQueueDebuggingOn, false)
#endif // DEBUG_EVENT_QUEUE

View file

@ -339,7 +339,7 @@ public:
*/
Q_INVOKABLE glm::quat jointToWorldRotation(const glm::quat& rotation, const int jointIndex = -1) const;
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override;
Q_INVOKABLE virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override;
virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData) override;
void updateDisplayNameAlpha(bool showDisplayName);

View file

@ -1209,7 +1209,7 @@ public:
const QString& getDisplayName() const { return _displayName; }
const QString& getSessionDisplayName() const { return _sessionDisplayName; }
bool getLookAtSnappingEnabled() const { return _lookAtSnappingEnabled; }
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL);
Q_INVOKABLE virtual void setSkeletonModelURL(const QUrl& skeletonModelURL);
virtual void setDisplayName(const QString& displayName);
virtual void setSessionDisplayName(const QString& sessionDisplayName) {

View file

@ -15,7 +15,7 @@
// These properties have JSDoc documentation in HMDScriptingInterface.h.
class AbstractHMDScriptingInterface : public QObject {
Q_OBJECT
Q_PROPERTY(bool active READ isHMDMode NOTIFY mountedChanged)
Q_PROPERTY(bool active READ isHMDMode NOTIFY displayModeChanged)
Q_PROPERTY(float ipd READ getIPD)
Q_PROPERTY(float eyeHeight READ getEyeHeight)
Q_PROPERTY(float playerHeight READ getPlayerHeight)

View file

@ -363,7 +363,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) {
ShapeType type = getShapeType();
auto model = getModel();
if (!model) {
if (!model || !model->isLoaded()) {
type = SHAPE_TYPE_NONE;
}

View file

@ -1770,6 +1770,7 @@ signals:
/**jsdoc
* Triggered on the client that is the physics simulation owner during the collision of two entities. Note: Isn't triggered
* for a collision with an avatar.
* <p>See also, {@link Script.addEventHandler}.</p>
* @function Entities.collisionWithEntity
* @param {Uuid} idA - The ID of one entity in the collision. For an entity script, this is the ID of the entity containing
* the script.
@ -1882,6 +1883,7 @@ signals:
/**jsdoc
* Triggered when a mouse button is clicked while the mouse cursor is on an entity, or a controller trigger is fully
* pressed while its laser is on an entity.
* <p>See also, {@link Script.addEventHandler}.</p>
* @function Entities.mousePressOnEntity
* @param {Uuid} entityID - The ID of the entity that was pressed.
* @param {PointerEvent} event - Details of the event.
@ -1906,6 +1908,7 @@ signals:
/**jsdoc
* Repeatedly triggered while the mouse cursor or controller laser moves on an entity.
* <p>See also, {@link Script.addEventHandler}.</p>
* @function Entities.mouseMoveOnEntity
* @param {Uuid} entityID - The ID of the entity that was moved on.
* @param {PointerEvent} event - Details of the event.
@ -1916,6 +1919,7 @@ signals:
/**jsdoc
* Triggered when a mouse button is released after clicking on an entity or the controller trigger is partly or fully
* released after pressing on an entity, even if the mouse pointer or controller laser has moved off the entity.
* <p>See also, {@link Script.addEventHandler}.</p>
* @function Entities.mouseReleaseOnEntity
* @param {Uuid} entityID - The ID of the entity that was originally pressed.
* @param {PointerEvent} event - Details of the event.
@ -1942,6 +1946,7 @@ signals:
/**jsdoc
* Triggered when a mouse button is clicked while the mouse cursor is on an entity. Note: Not triggered by controller.
* <p>See also, {@link Script.addEventHandler}.</p>
* @function Entities.clickDownOnEntity
* @param {Uuid} entityID - The ID of the entity that was clicked.
* @param {PointerEvent} event - Details of the event.
@ -1952,6 +1957,7 @@ signals:
/**jsdoc
* Repeatedly triggered while a mouse button continues to be held after clicking an entity, even if the mouse cursor has
* moved off the entity. Note: Not triggered by controller.
* <p>See also, {@link Script.addEventHandler}.</p>
* @function Entities.holdingClickOnEntity
* @param {Uuid} entityID - The ID of the entity that was originally clicked.
* @param {PointerEvent} event - Details of the event.
@ -1962,6 +1968,7 @@ signals:
/**jsdoc
* Triggered when a mouse button is released after clicking on an entity, even if the mouse cursor has moved off the
* entity. Note: Not triggered by controller.
* <p>See also, {@link Script.addEventHandler}.</p>
* @function Entities.clickReleaseOnEntity
* @param {Uuid} entityID - The ID of the entity that was originally clicked.
* @param {PointerEvent} event - Details of the event.
@ -1971,6 +1978,7 @@ signals:
/**jsdoc
* Triggered when the mouse cursor or controller laser starts hovering on an entity.
* <p>See also, {@link Script.addEventHandler}.</p>
* @function Entities.hoverEnterEntity
* @param {Uuid} entityID - The ID of the entity that is being hovered.
* @param {PointerEvent} event - Details of the event.
@ -1980,6 +1988,7 @@ signals:
/**jsdoc
* Repeatedly triggered when the mouse cursor or controller laser moves while hovering over an entity.
* <p>See also, {@link Script.addEventHandler}.</p>
* @function Entities.hoverOverEntity
* @param {Uuid} entityID - The ID of the entity that is being hovered.
* @param {PointerEvent} event - Details of the event.
@ -1989,6 +1998,7 @@ signals:
/**jsdoc
* Triggered when the mouse cursor or controller laser stops hovering over an entity.
* <p>See also, {@link Script.addEventHandler}.</p>
* @function Entities.hoverLeaveEntity
* @param {Uuid} entityID - The ID of the entity that was being hovered.
* @param {PointerEvent} event - Details of the event.
@ -1999,6 +2009,7 @@ signals:
/**jsdoc
* Triggered when an avatar enters an entity.
* <p>See also, {@link Script.addEventHandler}.</p>
* @function Entities.enterEntity
* @param {Uuid} entityID - The ID of the entity that the avatar entered.
* @returns {Signal}
@ -2032,6 +2043,7 @@ signals:
/**jsdoc
* Triggered when an avatar leaves an entity.
* <p>See also, {@link Script.addEventHandler}.</p>
* @function Entities.leaveEntity
* @param {Uuid} entityID - The ID of the entity that the avatar left.
* @returns {Signal}

View file

@ -27,7 +27,7 @@ namespace baker {
class GetModelPartsTask {
public:
using Input = hfm::Model::Pointer;
using Output = VaryingSet6<std::vector<hfm::Mesh>, hifi::URL, baker::MeshIndicesToModelNames, baker::BlendshapesPerMesh, QHash<QString, hfm::Material>, std::vector<hfm::Joint>>;
using Output = VaryingSet5<std::vector<hfm::Mesh>, hifi::URL, baker::MeshIndicesToModelNames, baker::BlendshapesPerMesh, std::vector<hfm::Joint>>;
using JobModel = Job::ModelIO<GetModelPartsTask, Input, Output>;
void run(const BakeContextPointer& context, const Input& input, Output& output) {
@ -40,8 +40,7 @@ namespace baker {
for (int i = 0; i < hfmModelIn->meshes.size(); i++) {
blendshapesPerMesh.push_back(hfmModelIn->meshes[i].blendshapes.toStdVector());
}
output.edit4() = hfmModelIn->materials;
output.edit5() = hfmModelIn->joints.toStdVector();
output.edit4() = hfmModelIn->joints.toStdVector();
}
};
@ -134,17 +133,16 @@ namespace baker {
const auto url = modelPartsIn.getN<GetModelPartsTask::Output>(1);
const auto meshIndicesToModelNames = modelPartsIn.getN<GetModelPartsTask::Output>(2);
const auto blendshapesPerMeshIn = modelPartsIn.getN<GetModelPartsTask::Output>(3);
const auto materials = modelPartsIn.getN<GetModelPartsTask::Output>(4);
const auto jointsIn = modelPartsIn.getN<GetModelPartsTask::Output>(5);
const auto jointsIn = modelPartsIn.getN<GetModelPartsTask::Output>(4);
// Calculate normals and tangents for meshes and blendshapes if they do not exist
// Note: Normals are never calculated here for OBJ models. OBJ files optionally define normals on a per-face basis, so for consistency normals are calculated beforehand in OBJSerializer.
const auto normalsPerMesh = model.addJob<CalculateMeshNormalsTask>("CalculateMeshNormals", meshesIn);
const auto calculateMeshTangentsInputs = CalculateMeshTangentsTask::Input(normalsPerMesh, meshesIn, materials).asVarying();
const auto calculateMeshTangentsInputs = CalculateMeshTangentsTask::Input(normalsPerMesh, meshesIn).asVarying();
const auto tangentsPerMesh = model.addJob<CalculateMeshTangentsTask>("CalculateMeshTangents", calculateMeshTangentsInputs);
const auto calculateBlendshapeNormalsInputs = CalculateBlendshapeNormalsTask::Input(blendshapesPerMeshIn, meshesIn).asVarying();
const auto normalsPerBlendshapePerMesh = model.addJob<CalculateBlendshapeNormalsTask>("CalculateBlendshapeNormals", calculateBlendshapeNormalsInputs);
const auto calculateBlendshapeTangentsInputs = CalculateBlendshapeTangentsTask::Input(normalsPerBlendshapePerMesh, blendshapesPerMeshIn, meshesIn, materials).asVarying();
const auto calculateBlendshapeTangentsInputs = CalculateBlendshapeTangentsTask::Input(normalsPerBlendshapePerMesh, blendshapesPerMeshIn, meshesIn).asVarying();
const auto tangentsPerBlendshapePerMesh = model.addJob<CalculateBlendshapeTangentsTask>("CalculateBlendshapeTangents", calculateBlendshapeTangentsInputs);
// Build the graphics::MeshPointer for each hfm::Mesh

View file

@ -19,7 +19,6 @@ void CalculateBlendshapeTangentsTask::run(const baker::BakeContextPointer& conte
const auto& normalsPerBlendshapePerMesh = input.get0();
const auto& blendshapesPerMesh = input.get1();
const auto& meshes = input.get2();
const auto& materials = input.get3();
auto& tangentsPerBlendshapePerMeshOut = output;
tangentsPerBlendshapePerMeshOut.reserve(normalsPerBlendshapePerMesh.size());
@ -30,16 +29,6 @@ void CalculateBlendshapeTangentsTask::run(const baker::BakeContextPointer& conte
tangentsPerBlendshapePerMeshOut.emplace_back();
auto& tangentsPerBlendshapeOut = tangentsPerBlendshapePerMeshOut[tangentsPerBlendshapePerMeshOut.size()-1];
// Check if we actually need to calculate the tangents, or just append empty arrays
bool needTangents = false;
for (const auto& meshPart : mesh.parts) {
auto materialIt = materials.find(meshPart.materialID);
if (materialIt != materials.end() && (*materialIt).needTangentSpace()) {
needTangents = true;
break;
}
}
for (size_t j = 0; j < blendshapes.size(); j++) {
const auto& blendshape = blendshapes[j];
const auto& tangentsIn = blendshape.tangents;
@ -53,8 +42,8 @@ void CalculateBlendshapeTangentsTask::run(const baker::BakeContextPointer& conte
continue;
}
// Check if we can and should calculate tangents (we need normals to calculate the tangents)
if (normals.empty() || !needTangents) {
// Check if we can calculate tangents (we need normals and texcoords to calculate the tangents)
if (normals.empty() || normals.size() != (size_t)mesh.texCoords.size()) {
continue;
}
tangentsOut.resize(normals.size());

View file

@ -18,7 +18,7 @@
// Calculate blendshape tangents if not already present in the blendshape
class CalculateBlendshapeTangentsTask {
public:
using Input = baker::VaryingSet4<std::vector<baker::NormalsPerBlendshape>, baker::BlendshapesPerMesh, std::vector<hfm::Mesh>, QHash<QString, hfm::Material>>;
using Input = baker::VaryingSet3<std::vector<baker::NormalsPerBlendshape>, baker::BlendshapesPerMesh, std::vector<hfm::Mesh>>;
using Output = std::vector<baker::TangentsPerBlendshape>;
using JobModel = baker::Job::ModelIO<CalculateBlendshapeTangentsTask, Input, Output>;

View file

@ -13,21 +13,9 @@
#include "ModelMath.h"
bool needTangents(const hfm::Mesh& mesh, const QHash<QString, hfm::Material>& materials) {
// Check if we actually need to calculate the tangents
for (const auto& meshPart : mesh.parts) {
auto materialIt = materials.find(meshPart.materialID);
if (materialIt != materials.end() && (*materialIt).needTangentSpace()) {
return true;
}
}
return false;
}
void CalculateMeshTangentsTask::run(const baker::BakeContextPointer& context, const Input& input, Output& output) {
const auto& normalsPerMesh = input.get0();
const std::vector<hfm::Mesh>& meshes = input.get1();
const auto& materials = input.get2();
auto& tangentsPerMeshOut = output;
tangentsPerMeshOut.reserve(meshes.size());
@ -39,10 +27,10 @@ void CalculateMeshTangentsTask::run(const baker::BakeContextPointer& context, co
auto& tangentsOut = tangentsPerMeshOut[tangentsPerMeshOut.size()-1];
// Check if we already have tangents and therefore do not need to do any calculation
// Otherwise confirm if we have the normals needed, and need to calculate the tangents
// Otherwise confirm if we have the normals and texcoords needed
if (!tangentsIn.empty()) {
tangentsOut = tangentsIn.toStdVector();
} else if (!normals.empty() && needTangents(mesh, materials)) {
} else if (!normals.empty() && mesh.vertices.size() == mesh.texCoords.size()) {
tangentsOut.resize(normals.size());
baker::calculateTangents(mesh,
[&mesh, &normals, &tangentsOut](int firstIndex, int secondIndex, glm::vec3* outVertices, glm::vec2* outTexCoords, glm::vec3& outNormal) {

View file

@ -22,7 +22,7 @@ class CalculateMeshTangentsTask {
public:
using NormalsPerMesh = std::vector<std::vector<glm::vec3>>;
using Input = baker::VaryingSet3<baker::NormalsPerMesh, std::vector<hfm::Mesh>, QHash<QString, hfm::Material>>;
using Input = baker::VaryingSet2<baker::NormalsPerMesh, std::vector<hfm::Mesh>>;
using Output = baker::TangentsPerMesh;
using JobModel = baker::Job::ModelIO<CalculateMeshTangentsTask, Input, Output>;

View file

@ -91,10 +91,10 @@ NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort)
connect(accountManager.data(), &AccountManager::newKeypair, this, &NodeList::sendDomainServerCheckIn);
// clear out NodeList when login is finished and we know our new username
connect(accountManager.data(), SIGNAL(usernameChanged(QString)) , this, SLOT(reset()));
connect(accountManager.data(), &AccountManager::usernameChanged , this, [this]{ reset("Username changed"); });
// clear our NodeList when logout is requested
connect(accountManager.data(), SIGNAL(logoutComplete()) , this, SLOT(reset()));
connect(accountManager.data(), &AccountManager::logoutComplete , this, [this]{ reset("Logged out"); });
// anytime we get a new node we will want to attempt to punch to it
connect(this, &LimitedNodeList::nodeAdded, this, &NodeList::startNodeHolePunch);
@ -412,6 +412,8 @@ void NodeList::sendDomainServerCheckIn() {
packetStream << FingerprintUtils::getMachineFingerprint();
}
packetStream << usecTimestampNow();
// pack our data to send to the domain-server including
// the hostname information (so the domain-server can see which place name we came in on)
packetStream << _ownerType.load() << publicSockAddr << localSockAddr << _nodeTypesOfInterest.toList();
@ -618,32 +620,14 @@ void NodeList::processDomainServerConnectionTokenPacket(QSharedPointer<ReceivedM
}
void NodeList::processDomainServerList(QSharedPointer<ReceivedMessage> message) {
if (_domainHandler.getSockAddr().isNull()) {
qWarning() << "IGNORING DomainList packet while not connected to a Domain Server";
// refuse to process this packet if we aren't currently connected to the DS
return;
}
// this is a packet from the domain server, reset the count of un-replied check-ins
_domainHandler.clearPendingCheckins();
// emit our signal so listeners know we just heard from the DS
emit receivedDomainServerList();
DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::ReceiveDSList);
// parse header information
QDataStream packetStream(message->getMessage());
// grab the domain's ID from the beginning of the packet
QUuid domainUUID;
packetStream >> domainUUID;
if (_domainHandler.isConnected() && _domainHandler.getUUID() != domainUUID) {
// Recieved packet from different domain.
qWarning() << "IGNORING DomainList packet from" << domainUUID << "while connected to" << _domainHandler.getUUID();
return;
}
Node::LocalID domainLocalID;
packetStream >> domainLocalID;
@ -654,6 +638,57 @@ void NodeList::processDomainServerList(QSharedPointer<ReceivedMessage> message)
packetStream >> newUUID;
packetStream >> newLocalID;
// pull the permissions/right/privileges for this node out of the stream
NodePermissions newPermissions;
packetStream >> newPermissions;
setPermissions(newPermissions);
// Is packet authentication enabled?
bool isAuthenticated;
packetStream >> isAuthenticated;
setAuthenticatePackets(isAuthenticated);
quint64 connectRequestTimestamp;
quint64 now = usecTimestampNow();
packetStream >> connectRequestTimestamp;
quint64 pingLagTime = (now - connectRequestTimestamp) / USECS_PER_MSEC;
quint64 domainServerPingReceiveTime;
packetStream >> domainServerPingReceiveTime;
quint64 domainServerRequestLag = (domainServerPingReceiveTime - connectRequestTimestamp) / USECS_PER_MSEC;
quint64 domainServerResponseLag = (now - domainServerPingReceiveTime) / USECS_PER_MSEC;
if (_domainHandler.getSockAddr().isNull()) {
qWarning(networking) << "IGNORING DomainList packet while not connected to a Domain Server: sent " << pingLagTime << " msec ago.";
qWarning(networking) << "DomainList request lag (with skew): " << domainServerRequestLag << "msec";
qWarning(networking) << "DomainList response lag (with skew): " << domainServerResponseLag << "msec";
// refuse to process this packet if we aren't currently connected to the DS
return;
}
// warn if ping lag is getting long
if (pingLagTime > MSECS_PER_SECOND) {
qCDebug(networking) << "DomainList ping is lagging: " << pingLagTime << "msec";
qCDebug(networking) << "DomainList request lag (with skew): " << domainServerRequestLag << "msec";
qCDebug(networking) << "DomainList response lag (with skew): " << domainServerResponseLag << "msec";
}
// this is a packet from the domain server, reset the count of un-replied check-ins
_domainHandler.clearPendingCheckins();
// emit our signal so listeners know we just heard from the DS
emit receivedDomainServerList();
DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::ReceiveDSList);
if (_domainHandler.isConnected() && _domainHandler.getUUID() != domainUUID) {
// Recieved packet from different domain.
qWarning() << "IGNORING DomainList packet from" << domainUUID << "while connected to"
<< _domainHandler.getUUID() << ": sent " << pingLagTime << " msec ago.";
qWarning(networking) << "DomainList request lag (with skew): " << domainServerRequestLag << "msec";
qWarning(networking) << "DomainList response lag (with skew): " << domainServerResponseLag << "msec";
return;
}
// when connected, if the session ID or local ID were not null and changed, we should reset
auto currentLocalID = getSessionLocalID();
auto currentSessionID = getSessionUUID();
@ -684,15 +719,6 @@ void NodeList::processDomainServerList(QSharedPointer<ReceivedMessage> message)
DependencyManager::get<AddressManager>()->lookupShareableNameForDomainID(domainUUID);
}
// pull the permissions/right/privileges for this node out of the stream
NodePermissions newPermissions;
packetStream >> newPermissions;
setPermissions(newPermissions);
// Is packet authentication enabled?
bool isAuthenticated;
packetStream >> isAuthenticated;
setAuthenticatePackets(isAuthenticated);
// pull each node in the packet
while (packetStream.device()->pos() < message->getSize()) {
parseNodeFromPacketStream(packetStream);

View file

@ -27,7 +27,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
case PacketType::StunResponse:
return 17;
case PacketType::DomainList:
return static_cast<PacketVersion>(DomainListVersion::AuthenticationOptional);
return static_cast<PacketVersion>(DomainListVersion::HasTimestamp);
case PacketType::EntityAdd:
case PacketType::EntityClone:
case PacketType::EntityEdit:
@ -72,7 +72,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
return static_cast<PacketVersion>(DomainConnectionDeniedVersion::IncludesExtraInfo);
case PacketType::DomainConnectRequest:
return static_cast<PacketVersion>(DomainConnectRequestVersion::AlwaysHasMachineFingerprint);
return static_cast<PacketVersion>(DomainConnectRequestVersion::HasTimestamp);
case PacketType::DomainServerAddedNode:
return static_cast<PacketVersion>(DomainServerAddedNodeVersion::PermissionsGrid);

View file

@ -344,7 +344,8 @@ enum class DomainConnectRequestVersion : PacketVersion {
HasProtocolVersions,
HasMACAddress,
HasMachineFingerprint,
AlwaysHasMachineFingerprint
AlwaysHasMachineFingerprint,
HasTimestamp
};
enum class DomainConnectionDeniedVersion : PacketVersion {
@ -363,7 +364,8 @@ enum class DomainListVersion : PacketVersion {
PermissionsGrid,
GetUsernameFromUUIDSupport,
GetMachineFingerprintFromUUIDSupport,
AuthenticationOptional
AuthenticationOptional,
HasTimestamp
};
enum class AudioVersion : PacketVersion {

View file

@ -9,15 +9,51 @@
#include "LinuxPlatform.h"
#include "platformJsonKeys.h"
#include <GPUIdent.h>
#include <thread>
using namespace platform;
static void getLCpuId( uint32_t* p, uint32_t ax )
{
#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
__asm __volatile
( "movl %%ebx, %%esi\n\t"
"cpuid\n\t"
"xchgl %%ebx, %%esi"
: "=a" (p[0]), "=S" (p[1]),
"=c" (p[2]), "=d" (p[3])
: "0" (ax)
);
#endif
}
void LinuxInstance::enumerateCpu() {
json cpu = {};
uint32_t cpuInfo[4]={0,0,0,0};
char CPUBrandString[16];
char CPUModelString[16];
char CPUClockString[16];
uint32_t nExIds;
getLCpuId(cpuInfo, 0x80000000);
nExIds = cpuInfo[0];
for (uint32_t i = 0x80000000; i <= nExIds; ++i) {
getLCpuId(cpuInfo, i);
// Interpret CPU brand string
if (i == 0x80000002) {
memcpy(CPUBrandString, cpuInfo, sizeof(cpuInfo));
} else if (i == 0x80000003) {
memcpy(CPUModelString, cpuInfo, sizeof(cpuInfo));
} else if (i == 0x80000004) {
memcpy(CPUClockString, cpuInfo, sizeof(cpuInfo));
}
}
cpu["cpuBrand"] = "";
cpu["cpuModel"] = "";
cpu["cpuClockSpeed"] = "";
cpu["cpuNumCores"] = "";
cpu["cpuBrand"] = CPUBrandString;
cpu["cpuModel"] = CPUModelString;
cpu["cpuClockSpeed"] = CPUClockString;
cpu["cpuNumCores"] = std::thread::hardware_concurrency();
_cpu.push_back(cpu);
}

View file

@ -378,6 +378,8 @@ void JitterSample::configure(const Config& config) {
}
} else if (config.stop) {
_sampleSequence.currentIndex = -1;
} else {
_sampleSequence.currentIndex = config.getIndex();
}
_scale = config.scale;
}
@ -392,10 +394,10 @@ void JitterSample::run(const render::RenderContextPointer& renderContext, Output
}
}
jitter.x = 0.0f;
jitter.y = 0.0f;
if (current >= 0) {
jitter = _sampleSequence.offsets[current];
} else {
jitter = glm::vec2(0.0f);
}
}

View file

@ -109,7 +109,6 @@ public:
void setDebugFXAA(bool debug) { debugFXAAX = (debug ? 0.0f : 1.0f); emit dirty();}
bool debugFXAA() const { return (debugFXAAX == 0.0f ? true : false); }
float blend{ 0.25f };
float sharpen{ 0.05f };

View file

@ -248,7 +248,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
// Debugging task is happening in the "over" layer after tone mapping and just before HUD
{ // Debug the bounds of the rendered items, still look at the zbuffer
const auto extraDebugBuffers = RenderDeferredTaskDebug::ExtraBuffers(linearDepthTarget, surfaceGeometryFramebuffer, ambientOcclusionFramebuffer, ambientOcclusionFramebuffer, scatteringResource, velocityBuffer);
const auto extraDebugBuffers = RenderDeferredTaskDebug::ExtraBuffers(linearDepthTarget, surfaceGeometryFramebuffer, ambientOcclusionFramebuffer, ambientOcclusionUniforms, scatteringResource, velocityBuffer);
const auto debugInputs = RenderDeferredTaskDebug::Input(fetchedItems, shadowTaskOutputs, lightingStageInputs, lightClusters, prepareDeferredOutputs, extraDebugBuffers,
deferredFrameTransform, jitter, lightingModel).asVarying();
task.addJob<RenderDeferredTaskDebug>("DebugRenderDeferredTask", debugInputs);

View file

@ -987,6 +987,31 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString&
};
};
/**jsdoc
* The name of an entity event. When the entity event occurs, any function that has been registered for that event via
* {@link Script.addEventHandler} is called with parameters per the entity event.
* <table>
* <thead>
* <tr><th>Event Name</th><th>Entity Event</th></tr>
* </thead>
* <tbody>
* <tr><td><code>"enterEntity"</code></td><td>{@link Entities.enterEntity}</td></tr>
* <tr><td><code>"leaveEntity"</code></td><td>{@link Entities.leaveEntity}</td></tr>
* <tr><td><code>"mousePressOnEntity"</code></td><td>{@link Entities.mousePressOnEntity}</td></tr>
* <tr><td><code>"mouseMoveOnEntity"</code></td><td>{@link Entities.mouseMoveOnEntity}</td></tr>
* <tr><td><code>"mouseReleaseOnEntity"</code></td><td>{@link Entities.mouseReleaseOnEntity}</td></tr>
* <tr><td><code>"clickDownOnEntity"</code></td><td>{@link Entities.clickDownOnEntity}</td></tr>
* <tr><td><code>"holdingClickOnEntity"</code></td><td>{@link Entities.holdingClickOnEntity}</td></tr>
* <tr><td><code>"clickReleaseOnEntity"</code></td><td>{@link Entities.clickReleaseOnEntity}</td></tr>
* <tr><td><code>"hoverEnterEntity"</code></td><td>{@link Entities.hoverEnterEntity}</td></tr>
* <tr><td><code>"hoverOverEntity"</code></td><td>{@link Entities.hoverOverEntity}</td></tr>
* <tr><td><code>"hoverLeaveEntity"</code></td><td>{@link Entities.hoverLeaveEntity}</td></tr>
* <tr><td><code>"collisionWithEntity"</code></td><td>{@link Entities.collisionWithEntity}</td></tr>
* </tbody>
* </table>
*
* @typedef {string} Script.EntityEvent
*/
connect(entities.data(), &EntityScriptingInterface::enterEntity, this, makeSingleEntityHandler("enterEntity"));
connect(entities.data(), &EntityScriptingInterface::leaveEntity, this, makeSingleEntityHandler("leaveEntity"));
@ -2147,7 +2172,7 @@ void ScriptEngine::loadEntityScript(const EntityItemID& entityID, const QString&
}
/**jsdoc
* Triggered when the script starts for a user.
* Triggered when the script starts for a user. See also, {@link Script.entityScriptPreloadFinished}.
* <p>Note: Can only be connected to via <code>this.preload = function (...) { ... }</code> in the entity script.</p>
* <table><tr><th>Available in:</th><td>Client Entity Scripts</td><td>Server Entity Scripts</td></tr></table>
* @function Entities.preload
@ -2161,7 +2186,7 @@ void ScriptEngine::loadEntityScript(const EntityItemID& entityID, const QString&
* this.entityID = entityID;
* print("Entity ID: " + this.entityID);
* };
* );
* });
*
* var entityID = Entities.addEntity({
* type: "Box",

View file

@ -100,6 +100,8 @@ public:
};
/**jsdoc
* The <code>Script</code> API provides facilities for working with scripts.
*
* @namespace Script
*
* @hifi-interface
@ -108,7 +110,14 @@ public:
* @hifi-server-entity
* @hifi-assignment-client
*
* @property {string} context
* @property {string} context - The context that the script is running in:
* <ul>
* <li><code>"client"</code>: An Interface or avatar script.</li>
* <li><code>"entity_client"</code>: A client entity script.</li>
* <li><code>"entity_server"</code>: A server entity script.</li>
* <li><code>"agent"</code>: An assignment client script.</li>
* </ul>
* <em>Read-only.</em>
*/
class ScriptEngine : public BaseScriptEngine, public EntitiesScriptEngineProvider {
Q_OBJECT
@ -150,9 +159,19 @@ public:
QList<EntityItemID> getListOfEntityScriptIDs();
/**jsdoc
* Stop the current script.
* Stops and unloads the current script.
* <p><strong>Warning:</strong> If an assignment client script, the script gets restarted after stopping.</p>
* @function Script.stop
* @param {boolean} [marshal=false]
* @param {boolean} [marshal=false] - Marshal.
* <p class="important">Deprecated: This parameter is deprecated and will be removed.</p>
* @example <caption>Stop a script after 5s.</caption>
* Script.setInterval(function () {
* print("Hello");
* }, 1000);
*
* Script.setTimeout(function () {
* Script.stop(true);
* }, 5000);
*/
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// NOTE - this is intended to be a public interface for Agent scripts, and local scripts, but not for EntityScripts
@ -167,18 +186,20 @@ public:
/**jsdoc
* @function Script.registerGlobalObject
* @param {string} name
* @param {object} object
* @param {string} name - Name.
* @param {object} object - Object.
* @deprecated This function is deprecated and will be removed.
*/
/// registers a global object by name
Q_INVOKABLE void registerGlobalObject(const QString& name, QObject* object);
/**jsdoc
* @function Script.registerGetterSetter
* @param {string} name
* @param {object} getter
* @param {object} setter
* @param {string} [parent=""]
* @param {string} name - Name.
* @param {function} getter - Getter.
* @param {function} setter - Setter.
* @param {string} [parent=""] - Parent.
* @deprecated This function is deprecated and will be removed.
*/
/// registers a global getter/setter
Q_INVOKABLE void registerGetterSetter(const QString& name, QScriptEngine::FunctionSignature getter,
@ -186,19 +207,21 @@ public:
/**jsdoc
* @function Script.registerFunction
* @param {string} name
* @param {object} function
* @param {number} [numArguments=-1]
* @param {string} name - Name.
* @param {function} function - Function.
* @param {number} [numArguments=-1] - Number of arguments.
* @deprecated This function is deprecated and will be removed.
*/
/// register a global function
Q_INVOKABLE void registerFunction(const QString& name, QScriptEngine::FunctionSignature fun, int numArguments = -1);
/**jsdoc
* @function Script.registerFunction
* @param {string} parent
* @param {string} name
* @param {object} function
* @param {number} [numArguments=-1]
* @param {string} parent - Parent.
* @param {string} name - Name.
* @param {function} function - Function.
* @param {number} [numArguments=-1] - Number of arguments.
* @deprecated This function is deprecated and will be removed.
*/
/// register a function as a method on a previously registered global object
Q_INVOKABLE void registerFunction(const QString& parent, const QString& name, QScriptEngine::FunctionSignature fun,
@ -206,27 +229,30 @@ public:
/**jsdoc
* @function Script.registerValue
* @param {string} name
* @param {object} value
* @param {string} name - Name.
* @param {object} value - Value.
* @deprecated This function is deprecated and will be removed.
*/
/// registers a global object by name
Q_INVOKABLE void registerValue(const QString& valueName, QScriptValue value);
/**jsdoc
* @function Script.evaluate
* @param {string} program
* @param {string} filename
* @param {number} [lineNumber=-1]
* @returns {object}
* @param {string} program - Program.
* @param {string} filename - File name.
* @param {number} [lineNumber=-1] - Line number.
* @returns {object} Object.
* @deprecated This function is deprecated and will be removed.
*/
/// evaluate some code in the context of the ScriptEngine and return the result
Q_INVOKABLE QScriptValue evaluate(const QString& program, const QString& fileName, int lineNumber = 1); // this is also used by the script tool widget
/**jsdoc
* @function Script.evaluateInClosure
* @param {object} locals
* @param {object} program
* @returns {object}
* @param {object} locals - Locals.
* @param {object} program - Program.
* @returns {object} Object.
* @deprecated This function is deprecated and will be removed.
*/
Q_INVOKABLE QScriptValue evaluateInClosure(const QScriptValue& locals, const QScriptProgram& program);
@ -237,38 +263,53 @@ public:
bool hasValidScriptSuffix(const QString& scriptFileName);
/**jsdoc
* Gets the context that the script is running in: Interface/avatar, client entity, server entity, or assignment client.
* @function Script.getContext
* @returns {string}
* @returns {string} The context that the script is running in:
* <ul>
* <li><code>"client"</code>: An Interface or avatar script.</li>
* <li><code>"entity_client"</code>: A client entity script.</li>
* <li><code>"entity_server"</code>: A server entity script.</li>
* <li><code>"agent"</code>: An assignment client script.</li>
* </ul>
*/
Q_INVOKABLE QString getContext() const;
/**jsdoc
* Checks whether the script is running as an Interface or avatar script.
* @function Script.isClientScript
* @returns {boolean}
* @returns {boolean} <code>true</code> if the script is running as an Interface or avatar script, <code>false</code> if it
* isn't.
*/
Q_INVOKABLE bool isClientScript() const { return _context == CLIENT_SCRIPT; }
/**jsdoc
* Checks whether the application was compiled as a debug build.
* @function Script.isDebugMode
* @returns {boolean}
* @returns {boolean} <code>true</code> if the application was compiled as a debug build, <code>false</code> if it was
* compiled as a release build.
*/
Q_INVOKABLE bool isDebugMode() const;
/**jsdoc
* Checks whether the script is running as a client entity script.
* @function Script.isEntityClientScript
* @returns {boolean}
* @returns {boolean} <code>true</code> if the script is running as a client entity script, <code>false</code> if it isn't.
*/
Q_INVOKABLE bool isEntityClientScript() const { return _context == ENTITY_CLIENT_SCRIPT; }
/**jsdoc
* Checks whether the script is running as a server entity script.
* @function Script.isEntityServerScript
* @returns {boolean}
* @returns {boolean} <code>true</code> if the script is running as a server entity script, <code>false</code> if it isn't.
*/
Q_INVOKABLE bool isEntityServerScript() const { return _context == ENTITY_SERVER_SCRIPT; }
/**jsdoc
* Checks whether the script is running as an assignment client script.
* @function Script.isAgentScript
* @returns {boolean}
* @returns {boolean} <code>true</code> if the script is running as an assignment client script, <code>false</code> if it
* isn't.
*/
Q_INVOKABLE bool isAgentScript() const { return _context == AGENT_SCRIPT; }
@ -276,25 +317,42 @@ public:
// NOTE - these are intended to be public interfaces available to scripts
/**jsdoc
* Adds a function to the list of functions called when an entity event occurs on a particular entity.
* @function Script.addEventHandler
* @param {Uuid} entityID
* @param {string} eventName
* @param {function} handler
* @param {Uuid} entityID - The ID of the entity.
* @param {Script.EntityEvent} eventName - The name of the entity event.
* @param {function} handler - The function to call when the entity event occurs on the entity. It can be either the name
* of a function or an in-line definition.
* @example <caption>Report when a mouse press occurs on a particular entity.</caption>
* var entityID = Entities.addEntity({
* type: "Box",
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })),
* dimensions: { x: 0.5, y: 0.5, z: 0.5 },
* lifetime: 300 // Delete after 5 minutes.
* });
*
* function reportMousePress(entityID, event) {
* print("Mouse pressed on entity: " + JSON.stringify(event));
* }
*
* Script.addEventHandler(entityID, "mousePressOnEntity", reportMousePress);
*/
Q_INVOKABLE void addEventHandler(const EntityItemID& entityID, const QString& eventName, QScriptValue handler);
/**jsdoc
* Removes a function from the list of functions called when an entity event occurs on a particular entity.
* @function Script.removeEventHandler
* @param {Uuid} entityID
* @param {string} eventName
* @param {function} handler
* @param {Uuid} entityID - The ID of the entity.
* @param {Script.EntityEvent} eventName - The name of the entity event.
* @param {function} handler - The name of the function to no longer call when the entity event occurs on the entity.
*/
Q_INVOKABLE void removeEventHandler(const EntityItemID& entityID, const QString& eventName, QScriptValue handler);
/**jsdoc
* Start a new Interface or entity script.
* Starts running another script in Interface.
* <table><tr><th>Available in:</th><td>Interface Scripts</td><td>Avatar Scripts</td></tr></table>
* @function Script.load
* @param {string} filename - The URL of the script to load. Can be relative to the current script.
* @param {string} filename - The URL of the script to load. This can be relative to the current script's URL.
* @example <caption>Load a script from another script.</caption>
* // First file: scriptA.js
* print("This is script A");
@ -303,7 +361,7 @@ public:
* print("This is script B");
* Script.load("scriptA.js");
*
* // If you run scriptB.js you should see both scripts in the running scripts list.
* // If you run scriptB.js you should see both scripts in the Running Scripts dialog.
* // And you should see the following output:
* // This is script B
* // This is script A
@ -311,22 +369,24 @@ public:
Q_INVOKABLE void load(const QString& loadfile);
/**jsdoc
* Include JavaScript from other files in the current script. If a callback is specified the files are loaded and included
* asynchronously, otherwise they are included synchronously (i.e., script execution blocks while the files are included).
* Includes JavaScript from other files in the current script. If a callback is specified, the files are loaded and
* included asynchronously, otherwise they are included synchronously (i.e., script execution blocks while the files are
* included).
* @function Script.include
* @variation 0
* @param {string[]} filenames - The URLs of the scripts to include. Each can be relative to the current script.
* @param {function} [callback=null] - The function to call back when the scripts have been included. Can be an in-line
* function or the name of a function.
* @param {function} [callback=null] - The function to call back when the scripts have been included. It can be either the
* name of a function or an in-line definition.
*/
Q_INVOKABLE void include(const QStringList& includeFiles, QScriptValue callback = QScriptValue());
/**jsdoc
* Include JavaScript from another file in the current script. If a callback is specified the file is loaded and included
* Includes JavaScript from another file in the current script. If a callback is specified, the file is loaded and included
* asynchronously, otherwise it is included synchronously (i.e., script execution blocks while the file is included).
* @function Script.include
* @param {string} filename - The URL of the script to include. Can be relative to the current script.
* @param {function} [callback=null] - The function to call back when the script has been included. Can be an in-line
* function or the name of a function.
* @param {string} filename - The URL of the script to include. It can be relative to the current script.
* @param {function} [callback=null] - The function to call back when the script has been included. It can be either the
* name of a function or an in-line definition.
* @example <caption>Include a script file asynchronously.</caption>
* // First file: scriptA.js
* print("This is script A");
@ -349,16 +409,21 @@ public:
// MODULE related methods
/**jsdoc
* Provides access to methods or objects provided in an external JavaScript or JSON file.
* See {@link https://docs.highfidelity.com/script/js-tips.html} for further details.
* @function Script.require
* @param {string} module
* @param {string} module - The module to use. May be a JavaScript file or the name of a system module such as
* <code>"sppUi"</code>.
*/
Q_INVOKABLE QScriptValue require(const QString& moduleId);
/**jsdoc
* @function Script.resetModuleCache
* @param {boolean} [deleteScriptCache=false]
* @param {boolean} [deleteScriptCache=false] - Delete script cache.
* @deprecated This function is deprecated and will be removed.
*/
Q_INVOKABLE void resetModuleCache(bool deleteScriptCache = false);
QScriptValue currentModule();
bool registerModuleWithParent(const QScriptValue& module, const QScriptValue& parent);
QScriptValue newModule(const QString& modulePath, const QScriptValue& parent = QScriptValue());
@ -366,53 +431,53 @@ public:
QScriptValue instantiateModule(const QScriptValue& module, const QString& sourceCode);
/**jsdoc
* Call a function at a set interval.
* Calls a function repeatedly, at a set interval.
* @function Script.setInterval
* @param {function} function - The function to call. Can be an in-line function or the name of a function.
* @param {function} function - The function to call. This can be either the name of a function or an in-line definition.
* @param {number} interval - The interval at which to call the function, in ms.
* @returns {object} A handle to the interval timer. Can be used by {@link Script.clearInterval}.
* @returns {object} A handle to the interval timer. This can be used in {@link Script.clearInterval}.
* @example <caption>Print a message every second.</caption>
* Script.setInterval(function () {
* print("Timer fired");
* print("Interval timer fired");
* }, 1000);
*/
Q_INVOKABLE QObject* setInterval(const QScriptValue& function, int intervalMS);
/**jsdoc
* Call a function after a delay.
* Calls a function once, after a delay.
* @function Script.setTimeout
* @param {function} function - The function to call. Can be an in-line function or the name of a function.
* @param {function} function - The function to call. This can be either the name of a function or an in-line definition.
* @param {number} timeout - The delay after which to call the function, in ms.
* @returns {object} A handle to the timeout timer. Can be used by {@link Script.clearTimeout}.
* @example <caption>Print a message after a second.</caption>
* @returns {object} A handle to the timeout timer. This can be used in {@link Script.clearTimeout}.
* @example <caption>Print a message once, after a second.</caption>
* Script.setTimeout(function () {
* print("Timer fired");
* print("Timeout timer fired");
* }, 1000);
*/
Q_INVOKABLE QObject* setTimeout(const QScriptValue& function, int timeoutMS);
/**jsdoc
* Stop an interval timer set by {@link Script.setInterval|setInterval}.
* Stops an interval timer set by {@link Script.setInterval|setInterval}.
* @function Script.clearInterval
* @param {object} timer - The interval timer to clear.
* @param {object} timer - The interval timer to stop.
* @example <caption>Stop an interval timer.</caption>
* // Print a message every second.
* var timer = Script.setInterval(function () {
* print("Timer fired");
* print("Interval timer fired");
* }, 1000);
*
* // Stop the timer after 10 seconds.
* Script.setTimeout(function () {
* print("Stop timer");
* print("Stop interval timer");
* Script.clearInterval(timer);
* }, 10000);
*/
Q_INVOKABLE void clearInterval(QObject* timer) { stopTimer(reinterpret_cast<QTimer*>(timer)); }
/**jsdoc
* Clear a timeout timer set by {@link Script.setTimeout|setTimeout}.
* Stops a timeout timer set by {@link Script.setTimeout|setTimeout}.
* @function Script.clearTimeout
* @param {object} timer - The timeout timer to clear.
* @param {object} timer - The timeout timer to stop.
* @example <caption>Stop a timeout timer.</caption>
* // Print a message after two seconds.
* var timer = Script.setTimeout(function () {
@ -425,34 +490,53 @@ public:
Q_INVOKABLE void clearTimeout(QObject* timer) { stopTimer(reinterpret_cast<QTimer*>(timer)); }
/**jsdoc
* Prints a message to the program log.
* <p>Alternatively, you can use {@link print}, {@link console.log}, or one of the other {@link console} methods.</p>
* @function Script.print
* @param {string} message
* @param {string} message - The message to print.
*/
/**jsdoc
* Prints a message to the program log.
* <p>This is an alias of {@link Script.print}.</p>
* @function print
* @param {string} message - The message to print.
*/
Q_INVOKABLE void print(const QString& message);
/**jsdoc
* Resolve a relative path to an absolute path.
* Resolves a relative path to an absolute path. The relative path is relative to the script's location.
* @function Script.resolvePath
* @param {string} path - The relative path to resolve.
* @returns {string} The absolute path.
* @example <caption>Report the directory and filename of the running script.</caption>
* print(Script.resolvePath(""));
* @example <caption>Report the directory of the running script.</caption>
* print(Script.resolvePath("."));
* @example <caption>Report the path to a file located relative to the running script.</caption>
* print(Script.resolvePath("../assets/sounds/hello.wav"));
*/
Q_INVOKABLE QUrl resolvePath(const QString& path) const;
/**jsdoc
* Gets the path to the resources directory for QML files.
* @function Script.resourcesPath
* @returns {string}
* @returns {string} The path to the resources directory for QML files.
*/
Q_INVOKABLE QUrl resourcesPath() const;
/**jsdoc
* Starts timing a section of code in order to send usage data about it to High Fidelity. Shouldn't be used outside of the
* standard scripts.
* @function Script.beginProfileRange
* @param {string} label
* @param {string} label - A name that identifies the section of code.
*/
Q_INVOKABLE void beginProfileRange(const QString& label) const;
/**jsdoc
* Finishes timing a section of code in order to send usage data about it to High Fidelity. Shouldn't be used outside of
* the standard scripts.
* @function Script.endProfileRange
* @param {string} label
* @param {string} label - A name that identifies the section of code.
*/
Q_INVOKABLE void endProfileRange(const QString& label) const;
@ -460,9 +544,10 @@ public:
// Entity Script Related methods
/**jsdoc
* Checks whether an entity has an entity script running.
* @function Script.isEntityScriptRunning
* @param {Uuid} entityID
* @returns {boolean}
* @param {Uuid} entityID - The ID of the entity.
* @returns {boolean} <code>true</code> if the entity has an entity script running, <code>false</code> if it doesn't.
*/
Q_INVOKABLE bool isEntityScriptRunning(const EntityItemID& entityID) {
QReadLocker locker { &_entityScriptsLock };
@ -474,60 +559,71 @@ public:
/**jsdoc
* @function Script.loadEntityScript
* @param {Uuid} entityID
* @param {string} script
* @param {boolean} forceRedownload
* @param {Uuid} entityID - Entity ID.
* @param {string} script - Script.
* @param {boolean} forceRedownload - Force re-download.
* @deprecated This function is deprecated and will be removed.
*/
Q_INVOKABLE void loadEntityScript(const EntityItemID& entityID, const QString& entityScript, bool forceRedownload);
/**jsdoc
* @function Script.unloadEntityScript
* @param {Uuid} entityID
* @param {boolean} [shouldRemoveFromMap=false]
* @param {Uuid} entityID - Entity ID.
* @param {boolean} [shouldRemoveFromMap=false] - Should remove from map.
* @deprecated This function is deprecated and will be removed.
*/
Q_INVOKABLE void unloadEntityScript(const EntityItemID& entityID, bool shouldRemoveFromMap = false); // will call unload method
/**jsdoc
* @function Script.unloadAllEntityScripts
* @deprecated This function is deprecated and will be removed.
*/
Q_INVOKABLE void unloadAllEntityScripts();
/**jsdoc
* Calls a method in an entity script.
* @function Script.callEntityScriptMethod
* @param {Uuid} entityID
* @param {string} methodName
* @param {string[]} parameters
* @param {Uuid} [remoteCallerID=Uuid.NULL]
* @param {Uuid} entityID - The ID of the entity running the entity script.
* @param {string} methodName - The name of the method to call.
* @param {string[]} [parameters=[]] - The parameters to call the specified method with.
* @param {Uuid} [remoteCallerID=Uuid.NULL] - An ID that identifies the caller.
*/
Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName,
const QStringList& params = QStringList(),
const QUuid& remoteCallerID = QUuid()) override;
/**jsdoc
* Calls a method in an entity script.
* @function Script.callEntityScriptMethod
* @param {Uuid} entityID
* @param {string} methodName
* @param {PointerEvent} event
* @param {Uuid} entityID - Entity ID.
* @param {string} methodName - Method name.
* @param {PointerEvent} event - Pointer event.
* @deprecated This function is deprecated and will be removed.
*/
Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const PointerEvent& event);
/**jsdoc
* Calls a method in an entity script.
* @function Script.callEntityScriptMethod
* @param {Uuid} entityID
* @param {string} methodName
* @param {Uuid} otherID
* @param {Collision} collision
* @param {Uuid} entityID - Entity ID.
* @param {string} methodName - Method name.
* @param {Uuid} otherID - Other entity ID.
* @param {Collision} collision - Collision.
* @deprecated This function is deprecated and will be removed.
*/
Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const EntityItemID& otherID, const Collision& collision);
/**jsdoc
* Manually runs the JavaScript garbage collector which reclaims memory by disposing of objects that are no longer
* reachable.
* @function Script.requestGarbageCollection
*/
Q_INVOKABLE void requestGarbageCollection() { collectGarbage(); }
/**jsdoc
* @function Script.generateUUID
* @returns {Uuid}
* @returns {Uuid} A new UUID.
* @deprecated This function is deprecated and will be removed. Use {@link Uuid.generate} instead.
*/
Q_INVOKABLE QUuid generateUUID() { return QUuid::createUuid(); }
@ -573,7 +669,7 @@ public slots:
/**jsdoc
* @function Script.callAnimationStateHandler
* @param {function} callback - Callback.
* @param {function} callback - Callback function.
* @param {object} parameters - Parameters.
* @param {string[]} names - Names.
* @param {boolean} useNames - Use names.
@ -584,7 +680,8 @@ public slots:
/**jsdoc
* @function Script.updateMemoryCost
* @param {number} deltaSize
* @param {number} deltaSize - Delta size.
* @deprecated This function is deprecated and will be removed.
*/
void updateMemoryCost(const qint64&);
@ -592,31 +689,37 @@ signals:
/**jsdoc
* @function Script.scriptLoaded
* @param {string} filename
* @param {string} filename - File name.
* @returns {Signal}
* @deprecated This signal is deprecated and will be removed.
*/
void scriptLoaded(const QString& scriptFilename);
/**jsdoc
* @function Script.errorLoadingScript
* @param {string} filename
* @param {string} filename - File name.
* @returns {Signal}
* @deprecated This signal is deprecated and will be removed.
*/
void errorLoadingScript(const QString& scriptFilename);
/**jsdoc
* Triggered regularly at a system-determined frequency.
* Triggered frequently at a system-determined interval.
* @function Script.update
* @param {number} deltaTime - The time since the last update, in s.
* @returns {Signal}
* @example <caption>Report script update intervals.</caption>
* Script.update.connect(function (deltaTime) {
* print("Update: " + deltaTime);
* });
*/
void update(float deltaTime);
/**jsdoc
* Triggered when the script is ending.
* Triggered when the script is stopping.
* @function Script.scriptEnding
* @returns {Signal}
* @example <caption>Connect to the <code>scriptEnding</code> signal.</caption>
* @example <caption>Report when a script is stopping.</caption>
* print("Script started");
*
* Script.scriptEnding.connect(function () {
@ -632,52 +735,60 @@ signals:
/**jsdoc
* @function Script.finished
* @param {string} filename
* @param {object} engine
* @param {string} filename - File name.
* @param {object} engine - Engine.
* @returns {Signal}
* @deprecated This signal is deprecated and will be removed.
*/
void finished(const QString& fileNameString, ScriptEnginePointer);
/**jsdoc
* @function Script.cleanupMenuItem
* @param {string} menuItem
* @param {string} menuItem - Menu item.
* @returns {Signal}
* @deprecated This signal is deprecated and will be removed.
*/
void cleanupMenuItem(const QString& menuItemString);
/**jsdoc
* Triggered when a script prints a message to the program log via {@link Script.print}, {@link print},
* {@link console.log}, {@link console.info}, {@link console.warn}, {@link console.error}, or {@link console.debug}.
* @function Script.printedMessage
* @param {string} message
* @param {string} scriptName
* @param {string} message - The message.
* @param {string} scriptName - The name of the script that generated the message.
* @returns {Signal}
*/
void printedMessage(const QString& message, const QString& scriptName);
/**jsdoc
* Triggered when a script generates an error or {@link console.error} is called.
* @function Script.errorMessage
* @param {string} message
* @param {string} scriptName
* @param {string} message - The error message.
* @param {string} scriptName - The name of the script that generated the error message.
* @returns {Signal}
*/
void errorMessage(const QString& message, const QString& scriptName);
/**jsdoc
* Triggered when a script generates a warning or {@link console.warn} is called.
* @function Script.warningMessage
* @param {string} message
* @param {string} scriptName
* @param {string} message - The warning message.
* @param {string} scriptName - The name of the script that generated the warning message.
* @returns {Signal}
*/
void warningMessage(const QString& message, const QString& scriptName);
/**jsdoc
* Triggered when a script generates an information message or {@link console.info} is called.
* @function Script.infoMessage
* @param {string} message
* @param {string} scriptName
* @param {string} message - The information message.
* @param {string} scriptName - The name of the script that generated the information message.
* @returns {Signal}
*/
void infoMessage(const QString& message, const QString& scriptName);
/**jsdoc
* Triggered when the running state of the script changes, e.g., from running to stopping.
* @function Script.runningStateChanged
* @returns {Signal}
*/
@ -686,26 +797,30 @@ signals:
/**jsdoc
* @function Script.clearDebugWindow
* @returns {Signal}
* @deprecated This signal is deprecated and will be removed.
*/
void clearDebugWindow();
/**jsdoc
* @function Script.loadScript
* @param {string} scriptName
* @param {boolean} isUserLoaded
* @param {string} scriptName - Script name.
* @param {boolean} isUserLoaded - Is user loaded.
* @returns {Signal}
* @deprecated This signal is deprecated and will be removed.
*/
void loadScript(const QString& scriptName, bool isUserLoaded);
/**jsdoc
* @function Script.reloadScript
* @param {string} scriptName
* @param {boolean} isUserLoaded
* @param {string} scriptName - Script name.
* @param {boolean} isUserLoaded - Is user loaded.
* @returns {Signal}
* @deprecated This signal is deprecated and will be removed.
*/
void reloadScript(const QString& scriptName, bool isUserLoaded);
/**jsdoc
* Triggered when the script has stopped.
* @function Script.doneRunning
* @returns {Signal}
*/
@ -714,14 +829,35 @@ signals:
/**jsdoc
* @function Script.entityScriptDetailsUpdated
* @returns {Signal}
* @deprecated This signal is deprecated and will be removed.
*/
// Emitted when an entity script is added or removed, or when the status of an entity
// script is updated (goes from RUNNING to ERROR_RUNNING_SCRIPT, for example)
void entityScriptDetailsUpdated();
/**jsdoc
* Triggered when the script starts for the user. See also, {@link Entities.preload}.
* <table><tr><th>Available in:</th><td>Client Entity Scripts</td><td>Server Entity Scripts</td></tr></table>
* @function Script.entityScriptPreloadFinished
* @param {Uuid} entityID - The ID of the entity that the script is running in.
* @returns {Signal}
* @example <caption>Get the ID of the entity that a client entity script is running in.</caption>
* var entityScript = (function () {
* this.entityID = Uuid.NULL;
*
* Script.entityScriptPreloadFinished.connect(function (entityID) {
* this.entityID = entityID;
* print("Entity ID: " + this.entityID);
* });
*
* var entityID = Entities.addEntity({
* type: "Box",
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })),
* dimensions: { x: 0.5, y: 0.5, z: 0.5 },
* color: { red: 255, green: 0, blue: 0 },
* script: "(" + entityScript + ")", // Could host the script on a Web server instead.
* lifetime: 300 // Delete after 5 minutes.
* });
*/
// Emitted when an entity script has finished running preload
void entityScriptPreloadFinished(const EntityItemID& entityID);
@ -731,16 +867,18 @@ protected:
/**jsdoc
* @function Script.executeOnScriptThread
* @param {object} function
* @param {ConnectionType} [type=2]
* @param {function} function - Function.
* @param {ConnectionType} [type=2] - Connection type.
* @deprecated This function is deprecated and will be removed.
*/
Q_INVOKABLE void executeOnScriptThread(std::function<void()> function, const Qt::ConnectionType& type = Qt::QueuedConnection );
/**jsdoc
* @function Script._requireResolve
* @param {string} module
* @param {string} [relativeTo=""]
* @returns {string}
* @param {string} module - Module.
* @param {string} [relativeTo=""] - Relative to.
* @returns {string} Result.
* @deprecated This function is deprecated and will be removed.
*/
// note: this is not meant to be called directly, but just to have QMetaObject take care of wiring it up in general;
// then inside of init() we just have to do "Script.require.resolve = Script._requireResolve;"
@ -763,12 +901,13 @@ protected:
/**jsdoc
* @function Script.entityScriptContentAvailable
* @param {Uuid} entityID
* @param {string} scriptOrURL
* @param {string} contents
* @param {boolean} isURL
* @param {boolean} success
* @param {string} status
* @param {Uuid} entityID - Entity ID.
* @param {string} scriptOrURL - Path.
* @param {string} contents - Contents.
* @param {boolean} isURL - Is a URL.
* @param {boolean} success - Success.
* @param {string} status - Status.
* @deprecated This function is deprecated and will be removed.
*/
Q_INVOKABLE void entityScriptContentAvailable(const EntityItemID& entityID, const QString& scriptOrURL, const QString& contents, bool isURL, bool success, const QString& status);

View file

@ -31,8 +31,32 @@ public:
BaseScriptEngine() {}
/**jsdoc
* @function Script.lintScript
* @param {string} sourceCode - Source code.
* @param {string} fileName - File name.
* @param {number} [lineNumber=1] - Line number.
* @returns {object} Object.
* @deprecated This function is deprecated and will be removed.
*/
Q_INVOKABLE QScriptValue lintScript(const QString& sourceCode, const QString& fileName, const int lineNumber = 1);
/**jsdoc
* @function Script.makeError
* @param {object} [other] - Other.
* @param {string} [type="Error"] - Error.
* @returns {object} Object.
* @deprecated This function is deprecated and will be removed.
*/
Q_INVOKABLE QScriptValue makeError(const QScriptValue& other = QScriptValue(), const QString& type = "Error");
/**jsdoc
* @function Script.formatExecption
* @param {object} exception - Exception.
* @param {boolean} inludeExtendeDetails - Include extended details.
* @returns {string} String.
* @deprecated This function is deprecated and will be removed.
*/
Q_INVOKABLE QString formatException(const QScriptValue& exception, bool includeExtendedDetails);
QScriptValue cloneUncaughtException(const QString& detail = QString());
@ -48,6 +72,25 @@ public:
// helper to detect and log warnings when other code invokes QScriptEngine/BaseScriptEngine in thread-unsafe ways
static bool IS_THREADSAFE_INVOCATION(const QThread *thread, const QString& method);
signals:
/**jsdoc
* @function Script.signalHandlerException
* @param {object} exception - Exception.
* @returns {Signal}
* @deprecated This signal is deprecated and will be removed.
*/
// Script.signalHandlerException is exposed by QScriptEngine.
/**jsdoc
* Triggered when a script generates an unhandled exception.
* @function Script.unhandledException
* @param {object} exception - The details of the exception.
* @returns {Signal}
* @example <caption>Report the details of an unhandled exception.</caption>
* Script.unhandledException.connect(function (exception) {
* print("Unhandled exception: " + JSON.stringify(exception));
* });
* var properties = JSON.parse("{ x: 1"); // Invalid JSON string.
*/
void unhandledException(const QScriptValue& exception);
protected:

View file

@ -28,8 +28,8 @@
#endif
#include <QtCore/QtGlobal>
#include <QtCore/QtGlobal>
#include "SharedLogging.h"
GPUIdent GPUIdent::_instance {};

View file

@ -0,0 +1,243 @@
//
// simplifiedStatusIndicator.js
//
// Created by Robin Wilson on 2019-05-17
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
function simplifiedStatusIndicator(properties) {
var that = this;
var DEBUG = false;
// #region HEARTBEAT
// Send heartbeat with updates to database
// When this stops, the database will set status to offline
var HEARTBEAT_TIMEOUT_MS = 5000,
heartbeat;
function startHeartbeatTimer() {
if (heartbeat) {
Script.clearTimeout(heartbeat);
heartbeat = false;
}
heartbeat = Script.setTimeout(function() {
heartbeat = false;
getStatus(setStatus);
}, HEARTBEAT_TIMEOUT_MS);
}
// #endregion HEARTBEAT
// #region SEND/GET STATUS REQUEST
function setStatusExternally(newStatus) {
if (!newStatus) {
return;
}
setStatus(newStatus);
}
var request = Script.require('request').request,
REQUEST_URL = "https://highfidelity.co/api/statusIndicator/";
function setStatus(newStatus) {
if (heartbeat) {
Script.clearTimeout(heartbeat);
heartbeat = false;
}
if (newStatus && currentStatus !== newStatus) {
currentStatus = newStatus;
that.statusChanged();
}
var queryParamString = "type=heartbeat";
queryParamString += "&username=" + AccountServices.username;
var displayNameToSend = MyAvatar.sessionDisplayName;
if (displayNameToSend === "") {
displayNameToSend = MyAvatar.displayName;
}
queryParamString += "&displayName=" + displayNameToSend;
queryParamString += "&status=" + currentStatus;
queryParamString += "&organization=" + location.hostname;
var uri = REQUEST_URL + "?" + queryParamString;
if (DEBUG) {
console.log("simplifiedStatusIndicator: setStatus: " + uri);
}
request({
uri: uri
}, function (error, response) {
startHeartbeatTimer();
if (error || !response || response.status !== "success") {
console.error("Error with setStatus: " + JSON.stringify(response));
return;
}
});
}
// Get status from database
function getStatus(callback) {
var queryParamString = "type=getStatus";
queryParamString += "&username=" + AccountServices.username;
var uri = REQUEST_URL + "?" + queryParamString;
if (DEBUG) {
console.log("simplifiedStatusIndicator: getStatus: " + uri);
}
request({
uri: uri
}, function (error, response) {
if (error || !response || response.status !== "success") {
console.error("Error with getStatus: " + JSON.stringify(response));
} else if (response.data.userStatus.toLowerCase() !== "offline") {
if (response.data.userStatus !== currentStatus) {
currentStatus = response.data.userStatus;
that.statusChanged();
}
}
if (callback) {
callback();
}
});
}
function getLocalStatus() {
return currentStatus;
}
// #endregion SEND/GET STATUS REQUEST
// #region SIGNALS
var currentStatus = "available"; // Default is available
function toggleStatus() {
if (currentStatus === "busy") {
currentStatus = "available";
// Else if current status is "available" OR anything else (custom)
} else {
currentStatus = "busy";
}
that.statusChanged();
setStatus();
}
// When avatar becomes active from being away
// Set status back to previousStatus
function onWentActive() {
if (currentStatus !== previousStatus) {
currentStatus = previousStatus;
that.statusChanged();
}
setStatus();
}
// When avatar goes away, set status to busy
var previousStatus;
function onWentAway() {
previousStatus = currentStatus;
if (currentStatus !== "busy") {
currentStatus = "busy";
that.statusChanged();
}
setStatus();
}
// Domain changed update avatar location
function onDomainChanged() {
var queryParamString = "type=updateEmployee";
queryParamString += "&username=" + AccountServices.username;
queryParamString += "&location=unknown";
var uri = REQUEST_URL + "?" + queryParamString;
if (DEBUG) {
console.log("simplifiedStatusIndicator: onDomainChanged: " + uri);
}
request({
uri: uri
}, function (error, response) {
if (error || !response || response.status !== "success") {
console.error("Error with onDomainChanged: " + JSON.stringify(response));
} else {
// successfully sent updateLocation
if (DEBUG) {
console.log("simplifiedStatusIndicator: Successfully updated location after domain change.");
}
}
});
}
function statusChanged() {
}
// #endregion SIGNALS
// #region APP LIFETIME
// Creates the app button and sets up signals and hearbeat
function startup() {
MyAvatar.wentAway.connect(onWentAway);
MyAvatar.wentActive.connect(onWentActive);
MyAvatar.displayNameChanged.connect(setStatus);
Window.domainChanged.connect(onDomainChanged);
getStatus(setStatus);
}
// Cleans up timeouts, signals, and overlays
function unload() {
MyAvatar.wentAway.disconnect(onWentAway);
MyAvatar.wentActive.disconnect(onWentActive);
MyAvatar.displayNameChanged.disconnect(setStatus);
Window.domainChanged.disconnect(onDomainChanged);
if (heartbeat) {
Script.clearTimeout(heartbeat);
heartbeat = false;
}
}
// #endregion APP LIFETIME
that.startup = startup;
that.unload = unload;
that.toggleStatus = toggleStatus;
that.setStatus = setStatus;
that.getLocalStatus = getLocalStatus;
that.statusChanged = statusChanged;
// Overwrite with the given properties
var overwriteableKeys = ["statusChanged"];
Object.keys(properties).forEach(function (key) {
if (overwriteableKeys.indexOf(key) > -1) {
that[key] = properties[key];
}
});
}
module.exports = simplifiedStatusIndicator;

View file

@ -238,40 +238,57 @@ function updateOutputDeviceMutedOverlay(isMuted) {
}
var savedAvatarGain = Audio.getAvatarGain();
var savedInjectorGain = Audio.getInjectorGain();
var savedLocalInjectorGain = Audio.getLocalInjectorGain();
var savedSystemInjectorGain = Audio.getSystemInjectorGain();
var savedAvatarGain = Audio.avatarGain;
var savedServerInjectorGain = Audio.serverInjectorGain;
var savedLocalInjectorGain = Audio.localInjectorGain;
var savedSystemInjectorGain = Audio.systemInjectorGain;
var MUTED_VALUE_DB = -60; // This should always match `SimplifiedConstants.qml` -> numericConstants -> mutedValue!
function setOutputMuted(outputMuted) {
updateOutputDeviceMutedOverlay(outputMuted);
if (outputMuted) {
savedAvatarGain = Audio.getAvatarGain();
savedInjectorGain = Audio.getInjectorGain();
savedLocalInjectorGain = Audio.getLocalInjectorGain();
savedSystemInjectorGain = Audio.getSystemInjectorGain();
savedAvatarGain = Audio.avatarGain;
savedServerInjectorGain = Audio.serverInjectorGain;
savedLocalInjectorGain = Audio.localInjectorGain;
savedSystemInjectorGain = Audio.systemInjectorGain;
Audio.setAvatarGain(-60);
Audio.setInjectorGain(-60);
Audio.setLocalInjectorGain(-60);
Audio.setSystemInjectorGain(-60);
Audio.avatarGain = MUTED_VALUE_DB;
Audio.serverInjectorGain = MUTED_VALUE_DB;
Audio.localInjectorGain = MUTED_VALUE_DB;
Audio.systemInjectorGain = MUTED_VALUE_DB;
} else {
if (savedAvatarGain === -60) {
if (savedAvatarGain === MUTED_VALUE_DB) {
savedAvatarGain = 0;
}
Audio.setAvatarGain(savedAvatarGain);
if (savedInjectorGain === -60) {
savedInjectorGain = 0;
Audio.avatarGain = savedAvatarGain;
if (savedServerInjectorGain === MUTED_VALUE_DB) {
savedServerInjectorGain = 0;
}
Audio.setInjectorGain(savedInjectorGain);
if (savedLocalInjectorGain === -60) {
Audio.serverInjectorGain = savedServerInjectorGain;
if (savedLocalInjectorGain === MUTED_VALUE_DB) {
savedLocalInjectorGain = 0;
}
Audio.setLocalInjectorGain(savedLocalInjectorGain);
if (savedSystemInjectorGain === -60) {
Audio.localInjectorGain = savedLocalInjectorGain;
if (savedSystemInjectorGain === MUTED_VALUE_DB) {
savedSystemInjectorGain = 0;
}
Audio.setSystemInjectorGain(savedSystemInjectorGain);
Audio.systemInjectorGain = savedSystemInjectorGain;
}
}
var WAIT_FOR_TOP_BAR_MS = 1000;
function sendLocalStatusToQml() {
var currentStatus = si.getLocalStatus();
if (topBarWindow && currentStatus) {
topBarWindow.sendToQml({
"source": "simplifiedUI.js",
"method": "updateStatusButton",
"data": {
"currentStatus": currentStatus
}
});
} else {
Script.setTimeout(sendLocalStatusToQml, WAIT_FOR_TOP_BAR_MS);
}
}
@ -295,6 +312,10 @@ function onMessageFromTopBar(message) {
setOutputMuted(message.data.outputMuted);
break;
case "toggleStatus":
si.toggleStatus();
break;
default:
console.log("Unrecognized message from " + TOP_BAR_MESSAGE_SOURCE + ": " + JSON.stringify(message));
break;
@ -312,7 +333,10 @@ function onTopBarClosed() {
function isOutputMuted() {
return Audio.getAvatarGain() === -60 && Audio.getInjectorGain() === -60 && Audio.getLocalInjectorGain() === -60 && Audio.getSystemInjectorGain() === -60;
return Audio.avatarGain === MUTED_VALUE_DB &&
Audio.serverInjectorGain === MUTED_VALUE_DB &&
Audio.localInjectorGain === MUTED_VALUE_DB &&
Audio.systemInjectorGain === MUTED_VALUE_DB;
}
@ -346,13 +370,11 @@ function loadSimplifiedTopBar() {
topBarWindow.fromQml.connect(onMessageFromTopBar);
topBarWindow.closed.connect(onTopBarClosed);
topBarWindow.sendToQml({
"source": "simplifiedUI.js",
"method": "updateOutputMuted",
"data": {
"outputMuted": isOutputMuted()
}
})
// The eventbridge takes a nonzero time to initialize, so we have to wait a bit
// for the QML to load and for that to happen before updating the UI.
Script.setTimeout(function() {
sendLocalStatusToQml();
}, WAIT_FOR_TOP_BAR_MS);
}
@ -435,7 +457,20 @@ function ensureFirstPersonCameraInHMD(isHMDMode) {
}
}
var simplifiedNametag = Script.require("./simplifiedNametag/simplifiedNametag.js");
function onStatusChanged() {
sendLocalStatusToQml();
}
function maybeUpdateOutputDeviceMutedOverlay() {
updateOutputDeviceMutedOverlay(isOutputMuted());
}
var simplifiedNametag = Script.require("./simplifiedNametag/simplifiedNametag.js?" + Date.now());
var SimplifiedStatusIndicator = Script.require("./simplifiedStatusIndicator/simplifiedStatusIndicator.js?" + Date.now());
var si;
var oldShowAudioTools;
var oldShowBubbleTools;
var keepExistingUIAndScriptsSetting = Settings.getValue("simplifiedUI/keepExistingUIAndScripts", false);
@ -456,11 +491,19 @@ function startup() {
loadSimplifiedTopBar();
simplifiedNametag.create();
si = new SimplifiedStatusIndicator({
statusChanged: onStatusChanged
});
si.startup();
updateInputDeviceMutedOverlay(Audio.muted);
updateOutputDeviceMutedOverlay(isOutputMuted());
Audio.mutedDesktopChanged.connect(onDesktopInputDeviceMutedChanged);
Window.geometryChanged.connect(onGeometryChanged);
HMD.displayModeChanged.connect(ensureFirstPersonCameraInHMD);
Audio.avatarGainChanged.connect(maybeUpdateOutputDeviceMutedOverlay);
Audio.localInjectorGainChanged.connect(maybeUpdateOutputDeviceMutedOverlay);
Audio.serverInjectorGainChanged.connect(maybeUpdateOutputDeviceMutedOverlay);
Audio.systemInjectorGainChanged.connect(maybeUpdateOutputDeviceMutedOverlay);
oldShowAudioTools = AvatarInputs.showAudioTools;
AvatarInputs.showAudioTools = false;
@ -506,10 +549,15 @@ function shutdown() {
maybeDeleteOutputDeviceMutedOverlay();
simplifiedNametag.destroy();
si.unload();
Audio.mutedDesktopChanged.disconnect(onDesktopInputDeviceMutedChanged);
Window.geometryChanged.disconnect(onGeometryChanged);
HMD.displayModeChanged.disconnect(ensureFirstPersonCameraInHMD);
Audio.avatarGainChanged.disconnect(maybeUpdateOutputDeviceMutedOverlay);
Audio.localInjectorGainChanged.disconnect(maybeUpdateOutputDeviceMutedOverlay);
Audio.serverInjectorGainChanged.disconnect(maybeUpdateOutputDeviceMutedOverlay);
Audio.systemInjectorGainChanged.disconnect(maybeUpdateOutputDeviceMutedOverlay);
AvatarInputs.showAudioTools = oldShowAudioTools;
AvatarInputs.showBubbleTools = oldShowBubbleTools;