Merge branch 'master' of https://github.com/highfidelity/hifi into workload

This commit is contained in:
samcake 2018-04-25 15:21:43 -07:00
commit 5db3eee4ad
86 changed files with 4658 additions and 791 deletions

View file

@ -20,6 +20,7 @@ To produce an executable installer on Windows, the following are required:
- [Inetc Plug-in for Nullsoft](http://nsis.sourceforge.net/Inetc_plug-in) - 1.0
- [NSISpcre Plug-in for Nullsoft](http://nsis.sourceforge.net/NSISpcre_plug-in) - 1.0
- [nsisSlideshow Plug-in for Nullsoft](http://nsis.sourceforge.net/NsisSlideshow_plug-in) - 1.7
- [Nsisunz plug-in for Nullsoft](http://nsis.sourceforge.net/Nsisunz_plug-in)
Run the `package` target to create an executable installer using the Nullsoft Scriptable Install System.

View file

@ -4,8 +4,8 @@ set(EXTERNAL_NAME serverless-content)
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC66-v4.zip
URL_MD5 d4f42f630986c83427ff39e1fe9908c6
URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC67-v2.zip
URL_MD5 2c69a1df69816b4b0b81630396fbd36e
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""

View file

@ -1005,6 +1005,23 @@ Function HandlePostInstallOptions
${EndIf}
FunctionEnd
Function OptionallyDownloadCampaignServerless
${If} $CampaignName != ""
InitPluginsDir
NSISdl::download_quiet http://cdn.highfidelity.com/installer/serverless/$CampaignName.zip $PLUGINSDIR\$CampaignName.zip
${If} ${FileExists} $PLUGINSDIR\$CampaignName.zip
; replace the installed serverless content with the campaign content
RMDir /r "$INSTDIR\resources\serverless"
CreateDirectory "$INSTDIR\resources\serverless"
nsisunz::Unzip "$PLUGINSDIR\$CampaignName.zip" "$INSTDIR\resources\serverless"
${EndIf}
${Endif}
FunctionEnd
;--------------------------------
;Installer Sections
@ -1158,6 +1175,9 @@ Section "-Core installation"
@CPACK_NSIS_EXTRA_INSTALL_COMMANDS@
; see if we have a campaign that we might need to grab special content for
Call OptionallyDownloadCampaignServerless
; Handle whichever post install options were set
Call HandlePostInstallOptions

View file

@ -15,6 +15,10 @@
#include <DependencyManager.h>
#include "Bookmarks.h"
/**jsdoc
* This API helps manage adding and deleting Avatar bookmarks
*/
class AvatarBookmarks: public Bookmarks, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
@ -23,6 +27,15 @@ public:
AvatarBookmarks();
void setupMenus(Menu* menubar, MenuWrapper* menu) override;
/**jsdoc
* Add the current Avatar to your Avatar Bookmarks
* @function AvatarBookmarks.addBookMark
*/
/**jsdoc
* @function AvatarBookmarks.deleteBookMark
*/
public slots:
void addBookmark();

View file

@ -1,4 +1,4 @@
//
//
// Bookmarks.h
// interface/src
//
@ -48,6 +48,10 @@ protected:
bool _isMenuSorted;
protected slots:
/**jsdoc
* Delete
* @function AvatarBookmarks.deleteBookmark
*/
void deleteBookmark();
private:

View file

@ -9,6 +9,11 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
/**jsdoc
* The LOD class manages your Level of Detail functions within interface
* @namespace LODManager
*/
#ifndef hifi_LODManager_h
#define hifi_LODManager_h
@ -39,28 +44,125 @@ class LODManager : public QObject, public Dependency {
SINGLETON_DEPENDENCY
public:
/**jsdoc
* @function LODManager.setAutomaticLODAdjust
* @param {boolean} value
*/
Q_INVOKABLE void setAutomaticLODAdjust(bool value) { _automaticLODAdjust = value; }
/**jsdoc
* @function LODManager.getAutomaticLODAdjust
* @returns {boolean}
*/
Q_INVOKABLE bool getAutomaticLODAdjust() const { return _automaticLODAdjust; }
/**jsdoc
* @function LODManager.setDesktopLODDecreaseFPS
* @param {float} value
*/
Q_INVOKABLE void setDesktopLODDecreaseFPS(float value);
/**jsdoc
* @function LODManager.getDesktopLODDecreaseFPS
* @returns {number}
*/
Q_INVOKABLE float getDesktopLODDecreaseFPS() const;
/**jsdoc
* @function LODManager.getDesktopLODIncreaseFPS
* @returns {number}
*/
Q_INVOKABLE float getDesktopLODIncreaseFPS() const;
/**jsdoc
* @function LODManager.setHMDLODDecreaseFPS
* @param {number} value
*/
Q_INVOKABLE void setHMDLODDecreaseFPS(float value);
/**jsdoc
* @function LODManager.getHMDLODDecreaseFPS
* @returns {number}
*/
Q_INVOKABLE float getHMDLODDecreaseFPS() const;
/**jsdoc
* @function LODManager.getHMDLODIncreaseFPS
* @returns {number}
*/
Q_INVOKABLE float getHMDLODIncreaseFPS() const;
// User Tweakable LOD Items
/**jsdoc
* @function LODManager.getLODFeedbackText
* @returns {string}
*/
Q_INVOKABLE QString getLODFeedbackText();
/**jsdoc
* @function LODManager.setOctreeSizeScale
* @param {number} sizeScale
*/
Q_INVOKABLE void setOctreeSizeScale(float sizeScale);
/**jsdoc
* @function LODManager.getOctreeSizeScale
* @returns {number}
*/
Q_INVOKABLE float getOctreeSizeScale() const { return _octreeSizeScale; }
/**jsdoc
* @function LODManager.setBoundaryLevelAdjust
* @param {number} boundaryLevelAdjust
*/
Q_INVOKABLE void setBoundaryLevelAdjust(int boundaryLevelAdjust);
/**jsdoc
* @function LODManager.getBoundaryLevelAdjust
* @returns {number}
*/
Q_INVOKABLE int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; }
/**jsdoc
* @function LODManager.getLODDecreaseFPS
* @returns {number}
*/
Q_INVOKABLE float getLODDecreaseFPS() const;
/**jsdoc
* @function LODManager.getLODIncreaseFPS
* @returns {number}
*/
Q_INVOKABLE float getLODIncreaseFPS() const;
/**jsdoc
* @namespace LODManager
* @property presentTime {number}
* @property engineRunTime {number}
* @property gpuTime {number}
* @property avgRenderTime {number}
* @property fps {number}
* @property lodLevel {number}
* @property lodDecreaseFPS {number}
* @property lodIncreaseFPS {number}
*/
Q_PROPERTY(float presentTime READ getPresentTime)
Q_PROPERTY(float engineRunTime READ getEngineRunTime)
Q_PROPERTY(float gpuTime READ getGPUTime)
@ -88,7 +190,19 @@ public:
float getLODLevel() const;
signals:
/**jsdoc
* @function LODManager.LODIncreased
* @returns {Signal}
*/
void LODIncreased();
/**jsdoc
* @function LODManager.LODDecreased
* @returns {Signal}
*/
void LODDecreased();
private:

View file

@ -22,6 +22,9 @@
#include <DependencyManager.h>
/**jsdoc
* @namespace SpeechRecognizer
*/
class SpeechRecognizer : public QObject, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
@ -31,12 +34,39 @@ public:
bool getEnabled() const { return _enabled; }
public slots:
/**jsdoc
* @function SpeechRecognizer.setEnabled
* @param {boolean} enabled
*/
void setEnabled(bool enabled);
/**jsdoc
* @function SpeechRecognizer.addCommand
* @param {string} command
*/
void addCommand(const QString& command);
/**jsdoc
* @function SpeechRecognizer.removeCommand
* @param {string} command
*/
void removeCommand(const QString& command);
signals:
/**jsdoc
* @function SpeechRecognizer.commandRecognized
* @param {string} command
* @returns {Signal}
*/
void commandRecognized(const QString& command);
/**jsdoc
* @function SpeechRecognizer.enabledUpdated
* @param {boolean} enabled
* @returns {Signal}
*/
void enabledUpdated(bool enabled);
protected:

View file

@ -25,6 +25,17 @@ class AudioScope : public QObject, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
/**jsdoc
* The AudioScope API helps control the Audio Scope features in Interface
* @namespace AudioScope
* @property {int} scopeInput
* @property {int} scopeOutputLeft
* @property {int} scopeOutputRight
* @property {int} triggerInput
* @property {int} triggerOutputLeft
* @property {int} triggerOutputRight
*/
Q_PROPERTY(QVector<int> scopeInput READ getScopeInput)
Q_PROPERTY(QVector<int> scopeOutputLeft READ getScopeOutputLeft)
Q_PROPERTY(QVector<int> scopeOutputRight READ getScopeOutputRight)
@ -40,42 +51,192 @@ public:
void reallocateScope(int frames);
public slots:
/**jsdoc
* @function AudioScope.toggle
*/
void toggle() { setVisible(!_isEnabled); }
/**jsdoc
* @function AudioScope.setVisible
* @param {boolean} visible
*/
void setVisible(bool visible);
/**jsdoc
* @function AudioScope.getVisible
* @param {boolean} visible
* @returns {boolean}
*/
bool getVisible() const { return _isEnabled; }
/**jsdoc
* @function AudioScope.togglePause
*/
void togglePause() { setPause(!_isPaused); }
/**jsdoc
* @function AudioScope.setPause
* @param {boolean}
*/
void setPause(bool paused) { _isPaused = paused; emit pauseChanged(); }
/**jsdoc
* @function AudioScope.getPause
* @returns {boolean}
*/
bool getPause() { return _isPaused; }
/**jsdoc
* @function AudioScope.toggleTrigger
*/
void toggleTrigger() { _autoTrigger = !_autoTrigger; }
/**jsdoc
* @function AudioScope.getAutoTrigger
* @returns {boolean}
*/
bool getAutoTrigger() { return _autoTrigger; }
/**jsdoc
* @function AudioScope.setAutoTrigger
* @param {boolean} autoTrigger
*/
void setAutoTrigger(bool autoTrigger) { _isTriggered = false; _autoTrigger = autoTrigger; }
/**jsdoc
* @function AudioScope.setTriggerValues
* @param {number} x
* @param {number} y
*/
void setTriggerValues(int x, int y) { _triggerValues.x = x; _triggerValues.y = y; }
/**jsdoc
* @function AudioScope.setTriggered
* @param {boolean} triggered
*/
void setTriggered(bool triggered) { _isTriggered = triggered; }
/**jsdoc
* @function AudioScope.getTriggered
* @returns {boolean}
*/
bool getTriggered() { return _isTriggered; }
/**jsdoc
* @function AudioScope.getFramesPerSecond
* @returns {number}
*/
float getFramesPerSecond();
/**jsdoc
* @function AudioScope.getFramesPerScope
* @returns {number}
*/
int getFramesPerScope() { return _framesPerScope; }
/**jsdoc
* @function AudioScope.selectAudioScopeFiveFrames
*/
void selectAudioScopeFiveFrames();
/**jsdoc
* @function AudioScope.selectAudioScopeTwentyFrames
*/
void selectAudioScopeTwentyFrames();
/**jsdoc
* @function AudioScope.selectAudioScopeFiftyFrames
*/
void selectAudioScopeFiftyFrames();
/**jsdoc
* @function AudioScope.getScopeInput
* @returns {number}
*/
QVector<int> getScopeInput() { return _scopeInputData; };
/**jsdoc
* @function AudioScope.getScopeOutputLeft
* @returns {number}
*/
QVector<int> getScopeOutputLeft() { return _scopeOutputLeftData; };
/**jsdoc
* @function AudioScope.getScopeOutputRight
* @returns {number}
*/
QVector<int> getScopeOutputRight() { return _scopeOutputRightData; };
/**jsdoc
* @function AudioScope.getTriggerInput
* @returns {number}
*/
QVector<int> getTriggerInput() { return _triggerInputData; };
/**jsdoc
* @function AudioScope.getTriggerOutputLeft
* @returns {number}
*/
QVector<int> getTriggerOutputLeft() { return _triggerOutputLeftData; };
/**jsdoc
* @function AudioScope.getTriggerOutputRight
* @returns {number}
*/
QVector<int> getTriggerOutputRight() { return _triggerOutputRightData; };
/**jsdoc
* @function AudioScope.setLocalEcho
* @parm {boolean} serverEcho
*/
void setLocalEcho(bool serverEcho);
/**jsdoc
* @function AudioScope.setServerEcho
* @parm {boolean} serverEcho
*/
void setServerEcho(bool serverEcho);
signals:
/**jsdoc
* @function AudioScope.pauseChanged
* @returns {Signal}
*/
void pauseChanged();
/**jsdoc
* @function AudioScope.triggered
* @returns {Signal}
*/
void triggered();
protected:

View file

@ -27,12 +27,86 @@
#include "AvatarMotionState.h"
#include "MyAvatar.h"
/**jsdoc
* The AvatarManager API has properties and methods which manage Avatars within the same domain.
* @namespace AvatarManager
*/
class AvatarManager : public AvatarHashMap {
Q_OBJECT
SINGLETON_DEPENDENCY
public:
// JSDOCS Copied over from AvatarHashMap (see AvatarHashMap.h for reason)
/**jsdoc
* @function AvatarManager.getAvatarIdentifiers
*/
/**jsdoc
* @function AvatarManager.getAvatarsInRange
* @param {Vec3} position
* @param {float} rangeMeters
* @returns {string[]}
*/
/**jsdoc
* @function AvatarManager.getAvatar
* @param {string} avatarID
* @returns {ScriptAvatarData}
*/
/**jsdoc
* @function AvatarManager.avatarAddedEvent
* @param {string} sessionUUID
* @returns {Signal}
*/
/**jsdoc
* @function AvatarManager.avatarRemovedEvent
* @param {string} sessionUUID
* @returns {Signal}
*/
/**jsdoc
* @function AvatarManager.avatarSessionChangedEvent
* @param {string} sessionUUID
* @param {string} oldUUID
* @returns {Signal}
*/
/**jsdoc
* @function AvatarManager.isAvatarInRange
* @param {string} position
* @param {string} range
* @returns {boolean}
*/
/**jsdoc
* @function AvatarManager.sessionUUIDChanged
* @param {string} sessionUUID
* @param {string} oldUUID
*/
/**jsdoc
* @function AvatarManager.processAvatarDataPacket
* @param {} message
* @param {} sendingNode
*/
/**jsdoc
* @function AvatarManager.processAvatarIdentityPacket
* @param {} message
* @param {} sendingNode
*/
/**jsdoc
* @function AvatarManager.processKillAvatar
* @param {} message
* @param {} sendingNode
*/
/// Registers the script types associated with the avatar manager.
static void registerMetaTypes(QScriptEngine* engine);
@ -43,6 +117,12 @@ public:
std::shared_ptr<MyAvatar> getMyAvatar() { return _myAvatar; }
glm::vec3 getMyAvatarPosition() const { return _myAvatar->getWorldPosition(); }
/**jsdoc
* @function AvatarManager.getAvatar
* @param {string} avatarID
* @returns {}
*/
// Null/Default-constructed QUuids will return MyAvatar
Q_INVOKABLE virtual ScriptAvatarData* getAvatar(QUuid avatarID) override { return new ScriptAvatar(getAvatarBySessionID(avatarID)); }
@ -66,24 +146,82 @@ public:
void handleChangedMotionStates(const VectorOfMotionStates& motionStates);
void handleCollisionEvents(const CollisionEvents& collisionEvents);
/**jsdoc
* @function AvatarManager.getAvatarDataRate
* @param {string} sessionID
* @param {string} [rateName=""]
* @returns {number}
*/
Q_INVOKABLE float getAvatarDataRate(const QUuid& sessionID, const QString& rateName = QString("")) const;
/**jsdoc
* @function AvatarManager.getAvatarUpdateRate
* @param {string} sessionID
* @param {string} [rateName=""]
* @returns {number}
*/
Q_INVOKABLE float getAvatarUpdateRate(const QUuid& sessionID, const QString& rateName = QString("")) const;
/**jsdoc
* @function AvatarManager.getAvatarSimulationRate
* @param {string} sessionID
* @param {string} [rateName=""]
* @returns {number}
*/
Q_INVOKABLE float getAvatarSimulationRate(const QUuid& sessionID, const QString& rateName = QString("")) const;
/**jsdoc
* @function AvatarManager.findRayIntersection
* @param {PickRay} ray
* @param {} avatarIdsToInclude
* @param {} avatarIdsToDiscard
* @returns {RayToAvatarIntersectionResult}
*/
Q_INVOKABLE RayToAvatarIntersectionResult findRayIntersection(const PickRay& ray,
const QScriptValue& avatarIdsToInclude = QScriptValue(),
const QScriptValue& avatarIdsToDiscard = QScriptValue());
/**jsdoc
* @function AvatarManager.findRayIntersectionVector
* @param {PickRay} ray
* @param {} avatarsToInclude
* @param {} avatarIdsToDiscard
* @returns {RayToAvatarIntersectionResult}
*/
Q_INVOKABLE RayToAvatarIntersectionResult findRayIntersectionVector(const PickRay& ray,
const QVector<EntityItemID>& avatarsToInclude,
const QVector<EntityItemID>& avatarsToDiscard);
// TODO: remove this HACK once we settle on optimal default sort coefficients
/**jsdoc
* @function AvatarManager.getAvatarSortCoefficient
* @param {string} name
* @returns {number}
*/
Q_INVOKABLE float getAvatarSortCoefficient(const QString& name);
/**jsdoc
* @function AvatarManager.setAvatarSortCoefficient
* @param {string} name
* @param {string} value
*/
Q_INVOKABLE void setAvatarSortCoefficient(const QString& name, const QScriptValue& value);
float getMyAvatarSendRate() const { return _myAvatarSendRate.rate(); }
public slots:
/**jsdoc
* @function AvatarManager.updateAvatarRenderStatus
* @param {boolean} shouldRenderAvatars
*/
void updateAvatarRenderStatus(bool shouldRenderAvatars);
private:

File diff suppressed because it is too large Load diff

View file

@ -26,6 +26,11 @@
#include <trackers/FaceTracker.h>
/**jsdoc
* The FaceTracker API helps manage facial tracking hardware.
* @namespace FaceTracker
*/
class DdeFaceTracker : public FaceTracker, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
@ -57,7 +62,18 @@ public:
void setEyeClosingThreshold(float eyeClosingThreshold);
public slots:
/**jsdoc
* @function FaceTracker.setEnabled
* @param {boolean} enabled
*/
void setEnabled(bool enabled) override;
/**jsdoc
* @function FaceTracker.calibrate
*/
void calibrate();
private slots:

View file

@ -34,7 +34,17 @@ void DownloadInfoResultFromScriptValue(const QScriptValue& object, DownloadInfoR
class AccountServicesScriptingInterface : public QObject {
Q_OBJECT
/**jsdoc
* The AccountServices API contains helper functions related to user connectivity
*
* @namespace AccountServices
* @property {string} username
* @property {boolean} loggedIn
* @property {string} findableBy
* @property {string} metaverseServerURL
*/
Q_PROPERTY(QString username READ getUsername NOTIFY myUsernameChanged)
Q_PROPERTY(bool loggedIn READ loggedIn NOTIFY loggedInChanged)
Q_PROPERTY(QString findableBy READ getFindableBy WRITE setFindableBy NOTIFY findableByChanged)
@ -48,11 +58,38 @@ public:
QUrl getMetaverseServerURL() { return DependencyManager::get<AccountManager>()->getMetaverseServerURL(); }
public slots:
/**jsdoc
* @function AccountServices.getDownloadInfo
* @returns {DownloadInfoResult}
*/
DownloadInfoResult getDownloadInfo();
/**jsdoc
* @function AccountServices.updateDownloadInfo
*/
void updateDownloadInfo();
/**jsdoc
* @function AccountServices.isLoggedIn
* @returns {boolean}
*/
bool isLoggedIn();
/**jsdoc
* @function AccountServices.checkAndSignalForAccessToken
* @returns {boolean}
*/
bool checkAndSignalForAccessToken();
/**jsdoc
* @function AccountServices.logOut
*/
void logOut();
private slots:
@ -66,11 +103,52 @@ private slots:
void onUsernameChanged(const QString& username);
signals:
/**jsdoc
* @function AccountServices.connected
* @returns {Signal}
*/
void connected();
/**jsdoc
* @function AccountServices.disconnected
* @params {string} reason
* @returns {Signal}
*/
void disconnected(const QString& reason);
/**jsdoc
* @function AccountServices.myUsernameChanged
* @params {string} username
* @returns {Signal}
*/
void myUsernameChanged(const QString& username);
/**jsdoc
* @function AccountServices.downloadInfoChanged
* @params {} info
* @returns {Signal}
*/
void downloadInfoChanged(DownloadInfoResult info);
/**jsdoc
* @function AccountServices.findableByChanged
* @params {string} discoverabilityMode
* @returns {Signal}
*/
void findableByChanged(const QString& discoverabilityMode);
/**jsdoc
* @function AccountServices.loggedInChanged
* @params {boolean} loggedIn
* @returns {Signal}
*/
void loggedInChanged(bool loggedIn);
private:

View file

@ -25,6 +25,18 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
Q_OBJECT
SINGLETON_DEPENDENCY
/**jsdoc
* The Audio API features tools to help control audio contexts and settings.
*
* @namespace Audio
* @property {boolean} muted
* @property {boolean} noiseReduction
* @property {boolean} inputVolume
* @property {boolean} inputLevel
* @property {string} context
* @property {} devices
*/
Q_PROPERTY(bool muted READ isMuted WRITE setMuted NOTIFY mutedChanged)
Q_PROPERTY(bool noiseReduction READ noiseReductionEnabled WRITE enableNoiseReduction NOTIFY noiseReductionChanged)
Q_PROPERTY(float inputVolume READ getInputVolume WRITE setInputVolume NOTIFY inputVolumeChanged)
@ -49,24 +61,111 @@ public:
void showMicMeter(bool show);
/**jsdoc
* @function Audio.setInputDevice
* @param {} device
* @param {boolean} isHMD
*/
Q_INVOKABLE void setInputDevice(const QAudioDeviceInfo& device, bool isHMD);
/**jsdoc
* @function Audio.setOutputDevice
* @param {} device
* @param {boolean} isHMD
*/
Q_INVOKABLE void setOutputDevice(const QAudioDeviceInfo& device, bool isHMD);
/**jsdoc
* @function Audio.setReverb
* @param {boolean} enable
*/
Q_INVOKABLE void setReverb(bool enable);
/**jsdoc
* @function Audio.setReverbOptions
* @param {} options
*/
Q_INVOKABLE void setReverbOptions(const AudioEffectOptions* options);
/**jsdoc
* @function Audio.setReverbOptions
* @param {string} filename
*/
Q_INVOKABLE bool startRecording(const QString& filename);
/**jsdoc
* @function Audio.stopRecording
*/
Q_INVOKABLE void stopRecording();
/**jsdoc
* @function Audio.getRecording
*/
Q_INVOKABLE bool getRecording();
signals:
/**jsdoc
* @function Audio.nop
* @returns {Signal}
*/
void nop();
/**jsdoc
* @function Audio.nop
* @param {bool} isMuted
* @returns {Signal}
*/
void mutedChanged(bool isMuted);
/**jsdoc
* @function Audio.noiseReductionChanged
* @param {bool} isEnabled
* @returns {Signal}
*/
void noiseReductionChanged(bool isEnabled);
/**jsdoc
* @function Audio.inputVolumeChanged
* @param {float} volume
* @returns {Signal}
*/
void inputVolumeChanged(float volume);
/**jsdoc
* @function Audio.inputLevelChanged
* @param {float} level
* @returns {Signal}
*/
void inputLevelChanged(float level);
/**jsdoc
* @function Audio.contextChanged
* @param {string} context
* @returns {Signal}
*/
void contextChanged(const QString& context);
public slots:
/**jsdoc
* @function Audio.onContextChanged
* @returns {Signal}
*/
void onContextChanged();
private slots:

View file

@ -15,6 +15,11 @@
#include <QObject>
#include <DependencyManager.h>
/**jsdoc
* The GooglePoly API allows you to interact with Google Poly models direct from inside High Fidelity.
* @namespace GooglePoly
*/
class GooglePolyScriptingInterface : public QObject, public Dependency {
Q_OBJECT
@ -22,15 +27,84 @@ public:
GooglePolyScriptingInterface();
public slots:
/**jsdoc
* @function GooglePoly.setAPIKey
* @param {string} key
*/
void setAPIKey(const QString& key);
/**jsdoc
* @function GooglePoly.getAssetList
* @param {string} keyword
* @param {string} category
* @param {string} format
* @returns {string}
*/
QString getAssetList(const QString& keyword, const QString& category, const QString& format);
/**jsdoc
* @function GooglePoly.getFBX
* @param {string} keyword
* @param {string} category
* @returns {string}
*/
QString getFBX(const QString& keyword, const QString& category);
/**jsdoc
* @function GooglePoly.getOBJ
* @param {string} keyword
* @param {string} category
* @returns {string}
*/
QString getOBJ(const QString& keyword, const QString& category);
QString getBlocks(const QString& keyword, const QString& categoryy);
/**jsdoc
* @function GooglePoly.getBlocks
* @param {string} keyword
* @param {string} category
* @returns {string}
*/
QString getBlocks(const QString& keyword, const QString& category);
/**jsdoc
* @function GooglePoly.getGLTF
* @param {string} keyword
* @param {string} category
* @returns {string}
*/
QString getGLTF(const QString& keyword, const QString& category);
/**jsdoc
* @function GooglePoly.getGLTF2
* @param {string} keyword
* @param {string} category
* @returns {string}
*/
QString getGLTF2(const QString& keyword, const QString& category);
/**jsdoc
* @function GooglePoly.getTilt
* @param {string} keyword
* @param {string} category
* @returns {string}
*/
QString getTilt(const QString& keyword, const QString& category);
/**jsdoc
* @function GooglePoly.getModelInfo
* @param {string} input
* @returns {string}
*/
QString getModelInfo(const QString& input);
private:

View file

@ -56,6 +56,19 @@ bool GameplayObjects::removeFromGameplayObjects(const OverlayID& overlayID) {
SelectionScriptingInterface::SelectionScriptingInterface() {
}
/**jsdoc
* <table>
* <thead>
* <tr><th>Value</th><th>Description</th></tr>
* </thead>
* <tbody>
* <tr><td><code>"avatar"</code></td><td></td></tr>
* <tr><td><code>"entity"</code></td><td></td></tr>
* <tr><td><code>"overlay"</code></td><td></td></tr>
* </tbody>
* </table>
* @typedef {string} Selection.ItemType
*/
bool SelectionScriptingInterface::addToSelectedItemsList(const QString& listName, const QString& itemType, const QUuid& id) {
if (itemType == "avatar") {
return addToGameplayObjects(listName, (QUuid)id);
@ -255,6 +268,12 @@ void SelectionScriptingInterface::printList(const QString& listName) {
}
}
/**jsdoc
* @typedef {object} Selection.SelectedItemsList
* @property {Uuid[]} avatars - The IDs of the avatars in the selection.
* @property {Uuid[]} entities - The IDs of the entities in the selection.
* @property {Uuid[]} overlays - The IDs of the overlays in the selection.
*/
QVariantMap SelectionScriptingInterface::getSelectedItemsList(const QString& listName) const {
QReadLocker lock(&_selectionListsLock);
QVariantMap list;
@ -461,6 +480,20 @@ bool SelectionHighlightStyle::fromVariantMap(const QVariantMap& properties) {
return true;
}
/**jsdoc
* @typedef {object} Selection.HighlightStyle
* @property {Color} outlineUnoccludedColor - Color of the specified highlight region.
* @property {Color} outlineOccludedColor - ""
* @property {Color} fillUnoccludedColor- ""
* @property {Color} fillOccludedColor- ""
* @property {number} outlineUnoccludedAlpha - Alpha value ranging from <code>0.0</code> (not visible) to <code>1.0</code>
* (fully opaque) for the specified highlight region.
* @property {number} outlineOccludedAlpha - ""
* @property {number} fillUnoccludedAlpha - ""
* @property {number} fillOccludedAlpha - ""
* @property {number} outlineWidth - Width of the outline, in pixels.
* @property {boolean} isOutlineSmooth - <code>true</code> to enable outline smooth fall-off.
*/
QVariantMap SelectionHighlightStyle::toVariantMap() const {
QVariantMap properties;

View file

@ -82,6 +82,46 @@ protected:
render::HighlightStyle _style;
};
/**jsdoc
* The <code>Selection</code> API provides a means of grouping together avatars, entities, and overlays in named lists.
* @namespace Selection
*
* @example <caption>Outline an entity when it is grabbed by a controller.</caption>
* // Create a box and copy the following text into the entity's "Script URL" field.
* (function () {
* print("Starting highlight script...............");
* var _this = this;
* var prevID = 0;
* var listName = "contextOverlayHighlightList";
* var listType = "entity";
*
* _this.startNearGrab = function(entityID){
* if (prevID !== entityID) {
* Selection.addToSelectedItemsList(listName, listType, entityID);
* prevID = entityID;
* }
* };
*
* _this.releaseGrab = function(entityID){
* if (prevID !== 0) {
* Selection.removeFromSelectedItemsList("contextOverlayHighlightList", listType, prevID);
* prevID = 0;
* }
* };
*
* var cleanup = function(){
* Entities.findEntities(MyAvatar.position, 1000).forEach(function(entity) {
* try {
* Selection.removeListFromMap(listName);
* } catch (e) {
* print("Error cleaning up.");
* }
* });
* };
*
* Script.scriptEnding.connect(cleanup);
* });
*/
class SelectionScriptingInterface : public QObject, public Dependency {
Q_OBJECT
@ -89,138 +129,120 @@ public:
SelectionScriptingInterface();
/**jsdoc
* Query the names of all the selection lists
* Get the names of all the selection lists.
* @function Selection.getListNames
* @return An array of names of all the selection lists
* @return {list[]} An array of names of all the selection lists.
*/
Q_INVOKABLE QStringList getListNames() const;
/**jsdoc
* Removes a named selection from the list of selections.
* Delete a named selection list.
* @function Selection.removeListFromMap
* @param listName {string} name of the selection
* @returns {bool} true if the selection existed and was successfully removed.
* @param {string} listName - The name of the selection list.
* @returns {boolean} <code>true</code> if the selection existed and was successfully removed, otherwise <code>false</code>.
*/
Q_INVOKABLE bool removeListFromMap(const QString& listName);
/**jsdoc
* Add an item in a selection.
* Add an item to a selection list.
* @function Selection.addToSelectedItemsList
* @param listName {string} name of the selection
* @param itemType {string} the type of the item (one of "avatar", "entity" or "overlay")
* @param id {EntityID} the Id of the item to add to the selection
* @returns {bool} true if the item was successfully added.
* @param {string} listName - The name of the selection list to add the item to.
* @param {Selection.ItemType} itemType - The type of the item being added.
* @param {Uuid} id - The ID of the item to add to the selection.
* @returns {boolean} <code>true</code> if the item was successfully added, otherwise <code>false</code>.
*/
Q_INVOKABLE bool addToSelectedItemsList(const QString& listName, const QString& itemType, const QUuid& id);
/**jsdoc
* Remove an item from a selection.
* Remove an item from a selection list.
* @function Selection.removeFromSelectedItemsList
* @param listName {string} name of the selection
* @param itemType {string} the type of the item (one of "avatar", "entity" or "overlay")
* @param id {EntityID} the Id of the item to remove
* @returns {bool} true if the item was successfully removed.
* @param {string} listName - The name of the selection list to remove the item from.
* @param {Selection.ItemType} itemType - The type of the item being removed.
* @param {Uuid} id - The ID of the item to remove.
* @returns {boolean} <code>true</code> if the item was successfully removed, otherwise <code>false</code>.
* <codefalse</code> is returned if the list doesn't contain any data.
*/
Q_INVOKABLE bool removeFromSelectedItemsList(const QString& listName, const QString& itemType, const QUuid& id);
/**jsdoc
* Remove all items from a selection.
* @function Selection.clearSelectedItemsList
* @param listName {string} name of the selection
* @returns {bool} true if the item was successfully cleared.
* @param {string} listName - The name of the selection list.
* @returns {boolean} <code>true</code> if the item was successfully cleared, otherwise <code>false</code>.
*/
Q_INVOKABLE bool clearSelectedItemsList(const QString& listName);
/**jsdoc
* Prints out the list of avatars, entities and overlays stored in a particular selection.
* Print out the list of avatars, entities, and overlays in a selection to the <em>debug log</em> (not the script log).
* @function Selection.printList
* @param listName {string} name of the selection
* @param {string} listName - The name of the selection list.
*/
Q_INVOKABLE void printList(const QString& listName);
/**jsdoc
* Query the list of avatars, entities and overlays stored in a particular selection.
* Get the list of avatars, entities, and overlays stored in a selection list.
* @function Selection.getList
* @param listName {string} name of the selection
* @return a js object describing the content of a selection list with the following properties:
* - "entities": [ and array of the entityID of the entities in the selection]
* - "avatars": [ and array of the avatarID of the avatars in the selection]
* - "overlays": [ and array of the overlayID of the overlays in the selection]
* If the list name doesn't exist, the function returns an empty js object with no properties.
* @param {string} listName - The name of the selection list.
* @return {Selection.SelectedItemsList} The content of a selection list. If the list name doesn't exist, the function
* returns an empty object with no properties.
*/
Q_INVOKABLE QVariantMap getSelectedItemsList(const QString& listName) const;
/**jsdoc
* Query the names of the highlighted selection lists
* Get the names of the highlighted selection lists.
* @function Selection.getHighlightedListNames
* @return An array of names of the selection list currently highlight enabled
* @return {string[]} An array of names of the selection list currently highlight enabled.
*/
Q_INVOKABLE QStringList getHighlightedListNames() const;
/**jsdoc
* Enable highlighting for the named selection.
* If the Selection doesn't exist, it will be created.
* All objects in the list will be displayed with the highlight effect as specified from the highlightStyle.
* The function can be called several times with different values in the style to modify it.
*
* Enable highlighting for a selection list.
* If the selection list doesn't exist, it will be created.
* All objects in the list will be displayed with the highlight effect specified.
* The function can be called several times with different values in the style to modify it.<br />
* Note: This function implicitly calls {@link Selection.enableListToScene}.
* @function Selection.enableListHighlight
* @param listName {string} name of the selection
* @param highlightStyle {jsObject} highlight style fields (see Selection.getListHighlightStyle for a detailed description of the highlightStyle).
* @returns {bool} true if the selection was successfully enabled for highlight.
*
* Note: This function will implicitly call Selection.enableListToScene
* @param {string} listName - The name of the selection list.
* @param {Selection.HighlightStyle} highlightStyle - The highlight style.
* @returns {boolean} true if the selection was successfully enabled for highlight.
*/
Q_INVOKABLE bool enableListHighlight(const QString& listName, const QVariantMap& highlightStyle);
/**jsdoc
* Disable highlighting for the named selection.
* If the Selection doesn't exist or wasn't enabled for highliting then nothing happens simply returning false.
*
* Disable highlighting for the selection list.
* If the selection list doesn't exist or wasn't enabled for highlighting then nothing happens and <code>false</code> is
* returned.<br />
* Note: This function implicitly calls {@link Selection.disableListToScene}.
* @function Selection.disableListHighlight
* @param listName {string} name of the selection
* @returns {bool} true if the selection was successfully disabled for highlight, false otherwise.
*
* Note: This function will implicitly call Selection.disableListToScene
* @param {string} listName - The name of the selection list.
* @returns {boolean} <code>true</code> if the selection was successfully disabled for highlight, otherwise
* <code>false</code>.
*/
Q_INVOKABLE bool disableListHighlight(const QString& listName);
/**jsdoc
* Enable scene selection for the named selection.
* Enable scene selection for the selection list.
* If the Selection doesn't exist, it will be created.
* All objects in the list will be sent to a scene selection.
*
* @function Selection.enableListToScene
* @param listName {string} name of the selection
* @returns {bool} true if the selection was successfully enabled on the scene.
* @param {string} listName - The name of the selection list.
* @returns {boolean} <code>true</code> if the selection was successfully enabled on the scene, otherwise <code>false</code>.
*/
Q_INVOKABLE bool enableListToScene(const QString& listName);
/**jsdoc
* Disable scene selection for the named selection.
* If the Selection doesn't exist or wasn't enabled on the scene then nothing happens simply returning false.
*
* If the selection list doesn't exist or wasn't enabled on the scene then nothing happens and <code>false</code> is
* returned.
* @function Selection.disableListToScene
* @param listName {string} name of the selection
* @returns {bool} true if the selection was successfully disabled on the scene, false otherwise.
* @param {string} listName - The name of the selection list.
* @returns {boolean} true if the selection was successfully disabled on the scene, false otherwise.
*/
Q_INVOKABLE bool disableListToScene(const QString& listName);
/**jsdoc
* Query the highlight style values for the named selection.
* If the Selection doesn't exist or hasn't been highlight enabled yet, it will return an empty object.
* Otherwise, the jsObject describes the highlight style properties:
* - outlineUnoccludedColor: {xColor} Color of the specified highlight region
* - outlineOccludedColor: {xColor} "
* - fillUnoccludedColor: {xColor} "
* - fillOccludedColor: {xColor} "
*
* - outlineUnoccludedAlpha: {float} Alpha value ranging from 0.0 (not visible) to 1.0 (fully opaque) for the specified highlight region
* - outlineOccludedAlpha: {float} "
* - fillUnoccludedAlpha: {float} "
* - fillOccludedAlpha: {float} "
*
* - outlineWidth: {float} width of the outline expressed in pixels
* - isOutlineSmooth: {bool} true to enable oultine smooth falloff
*
* Get the highlight style values for the a selection list.
* If the selection doesn't exist or hasn't been highlight enabled yet, an empty object is returned.
* @function Selection.getListHighlightStyle
* @param listName {string} name of the selection
* @returns {jsObject} highlight style as described above
* @param {string} listName - The name of the selection list.
* @returns {Selection.HighlightStyle} highlight style
*/
Q_INVOKABLE QVariantMap getListHighlightStyle(const QString& listName) const;
@ -232,6 +254,12 @@ public:
void onSelectedItemsListChanged(const QString& listName);
signals:
/**jsoc
* Triggered when a list's content changes.
* @function Selection.selectedItemsListChanged
* @param {string} listName - The name of the selection list that changed.
* @returns {Signal}
*/
void selectedItemsListChanged(const QString& listName);
private:

View file

@ -23,6 +23,15 @@ class AvatarInputs : public QObject {
Q_OBJECT
HIFI_QML_DECL
/**jsdoc
* API to help manage your Avatar's input
* @namespace AvatarInputs
* @param {boolean} cameraEnabled
* @param {boolean} cameraMuted
* @param {boolean} isHMD
* @param {boolean} showAudioTools
*/
AI_PROPERTY(bool, cameraEnabled, false)
AI_PROPERTY(bool, cameraMuted, false)
AI_PROPERTY(bool, isHMD, false)
@ -31,22 +40,70 @@ class AvatarInputs : public QObject {
public:
static AvatarInputs* getInstance();
/**jsdoc
* @function AvatarInputs.loudnessToAudioLevel
* @param {number} loudness
* @returns {number}
*/
Q_INVOKABLE float loudnessToAudioLevel(float loudness);
AvatarInputs(QObject* parent = nullptr);
void update();
bool showAudioTools() const { return _showAudioTools; }
public slots:
/**jsdoc
* @function AvatarInputs.setShowAudioTools
* @param {boolean} showAudioTools
*/
void setShowAudioTools(bool showAudioTools);
signals:
/**jsdoc
* @function AvatarInputs.cameraEnabledChanged
* @returns {Signal}
*/
void cameraEnabledChanged();
/**jsdoc
* @function AvatarInputs.cameraMutedChanged
* @returns {Signal}
*/
void cameraMutedChanged();
/**jsdoc
* @function AvatarInputs.isHMDChanged
* @returns {Signal}
*/
void isHMDChanged();
/**jsdoc
* @function AvatarInputs.showAudioToolsChanged
* @param {boolean} show
* @returns {Signal}
*/
void showAudioToolsChanged(bool show);
protected:
/**jsdoc
* @function AvatarInputs.resetSensors
*/
Q_INVOKABLE void resetSensors();
/**jsdoc
* @function AvatarInputs.toggleCameraMute
*/
Q_INVOKABLE void toggleCameraMute();
private:

View file

@ -331,16 +331,20 @@ void Web3DOverlay::render(RenderArgs* args) {
renderTransform.setScale(1.0f);
batch.setModelTransform(renderTransform);
// Turn off jitter for these entities
batch.pushProjectionJitter();
auto geometryCache = DependencyManager::get<GeometryCache>();
if (color.a < OPAQUE_ALPHA_THRESHOLD) {
geometryCache->bindWebBrowserProgram(batch, true);
} else {
geometryCache->bindWebBrowserProgram(batch);
}
vec2 halfSize = vec2(size.x, size.y) / 2.0f;
geometryCache->renderQuad(batch, halfSize * -1.0f, halfSize, vec2(0), vec2(1), color, _geometryId);
batch.popProjectionJitter(); // Restore jitter
batch.setResourceTexture(0, nullptr); // restore default white color after me
}
Transform Web3DOverlay::evalRenderTransform() {

View file

@ -20,6 +20,11 @@
#include <FBXReader.h>
#include <ResourceCache.h>
/**jsdoc
* API to manage Animation Cache resources
* @namespace AnimationCache
*/
class Animation;
typedef QSharedPointer<Animation> AnimationPointer;
@ -29,19 +34,80 @@ class AnimationCache : public ResourceCache, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
public:
// Copied over from ResourceCache (see ResourceCache.h for reason)
/**jsdoc
* @namespace AnimationCache
* @augments ResourceCache
* @property numTotal {number} total number of total resources
* @property numCached {number} total number of cached resource
* @property sizeTotal {number} size in bytes of all resources
* @property sizeCached {number} size in bytes of all cached resources
*/
public:
/**jsdoc
* Returns the total number of resources
* @function AnimationCache.getNumTotalResources
* @returns {number}
*/
/**jsdoc
* Returns the total size in bytes of all resources
* @function AnimationCache.getSizeTotalResources
* @returns {number}
*/
/**jsdoc
* Returns the total number of cached resources
* @function AnimationCache.getNumCachedResources
* @returns {number}
*/
/**jsdoc
* Returns the total size in bytes of cached resources
* @function AnimationCache.getSizeCachedResources
* @returns {number}
*/
/**jsdoc
* Returns list of all resource urls
* @function AnimationCache.getResourceList
* @returns {string[]}
*/
/**jsdoc
* Asynchronously loads a resource from the spedified URL and returns it.
* @param url {string} url of resource to load
* @param fallback {string} fallback URL if load of the desired url fails
* @function AnimationCache.getResource
* @returns {Resource}
*/
/**jsdoc
* Prefetches a resource.
* @param url {string} url of resource to load
* @function AnimationCache.prefetch
* @returns {Resource}
*/
/**jsdoc
* @param {number} deltaSize
* @function AnimationCache.updateTotalSize
* @returns {Resource}
*/
/**jsdoc
* @function AnimationCache.dirty
* @returns {Signal}
*/
/**jsdoc
* Returns animation resource for particular animation
* @function AnimationCache.getAnimation
* @param url {string} url to load
* @return {Resource} animation
* @returns {Resource} animation
*/
Q_INVOKABLE AnimationPointer getAnimation(const QString& url) { return getAnimation(QUrl(url)); }
Q_INVOKABLE AnimationPointer getAnimation(const QUrl& url);

View file

@ -38,6 +38,49 @@ class MixedProcessedAudioStream;
class AudioStreamStatsInterface : public QObject {
Q_OBJECT
/**jsdoc
* Audio stats from the Audio Mixer
* @namespace AudioStats.mixerStream
* @param {number} lossRate
* @param {number} lossCount
* @param {number} lossRateWindow
* @param {number} lossCountWindow
* @param {number} framesDesired
* @param {number} framesAvailable
* @param {number} framesAvailableAvg
* @param {number} unplayedMsMax
* @param {number} starveCount
* @param {number} lastStarveDurationCount
* @param {number} dropCount
* @param {number} overflowCount
* @param {number} timegapMsMax
* @param {number} timegapMsAvg
* @param {number} timegapMsMaxWindow
* @param {number} timegapMsAvgWindow
*/
/**jsdoc
* Audio stats from the Client Mixer
* @namespace AudioStats.clientMixer
* @param {number} lossRate
* @param {number} lossCount
* @param {number} lossRateWindow
* @param {number} lossCountWindow
* @param {number} framesDesired
* @param {number} framesAvailable
* @param {number} framesAvailableAvg
* @param {number} unplayedMsMax
* @param {number} starveCount
* @param {number} lastStarveDurationCount
* @param {number} dropCount
* @param {number} overflowCount
* @param {number} timegapMsMax
* @param {number} timegapMsAvg
* @param {number} timegapMsMaxWindow
* @param {number} timegapMsAvgWindow
*/
AUDIO_PROPERTY(float, lossRate)
AUDIO_PROPERTY(float, lossCount)
AUDIO_PROPERTY(float, lossRateWindow)
@ -68,6 +111,20 @@ private:
class AudioStatsInterface : public QObject {
Q_OBJECT
/**jsdoc
* Audio stats from the client
* @namespace AudioStats
* @param {number} pingMs
* @param {number} inputReadMsMax
* @param {number} inputUnplayedMsMax
* @param {number} outputUnplayedMsMax
* @param {number} sentTimegapMsMax
* @param {number} sentTimegapMsAvg
* @param {number} sentTimegapMsMaxWindow
* @param {number} sentTimegapMsAvgWindow
*/
AUDIO_PROPERTY(float, pingMs);
AUDIO_PROPERTY(float, inputReadMsMax);
@ -97,8 +154,26 @@ public:
void updateInjectorStreams(const QHash<QUuid, AudioStreamStats>& stats);
signals:
/**jsdoc
* @function AudioStats.mixerStreamChanged
* @returns {Signal}
*/
void mixerStreamChanged();
/**jsdoc
* @function AudioStats.clientStreamChanged
* @returns {Signal}
*/
void clientStreamChanged();
/**jsdoc
* @function AudioStats.injectorStreamsChanged
* @returns {Signal}
*/
void injectorStreamsChanged();
private:

View file

@ -16,12 +16,97 @@
#include "Sound.h"
/**jsdoc
* API to manage Sound Cache resources
* @namespace SoundCache
*/
/// Scriptable interface for sound loading.
class SoundCache : public ResourceCache, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
public:
// Copied over from ResourceCache (see ResourceCache.h for reason)
/**jsdoc
* @namespace SoundCache
* @property numTotal {number} total number of total resources
* @property numCached {number} total number of cached resource
* @property sizeTotal {number} size in bytes of all resources
* @property sizeCached {number} size in bytes of all cached resources
*/
/**jsdoc
* Returns the total number of resources
* @function SoundCache.getNumTotalResources
* @returns {number}
*/
/**jsdoc
* Returns the total size in bytes of all resources
* @function SoundCache.getSizeTotalResources
* @returns {number}
*/
/**jsdoc
* Returns the total number of cached resources
* @function SoundCache.getNumCachedResources
* @returns {number}
*/
/**jsdoc
* Returns the total size in bytes of cached resources
* @function SoundCache.getSizeCachedResources
* @returns {number}
*/
/**jsdoc
* Returns list of all resource urls
* @function SoundCache.getResourceList
* @returns {string[]}
*/
/**jsdoc
* Returns animation resource for particular animation
* @function SoundCache.getAnimation
* @param url {string} url to load
* @returns {Resource} animation
*/
/**jsdoc
* Asynchronously loads a resource from the spedified URL and returns it.
* @param url {string} url of resource to load
* @param fallback {string} fallback URL if load of the desired url fails
* @function SoundCache.getResource
* @returns {Resource}
*/
/**jsdoc
* Prefetches a resource.
* @param url {string} url of resource to load
* @function SoundCache.prefetch
* @returns {Resource}
*/
/**jsdoc
* @param {number} deltaSize
* @function SoundCache.updateTotalSize
* @returns {Resource}
*/
/**jsdoc
* @function SoundCache.dirty
* @returns {Signal}
*/
/**jsdoc
* @function SoundCache.getSound
* @param {string} url
* @returns {}
*/
Q_INVOKABLE SharedSoundPointer getSound(const QUrl& url);
protected:
virtual QSharedPointer<Resource> createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,

View file

@ -57,15 +57,7 @@ using AvatarPhysicsCallback = std::function<void(uint32_t)>;
class Avatar : public AvatarData, public scriptable::ModelProvider {
Q_OBJECT
/**jsdoc
* An avatar is representation of yourself or another user. The Avatar API can be used to query or manipulate the avatar of a user.
* NOTE: Avatar extends AvatarData, see those namespace for more properties/methods.
*
* @namespace Avatar
* @augments AvatarData
*
* @property skeletonOffset {Vec3} can be used to apply a translation offset between the avatar's position and the registration point of the 3d model.
*/
// This property has JSDoc in MyAvatar.h.
Q_PROPERTY(glm::vec3 skeletonOffset READ getSkeletonOffset WRITE setSkeletonOffset)
public:
@ -128,14 +120,25 @@ public:
virtual int getJointIndex(const QString& name) const override;
virtual QStringList getJointNames() const override;
/**jsdoc
* @function MyAvatar.getDefaultJointRotation
* @param {number} index
* @returns {Quat}
*/
Q_INVOKABLE virtual glm::quat getDefaultJointRotation(int index) const;
/**jsdoc
* @function MyAvatar.getDefaultJointTranslation
* @param {number} index
* @returns {Vec3}
*/
Q_INVOKABLE virtual glm::vec3 getDefaultJointTranslation(int index) const;
/**jsdoc
* Provides read only access to the default joint rotations in avatar coordinates.
* The default pose of the avatar is defined by the position and orientation of all bones
* in the avatar's model file. Typically this is a t-pose.
* @function Avatar.getAbsoluteDefaultJointRotationInObjectFrame
* in the avatar's model file. Typically this is a T-pose.
* @function MyAvatar.getAbsoluteDefaultJointRotationInObjectFrame
* @param index {number} index number
* @returns {Quat} The rotation of this joint in avatar coordinates.
*/
@ -144,8 +147,8 @@ public:
/**jsdoc
* Provides read only access to the default joint translations in avatar coordinates.
* The default pose of the avatar is defined by the position and orientation of all bones
* in the avatar's model file. Typically this is a t-pose.
* @function Avatar.getAbsoluteDefaultJointTranslationInObjectFrame
* in the avatar's model file. Typically this is a T-pose.
* @function MyAvatar.getAbsoluteDefaultJointTranslationInObjectFrame
* @param index {number} index number
* @returns {Vec3} The position of this joint in avatar coordinates.
*/
@ -170,14 +173,65 @@ public:
virtual void applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetration) { }
/**jsdoc
* Set the offset applied to the current avatar. The offset adjusts the position that the avatar is rendered. For example,
* with an offset of <code>{ x: 0, y: 0.1, z: 0 }</code>, your avatar will appear to be raised off the ground slightly.
* @function MyAvatar.setSkeletonOffset
* @param {Vec3} offset - The skeleton offset to set.
* @example <caption>Raise your avatar off the ground a little.</caption>
* // Raise your avatar off the ground a little.
* MyAvatar.setSkeletonOffset({ x: 0, y: 0.1: z: 0 });
*
* // Restore its offset after 5s.
* Script.setTimeout(function () {
* MyAvatar.setSkeletonOffset(Vec3.ZERO);
* }, 5000);
*/
Q_INVOKABLE void setSkeletonOffset(const glm::vec3& offset);
/**jsdoc
* Get the offset applied to the current avatar. The offset adjusts the position that the avatar is rendered. For example,
* with an offset of <code>{ x: 0, y: 0.1, z: 0 }</code>, your avatar will appear to be raised off the ground slightly.
* @function MyAvatar.getSkeletonOffset
* @returns {Vec3} The current skeleton offset.
* @example <caption>Report your avatar's current skeleton offset.</caption>
* print(JSON.stringify(MyAvatar.getSkeletonOffset());
*/
Q_INVOKABLE glm::vec3 getSkeletonOffset() { return _skeletonOffset; }
virtual glm::vec3 getSkeletonPosition() const;
/**jsdoc
* Get the position of a joint in the current avatar.
* @function MyAvatar.getJointPosition
* @param {number} index - The index of the joint.
* @returns {Vec3} The position of the joint in world coordinates.
*/
Q_INVOKABLE glm::vec3 getJointPosition(int index) const;
/**jsdoc
* Get the position of a joint in the current avatar.
* @function MyAvatar.getJointPosition
* @param {string} name - The name of the joint.
* @returns {Vec3} The position of the joint in world coordinates.
* @example <caption>Report the position of your avatar's hips.</caption>
* print(JSON.stringify(MyAvatar.getJointPosition("Hips")));
*/
Q_INVOKABLE glm::vec3 getJointPosition(const QString& name) const;
/**jsdoc
* Get the position of the current avatar's neck in world coordinates.
* @function MyAvatar.getNeckPosition
* @returns {Vec3} The position of the neck in world coordinates.
* @example <caption>Report the position of your avatar's neck.</caption>
* print(JSON.stringify(MyAvatar.getNeckPosition()));
*/
Q_INVOKABLE glm::vec3 getNeckPosition() const;
/**jsdoc
* @function MyAvatar.getAcceleration
* @returns {Vec3}
*/
Q_INVOKABLE glm::vec3 getAcceleration() const { return _acceleration; }
/// Scales a world space position vector relative to the avatar position and scale
@ -201,24 +255,47 @@ public:
void setPositionViaScript(const glm::vec3& position) override;
void setOrientationViaScript(const glm::quat& orientation) override;
// these call through to the SpatiallyNestable versions, but they are here to expose these to javascript.
/**jsdoc
* @function MyAvatar.getParentID
* @returns {Uuid}
*/
// This calls through to the SpatiallyNestable versions, but is here to expose these to JavaScript.
Q_INVOKABLE virtual const QUuid getParentID() const override { return SpatiallyNestable::getParentID(); }
/**jsdoc
* @function MyAvatar.setParentID
* @param {Uuid} parentID
*/
// This calls through to the SpatiallyNestable versions, but is here to expose these to JavaScript.
Q_INVOKABLE virtual void setParentID(const QUuid& parentID) override;
/**jsdoc
* @function MyAvatar.getParentJointIndex
* @returns {number}
*/
// This calls through to the SpatiallyNestable versions, but is here to expose these to JavaScript.
Q_INVOKABLE virtual quint16 getParentJointIndex() const override { return SpatiallyNestable::getParentJointIndex(); }
/**jsdoc
* @function MyAvatar.setParentJointIndex
* @param {number} parentJointIndex
*/
// This calls through to the SpatiallyNestable versions, but is here to expose these to JavaScript.
Q_INVOKABLE virtual void setParentJointIndex(quint16 parentJointIndex) override;
/**jsdoc
* Information about a single joint in an Avatar's skeleton hierarchy.
* @typedef Avatar.SkeletonJoint
* @property {string} name - name of joint
* @property {number} index - joint index
* @property {number} parentIndex - index of this joint's parent (-1 if no parent)
*/
/**jsdoc
* Returns an array of joints, where each joint is an object containing name, index and parentIndex fields.
* @function Avatar.getSkeleton
* @returns {Avatar.SkeletonJoint[]} returns a list of information about each joint in this avatar's skeleton.
* Returns an array of joints, where each joint is an object containing name, index, and parentIndex fields.
* @function MyAvatar.getSkeleton
* @returns {MyAvatar.SkeletonJoint[]} A list of information about each joint in this avatar's skeleton.
*/
/**jsdoc
* Information about a single joint in an Avatar's skeleton hierarchy.
* @typedef MyAvatar.SkeletonJoint
* @property {string} name - Joint name.
* @property {number} index - Joint index.
* @property {number} parentIndex - Index of this joint's parent (-1 if no parent).
*/
Q_INVOKABLE QList<QVariant> getSkeleton();
@ -235,6 +312,11 @@ public:
void setTargetScale(float targetScale) override;
float getTargetScale() const { return _targetScale; }
/**jsdoc
* @function MyAvatar.getSimulationRate
* @param {string} [rateName=""]
* @returns {number}
*/
Q_INVOKABLE float getSimulationRate(const QString& rateName = QString("")) const;
bool hasNewJointData() const { return _hasNewJointData; }
@ -256,6 +338,7 @@ public:
bool isFading() const { return _isFading; }
void updateFadingStatus(render::ScenePointer scene);
// JSDoc is in AvatarData.h.
Q_INVOKABLE virtual float getEyeHeight() const override;
// returns eye height of avatar in meters, ignoring avatar scale.
@ -282,16 +365,57 @@ public slots:
// FIXME - these should be migrated to use Pose data instead
// thread safe, will return last valid palm from cache
/**jsdoc
* Get the position of the left palm in world coordinates.
* @function MyAvatar.getLeftPalmPosition
* @returns {Vec3} The position of the left palm in world coordinates.
* @example <caption>Report the position of your avatar's left palm.</caption>
* print(JSON.stringify(MyAvatar.getLeftPalmPosition()));
*/
glm::vec3 getLeftPalmPosition() const;
/**jsdoc
* Get the rotation of the left palm in world coordinates.
* @function MyAvatar.getLeftPalmRotation
* @returns {Vec3} The rotation of the left palm in world coordinates.
* @example <caption>Report the rotation of your avatar's left palm.</caption>
* print(JSON.stringify(MyAvatar.getLeftPalmRotation()));
*/
glm::quat getLeftPalmRotation() const;
/**jsdoc
* Get the position of the right palm in world coordinates.
* @function MyAvatar.getRightPalmPosition
* @returns {Vec3} The position of the right palm in world coordinates.
* @example <caption>Report the position of your avatar's right palm.</caption>
* print(JSON.stringify(MyAvatar.getRightPalmPosition()));
*/
glm::vec3 getRightPalmPosition() const;
/**jsdoc
* Get the rotation of the right palm in world coordinates.
* @function MyAvatar.getRightPalmRotation
* @returns {Vec3} The rotation of the right palm in world coordinates.
* @example <caption>Report the rotation of your avatar's right palm.</caption>
* print(JSON.stringify(MyAvatar.getRightPalmRotation()));
*/
glm::quat getRightPalmRotation() const;
// hooked up to Model::setURLFinished signal
void setModelURLFinished(bool success);
// hooked up to Model::rigReady & rigReset signals
/**jsdoc
* @function MyAvatar.rigReady
* @returns {Signal}
*/
// Hooked up to Model::rigReady signal
void rigReady();
/**jsdoc
* @function MyAvatar.rigReset
* @returns {Signal}
*/
// Jooked up to Model::rigReset signal
void rigReset();
protected:

View file

@ -2362,6 +2362,15 @@ glm::vec3 AvatarData::getAbsoluteJointTranslationInObjectFrame(int index) const
return glm::vec3();
}
/**jsdoc
* @typedef MyAvatar.AttachmentData
* @property {string} modelUrl
* @property {string} jointName
* @property {Vec3} translation
* @property {Vec3} rotation
* @property {number} scale
* @property {boolean} soft
*/
QVariant AttachmentData::toVariant() const {
QVariantMap result;
result["modelUrl"] = modelURL;

View file

@ -353,6 +353,7 @@ public:
class AvatarData : public QObject, public SpatiallyNestable {
Q_OBJECT
// The following properties have JSDoc in MyAvatar.h.
Q_PROPERTY(glm::vec3 position READ getWorldPosition WRITE setPositionViaScript)
Q_PROPERTY(float scale READ getTargetScale WRITE setTargetScale)
Q_PROPERTY(float density READ getDensity)
@ -505,7 +506,7 @@ public:
/**jsdoc
* returns the minimum scale allowed for this avatar in the current domain.
* This value can change as the user changes avatars or when changing domains.
* @function AvatarData.getDomainMinScale
* @function MyAvatar.getDomainMinScale
* @returns {number} minimum scale allowed for this avatar in the current domain.
*/
Q_INVOKABLE float getDomainMinScale() const;
@ -513,7 +514,7 @@ public:
/**jsdoc
* returns the maximum scale allowed for this avatar in the current domain.
* This value can change as the user changes avatars or when changing domains.
* @function AvatarData.getDomainMaxScale
* @function MyAvatar.getDomainMaxScale
* @returns {number} maximum scale allowed for this avatar in the current domain.
*/
Q_INVOKABLE float getDomainMaxScale() const;
@ -529,16 +530,16 @@ public:
/**jsdoc
* Provides read only access to the current eye height of the avatar.
* This height is only an estimate and might be incorrect for avatars that are missing standard joints.
* @function AvatarData.getEyeHeight
* @returns {number} eye height of avatar in meters
* @function MyAvatar.getEyeHeight
* @returns {number} Eye height of avatar in meters.
*/
Q_INVOKABLE virtual float getEyeHeight() const { return _targetScale * getUnscaledEyeHeight(); }
/**jsdoc
* Provides read only access to the current height of the avatar.
* This height is only an estimate and might be incorrect for avatars that are missing standard joints.
* @function AvatarData.getHeight
* @returns {number} height of avatar in meters
* @function MyAvatar.getHeight
* @returns {number} Height of avatar in meters.
*/
Q_INVOKABLE virtual float getHeight() const;
@ -547,49 +548,372 @@ public:
void setDomainMinimumHeight(float domainMinimumHeight);
void setDomainMaximumHeight(float domainMaximumHeight);
// Hand State
/**jsdoc
* @function MyAvatar.setHandState
* @param {string} state
*/
Q_INVOKABLE void setHandState(char s) { _handState = s; }
/**jsdoc
* @function MyAvatar.getHandState
* @returns {string}
*/
Q_INVOKABLE char getHandState() const { return _handState; }
const QVector<JointData>& getRawJointData() const { return _jointData; }
/**jsdoc
* @function MyAvatar.setRawJointData
* @param {JointData[]} data
*/
Q_INVOKABLE void setRawJointData(QVector<JointData> data);
/**jsdoc
* Set a specific joint's rotation and position relative to its parent.
* <p>Setting joint data completely overrides/replaces all motion from the default animation system including inverse
* kinematics, but just for the specified joint. So for example, if you were to procedurally manipulate the finger joints,
* the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate
* joints in the inverse kinematics chain, the inverse kinematics might not function as you expect. For example, if you set
* the rotation of the elbow, the hand inverse kinematics position won't end up in the right place.</p>
* @function MyAvatar.setJointData
* @param {number} index - The index of the joint.
* @param {Quat} rotation - The rotation of the joint relative to its parent.
* @param {Vec3} translation - The translation of the joint relative to its parent.
* @example <caption>Set your avatar to it's default T-pose for a while.<br />
* <img alt="Avatar in T-pose" src="https://docs.highfidelity.com/user/pages/06.api-reference/25.myavatar/t-pose.png" />
* </caption>
* // Set all joint translations and rotations to defaults.
* var i, length, rotation, translation;
* for (i = 0, length = MyAvatar.getJointNames().length; i < length; i++) {
* rotation = MyAvatar.getDefaultJointRotation(i);
* translation = MyAvatar.getDefaultJointTranslation(i);
* MyAvatar.setJointData(i, rotation, translation);
* }
*
* // Restore your avatar's motion after 5s.
* Script.setTimeout(function () {
* MyAvatar.clearJointsData();
* }, 5000);
*/
Q_INVOKABLE virtual void setJointData(int index, const glm::quat& rotation, const glm::vec3& translation);
/**jsdoc
* Set a specific joint's rotation relative to its parent.
* <p>Setting joint data completely overrides/replaces all motion from the default animation system including inverse
* kinematics, but just for the specified joint. So for example, if you were to procedurally manipulate the finger joints,
* the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate
* joints in the inverse kinematics chain, the inverse kinematics might not function as you expect. For example, if you set
* the rotation of the elbow, the hand inverse kinematics position won't end up in the right place.</p>
* @function MyAvatar.setJointRotation
* @param {number} index - The index of the joint.
* @param {Quat} rotation - The rotation of the joint relative to its parent.
*/
Q_INVOKABLE virtual void setJointRotation(int index, const glm::quat& rotation);
/**jsdoc
* Set a specific joint's translation relative to its parent.
* <p>Setting joint data completely overrides/replaces all motion from the default animation system including inverse
* kinematics, but just for the specified joint. So for example, if you were to procedurally manipulate the finger joints,
* the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate
* joints in the inverse kinematics chain, the inverse kinematics might not function as you expect. For example, if you set
* the rotation of the elbow, the hand inverse kinematics position won't end up in the right place.</p>
* @function MyAvatar.setJointTranslation
* @param {number} index - The index of the joint.
* @param {Vec3} translation - The translation of the joint relative to its parent.
*/
Q_INVOKABLE virtual void setJointTranslation(int index, const glm::vec3& translation);
/**jsdoc
* Clear joint translations and rotations set by script for a specific joint. This restores all motion from the default
* animation system including inverse kinematics for that joint.
* <p>Note: This is slightly faster than the function variation that specifies the joint name.</p>
* @function MyAvatar.clearJointData
* @param {number} index - The index of the joint.
*/
Q_INVOKABLE virtual void clearJointData(int index);
/**jsdoc
* @function MyAvatar.isJointDataValid
* @param {number} index
* @returns {boolean}
*/
Q_INVOKABLE bool isJointDataValid(int index) const;
/**jsdoc
* Get the rotation of a joint relative to its parent. For information on the joint hierarchy used, see
* <a href="https://docs.highfidelity.com/create-and-explore/avatars/avatar-standards">Avatar Standards</a>.
* @function MyAvatar.getJointRotation
* @param {number} index - The index of the joint.
* @returns {Quat} The rotation of the joint relative to its parent.
*/
Q_INVOKABLE virtual glm::quat getJointRotation(int index) const;
/**jsdoc
* Get the translation of a joint relative to its parent. For information on the joint hierarchy used, see
* <a href="https://docs.highfidelity.com/create-and-explore/avatars/avatar-standards">Avatar Standards</a>.
* @function MyAvatar.getJointTranslation
* @param {number} index - The index of the joint.
* @returns {Vec3} The translation of the joint relative to its parent.
*/
Q_INVOKABLE virtual glm::vec3 getJointTranslation(int index) const;
/**jsdoc
* Set a specific joint's rotation and position relative to its parent.
* <p>Setting joint data completely overrides/replaces all motion from the default animation system including inverse
* kinematics, but just for the specified joint. So for example, if you were to procedurally manipulate the finger joints,
* the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate
* joints in the inverse kinematics chain, the inverse kinematics might not function as you expect. For example, if you set
* the rotation of the elbow, the hand inverse kinematics position won't end up in the right place.</p>
* @function MyAvatar.setJointData
* @param {string} name - The name of the joint.
* @param {Quat} rotation - The rotation of the joint relative to its parent.
* @param {Vec3} translation - The translation of the joint relative to its parent.
*/
Q_INVOKABLE virtual void setJointData(const QString& name, const glm::quat& rotation, const glm::vec3& translation);
/**jsdoc
* Set a specific joint's rotation relative to its parent.
* <p>Setting joint data completely overrides/replaces all motion from the default animation system including inverse
* kinematics, but just for the specified joint. So for example, if you were to procedurally manipulate the finger joints,
* the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate
* joints in the inverse kinematics chain, the inverse kinematics might not function as you expect. For example, if you set
* the rotation of the elbow, the hand inverse kinematics position won't end up in the right place.</p>
* @function MyAvatar.setJointRotation
* @param {string} name - The name of the joint.
* @param {Quat} rotation - The rotation of the joint relative to its parent.
* @example <caption>Set your avatar to its default T-pose then rotate its right arm.<br />
* <img alt="Avatar in T-pose with arm rotated"
* src="https://docs.highfidelity.com/user/pages/06.api-reference/25.myavatar/armpose.png" /></caption>
* // Set all joint translations and rotations to defaults.
* var i, length, rotation, translation;
* for (i = 0, length = MyAvatar.getJointNames().length; i < length; i++) {
* rotation = MyAvatar.getDefaultJointRotation(i);
* translation = MyAvatar.getDefaultJointTranslation(i);
* MyAvatar.setJointData(i, rotation, translation);
* }
*
* // Rotate the right arm.
* var newArmRotation = { x: 0.47, y: 0.22, z: -0.02, w: 0.87 };
* MyAvatar.setJointRotation("RightArm", newArmRotation);
*
* // Restore your avatar's motion after 5s.
* Script.setTimeout(function () {
* MyAvatar.clearJointsData();
* }, 5000);
*/
Q_INVOKABLE virtual void setJointRotation(const QString& name, const glm::quat& rotation);
/**jsdoc
* Set a specific joint's translation relative to its parent.
* <p>Setting joint data completely overrides/replaces all motion from the default animation system including inverse
* kinematics, but just for the specified joint. So for example, if you were to procedurally manipulate the finger joints,
* the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate
* joints in the inverse kinematics chain, the inverse kinematics might not function as you expect. For example, if you set
* the rotation of the elbow, the hand inverse kinematics position won't end up in the right place.</p>
* @function MyAvatar.setJointTranslation
* @param {string} name - The name of the joint.
* @param {Vec3} translation - The translation of the joint relative to its parent.
* @example <caption>Stretch your avatar's neck. Depending on the avatar you are using, you will either see a gap between
* the head and body or you will see the neck stretched.<br />
* <img alt="Avatar with neck stretched"
* src="https://docs.highfidelity.com/user/pages/06.api-reference/25.myavatar/stretched-neck.png" /></caption>
* // Stretch your avatar's neck.
* MyAvatar.setJointTranslation("Neck", { x: 0, y: 25, z: 0 });
*
* // Restore your avatar's neck after 5s.
* Script.setTimeout(function () {
* MyAvatar.clearJointData("Neck");
* }, 5000);
*/
Q_INVOKABLE virtual void setJointTranslation(const QString& name, const glm::vec3& translation);
/**jsdoc
* Clear joint translations and rotations set by script for a specific joint. This restores all motion from the default
* animation system including inverse kinematics for that joint.
* <p>Note: This is slightly slower than the function variation that specifies the joint index.</p>
* @function MyAvatar.clearJointData
* @param {string} name - The name of the joint.
* @example <caption>Offset and restore the position of your avatar's head.</caption>
* // Move your avatar's head up by 25cm from where it should be.
* MyAvatar.setJointTranslation("Neck", { x: 0, y: 0.25, z: 0 });
*
* // Restore your avatar's head to its default position after 5s.
* Script.setTimeout(function () {
* MyAvatar.clearJointData("Neck");
* }, 5000);
*/
Q_INVOKABLE virtual void clearJointData(const QString& name);
/**jsdoc
* @function MyAvatar.isJointDataValid
* @param {string} name
* @returns {boolean}
*/
Q_INVOKABLE virtual bool isJointDataValid(const QString& name) const;
/**jsdoc
* Get the rotation of a joint relative to its parent. For information on the joint hierarchy used, see
* <a href="https://docs.highfidelity.com/create-and-explore/avatars/avatar-standards">Avatar Standards</a>.
* @function MyAvatar.getJointRotation
* @param {string} name - The name of the joint.
* @returns {Quat} The rotation of the joint relative to its parent.
* @example <caption>Report the rotation of your avatar's hips joint.</caption>
* print(JSON.stringify(MyAvatar.getJointRotation("Hips")));
*/
Q_INVOKABLE virtual glm::quat getJointRotation(const QString& name) const;
/**jsdoc
* Get the translation of a joint relative to its parent. For information on the joint hierarchy used, see
* <a href="https://docs.highfidelity.com/create-and-explore/avatars/avatar-standards">Avatar Standards</a>.
* @function MyAvatar.getJointTranslation
* @param {number} name - The name of the joint.
* @returns {Vec3} The translation of the joint relative to its parent.
* @example <caption>Report the translation of your avatar's hips joint.</caption>
* print(JSON.stringify(MyAvatar.getJointRotation("Hips")));
*/
Q_INVOKABLE virtual glm::vec3 getJointTranslation(const QString& name) const;
/**jsdoc
* Get the rotations of all joints in the current avatar. Each joint's rotation is relative to its parent joint.
* @function MyAvatar.getJointRotations
* @returns {Quat[]} The rotations of all joints relative to each's parent. The values are in the same order as the array
* returned by {@link MyAvatar.getJointNames}.
* @example <caption>Report the rotations of all your avatar's joints.</caption>
* print(JSON.stringify(MyAvatar.getJointRotations()));
*/
Q_INVOKABLE virtual QVector<glm::quat> getJointRotations() const;
/**jsdoc
* @function MyAvatar.getJointTranslations
* @returns {Vec3[]}
*/
Q_INVOKABLE virtual QVector<glm::vec3> getJointTranslations() const;
/**jsdoc
* Set the rotations of all joints in the current avatar. Each joint's rotation is relative to its parent joint.
* <p>Setting joint data completely overrides/replaces all motion from the default animation system including inverse
* kinematics, but just for the specified joint. So for example, if you were to procedurally manipulate the finger joints,
* the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate
* joints in the inverse kinematics chain, the inverse kinematics might not function as you expect. For example, if you set
* the rotation of the elbow, the hand inverse kinematics position won't end up in the right place.</p>
* @function MyAvatar.setJointRotations
* @param {Quat[]} jointRotations - The rotations for all joints in the avatar. The values are in the same order as the
* array returned by {@link MyAvatar.getJointNames}.
* @example <caption>Set your avatar to its default T-pose then rotate its right arm.<br />
* <img alt="Avatar in T-pose" src="https://docs.highfidelity.com/user/pages/06.api-reference/25.myavatar/armpose.png" />
* </caption>
* // Set all joint translations and rotations to defaults.
* var i, length, rotation, translation;
* for (i = 0, length = MyAvatar.getJointNames().length; i < length; i++) {
* rotation = MyAvatar.getDefaultJointRotation(i);
* translation = MyAvatar.getDefaultJointTranslation(i);
* MyAvatar.setJointData(i, rotation, translation);
* }
*
* // Get all join rotations.
* var jointRotations = MyAvatar.getJointRotations();
*
* // Update the rotation of the right arm in the array.
* jointRotations[MyAvatar.getJointIndex("RightArm")] = { x: 0.47, y: 0.22, z: -0.02, w: 0.87 };
*
* // Update all joint rotations.
* MyAvatar.setJointRotations(jointRotations);
*
* // Restore your avatar's motion after 5s.
* Script.setTimeout(function () {
* MyAvatar.clearJointsData();
* }, 5000);
*/
Q_INVOKABLE virtual void setJointRotations(const QVector<glm::quat>& jointRotations);
/**jsdoc
* @function MyAvatar.setJointTranslations
* @param {Vec3[]} translations
*/
Q_INVOKABLE virtual void setJointTranslations(const QVector<glm::vec3>& jointTranslations);
/**jsdoc
* Clear all joint translations and rotations that have been set by script. This restores all motion from the default
* animation system including inverse kinematics for all joints.
* @function MyAvatar.clearJointsData
* @example <caption>Set your avatar to it's default T-pose for a while.</caption>
* // Set all joint translations and rotations to defaults.
* var i, length, rotation, translation;
* for (i = 0, length = MyAvatar.getJointNames().length; i < length; i++) {
* rotation = MyAvatar.getDefaultJointRotation(i);
* translation = MyAvatar.getDefaultJointTranslation(i);
* MyAvatar.setJointData(i, rotation, translation);
* }
*
* // Restore your avatar's motion after 5s.
* Script.setTimeout(function () {
* MyAvatar.clearJointsData();
* }, 5000);
*/
Q_INVOKABLE virtual void clearJointsData();
/**jsdoc
* Get the joint index for a named joint. The joint index value is the position of the joint in the array returned by
* {@link MyAvatar.getJointNames}.
* @function MyAvatar.getJointIndex
* @param {string} name - The name of the joint.
* @returns {number} The index of the joint.
* @example <caption>Report the index of your avatar's left arm joint.</caption>
* print(JSON.stringify(MyAvatar.getJointIndex("LeftArm"));
*/
/// Returns the index of the joint with the specified name, or -1 if not found/unknown.
Q_INVOKABLE virtual int getJointIndex(const QString& name) const;
/**jsdoc
* Get the names of all the joints in the current avatar.
* @function MyAvatar.getJointNames
* @returns {string[]} The joint names.
* @example <caption>Report the names of all the joints in your current avatar.</caption>
* print(JSON.stringify(MyAvatar.getJointNames()));
*/
Q_INVOKABLE virtual QStringList getJointNames() const;
/**jsdoc
* @function MyAvatar.setBlendshape
* @param {string} name
* @param {number} value
*/
Q_INVOKABLE void setBlendshape(QString name, float val) { _headData->setBlendshape(name, val); }
/**jsdoc
* @function MyAvatar.getAttachmentsVariant
* @returns {object}
*/
// FIXME: Can this name be improved? Can it be deprecated?
Q_INVOKABLE QVariantList getAttachmentsVariant() const;
/**jsdoc
* @function MyAvatar.setAttachmentsVariant
* @param {object} variant
*/
// FIXME: Can this name be improved? Can it be deprecated?
Q_INVOKABLE void setAttachmentsVariant(const QVariantList& variant);
/**jsdoc
* @function MyAvatar.updateAvatarEntity
* @param {Uuid} entityID
* @param {string} entityData
*/
Q_INVOKABLE void updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData);
/**jsdoc
* @function MyAvatar.clearAvatarEntity
* @param {Uuid} entityID
*/
Q_INVOKABLE void clearAvatarEntity(const QUuid& entityID);
/**jsdoc
* @function MyAvatar.setForceFaceTrackerConnected
* @param {boolean} connected
*/
Q_INVOKABLE void setForceFaceTrackerConnected(bool connected) { _forceFaceTrackerConnected = connected; }
// key state
@ -627,15 +951,96 @@ public:
markIdentityDataChanged();
}
/**jsdoc
* Get information about all models currently attached to your avatar.
* @function MyAvatar.getAttachmentData
* @returns {MyAvatar.AttachmentData[]} Information about all models attached to your avatar.
* @example <caption>Report the URLs of all current attachments.</caption>
* var attachments = MyAvatar.getaAttachmentData();
* for (var i = 0; i < attachments.length; i++) {
* print (attachments[i].modelURL);
* }
*/
Q_INVOKABLE QVector<AttachmentData> getAttachmentData() const;
/**jsdoc
* Set all models currently attached to your avatar. For example, if you retrieve attachment data using
* {@link MyAvatar.getAttachmentData}, make changes to it, and then want to update your avatar's attachments per the
* changed data. You can also remove all attachments by using setting <code>attachmentData</code> to <code>null</code>.
* @function MyAvatar.setAttachmentData
* @param {MyAvatar.AttachmentData[]} attachmentData - The attachment data defining the models to have attached to your avatar. Use
* <code>null</code> to remove all attachments.
* @example <caption>Remove a hat attachment if your avatar is wearing it.</caption>
* var hatURL = "https://s3.amazonaws.com/hifi-public/tony/cowboy-hat.fbx";
* var attachments = MyAvatar.getAttachmentData();
*
* for (var i = 0; i < attachments.length; i++) {
* if (attachments[i].modelURL === hatURL) {
* attachments.splice(i, 1);
* MyAvatar.setAttachmentData(attachments);
* break;
* }
* }
*/
Q_INVOKABLE virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData);
/**jsdoc
* Attach a model to your avatar. For example, you can give your avatar a hat to wear, a guitar to hold, or a surfboard to
* stand on.
* <p>Note: Attached models are models only; they are not entities and can not be manipulated using the {@link Entities} API.
* Nor can you use this function to attach an entity (such as a sphere or a box) to your avatar.</p>
* @function MyAvatar.attach
* @param {string} modelURL - The URL of the model to attach. Models can be .FBX or .OBJ format.
* @param {string} [jointName=""] - The name of the avatar joint (see {@link MyAvatar.getJointNames}) to attach the model
* to.
* @param {Vec3} [translation=Vec3.ZERO] - The offset to apply to the model relative to the joint position.
* @param {Quat} [rotation=Quat.IDENTITY] - The rotation to apply to the model relative to the joint orientation.
* @param {number} [scale=1.0] - The scale to apply to the model.
* @param {boolean} [isSoft=false] - If the model has a skeleton, set this to <code>true</code> so that the bones of the
* attached model's skeleton are be rotated to fit the avatar's current pose. <code>isSoft</code> is used, for example,
* to have clothing that moves with the avatar.<br />
* If <code>true</code>, the <code>translation</code>, <code>rotation</code>, and <code>scale</code> parameters are
* ignored.
* @param {boolean} [allowDuplicates=false]
* @param {boolean} [useSaved=true]
* @example <caption>Attach a cowboy hat to your avatar's head.</caption>
* var attachment = {
* modelURL: "https://s3.amazonaws.com/hifi-public/tony/cowboy-hat.fbx",
* jointName: "Head",
* translation: {"x": 0, "y": 0.25, "z": 0},
* rotation: {"x": 0, "y": 0, "z": 0, "w": 1},
* scale: 1,
* isSoft: false
* };
*
* MyAvatar.attach(attachment.modelURL,
* attachment.jointName,
* attachment.translation,
* attachment.rotation,
* attachment.scale,
* attachment.isSoft);
*/
Q_INVOKABLE virtual void attach(const QString& modelURL, const QString& jointName = QString(),
const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(),
float scale = 1.0f, bool isSoft = false,
bool allowDuplicates = false, bool useSaved = true);
/**jsdoc
* Detach the most recently attached instance of a particular model from either a specific joint or any joint.
* @function MyAvatar.detachOne
* @param {string} modelURL - The URL of the model to detach.
* @param {string} [jointName=""] - The name of the joint to detach the model from. If <code>""</code>, then the most
* recently attached model is removed from which ever joint it was attached to.
*/
Q_INVOKABLE void detachOne(const QString& modelURL, const QString& jointName = QString());
/**jsdoc
* Detach all instances of a particular model from either a specific joint or all joints.
* @function MyAvatar.detachAll
* @param {string} modelURL - The URL of the model to detach.
* @param {string} [jointName=""] - The name of the joint to detach the model from. If <code>""</code>, then the model is
* detached from all joints.
*/
Q_INVOKABLE void detachAll(const QString& modelURL, const QString& jointName = QString());
QString getSkeletonModelURLFromScript() const { return _skeletonModelURL.toString(); }
@ -657,19 +1062,63 @@ public:
glm::vec3 getClientGlobalPosition() const { return _globalPosition; }
glm::vec3 getGlobalBoundingBoxCorner() const { return _globalPosition + _globalBoundingBoxOffset - _globalBoundingBoxDimensions; }
/**jsdoc
* @function MyAvatar.getAvatarEntityData
* @returns {object}
*/
Q_INVOKABLE AvatarEntityMap getAvatarEntityData() const;
/**jsdoc
* @function MyAvatar.setAvatarEntityData
* @param {object} avatarEntityData
*/
Q_INVOKABLE void setAvatarEntityData(const AvatarEntityMap& avatarEntityData);
virtual void setAvatarEntityDataChanged(bool value) { _avatarEntityDataChanged = value; }
void insertDetachedEntityID(const QUuid entityID);
AvatarEntityIDs getAndClearRecentlyDetachedIDs();
/**jsdoc
* @function MyAvatar.getSensorToWorldMatrix
* @returns {Mat4}
*/
// thread safe
Q_INVOKABLE glm::mat4 getSensorToWorldMatrix() const;
/**jsdoc
* @function MyAvatar.getSensorToWorldScale
* @returns {number}
*/
// thread safe
Q_INVOKABLE float getSensorToWorldScale() const;
/**jsdoc
* @function MyAvatar.getControllerLeftHandMatrix
* @returns {Mat4}
*/
// thread safe
Q_INVOKABLE glm::mat4 getControllerLeftHandMatrix() const;
/**jsdoc
* @function MyAvatar.getControllerRightHandMatrix
* @returns {Mat4}
*/
// thread safe
Q_INVOKABLE glm::mat4 getControllerRightHandMatrix() const;
/**jsdoc
* @function MyAvatar.getDataRate
* @param {string} [rateName=""]
* @returns {number}
*/
Q_INVOKABLE float getDataRate(const QString& rateName = QString("")) const;
/**jsdoc
* @function MyAvatar.getUpdateRate
* @param {string} [rateName=""]
* @returns {number}
*/
Q_INVOKABLE float getUpdateRate(const QString& rateName = QString("")) const;
int getJointCount() const { return _jointData.size(); }
@ -705,17 +1154,60 @@ public:
virtual void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) {}
signals:
/**jsdoc
* @function MyAvatar.displayNameChanged
* @returns {Signal}
*/
void displayNameChanged();
/**jsdoc
* @function MyAvatar.sessionDisplayNameChanged
* @returns {Signal}
*/
void sessionDisplayNameChanged();
/**jsdoc
* @function MyAvatar.skeletonModelURLChanged
* @returns {Signal}
*/
void skeletonModelURLChanged();
/**jsdoc
* @function MyAvatar.lookAtSnappingChanged
* @param {boolean} enabled
* @returns {Signal}
*/
void lookAtSnappingChanged(bool enabled);
/**jsdoc
* @function MyAvatar.sessionUUIDChanged
* @returns {Signal}
*/
void sessionUUIDChanged();
public slots:
/**jsdoc
* @function MyAvatar.sendAvatarDataPacket
* @param {boolean} [sendAll=false]
*/
void sendAvatarDataPacket(bool sendAll = false);
/**jsdoc
* @function MyAvatar.sendIdentityPacket
*/
void sendIdentityPacket();
/**jsdoc
* @function MyAvatar.setJointMappingsFromNetworkReply
*/
void setJointMappingsFromNetworkReply();
/**jsdoc
* @function MyAvatar.setSessionUUID
* @param {Uuid} sessionUUID
*/
virtual void setSessionUUID(const QUuid& sessionUUID) {
if (sessionUUID != getID()) {
if (sessionUUID == QUuid()) {
@ -727,13 +1219,45 @@ public slots:
}
}
/**jsdoc
* @function MyAvatar.getAbsoluteJointRotationInObjectFrame
* @param {number} index
* @returns {Quat}
*/
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override;
/**jsdoc
* @function MyAvatar.getAbsoluteJointTranslationInObjectFrame
* @param {number} index
* @returns {Vec3}
*/
virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override;
/**jsdoc
* @function MyAvatar.setAbsoluteJointRotationInObjectFrame
* @param {number} index
* @param {Quat} rotation
* @returns {boolean}
*/
virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) override { return false; }
/**jsdoc
* @function MyAvatar.setAbsoluteJointTranslationInObjectFrame
* @param {number} index
* @param {Vec3} translation
* @returns {boolean}
*/
virtual bool setAbsoluteJointTranslationInObjectFrame(int index, const glm::vec3& translation) override { return false; }
/**jsdoc
* @function MyAvatar.getTargetScale
* @returns {number}
*/
float getTargetScale() const { return _targetScale; } // why is this a slot?
/**jsdoc
* @function MyAvatar.resetLastSent
*/
void resetLastSent() { _lastToByteArray = 0; }
protected:

View file

@ -9,6 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_AvatarHashMap_h
#define hifi_AvatarHashMap_h
@ -29,6 +30,15 @@
#include "AvatarData.h"
/**jsdoc
* The AvatarHashMap API deals with functionality related to Avatar information and connectivity
* @namespace AvatarHashMap
*/
// JSDoc 3.5.5 doesn't augment @property definitions.
// These functions are being copied into Avatar classes which inherit the AvatarHashMap
class AvatarHashMap : public QObject, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
@ -39,9 +49,28 @@ public:
int size() { return _avatarHash.size(); }
// Currently, your own avatar will be included as the null avatar id.
/**jsdoc
* @function AvatarHashMap.getAvatarIdentifiers
*/
Q_INVOKABLE QVector<QUuid> getAvatarIdentifiers();
/**jsdoc
* @function AvatarHashMap.getAvatarsInRange
* @param {Vec3} position
* @param {float} rangeMeters
* @returns {string[]}
*/
Q_INVOKABLE QVector<QUuid> getAvatarsInRange(const glm::vec3& position, float rangeMeters) const;
/**jsdoc
* @function AvatarHashMap.getAvatar
* @param {string} avatarID
* @returns {ScriptAvatarData}
*/
// Null/Default-constructed QUuids will return MyAvatar
Q_INVOKABLE virtual ScriptAvatarData* getAvatar(QUuid avatarID) { return new ScriptAvatarData(getAvatarBySessionID(avatarID)); }
@ -49,18 +78,75 @@ public:
int numberOfAvatarsInRange(const glm::vec3& position, float rangeMeters);
signals:
/**jsdoc
* @function AvatarHashMap.avatarAddedEvent
* @param {string} sessionUUID
* @returns {Signal}
*/
void avatarAddedEvent(const QUuid& sessionUUID);
/**jsdoc
* @function AvatarHashMap.avatarRemovedEvent
* @param {string} sessionUUID
* @returns {Signal}
*/
void avatarRemovedEvent(const QUuid& sessionUUID);
/**jsdoc
* @function AvatarHashMap.avatarSessionChangedEvent
* @param {string} sessionUUID
* @param {string} oldUUID
* @returns {Signal}
*/
void avatarSessionChangedEvent(const QUuid& sessionUUID,const QUuid& oldUUID);
public slots:
/**jsdoc
* @function AvatarHashMap.isAvatarInRange
* @param {string} position
* @param {string} range
* @returns {boolean}
*/
bool isAvatarInRange(const glm::vec3 & position, const float range);
protected slots:
/**jsdoc
* @function AvatarHashMap.sessionUUIDChanged
* @param {string} sessionUUID
* @param {string} oldUUID
*/
void sessionUUIDChanged(const QUuid& sessionUUID, const QUuid& oldUUID);
/**jsdoc
* @function AvatarHashMap.processAvatarDataPacket
* @param {} message
* @param {} sendingNode
*/
void processAvatarDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
/**jsdoc
* @function AvatarHashMap.processAvatarIdentityPacket
* @param {} message
* @param {} sendingNode
*/
void processAvatarIdentityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
/**jsdoc
* @function AvatarHashMap.processKillAvatar
* @param {} message
* @param {} sendingNode
*/
void processKillAvatar(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
protected:

View file

@ -172,6 +172,17 @@ private:
ReticleInterface* _reticleInterface { nullptr };
};
/**jsdoc
* @namespace Reticle
* @property {boolean} allowMouseCapture
* @property {number} depth
* @property {Vec2} maximumPosition
* @property {boolean} mouseCaptured
* @property {boolean} pointingAtSystemOverlay
* @property {Vec2} position
* @property {number} scale
* @property {boolean} visible
*/
// Scripting interface available to control the Reticle
class ReticleInterface : public QObject {
Q_OBJECT
@ -187,25 +198,82 @@ class ReticleInterface : public QObject {
public:
ReticleInterface(CompositorHelper* outer) : QObject(outer), _compositor(outer) {}
/**jsdoc
* @function Reticle.isMouseCaptured
* @returns {boolean}
*/
Q_INVOKABLE bool isMouseCaptured() { return _compositor->shouldCaptureMouse(); }
/**jsdoc
* @function Reticle.getAllowMouseCapture
* @returns {boolean}
*/
Q_INVOKABLE bool getAllowMouseCapture() { return _compositor->getAllowMouseCapture(); }
/**jsdoc
* @function Reticle.setAllowMouseCapture
* @param {boolean} allowMouseCaptured
*/
Q_INVOKABLE void setAllowMouseCapture(bool value) { return _compositor->setAllowMouseCapture(value); }
/**jsdoc
* @function Reticle.isPointingAtSystemOverlay
* @returns {boolean}
*/
Q_INVOKABLE bool isPointingAtSystemOverlay() { return !_compositor->getReticleOverDesktop(); }
/**jsdoc
* @function Reticle.getVisible
* @returns {boolean}
*/
Q_INVOKABLE bool getVisible() { return _compositor->getReticleVisible(); }
/**jsdoc
* @function Reticle.setVisible
* @param {boolean} visible
*/
Q_INVOKABLE void setVisible(bool visible) { _compositor->setReticleVisible(visible); }
/**jsdoc
* @function Reticle.getDepth
* @returns {number}
*/
Q_INVOKABLE float getDepth() { return _compositor->getReticleDepth(); }
/**jsdoc
* @function Reticle.setDepth
* @param {number} depth
*/
Q_INVOKABLE void setDepth(float depth) { _compositor->setReticleDepth(depth); }
/**jsdoc
* @function Reticle.getScale
* @returns {number}
*/
Q_INVOKABLE float getScale() const;
/**jsdoc
* @function Reticle.setScale
* @param {number} scale
*/
Q_INVOKABLE void setScale(float scale);
/**jsdoc
* @function Reticle.getPosition
* @returns {Vec2}
*/
Q_INVOKABLE QVariant getPosition() const;
/**jsdoc
* @function Reticle.setPosition
* @param {Vec2} position
*/
Q_INVOKABLE void setPosition(QVariant position);
/**jsdoc
* @function Reticle.getMaximumPosition
* @returns {Vec2}
*/
Q_INVOKABLE glm::vec2 getMaximumPosition() { return _compositor->getReticleMaximumPosition(); }
private:

View file

@ -249,8 +249,11 @@ void WebEntityRenderer::doRender(RenderArgs* args) {
batch.setResourceTexture(0, _texture);
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
// Turn off jitter for these entities
batch.pushProjectionJitter();
DependencyManager::get<GeometryCache>()->bindWebBrowserProgram(batch, fadeRatio < OPAQUE_ALPHA_THRESHOLD);
DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f, 1.0f, 1.0f, fadeRatio), _geometryId);
batch.popProjectionJitter();
}
bool WebEntityRenderer::hasWebSurface() {

View file

@ -766,6 +766,36 @@ QVector<QUuid> EntityScriptingInterface::findEntitiesByType(const QString entity
return result;
}
QVector<QUuid> EntityScriptingInterface::findEntitiesByName(const QString entityName, const glm::vec3& center, float radius, bool caseSensitiveSearch) const {
QVector<QUuid> result;
if (_entityTree) {
QVector<EntityItemPointer> entities;
_entityTree->withReadLock([&] {
_entityTree->findEntities(center, radius, entities);
});
if (caseSensitiveSearch) {
foreach(EntityItemPointer entity, entities) {
if (entity->getName() == entityName) {
result << entity->getEntityItemID();
}
}
} else {
QString entityNameLowerCase = entityName.toLower();
foreach(EntityItemPointer entity, entities) {
QString entityItemLowerCase = entity->getName().toLower();
if (entityItemLowerCase == entityNameLowerCase) {
result << entity->getEntityItemID();
}
}
}
}
return result;
}
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking,
const QScriptValue& entityIdsToInclude, const QScriptValue& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) {
QVector<EntityItemID> entitiesToInclude = qVectorEntityItemIDFromScriptValue(entityIdsToInclude);

View file

@ -387,6 +387,21 @@ public slots:
/// this function will not find any entities in script engine contexts which don't have access to entities
Q_INVOKABLE QVector<QUuid> findEntitiesByType(const QString entityType, const glm::vec3& center, float radius) const;
/**jsdoc
* Find all entities of a particular name that intersect a sphere defined by a center point and radius.
* @function Entities.findEntitiesByName
* @param {Entities.EntityType} entityName - The name of the entity to search for.
* @param {Vec3} center - The point about which to search.
* @param {number} radius - The radius within which to search.
* @param {bool} caseSensitiveSearch - Choose whether to to return case sensitive results back.
* @returns {Uuid[]} An array of entity IDs of the specified type that intersect the search sphere. The array is empty if
* no entities could be found.
* @example <caption>Get back a list of entities</caption>
* var entityIDs = Entities.findEntitiesByName("Light-Target", MyAvatar.position, 10, false);
* print("Number of Entities with the name Light-Target " + entityIDs.length);
*/
Q_INVOKABLE QVector<QUuid> findEntitiesByName(const QString entityName, const glm::vec3& center, float radius, bool caseSensitiveSearch = false ) const;
/**jsdoc
* Find the first entity intersected by a {@link PickRay}. <code>Light</code> and <code>Zone</code> entities are not
* intersected unless they've been configured as pickable using {@link Entities.setLightsArePickable|setLightsArePickable}

View file

@ -44,8 +44,9 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] =
(&::gpu::gl::GLBackend::do_setModelTransform),
(&::gpu::gl::GLBackend::do_setViewTransform),
(&::gpu::gl::GLBackend::do_setProjectionTransform),
(&::gpu::gl::GLBackend::do_setViewportTransform),
(&::gpu::gl::GLBackend::do_setProjectionTransform),
(&::gpu::gl::GLBackend::do_setProjectionJitter),
(&::gpu::gl::GLBackend::do_setViewportTransform),
(&::gpu::gl::GLBackend::do_setDepthRangeTransform),
(&::gpu::gl::GLBackend::do_setPipeline),
@ -166,7 +167,18 @@ void GLBackend::renderPassTransfer(const Batch& batch) {
case Batch::COMMAND_drawIndexedInstanced:
case Batch::COMMAND_multiDrawIndirect:
case Batch::COMMAND_multiDrawIndexedIndirect:
_transform.preUpdate(_commandIndex, _stereo);
{
Vec2u outputSize{ 1,1 };
if (_output._framebuffer) {
outputSize.x = _output._framebuffer->getWidth();
outputSize.y = _output._framebuffer->getHeight();
} else if (glm::dot(_transform._projectionJitter, _transform._projectionJitter)>0.0f) {
qCWarning(gpugllogging) << "Jittering needs to have a frame buffer to be set";
}
_transform.preUpdate(_commandIndex, _stereo, outputSize);
}
break;
case Batch::COMMAND_disableContextStereo:
@ -179,8 +191,10 @@ void GLBackend::renderPassTransfer(const Batch& batch) {
case Batch::COMMAND_setViewportTransform:
case Batch::COMMAND_setViewTransform:
case Batch::COMMAND_setProjectionTransform: {
CommandCall call = _commandCalls[(*command)];
case Batch::COMMAND_setProjectionTransform:
case Batch::COMMAND_setProjectionJitter:
{
CommandCall call = _commandCalls[(*command)];
(this->*(call))(batch, *offset);
break;
}
@ -254,6 +268,8 @@ void GLBackend::render(const Batch& batch) {
if (!batch.isStereoEnabled()) {
_stereo._enable = false;
}
// Reset jitter
_transform._projectionJitter = Vec2(0.0f, 0.0f);
{
PROFILE_RANGE(render_gpu_gl_detail, "Transfer");

View file

@ -126,6 +126,7 @@ public:
virtual void do_setModelTransform(const Batch& batch, size_t paramOffset) final;
virtual void do_setViewTransform(const Batch& batch, size_t paramOffset) final;
virtual void do_setProjectionTransform(const Batch& batch, size_t paramOffset) final;
virtual void do_setProjectionJitter(const Batch& batch, size_t paramOffset) final;
virtual void do_setViewportTransform(const Batch& batch, size_t paramOffset) final;
virtual void do_setDepthRangeTransform(const Batch& batch, size_t paramOffset) final;
@ -367,6 +368,7 @@ protected:
Mat4 _projection;
Vec4i _viewport { 0, 0, 1, 1 };
Vec2 _depthRange { 0.0f, 1.0f };
Vec2 _projectionJitter{ 0.0f, 0.0f };
bool _invalidView { false };
bool _invalidProj { false };
bool _invalidViewport { false };
@ -379,7 +381,7 @@ protected:
mutable List::const_iterator _camerasItr;
mutable size_t _currentCameraOffset{ INVALID_OFFSET };
void preUpdate(size_t commandIndex, const StereoState& stereo);
void preUpdate(size_t commandIndex, const StereoState& stereo, Vec2u framebufferSize);
void update(size_t commandIndex, const StereoState& stereo) const;
void bindCurrentCamera(int stereoSide) const;
} _transform;

View file

@ -28,6 +28,12 @@ void GLBackend::do_setProjectionTransform(const Batch& batch, size_t paramOffset
_transform._invalidProj = true;
}
void GLBackend::do_setProjectionJitter(const Batch& batch, size_t paramOffset) {
_transform._projectionJitter.x = batch._params[paramOffset]._float;
_transform._projectionJitter.y = batch._params[paramOffset+1]._float;
_transform._invalidProj = true;
}
void GLBackend::do_setViewportTransform(const Batch& batch, size_t paramOffset) {
memcpy(&_transform._viewport, batch.readData(batch._params[paramOffset]._uint), sizeof(Vec4i));
@ -90,7 +96,7 @@ void GLBackend::syncTransformStateCache() {
_transform._enabledDrawcallInfoBuffer = false;
}
void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const StereoState& stereo) {
void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const StereoState& stereo, Vec2u framebufferSize) {
// Check all the dirty flags and update the state accordingly
if (_invalidViewport) {
_camera._viewport = glm::vec4(_viewport);
@ -117,20 +123,21 @@ void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const Stereo
if (_invalidView || _invalidProj || _invalidViewport) {
size_t offset = _cameraUboSize * _cameras.size();
Vec2 finalJitter = _projectionJitter / Vec2(framebufferSize);
_cameraOffsets.push_back(TransformStageState::Pair(commandIndex, offset));
if (stereo.isStereo()) {
#ifdef GPU_STEREO_CAMERA_BUFFER
_cameras.push_back(CameraBufferElement(_camera.getEyeCamera(0, stereo, _view), _camera.getEyeCamera(1, stereo, _view)));
_cameras.push_back(CameraBufferElement(_camera.getEyeCamera(0, stereo, _view, finalJitter), _camera.getEyeCamera(1, stereo, _view, finalJitter)));
#else
_cameras.push_back((_camera.getEyeCamera(0, stereo, _view)));
_cameras.push_back((_camera.getEyeCamera(1, stereo, _view)));
_cameras.push_back((_camera.getEyeCamera(0, stereo, _view, finalJitter)));
_cameras.push_back((_camera.getEyeCamera(1, stereo, _view, finalJitter)));
#endif
} else {
#ifdef GPU_STEREO_CAMERA_BUFFER
_cameras.push_back(CameraBufferElement(_camera.recomputeDerived(_view)));
_cameras.push_back(CameraBufferElement(_camera.getMonoCamera(_view, finalJitter)));
#else
_cameras.push_back((_camera.recomputeDerived(_view)));
_cameras.push_back((_camera.getMonoCamera(_view, finalJitter)));
#endif
}
}

View file

@ -265,6 +265,22 @@ void Batch::setProjectionTransform(const Mat4& proj) {
_params.emplace_back(cacheData(sizeof(Mat4), &proj));
}
void Batch::setProjectionJitter(float jx, float jy) {
_projectionJitter.x = jx;
_projectionJitter.y = jy;
pushProjectionJitter(jx, jy);
}
void Batch::pushProjectionJitter(float jx, float jy) {
ADD_COMMAND(setProjectionJitter);
_params.emplace_back(jx);
_params.emplace_back(jy);
}
void Batch::popProjectionJitter() {
pushProjectionJitter(_projectionJitter.x, _projectionJitter.y);
}
void Batch::setViewportTransform(const Vec4i& viewport) {
ADD_COMMAND(setViewportTransform);

View file

@ -167,6 +167,10 @@ public:
void resetViewTransform() { setViewTransform(Transform(), false); }
void setViewTransform(const Transform& view, bool camera = true);
void setProjectionTransform(const Mat4& proj);
void setProjectionJitter(float jx = 0.0f, float jy = 0.0f);
// Very simple 1 level stack management of jitter.
void pushProjectionJitter(float jx = 0.0f, float jy = 0.0f);
void popProjectionJitter();
// Viewport is xy = low left corner in framebuffer, zw = width height of the viewport, expressed in pixels
void setViewportTransform(const Vec4i& viewport);
void setDepthRangeTransform(float nearDepth, float farDepth);
@ -292,8 +296,9 @@ public:
COMMAND_setModelTransform,
COMMAND_setViewTransform,
COMMAND_setProjectionTransform,
COMMAND_setViewportTransform,
COMMAND_setProjectionTransform,
COMMAND_setProjectionJitter,
COMMAND_setViewportTransform,
COMMAND_setDepthRangeTransform,
COMMAND_setPipeline,
@ -496,6 +501,7 @@ public:
NamedBatchDataMap _namedData;
glm::vec2 _projectionJitter{ 0.0f, 0.0f };
bool _enableStereo{ true };
bool _enableSkybox { false };

View file

@ -41,15 +41,19 @@ vec3 color_LinearToYCoCg(vec3 rgb) {
);
}
vec3 color_YCoCgToLinear(vec3 ycocg) {
vec3 color_YCoCgToUnclampedLinear(vec3 ycocg) {
// R = Y + Co - Cg
// G = Y + Cg
// B = Y - Co - Cg
return clamp(vec3(
return vec3(
ycocg.x + ycocg.y - ycocg.z,
ycocg.x + ycocg.z,
ycocg.x - ycocg.y - ycocg.z
), vec3(0.0), vec3(1.0));
);
}
vec3 color_YCoCgToLinear(vec3 ycocg) {
return clamp(color_YCoCgToUnclampedLinear(ycocg), vec3(0.0), vec3(1.0));
}
<@func declareColorWheel()@>

View file

@ -222,7 +222,7 @@ const Backend::TransformCamera& Backend::TransformCamera::recomputeDerived(const
return *this;
}
Backend::TransformCamera Backend::TransformCamera::getEyeCamera(int eye, const StereoState& _stereo, const Transform& xformView) const {
Backend::TransformCamera Backend::TransformCamera::getEyeCamera(int eye, const StereoState& _stereo, const Transform& xformView, Vec2 normalizedJitter) const {
TransformCamera result = *this;
Transform offsetTransform = xformView;
if (!_stereo._skybox) {
@ -231,6 +231,9 @@ Backend::TransformCamera Backend::TransformCamera::getEyeCamera(int eye, const S
// FIXME: If "skybox" the ipd is set to 0 for now, let s try to propose a better solution for this in the future
}
result._projection = _stereo._eyeProjections[eye];
normalizedJitter.x *= 2.0f;
result._projection[2][0] += normalizedJitter.x;
result._projection[2][1] += normalizedJitter.y;
result.recomputeDerived(offsetTransform);
result._stereoInfo = Vec4(1.0f, (float)eye, 0.0f, 0.0f);
@ -238,6 +241,14 @@ Backend::TransformCamera Backend::TransformCamera::getEyeCamera(int eye, const S
return result;
}
Backend::TransformCamera Backend::TransformCamera::getMonoCamera(const Transform& xformView, Vec2 normalizedJitter) const {
TransformCamera result = *this;
result._projection[2][0] += normalizedJitter.x;
result._projection[2][1] += normalizedJitter.y;
result.recomputeDerived(xformView);
return result;
}
// Counters for Buffer and Texture usage in GPU/Context
ContextMetricSize Backend::freeGPUMemSize;

View file

@ -64,19 +64,16 @@ public:
virtual void recycle() const = 0;
virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) = 0;
// UBO class... layout MUST match the layout in Transform.slh
class TransformCamera {
public:
mutable Mat4 _view;
mutable Mat4 _viewInverse;
mutable Mat4 _projectionViewUntranslated;
Mat4 _projection;
mutable Mat4 _projectionInverse;
Vec4 _viewport; // Public value is int but float in the shader to stay in floats for all the transform computations.
mutable Vec4 _stereoInfo;
// Shared header between C++ and GLSL
#include "TransformCamera_shared.slh"
class TransformCamera : public _TransformCamera {
public:
const Backend::TransformCamera& recomputeDerived(const Transform& xformView) const;
TransformCamera getEyeCamera(int eye, const StereoState& stereo, const Transform& xformView) const;
// Jitter should be divided by framebuffer size
TransformCamera getMonoCamera(const Transform& xformView, Vec2 normalizedJitter) const;
// Jitter should be divided by framebuffer size
TransformCamera getEyeCamera(int eye, const StereoState& stereo, const Transform& xformView, Vec2 normalizedJitter) const;
};
@ -136,7 +133,6 @@ protected:
friend class Context;
mutable ContextStats _stats;
StereoState _stereo;
};
class Context {

View file

@ -11,20 +11,14 @@
<@def GPU_TRANSFORM_STATE_SLH@>
<@func declareStandardCameraTransform()@>
struct TransformCamera {
mat4 _view;
mat4 _viewInverse;
mat4 _projectionViewUntranslated;
mat4 _projection;
mat4 _projectionInverse;
vec4 _viewport;
vec4 _stereoInfo;
};
<@include gpu/TransformCamera_shared.slh@>
#define TransformCamera _TransformCamera
layout(std140) uniform transformCameraBuffer {
#ifdef GPU_TRANSFORM_IS_STEREO
#ifdef GPU_TRANSFORM_STEREO_CAMERA
TransformCamera _camera[2];
TransformCamera _camera[2];
#else
TransformCamera _camera;
#endif

View file

@ -0,0 +1,26 @@
// glsl / C++ compatible source as interface for FadeEffect
#ifdef __cplusplus
# define _MAT4 Mat4
# define _VEC4 Vec4
# define _MUTABLE mutable
#else
# define _MAT4 mat4
# define _VEC4 vec4
# define _MUTABLE
#endif
struct _TransformCamera {
_MUTABLE _MAT4 _view;
_MUTABLE _MAT4 _viewInverse;
_MUTABLE _MAT4 _projectionViewUntranslated;
_MAT4 _projection;
_MUTABLE _MAT4 _projectionInverse;
_VEC4 _viewport; // Public value is int but float in the shader to stay in floats for all the transform computations.
_MUTABLE _VEC4 _stereoInfo;
};
// <@if 1@>
// Trigger Scribe include
// <@endif@> <!def that !>
//

View file

@ -21,6 +21,11 @@
#include "FBXReader.h"
#include "TextureCache.h"
/**jsdoc
* API to manage Model Cache resources
* @namespace ModelCache
*/
// Alias instead of derive to avoid copying
class NetworkTexture;
@ -136,7 +141,73 @@ class ModelCache : public ResourceCache, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
public:
/**jsdoc
* @namespace ModelCache
* @property numTotal {number} total number of total resources
* @property numCached {number} total number of cached resource
* @property sizeTotal {number} size in bytes of all resources
* @property sizeCached {number} size in bytes of all cached resources
*/
/**jsdoc
* Returns the total number of resources
* @function ModelCache.getNumTotalResources
* @returns {number}
*/
/**jsdoc
* Returns the total size in bytes of all resources
* @function ModelCache.getSizeTotalResources
* @returns {number}
*/
/**jsdoc
* Returns the total number of cached resources
* @function ModelCache.getNumCachedResources
* @returns {number}
*/
/**jsdoc
* Returns the total size in bytes of cached resources
* @function ModelCache.getSizeCachedResources
* @returns {number}
*/
/**jsdoc
* Returns list of all resource urls
* @function ModelCache.getResourceList
* @returns {string[]}
*/
/**jsdoc
* Asynchronously loads a resource from the spedified URL and returns it.
* @param url {string} url of resource to load
* @param fallback {string} fallback URL if load of the desired url fails
* @function ModelCache.getResource
* @returns {Resource}
*/
/**jsdoc
* Prefetches a resource.
* @param url {string} url of resource to load
* @function ModelCache.prefetch
* @returns {Resource}
*/
/**jsdoc
* @param {number} deltaSize
* @function ModelCache.updateTotalSize
* @returns {Resource}
*/
/**jsdoc
* @function ModelCache.dirty
* @returns {Signal}
*/
GeometryResource::Pointer getGeometryResource(const QUrl& url,
const QVariantHash& mapping = QVariantHash(),
const QUrl& textureBaseUrl = QUrl());

View file

@ -137,12 +137,90 @@ using NetworkTexturePointer = QSharedPointer<NetworkTexture>;
Q_DECLARE_METATYPE(QWeakPointer<NetworkTexture>)
/**jsdoc
* API to manage Texture Cache resources
* @namespace TextureCache
*/
/// Stores cached textures, including render-to-texture targets.
class TextureCache : public ResourceCache, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
public:
// Copied over from ResourceCache (see ResourceCache.h for reason)
/**jsdoc
* @namespace TextureCache
* @property numTotal {number} total number of total resources
* @property numCached {number} total number of cached resource
* @property sizeTotal {number} size in bytes of all resources
* @property sizeCached {number} size in bytes of all cached resources
*/
/**jsdoc
* Returns the total number of resources
* @function TextureCache.getNumTotalResources
* @returns {number}
*/
/**jsdoc
* Returns the total size in bytes of all resources
* @function TextureCache.getSizeTotalResources
* @returns {number}
*/
/**jsdoc
* Returns the total number of cached resources
* @function TextureCache.getNumCachedResources
* @returns {number}
*/
/**jsdoc
* Returns the total size in bytes of cached resources
* @function TextureCache.getSizeCachedResources
* @returns {number}
*/
/**jsdoc
* Returns list of all resource urls
* @function TextureCache.getResourceList
* @returns {string[]}
*/
/**jsdoc
* Returns animation resource for particular animation
* @function TextureCache.getAnimation
* @param url {string} url to load
* @returns {Resource} animation
*/
/**jsdoc
* Asynchronously loads a resource from the spedified URL and returns it.
* @param url {string} url of resource to load
* @param fallback {string} fallback URL if load of the desired url fails
* @function TextureCache.getResource
* @returns {Resource}
*/
/**jsdoc
* Prefetches a resource.
* @param url {string} url of resource to load
* @function TextureCache.prefetch
* @returns {Resource}
*/
/**jsdoc
* @param {number} deltaSize
* @function TextureCache.updateTotalSize
* @returns {Resource}
*/
/**jsdoc
* @function TextureCache.dirty
* @returns {Signal}
*/
/// Returns the ID of the permutation/normal texture used for Perlin noise shader programs. This texture
/// has two lines: the first, a set of random numbers in [0, 255] to be used as permutation offsets, and
/// the second, a set of random unit vectors to be used as noise gradients.
@ -180,6 +258,10 @@ public:
static const int DEFAULT_SPECTATOR_CAM_HEIGHT { 1024 };
signals:
/**jsdoc
* @function TextureCache.spectatorCameraFramebufferReset
* @returns {Signal}
*/
void spectatorCameraFramebufferReset();
protected:

View file

@ -1,94 +0,0 @@
//
// HMACAuth.cpp
// libraries/networking/src
//
// Created by Simon Walton on 3/19/2018.
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <openssl/opensslv.h>
#include <openssl/hmac.h>
#include "HMACAuth.h"
#include <QUuid>
#if OPENSSL_VERSION_NUMBER >= 0x10100000
HMACAuth::HMACAuth(AuthMethod authMethod)
: _hmacContext(HMAC_CTX_new())
, _authMethod(authMethod) { }
HMACAuth::~HMACAuth()
{
HMAC_CTX_free(_hmacContext);
}
#else
HMACAuth::HMACAuth(AuthMethod authMethod)
: _hmacContext(new HMAC_CTX())
, _authMethod(authMethod) {
HMAC_CTX_init(_hmacContext);
}
HMACAuth::~HMACAuth() {
HMAC_CTX_cleanup(_hmacContext);
delete _hmacContext;
}
#endif
bool HMACAuth::setKey(const char* keyValue, int keyLen) {
const EVP_MD* sslStruct = nullptr;
switch (_authMethod) {
case MD5:
sslStruct = EVP_md5();
break;
case SHA1:
sslStruct = EVP_sha1();
break;
case SHA224:
sslStruct = EVP_sha224();
break;
case SHA256:
sslStruct = EVP_sha256();
break;
case RIPEMD160:
sslStruct = EVP_ripemd160();
break;
default:
return false;
}
QMutexLocker lock(&_lock);
return (bool) HMAC_Init_ex(_hmacContext, keyValue, keyLen, sslStruct, nullptr);
}
bool HMACAuth::setKey(const QUuid& uidKey) {
const QByteArray rfcBytes(uidKey.toRfc4122());
return setKey(rfcBytes.constData(), rfcBytes.length());
}
bool HMACAuth::addData(const char* data, int dataLen) {
QMutexLocker lock(&_lock);
return (bool) HMAC_Update(_hmacContext, reinterpret_cast<const unsigned char*>(data), dataLen);
}
HMACAuth::HMACHash HMACAuth::result() {
HMACHash hashValue(EVP_MAX_MD_SIZE);
unsigned int hashLen;
QMutexLocker lock(&_lock);
HMAC_Final(_hmacContext, &hashValue[0], &hashLen);
hashValue.resize((size_t) hashLen);
// Clear state for possible reuse.
HMAC_Init_ex(_hmacContext, nullptr, 0, nullptr, nullptr);
return hashValue;
}

View file

@ -1,40 +0,0 @@
//
// HMACAuth.h
// libraries/networking/src
//
// Created by Simon Walton on 3/19/2018.
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_HMACAuth_h
#define hifi_HMACAuth_h
#include <vector>
#include <memory>
#include <QtCore/QMutex>
class QUuid;
class HMACAuth {
public:
enum AuthMethod { MD5, SHA1, SHA224, SHA256, RIPEMD160 };
using HMACHash = std::vector<unsigned char>;
explicit HMACAuth(AuthMethod authMethod = MD5);
~HMACAuth();
bool setKey(const char* keyValue, int keyLen);
bool setKey(const QUuid& uidKey);
bool addData(const char* data, int dataLen);
HMACHash result();
private:
QMutex _lock;
struct hmac_ctx_st* _hmacContext;
AuthMethod _authMethod;
};
#endif // hifi_HMACAuth_h

View file

@ -36,7 +36,6 @@
#include "HifiSockAddr.h"
#include "NetworkLogging.h"
#include "udt/Packet.h"
#include "HMACAuth.h"
static Setting::Handle<quint16> LIMITED_NODELIST_LOCAL_PORT("LimitedNodeList.LocalPort", 0);
@ -331,7 +330,7 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe
if (verifiedPacket && !ignoreVerification) {
QByteArray packetHeaderHash = NLPacket::verificationHashInHeader(packet);
QByteArray expectedHash = NLPacket::hashForPacketAndHMAC(packet, sourceNode->getAuthenticateHash());
QByteArray expectedHash = NLPacket::hashForPacketAndSecret(packet, sourceNode->getConnectionSecret());
// check if the md5 hash in the header matches the hash we would expect
if (packetHeaderHash != expectedHash) {
@ -371,15 +370,15 @@ void LimitedNodeList::collectPacketStats(const NLPacket& packet) {
_numCollectedBytes += packet.getDataSize();
}
void LimitedNodeList::fillPacketHeader(const NLPacket& packet, HMACAuth* hmacAuth) {
void LimitedNodeList::fillPacketHeader(const NLPacket& packet, const QUuid& connectionSecret) {
if (!PacketTypeEnum::getNonSourcedPackets().contains(packet.getType())) {
packet.writeSourceID(getSessionLocalID());
}
if (hmacAuth
if (!connectionSecret.isNull()
&& !PacketTypeEnum::getNonSourcedPackets().contains(packet.getType())
&& !PacketTypeEnum::getNonVerifiedPackets().contains(packet.getType())) {
packet.writeVerificationHash(*hmacAuth);
packet.writeVerificationHashGivenSecret(connectionSecret);
}
}
@ -395,17 +394,17 @@ qint64 LimitedNodeList::sendUnreliablePacket(const NLPacket& packet, const Node&
emit dataSent(destinationNode.getType(), packet.getDataSize());
destinationNode.recordBytesSent(packet.getDataSize());
return sendUnreliablePacket(packet, *destinationNode.getActiveSocket(), &destinationNode.getAuthenticateHash());
return sendUnreliablePacket(packet, *destinationNode.getActiveSocket(), destinationNode.getConnectionSecret());
}
qint64 LimitedNodeList::sendUnreliablePacket(const NLPacket& packet, const HifiSockAddr& sockAddr,
HMACAuth* hmacAuth) {
const QUuid& connectionSecret) {
Q_ASSERT(!packet.isPartOfMessage());
Q_ASSERT_X(!packet.isReliable(), "LimitedNodeList::sendUnreliablePacket",
"Trying to send a reliable packet unreliably.");
collectPacketStats(packet);
fillPacketHeader(packet, hmacAuth);
fillPacketHeader(packet, connectionSecret);
return _nodeSocket.writePacket(packet, sockAddr);
}
@ -418,7 +417,7 @@ qint64 LimitedNodeList::sendPacket(std::unique_ptr<NLPacket> packet, const Node&
emit dataSent(destinationNode.getType(), packet->getDataSize());
destinationNode.recordBytesSent(packet->getDataSize());
return sendPacket(std::move(packet), *activeSocket, &destinationNode.getAuthenticateHash());
return sendPacket(std::move(packet), *activeSocket, destinationNode.getConnectionSecret());
} else {
qCDebug(networking) << "LimitedNodeList::sendPacket called without active socket for node" << destinationNode << "- not sending";
return ERROR_SENDING_PACKET_BYTES;
@ -426,18 +425,18 @@ qint64 LimitedNodeList::sendPacket(std::unique_ptr<NLPacket> packet, const Node&
}
qint64 LimitedNodeList::sendPacket(std::unique_ptr<NLPacket> packet, const HifiSockAddr& sockAddr,
HMACAuth* hmacAuth) {
const QUuid& connectionSecret) {
Q_ASSERT(!packet->isPartOfMessage());
if (packet->isReliable()) {
collectPacketStats(*packet);
fillPacketHeader(*packet, hmacAuth);
fillPacketHeader(*packet, connectionSecret);
auto size = packet->getDataSize();
_nodeSocket.writePacket(std::move(packet), sockAddr);
return size;
} else {
return sendUnreliablePacket(*packet, sockAddr, hmacAuth);
return sendUnreliablePacket(*packet, sockAddr, connectionSecret);
}
}
@ -446,14 +445,13 @@ qint64 LimitedNodeList::sendUnreliableUnorderedPacketList(NLPacketList& packetLi
if (activeSocket) {
qint64 bytesSent = 0;
auto& connectionHash = destinationNode.getAuthenticateHash();
auto connectionSecret = destinationNode.getConnectionSecret();
// close the last packet in the list
packetList.closeCurrentPacket();
while (!packetList._packets.empty()) {
bytesSent += sendPacket(packetList.takeFront<NLPacket>(), *activeSocket,
&connectionHash);
bytesSent += sendPacket(packetList.takeFront<NLPacket>(), *activeSocket, connectionSecret);
}
emit dataSent(destinationNode.getType(), bytesSent);
@ -466,14 +464,14 @@ qint64 LimitedNodeList::sendUnreliableUnorderedPacketList(NLPacketList& packetLi
}
qint64 LimitedNodeList::sendUnreliableUnorderedPacketList(NLPacketList& packetList, const HifiSockAddr& sockAddr,
HMACAuth* hmacAuth) {
const QUuid& connectionSecret) {
qint64 bytesSent = 0;
// close the last packet in the list
packetList.closeCurrentPacket();
while (!packetList._packets.empty()) {
bytesSent += sendPacket(packetList.takeFront<NLPacket>(), sockAddr, hmacAuth);
bytesSent += sendPacket(packetList.takeFront<NLPacket>(), sockAddr, connectionSecret);
}
return bytesSent;
@ -501,7 +499,7 @@ qint64 LimitedNodeList::sendPacketList(std::unique_ptr<NLPacketList> packetList,
for (std::unique_ptr<udt::Packet>& packet : packetList->_packets) {
NLPacket* nlPacket = static_cast<NLPacket*>(packet.get());
collectPacketStats(*nlPacket);
fillPacketHeader(*nlPacket, &destinationNode.getAuthenticateHash());
fillPacketHeader(*nlPacket, destinationNode.getConnectionSecret());
}
return _nodeSocket.writePacketList(std::move(packetList), *activeSocket);
@ -524,7 +522,7 @@ qint64 LimitedNodeList::sendPacket(std::unique_ptr<NLPacket> packet, const Node&
auto& destinationSockAddr = (overridenSockAddr.isNull()) ? *destinationNode.getActiveSocket()
: overridenSockAddr;
return sendPacket(std::move(packet), destinationSockAddr, &destinationNode.getAuthenticateHash());
return sendPacket(std::move(packet), destinationSockAddr, destinationNode.getConnectionSecret());
}
int LimitedNodeList::updateNodeWithDataFromPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {

View file

@ -138,17 +138,19 @@ public:
// use sendUnreliablePacket to send an unreliable packet (that you do not need to move)
// either to a node (via its active socket) or to a manual sockaddr
qint64 sendUnreliablePacket(const NLPacket& packet, const Node& destinationNode);
qint64 sendUnreliablePacket(const NLPacket& packet, const HifiSockAddr& sockAddr, HMACAuth* hmacAuth = nullptr);
qint64 sendUnreliablePacket(const NLPacket& packet, const HifiSockAddr& sockAddr,
const QUuid& connectionSecret = QUuid());
// use sendPacket to send a moved unreliable or reliable NL packet to a node's active socket or manual sockaddr
qint64 sendPacket(std::unique_ptr<NLPacket> packet, const Node& destinationNode);
qint64 sendPacket(std::unique_ptr<NLPacket> packet, const HifiSockAddr& sockAddr, HMACAuth* hmacAuth = nullptr);
qint64 sendPacket(std::unique_ptr<NLPacket> packet, const HifiSockAddr& sockAddr,
const QUuid& connectionSecret = QUuid());
// use sendUnreliableUnorderedPacketList to unreliably send separate packets from the packet list
// either to a node's active socket or to a manual sockaddr
qint64 sendUnreliableUnorderedPacketList(NLPacketList& packetList, const Node& destinationNode);
qint64 sendUnreliableUnorderedPacketList(NLPacketList& packetList, const HifiSockAddr& sockAddr,
HMACAuth* hmacAuth = nullptr);
const QUuid& connectionSecret = QUuid());
// use sendPacketList to send reliable packet lists (ordered or unordered) to a node's active socket
// or to a manual sock addr
@ -370,7 +372,7 @@ protected:
qint64 writePacket(const NLPacket& packet, const HifiSockAddr& destinationSockAddr,
const QUuid& connectionSecret = QUuid());
void collectPacketStats(const NLPacket& packet);
void fillPacketHeader(const NLPacket& packet, HMACAuth* hmacAuth = nullptr);
void fillPacketHeader(const NLPacket& packet, const QUuid& connectionSecret = QUuid());
void setLocalSocket(const HifiSockAddr& sockAddr);

View file

@ -11,8 +11,6 @@
#include "NLPacket.h"
#include "HMACAuth.h"
int NLPacket::localHeaderSize(PacketType type) {
bool nonSourced = PacketTypeEnum::getNonSourcedPackets().contains(type);
bool nonVerified = PacketTypeEnum::getNonVerifiedPackets().contains(type);
@ -152,14 +150,18 @@ QByteArray NLPacket::verificationHashInHeader(const udt::Packet& packet) {
return QByteArray(packet.getData() + offset, NUM_BYTES_MD5_HASH);
}
QByteArray NLPacket::hashForPacketAndHMAC(const udt::Packet& packet, HMACAuth& hash) {
QByteArray NLPacket::hashForPacketAndSecret(const udt::Packet& packet, const QUuid& connectionSecret) {
QCryptographicHash hash(QCryptographicHash::Md5);
int offset = Packet::totalHeaderSize(packet.isPartOfMessage()) + sizeof(PacketType) + sizeof(PacketVersion)
+ NUM_BYTES_LOCALID + NUM_BYTES_MD5_HASH;
// add the packet payload and the connection UUID
hash.addData(packet.getData() + offset, packet.getDataSize() - offset);
auto hashResult { hash.result() };
return QByteArray((const char*) hashResult.data(), (int) hashResult.size());
hash.addData(connectionSecret.toRfc4122());
// return the hash
return hash.result();
}
void NLPacket::writeTypeAndVersion() {
@ -212,14 +214,14 @@ void NLPacket::writeSourceID(LocalID sourceID) const {
_sourceID = sourceID;
}
void NLPacket::writeVerificationHash(HMACAuth& hmacAuth) const {
void NLPacket::writeVerificationHashGivenSecret(const QUuid& connectionSecret) const {
Q_ASSERT(!PacketTypeEnum::getNonSourcedPackets().contains(_type) &&
!PacketTypeEnum::getNonVerifiedPackets().contains(_type));
auto offset = Packet::totalHeaderSize(isPartOfMessage()) + sizeof(PacketType) + sizeof(PacketVersion)
+ NUM_BYTES_LOCALID;
QByteArray verificationHash = hashForPacketAndHMAC(*this, hmacAuth);
QByteArray verificationHash = hashForPacketAndSecret(*this, connectionSecret);
memcpy(_packet.get() + offset, verificationHash.data(), verificationHash.size());
}

View file

@ -18,8 +18,6 @@
#include "udt/Packet.h"
class HMACAuth;
class NLPacket : public udt::Packet {
Q_OBJECT
public:
@ -71,7 +69,7 @@ public:
static LocalID sourceIDInHeader(const udt::Packet& packet);
static QByteArray verificationHashInHeader(const udt::Packet& packet);
static QByteArray hashForPacketAndHMAC(const udt::Packet& packet, HMACAuth& hash);
static QByteArray hashForPacketAndSecret(const udt::Packet& packet, const QUuid& connectionSecret);
PacketType getType() const { return _type; }
void setType(PacketType type);
@ -80,9 +78,9 @@ public:
void setVersion(PacketVersion version);
LocalID getSourceID() const { return _sourceID; }
void writeSourceID(LocalID sourceID) const;
void writeVerificationHash(HMACAuth& hmacAuth) const;
void writeVerificationHashGivenSecret(const QUuid& connectionSecret) const;
protected:

View file

@ -86,10 +86,10 @@ NodeType_t NodeType::fromString(QString type) {
Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket,
const HifiSockAddr& localSocket, QObject* parent) :
const HifiSockAddr& localSocket, QObject* parent) :
NetworkPeer(uuid, publicSocket, localSocket, parent),
_type(type),
_authenticateHash(new HMACAuth), _pingMs(-1), // "Uninitialized"
_pingMs(-1), // "Uninitialized"
_clockSkewUsec(0),
_mutex(),
_clockSkewMovingPercentile(30, 0.8f) // moving 80th percentile of 30 samples
@ -108,7 +108,6 @@ void Node::setType(char type) {
_symmetricSocket.setObjectName(typeString);
}
void Node::updateClockSkewUsec(qint64 clockSkewSample) {
_clockSkewMovingPercentile.updatePercentile(clockSkewSample);
_clockSkewUsec = (quint64)_clockSkewMovingPercentile.getValueAtPercentile();
@ -195,12 +194,3 @@ QDebug operator<<(QDebug debug, const Node& node) {
debug.nospace() << node.getPublicSocket() << "/" << node.getLocalSocket();
return debug.nospace();
}
void Node::setConnectionSecret(const QUuid& connectionSecret) {
if (_connectionSecret == connectionSecret) {
return;
}
_connectionSecret = connectionSecret;
_authenticateHash->setKey(_connectionSecret);
}

View file

@ -33,7 +33,6 @@
#include "SimpleMovingAverage.h"
#include "MovingPercentile.h"
#include "NodePermissions.h"
#include "HMACAuth.h"
class Node : public NetworkPeer {
Q_OBJECT
@ -56,8 +55,7 @@ public:
void setIsUpstream(bool isUpstream) { _isUpstream = isUpstream; }
const QUuid& getConnectionSecret() const { return _connectionSecret; }
void setConnectionSecret(const QUuid& connectionSecret);
HMACAuth& getAuthenticateHash() const { return *_authenticateHash; }
void setConnectionSecret(const QUuid& connectionSecret) { _connectionSecret = connectionSecret; }
NodeData* getLinkedData() const { return _linkedData.get(); }
void setLinkedData(std::unique_ptr<NodeData> linkedData) { _linkedData = std::move(linkedData); }
@ -99,7 +97,6 @@ private:
NodeType_t _type;
QUuid _connectionSecret;
std::unique_ptr<HMACAuth> _authenticateHash;
std::unique_ptr<NodeData> _linkedData;
bool _isReplicated { false };
int _pingMs;

View file

@ -1,4 +1,4 @@
//
//
// ResourceCache.h
// libraries/shared/src
//
@ -85,9 +85,7 @@ private:
/// Wrapper to expose resources to JS/QML
class ScriptableResource : public QObject {
Q_OBJECT
Q_PROPERTY(QUrl url READ getURL)
Q_PROPERTY(int state READ getState NOTIFY stateChanged)
/**jsdoc
* @constructor Resource
@ -95,6 +93,12 @@ class ScriptableResource : public QObject {
* @property state {Resource.State} current loading state
*/
Q_OBJECT
Q_PROPERTY(QUrl url READ getURL)
Q_PROPERTY(int state READ getState NOTIFY stateChanged)
public:
/**jsdoc
@ -181,11 +185,8 @@ Q_DECLARE_METATYPE(ScriptableResource*);
/// Base class for resource caches.
class ResourceCache : public QObject {
Q_OBJECT
Q_PROPERTY(size_t numTotal READ getNumTotalResources NOTIFY dirty)
Q_PROPERTY(size_t numCached READ getNumCachedResources NOTIFY dirty)
Q_PROPERTY(size_t sizeTotal READ getSizeTotalResources NOTIFY dirty)
Q_PROPERTY(size_t sizeCached READ getSizeCachedResources NOTIFY dirty)
// JSDoc 3.5.5 doesn't augment @property definitions.
// These functions are being copied into the different exposed cache classes
/**jsdoc
* @namespace ResourceCache
* @property numTotal {number} total number of total resources
@ -193,6 +194,10 @@ class ResourceCache : public QObject {
* @property sizeTotal {number} size in bytes of all resources
* @property sizeCached {number} size in bytes of all cached resources
*/
Q_PROPERTY(size_t numTotal READ getNumTotalResources NOTIFY dirty)
Q_PROPERTY(size_t numCached READ getNumCachedResources NOTIFY dirty)
Q_PROPERTY(size_t sizeTotal READ getSizeTotalResources NOTIFY dirty)
Q_PROPERTY(size_t sizeCached READ getSizeCachedResources NOTIFY dirty)
public:
/**jsdoc

View file

@ -91,7 +91,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
case PacketType::Ping:
return static_cast<PacketVersion>(PingVersion::IncludeConnectionID);
default:
return 19;
return 20;
}
}

View file

@ -187,7 +187,8 @@ const int AntialiasingPass_DepthMapSlot = 3;
const int AntialiasingPass_NextMapSlot = 4;
Antialiasing::Antialiasing() {
Antialiasing::Antialiasing(bool isSharpenEnabled) :
_isSharpenEnabled{ isSharpenEnabled } {
_antialiasingBuffers = std::make_shared<gpu::FramebufferSwapChain>(2U);
}
@ -282,8 +283,11 @@ const gpu::PipelinePointer& Antialiasing::getDebugBlendPipeline() {
}
void Antialiasing::configure(const Config& config) {
_sharpen = config.sharpen;
_params.edit().blend = config.blend;
_sharpen = config.sharpen * 0.25f;
if (!_isSharpenEnabled) {
_sharpen = 0.0f;
}
_params.edit().blend = config.blend * config.blend;
_params.edit().covarianceGamma = config.covarianceGamma;
_params.edit().setConstrainColor(config.constrainColor);
@ -328,11 +332,11 @@ void Antialiasing::run(const render::RenderContextPointer& renderContext, const
if (!_antialiasingBuffers->get(0)) {
// Link the antialiasing FBO to texture
auto format = sourceBuffer->getRenderBuffer(0)->getTexelFormat();
auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP);
for (int i = 0; i < 2; i++) {
auto& antiAliasingBuffer = _antialiasingBuffers->edit(i);
antiAliasingBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("antialiasing"));
auto format = gpu::Element::COLOR_SRGBA_32; // DependencyManager::get<FramebufferCache>()->getLightingTexture()->getTexelFormat();
auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR);
_antialiasingTextures[i] = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, defaultSampler);
antiAliasingBuffer->setRenderBuffer(0, _antialiasingTextures[i]);
}
@ -380,8 +384,6 @@ void Antialiasing::run(const render::RenderContextPointer& renderContext, const
batch.setResourceTexture(AntialiasingPass_VelocityMapSlot, nullptr);
batch.setResourceTexture(AntialiasingPass_NextMapSlot, nullptr);
});
args->popViewFrustum();
}
@ -495,7 +497,7 @@ void JitterSample::configure(const Config& config) {
_scale = config.scale;
}
void JitterSample::run(const render::RenderContextPointer& renderContext) {
void JitterSample::run(const render::RenderContextPointer& renderContext, Output& jitter) {
auto& current = _sampleSequence.currentIndex;
if (!_freeze) {
if (current >= 0) {
@ -504,39 +506,7 @@ void JitterSample::run(const render::RenderContextPointer& renderContext) {
current = -1;
}
}
auto args = renderContext->args;
auto viewFrustum = args->getViewFrustum();
auto jit = _sampleSequence.offsets[(current < 0 ? SEQUENCE_LENGTH : current)];
auto width = (float)args->_viewport.z;
auto height = (float)args->_viewport.w;
auto jx = 2.0f * jit.x / width;
auto jy = 2.0f * jit.y / height;
if (!args->isStereo()) {
auto projMat = viewFrustum.getProjection();
projMat[2][0] += jx;
projMat[2][1] += jy;
viewFrustum.setProjection(projMat);
viewFrustum.calculate();
args->pushViewFrustum(viewFrustum);
} else {
mat4 projMats[2];
args->_context->getStereoProjections(projMats);
jx *= 2.0f;
for (int i = 0; i < 2; i++) {
auto& projMat = projMats[i];
projMat[2][0] += jx;
projMat[2][1] += jy;
}
args->_context->setStereoProjections(projMats);
}
jitter = _sampleSequence.offsets[(current < 0 ? SEQUENCE_LENGTH : current)];
}

View file

@ -58,14 +58,15 @@ class JitterSample {
public:
enum {
SEQUENCE_LENGTH = 128
SEQUENCE_LENGTH = 64
};
using Config = JitterSampleConfig;
using JobModel = render::Job::Model<JitterSample, Config>;
using Output = glm::vec2;
using JobModel = render::Job::ModelO<JitterSample, Output, Config>;
void configure(const Config& config);
void run(const render::RenderContextPointer& renderContext);
void run(const render::RenderContextPointer& renderContext, Output& jitter);
private:
@ -105,11 +106,11 @@ class AntialiasingConfig : public render::Job::Config {
public:
AntialiasingConfig() : render::Job::Config(true) {}
float blend{ 0.05f };
float sharpen{ 0.15f };
float blend{ 0.25f };
float sharpen{ 0.05f };
bool constrainColor{ true };
float covarianceGamma{ 0.9f };
float covarianceGamma{ 0.65f };
bool feedbackColor{ false };
float debugX{ 0.0f };
@ -131,7 +132,7 @@ signals:
struct TAAParams {
float nope{ 0.0f };
float blend{ 0.05f };
float blend{ 0.15f };
float covarianceGamma{ 1.0f };
float debugShowVelocityThreshold{ 1.0f };
@ -168,7 +169,7 @@ public:
using Config = AntialiasingConfig;
using JobModel = render::Job::ModelI<Antialiasing, Inputs, Config>;
Antialiasing();
Antialiasing(bool isSharpenEnabled = true);
~Antialiasing();
void configure(const Config& config);
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs);
@ -189,6 +190,7 @@ private:
TAAParamsBuffer _params;
float _sharpen{ 0.15f };
int _sharpenLoc{ -1 };
bool _isSharpenEnabled{ true };
};

View file

@ -13,7 +13,7 @@
uniform sampler2D blurMap0;
uniform sampler2D blurMap1;
uniform sampler2D blurMap2;
uniform float intensity;
uniform vec3 intensity;
in vec2 varTexCoord0;
out vec4 outFragColor;
@ -23,5 +23,5 @@ void main(void) {
vec4 blur1 = texture(blurMap1, varTexCoord0);
vec4 blur2 = texture(blurMap2, varTexCoord0);
outFragColor = vec4((blur0.rgb+blur1.rgb+blur2.rgb)*intensity, 1.0f);
outFragColor = vec4(blur0.rgb*intensity.x + blur1.rgb*intensity.y + blur2.rgb*intensity.z, 1.0f);
}

View file

@ -50,7 +50,7 @@ void BloomThreshold::run(const render::RenderContextPointer& renderContext, cons
if (!_outputBuffer || _outputBuffer->getSize() != bufferSize) {
auto colorTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(inputBuffer->getTexelFormat(), bufferSize.x, bufferSize.y,
gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)));
gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT, gpu::Sampler::WRAP_CLAMP)));
_outputBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("BloomThreshold"));
_outputBuffer->setRenderBuffer(0, colorTexture);
@ -93,12 +93,14 @@ void BloomThreshold::run(const render::RenderContextPointer& renderContext, cons
outputs = _outputBuffer;
}
BloomApply::BloomApply() {
BloomApply::BloomApply() : _intensities{ 1.0f, 1.0f, 1.0f } {
}
void BloomApply::configure(const Config& config) {
_intensity = config.intensity;
_intensities.x = config.intensity / 3.0f;
_intensities.y = _intensities.x;
_intensities.z = _intensities.x;
}
void BloomApply::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) {
@ -106,10 +108,10 @@ void BloomApply::run(const render::RenderContextPointer& renderContext, const In
assert(renderContext->args->hasViewFrustum());
RenderArgs* args = renderContext->args;
static auto BLUR0_SLOT = 0;
static auto BLUR1_SLOT = 1;
static auto BLUR2_SLOT = 2;
static auto INTENSITY_SLOT = 3;
static const auto BLUR0_SLOT = 0;
static const auto BLUR1_SLOT = 1;
static const auto BLUR2_SLOT = 2;
static const auto INTENSITY_SLOT = 3;
if (!_pipeline) {
auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS();
@ -149,7 +151,7 @@ void BloomApply::run(const render::RenderContextPointer& renderContext, const In
batch.setResourceTexture(BLUR0_SLOT, blur0FB->getRenderBuffer(0));
batch.setResourceTexture(BLUR1_SLOT, blur1FB->getRenderBuffer(0));
batch.setResourceTexture(BLUR2_SLOT, blur2FB->getRenderBuffer(0));
batch._glUniform1f(INTENSITY_SLOT, _intensity / 3.0f);
batch._glUniform3f(INTENSITY_SLOT, _intensities.x, _intensities.y, _intensities.z);
batch.draw(gpu::TRIANGLE_STRIP, 4);
});
}
@ -306,19 +308,19 @@ float BloomConfig::getIntensity() const {
void BloomConfig::setSize(float value) {
std::string blurName{ "BloomBlurN" };
auto sigma = 0.5f+value*3.5f;
auto task = static_cast<render::Task::TaskConcept*>(_task);
for (auto i = 0; i < BLOOM_BLUR_LEVEL_COUNT; i++) {
blurName.back() = '0' + i;
auto task = static_cast<render::Task::TaskConcept*>(_task);
auto blurJobIt = task->editJob(blurName);
assert(blurJobIt != task->_jobs.end());
auto& gaussianBlur = blurJobIt->edit<render::BlurGaussian>();
auto gaussianBlurParams = gaussianBlur.getParameters();
gaussianBlurParams->setFilterGaussianTaps(5, sigma);
// Gaussian blur increases at each level to have a slower rolloff on the edge
// of the response
sigma *= 1.5f;
gaussianBlurParams->setFilterGaussianTaps(9, sigma);
}
auto blurJobIt = task->getJob("BloomApply");
assert(blurJobIt != task->_jobs.end());
blurJobIt->getConfiguration()->setProperty("sigma", sigma);
}
Bloom::Bloom() {
@ -350,7 +352,7 @@ void Bloom::build(JobModel& task, const render::Varying& inputs, render::Varying
// Mix all blur levels at quarter resolution
const auto applyInput = BloomApply::Inputs(bloomInputBuffer, blurFB0, blurFB1, blurFB2).asVarying();
task.addJob<BloomApply>("BloomApply", applyInput);
// And them blend result in additive manner on top of final color buffer
// And then blend result in additive manner on top of final color buffer
const auto drawInput = BloomDraw::Inputs(frameBuffer, bloomInputBuffer).asVarying();
task.addJob<BloomDraw>("BloomDraw", drawInput);

View file

@ -25,7 +25,7 @@ public:
BloomConfig() : render::Task::Config(false) {}
float size{ 0.8f };
float size{ 0.7f };
void setIntensity(float value);
float getIntensity() const;
@ -41,7 +41,7 @@ class BloomThresholdConfig : public render::Job::Config {
public:
float threshold{ 1.25f };
float threshold{ 0.9f };
signals:
void dirty();
@ -71,10 +71,12 @@ private:
class BloomApplyConfig : public render::Job::Config {
Q_OBJECT
Q_PROPERTY(float intensity MEMBER intensity NOTIFY dirty)
Q_PROPERTY(float sigma MEMBER sigma NOTIFY dirty)
public:
float intensity{ 0.8f };
float intensity{ 0.25f };
float sigma{ 1.0f };
signals:
void dirty();
@ -94,7 +96,7 @@ public:
private:
gpu::PipelinePointer _pipeline;
float _intensity{ 1.0f };
glm::vec3 _intensities;
};
class BloomDraw {

View file

@ -18,7 +18,7 @@ DeferredFrameTransform::DeferredFrameTransform() {
_frameTransformBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(FrameTransform), (const gpu::Byte*) &frameTransform));
}
void DeferredFrameTransform::update(RenderArgs* args) {
void DeferredFrameTransform::update(RenderArgs* args, glm::vec2 jitter) {
// Update the depth info with near and far (same for stereo)
auto nearZ = args->getViewFrustum().getNearClip();
@ -38,12 +38,23 @@ void DeferredFrameTransform::update(RenderArgs* args) {
args->getViewFrustum().evalProjectionMatrix(frameTransformBuffer.projectionMono);
// There may be some sort of mismatch here if the viewport size isn't the same as the frame buffer size as
// jitter is normalized by frame buffer size in TransformCamera. But we should be safe.
jitter.x /= args->_viewport.z;
jitter.y /= args->_viewport.w;
// Running in stereo ?
bool isStereo = args->isStereo();
if (!isStereo) {
frameTransformBuffer.projection[0] = frameTransformBuffer.projectionMono;
frameTransformBuffer.projectionUnjittered[0] = frameTransformBuffer.projectionMono;
frameTransformBuffer.invProjectionUnjittered[0] = glm::inverse(frameTransformBuffer.projectionUnjittered[0]);
frameTransformBuffer.stereoInfo = glm::vec4(0.0f, (float)args->_viewport.z, 0.0f, 0.0f);
frameTransformBuffer.invpixelInfo = glm::vec4(1.0f / args->_viewport.z, 1.0f / args->_viewport.w, 0.0f, 0.0f);
frameTransformBuffer.projection[0] = frameTransformBuffer.projectionUnjittered[0];
frameTransformBuffer.projection[0][2][0] += jitter.x;
frameTransformBuffer.projection[0][2][1] += jitter.y;
frameTransformBuffer.invProjection[0] = glm::inverse(frameTransformBuffer.projection[0]);
} else {
@ -52,22 +63,28 @@ void DeferredFrameTransform::update(RenderArgs* args) {
args->_context->getStereoProjections(projMats);
args->_context->getStereoViews(eyeViews);
jitter.x *= 2.0f;
for (int i = 0; i < 2; i++) {
// Compose the mono Eye space to Stereo clip space Projection Matrix
auto sideViewMat = projMats[i] * eyeViews[i];
frameTransformBuffer.projection[i] = sideViewMat;
frameTransformBuffer.invProjection[i] = glm::inverse(sideViewMat);
}
frameTransformBuffer.projectionUnjittered[i] = sideViewMat;
frameTransformBuffer.invProjectionUnjittered[i] = glm::inverse(sideViewMat);
frameTransformBuffer.projection[i] = frameTransformBuffer.projectionUnjittered[i];
frameTransformBuffer.projection[i][2][0] += jitter.x;
frameTransformBuffer.projection[i][2][1] += jitter.y;
frameTransformBuffer.invProjection[i] = glm::inverse(frameTransformBuffer.projection[i]);
}
frameTransformBuffer.stereoInfo = glm::vec4(1.0f, (float)(args->_viewport.z >> 1), 0.0f, 1.0f);
frameTransformBuffer.invpixelInfo = glm::vec4(1.0f / (float)(args->_viewport.z >> 1), 1.0f / args->_viewport.w, 0.0f, 0.0f);
}
}
void GenerateDeferredFrameTransform::run(const render::RenderContextPointer& renderContext, DeferredFrameTransformPointer& frameTransform) {
void GenerateDeferredFrameTransform::run(const render::RenderContextPointer& renderContext, const Input& jitter, Output& frameTransform) {
if (!frameTransform) {
frameTransform = std::make_shared<DeferredFrameTransform>();
}
frameTransform->update(renderContext->args);
frameTransform->update(renderContext->args, jitter);
}

View file

@ -25,7 +25,7 @@ public:
DeferredFrameTransform();
void update(RenderArgs* args);
void update(RenderArgs* args, glm::vec2 jitter);
UniformBufferView getFrameTransformBuffer() const { return _frameTransformBuffer; }
@ -43,16 +43,20 @@ protected:
glm::vec4 depthInfo;
// Stereo info is { isStereoFrame, halfWidth }
glm::vec4 stereoInfo{ 0.0 };
// Mono proj matrix or Left and Right proj matrix going from Mono Eye space to side clip space
glm::mat4 projection[2];
// Inverse proj matrix or Left and Right proj matrix going from Mono Eye space to side clip space
glm::mat4 invProjection[2];
// THe mono projection for sure
// Mono proj matrix or Left and Right proj matrix going from Mono Eye space to side clip space
glm::mat4 projection[2];
// Inverse proj matrix or Left and Right proj matrix going from Mono Eye space to side clip space
glm::mat4 invProjection[2];
// THe mono projection for sure
glm::mat4 projectionMono;
// Inv View matrix from eye space (mono) to world space
glm::mat4 invView;
// View matrix from world space to eye space (mono)
glm::mat4 view;
// Mono proj matrix or Left and Right proj matrix going from Mono Eye space to side clip space without jittering
glm::mat4 projectionUnjittered[2];
// Inverse proj matrix or Left and Right proj matrix going from Mono Eye space to side clip space without jittering
glm::mat4 invProjectionUnjittered[2];
FrameTransform() {}
};
@ -68,11 +72,14 @@ using DeferredFrameTransformPointer = std::shared_ptr<DeferredFrameTransform>;
class GenerateDeferredFrameTransform {
public:
using JobModel = render::Job::ModelO<GenerateDeferredFrameTransform, DeferredFrameTransformPointer>;
using Input = glm::vec2;
using Output = DeferredFrameTransformPointer;
using JobModel = render::Job::ModelIO<GenerateDeferredFrameTransform, Input, Output>;
GenerateDeferredFrameTransform() {}
void run(const render::RenderContextPointer& renderContext, DeferredFrameTransformPointer& frameTransform);
void run(const render::RenderContextPointer& renderContext, const Input& jitter, Output& frameTransform);
private:
};

View file

@ -35,6 +35,8 @@ struct DeferredFrameTransform {
mat4 _projectionMono;
mat4 _viewInverse;
mat4 _view;
mat4 _projectionUnJittered[2];
mat4 _invProjectionUnJittered[2];
};
uniform deferredFrameTransformBuffer {
@ -62,6 +64,12 @@ mat4 getProjection(int side) {
mat4 getProjectionMono() {
return frameTransform._projectionMono;
}
mat4 getUnjitteredProjection(int side) {
return frameTransform._projectionUnJittered[side];
}
mat4 getUnjitteredInvProjection(int side) {
return frameTransform._invProjectionUnJittered[side];
}
// positive near distance of the projection
float getProjectionNear() {
@ -138,6 +146,14 @@ vec3 evalEyePositionFromZdb(int side, float Zdb, vec2 texcoord) {
return eyePos.xyz / eyePos.w;
}
vec3 evalUnjitteredEyePositionFromZdb(int side, float Zdb, vec2 texcoord) {
// compute the view space position using the depth
vec3 clipPos;
clipPos.xyz = vec3(texcoord.xy, Zdb) * 2.0 - 1.0;
vec4 eyePos = frameTransform._invProjectionUnJittered[side] * vec4(clipPos.xyz, 1.0);
return eyePos.xyz / eyePos.w;
}
vec3 evalEyePositionFromZeye(int side, float Zeye, vec2 texcoord) {
float Zdb = evalZdbFromZeye(Zeye);
return evalEyePositionFromZdb(side, Zdb, texcoord);

View file

@ -2207,7 +2207,7 @@ static void buildWebShader(const gpu::ShaderPointer& vertShader, const gpu::Shad
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
PrepareStencil::testMaskDrawShape(*state);
PrepareStencil::testMaskDrawShapeNoAA(*state);
pipelinePointerOut = gpu::Pipeline::create(shaderPointerOut, state);
}

View file

@ -11,7 +11,6 @@
<@if not MODEL_MATERIAL_TEXTURES_SLH@>
<@def MODEL_MATERIAL_TEXTURES_SLH@>
<@func declareMaterialTexMapArrayBuffer()@>
const int MAX_TEXCOORDS = 2;
@ -48,6 +47,8 @@ TexMapArray getTexMapArray() {
<@func declareMaterialTextures(withAlbedo, withRoughness, withNormal, withMetallic, withEmissive, withOcclusion, withScattering)@>
#define TAA_TEXTURE_LOD_BIAS -1.0
<@include gpu/TextureTable.slh@>
#ifdef GPU_TEXTURE_TABLE_BINDLESS
@ -66,6 +67,7 @@ TextureTable(0, matTex);
<@if withAlbedo@>
#define albedoMap 0
vec4 fetchAlbedoMap(vec2 uv) {
// Should take into account TAA_TEXTURE_LOD_BIAS?
return tableTexValue(matTex, albedoMap, uv);
}
<@endif@>
@ -73,6 +75,7 @@ vec4 fetchAlbedoMap(vec2 uv) {
<@if withRoughness@>
#define roughnessMap 4
float fetchRoughnessMap(vec2 uv) {
// Should take into account TAA_TEXTURE_LOD_BIAS?
return tableTexValue(matTex, roughnessMap, uv).r;
}
<@endif@>
@ -80,6 +83,7 @@ float fetchRoughnessMap(vec2 uv) {
<@if withNormal@>
#define normalMap 1
vec3 fetchNormalMap(vec2 uv) {
// Should take into account TAA_TEXTURE_LOD_BIAS?
return tableTexValue(matTex, normalMap, uv).xyz;
}
<@endif@>
@ -87,6 +91,7 @@ vec3 fetchNormalMap(vec2 uv) {
<@if withMetallic@>
#define metallicMap 2
float fetchMetallicMap(vec2 uv) {
// Should take into account TAA_TEXTURE_LOD_BIAS?
return tableTexValue(matTex, metallicMap, uv).r;
}
<@endif@>
@ -94,6 +99,7 @@ float fetchMetallicMap(vec2 uv) {
<@if withEmissive@>
#define emissiveMap 3
vec3 fetchEmissiveMap(vec2 uv) {
// Should take into account TAA_TEXTURE_LOD_BIAS?
return tableTexValue(matTex, emissiveMap, uv).rgb;
}
<@endif@>
@ -119,14 +125,14 @@ float fetchScatteringMap(vec2 uv) {
<@if withAlbedo@>
uniform sampler2D albedoMap;
vec4 fetchAlbedoMap(vec2 uv) {
return texture(albedoMap, uv);
return texture(albedoMap, uv, TAA_TEXTURE_LOD_BIAS);
}
<@endif@>
<@if withRoughness@>
uniform sampler2D roughnessMap;
float fetchRoughnessMap(vec2 uv) {
return (texture(roughnessMap, uv).r);
return (texture(roughnessMap, uv, TAA_TEXTURE_LOD_BIAS).r);
}
<@endif@>
@ -134,7 +140,7 @@ float fetchRoughnessMap(vec2 uv) {
uniform sampler2D normalMap;
vec3 fetchNormalMap(vec2 uv) {
// unpack normal, swizzle to get into hifi tangent space with Y axis pointing out
vec2 t = 2.0 * (texture(normalMap, uv).rg - vec2(0.5, 0.5));
vec2 t = 2.0 * (texture(normalMap, uv, TAA_TEXTURE_LOD_BIAS).rg - vec2(0.5, 0.5));
vec2 t2 = t*t;
return vec3(t.x, sqrt(1.0 - t2.x - t2.y), t.y);
}
@ -143,14 +149,14 @@ vec3 fetchNormalMap(vec2 uv) {
<@if withMetallic@>
uniform sampler2D metallicMap;
float fetchMetallicMap(vec2 uv) {
return (texture(metallicMap, uv).r);
return (texture(metallicMap, uv, TAA_TEXTURE_LOD_BIAS).r);
}
<@endif@>
<@if withEmissive@>
uniform sampler2D emissiveMap;
vec3 fetchEmissiveMap(vec2 uv) {
return texture(emissiveMap, uv).rgb;
return texture(emissiveMap, uv, TAA_TEXTURE_LOD_BIAS).rgb;
}
<@endif@>
@ -164,7 +170,7 @@ float fetchOcclusionMap(vec2 uv) {
<@if withScattering@>
uniform sampler2D scatteringMap;
float fetchScatteringMap(vec2 uv) {
float scattering = texture(scatteringMap, uv).r; // boolean scattering for now
float scattering = texture(scatteringMap, uv, TAA_TEXTURE_LOD_BIAS).r; // boolean scattering for now
return max(((scattering - 0.1) / 0.9), 0.0);
return texture(scatteringMap, uv).r; // boolean scattering for now
}
@ -234,8 +240,8 @@ vec3 fetchLightmapMap(vec2 uv) {
vec3 normalizedTangent = normalize(<$interpolatedTangent$>.xyz);
vec3 normalizedBitangent = cross(normalizedNormal, normalizedTangent);
// attenuate the normal map divergence from the mesh normal based on distance
// The attenuation range [20,100] meters from the eye is arbitrary for now
vec3 localNormal = mix(<$fetchedNormal$>, vec3(0.0, 1.0, 0.0), smoothstep(20.0, 100.0, (-<$fragPosES$>).z));
// The attenuation range [30,100] meters from the eye is arbitrary for now
vec3 localNormal = mix(<$fetchedNormal$>, vec3(0.0, 1.0, 0.0), smoothstep(30.0, 100.0, (-<$fragPosES$>).z));
<$normal$> = vec3(normalizedBitangent * localNormal.x + normalizedNormal * localNormal.y + normalizedTangent * localNormal.z);
}
<@endfunc@>

View file

@ -46,6 +46,7 @@ void DrawOverlay3D::run(const RenderContextPointer& renderContext, const Inputs&
const auto& inItems = inputs.get0();
const auto& lightingModel = inputs.get1();
const auto jitter = inputs.get2();
config->setNumDrawn((int)inItems.size());
emit config->numDrawnChanged();
@ -75,7 +76,8 @@ void DrawOverlay3D::run(const RenderContextPointer& renderContext, const Inputs&
args->getViewFrustum().evalViewTransform(viewMat);
batch.setProjectionTransform(projMat);
batch.setViewTransform(viewMat);
batch.setProjectionJitter(jitter.x, jitter.y);
batch.setViewTransform(viewMat);
// Setup lighting model for all items;
batch.setUniformBuffer(render::ShapePipeline::Slot::LIGHTING_MODEL, lightingModel->getParametersBuffer());

View file

@ -60,7 +60,7 @@ protected:
class DrawOverlay3D {
public:
using Inputs = render::VaryingSet2 <render::ItemBounds, LightingModelPointer>;
using Inputs = render::VaryingSet3<render::ItemBounds, LightingModelPointer, glm::vec2>;
using Config = DrawOverlay3DConfig;
using JobModel = render::Job::ModelI<DrawOverlay3D, Inputs, Config>;

View file

@ -95,10 +95,10 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
fadeEffect->build(task, opaques);
task.addJob<JitterSample>("JitterCam");
const auto jitter = task.addJob<JitterSample>("JitterCam");
// Prepare deferred, generate the shared Deferred Frame Transform
const auto deferredFrameTransform = task.addJob<GenerateDeferredFrameTransform>("DeferredFrameTransform");
const auto deferredFrameTransform = task.addJob<GenerateDeferredFrameTransform>("DeferredFrameTransform", jitter);
const auto lightingModel = task.addJob<MakeLightingModel>("LightingModel");
@ -116,12 +116,11 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
task.addJob<PrepareStencil>("PrepareStencil", primaryFramebuffer);
// Render opaque objects in DeferredBuffer
const auto opaqueInputs = DrawStateSortDeferred::Inputs(opaques, lightingModel).asVarying();
const auto opaqueInputs = DrawStateSortDeferred::Inputs(opaques, lightingModel, jitter).asVarying();
task.addJob<DrawStateSortDeferred>("DrawOpaqueDeferred", opaqueInputs, shapePlumber);
task.addJob<EndGPURangeTimer>("OpaqueRangeTimer", opaqueRangeTimer);
// Opaque all rendered
// Linear Depth Pass
@ -188,8 +187,37 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
task.addJob<DebugLightClusters>("DebugLightClusters", debugLightClustersInputs);
}
const auto outlineRangeTimer = task.addJob<BeginGPURangeTimer>("BeginHighlightRangeTimer", "Highlight");
// Select items that need to be outlined
const auto selectionBaseName = "contextOverlayHighlightList";
const auto selectedItems = addSelectItemJobs(task, selectionBaseName, metas, opaques, transparents);
const auto outlineInputs = DrawHighlightTask::Inputs(items.get0(), deferredFramebuffer, lightingFramebuffer, deferredFrameTransform).asVarying();
task.addJob<DrawHighlightTask>("DrawHighlight", outlineInputs);
task.addJob<EndGPURangeTimer>("HighlightRangeTimer", outlineRangeTimer);
const auto overlaysInFrontRangeTimer = task.addJob<BeginGPURangeTimer>("BeginOverlaysInFrontRangeTimer", "BeginOverlaysInFrontRangeTimer");
// Layered Overlays
const auto filteredOverlaysOpaque = task.addJob<FilterLayeredItems>("FilterOverlaysLayeredOpaque", overlayOpaques, Item::LAYER_3D_FRONT);
const auto filteredOverlaysTransparent = task.addJob<FilterLayeredItems>("FilterOverlaysLayeredTransparent", overlayTransparents, Item::LAYER_3D_FRONT);
const auto overlaysInFrontOpaque = filteredOverlaysOpaque.getN<FilterLayeredItems::Outputs>(0);
const auto overlaysInFrontTransparent = filteredOverlaysTransparent.getN<FilterLayeredItems::Outputs>(0);
const auto overlayInFrontOpaquesInputs = DrawOverlay3D::Inputs(overlaysInFrontOpaque, lightingModel, jitter).asVarying();
const auto overlayInFrontTransparentsInputs = DrawOverlay3D::Inputs(overlaysInFrontTransparent, lightingModel, jitter).asVarying();
task.addJob<DrawOverlay3D>("DrawOverlayInFrontOpaque", overlayInFrontOpaquesInputs, true);
task.addJob<DrawOverlay3D>("DrawOverlayInFrontTransparent", overlayInFrontTransparentsInputs, false);
task.addJob<EndGPURangeTimer>("OverlaysInFrontRangeTimer", overlaysInFrontRangeTimer);
const auto toneAndPostRangeTimer = task.addJob<BeginGPURangeTimer>("BeginToneAndPostRangeTimer", "PostToneOverlaysAntialiasing");
// AA job before bloom to limit flickering
const auto antialiasingInputs = Antialiasing::Inputs(deferredFrameTransform, lightingFramebuffer, linearDepthTarget, velocityBuffer).asVarying();
task.addJob<Antialiasing>("Antialiasing", antialiasingInputs);
// Add bloom
const auto bloomInputs = Bloom::Inputs(deferredFrameTransform, lightingFramebuffer).asVarying();
task.addJob<Bloom>("Bloom", bloomInputs);
@ -198,16 +226,6 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
const auto toneMappingInputs = ToneMappingDeferred::Inputs(lightingFramebuffer, primaryFramebuffer).asVarying();
task.addJob<ToneMappingDeferred>("ToneMapping", toneMappingInputs);
const auto outlineRangeTimer = task.addJob<BeginGPURangeTimer>("BeginHighlightRangeTimer", "Highlight");
// Select items that need to be outlined
const auto selectionBaseName = "contextOverlayHighlightList";
const auto selectedItems = addSelectItemJobs(task, selectionBaseName, metas, opaques, transparents);
const auto outlineInputs = DrawHighlightTask::Inputs(items.get0(), deferredFramebuffer, primaryFramebuffer, deferredFrameTransform).asVarying();
task.addJob<DrawHighlightTask>("DrawHighlight", outlineInputs);
task.addJob<EndGPURangeTimer>("HighlightRangeTimer", outlineRangeTimer);
{ // Debug the bounds of the rendered items, still look at the zbuffer
task.addJob<DrawBounds>("DrawMetaBounds", metas);
task.addJob<DrawBounds>("DrawOpaqueBounds", opaques);
@ -230,26 +248,11 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
task.addJob<DrawBounds>("DrawSelectionBounds", selectedItems);
}
// Layered Overlays
const auto filteredOverlaysOpaque = task.addJob<FilterLayeredItems>("FilterOverlaysLayeredOpaque", overlayOpaques, Item::LAYER_3D_FRONT);
const auto filteredOverlaysTransparent = task.addJob<FilterLayeredItems>("FilterOverlaysLayeredTransparent", overlayTransparents, Item::LAYER_3D_FRONT);
const auto overlaysInFrontOpaque = filteredOverlaysOpaque.getN<FilterLayeredItems::Outputs>(0);
const auto overlaysInFrontTransparent = filteredOverlaysTransparent.getN<FilterLayeredItems::Outputs>(0);
const auto overlayInFrontOpaquesInputs = DrawOverlay3D::Inputs(overlaysInFrontOpaque, lightingModel).asVarying();
const auto overlayInFrontTransparentsInputs = DrawOverlay3D::Inputs(overlaysInFrontTransparent, lightingModel).asVarying();
task.addJob<DrawOverlay3D>("DrawOverlayInFrontOpaque", overlayInFrontOpaquesInputs, true);
task.addJob<DrawOverlay3D>("DrawOverlayInFrontTransparent", overlayInFrontTransparentsInputs, false);
{ // Debug the bounds of the rendered Overlay items that are marked drawInFront, still look at the zbuffer
task.addJob<DrawBounds>("DrawOverlayInFrontOpaqueBounds", overlaysInFrontOpaque);
task.addJob<DrawBounds>("DrawOverlayInFrontTransparentBounds", overlaysInFrontTransparent);
}
// AA job
const auto antialiasingInputs = Antialiasing::Inputs(deferredFrameTransform, primaryFramebuffer, linearDepthTarget, velocityBuffer).asVarying();
task.addJob<Antialiasing>("Antialiasing", antialiasingInputs);
// Debugging stages
{
// Debugging Deferred buffer job
@ -285,9 +288,10 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
const auto overlaysHUDOpaque = filteredOverlaysOpaque.getN<FilterLayeredItems::Outputs>(1);
const auto overlaysHUDTransparent = filteredOverlaysTransparent.getN<FilterLayeredItems::Outputs>(1);
const auto nullJitter = Varying(glm::vec2(0.0f, 0.0f));
const auto overlayHUDOpaquesInputs = DrawOverlay3D::Inputs(overlaysHUDOpaque, lightingModel).asVarying();
const auto overlayHUDTransparentsInputs = DrawOverlay3D::Inputs(overlaysHUDTransparent, lightingModel).asVarying();
const auto overlayHUDOpaquesInputs = DrawOverlay3D::Inputs(overlaysHUDOpaque, lightingModel, nullJitter).asVarying();
const auto overlayHUDTransparentsInputs = DrawOverlay3D::Inputs(overlaysHUDTransparent, lightingModel, nullJitter).asVarying();
task.addJob<DrawOverlay3D>("DrawOverlayHUDOpaque", overlayHUDOpaquesInputs, true);
task.addJob<DrawOverlay3D>("DrawOverlayHUDTransparent", overlayHUDTransparentsInputs, false);
@ -379,6 +383,7 @@ void DrawStateSortDeferred::run(const RenderContextPointer& renderContext, const
const auto& inItems = inputs.get0();
const auto& lightingModel = inputs.get1();
const auto jitter = inputs.get2();
RenderArgs* args = renderContext->args;
@ -395,6 +400,7 @@ void DrawStateSortDeferred::run(const RenderContextPointer& renderContext, const
args->getViewFrustum().evalViewTransform(viewMat);
batch.setProjectionTransform(projMat);
batch.setProjectionJitter(jitter.x, jitter.y);
batch.setViewTransform(viewMat);
// Setup lighting model for all items;

View file

@ -81,7 +81,7 @@ protected:
class DrawStateSortDeferred {
public:
using Inputs = render::VaryingSet2<render::ItemBounds, LightingModelPointer>;
using Inputs = render::VaryingSet3<render::ItemBounds, LightingModelPointer, glm::vec2>;
using Config = DrawStateSortConfig;
using JobModel = render::Job::ModelI<DrawStateSortDeferred, Inputs, Config>;

View file

@ -35,6 +35,9 @@ void main(void) {
pixels[7] = texelFetch(colorTexture, ivec2(gl_FragCoord.xy)+ivec2(0,1), 0);
pixels[8] = texelFetch(colorTexture, ivec2(gl_FragCoord.xy)+ivec2(1,1), 0);
sharpenedPixel = pixels[4]*7.8 - (pixels[1]+pixels[3]+pixels[5]+pixels[7]) - (pixels[0]+pixels[2]+pixels[6]+pixels[8])*0.7;
outFragColor = mix(pixels[4], sharpenedPixel, sharpenIntensity);
sharpenedPixel = pixels[4]*6.8 - (pixels[1]+pixels[3]+pixels[5]+pixels[7]) - (pixels[0]+pixels[2]+pixels[6]+pixels[8])*0.7;
vec4 minColor = max(vec4(0), pixels[4]-vec4(0.5));
vec4 maxColor = pixels[4]+vec4(0.5);
outFragColor = clamp(pixels[4] + sharpenedPixel * sharpenIntensity, minColor, maxColor);
}

View file

@ -20,14 +20,15 @@ uniform vec4 Color;
in vec3 _normalWS;
in vec2 _texCoord0;
const float gamma = 2.2;
const float smoothing = 32.0;
#define TAA_TEXTURE_LOD_BIAS -3.0
const float interiorCutoff = 0.8;
const float outlineExpansion = 0.2;
const float taaBias = pow(2.0, TAA_TEXTURE_LOD_BIAS);
void main() {
float evalSDF(vec2 texCoord) {
// retrieve signed distance
float sdf = texture(Font, _texCoord0).g;
float sdf = textureLod(Font, texCoord, TAA_TEXTURE_LOD_BIAS).g;
if (Outline) {
if (sdf > interiorCutoff) {
sdf = 1.0 - sdf;
@ -35,12 +36,22 @@ void main() {
sdf += outlineExpansion;
}
}
// perform adaptive anti-aliasing of the edges
// The larger we're rendering, the less anti-aliasing we need
float s = smoothing * length(fwidth(_texCoord0));
float w = clamp(s, 0.0, 0.5);
float a = smoothstep(0.5 - w, 0.5 + w, sdf);
// Rely on TAA for anti-aliasing
return step(0.5, sdf);
}
void main() {
vec2 dxTexCoord = dFdx(_texCoord0) * 0.5 * taaBias;
vec2 dyTexCoord = dFdy(_texCoord0) * 0.5 * taaBias;
// Perform 4x supersampling for anisotropic filtering
float a;
a = evalSDF(_texCoord0);
a += evalSDF(_texCoord0+dxTexCoord);
a += evalSDF(_texCoord0+dyTexCoord);
a += evalSDF(_texCoord0+dxTexCoord+dyTexCoord);
a *= 0.25;
// discard if invisible
if (a < 0.01) {
discard;

View file

@ -76,47 +76,39 @@ vec2 taa_getRegionFXAA() {
#define USE_YCOCG 1
vec4 taa_fetchColor(sampler2D map, vec2 uv) {
vec4 c = texture(map, uv);
// Apply rapid pseudo tonemapping as TAA is applied to a tonemapped image, using luminance as weight, as proposed in
// https://de45xmedrsdbp.cloudfront.net/Resources/files/TemporalAA_small-59732822.pdf
float lum = dot(vec3(0.3,0.5,0.2),c.rgb);
c.rgb = c.rgb / (1.0+lum);
#if USE_YCOCG
vec4 c = texture(map, uv);
return vec4(color_LinearToYCoCg(c.rgb), c.a);
return vec4(color_LinearToYCoCg(c.rgb), c.a);
#else
return texture(map, uv);
return c;
#endif
}
vec3 taa_resolveColor(vec3 color) {
#if USE_YCOCG
return color_YCoCgToLinear(color);
#else
return color;
color = max(vec3(0), color_YCoCgToUnclampedLinear(color));
#endif
// Apply rapid inverse tonemapping, using luminance as weight, as proposed in
// https://de45xmedrsdbp.cloudfront.net/Resources/files/TemporalAA_small-59732822.pdf
float lum = dot(vec3(0.3,0.5,0.2),color.rgb);
color = color / (1.0-lum);
return color;
}
vec4 taa_fetchSourceMap(vec2 uv) {
#if USE_YCOCG
vec4 c = texture(sourceMap, uv);
return vec4(color_LinearToYCoCg(c.rgb), c.a);
#else
return texture(sourceMap, uv);
#endif
return taa_fetchColor(sourceMap, uv);
}
vec4 taa_fetchHistoryMap(vec2 uv) {
#if USE_YCOCG
vec4 c = texture(historyMap, uv);
return vec4(color_LinearToYCoCg(c.rgb), c.a);
#else
return texture(historyMap, uv);
#endif
return taa_fetchColor(historyMap, uv);
}
vec4 taa_fetchNextMap(vec2 uv) {
#if USE_YCOCG
vec4 c = texture(nextMap, uv);
return vec4(color_LinearToYCoCg(c.rgb), c.a);
#else
return texture(nextMap, uv);
#endif
return taa_fetchColor(nextMap, uv);
}
vec2 taa_fetchVelocityMap(vec2 uv) {

View file

@ -28,11 +28,11 @@ void main(void) {
float Zdb = texelFetch(depthMap, ivec2(gl_FragCoord.xy), 0).x;
// The position of the pixel fragment in Eye space then in world space
vec3 eyePos = evalEyePositionFromZdb(stereoSide.x, Zdb, texcoordPos);
vec3 eyePos = evalUnjitteredEyePositionFromZdb(stereoSide.x, Zdb, texcoordPos);
vec3 worldPos = (getViewInverse() * vec4(eyePos, 1.0)).xyz;
vec3 prevEyePos = (getPreviousView() * vec4(worldPos, 1.0)).xyz;
vec4 prevClipPos = (frameTransform._projection[stereoSide.x] * vec4(prevEyePos, 1.0));
vec4 prevClipPos = (getUnjitteredProjection(stereoSide.x) * vec4(prevEyePos, 1.0));
vec2 prevUV = 0.5 * (prevClipPos.xy / prevClipPos.w) + vec2(0.5);
//vec2 imageSize = getWidthHeight(0);

View file

@ -163,7 +163,7 @@ bool BlurInOutResource::updateResources(const gpu::FramebufferPointer& sourceFra
//if (sourceFramebuffer->hasDepthStencil()) {
// _blurredFramebuffer->setDepthStencilBuffer(sourceFramebuffer->getDepthStencilBuffer(), sourceFramebuffer->getDepthStencilBufferFormat());
//}
auto blurringSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT);
auto blurringSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT, gpu::Sampler::WRAP_CLAMP);
auto blurringTarget = gpu::Texture::createRenderBuffer(sourceFramebuffer->getRenderBuffer(0)->getTexelFormat(), blurBufferSize.x, blurBufferSize.y, gpu::Texture::SINGLE_MIP, blurringSampler);
_blurredFramebuffer->setRenderBuffer(0, blurringTarget);
}
@ -186,7 +186,7 @@ bool BlurInOutResource::updateResources(const gpu::FramebufferPointer& sourceFra
/* if (sourceFramebuffer->hasDepthStencil()) {
_outputFramebuffer->setDepthStencilBuffer(sourceFramebuffer->getDepthStencilBuffer(), sourceFramebuffer->getDepthStencilBufferFormat());
}*/
auto blurringSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT);
auto blurringSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT, gpu::Sampler::WRAP_CLAMP);
auto blurringTarget = gpu::Texture::createRenderBuffer(sourceFramebuffer->getRenderBuffer(0)->getTexelFormat(), blurBufferSize.x, blurBufferSize.y, gpu::Texture::SINGLE_MIP, blurringSampler);
_outputFramebuffer->setRenderBuffer(0, blurringTarget);
}

View file

@ -25,6 +25,7 @@
#include <QtNetwork/QNetworkDiskCache>
/**jsdoc
* The Assets API allows you to communicate with the Asset Browser
* @namespace Assets
*/
class AssetScriptingInterface : public BaseAssetScriptingInterface, QScriptable {
@ -53,8 +54,7 @@ public:
/**jsdoc
* Download data from the connected domain's asset server.
* @function Assets.downloadData
* @static
* @param url {string} url of asset to download, must be atp scheme url.
* @param url {string} URL of asset to download, must be ATP scheme URL.
* @param callback {Assets~downloadDataCallback}
*/
@ -69,7 +69,6 @@ public:
/**jsdoc
* Sets up a path to hash mapping within the connected domain's asset server
* @function Assets.setMapping
* @static
* @param path {string}
* @param hash {string}
* @param callback {Assets~setMappingCallback}
@ -80,12 +79,12 @@ public:
* @callback Assets~setMappingCallback
* @param {string} error
*/
Q_INVOKABLE void setMapping(QString path, QString hash, QScriptValue callback);
/**jsdoc
* Look up a path to hash mapping within the connected domain's asset server
* @function Assets.getMapping
* @static
* @param path {string}
* @param callback {Assets~getMappingCallback}
*/
@ -93,11 +92,26 @@ public:
/**jsdoc
* Called when getMapping is complete.
* @callback Assets~getMappingCallback
* @param error {string} error description if the path could not be resolved; otherwise a null value.
* @param assetID {string} hash value if found, else an empty string
* @param error {string} error description if the path could not be resolved; otherwise a null value.
*/
/**jsdoc
* Called when getMapping is complete.
* @callback Assets~getMappingCallback
* @param assetID {string} hash value if found, else an empty string
* @param error {string} error description if the path could not be resolved; otherwise a null value.
*/
Q_INVOKABLE void getMapping(QString path, QScriptValue callback);
/**jsdoc
* @function Assets.setBakingEnabled
* @param path {string}
* @param enabled {boolean}
* @param callback {}
*/
Q_INVOKABLE void setBakingEnabled(QString path, bool enabled, QScriptValue callback);
#if (PR_BUILD || DEV_BUILD)
@ -110,31 +124,35 @@ public:
* @param {URL|Assets.GetOptions} options An atp: style URL, hash, or relative mapped path; or an {@link Assets.GetOptions} object with request parameters
* @param {Assets~getAssetCallback} scope[callback] A scope callback function to receive (error, results) values
*/
/**jsdoc
* A set of properties that can be passed to {@link Assets.getAsset}.
* @typedef {Object} Assets.GetOptions
* @property {URL} [url] an "atp:" style URL, hash, or relative mapped path to fetch
* @property {string} [responseType=text] the desired reponse type (text | arraybuffer | json)
* @property {boolean} [decompress=false] whether to attempt gunzip decompression on the fetched data
* See: {@link Assets.putAsset} and its .compress=true option
*/
/**jsdoc
* A set of properties that can be passed to {@link Assets.getAsset}.
* @typedef {Object} Assets.GetOptions
* @property {URL} [url] an "atp:" style URL, hash, or relative mapped path to fetch
* @property {string} [responseType=text] the desired reponse type (text | arraybuffer | json)
* @property {boolean} [decompress=false] whether to attempt gunzip decompression on the fetched data
* See: {@link Assets.putAsset} and its .compress=true option
*/
/**jsdoc
* Called when Assets.getAsset is complete.
* @callback Assets~getAssetCallback
* @param {string} error - contains error message or null value if no error occured fetching the asset
* @param {Asset~getAssetResult} result - result object containing, on success containing asset metadata and contents
*/
/**jsdoc
* Result value returned by {@link Assets.getAsset}.
* @typedef {Object} Assets~getAssetResult
* @property {url} [url] the resolved "atp:" style URL for the fetched asset
* @property {string} [hash] the resolved hash for the fetched asset
* @property {string|ArrayBuffer|Object} [response] response data (possibly converted per .responseType value)
* @property {string} [responseType] response type (text | arraybuffer | json)
* @property {string} [contentType] detected asset mime-type (autodetected)
* @property {number} [byteLength] response data size in bytes
* @property {number} [decompressed] flag indicating whether data was decompressed
*/
/**jsdoc
* Result value returned by {@link Assets.getAsset}.
* @typedef {Object} Assets~getAssetResult
* @property {url} [url] the resolved "atp:" style URL for the fetched asset
* @property {string} [hash] the resolved hash for the fetched asset
* @property {string|ArrayBuffer|Object} [response] response data (possibly converted per .responseType value)
* @property {string} [responseType] response type (text | arraybuffer | json)
* @property {string} [contentType] detected asset mime-type (autodetected)
* @property {number} [byteLength] response data size in bytes
* @property {number} [decompressed] flag indicating whether data was decompressed
*/
Q_INVOKABLE void getAsset(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue());
/**jsdoc
@ -143,46 +161,131 @@ public:
* @param {Assets.PutOptions} options A PutOptions object with upload parameters
* @param {Assets~putAssetCallback} scope[callback] A scoped callback function invoked with (error, results)
*/
/**jsdoc
* A set of properties that can be passed to {@link Assets.putAsset}.
* @typedef {Object} Assets.PutOptions
* @property {ArrayBuffer|string} [data] byte buffer or string value representing the new asset's content
* @property {string} [path=null] ATP path mapping to automatically create (upon successful upload to hash)
* @property {boolean} [compress=false] whether to gzip compress data before uploading
*/
/**jsdoc
* A set of properties that can be passed to {@link Assets.putAsset}.
* @typedef {Object} Assets.PutOptions
* @property {ArrayBuffer|string} [data] byte buffer or string value representing the new asset's content
* @property {string} [path=null] ATP path mapping to automatically create (upon successful upload to hash)
* @property {boolean} [compress=false] whether to gzip compress data before uploading
*/
/**jsdoc
* Called when Assets.putAsset is complete.
* @callback Assets~puttAssetCallback
* @param {string} error - contains error message (or null value if no error occured while uploading/mapping the new asset)
* @param {Asset~putAssetResult} result - result object containing error or result status of asset upload
*/
/**jsdoc
* Result value returned by {@link Assets.putAsset}.
* @typedef {Object} Assets~putAssetResult
* @property {url} [url] the resolved "atp:" style URL for the uploaded asset (based on .path if specified, otherwise on the resulting ATP hash)
* @property {string} [path] the uploaded asset's resulting ATP path (or undefined if no path mapping was assigned)
* @property {string} [hash] the uploaded asset's resulting ATP hash
* @property {boolean} [compressed] flag indicating whether the data was compressed before upload
* @property {number} [byteLength] flag indicating final byte size of the data uploaded to the ATP server
*/
/**jsdoc
* Result value returned by {@link Assets.putAsset}.
* @typedef {Object} Assets~putAssetResult
* @property {url} [url] the resolved "atp:" style URL for the uploaded asset (based on .path if specified, otherwise on the resulting ATP hash)
* @property {string} [path] the uploaded asset's resulting ATP path (or undefined if no path mapping was assigned)
* @property {string} [hash] the uploaded asset's resulting ATP hash
* @property {boolean} [compressed] flag indicating whether the data was compressed before upload
* @property {number} [byteLength] flag indicating final byte size of the data uploaded to the ATP server
*/
Q_INVOKABLE void putAsset(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue());
/**jsdoc
* @function Assets.deleteAsset
* @property {} options
* @property {} scope
* @property {} [callback = ""]
*/
Q_INVOKABLE void deleteAsset(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue());
/**jsdoc
* @function Assets.resolveAsset
* @property {} options
* @property {} scope
* @property {} [callback = ""]
*/
Q_INVOKABLE void resolveAsset(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue());
/**jsdoc
* @function Assets.decompressData
* @property {} options
* @property {} scope
* @property {} [callback = ""]
*/
Q_INVOKABLE void decompressData(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue());
/**jsdoc
* @function Assets.compressData
* @property {} options
* @property {} scope
* @property {} [callback = ""]
*/
Q_INVOKABLE void compressData(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue());
/**jsdoc
* @function Assets.initializeCache
* @returns {boolean}
*/
Q_INVOKABLE bool initializeCache() { return Parent::initializeCache(); }
/**jsdoc
* @function Assets.canWriteCacheValue
* @property {string} url
* @returns {boolean}
*/
Q_INVOKABLE bool canWriteCacheValue(const QUrl& url);
/**jsdoc
* @function Assets.getCacheStatus
* @property {} scope
* @property {} [callback = ""]
*/
Q_INVOKABLE void getCacheStatus(QScriptValue scope, QScriptValue callback = QScriptValue()) {
jsPromiseReady(Parent::getCacheStatus(), scope, callback);
}
/**jsdoc
* @function Assets.queryCacheMeta
* @property {} options
* @property {} scope
* @property {} [callback = ""]
*/
Q_INVOKABLE void queryCacheMeta(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue());
/**jsdoc
* @function Assets.loadFromCache
* @property {} options
* @property {} scope
* @property {} [callback = ""]
*/
Q_INVOKABLE void loadFromCache(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue());
/**jsdoc
* @function Assets.saveToCache
* @property {} options
* @property {} scope
* @property {} [callback = ""]
*/
Q_INVOKABLE void saveToCache(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue());
/**jsdoc
* @function Assets.saveToCache
* @property {} url
* @property {} data
* @property {} metadata
* @property {} scope
* @property {} [callback = ""]
*/
Q_INVOKABLE void saveToCache(const QUrl& url, const QByteArray& data, const QVariantMap& metadata,
QScriptValue scope, QScriptValue callback = QScriptValue());
protected:

View file

@ -23,6 +23,9 @@
class QScriptEngine;
class QScriptValue;
/**jsdoc
* @namespace Recording
*/
class RecordingScriptingInterface : public QObject, public Dependency {
Q_OBJECT
@ -32,43 +35,196 @@ public:
void setScriptEngine(QSharedPointer<BaseScriptEngine> scriptEngine) { _scriptEngine = scriptEngine; }
public slots:
/**jsdoc
* @function Recording.loadRecording
* @param {string} url
* @param {Recording~loadRecordingCallback} [callback=null]
*/
/**jsdoc
* Called when {@link Recording.loadRecording} is complete.
* @callback Recording~loadRecordingCallback
* @param {boolean} success
* @param {string} url
*/
void loadRecording(const QString& url, QScriptValue callback = QScriptValue());
/**jsdoc
* @function Recording.startPlaying
*/
void startPlaying();
/**jsdoc
* @function Recording.pausePlayer
*/
void pausePlayer();
/**jsdoc
* @function Recording.stopPlaying
*/
void stopPlaying();
/**jsdoc
* @function Recording.isPlaying
* @returns {boolean}
*/
bool isPlaying() const;
/**jsdoc
* @function Recording.isPaused
* @returns {boolean}
*/
bool isPaused() const;
/**jsdoc
* @function Recording.playerElapsed
* @returns {number}
*/
float playerElapsed() const;
/**jsdoc
* @function Recording.playerLength
* @returns {number}
*/
float playerLength() const;
/**jsdoc
* @function Recording.setPlayerVolume
* @param {number} volume
*/
void setPlayerVolume(float volume);
/**jsdoc
* @function Recording.setPlayerAudioOffset
* @param {number} audioOffset
*/
void setPlayerAudioOffset(float audioOffset);
/**jsdoc
* @function Recording.setPlayerTime
* @param {number} time
*/
void setPlayerTime(float time);
/**jsdoc
* @function Recording.setPlayerLoop
* @param {boolean} loop
*/
void setPlayerLoop(bool loop);
/**jsdoc
* @function Recording.setPlayerUseDisplayName
* @param {boolean} useDisplayName
*/
void setPlayerUseDisplayName(bool useDisplayName);
/**jsdoc
* @function Recording.setPlayerUseAttachments
* @param {boolean} useAttachments
*/
void setPlayerUseAttachments(bool useAttachments);
/**jsdoc
* @function Recording.setPlayerUseHeadModel
* @param {boolean} useHeadModel
* @todo <strong>Note:</strong> This function currently has no effect.
*/
void setPlayerUseHeadModel(bool useHeadModel);
/**jsdoc
* @function Recording.setPlayerUseSkeletonModel
* @param {boolean} useSkeletonModel
* @todo <strong>Note:</strong> This function currently doesn't work.
*/
void setPlayerUseSkeletonModel(bool useSkeletonModel);
/**jsdoc
* @function Recording.setPlayFromCurrentLocation
* @param {boolean} playFromCurrentLocation
*/
void setPlayFromCurrentLocation(bool playFromCurrentLocation);
/**jsdoc
* @function Recording.getPlayerUseDisplayName
* @returns {boolean}
*/
bool getPlayerUseDisplayName() { return _useDisplayName; }
/**jsdoc
* @function Recording.getPlayerUseAttachments
* @returns {boolean}
*/
bool getPlayerUseAttachments() { return _useAttachments; }
/**jsdoc
* @function Recording.getPlayerUseHeadModel
* @returns {boolean}
*/
bool getPlayerUseHeadModel() { return _useHeadModel; }
/**jsdoc
* @function Recording.getPlayerUseSkeletonModel
* @returns {boolean}
*/
bool getPlayerUseSkeletonModel() { return _useSkeletonModel; }
/**jsdoc
* @function Recording.getPlayFromCurrentLocation
* @returns {boolean}
*/
bool getPlayFromCurrentLocation() { return _playFromCurrentLocation; }
/**jsdoc
* @function Recording.startRecording
*/
void startRecording();
/**jsdoc
* @function Recording.stopRecording
*/
void stopRecording();
/**jsdoc
* @function Recording.isRecording
* @returns {boolean}
*/
bool isRecording() const;
/**jsdoc
* @function Recording.recorderElapsed
* @returns {number}
*/
float recorderElapsed() const;
/**jsdoc
* @function Recording.getDefaultRecordingSaveDirectory
* @returns {string}
*/
QString getDefaultRecordingSaveDirectory();
/**jsdoc
* @function Recording.saveRecording
* @param {string} filename
*/
void saveRecording(const QString& filename);
/**jsdoc
* @function Recording.saveRecordingToAsset
* @param {function} getClipAtpUrl
*/
bool saveRecordingToAsset(QScriptValue getClipAtpUrl);
/**jsdoc
* @function Recording.loadLastRecording
*/
void loadLastRecording();
protected:

View file

@ -19,6 +19,13 @@
// TODO: if QT moc ever supports nested classes, subclass these to the interface instead of namespacing
namespace SceneScripting {
/**jsdoc
* @typedef Scene.Stage.Location
* @property {number} longitude
* @property {number} latitude
* @property {number} altitude
*/
class Location : public QObject {
Q_OBJECT
@ -41,6 +48,11 @@ namespace SceneScripting {
};
using LocationPointer = std::unique_ptr<Location>;
/**jsdoc
* @typedef Scene.Stage.Time
* @property {number} hour
* @property {number} day
*/
class Time : public QObject {
Q_OBJECT
@ -60,6 +72,13 @@ namespace SceneScripting {
};
using TimePointer = std::unique_ptr<Time>;
/**jsdoc
* @typedef Scene.Stage.KeyLight
* @property {Vec3} color
* @property {number} intensity
* @property {number} ambientIntensity
* @property {Vec3} direction
*/
class KeyLight : public QObject {
Q_OBJECT
@ -91,6 +110,14 @@ namespace SceneScripting {
};
using KeyLightPointer = std::unique_ptr<KeyLight>;
/**jsdoc
* @class Scene.Stage
* @property {string} backgroundMode
* @property {Scene.Stage.KeyLight} keyLight
* @property {Scene.Stage.Location} location
* @property {boolean} sunModel
* @property {Scene.Stage.Time} time
*/
class Stage : public QObject {
Q_OBJECT
@ -99,10 +126,22 @@ namespace SceneScripting {
: _skyStage{ skyStage },
_location{ new Location{ skyStage } }, _time{ new Time{ skyStage } }, _keyLight{ new KeyLight{ skyStage } }{}
/**jsdoc
* @function Scene.Stage.setOrientation
* @param {Quat} orientation
*/
Q_INVOKABLE void setOrientation(const glm::quat& orientation) const;
Q_PROPERTY(Location* location READ getLocation)
Location* getLocation() const { return _location.get(); }
/**jsdoc
* @function Scene.Stage.setLocation
* @param {number} longitude
* @param {number} latitude
* @param {number} altitude
*/
Q_INVOKABLE void setLocation(float longitude, float latitude, float altitude);
Q_PROPERTY(Time* time READ getTime)
@ -130,10 +169,15 @@ namespace SceneScripting {
using StagePointer = std::unique_ptr<Stage>;
};
/**jsdoc
* @namespace Scene
* @property {boolean} shouldRenderAvatars
* @property {boolean} shouldRenderEntities
* @property {Scene.Stage} stage
*/
class SceneScriptingInterface : public QObject, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
public:
Q_PROPERTY(bool shouldRenderAvatars READ shouldRenderAvatars WRITE setShouldRenderAvatars)
@ -149,7 +193,19 @@ public:
graphics::SunSkyStagePointer getSkyStage() const;
signals:
/**jsdoc
* @function Scene.shouldRenderAvatarsChanged
* @param {boolean} shouldRenderAvatars
* @returns {Signal}
*/
void shouldRenderAvatarsChanged(bool shouldRenderAvatars);
/**jsdoc
* @function Scene.shouldRenderEntitiesChanged
* @param {boolean} shouldRenderEntities
* @returns {Signal}
*/
void shouldRenderEntitiesChanged(bool shouldRenderEntities);
protected:

View file

@ -87,6 +87,10 @@ public:
QUrl definingSandboxURL { QUrl("about:EntityScript") };
};
/**jsdoc
* @namespace Script
* @property {string} context
*/
class ScriptEngine : public BaseScriptEngine, public EntitiesScriptEngineProvider {
Q_OBJECT
Q_PROPERTY(QString context READ getContext)
@ -115,6 +119,11 @@ public:
QString getFilename() const;
/**jsdoc
* Stop the current script.
* @function Script.stop
* @param {boolean} [marshal=false]
*/
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// NOTE - this is intended to be a public interface for Agent scripts, and local scripts, but not for EntityScripts
Q_INVOKABLE void stop(bool marshal = false);
@ -126,26 +135,69 @@ public:
// NOTE - these are NOT intended to be public interfaces available to scripts, the are only Q_INVOKABLE so we can
// properly ensure they are only called on the correct thread
/**jsdoc
* @function Script.registerGlobalObject
* @param {string} name
* @param {object} object
*/
/// 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=""]
*/
/// registers a global getter/setter
Q_INVOKABLE void registerGetterSetter(const QString& name, QScriptEngine::FunctionSignature getter,
QScriptEngine::FunctionSignature setter, const QString& parent = QString(""));
/**jsdoc
* @function Script.registerFunction
* @param {string} name
* @param {object} function
* @param {number} [numArguments=-1]
*/
/// 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]
*/
/// 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,
int numArguments = -1);
/**jsdoc
* @function Script.registerValue
* @param {string} name
* @param {object} value
*/
/// 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}
*/
/// 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}
*/
Q_INVOKABLE QScriptValue evaluateInClosure(const QScriptValue& locals, const QScriptProgram& program);
/// if the script engine is not already running, this will download the URL and start the process of seting it up
@ -154,24 +206,122 @@ public:
void loadURL(const QUrl& scriptURL, bool reload);
bool hasValidScriptSuffix(const QString& scriptFileName);
/**jsdoc
* @function Script.getContext
* @returns {string}
*/
Q_INVOKABLE QString getContext() const;
/**jsdoc
* @function Script.isClientScript
* @returns {boolean}
*/
Q_INVOKABLE bool isClientScript() const { return _context == CLIENT_SCRIPT; }
/**jsdoc
* @function Script.isEntityClientScript
* @returns {boolean}
*/
Q_INVOKABLE bool isEntityClientScript() const { return _context == ENTITY_CLIENT_SCRIPT; }
/**jsdoc
* @function Script.isEntityServerScript
* @returns {boolean}
*/
Q_INVOKABLE bool isEntityServerScript() const { return _context == ENTITY_SERVER_SCRIPT; }
/**jsdoc
* @function Script.isAgentScript
* @returns {boolean}
*/
Q_INVOKABLE bool isAgentScript() const { return _context == AGENT_SCRIPT; }
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// NOTE - these are intended to be public interfaces available to scripts
/**jsdoc
* @function Script.addEventHandler
* @param {Uuid} entityID
* @param {string} eventName
* @param {function} handler
*/
Q_INVOKABLE void addEventHandler(const EntityItemID& entityID, const QString& eventName, QScriptValue handler);
/**jsdoc
* @function Script.removeEventHandler
* @param {Uuid} entityID
* @param {string} eventName
* @param {function} handler
*/
Q_INVOKABLE void removeEventHandler(const EntityItemID& entityID, const QString& eventName, QScriptValue handler);
/**jsdoc
* Start a new Interface or entity script.
* @function Script.load
* @param {string} filename - The URL of the script to load. Can be relative to the current script.
* @example <caption>Load a script from another script.</caption>
* // First file: scriptA.js
* print("This is script A");
*
* // Second file: scriptB.js
* print("This is script B");
* Script.load("scriptA.js");
*
* // If you run scriptB.js you should see both scripts in the running scripts list.
* // And you should see the following output:
* // This is script B
* // This is script A
*/
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).
* @function Script.include
* @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.
*/
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
* 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.
* @example <caption>Include a script file asynchronously.</caption>
* // First file: scriptA.js
* print("This is script A");
*
* // Second file: scriptB.js
* print("This is script B");
* Script.include("scriptA.js", function () {
* print("Script A has been included");
* });
*
* // If you run scriptB.js you should see only scriptB.js in the running scripts list.
* // And you should see the following output:
* // This is script B
* // This is script A
* // Script A has been included
*/
Q_INVOKABLE void include(const QString& includeFile, QScriptValue callback = QScriptValue());
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// MODULE related methods
/**jsdoc
* @function Script.require
* @param {string} module
*/
Q_INVOKABLE QScriptValue require(const QString& moduleId);
/**jsdoc
* @function Script.resetModuleCache
* @param {boolean} [deleteScriptCache=false]
*/
Q_INVOKABLE void resetModuleCache(bool deleteScriptCache = false);
QScriptValue currentModule();
bool registerModuleWithParent(const QScriptValue& module, const QScriptValue& parent);
@ -179,34 +329,168 @@ public:
QVariantMap fetchModuleSource(const QString& modulePath, const bool forceDownload = false);
QScriptValue instantiateModule(const QScriptValue& module, const QString& sourceCode);
/**jsdoc
* Call a function 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 {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}.
* @example <caption>Print a message every second.</caption>
* Script.setInterval(function () {
* print("Timer fired");
* }, 1000);
*/
Q_INVOKABLE QObject* setInterval(const QScriptValue& function, int intervalMS);
/**jsdoc
* Call a function 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 {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>
* Script.setTimeout(function () {
* print("Timer fired");
* }, 1000);
*/
Q_INVOKABLE QObject* setTimeout(const QScriptValue& function, int timeoutMS);
/**jsdoc
* Stop an interval timer set by {@link Script.setInterval|setInterval}.
* @function Script.clearInterval
* @param {object} timer - The interval timer to clear.
* @example <caption>Stop an interval timer.</caption>
* // Print a message every second.
* var timer = Script.setInterval(function () {
* print("Timer fired");
* }, 1000);
*
* // Stop the timer after 10 seconds.
* Script.setTimeout(function () {
* print("Stop 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}.
* @function Script.clearTimeout
* @param {object} timer - The timeout timer to clear.
* @example <caption>Stop a timeout timer.</caption>
* // Print a message after two seconds.
* var timer = Script.setTimeout(function () {
* print("Timer fired");
* }, 2000);
*
* // Uncomment the following line to stop the timer from firing.
* //Script.clearTimeout(timer);
*/
Q_INVOKABLE void clearTimeout(QObject* timer) { stopTimer(reinterpret_cast<QTimer*>(timer)); }
/**jsdoc
* @function Script.print
* @param {string} message
*/
Q_INVOKABLE void print(const QString& message);
/**jsdoc
* Resolve a relative path to an absolute path.
* @function Script.resolvePath
* @param {string} path - The relative path to resolve.
* @returns {string} The absolute path.
*/
Q_INVOKABLE QUrl resolvePath(const QString& path) const;
/**jsdoc
* @function Script.resourcesPath
* @returns {string}
*/
Q_INVOKABLE QUrl resourcesPath() const;
/**jsdoc
* @function Script.beginProfileRange
* @param {string} label
*/
Q_INVOKABLE void beginProfileRange(const QString& label) const;
/**jsdoc
* @function Script.endProfileRange
* @param {string} label
*/
Q_INVOKABLE void endProfileRange(const QString& label) const;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Entity Script Related methods
/**jsdoc
* @function Script.isEntityScriptRunning
* @param {Uuid} entityID
* @returns {boolean}
*/
Q_INVOKABLE bool isEntityScriptRunning(const EntityItemID& entityID) {
return _entityScripts.contains(entityID) && _entityScripts[entityID].status == EntityScriptStatus::RUNNING;
}
QVariant cloneEntityScriptDetails(const EntityItemID& entityID);
QFuture<QVariant> getLocalEntityScriptDetails(const EntityItemID& entityID) override;
/**jsdoc
* @function Script.loadEntityScript
* @param {Uuid} entityID
* @param {string} script
* @param {boolean} forceRedownload
*/
Q_INVOKABLE void loadEntityScript(const EntityItemID& entityID, const QString& entityScript, bool forceRedownload);
/**jsdoc
* @function Script.unloadEntityScript
* @param {Uuid} entityID
* @param {boolean} [shouldRemoveFromMap=false]
*/
Q_INVOKABLE void unloadEntityScript(const EntityItemID& entityID, bool shouldRemoveFromMap = false); // will call unload method
/**jsdoc
* @function Script.unloadAllEntityScripts
*/
Q_INVOKABLE void unloadAllEntityScripts();
/**jsdoc
* @function Script.callEntityScriptMethod
* @param {Uuid} entityID
* @param {string} methodName
* @param {string[]} parameters
* @param {Uuid} [remoteCallerID=Uuid.NULL]
*/
Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName,
const QStringList& params = QStringList(),
const QUuid& remoteCallerID = QUuid()) override;
/**jsdoc
* @function Script.callEntityScriptMethod
* @param {Uuid} entityID
* @param {string} methodName
* @param {PointerEvent} event
*/
Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const PointerEvent& event);
/**jsdoc
* @function Script.callEntityScriptMethod
* @param {Uuid} entityID
* @param {string} methodName
* @param {Uuid} otherID
* @param {Collision} collision
*/
Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const EntityItemID& otherID, const Collision& collision);
/**jsdoc
* @function Script.requestGarbageCollection
*/
Q_INVOKABLE void requestGarbageCollection() { collectGarbage(); }
/**jsdoc
* @function Script.generateUUID
* @returns {Uuid}
*/
Q_INVOKABLE QUuid generateUUID() { return QUuid::createUuid(); }
bool isFinished() const { return _isFinished; } // used by Application and ScriptWidget
@ -239,33 +523,170 @@ public:
bool getEntityScriptDetails(const EntityItemID& entityID, EntityScriptDetails &details) const;
public slots:
/**jsdoc
* @function Script.callAnimationStateHandler
* @param {function} callback
* @param {object} parameters
* @param {string[]} names
* @param {boolean} useNames
* @param {object} resultHandler
*/
void callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters, QStringList names, bool useNames, AnimVariantResultHandler resultHandler);
/**jsdoc
* @function Script.updateMemoryCost
* @param {number} deltaSize
*/
void updateMemoryCost(const qint64&);
signals:
/**jsdoc
* @function Script.scriptLoaded
* @param {string} filename
* @returns {Signal}
*/
void scriptLoaded(const QString& scriptFilename);
/**jsdoc
* @function Script.errorLoadingScript
* @param {string} filename
* @returns {Signal}
*/
void errorLoadingScript(const QString& scriptFilename);
/**jsdoc
* Triggered regularly at a system-determined frequency.
* @function Script.update
* @param {number} deltaTime - The time since the last update, in s.
* @returns {Signal}
*/
void update(float deltaTime);
/**jsdoc
* Triggered when the script is ending.
* @function Script.scriptEnding
* @returns {Signal}
* @example <caption>Connect to the <code>scriptEnding</code> signal.</caption>
* print("Script started");
*
* Script.scriptEnding.connect(function () {
* print("Script ending");
* });
*
* Script.setTimeout(function () {
* print("Stopping script");
* Script.stop();
* }, 1000);
*/
void scriptEnding();
/**jsdoc
* @function Script.finished
* @param {string} filename
* @param {object} engine
* @returns {Signal}
*/
void finished(const QString& fileNameString, ScriptEnginePointer);
/**jsdoc
* @function Script.cleanupMenuItem
* @param {string} menuItem
* @returns {Signal}
*/
void cleanupMenuItem(const QString& menuItemString);
/**jsdoc
* @function Script.printedMessage
* @param {string} message
* @param {string} scriptName
* @returns {Signal}
*/
void printedMessage(const QString& message, const QString& scriptName);
/**jsdoc
* @function Script.errorMessage
* @param {string} message
* @param {string} scriptName
* @returns {Signal}
*/
void errorMessage(const QString& message, const QString& scriptName);
/**jsdoc
* @function Script.warningMessage
* @param {string} message
* @param {string} scriptName
* @returns {Signal}
*/
void warningMessage(const QString& message, const QString& scriptName);
/**jsdoc
* @function Script.infoMessage
* @param {string} message
* @param {string} scriptName
* @returns {Signal}
*/
void infoMessage(const QString& message, const QString& scriptName);
/**jsdoc
* @function Script.runningStateChanged
* @returns {Signal}
*/
void runningStateChanged();
/**jsdoc
* @function Script.clearDebugWindow
* @returns {Signal}
*/
void clearDebugWindow();
/**jsdoc
* @function Script.loadScript
* @param {string} scriptName
* @param {boolean} isUserLoaded
* @returns {Signal}
*/
void loadScript(const QString& scriptName, bool isUserLoaded);
/**jsdoc
* @function Script.reloadScript
* @param {string} scriptName
* @param {boolean} isUserLoaded
* @returns {Signal}
*/
void reloadScript(const QString& scriptName, bool isUserLoaded);
/**jsdoc
* @function Script.doneRunning
* @returns {Signal}
*/
void doneRunning();
/**jsdoc
* @function Script.entityScriptDetailsUpdated
* @returns {Signal}
*/
// 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();
protected:
void init();
/**jsdoc
* @function Script.executeOnScriptThread
* @param {object} function
* @param {ConnectionType} [type=2]
*/
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}
*/
// 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;"
Q_INVOKABLE QString _requireResolve(const QString& moduleId, const QString& relativeTo = QString());
@ -285,6 +706,16 @@ protected:
QHash<EntityItemID, RegisteredEventHandlers> _registeredHandlers;
void forwardHandlerCall(const EntityItemID& entityID, const QString& eventName, QScriptValueList eventHanderArgs);
/**jsdoc
* @function Script.entityScriptContentAvailable
* @param {Uuid} entityID
* @param {string} scriptOrURL
* @param {string} contents
* @param {boolean} isURL
* @param {boolean} success
* @param {string} status
*/
Q_INVOKABLE void entityScriptContentAvailable(const EntityItemID& entityID, const QString& scriptOrURL, const QString& contents, bool isURL, bool success, const QString& status);
EntityItemID currentEntityIdentifier {}; // Contains the defining entity script entity id during execution, if any. Empty for interface script execution.

View file

@ -27,6 +27,14 @@
class ScriptEngine;
/**jsdoc
* @namespace ScriptDiscoveryService
* @property {string} debugScriptUrl
* @property {string} defaultScriptsPath
* @property {ScriptsModel} scriptsModel
* @property {ScriptsModelFilter} scriptsModelFilter
*/
class NativeScriptInitializers : public ScriptInitializerMixin {
public:
bool registerNativeScriptInitializer(NativeScriptInitializer initializer) override;
@ -63,18 +71,64 @@ public:
QString getDefaultScriptsLocation() const;
/**jsdoc
* @function ScriptDiscoveryService.loadOneScript
* @param {string} filename
*/
Q_INVOKABLE void loadOneScript(const QString& scriptFilename);
/**jsdoc
* @function ScriptDiscoveryService.loadScript
* @param {string} [filename=""]
* @param {boolean} [isUserLoaded=true]
* @param {boolean} [loadScriptFromEditor=false]
* @param {boolean} [activateMainWindow=false]
* @param {boolean} [reload=false]
* @returns {boolean}
*/
Q_INVOKABLE ScriptEnginePointer loadScript(const QUrl& scriptFilename = QString(),
bool isUserLoaded = true, bool loadScriptFromEditor = false, bool activateMainWindow = false, bool reload = false);
/**jsdoc
* @function ScriptDiscoveryService.stopScript
* @param {string} scriptHash
* @param {boolean} [restart=false]
* @returns {boolean}
*/
Q_INVOKABLE bool stopScript(const QString& scriptHash, bool restart = false);
/**jsdoc
* @function ScriptDiscoveryService.reloadAllScripts
*/
Q_INVOKABLE void reloadAllScripts();
/**jsdoc
* @function ScriptDiscoveryService.stopAllScripts
* @param {boolean} [restart=false]
*/
Q_INVOKABLE void stopAllScripts(bool restart = false);
/**jsdoc
* @function ScriptDiscoveryService.getRunning
* @returns {object[]}
*/
Q_INVOKABLE QVariantList getRunning();
/**jsdoc
* @function ScriptDiscoveryService.getPublic
* @returns {object[]}
*/
Q_INVOKABLE QVariantList getPublic();
/**jsdoc
* @function ScriptDiscoveryService.getLocal
* @returns {object[]}
*/
Q_INVOKABLE QVariantList getLocal();
// FIXME: Move to other Q_PROPERTY declarations.
Q_PROPERTY(QString defaultScriptsPath READ getDefaultScriptsLocation)
void defaultScriptsLocationOverridden(bool overridden) { _defaultScriptsLocationOverridden = overridden; };
@ -86,25 +140,120 @@ public:
void addScriptEngine(ScriptEnginePointer);
signals:
/**jsdoc
* @function ScriptDiscoveryService.scriptCountChanged
* @returns {Signal}
*/
void scriptCountChanged();
/**jsdoc
* @function ScriptDiscoveryService.scriptsReloading
* @returns {Signal}
*/
void scriptsReloading();
/**jsdoc
* @function ScriptDiscoveryService.scriptLoadError
* @param {string} filename
* @param {string} error
* @returns {Signal}
*/
void scriptLoadError(const QString& filename, const QString& error);
/**jsdoc
* @function ScriptDiscoveryService.printedMessage
* @param {string} message
* @param {string} engineName
* @returns {Signal}
*/
void printedMessage(const QString& message, const QString& engineName);
/**jsdoc
* @function ScriptDiscoveryService.errorMessage
* @param {string} message
* @param {string} engineName
* @returns {Signal}
*/
void errorMessage(const QString& message, const QString& engineName);
/**jsdoc
* @function ScriptDiscoveryService.warningMessage
* @param {string} message
* @param {string} engineName
* @returns {Signal}
*/
void warningMessage(const QString& message, const QString& engineName);
/**jsdoc
* @function ScriptDiscoveryService.infoMessage
* @param {string} message
* @param {string} engineName
* @returns {Signal}
*/
void infoMessage(const QString& message, const QString& engineName);
/**jsdoc
* @function ScriptDiscoveryService.errorLoadingScript
* @param {string} url
* @returns {Signal}
*/
void errorLoadingScript(const QString& url);
/**jsdoc
* @function ScriptDiscoveryService.clearDebugWindow
* @returns {Signal}
*/
void clearDebugWindow();
public slots:
/**jsdoc
* @function ScriptDiscoveryService.onPrintedMessage
* @param {string} message
* @param {string} scriptName
*/
void onPrintedMessage(const QString& message, const QString& scriptName);
/**jsdoc
* @function ScriptDiscoveryService.onErrorMessage
* @param {string} message
* @param {string} scriptName
*/
void onErrorMessage(const QString& message, const QString& scriptName);
/**jsdoc
* @function ScriptDiscoveryService.onWarningMessage
* @param {string} message
* @param {string} scriptName
*/
void onWarningMessage(const QString& message, const QString& scriptName);
/**jsdoc
* @function ScriptDiscoveryService.onInfoMessage
* @param {string} message
* @param {string} scriptName
*/
void onInfoMessage(const QString& message, const QString& scriptName);
/**jsdoc
* @function ScriptDiscoveryService.onErrorLoadingScript
* @param {string} url
*/
void onErrorLoadingScript(const QString& url);
/**jsdoc
* @function ScriptDiscoveryService.onClearDebugWindow
*/
void onClearDebugWindow();
protected slots:
/**jsdoc
* @function ScriptDiscoveryService.onScriptFinished
* @param {string} filename
* @param {object} engine
*/
void onScriptFinished(const QString& fileNameString, ScriptEnginePointer engine);
protected:

View file

@ -63,17 +63,68 @@ public:
TreeNodeFolder(const QString& foldername, TreeNodeFolder* parent);
};
/**jsdoc
* <p>Provided as a property of {@link ScriptDiscoveryService}.</p>
* <p>Has properties and functions below in addition to those of <a href="http://doc.qt.io/qt-5/qabstractitemmodel.html">
* http://doc.qt.io/qt-5/qabstractitemmodel.html</a>.</p>
* @class ScriptsModel
*/
class ScriptsModel : public QAbstractItemModel {
Q_OBJECT
public:
ScriptsModel(QObject* parent = NULL);
~ScriptsModel();
/**jsdoc
* @function ScriptsModel.index
* @param {number} row
* @param {number} column
* @param {QModelIndex} parent
* @returns {QModelIndex}
*/
QModelIndex index(int row, int column, const QModelIndex& parent) const override;
/**jsdoc
* @function ScriptsModel.parent
* @param {QModelIndex} child
* @returns {QModelIndex}
*/
QModelIndex parent(const QModelIndex& child) const override;
/**jsdoc
* @function ScriptsModel.data
* @param {QModelIndex} index
* @param {number} [role=0]
* returns {string}
*/
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
/**jsdoc
* @function ScriptsModel.rowCount
* @param {QmodelIndex} [parent=null]
* @returns {number}
*/
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
/**jsdoc
* @function ScriptsModel.columnCount
* @param {QmodelIndex} [parent=null]
* @returns {number}
*/
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
/**jsdoc
* @function ScriptsModel.getTreeNodeFromIndex
* @param {QmodelIndex} index
* @returns {TreeNodeBase}
*/
TreeNodeBase* getTreeNodeFromIndex(const QModelIndex& index) const;
/**jsdoc
* @function ScriptsModel.getFolderNodes
* @param {TreeNodeFolder} parent
* @returns {TreeNodeBase[]}
*/
QList<TreeNodeBase*> getFolderNodes(TreeNodeFolder* parent) const;
enum Role {

View file

@ -15,6 +15,12 @@
#include "ScriptsModel.h"
#include <QSortFilterProxyModel>
/**jsdoc
* <p>Provided as a property of {@link ScriptDiscoveryService}.</p>
* <p>Has properties and functions per <a href="http://doc.qt.io/qt-5/qsortfilterproxymodel.html">
* http://doc.qt.io/qt-5/qsortfilterproxymodel.html</a>.</p>
* @class ScriptsModelFilter
*/
class ScriptsModelFilter : public QSortFilterProxyModel {
Q_OBJECT
public:

View file

@ -17,8 +17,12 @@
#include <DependencyManager.h>
/**jsdoc
* @namespace Users
*/
* @namespace Users
* @property {boolean} canKick - <code>true</code> if the domain server allows the node or avatar to kick (ban) avatars,
* otherwise <code>false</code>. <em>Read-only.</em>
* @property {boolean} requestsDomainListData - <code>true</code> if the avatar requests extra data from the mixers (such as
* positional data of an avatar you've ignored). <em>Read-only.</em>
*/
class UsersScriptingInterface : public QObject, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
@ -32,126 +36,151 @@ public:
public slots:
/**jsdoc
* Ignore another user.
* @function Users.ignore
* @param {nodeID} nodeID The node or session ID of the user you want to ignore.
* @param {bool} enable True for ignored; false for un-ignored.
*/
* Personally ignore another user, making them disappear for you and you disappear for them.
* @function Users.ignore
* @param {Uuid} nodeID The node or session ID of the user you want to ignore.
* @param {boolean} enable True for ignored; false for un-ignored.
*/
void ignore(const QUuid& nodeID, bool ignoreEnabled = true);
/**jsdoc
* Gets a bool containing whether you have ignored the given Avatar UUID.
* @function Users.getIgnoreStatus
* @param {nodeID} nodeID The node or session ID of the user whose ignore status you want.
*/
* Get whether or not you have ignored the node with the given UUID.
* @function Users.getIgnoreStatus
* @param {Uuid} nodeID The node or session ID of the user whose ignore status you want.
* @returns {boolean}
*/
bool getIgnoreStatus(const QUuid& nodeID);
/**jsdoc
* Mute another user for you and you only.
* @function Users.personalMute
* @param {nodeID} nodeID The node or session ID of the user you want to mute.
* @param {bool} enable True for enabled; false for disabled.
*/
* Mute another user for you and you only. They won't be able to hear you, and you won't be able to hear them.
* @function Users.personalMute
* @param {Uuid} nodeID The node or session ID of the user you want to mute.
* @param {boolean} muteEnabled True for enabled; false for disabled.
*/
void personalMute(const QUuid& nodeID, bool muteEnabled = true);
/**jsdoc
* Requests a bool containing whether you have personally muted the given Avatar UUID.
* @function Users.requestPersonalMuteStatus
* @param {nodeID} nodeID The node or session ID of the user whose personal mute status you want.
*/
* Get whether or not you have personally muted the node with the given UUID.
* @function Users.requestPersonalMuteStatus
* @param {Uuid} nodeID The node or session ID of the user whose personal mute status you want.
* @returns {boolean}
*/
bool getPersonalMuteStatus(const QUuid& nodeID);
/**jsdoc
* Sets an avatar's gain for you and you only.
* Units are Decibels (dB)
* @function Users.setAvatarGain
* @param {nodeID} nodeID The node or session ID of the user whose gain you want to modify, or null to set the master gain.
* @param {float} gain The gain of the avatar you'd like to set. Units are dB.
* Sets an avatar's gain for you and you only.
* Units are Decibels (dB)
* @function Users.setAvatarGain
* @param {Uuid} nodeID The node or session ID of the user whose gain you want to modify, or null to set the master gain.
* @param {number} gain The gain of the avatar you'd like to set. Units are dB.
*/
void setAvatarGain(const QUuid& nodeID, float gain);
/**jsdoc
* Gets an avatar's gain for you and you only.
* @function Users.getAvatarGain
* @param {nodeID} nodeID The node or session ID of the user whose gain you want to get, or null to get the master gain.
* @return {float} gain (in dB)
* Gets an avatar's gain for you and you only.
* @function Users.getAvatarGain
* @param {Uuid} nodeID The node or session ID of the user whose gain you want to get, or null to get the master gain.
* @returns {number} gain (in dB)
*/
float getAvatarGain(const QUuid& nodeID);
/**jsdoc
* Kick another user.
* @function Users.kick
* @param {nodeID} nodeID The node or session ID of the user you want to kick.
*/
* Kick/ban another user. Removes them from the server and prevents them from returning. Bans by either user name (if
* available) or machine fingerprint otherwise. This will only do anything if you're an admin of the domain you're in.
* @function Users.kick
* @param {Uuid} nodeID The node or session ID of the user you want to kick.
*/
void kick(const QUuid& nodeID);
/**jsdoc
* Mute another user for everyone.
* @function Users.mute
* @param {nodeID} nodeID The node or session ID of the user you want to mute.
*/
* Mutes another user's microphone for everyone. Not permanent; the silenced user can unmute themselves with the UNMUTE
* button in their HUD. This will only do anything if you're an admin of the domain you're in.
* @function Users.mute
* @param {Uuid} nodeID The node or session ID of the user you want to mute.
*/
void mute(const QUuid& nodeID);
/**jsdoc
* Returns a string containing the username associated with the given Avatar UUID
* Get the user name and machine fingerprint associated with the given UUID. This will only do anything if you're an admin
* of the domain you're in.
* @function Users.getUsernameFromID
* @param {nodeID} nodeID The node or session ID of the user whose username you want.
* @param {Uuid} nodeID The node or session ID of the user whose username you want.
*/
void requestUsernameFromID(const QUuid& nodeID);
/**jsdoc
* Returns `true` if the DomainServer will allow this Node/Avatar to make kick
* @function Users.getCanKick
* @return {bool} `true` if the client can kick other users, `false` if not.
*/
* Returns `true` if the DomainServer will allow this Node/Avatar to make kick.
* @function Users.getCanKick
* @returns {boolean} <code>true</code> if the domain server allows the client to kick (ban) other users, otherwise
* <code>false</code>.
*/
bool getCanKick();
/**jsdoc
* Toggle the state of the ignore in radius feature
* @function Users.toggleIgnoreRadius
*/
* Toggle the state of the space bubble feature.
* @function Users.toggleIgnoreRadius
*/
void toggleIgnoreRadius();
/**jsdoc
* Enables the ignore radius feature.
* @function Users.enableIgnoreRadius
*/
* Enables the space bubble feature.
* @function Users.enableIgnoreRadius
*/
void enableIgnoreRadius();
/**jsdoc
* Disables the ignore radius feature.
* @function Users.disableIgnoreRadius
*/
* Disables the space bubble feature.
* @function Users.disableIgnoreRadius
*/
void disableIgnoreRadius();
/**jsdoc
* Returns `true` if the ignore in radius feature is enabled
* @function Users.getIgnoreRadiusEnabled
* @return {bool} `true` if the ignore in radius feature is enabled, `false` if not.
*/
* Returns `true` if the space bubble feature is enabled.
* @function Users.getIgnoreRadiusEnabled
* @returns {boolean} <code>true</code> if the space bubble is enabled, otherwise <code>false</code>.
*/
bool getIgnoreRadiusEnabled();
signals:
/**jsdoc
* @function Users.canKickChanged
* @param {boolean} canKick
* @returns {Signal}
*/
void canKickChanged(bool canKick);
/**jsdoc
* @function Users.ignoreRadiusEnabledChanged
* @param {boolean} isEnabled
* @returns {Signal}
*/
void ignoreRadiusEnabledChanged(bool isEnabled);
/**jsdoc
* Notifies scripts that another user has entered the ignore radius
* @function Users.enteredIgnoreRadius
*/
* Notifies scripts that another user has entered the ignore radius.
* @function Users.enteredIgnoreRadius
* @returns {Signal}
*/
void enteredIgnoreRadius();
/**jsdoc
* Notifies scripts of the username and machine fingerprint associated with a UUID.
* Username and machineFingerprint will be their default constructor output if the requesting user isn't an admin.
* @function Users.usernameFromIDReply
* Notifies scripts of the user name and machine fingerprint associated with a UUID.
* Username and machineFingerprint will be their default constructor output if the requesting user isn't an admin.
* @function Users.usernameFromIDReply
* @param {Uuid} nodeID
* @param {string} userName
* @param {string} machineFingerprint
* @param {boolean} isAdmin
* @returns {Signal}
*/
void usernameFromIDReply(const QString& nodeID, const QString& username, const QString& machineFingerprint, bool isAdmin);
/**jsdoc
* Notifies scripts that a user has disconnected from the domain
* Notifies scripts that a user has disconnected from the domain.
* @function Users.avatarDisconnected
* @param {nodeID} NodeID The session ID of the avatar that has disconnected
* @param {Uuid} nodeID The session ID of the avatar that has disconnected.
* @returns {Signal}
*/
void avatarDisconnected(const QUuid& nodeID);

View file

@ -18,6 +18,7 @@
#include <SettingHandle.h>
/// Base class for face trackers (DDE, BinaryVR).
class FaceTracker : public QObject {
Q_OBJECT
@ -58,11 +59,34 @@ public:
QVector<float>& coefficients);
signals:
/**jsdoc
* @function FaceTracker.muteToggled
* @returns {Signal}
*/
void muteToggled();
public slots:
/**jsdoc
* @function FaceTracker.setEnabled
* @param {boolean} enabled
*/
virtual void setEnabled(bool enabled) = 0;
/**jsdoc
* @function FaceTracker.toggleMute
*/
void toggleMute();
/**jsdoc
* @function FaceTracker.getMuted
* @returns {boolean}
*/
bool getMuted() { return _isMuted; }
protected:

View file

@ -44,6 +44,23 @@ class OffscreenQmlSurface;
class TabletScriptingInterface : public QObject, public Dependency {
Q_OBJECT
public:
/**jsdoc
* <table>
* <thead>
* <tr><th>Value</th><th>Description</th></tr>
* </thead>
* <tbody>
* <tr><td><code>0</code></td><td>Button click.</td></tr>
* <tr><td><code>1</code></td><td>Button hover.</td></tr>
* <tr><td><code>2</code></td><td>Tablet open.</td></tr>
* <tr><td><code>3</code></td><td>Tablet hands in.</td></tr>
* <tr><td><code>4</code></td><td>Tablet hands out.</td></tr>
* <tr><td><code>5</code></td><td>Last.</td></tr>
* </tbody>
* </table>
* @typedef {number} Tablet.AudioEvents
*/
enum TabletAudioEvents { ButtonClick, ButtonHover, TabletOpen, TabletHandsIn, TabletHandsOut, Last};
Q_ENUM(TabletAudioEvents)
@ -58,14 +75,19 @@ public:
void setToolbarScriptingInterface(ToolbarScriptingInterface* toolbarScriptingInterface) { _toolbarScriptingInterface = toolbarScriptingInterface; }
/**jsdoc
* Creates or retruns a new TabletProxy and returns it.
* Creates or returns a new TabletProxy and returns it.
* @function Tablet.getTablet
* @param name {String} tablet name
* @return {TabletProxy} tablet instance
* @param {string} name - Tablet name.
* @returns {TabletProxy} Tablet instance.
*/
Q_INVOKABLE TabletProxy* getTablet(const QString& tabletId);
void preloadSounds();
/**jsdoc
* @function Tablet.playSound
* @param {Tablet.AudioEvents} sound
*/
Q_INVOKABLE void playSound(TabletAudioEvents aEvent);
void setToolbarMode(bool toolbarMode);
@ -79,8 +101,8 @@ public:
QObject* getFlags();
signals:
/**jsdoc
* Signaled when a tablet message or dialog is created
* @function TabletProxy#tabletNotification
* Triggered when a tablet message or dialog is created.
* @function Tablet.tabletNotification
* @returns {Signal}
*/
void tabletNotification();
@ -98,6 +120,9 @@ protected:
bool _toolbarMode { false };
};
/**jsdoc
* @typedef {object} TabletProxy#ButtonList
*/
class TabletButtonListModel : public QAbstractListModel {
Q_OBJECT
@ -151,9 +176,12 @@ Q_DECLARE_METATYPE(TabletButtonsProxyModel*);
/**jsdoc
* @class TabletProxy
* @property name {string} READ_ONLY: name of this tablet
* @property toolbarMode {bool} - used to transition this tablet into and out of toolbar mode.
* @property {string} name - Name of this tablet. <em>Read-only.</em>
* @property {boolean} toolbarMode - Used to transition this tablet into and out of toolbar mode.
* When tablet is in toolbar mode, all its buttons will appear in a floating toolbar.
* @property {boolean} landscape
* @property {boolean} tabletShown <em>Read-only.</em>
* @property {TabletProxy#ButtonList} buttons <em>Read-only.</em>
*/
class TabletProxy : public QObject {
Q_OBJECT
@ -172,88 +200,136 @@ public:
bool getToolbarMode() const { return _toolbarMode; }
void setToolbarMode(bool toolbarMode);
/**jsdoc
* @function TabletProxy#gotoMenuScreen
* @param {string} [submenu=""]
*/
Q_INVOKABLE void gotoMenuScreen(const QString& submenu = "");
Q_INVOKABLE void initialScreen(const QVariant& url);
/**jsdoc
* transition to the home screen
* @function TabletProxy#initialScreen
* @param {string} url
*/
Q_INVOKABLE void initialScreen(const QVariant& url);
/**jsdoc
* Transition to the home screen.
* @function TabletProxy#gotoHomeScreen
*/
Q_INVOKABLE void gotoHomeScreen();
/**jsdoc
* show the specified web url on the tablet.
* Show the specified Web url on the tablet.
* @function TabletProxy#gotoWebScreen
* @param url {string} url of web page.
* @param [injectedJavaScriptUrl] {string} optional url to an additional JS script to inject into the web page.
* @param {string} url - URL of web page.
* @param {string} [injectedJavaScriptUrl=""] - URL to an additional JS script to inject into the web page.
* @param {boolean} [loadOtherBase=false]
*/
Q_INVOKABLE void gotoWebScreen(const QString& url);
Q_INVOKABLE void gotoWebScreen(const QString& url, const QString& injectedJavaScriptUrl, bool loadOtherBase = false);
/**jsdoc
* @function TabletProxy#loadQMLSource
* @param {string} path
* @param {boolean} [resizable=false]
*/
Q_INVOKABLE void loadQMLSource(const QVariant& path, bool resizable = false);
// FIXME: This currently relies on a script initializing the tablet (hence the bool denoting success);
// it should be initialized internally so it cannot fail
Q_INVOKABLE bool pushOntoStack(const QVariant& path);
Q_INVOKABLE void popFromStack();
Q_INVOKABLE void loadQMLOnTop(const QVariant& path);
Q_INVOKABLE void loadWebScreenOnTop(const QVariant& url);
Q_INVOKABLE void loadWebScreenOnTop(const QVariant& url, const QString& injectedJavaScriptUrl);
Q_INVOKABLE void returnToPreviousApp();
/**jsdoc
* Check if the tablet has a message dialog open
* @function TabletProxy#pushOntoStack
* @param {string} path
* @returns {boolean}
*/
Q_INVOKABLE bool pushOntoStack(const QVariant& path);
/**jsdoc
* @function TabletProxy#popFromStack
*/
Q_INVOKABLE void popFromStack();
/**jsdoc
* @function TabletProxy#loadQMLOnTop
* @param {string} path
*/
Q_INVOKABLE void loadQMLOnTop(const QVariant& path);
/**jsdoc
* @function TabletProxy#loadWebScreenOnTop
* @param {string} path
* @param {string} [injectedJavaScriptURL=""]
*/
Q_INVOKABLE void loadWebScreenOnTop(const QVariant& url);
Q_INVOKABLE void loadWebScreenOnTop(const QVariant& url, const QString& injectedJavaScriptUrl);
/**jsdoc
* @function TabletProxy#returnToPreviousApp
*/
Q_INVOKABLE void returnToPreviousApp();
/**jsdoc
* Check if the tablet has a message dialog open.
* @function TabletProxy#isMessageDialogOpen
* @returns {boolean}
*/
Q_INVOKABLE bool isMessageDialogOpen();
/**jsdoc
* Creates a new button, adds it to this and returns it.
* @function TabletProxy#addButton
* @param properties {Object} button properties UI_TABLET_HACK: enumerate these when we figure out what they should be!
* @param {object} properties - Button properties.
* @returns {TabletButtonProxy}
*/
//FIXME: UI_TABLET_HACK: enumerate the button properties when we figure out what they should be!
Q_INVOKABLE TabletButtonProxy* addButton(const QVariant& properties);
/**jsdoc
* removes button from the tablet
* @function TabletProxy.removeButton
* @param tabletButtonProxy {TabletButtonProxy} button to be removed
* Removes a button from the tablet.
* @function TabletProxy#removeButton
* @param {TabletButtonProxy} button - The button to be removed
*/
Q_INVOKABLE void removeButton(TabletButtonProxy* tabletButtonProxy);
/**jsdoc
* Used to send an event to the html/js embedded in the tablet
* Used to send an event to the HTML/JavaScript embedded in the tablet.
* @function TabletProxy#emitScriptEvent
* @param msg {object|string}
* @param {object|string} message
*/
Q_INVOKABLE void emitScriptEvent(const QVariant& msg);
/**jsdoc
* Used to send an event to the qml embedded in the tablet
* Used to send an event to the QML embedded in the tablet.
* @function TabletProxy#sendToQml
* @param msg {object|string}
* @param {object|string} message
*/
Q_INVOKABLE void sendToQml(const QVariant& msg);
/**jsdoc
* Check if the tablet is on the homescreen
* @function TabletProxy#onHomeScreen()
* Check if the tablet is on the home screen.
* @function TabletProxy#onHomeScreen
* @returns {boolean}
*/
Q_INVOKABLE bool onHomeScreen();
/**jsdoc
* set tablet into our out of landscape mode
* Set tablet into or out of landscape mode.
* @function TabletProxy#setLandscape
* @param landscape {bool} true for landscape, false for portrait
* @param {boolean} landscape - <code>true</code> for landscape, <ode>false</code> for portrait.
*/
Q_INVOKABLE void setLandscape(bool landscape) { _landscape = landscape; }
/**jsdoc
* @function TabletProxy#getLandscape
* @returns {boolean}
*/
Q_INVOKABLE bool getLandscape() { return _landscape; }
/**jsdoc
* @function TabletProxy#isPathLoaded
* @param {string} path
* @returns {boolean}
*/
Q_INVOKABLE bool isPathLoaded(const QVariant& path);
QQuickItem* getTabletRoot() const { return _qmlTabletRoot; }
@ -268,17 +344,17 @@ public:
signals:
/**jsdoc
* Signaled when this tablet receives an event from the html/js embedded in the tablet
* Signaled when this tablet receives an event from the html/js embedded in the tablet.
* @function TabletProxy#webEventReceived
* @param msg {object|string}
* @param {object|string} message
* @returns {Signal}
*/
void webEventReceived(QVariant msg);
/**jsdoc
* Signaled when this tablet receives an event from the qml embedded in the tablet
* Signaled when this tablet receives an event from the qml embedded in the tablet.
* @function TabletProxy#fromQml
* @param msg {object|string}
* @param {object|string} message
* @returns {Signal}
*/
void fromQml(QVariant msg);
@ -286,18 +362,21 @@ signals:
/**jsdoc
* Signaled when this tablet screen changes.
* @function TabletProxy#screenChanged
* @param type {string} - "Home", "Web", "Menu", "QML", "Closed"
* @param url {string} - only valid for Web and QML.
* @param type {string} - "Home", "Web", "Menu", "QML", "Closed".
* @param url {string} - Only valid for Web and QML.
*/
void screenChanged(QVariant type, QVariant url);
/**jsdoc
* Signaled when the tablet becomes visible or becomes invisible
* @function TabletProxy#isTabletShownChanged
* @returns {Signal}
*/
* Signaled when the tablet becomes visible or becomes invisible.
* @function TabletProxy#isTabletShownChanged
* @returns {Signal}
*/
void tabletShownChanged();
/**jsdoc
* @function TabletProxy#toolbarModeChanged
*/
void toolbarModeChanged();
protected slots:
@ -331,7 +410,8 @@ Q_DECLARE_METATYPE(TabletProxy*);
/**jsdoc
* @class TabletButtonProxy
* @property uuid {QUuid} READ_ONLY: uniquely identifies this button
* @property {Uuid} uuid - Uniquely identifies this button. <em>Read-only.</em>
* @property {TabletButtonProxy.ButtonProperties} properties
*/
class TabletButtonProxy : public QObject {
Q_OBJECT
@ -344,48 +424,55 @@ public:
QUuid getUuid() const { return _uuid; }
/**jsdoc
* Returns the current value of this button's properties
* Returns the current value of this button's properties.
* @function TabletButtonProxy#getProperties
* @returns {ButtonProperties}
* @returns {TabletButtonProxy.ButtonProperties}
*/
Q_INVOKABLE QVariantMap getProperties();
/**jsdoc
* Replace the values of some of this button's properties
* Replace the values of some of this button's properties.
* @function TabletButtonProxy#editProperties
* @param {ButtonProperties} properties - set of properties to change
* @param {TabletButtonProxy.ButtonProperties} properties - Set of properties to change.
*/
Q_INVOKABLE void editProperties(const QVariantMap& properties);
signals:
/**jsdoc
* Signaled when this button has been clicked on by the user.
* Triggered when this button has been clicked on by the user.
* @function TabletButtonProxy#clicked
* @returns {Signal}
*/
void clicked();
/**jsdoc
* @function TabletButtonProxy#propertiesChanged
* @returns {Signal}
*/
void propertiesChanged();
protected:
QUuid _uuid;
int _stableOrder;
/**jsdoc
* @typedef TabletButtonProxy.ButtonProperties
* @property {string} icon - URL to button icon. (50 x 50)
* @property {string} hoverIcon - URL to button icon, displayed during mouse hover. (50 x 50)
* @property {string} activeHoverIcon - URL to button icon used when button is active, and during mouse hover. (50 x 50)
* @property {string} activeIcon - URL to button icon used when button is active. (50 x 50)
* @property {string} text - Button caption.
* @property {string} hoverText - Button caption when button is not-active but during mouse hover.
* @property {string} activeText - Button caption when button is active.
* @property {string} activeHoverText - Button caption when button is active and during mouse hover.
* @property {boolean} isActive - <code>true</code> when button is active.
* @property {number} sortOrder - Determines sort order on tablet. lower numbers will appear before larger numbers.
* Default is 100.
*/
// FIXME: There are additional properties.
QVariantMap _properties;
};
Q_DECLARE_METATYPE(TabletButtonProxy*);
/**jsdoc
* @typedef TabletButtonProxy.ButtonProperties
* @property {string} icon - url to button icon. (50 x 50)
* @property {string} hoverIcon - url to button icon, displayed during mouse hover. (50 x 50)
* @property {string} activeHoverIcon - url to button icon used when button is active, and during mouse hover. (50 x 50)
* @property {string} activeIcon - url to button icon used when button is active. (50 x 50)
* @property {string} text - button caption
* @property {string} hoverText - button caption when button is not-active but during mouse hover.
* @property {string} activeText - button caption when button is active
* @property {string} activeHoverText - button caption when button is active and during mouse hover.
* @property {string} isActive - true when button is active.
* @property {number} sortOrder - determines sort order on tablet. lower numbers will appear before larger numbers. default is 100
*/
#endif // hifi_TabletScriptingInterface_h

View file

@ -98,6 +98,7 @@ Rectangle {
property: "covarianceGamma"
max: 1.5
min: 0.5
height: 38
}
Separator {}
HifiControls.CheckBox {
@ -114,6 +115,7 @@ Rectangle {
property: "blend"
max: 1.0
min: 0.0
height: 38
}
ConfigSlider {
@ -162,6 +164,7 @@ Rectangle {
property: "debugShowVelocityThreshold"
max: 50
min: 0.0
height: 38
}
ConfigSlider {
label: qsTr("Debug Orb Zoom")
@ -170,6 +173,7 @@ Rectangle {
property: "debugOrbZoom"
max: 32.0
min: 1.0
height: 38
}
}
}

View file

@ -93,9 +93,10 @@ Item {
integral: false
config: root.config
property: "intensity"
max: 5.0
max: 1.0
min: 0.0
width: 280
height:38
}
ConfigSlider {
label: "Size"
@ -105,6 +106,7 @@ Item {
max: 1.0
min: 0.0
width: 280
height:38
}
ConfigSlider {
label: "Threshold"
@ -114,6 +116,7 @@ Item {
max: 2.0
min: 0.0
width: 280
height:38
}
}
}

View file

@ -15,6 +15,6 @@ var window = new OverlayWindow({
title: 'Bloom',
source: qml,
width: 285,
height: 170,
height: 210,
});
window.closed.connect(function() { Script.stop(); });

View file

@ -838,11 +838,31 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
}
};
var deleteEntity = function(entityID) {
if (rightEquipEntity.targetEntityID === entityID) {
rightEquipEntity.endEquipEntity();
}
if (leftEquipEntity.targetEntityID === entityID) {
leftEquipEntity.endEquipEntity();
}
};
var clearEntities = function() {
if (rightEquipEntity.targetEntityID) {
rightEquipEntity.endEquipEntity();
}
if (leftEquipEntity.targetEntityID) {
leftEquipEntity.endEquipEntity();
}
};
Messages.subscribe('Hifi-Hand-Grab');
Messages.subscribe('Hifi-Hand-Drop');
Messages.messageReceived.connect(handleMessage);
Controller.mousePressEvent.connect(onMousePress);
Controller.keyPressEvent.connect(onKeyPress);
Entities.deletingEntity.connect(deleteEntity);
Entities.clearingEntities.connect(clearEntities);
var leftEquipEntity = new EquipEntity(LEFT_HAND);
var rightEquipEntity = new EquipEntity(RIGHT_HAND);
@ -859,6 +879,8 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
Messages.messageReceived.disconnect(handleMessage);
Controller.mousePressEvent.disconnect(onMousePress);
Controller.keyPressEvent.disconnect(onKeyPress);
Entities.deletingEntity.disconnect(deleteEntity);
Entities.clearingEntities.disconnect(clearEntities);
}
Script.scriptEnding.connect(cleanup);
}());

View file

@ -98,7 +98,7 @@ void TestWindow::beginFrame() {
_preparePrimaryFramebuffer.run(_renderContext, primaryFramebuffer);
DeferredFrameTransformPointer frameTransform;
_generateDeferredFrameTransform.run(_renderContext, frameTransform);
_generateDeferredFrameTransform.run(_renderContext, glm::vec2(0.0f, 0.0f), frameTransform);
LightingModelPointer lightingModel;
_generateLightingModel.run(_renderContext, lightingModel);

View file

@ -17,12 +17,22 @@ exports.handlers = {
// directories to scan for jsdoc comments
var dirList = [
'../../interface/src',
'../../interface/src/assets',
'../../interface/src/audio',
'../../interface/src/avatar',
'../../interface/src/commerce',
'../../interface/src/devices',
'../../interface/src/java',
'../../interface/src/networking',
'../../interface/src/ui/',
'../../interface/src/scripting',
'../../interface/src/ui/overlays',
'../../interface/src/raypick',
'../../libraries/animation/src',
'../../libraries/audio-client/src',
'../../libraries/audio/src',
'../../libraries/avatars/src',
'../../libraries/avatars-renderer/src/avatars-renderer',
'../../libraries/controllers/src/controllers/',
'../../libraries/controllers/src/controllers/impl/',
'../../libraries/display-plugins/src/display-plugins/',
@ -37,6 +47,7 @@ exports.handlers = {
'../../libraries/script-engine/src',
'../../libraries/shared/src',
'../../libraries/shared/src/shared',
'../../libraries/ui/src/ui',
'../../plugins/oculus/src',
'../../plugins/openvr/src',
];