mirror of
https://github.com/AleziaKurdis/overte.git
synced 2025-04-08 23:12:16 +02:00
Merge commit 'refs/pull/13165/head' of https://github.com/highfidelity/hifi into gravprep-redo
This commit is contained in:
commit
bbb1aa404f
51 changed files with 986 additions and 418 deletions
|
@ -17,20 +17,144 @@
|
|||
#include <AvatarData.h>
|
||||
#include <ScriptEngine.h>
|
||||
|
||||
/**jsdoc
|
||||
* The <code>Avatar</code> API is used to manipulate scriptable avatars on the domain. This API is a subset of the
|
||||
* {@link MyAvatar} API.
|
||||
*
|
||||
* <p><strong>Note:</strong> In the examples, use "<code>Avatar</code>" instead of "<code>MyAvatar</code>".</p>
|
||||
*
|
||||
* @namespace Avatar
|
||||
*
|
||||
* @hifi-assignment-client
|
||||
*
|
||||
* @property {Vec3} position
|
||||
* @property {number} scale
|
||||
* @property {number} density <em>Read-only.</em>
|
||||
* @property {Vec3} handPosition
|
||||
* @property {number} bodyYaw - The rotation left or right about an axis running from the head to the feet of the avatar.
|
||||
* Yaw is sometimes called "heading".
|
||||
* @property {number} bodyPitch - The rotation about an axis running from shoulder to shoulder of the avatar. Pitch is
|
||||
* sometimes called "elevation".
|
||||
* @property {number} bodyRoll - The rotation about an axis running from the chest to the back of the avatar. Roll is
|
||||
* sometimes called "bank".
|
||||
* @property {Quat} orientation
|
||||
* @property {Quat} headOrientation - The orientation of the avatar's head.
|
||||
* @property {number} headPitch - The rotation about an axis running from ear to ear of the avatar's head. Pitch is
|
||||
* sometimes called "elevation".
|
||||
* @property {number} headYaw - The rotation left or right about an axis running from the base to the crown of the avatar's
|
||||
* head. Yaw is sometimes called "heading".
|
||||
* @property {number} headRoll - The rotation about an axis running from the nose to the back of the avatar's head. Roll is
|
||||
* sometimes called "bank".
|
||||
* @property {Vec3} velocity
|
||||
* @property {Vec3} angularVelocity
|
||||
* @property {number} audioLoudness
|
||||
* @property {number} audioAverageLoudness
|
||||
* @property {string} displayName
|
||||
* @property {string} sessionDisplayName - Sanitized, defaulted version displayName that is defined by the AvatarMixer
|
||||
* rather than by Interface clients. The result is unique among all avatars present at the time.
|
||||
* @property {boolean} lookAtSnappingEnabled
|
||||
* @property {string} skeletonModelURL
|
||||
* @property {AttachmentData[]} attachmentData
|
||||
* @property {string[]} jointNames - The list of joints in the current avatar model. <em>Read-only.</em>
|
||||
* @property {Uuid} sessionUUID <em>Read-only.</em>
|
||||
* @property {Mat4} sensorToWorldMatrix <em>Read-only.</em>
|
||||
* @property {Mat4} controllerLeftHandMatrix <em>Read-only.</em>
|
||||
* @property {Mat4} controllerRightHandMatrix <em>Read-only.</em>
|
||||
* @property {number} sensorToWorldScale <em>Read-only.</em>
|
||||
*
|
||||
* @borrows MyAvatar.getDomainMinScale as getDomainMinScale
|
||||
* @borrows MyAvatar.getDomainMaxScale as getDomainMaxScale
|
||||
* @borrows MyAvatar.canMeasureEyeHeight as canMeasureEyeHeight
|
||||
* @borrows MyAvatar.getEyeHeight as getEyeHeight
|
||||
* @borrows MyAvatar.getHeight as getHeight
|
||||
* @borrows MyAvatar.setHandState as setHandState
|
||||
* @borrows MyAvatar.getHandState as getHandState
|
||||
* @borrows MyAvatar.setRawJointData as setRawJointData
|
||||
* @borrows MyAvatar.setJointData as setJointData
|
||||
* @borrows MyAvatar.setJointRotation as setJointRotation
|
||||
* @borrows MyAvatar.setJointTranslation as setJointTranslation
|
||||
* @borrows MyAvatar.clearJointData as clearJointData
|
||||
* @borrows MyAvatar.isJointDataValid as isJointDataValid
|
||||
* @borrows MyAvatar.getJointRotation as getJointRotation
|
||||
* @borrows MyAvatar.getJointTranslation as getJointTranslation
|
||||
* @borrows MyAvatar.getJointRotations as getJointRotations
|
||||
* @borrows MyAvatar.getJointTranslations as getJointTranslations
|
||||
* @borrows MyAvatar.setJointRotations as setJointRotations
|
||||
* @borrows MyAvatar.setJointTranslations as setJointTranslations
|
||||
* @borrows MyAvatar.clearJointsData as clearJointsData
|
||||
* @borrows MyAvatar.getJointIndex as getJointIndex
|
||||
* @borrows MyAvatar.getJointNames as getJointNames
|
||||
* @borrows MyAvatar.setBlendshape as setBlendshape
|
||||
* @borrows MyAvatar.getAttachmentsVariant as getAttachmentsVariant
|
||||
* @borrows MyAvatar.setAttachmentsVariant as setAttachmentsVariant
|
||||
* @borrows MyAvatar.updateAvatarEntity as updateAvatarEntity
|
||||
* @borrows MyAvatar.clearAvatarEntity as clearAvatarEntity
|
||||
* @borrows MyAvatar.setForceFaceTrackerConnected as setForceFaceTrackerConnected
|
||||
* @borrows MyAvatar.getAttachmentData as getAttachmentData
|
||||
* @borrows MyAvatar.setAttachmentData as setAttachmentData
|
||||
* @borrows MyAvatar.attach as attach
|
||||
* @borrows MyAvatar.detachOne as detachOne
|
||||
* @borrows MyAvatar.detachAll as detachAll
|
||||
* @borrows MyAvatar.getAvatarEntityData as getAvatarEntityData
|
||||
* @borrows MyAvatar.setAvatarEntityData as setAvatarEntityData
|
||||
* @borrows MyAvatar.getSensorToWorldMatrix as getSensorToWorldMatrix
|
||||
* @borrows MyAvatar.getSensorToWorldScale as getSensorToWorldScale
|
||||
* @borrows MyAvatar.getControllerLeftHandMatrix as getControllerLeftHandMatrix
|
||||
* @borrows MyAvatar.getControllerRightHandMatrix as getControllerRightHandMatrix
|
||||
* @borrows MyAvatar.getDataRate as getDataRate
|
||||
* @borrows MyAvatar.getUpdateRate as getUpdateRate
|
||||
* @borrows MyAvatar.displayNameChanged as displayNameChanged
|
||||
* @borrows MyAvatar.sessionDisplayNameChanged as sessionDisplayNameChanged
|
||||
* @borrows MyAvatar.skeletonModelURLChanged as skeletonModelURLChanged
|
||||
* @borrows MyAvatar.lookAtSnappingChanged as lookAtSnappingChanged
|
||||
* @borrows MyAvatar.sessionUUIDChanged as sessionUUIDChanged
|
||||
* @borrows MyAvatar.sendAvatarDataPacket as sendAvatarDataPacket
|
||||
* @borrows MyAvatar.sendIdentityPacket as sendIdentityPacket
|
||||
* @borrows MyAvatar.setJointMappingsFromNetworkReply as setJointMappingsFromNetworkReply
|
||||
* @borrows MyAvatar.setSessionUUID as setSessionUUID
|
||||
* @borrows MyAvatar.getAbsoluteJointRotationInObjectFrame as getAbsoluteJointRotationInObjectFrame
|
||||
* @borrows MyAvatar.getAbsoluteJointTranslationInObjectFrame as getAbsoluteJointTranslationInObjectFrame
|
||||
* @borrows MyAvatar.setAbsoluteJointRotationInObjectFrame as setAbsoluteJointRotationInObjectFrame
|
||||
* @borrows MyAvatar.setAbsoluteJointTranslationInObjectFrame as setAbsoluteJointTranslationInObjectFrame
|
||||
* @borrows MyAvatar.getTargetScale as getTargetScale
|
||||
* @borrows MyAvatar.resetLastSent as resetLastSent
|
||||
*/
|
||||
|
||||
class ScriptableAvatar : public AvatarData, public Dependency {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* @function Avatar.startAnimation
|
||||
* @param {string} url
|
||||
* @param {number} [fps=30]
|
||||
* @param {number} [priority=1]
|
||||
* @param {boolean} [loop=false]
|
||||
* @param {boolean} [hold=false]
|
||||
* @param {number} [firstFrame=0]
|
||||
* @param {number} [lastFrame=3.403e+38]
|
||||
* @param {string[]} [maskedJoints=[]]
|
||||
*/
|
||||
/// Allows scripts to run animations.
|
||||
Q_INVOKABLE void startAnimation(const QString& url, float fps = 30.0f, float priority = 1.0f, bool loop = false,
|
||||
bool hold = false, float firstFrame = 0.0f, float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList());
|
||||
bool hold = false, float firstFrame = 0.0f, float lastFrame = FLT_MAX,
|
||||
const QStringList& maskedJoints = QStringList());
|
||||
|
||||
/**jsdoc
|
||||
* @function Avatar.stopAnimation
|
||||
*/
|
||||
Q_INVOKABLE void stopAnimation();
|
||||
|
||||
/**jsdoc
|
||||
* @function Avatar.getAnimationDetails
|
||||
* @returns {Avatar.AnimationDetails}
|
||||
*/
|
||||
Q_INVOKABLE AnimationDetails getAnimationDetails();
|
||||
|
||||
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override;
|
||||
|
||||
virtual QByteArray toByteArrayStateful(AvatarDataDetail dataDetail, bool dropFaceTracking = false) override;
|
||||
|
||||
|
||||
private slots:
|
||||
void update(float deltatime);
|
||||
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
//
|
||||
// Sends messages over the EventBridge when text input is required.
|
||||
//
|
||||
|
||||
/* global document, window, console, setTimeout, setInterval, EventBridge */
|
||||
|
||||
(function () {
|
||||
var POLL_FREQUENCY = 500; // ms
|
||||
var MAX_WARNINGS = 3;
|
||||
|
@ -37,22 +40,24 @@
|
|||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function shouldSetNumeric() {
|
||||
return document.activeElement.type === "number";
|
||||
};
|
||||
}
|
||||
|
||||
function scheduleBringToView(timeout) {
|
||||
|
||||
var timer = setTimeout(function () {
|
||||
clearTimeout(timer);
|
||||
|
||||
setTimeout(function () {
|
||||
// If the element is not visible because the keyboard has been raised over the top of it, scroll it up into view.
|
||||
// If the element is not visible because the keyboard raising has moved it off screen, scroll it down into view.
|
||||
var elementRect = document.activeElement.getBoundingClientRect();
|
||||
var absoluteElementTop = elementRect.top + window.scrollY;
|
||||
var middle = absoluteElementTop - (window.innerHeight / 2);
|
||||
|
||||
window.scrollTo(0, middle);
|
||||
var VISUAL_MARGIN = 3;
|
||||
var delta = elementRect.y + elementRect.height + VISUAL_MARGIN - window.innerHeight;
|
||||
if (delta > 0) {
|
||||
window.scrollBy(0, delta);
|
||||
} else if (elementRect.y < VISUAL_MARGIN) {
|
||||
window.scrollBy(0, elementRect.y - VISUAL_MARGIN);
|
||||
}
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
|
@ -62,11 +67,13 @@
|
|||
var passwordField = shouldSetPasswordField();
|
||||
|
||||
if (isWindowFocused &&
|
||||
(keyboardRaised !== window.isKeyboardRaised || numericKeyboard !== window.isNumericKeyboard || passwordField !== window.isPasswordField)) {
|
||||
(keyboardRaised !== window.isKeyboardRaised || numericKeyboard !== window.isNumericKeyboard
|
||||
|| passwordField !== window.isPasswordField)) {
|
||||
|
||||
if (typeof EventBridge !== "undefined" && EventBridge !== null) {
|
||||
EventBridge.emitWebEvent(
|
||||
keyboardRaised ? ("_RAISE_KEYBOARD" + (numericKeyboard ? "_NUMERIC" : "") + (passwordField ? "_PASSWORD" : "")) : "_LOWER_KEYBOARD"
|
||||
keyboardRaised ? ("_RAISE_KEYBOARD" + (numericKeyboard ? "_NUMERIC" : "")
|
||||
+ (passwordField ? "_PASSWORD" : "")) : "_LOWER_KEYBOARD"
|
||||
);
|
||||
} else {
|
||||
if (numWarnings < MAX_WARNINGS) {
|
||||
|
@ -77,7 +84,7 @@
|
|||
|
||||
if (!window.isKeyboardRaised) {
|
||||
scheduleBringToView(250); // Allow time for keyboard to be raised in QML.
|
||||
// 2DO: should it be rather done from 'client area height changed' event?
|
||||
// 2DO: should it be rather done from 'client area height changed' event?
|
||||
}
|
||||
|
||||
window.isKeyboardRaised = keyboardRaised;
|
||||
|
|
|
@ -49,6 +49,7 @@ Item {
|
|||
property string upgradeTitle;
|
||||
property bool updateAvailable: root.upgradeUrl !== "" && !root.isShowingMyItems;
|
||||
property bool isShowingMyItems;
|
||||
property bool valid;
|
||||
|
||||
property string originalStatusText;
|
||||
property string originalStatusColor;
|
||||
|
@ -239,6 +240,7 @@ Item {
|
|||
width: 62;
|
||||
|
||||
onLoaded: {
|
||||
item.enabled = root.valid;
|
||||
item.buttonGlyphText = hifi.glyphs.gift;
|
||||
item.buttonText = "Gift";
|
||||
item.buttonClicked = function() {
|
||||
|
@ -463,7 +465,7 @@ Item {
|
|||
|
||||
Item {
|
||||
id: statusContainer;
|
||||
visible: root.purchaseStatus === "pending" || root.purchaseStatus === "invalidated" || root.numberSold > -1;
|
||||
visible: root.purchaseStatus === "pending" || !root.valid || root.numberSold > -1;
|
||||
anchors.left: itemName.left;
|
||||
anchors.right: itemName.right;
|
||||
anchors.top: itemName.bottom;
|
||||
|
@ -480,7 +482,7 @@ Item {
|
|||
text: {
|
||||
if (root.purchaseStatus === "pending") {
|
||||
"PENDING..."
|
||||
} else if (root.purchaseStatus === "invalidated") {
|
||||
} else if (!root.valid) {
|
||||
"INVALIDATED"
|
||||
} else if (root.numberSold > -1) {
|
||||
("Sales: " + root.numberSold + "/" + (root.limitedRun === -1 ? "\u221e" : root.limitedRun))
|
||||
|
@ -492,7 +494,7 @@ Item {
|
|||
color: {
|
||||
if (root.purchaseStatus === "pending") {
|
||||
hifi.colors.blueAccent
|
||||
} else if (root.purchaseStatus === "invalidated") {
|
||||
} else if (!root.valid) {
|
||||
hifi.colors.redAccent
|
||||
} else {
|
||||
hifi.colors.baseGray
|
||||
|
@ -506,7 +508,7 @@ Item {
|
|||
text: {
|
||||
if (root.purchaseStatus === "pending") {
|
||||
hifi.glyphs.question
|
||||
} else if (root.purchaseStatus === "invalidated") {
|
||||
} else if (!root.valid) {
|
||||
hifi.glyphs.question
|
||||
} else {
|
||||
""
|
||||
|
@ -523,7 +525,7 @@ Item {
|
|||
color: {
|
||||
if (root.purchaseStatus === "pending") {
|
||||
hifi.colors.blueAccent
|
||||
} else if (root.purchaseStatus === "invalidated") {
|
||||
} else if (!root.valid) {
|
||||
hifi.colors.redAccent
|
||||
} else {
|
||||
hifi.colors.baseGray
|
||||
|
@ -538,7 +540,7 @@ Item {
|
|||
onClicked: {
|
||||
if (root.purchaseStatus === "pending") {
|
||||
sendToPurchases({method: 'showPendingLightbox'});
|
||||
} else if (root.purchaseStatus === "invalidated") {
|
||||
} else if (!root.valid) {
|
||||
sendToPurchases({method: 'showInvalidatedLightbox'});
|
||||
}
|
||||
}
|
||||
|
@ -546,7 +548,7 @@ Item {
|
|||
if (root.purchaseStatus === "pending") {
|
||||
statusText.color = hifi.colors.blueHighlight;
|
||||
statusIcon.color = hifi.colors.blueHighlight;
|
||||
} else if (root.purchaseStatus === "invalidated") {
|
||||
} else if (!root.valid) {
|
||||
statusText.color = hifi.colors.redAccent;
|
||||
statusIcon.color = hifi.colors.redAccent;
|
||||
}
|
||||
|
@ -555,7 +557,7 @@ Item {
|
|||
if (root.purchaseStatus === "pending") {
|
||||
statusText.color = hifi.colors.blueAccent;
|
||||
statusIcon.color = hifi.colors.blueAccent;
|
||||
} else if (root.purchaseStatus === "invalidated") {
|
||||
} else if (!root.valid) {
|
||||
statusText.color = hifi.colors.redHighlight;
|
||||
statusIcon.color = hifi.colors.redHighlight;
|
||||
}
|
||||
|
@ -645,8 +647,8 @@ Item {
|
|||
width: 160;
|
||||
height: 40;
|
||||
enabled: root.hasPermissionToRezThis &&
|
||||
root.purchaseStatus !== "invalidated" &&
|
||||
MyAvatar.skeletonModelURL !== root.itemHref;
|
||||
MyAvatar.skeletonModelURL !== root.itemHref &&
|
||||
root.valid;
|
||||
|
||||
onHoveredChanged: {
|
||||
if (hovered) {
|
||||
|
|
|
@ -616,6 +616,7 @@ Rectangle {
|
|||
upgradeTitle: model.upgrade_title;
|
||||
itemType: model.itemType;
|
||||
isShowingMyItems: root.isShowingMyItems;
|
||||
valid: model.valid;
|
||||
anchors.topMargin: 10;
|
||||
anchors.bottomMargin: 10;
|
||||
|
||||
|
@ -995,10 +996,6 @@ Rectangle {
|
|||
|
||||
for (var i = 0; i < purchasesModel.count; i++) {
|
||||
if (purchasesModel.get(i).title.toLowerCase().indexOf(filterBar.text.toLowerCase()) !== -1) {
|
||||
if (!purchasesModel.get(i).valid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (purchasesModel.get(i).status !== "confirmed" && !root.isShowingMyItems) {
|
||||
tempPurchasesModel.insert(0, purchasesModel.get(i));
|
||||
} else if ((root.isShowingMyItems && purchasesModel.get(i).edition_number === "0") ||
|
||||
|
@ -1055,10 +1052,6 @@ Rectangle {
|
|||
var currentId;
|
||||
for (var i = 0; i < tempPurchasesModel.count; i++) {
|
||||
currentId = tempPurchasesModel.get(i).id;
|
||||
|
||||
if (!purchasesModel.get(i).valid) {
|
||||
continue;
|
||||
}
|
||||
filteredPurchasesModel.append(tempPurchasesModel.get(i));
|
||||
filteredPurchasesModel.setProperty(i, 'cardBackVisible', false);
|
||||
filteredPurchasesModel.setProperty(i, 'isInstalled', ((root.installedApps).indexOf(currentId) > -1));
|
||||
|
|
|
@ -735,9 +735,9 @@ extern InputPluginList getInputPlugins();
|
|||
extern void saveInputPluginSettings(const InputPluginList& plugins);
|
||||
|
||||
// Parameters used for running tests from teh command line
|
||||
const QString TEST_SCRIPT_COMMAND { "--testScript" };
|
||||
const QString TEST_QUIT_WHEN_FINISHED_OPTION { "quitWhenFinished" };
|
||||
const QString TEST_SNAPSHOT_LOCATION_COMMAND { "--testSnapshotLocation" };
|
||||
const QString TEST_SCRIPT_COMMAND{ "--testScript" };
|
||||
const QString TEST_QUIT_WHEN_FINISHED_OPTION{ "quitWhenFinished" };
|
||||
const QString TEST_RESULTS_LOCATION_COMMAND{ "--testResultsLocation" };
|
||||
|
||||
bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
|
||||
const char** constArgv = const_cast<const char**>(argv);
|
||||
|
@ -1015,22 +1015,25 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
|
||||
// If the URL scheme is http(s) or ftp, then use as is, else - treat it as a local file
|
||||
// This is done so as not break previous command line scripts
|
||||
if (testScriptPath.left(URL_SCHEME_HTTP.length()) == URL_SCHEME_HTTP || testScriptPath.left(URL_SCHEME_FTP.length()) == URL_SCHEME_FTP) {
|
||||
if (testScriptPath.left(URL_SCHEME_HTTP.length()) == URL_SCHEME_HTTP ||
|
||||
testScriptPath.left(URL_SCHEME_FTP.length()) == URL_SCHEME_FTP) {
|
||||
|
||||
setProperty(hifi::properties::TEST, QUrl::fromUserInput(testScriptPath));
|
||||
} else if (QFileInfo(testScriptPath).exists()) {
|
||||
setProperty(hifi::properties::TEST, QUrl::fromLocalFile(testScriptPath));
|
||||
}
|
||||
|
||||
// quite when finished parameter must directly follow the test script
|
||||
// quite when finished parameter must directly follow the test script
|
||||
if ((i + 2) < args.size() && args.at(i + 2) == TEST_QUIT_WHEN_FINISHED_OPTION) {
|
||||
quitWhenFinished = true;
|
||||
}
|
||||
} else if (args.at(i) == TEST_SNAPSHOT_LOCATION_COMMAND) {
|
||||
} else if (args.at(i) == TEST_RESULTS_LOCATION_COMMAND) {
|
||||
// Set test snapshot location only if it is a writeable directory
|
||||
QString pathname(args.at(i + 1));
|
||||
QFileInfo fileInfo(pathname);
|
||||
QString path(args.at(i + 1));
|
||||
|
||||
QFileInfo fileInfo(path);
|
||||
if (fileInfo.isDir() && fileInfo.isWritable()) {
|
||||
testSnapshotLocation = pathname;
|
||||
TestScriptingInterface::getInstance()->setTestResultsLocation(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7588,7 +7591,9 @@ void Application::loadAvatarBrowser() const {
|
|||
void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio, const QString& filename) {
|
||||
postLambdaEvent([notify, includeAnimated, aspectRatio, filename, this] {
|
||||
// Get a screenshot and save it
|
||||
QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio), filename, testSnapshotLocation);
|
||||
QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio), filename,
|
||||
TestScriptingInterface::getInstance()->getTestResultsLocation());
|
||||
|
||||
// If we're not doing an animated snapshot as well...
|
||||
if (!includeAnimated) {
|
||||
// Tell the dependency manager that the capture of the still snapshot has taken place.
|
||||
|
@ -7602,7 +7607,9 @@ void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRa
|
|||
|
||||
void Application::takeSecondaryCameraSnapshot(const QString& filename) {
|
||||
postLambdaEvent([filename, this] {
|
||||
QString snapshotPath = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot(), filename, testSnapshotLocation);
|
||||
QString snapshotPath = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot(), filename,
|
||||
TestScriptingInterface::getInstance()->getTestResultsLocation());
|
||||
|
||||
emit DependencyManager::get<WindowScriptingInterface>()->stillSnapshotTaken(snapshotPath, true);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -419,7 +419,6 @@ public slots:
|
|||
void updateVerboseLogging();
|
||||
Q_INVOKABLE void openAndroidActivity(const QString& activityName);
|
||||
|
||||
|
||||
private slots:
|
||||
void onDesktopRootItemCreated(QQuickItem* qmlContext);
|
||||
void onDesktopRootContextCreated(QQmlContext* qmlContext);
|
||||
|
@ -753,7 +752,6 @@ private:
|
|||
std::atomic<bool> _pendingIdleEvent { true };
|
||||
std::atomic<bool> _pendingRenderEvent { true };
|
||||
|
||||
QString testSnapshotLocation;
|
||||
bool quitWhenFinished { false };
|
||||
};
|
||||
#endif // hifi_Application_h
|
||||
|
|
|
@ -70,7 +70,7 @@ void LODManager::autoAdjustLOD(float realTimeDelta) {
|
|||
// Note: we MUST clamp the blend to 1.0 for stability
|
||||
float blend = (realTimeDelta < LOD_ADJUST_RUNNING_AVG_TIMESCALE) ? realTimeDelta / LOD_ADJUST_RUNNING_AVG_TIMESCALE : 1.0f;
|
||||
_avgRenderTime = (1.0f - blend) * _avgRenderTime + blend * maxRenderTime; // msec
|
||||
if (!_automaticLODAdjust) {
|
||||
if (!_automaticLODAdjust || _avgRenderTime == 0.0f) {
|
||||
// early exit
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -193,7 +193,6 @@ namespace MenuOption {
|
|||
const QString ShowOtherLookAtVectors = "Show Other Eye Vectors";
|
||||
const QString EnableLookAtSnapping = "Enable LookAt Snapping";
|
||||
const QString ShowRealtimeEntityStats = "Show Realtime Entity Stats";
|
||||
const QString StandingHMDSensorMode = "Standing HMD Sensor Mode";
|
||||
const QString SimulateEyeTracking = "Simulate";
|
||||
const QString SMIEyeTracking = "SMI Eye Tracking";
|
||||
const QString SparseTextureManagement = "Enable Sparse Texture Management";
|
||||
|
|
|
@ -29,10 +29,25 @@
|
|||
|
||||
/**jsdoc
|
||||
* The AvatarManager API has properties and methods which manage Avatars within the same domain.
|
||||
*
|
||||
* <p><strong>Note:</strong> This API is also provided to Interface and client entity scripts as the synonym,
|
||||
* <code>AvatarList</code>. For assignment client scripts, see the separate {@link AvatarList} API.
|
||||
*
|
||||
* @namespace AvatarManager
|
||||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
*
|
||||
* @borrows AvatarList.getAvatarIdentifiers as getAvatarIdentifiers
|
||||
* @borrows AvatarList.getAvatarsInRange as getAvatarsInRange
|
||||
* @borrows AvatarList.avatarAddedEvent as avatarAddedEvent
|
||||
* @borrows AvatarList.avatarRemovedEvent as avatarRemovedEvent
|
||||
* @borrows AvatarList.avatarSessionChangedEvent as avatarSessionChangedEvent
|
||||
* @borrows AvatarList.isAvatarInRange as isAvatarInRange
|
||||
* @borrows AvatarList.sessionUUIDChanged as sessionUUIDChanged
|
||||
* @borrows AvatarList.processAvatarDataPacket as processAvatarDataPacket
|
||||
* @borrows AvatarList.processAvatarIdentityPacket as processAvatarIdentityPacket
|
||||
* @borrows AvatarList.processKillAvatar as processKillAvatar
|
||||
*/
|
||||
|
||||
class AvatarManager : public AvatarHashMap {
|
||||
|
|
|
@ -137,9 +137,9 @@ class MyAvatar : public Avatar {
|
|||
* @property {number} scale
|
||||
* @property {number} density <em>Read-only.</em>
|
||||
* @property {Vec3} handPosition
|
||||
* @property {number} bodyYaw - The rotation left or right about an axis running from the head to the feet of MyAvatar. Yaw
|
||||
* is sometimes called "heading".
|
||||
* @property {number} bodyPitch - The rotation about an axis running from shoulder to shoulder of MyAvatar. Pitch is
|
||||
* @property {number} bodyYaw - The rotation left or right about an axis running from the head to the feet of the avatar.
|
||||
* Yaw is sometimes called "heading".
|
||||
* @property {number} bodyPitch - The rotation about an axis running from shoulder to shoulder of the avatar. Pitch is
|
||||
* sometimes called "elevation".
|
||||
* @property {number} bodyRoll - The rotation about an axis running from the chest to the back of the avatar. Roll is
|
||||
* sometimes called "bank".
|
||||
|
|
|
@ -20,24 +20,122 @@ class LaserPointerScriptingInterface : public QObject, public Dependency {
|
|||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
||||
/**jsdoc
|
||||
* Synonym for {@link Pointers} as used for laser pointers.
|
||||
*
|
||||
* @namespace LaserPointers
|
||||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
*/
|
||||
public:
|
||||
|
||||
/**jsdoc
|
||||
* @function LaserPointers.createLaserPointer
|
||||
* @param {Pointers.LaserPointerProperties} properties
|
||||
* @returns {number}
|
||||
*/
|
||||
Q_INVOKABLE unsigned int createLaserPointer(const QVariant& properties) const;
|
||||
|
||||
/**jsdoc
|
||||
* @function LaserPointers.enableLaserPointer
|
||||
* @param {number} id
|
||||
*/
|
||||
Q_INVOKABLE void enableLaserPointer(unsigned int uid) const { DependencyManager::get<PointerManager>()->enablePointer(uid); }
|
||||
|
||||
/**jsdoc
|
||||
* @function LaserPointers.disableLaserPointer
|
||||
* @param {number} id
|
||||
*/
|
||||
Q_INVOKABLE void disableLaserPointer(unsigned int uid) const { DependencyManager::get<PointerManager>()->disablePointer(uid); }
|
||||
|
||||
/**jsdoc
|
||||
* @function LaserPointers.removeLaserPointer
|
||||
* @param {number} id
|
||||
*/
|
||||
Q_INVOKABLE void removeLaserPointer(unsigned int uid) const { DependencyManager::get<PointerManager>()->removePointer(uid); }
|
||||
|
||||
/**jsdoc
|
||||
* @function LaserPointers.editRenderState
|
||||
* @param {number} id
|
||||
* @param {string} renderState
|
||||
* @param {Pointers.RayPointerRenderState} properties
|
||||
*/
|
||||
Q_INVOKABLE void editRenderState(unsigned int uid, const QString& renderState, const QVariant& properties) const;
|
||||
|
||||
/**jsdoc
|
||||
* @function LaserPointers.setRenderState
|
||||
* @param {string} renderState
|
||||
* @param {number} id
|
||||
*/
|
||||
Q_INVOKABLE void setRenderState(unsigned int uid, const QString& renderState) const { DependencyManager::get<PointerManager>()->setRenderState(uid, renderState.toStdString()); }
|
||||
|
||||
/**jsdoc
|
||||
* @function LaserPointers.getPrevRayPickResult
|
||||
* @param {number} id
|
||||
* @returns {RayPickResult}
|
||||
*/
|
||||
Q_INVOKABLE QVariantMap getPrevRayPickResult(unsigned int uid) const;
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* @function LaserPointers.setPrecisionPicking
|
||||
* @param {number} id
|
||||
* @param {boolean} precisionPicking
|
||||
*/
|
||||
Q_INVOKABLE void setPrecisionPicking(unsigned int uid, bool precisionPicking) const { DependencyManager::get<PointerManager>()->setPrecisionPicking(uid, precisionPicking); }
|
||||
|
||||
/**jsdoc
|
||||
* @function LaserPointers.setLaserLength
|
||||
* @param {number} id
|
||||
* @param {number} laserLength
|
||||
*/
|
||||
Q_INVOKABLE void setLaserLength(unsigned int uid, float laserLength) const { DependencyManager::get<PointerManager>()->setLength(uid, laserLength); }
|
||||
|
||||
/**jsdoc
|
||||
* @function LaserPointers.setIgnoreItems
|
||||
* @param {number} id
|
||||
* @param {Uuid[]} ignoreItems
|
||||
*/
|
||||
Q_INVOKABLE void setIgnoreItems(unsigned int uid, const QScriptValue& ignoreEntities) const;
|
||||
|
||||
/**jsdoc
|
||||
* @function LaserPointers.setIncludeItems
|
||||
* @param {number} id
|
||||
* @param {Uuid[]} includeItems
|
||||
*/
|
||||
Q_INVOKABLE void setIncludeItems(unsigned int uid, const QScriptValue& includeEntities) const;
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* @function LaserPointers.setLockEndUUID
|
||||
* @param {number} id
|
||||
* @param {Uuid} itemID
|
||||
* @param {boolean} isOverlay
|
||||
* @param {Mat4} [offsetMat]
|
||||
*/
|
||||
Q_INVOKABLE void setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isOverlay, const glm::mat4& offsetMat = glm::mat4()) const { DependencyManager::get<PointerManager>()->setLockEndUUID(uid, objectID, isOverlay, offsetMat); }
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* @function LaserPointers.isLeftHand
|
||||
* @param {number} id
|
||||
* @returns {boolean}
|
||||
*/
|
||||
Q_INVOKABLE bool isLeftHand(unsigned int uid) { return DependencyManager::get<PointerManager>()->isLeftHand(uid); }
|
||||
|
||||
/**jsdoc
|
||||
* @function LaserPointers.isRightHand
|
||||
* @param {number} id
|
||||
* @returns {boolean}
|
||||
*/
|
||||
Q_INVOKABLE bool isRightHand(unsigned int uid) { return DependencyManager::get<PointerManager>()->isRightHand(uid); }
|
||||
|
||||
/**jsdoc
|
||||
* @function LaserPointers.isMouse
|
||||
* @param {number} id
|
||||
* @returns {boolean}
|
||||
*/
|
||||
Q_INVOKABLE bool isMouse(unsigned int uid) { return DependencyManager::get<PointerManager>()->isMouse(uid); }
|
||||
};
|
||||
|
||||
|
|
|
@ -31,6 +31,20 @@ unsigned int PickScriptingInterface::createPick(const PickQuery::PickType type,
|
|||
}
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* A set of properties that can be passed to {@link Picks.createPick} to create a new Ray Pick.
|
||||
* @typedef {object} Picks.RayPickProperties
|
||||
* @property {boolean} [enabled=false] If this Pick should start enabled or not. Disabled Picks do not updated their pick results.
|
||||
* @property {number} [filter=Picks.PICK_NOTHING] The filter for this Pick to use, constructed using filter flags combined using bitwise OR.
|
||||
* @property {float} [maxDistance=0.0] The max distance at which this Pick will intersect. 0.0 = no max. < 0.0 is invalid.
|
||||
* @property {string} [joint] Only for Joint or Mouse Ray Picks. If "Mouse", it will create a Ray Pick that follows the system mouse, in desktop or HMD.
|
||||
* If "Avatar", it will create a Joint Ray Pick that follows your avatar's head. Otherwise, it will create a Joint Ray Pick that follows the given joint, if it
|
||||
* exists on your current avatar.
|
||||
* @property {Vec3} [posOffset=Vec3.ZERO] Only for Joint Ray Picks. A local joint position offset, in meters. x = upward, y = forward, z = lateral
|
||||
* @property {Vec3} [dirOffset=Vec3.UP] Only for Joint Ray Picks. A local joint direction offset. x = upward, y = forward, z = lateral
|
||||
* @property {Vec3} [position] Only for Static Ray Picks. The world-space origin of the ray.
|
||||
* @property {Vec3} [direction=-Vec3.UP] Only for Static Ray Picks. The world-space direction of the ray.
|
||||
*/
|
||||
unsigned int PickScriptingInterface::createRayPick(const QVariant& properties) {
|
||||
QVariantMap propMap = properties.toMap();
|
||||
|
||||
|
@ -83,6 +97,14 @@ unsigned int PickScriptingInterface::createRayPick(const QVariant& properties) {
|
|||
return PickManager::INVALID_PICK_ID;
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* A set of properties that can be passed to {@link Picks.createPick} to create a new Stylus Pick.
|
||||
* @typedef {object} Picks.StylusPickProperties
|
||||
* @property {number} [hand=-1] An integer. 0 == left, 1 == right. Invalid otherwise.
|
||||
* @property {boolean} [enabled=false] If this Pick should start enabled or not. Disabled Picks do not updated their pick results.
|
||||
* @property {number} [filter=Picks.PICK_NOTHING] The filter for this Pick to use, constructed using filter flags combined using bitwise OR.
|
||||
* @property {float} [maxDistance=0.0] The max distance at which this Pick will intersect. 0.0 = no max. < 0.0 is invalid.
|
||||
*/
|
||||
unsigned int PickScriptingInterface::createStylusPick(const QVariant& properties) {
|
||||
QVariantMap propMap = properties.toMap();
|
||||
|
||||
|
|
|
@ -22,19 +22,22 @@
|
|||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
*
|
||||
* @property PICK_NOTHING {number} A filter flag. Don't intersect with anything.
|
||||
* @property PICK_ENTITIES {number} A filter flag. Include entities when intersecting.
|
||||
* @property PICK_OVERLAYS {number} A filter flag. Include overlays when intersecting.
|
||||
* @property PICK_AVATARS {number} A filter flag. Include avatars when intersecting.
|
||||
* @property PICK_HUD {number} A filter flag. Include the HUD sphere when intersecting in HMD mode.
|
||||
* @property PICK_COARSE {number} A filter flag. Pick against coarse meshes, instead of exact meshes.
|
||||
* @property PICK_INCLUDE_INVISIBLE {number} A filter flag. Include invisible objects when intersecting.
|
||||
* @property PICK_INCLUDE_NONCOLLIDABLE {number} A filter flag. Include non-collidable objects when intersecting.
|
||||
* @property INTERSECTED_NONE {number} An intersection type. Intersected nothing with the given filter flags.
|
||||
* @property INTERSECTED_ENTITY {number} An intersection type. Intersected an entity.
|
||||
* @property INTERSECTED_OVERLAY {number} An intersection type. Intersected an overlay.
|
||||
* @property INTERSECTED_AVATAR {number} An intersection type. Intersected an avatar.
|
||||
* @property INTERSECTED_HUD {number} An intersection type. Intersected the HUD sphere.
|
||||
* @property PICK_NOTHING {number} A filter flag. Don't intersect with anything. <em>Read-only.</em>
|
||||
* @property PICK_ENTITIES {number} A filter flag. Include entities when intersecting. <em>Read-only.</em>
|
||||
* @property PICK_OVERLAYS {number} A filter flag. Include overlays when intersecting. <em>Read-only.</em>
|
||||
* @property PICK_AVATARS {number} A filter flag. Include avatars when intersecting. <em>Read-only.</em>
|
||||
* @property PICK_HUD {number} A filter flag. Include the HUD sphere when intersecting in HMD mode. <em>Read-only.</em>
|
||||
* @property PICK_COARSE {number} A filter flag. Pick against coarse meshes, instead of exact meshes. <em>Read-only.</em>
|
||||
* @property PICK_INCLUDE_INVISIBLE {number} A filter flag. Include invisible objects when intersecting. <em>Read-only.</em>
|
||||
* @property PICK_INCLUDE_NONCOLLIDABLE {number} A filter flag. Include non-collidable objects when intersecting.
|
||||
* <em>Read-only.</em>
|
||||
* @property PICK_ALL_INTERSECTIONS {number} <em>Read-only.</em>
|
||||
* @property INTERSECTED_NONE {number} An intersection type. Intersected nothing with the given filter flags. <em>Read-only.</em>
|
||||
* @property INTERSECTED_ENTITY {number} An intersection type. Intersected an entity. <em>Read-only.</em>
|
||||
* @property INTERSECTED_OVERLAY {number} An intersection type. Intersected an overlay. <em>Read-only.</em>
|
||||
* @property INTERSECTED_AVATAR {number} An intersection type. Intersected an avatar. <em>Read-only.</em>
|
||||
* @property INTERSECTED_HUD {number} An intersection type. Intersected the HUD sphere. <em>Read-only.</em>
|
||||
* @property {number} perFrameTimeBudget - The max number of usec to spend per frame updating Pick results. <em>Read-only.</em>
|
||||
*/
|
||||
|
||||
class PickScriptingInterface : public QObject, public Dependency {
|
||||
|
@ -61,46 +64,31 @@ public:
|
|||
|
||||
void registerMetaTypes(QScriptEngine* engine);
|
||||
|
||||
/**jsdoc
|
||||
* A set of properties that can be passed to {@link Picks.createPick} to create a new Pick.
|
||||
*
|
||||
* Different {@link Picks.PickType}s use different properties, and within one PickType, the properties you choose can lead to a wide range of behaviors. For example,
|
||||
* with PickType.Ray, depending on which optional parameters you pass, you could create a Static Ray Pick, a Mouse Ray Pick, or a Joint Ray Pick.
|
||||
*
|
||||
* @typedef {Object} Picks.PickProperties
|
||||
* @property {boolean} [enabled=false] If this Pick should start enabled or not. Disabled Picks do not updated their pick results.
|
||||
* @property {number} [filter=Picks.PICK_NOTHING] The filter for this Pick to use, constructed using filter flags combined using bitwise OR.
|
||||
* @property {float} [maxDistance=0.0] The max distance at which this Pick will intersect. 0.0 = no max. < 0.0 is invalid.
|
||||
* @property {string} [joint] Only for Joint or Mouse Ray Picks. If "Mouse", it will create a Ray Pick that follows the system mouse, in desktop or HMD.
|
||||
* If "Avatar", it will create a Joint Ray Pick that follows your avatar's head. Otherwise, it will create a Joint Ray Pick that follows the given joint, if it
|
||||
* exists on your current avatar.
|
||||
* @property {Vec3} [posOffset=Vec3.ZERO] Only for Joint Ray Picks. A local joint position offset, in meters. x = upward, y = forward, z = lateral
|
||||
* @property {Vec3} [dirOffset=Vec3.UP] Only for Joint Ray Picks. A local joint direction offset. x = upward, y = forward, z = lateral
|
||||
* @property {Vec3} [position] Only for Static Ray Picks. The world-space origin of the ray.
|
||||
* @property {Vec3} [direction=-Vec3.UP] Only for Static Ray Picks. The world-space direction of the ray.
|
||||
* @property {number} [hand=-1] Only for Stylus Picks. An integer. 0 == left, 1 == right. Invalid otherwise.
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* Adds a new Pick.
|
||||
* Different {@link PickType}s use different properties, and within one PickType, the properties you choose can lead to a wide range of behaviors. For example,
|
||||
* with PickType.Ray, depending on which optional parameters you pass, you could create a Static Ray Pick, a Mouse Ray Pick, or a Joint Ray Pick.
|
||||
* @function Picks.createPick
|
||||
* @param {Picks.PickType} type A PickType that specifies the method of picking to use
|
||||
* @param {Picks.PickProperties} properties A PickProperties object, containing all the properties for initializing this Pick
|
||||
* @param {PickType} type A PickType that specifies the method of picking to use
|
||||
* @param {Picks.RayPickProperties|Picks.StylusPickProperties} properties A PickProperties object, containing all the properties for initializing this Pick
|
||||
* @returns {number} The ID of the created Pick. Used for managing the Pick. 0 if invalid.
|
||||
*/
|
||||
Q_INVOKABLE unsigned int createPick(const PickQuery::PickType type, const QVariant& properties);
|
||||
|
||||
/**jsdoc
|
||||
* Enables a Pick.
|
||||
* @function Picks.enablePick
|
||||
* @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}.
|
||||
*/
|
||||
Q_INVOKABLE void enablePick(unsigned int uid);
|
||||
|
||||
/**jsdoc
|
||||
* Disables a Pick.
|
||||
* @function Picks.disablePick
|
||||
* @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}.
|
||||
*/
|
||||
Q_INVOKABLE void disablePick(unsigned int uid);
|
||||
|
||||
/**jsdoc
|
||||
* Removes a Pick.
|
||||
* @function Picks.removePick
|
||||
|
@ -111,7 +99,7 @@ public:
|
|||
/**jsdoc
|
||||
* An intersection result for a Ray Pick.
|
||||
*
|
||||
* @typedef {Object} Picks.RayPickResult
|
||||
* @typedef {Object} RayPickResult
|
||||
* @property {number} type The intersection type.
|
||||
* @property {boolean} intersects If there was a valid intersection (type != INTERSECTED_NONE)
|
||||
* @property {Uuid} objectID The ID of the intersected object. Uuid.NULL for the HUD or invalid intersections.
|
||||
|
@ -125,7 +113,7 @@ public:
|
|||
/**jsdoc
|
||||
* An intersection result for a Stylus Pick.
|
||||
*
|
||||
* @typedef {Object} Picks.StylusPickResult
|
||||
* @typedef {Object} StylusPickResult
|
||||
* @property {number} type The intersection type.
|
||||
* @property {boolean} intersects If there was a valid intersection (type != INTERSECTED_NONE)
|
||||
* @property {Uuid} objectID The ID of the intersected object. Uuid.NULL for the HUD or invalid intersections.
|
||||
|
@ -140,7 +128,7 @@ public:
|
|||
* Get the most recent pick result from this Pick. This will be updated as long as the Pick is enabled.
|
||||
* @function Picks.getPrevPickResult
|
||||
* @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}.
|
||||
* @returns {PickResult} The most recent intersection result. This will be slightly different for different PickTypes. See {@link Picks.RayPickResult} and {@link Picks.StylusPickResult}.
|
||||
* @returns {RayPickResult|StylusPickResult} The most recent intersection result. This will be different for different PickTypes.
|
||||
*/
|
||||
Q_INVOKABLE QVariantMap getPrevPickResult(unsigned int uid);
|
||||
|
||||
|
@ -151,6 +139,7 @@ public:
|
|||
* @param {boolean} precisionPicking Whether or not to use precision picking
|
||||
*/
|
||||
Q_INVOKABLE void setPrecisionPicking(unsigned int uid, bool precisionPicking);
|
||||
|
||||
/**jsdoc
|
||||
* Sets a list of Entity IDs, Overlay IDs, and/or Avatar IDs to ignore during intersection. Not used by Stylus Picks.
|
||||
* @function Picks.setIgnoreItems
|
||||
|
@ -158,6 +147,7 @@ public:
|
|||
* @param {Uuid[]} ignoreItems A list of IDs to ignore.
|
||||
*/
|
||||
Q_INVOKABLE void setIgnoreItems(unsigned int uid, const QScriptValue& ignoreItems);
|
||||
|
||||
/**jsdoc
|
||||
* Sets a list of Entity IDs, Overlay IDs, and/or Avatar IDs to include during intersection, instead of intersecting with everything. Stylus
|
||||
* Picks <b>only</b> intersect with objects in their include list.
|
||||
|
@ -174,6 +164,7 @@ public:
|
|||
* @returns {boolean} True if the Pick is a Joint Ray Pick with joint == "_CONTROLLER_LEFTHAND" or "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND", or a Stylus Pick with hand == 0.
|
||||
*/
|
||||
Q_INVOKABLE bool isLeftHand(unsigned int uid);
|
||||
|
||||
/**jsdoc
|
||||
* Check if a Pick is associated with the right hand.
|
||||
* @function Picks.isRightHand
|
||||
|
@ -181,6 +172,7 @@ public:
|
|||
* @returns {boolean} True if the Pick is a Joint Ray Pick with joint == "_CONTROLLER_RIGHTHAND" or "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND", or a Stylus Pick with hand == 1.
|
||||
*/
|
||||
Q_INVOKABLE bool isRightHand(unsigned int uid);
|
||||
|
||||
/**jsdoc
|
||||
* Check if a Pick is associated with the system mouse.
|
||||
* @function Picks.isMouse
|
||||
|
@ -189,28 +181,96 @@ public:
|
|||
*/
|
||||
Q_INVOKABLE bool isMouse(unsigned int uid);
|
||||
|
||||
// FIXME: Move to other property definitions.
|
||||
Q_PROPERTY(unsigned int perFrameTimeBudget READ getPerFrameTimeBudget WRITE setPerFrameTimeBudget)
|
||||
/**jsdoc
|
||||
* The max number of usec to spend per frame updating Pick results.
|
||||
* @typedef {number} Picks.perFrameTimeBudget
|
||||
*/
|
||||
|
||||
unsigned int getPerFrameTimeBudget() const;
|
||||
void setPerFrameTimeBudget(unsigned int numUsecs);
|
||||
|
||||
public slots:
|
||||
|
||||
/**jsdoc
|
||||
* @function Picks.PICK_NOTHING
|
||||
* @returns {number}
|
||||
*/
|
||||
static constexpr unsigned int PICK_NOTHING() { return 0; }
|
||||
|
||||
/**jsdoc
|
||||
* @function Picks.PICK_ENTITIES
|
||||
* @returns {number}
|
||||
*/
|
||||
static constexpr unsigned int PICK_ENTITIES() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_ENTITIES); }
|
||||
|
||||
/**jsdoc
|
||||
* @function Picks.PICK_OVERLAYS
|
||||
* @returns {number}
|
||||
*/
|
||||
static constexpr unsigned int PICK_OVERLAYS() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_OVERLAYS); }
|
||||
|
||||
/**jsdoc
|
||||
* @function Picks.PICK_AVATARS
|
||||
* @returns {number}
|
||||
*/
|
||||
static constexpr unsigned int PICK_AVATARS() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_AVATARS); }
|
||||
|
||||
/**jsdoc
|
||||
* @function Picks.PICK_HUD
|
||||
* @returns {number}
|
||||
*/
|
||||
static constexpr unsigned int PICK_HUD() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_HUD); }
|
||||
|
||||
/**jsdoc
|
||||
* @function Picks.PICK_COARSE
|
||||
* @returns {number}
|
||||
*/
|
||||
static constexpr unsigned int PICK_COARSE() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_COARSE); }
|
||||
|
||||
/**jsdoc
|
||||
* @function Picks.PICK_INCLUDE_INVISIBLE
|
||||
* @returns {number}
|
||||
*/
|
||||
static constexpr unsigned int PICK_INCLUDE_INVISIBLE() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_INCLUDE_INVISIBLE); }
|
||||
|
||||
/**jsdoc
|
||||
* @function Picks.PICK_INCLUDE_NONCOLLIDABLE
|
||||
* @returns {number}
|
||||
*/
|
||||
static constexpr unsigned int PICK_INCLUDE_NONCOLLIDABLE() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_INCLUDE_NONCOLLIDABLE); }
|
||||
|
||||
/**jsdoc
|
||||
* @function Picks.PICK_ALL_INTERSECTIONS
|
||||
* @returns {number}
|
||||
*/
|
||||
static constexpr unsigned int PICK_ALL_INTERSECTIONS() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_ALL_INTERSECTIONS); }
|
||||
|
||||
/**jsdoc
|
||||
* @function Picks.INTERSECTED_NONE
|
||||
* @returns {number}
|
||||
*/
|
||||
static constexpr unsigned int INTERSECTED_NONE() { return IntersectionType::NONE; }
|
||||
|
||||
/**jsdoc
|
||||
* @function Picks.INTERSECTED_ENTITY
|
||||
* @returns {number}
|
||||
*/
|
||||
static constexpr unsigned int INTERSECTED_ENTITY() { return IntersectionType::ENTITY; }
|
||||
|
||||
/**jsdoc
|
||||
* @function Picks.INTERSECTED_OVERLAY
|
||||
* @returns {number}
|
||||
*/
|
||||
static constexpr unsigned int INTERSECTED_OVERLAY() { return IntersectionType::OVERLAY; }
|
||||
|
||||
/**jsdoc
|
||||
* @function Picks.INTERSECTED_AVATAR
|
||||
* @returns {number}
|
||||
*/
|
||||
static constexpr unsigned int INTERSECTED_AVATAR() { return IntersectionType::AVATAR; }
|
||||
|
||||
/**jsdoc
|
||||
* @function Picks.INTERSECTED_HUD
|
||||
* @returns {number}
|
||||
*/
|
||||
static constexpr unsigned int INTERSECTED_HUD() { return IntersectionType::HUD; }
|
||||
};
|
||||
|
||||
|
|
|
@ -42,6 +42,12 @@ unsigned int PointerScriptingInterface::createPointer(const PickQuery::PickType&
|
|||
}
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* A set of properties that can be passed to {@link Pointers.createPointer} to create a new Pointer. Contains the relevant {@link Picks.PickProperties} to define the underlying Pick.
|
||||
* @typedef {object} Pointers.StylusPointerProperties
|
||||
* @property {boolean} [hover=false] If this pointer should generate hover events.
|
||||
* @property {boolean} [enabled=false]
|
||||
*/
|
||||
unsigned int PointerScriptingInterface::createStylus(const QVariant& properties) const {
|
||||
QVariantMap propertyMap = properties.toMap();
|
||||
|
||||
|
@ -58,6 +64,48 @@ unsigned int PointerScriptingInterface::createStylus(const QVariant& properties)
|
|||
return DependencyManager::get<PointerManager>()->addPointer(std::make_shared<StylusPointer>(properties, StylusPointer::buildStylusOverlay(propertyMap), hover, enabled));
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* A set of properties used to define the visual aspect of a Ray Pointer in the case that the Pointer is not intersecting something. Same as a {@link Pointers.RayPointerRenderState},
|
||||
* but with an additional distance field.
|
||||
*
|
||||
* @typedef {Object} Pointers.DefaultRayPointerRenderState
|
||||
* @augments Pointers.RayPointerRenderState
|
||||
* @property {number} distance The distance at which to render the end of this Ray Pointer, if one is defined.
|
||||
*/
|
||||
/**jsdoc
|
||||
* A set of properties used to define the visual aspect of a Ray Pointer in the case that the Pointer is intersecting something.
|
||||
*
|
||||
* @typedef {Object} Pointers.RayPointerRenderState
|
||||
* @property {string} name The name of this render state, used by {@link Pointers.setRenderState} and {@link Pointers.editRenderState}
|
||||
* @property {Overlays.OverlayProperties} [start] All of the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a <code>type</code> field).
|
||||
* An overlay to represent the beginning of the Ray Pointer, if desired.
|
||||
* @property {Overlays.OverlayProperties} [path] All of the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a <code>type</code> field), which <b>must</b> be <code>"line3d"</code>.
|
||||
* An overlay to represent the path of the Ray Pointer, if desired.
|
||||
* @property {Overlays.OverlayProperties} [end] All of the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a <code>type</code> field).
|
||||
* An overlay to represent the end of the Ray Pointer, if desired.
|
||||
*/
|
||||
/**jsdoc
|
||||
* A trigger mechanism for Ray Pointers.
|
||||
*
|
||||
* @typedef {Object} Pointers.Trigger
|
||||
* @property {Controller.Standard|Controller.Actions|function} action This can be a built-in Controller action, like Controller.Standard.LTClick, or a function that evaluates to >= 1.0 when you want to trigger <code>button</code>.
|
||||
* @property {string} button Which button to trigger. "Primary", "Secondary", "Tertiary", and "Focus" are currently supported. Only "Primary" will trigger clicks on web surfaces. If "Focus" is triggered,
|
||||
* it will try to set the entity or overlay focus to the object at which the Pointer is aimed. Buttons besides the first three will still trigger events, but event.button will be "None".
|
||||
*/
|
||||
/**jsdoc
|
||||
* A set of properties that can be passed to {@link Pointers.createPointer} to create a new Pointer. Contains the relevant {@link Picks.PickProperties} to define the underlying Pick.
|
||||
* @typedef {object} Pointers.LaserPointerProperties
|
||||
* @property {boolean} [faceAvatar=false] Ray Pointers only. If true, the end of the Pointer will always rotate to face the avatar.
|
||||
* @property {boolean} [centerEndY=true] Ray Pointers only. If false, the end of the Pointer will be moved up by half of its height.
|
||||
* @property {boolean} [lockEnd=false] Ray Pointers only. If true, the end of the Pointer will lock on to the center of the object at which the laser is pointing.
|
||||
* @property {boolean} [distanceScaleEnd=false] Ray Pointers only. If true, the dimensions of the end of the Pointer will scale linearly with distance.
|
||||
* @property {boolean} [scaleWithAvatar=false] Ray Pointers only. If true, the width of the Pointer's path will scale linearly with your avatar's scale.
|
||||
* @property {boolean} [enabled=false]
|
||||
* @property {Pointers.RayPointerRenderState[]} [renderStates] Ray Pointers only. A list of different visual states to switch between.
|
||||
* @property {Pointers.DefaultRayPointerRenderState[]} [defaultRenderStates] Ray Pointers only. A list of different visual states to use if there is no intersection.
|
||||
* @property {boolean} [hover=false] If this Pointer should generate hover events.
|
||||
* @property {Pointers.Trigger[]} [triggers] Ray Pointers only. A list of different triggers mechanisms that control this Pointer's click event generation.
|
||||
*/
|
||||
unsigned int PointerScriptingInterface::createLaserPointer(const QVariant& properties) const {
|
||||
QVariantMap propertyMap = properties.toMap();
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
/**jsdoc
|
||||
* The Pointers API lets you create and manage objects for repeatedly calculating intersections in different ways, as well as the visual representation of those objects.
|
||||
* Pointers can also be configured to automatically generate PointerEvents.
|
||||
* Pointers can also be configured to automatically generate {@link PointerEvent}s on {@link Entities} and {@link Overlays}.
|
||||
*
|
||||
* @namespace Pointers
|
||||
*
|
||||
|
@ -33,59 +33,12 @@ public:
|
|||
unsigned int createStylus(const QVariant& properties) const;
|
||||
|
||||
/**jsdoc
|
||||
* A set of properties that can be passed to {@link Pointers.createPointer} to create a new Pointer. Also contains the relevant {@link Picks.PickProperties} to define the underlying Pick.
|
||||
*
|
||||
* Adds a new Pointer
|
||||
* Different {@link PickType}s use different properties, and within one PickType, the properties you choose can lead to a wide range of behaviors. For example,
|
||||
* with PickType.Ray, depending on which optional parameters you pass, you could create a Static Ray Pointer, a Mouse Ray Pointer, or a Joint Ray Pointer.
|
||||
*
|
||||
* @typedef {Object} Pointers.PointerProperties
|
||||
* @property {boolean} [hover=false] If this Pointer should generate hover events.
|
||||
* @property {boolean} [faceAvatar=false] Ray Pointers only. If true, the end of the Pointer will always rotate to face the avatar.
|
||||
* @property {boolean} [centerEndY=true] Ray Pointers only. If false, the end of the Pointer will be moved up by half of its height.
|
||||
* @property {boolean} [lockEnd=false] Ray Pointers only. If true, the end of the Pointer will lock on to the center of the object at which the laser is pointing.
|
||||
* @property {boolean} [distanceScaleEnd=false] Ray Pointers only. If true, the dimensions of the end of the Pointer will scale linearly with distance.
|
||||
* @property {boolean} [scaleWithAvatar=false] Ray Pointers only. If true, the width of the Pointer's path will scale linearly with your avatar's scale.
|
||||
* @property {Pointers.RayPointerRenderState[]} [renderStates] Ray Pointers only. A list of different visual states to switch between.
|
||||
* @property {Pointers.DefaultRayPointerRenderState[]} [defaultRenderStates] Ray Pointers only. A list of different visual states to use if there is no intersection.
|
||||
* @property {Pointers.Trigger[]} [triggers] Ray Pointers only. A list of different triggers mechanisms that control this Pointer's click event generation.
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* A set of properties used to define the visual aspect of a Ray Pointer in the case that the Pointer is intersecting something.
|
||||
*
|
||||
* @typedef {Object} Pointers.RayPointerRenderState
|
||||
* @property {string} name The name of this render state, used by {@link Pointers.setRenderState} and {@link Pointers.editRenderState}
|
||||
* @property {OverlayProperties} [start] All of the properties you would normally pass to {@Overlays.addOverlay}, plus the type (as a <code>type</code> field).
|
||||
* An overlay to represent the beginning of the Ray Pointer, if desired.
|
||||
* @property {OverlayProperties} [path] All of the properties you would normally pass to {@Overlays.addOverlay}, plus the type (as a <code>type</code> field), which <b>must</b> be "line3d".
|
||||
* An overlay to represent the path of the Ray Pointer, if desired.
|
||||
* @property {OverlayProperties} [end] All of the properties you would normally pass to {@Overlays.addOverlay}, plus the type (as a <code>type</code> field).
|
||||
* An overlay to represent the end of the Ray Pointer, if desired.
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* A set of properties used to define the visual aspect of a Ray Pointer in the case that the Pointer is not intersecting something. Same as a {@link Pointers.RayPointerRenderState},
|
||||
* but with an additional distance field.
|
||||
*
|
||||
* @typedef {Object} Pointers.DefaultRayPointerRenderState
|
||||
* @augments Pointers.RayPointerRenderState
|
||||
* @property {number} distance The distance at which to render the end of this Ray Pointer, if one is defined.
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* A trigger mechanism for Ray Pointers.
|
||||
*
|
||||
* @typedef {Object} Pointers.Trigger
|
||||
* @property {Controller.Action} action This can be a built-in Controller action, like Controller.Standard.LTClick, or a function that evaluates to >= 1.0 when you want to trigger <code>button</code>.
|
||||
* @property {string} button Which button to trigger. "Primary", "Secondary", "Tertiary", and "Focus" are currently supported. Only "Primary" will trigger clicks on web surfaces. If "Focus" is triggered,
|
||||
* it will try to set the entity or overlay focus to the object at which the Pointer is aimed. Buttons besides the first three will still trigger events, but event.button will be "None".
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* Adds a new Pointer
|
||||
* @function Pointers.createPointer
|
||||
* @param {Picks.PickType} type A PickType that specifies the method of picking to use
|
||||
* @param {Pointers.PointerProperties} properties A PointerProperties object, containing all the properties for initializing this Pointer <b>and</b> the {@link Picks.PickProperties} for the Pick that
|
||||
* @param {PickType} type A PickType that specifies the method of picking to use
|
||||
* @param {Pointers.LaserPointerProperties|Pointers.StylusPointerProperties} properties A PointerProperties object, containing all the properties for initializing this Pointer <b>and</b> the {@link Picks.PickProperties} for the Pick that
|
||||
* this Pointer will use to do its picking.
|
||||
* @returns {number} The ID of the created Pointer. Used for managing the Pointer. 0 if invalid.
|
||||
*
|
||||
|
@ -121,32 +74,37 @@ public:
|
|||
* Pointers.setRenderState(pointer, "test");
|
||||
*/
|
||||
Q_INVOKABLE unsigned int createPointer(const PickQuery::PickType& type, const QVariant& properties);
|
||||
|
||||
/**jsdoc
|
||||
* Enables a Pointer.
|
||||
* @function Pointers.enablePointer
|
||||
* @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}.
|
||||
*/
|
||||
Q_INVOKABLE void enablePointer(unsigned int uid) const { DependencyManager::get<PointerManager>()->enablePointer(uid); }
|
||||
|
||||
/**jsdoc
|
||||
* Disables a Pointer.
|
||||
* @function Pointers.disablePointer
|
||||
* @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}.
|
||||
*/
|
||||
Q_INVOKABLE void disablePointer(unsigned int uid) const { DependencyManager::get<PointerManager>()->disablePointer(uid); }
|
||||
|
||||
/**jsdoc
|
||||
* Removes a Pointer.
|
||||
* @function Pointers.removePointer
|
||||
* @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}.
|
||||
*/
|
||||
Q_INVOKABLE void removePointer(unsigned int uid) const { DependencyManager::get<PointerManager>()->removePointer(uid); }
|
||||
|
||||
/**jsdoc
|
||||
* Edit some visual aspect of a Pointer. Currently only supported for Ray Pointers.
|
||||
* @function Pointers.editRenderState
|
||||
* @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}.
|
||||
* @param {string} renderState The name of the render state you want to edit.
|
||||
* @param {RenderState} properties The new properties for <code>renderState</code>. For Ray Pointers, a {@link Pointers.RayPointerRenderState}.
|
||||
* @param {Pointers.RayPointerRenderState} properties The new properties for <code>renderStates</code> item.
|
||||
*/
|
||||
Q_INVOKABLE void editRenderState(unsigned int uid, const QString& renderState, const QVariant& properties) const;
|
||||
|
||||
/**jsdoc
|
||||
* Set the render state of a Pointer. For Ray Pointers, this means switching between their {@link Pointers.RayPointerRenderState}s, or "" to turn off rendering and hover/trigger events.
|
||||
* For Stylus Pointers, there are three built-in options: "events on" (render and send events, the default), "events off" (render but don't send events), and "disabled" (don't render, don't send events).
|
||||
|
@ -156,14 +114,16 @@ public:
|
|||
*/
|
||||
Q_INVOKABLE void setRenderState(unsigned int uid, const QString& renderState) const { DependencyManager::get<PointerManager>()->setRenderState(uid, renderState.toStdString()); }
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* Get the most recent pick result from this Pointer. This will be updated as long as the Pointer is enabled, regardless of the render state.
|
||||
* @function Pointers.getPrevPickResult
|
||||
* @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}.
|
||||
* @returns {PickResult} The most recent intersection result. This will be slightly different for different PickTypes. See {@link Picks.RayPickResult} and {@link Picks.StylusPickResult}.
|
||||
* @returns {RayPickResult|StylusPickResult} The most recent intersection result. This will be slightly different for different PickTypes.
|
||||
*/
|
||||
Q_INVOKABLE QVariantMap getPrevPickResult(unsigned int uid) const;
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* Sets whether or not to use precision picking.
|
||||
* @function Pointers.setPrecisionPicking
|
||||
|
@ -171,6 +131,7 @@ public:
|
|||
* @param {boolean} precisionPicking Whether or not to use precision picking
|
||||
*/
|
||||
Q_INVOKABLE void setPrecisionPicking(unsigned int uid, bool precisionPicking) const { DependencyManager::get<PointerManager>()->setPrecisionPicking(uid, precisionPicking); }
|
||||
|
||||
/**jsdoc
|
||||
* Sets the length of this Pointer. No effect on Stylus Pointers.
|
||||
* @function Pointers.setLength
|
||||
|
@ -178,6 +139,7 @@ public:
|
|||
* @param {float} length The desired length of the Pointer.
|
||||
*/
|
||||
Q_INVOKABLE void setLength(unsigned int uid, float length) const { DependencyManager::get<PointerManager>()->setLength(uid, length); }
|
||||
|
||||
/**jsdoc
|
||||
* Sets a list of Entity IDs, Overlay IDs, and/or Avatar IDs to ignore during intersection. Not used by Stylus Pointers.
|
||||
* @function Pointers.setIgnoreItems
|
||||
|
@ -185,6 +147,7 @@ public:
|
|||
* @param {Uuid[]} ignoreItems A list of IDs to ignore.
|
||||
*/
|
||||
Q_INVOKABLE void setIgnoreItems(unsigned int uid, const QScriptValue& ignoreEntities) const;
|
||||
|
||||
/**jsdoc
|
||||
* Sets a list of Entity IDs, Overlay IDs, and/or Avatar IDs to include during intersection, instead of intersecting with everything. Stylus
|
||||
* Pointers <b>only</b> intersect with objects in their include list.
|
||||
|
@ -194,17 +157,19 @@ public:
|
|||
*/
|
||||
Q_INVOKABLE void setIncludeItems(unsigned int uid, const QScriptValue& includeEntities) const;
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* Lock a Pointer onto a specific object (overlay, entity, or avatar). Optionally, provide an offset in object-space, otherwise the Pointer will lock on to the center of the object.
|
||||
* Not used by Stylus Pointers.
|
||||
* @function Pointers.setLockEndUUID
|
||||
* @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}.
|
||||
* @param {QUuid} objectID The ID of the object to which to lock on.
|
||||
* @param {Uuid} objectID The ID of the object to which to lock on.
|
||||
* @param {boolean} isOverlay False for entities or avatars, true for overlays
|
||||
* @param {Mat4} [offsetMat] The offset matrix to use if you do not want to lock on to the center of the object.
|
||||
*/
|
||||
Q_INVOKABLE void setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isOverlay, const glm::mat4& offsetMat = glm::mat4()) const { DependencyManager::get<PointerManager>()->setLockEndUUID(uid, objectID, isOverlay, offsetMat); }
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* Check if a Pointer is associated with the left hand.
|
||||
* @function Pointers.isLeftHand
|
||||
|
@ -212,6 +177,7 @@ public:
|
|||
* @returns {boolean} True if the Pointer is a Joint Ray Pointer with joint == "_CONTROLLER_LEFTHAND" or "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND", or a Stylus Pointer with hand == 0
|
||||
*/
|
||||
Q_INVOKABLE bool isLeftHand(unsigned int uid) { return DependencyManager::get<PointerManager>()->isLeftHand(uid); }
|
||||
|
||||
/**jsdoc
|
||||
* Check if a Pointer is associated with the right hand.
|
||||
* @function Pointers.isRightHand
|
||||
|
@ -219,6 +185,7 @@ public:
|
|||
* @returns {boolean} True if the Pointer is a Joint Ray Pointer with joint == "_CONTROLLER_RIGHTHAND" or "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND", or a Stylus Pointer with hand == 1
|
||||
*/
|
||||
Q_INVOKABLE bool isRightHand(unsigned int uid) { return DependencyManager::get<PointerManager>()->isRightHand(uid); }
|
||||
|
||||
/**jsdoc
|
||||
* Check if a Pointer is associated with the system mouse.
|
||||
* @function Pointers.isMouse
|
||||
|
|
|
@ -18,6 +18,30 @@
|
|||
|
||||
#include "PickScriptingInterface.h"
|
||||
|
||||
/**jsdoc
|
||||
* Synonym for {@link Picks} as used for ray picks.
|
||||
*
|
||||
* @namespace RayPick
|
||||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
*
|
||||
* @property {number} PICK_NOTHING <em>Read-only.</em>
|
||||
* @property {number} PICK_ENTITIES <em>Read-only.</em>
|
||||
* @property {number} PICK_OVERLAYS <em>Read-only.</em>
|
||||
* @property {number} PICK_AVATARS <em>Read-only.</em>
|
||||
* @property {number} PICK_HUD <em>Read-only.</em>
|
||||
* @property {number} PICK_COARSE <em>Read-only.</em>
|
||||
* @property {number} PICK_INCLUDE_INVISIBLE <em>Read-only.</em>
|
||||
* @property {number} PICK_INCLUDE_NONCOLLIDABLE <em>Read-only.</em>
|
||||
* @property {number} PICK_ALL_INTERSECTIONS <em>Read-only.</em>
|
||||
* @property {number} INTERSECTED_NONE <em>Read-only.</em>
|
||||
* @property {number} INTERSECTED_ENTITY <em>Read-only.</em>
|
||||
* @property {number} INTERSECTED_OVERLAY <em>Read-only.</em>
|
||||
* @property {number} INTERSECTED_AVATAR <em>Read-only.</em>
|
||||
* @property {number} INTERSECTED_HUD <em>Read-only.</em>
|
||||
*/
|
||||
|
||||
class RayPickScriptingInterface : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(unsigned int PICK_NOTHING READ PICK_NOTHING CONSTANT)
|
||||
|
@ -37,34 +61,167 @@ class RayPickScriptingInterface : public QObject, public Dependency {
|
|||
SINGLETON_DEPENDENCY
|
||||
|
||||
public:
|
||||
|
||||
/**jsdoc
|
||||
* @function RayPick.createRayPick
|
||||
* @param {Picks.RayPickProperties}
|
||||
* @returns {number}
|
||||
*/
|
||||
Q_INVOKABLE unsigned int createRayPick(const QVariant& properties);
|
||||
|
||||
/**jsdoc
|
||||
* @function RayPick.enableRayPick
|
||||
* @param {number} id
|
||||
*/
|
||||
Q_INVOKABLE void enableRayPick(unsigned int uid);
|
||||
|
||||
/**jsdoc
|
||||
* @function RayPick.disableRayPick
|
||||
* @param {number} id
|
||||
*/
|
||||
Q_INVOKABLE void disableRayPick(unsigned int uid);
|
||||
|
||||
/**jsdoc
|
||||
* @function RayPick.removeRayPick
|
||||
* @param {number} id
|
||||
*/
|
||||
Q_INVOKABLE void removeRayPick(unsigned int uid);
|
||||
|
||||
/**jsdoc
|
||||
* @function RayPick.getPrevRayPickResult
|
||||
* @param {number} id
|
||||
* @returns {RayPickResult}
|
||||
*/
|
||||
Q_INVOKABLE QVariantMap getPrevRayPickResult(unsigned int uid);
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* @function RayPick.setPrecisionPicking
|
||||
* @param {number} id
|
||||
* @param {boolean} precisionPicking
|
||||
*/
|
||||
Q_INVOKABLE void setPrecisionPicking(unsigned int uid, bool precisionPicking);
|
||||
|
||||
/**jsdoc
|
||||
* @function RayPick.setIgnoreItems
|
||||
* @param {number} id
|
||||
* @param {Uuid[]) ignoreEntities
|
||||
*/
|
||||
Q_INVOKABLE void setIgnoreItems(unsigned int uid, const QScriptValue& ignoreEntities);
|
||||
|
||||
/**jsdoc
|
||||
* @function RayPick.setIncludeItems
|
||||
* @param {number} id
|
||||
* @param {Uuid[]) includeEntities
|
||||
*/
|
||||
Q_INVOKABLE void setIncludeItems(unsigned int uid, const QScriptValue& includeEntities);
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* @function RayPick.isLeftHand
|
||||
* @param {number} id
|
||||
* @returns {boolean}
|
||||
*/
|
||||
Q_INVOKABLE bool isLeftHand(unsigned int uid);
|
||||
|
||||
/**jsdoc
|
||||
* @function RayPick.isRightHand
|
||||
* @param {number} id
|
||||
* @returns {boolean}
|
||||
*/
|
||||
Q_INVOKABLE bool isRightHand(unsigned int uid);
|
||||
|
||||
/**jsdoc
|
||||
* @function RayPick.isMouse
|
||||
* @param {number} id
|
||||
* @returns {boolean}
|
||||
*/
|
||||
Q_INVOKABLE bool isMouse(unsigned int uid);
|
||||
|
||||
public slots:
|
||||
|
||||
/**jsdoc
|
||||
* @function RayPick.PICK_NOTHING
|
||||
* @returns {number}
|
||||
*/
|
||||
static unsigned int PICK_NOTHING() { return PickScriptingInterface::PICK_NOTHING(); }
|
||||
|
||||
/**jsdoc
|
||||
* @function RayPick.PICK_ENTITIES
|
||||
* @returns {number}
|
||||
*/
|
||||
static unsigned int PICK_ENTITIES() { return PickScriptingInterface::PICK_ENTITIES(); }
|
||||
|
||||
/**jsdoc
|
||||
* @function RayPick.PICK_OVERLAYS
|
||||
* @returns {number}
|
||||
*/
|
||||
static unsigned int PICK_OVERLAYS() { return PickScriptingInterface::PICK_OVERLAYS(); }
|
||||
|
||||
/**jsdoc
|
||||
* @function RayPick.PICK_AVATARS
|
||||
* @returns {number}
|
||||
*/
|
||||
static unsigned int PICK_AVATARS() { return PickScriptingInterface::PICK_AVATARS(); }
|
||||
|
||||
/**jsdoc
|
||||
* @function RayPick.PICK_HUD
|
||||
* @returns {number}
|
||||
*/
|
||||
static unsigned int PICK_HUD() { return PickScriptingInterface::PICK_HUD(); }
|
||||
|
||||
/**jsdoc
|
||||
* @function RayPick.PICK_COARSE
|
||||
* @returns {number}
|
||||
*/
|
||||
static unsigned int PICK_COARSE() { return PickScriptingInterface::PICK_COARSE(); }
|
||||
|
||||
/**jsdoc
|
||||
* @function RayPick.PICK_INCLUDE_INVISIBLE
|
||||
* @returns {number}
|
||||
*/
|
||||
static unsigned int PICK_INCLUDE_INVISIBLE() { return PickScriptingInterface::PICK_INCLUDE_INVISIBLE(); }
|
||||
|
||||
/**jsdoc
|
||||
* @function RayPick.PICK_INCLUDE_NONCOLLIDABLE
|
||||
* @returns {number}
|
||||
*/
|
||||
static unsigned int PICK_INCLUDE_NONCOLLIDABLE() { return PickScriptingInterface::PICK_INCLUDE_NONCOLLIDABLE(); }
|
||||
|
||||
/**jsdoc
|
||||
* @function RayPick.PICK_ALL_INTERSECTIONS
|
||||
* @returns {number}
|
||||
*/
|
||||
static unsigned int PICK_ALL_INTERSECTIONS() { return PickScriptingInterface::PICK_ALL_INTERSECTIONS(); }
|
||||
|
||||
/**jsdoc
|
||||
* @function RayPick.INTERSECTED_NONE
|
||||
* @returns {number}
|
||||
*/
|
||||
static unsigned int INTERSECTED_NONE() { return PickScriptingInterface::INTERSECTED_NONE(); }
|
||||
|
||||
/**jsdoc
|
||||
* @function RayPick.INTERSECTED_ENTITY
|
||||
* @returns {number}
|
||||
*/
|
||||
static unsigned int INTERSECTED_ENTITY() { return PickScriptingInterface::INTERSECTED_ENTITY(); }
|
||||
|
||||
/**jsdoc
|
||||
* @function RayPick.INTERSECTED_OVERLAY
|
||||
* @returns {number}
|
||||
*/
|
||||
static unsigned int INTERSECTED_OVERLAY() { return PickScriptingInterface::INTERSECTED_OVERLAY(); }
|
||||
|
||||
/**jsdoc
|
||||
* @function RayPick.INTERSECTED_AVATAR
|
||||
* @returns {number}
|
||||
*/
|
||||
static unsigned int INTERSECTED_AVATAR() { return PickScriptingInterface::INTERSECTED_AVATAR(); }
|
||||
|
||||
/**jsdoc
|
||||
* @function RayPick.INTERSECTED_HUD
|
||||
* @returns {number}
|
||||
*/
|
||||
static unsigned int INTERSECTED_HUD() { return PickScriptingInterface::INTERSECTED_HUD(); }
|
||||
};
|
||||
|
||||
|
|
|
@ -160,3 +160,29 @@ void TestScriptingInterface::clearCaches() {
|
|||
qApp->reloadResourceCaches();
|
||||
}
|
||||
|
||||
// Writes a JSON object from javascript to a file
|
||||
void TestScriptingInterface::saveObject(QVariant variant, const QString& filename) {
|
||||
if (_testResultsLocation.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonDocument jsonDocument;
|
||||
jsonDocument = QJsonDocument::fromVariant(variant);
|
||||
if (jsonDocument.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray jsonData = jsonDocument.toJson();
|
||||
|
||||
// Append trailing slash if needed
|
||||
if (_testResultsLocation.right(1) != "/") {
|
||||
_testResultsLocation += "/";
|
||||
}
|
||||
|
||||
QString filepath = QDir::cleanPath(_testResultsLocation + filename);
|
||||
QFile file(filepath);
|
||||
|
||||
file.open(QFile::WriteOnly);
|
||||
file.write(jsonData);
|
||||
file.close();
|
||||
}
|
||||
|
|
|
@ -18,6 +18,10 @@ class QScriptValue;
|
|||
class TestScriptingInterface : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
void setTestResultsLocation(const QString path) { _testResultsLocation = path; }
|
||||
const QString& getTestResultsLocation() { return _testResultsLocation; };
|
||||
|
||||
public slots:
|
||||
static TestScriptingInterface* getInstance();
|
||||
|
||||
|
@ -46,7 +50,6 @@ public slots:
|
|||
*/
|
||||
void waitIdle();
|
||||
|
||||
|
||||
bool waitForConnection(qint64 maxWaitMs = 10000);
|
||||
|
||||
void wait(int milliseconds);
|
||||
|
@ -83,8 +86,14 @@ public slots:
|
|||
*/
|
||||
void clearCaches();
|
||||
|
||||
/**jsdoc
|
||||
* Save a JSON object to a file in the test results location
|
||||
*/
|
||||
void saveObject(QVariant v, const QString& filename);
|
||||
|
||||
private:
|
||||
bool waitForCondition(qint64 maxWaitMs, std::function<bool()> condition);
|
||||
QString _testResultsLocation;
|
||||
};
|
||||
|
||||
#endif // hifi_TestScriptingInterface_h
|
||||
#endif // hifi_TestScriptingInterface_h
|
||||
|
|
|
@ -2363,7 +2363,7 @@ glm::vec3 AvatarData::getAbsoluteJointTranslationInObjectFrame(int index) const
|
|||
}
|
||||
|
||||
/**jsdoc
|
||||
* @typedef MyAvatar.AttachmentData
|
||||
* @typedef AttachmentData
|
||||
* @property {string} modelUrl
|
||||
* @property {string} jointName
|
||||
* @property {Vec3} translation
|
||||
|
|
|
@ -351,7 +351,7 @@ public:
|
|||
class AvatarData : public QObject, public SpatiallyNestable {
|
||||
Q_OBJECT
|
||||
|
||||
// The following properties have JSDoc in MyAvatar.h.
|
||||
// The following properties have JSDoc in MyAvatar.h and ScriptableAvatar.h
|
||||
Q_PROPERTY(glm::vec3 position READ getWorldPosition WRITE setPositionViaScript)
|
||||
Q_PROPERTY(float scale READ getTargetScale WRITE setTargetScale)
|
||||
Q_PROPERTY(float density READ getDensity)
|
||||
|
@ -502,7 +502,7 @@ public:
|
|||
float getDomainLimitedScale() const;
|
||||
|
||||
/**jsdoc
|
||||
* returns the minimum scale allowed for this avatar in the current domain.
|
||||
* 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 MyAvatar.getDomainMinScale
|
||||
* @returns {number} minimum scale allowed for this avatar in the current domain.
|
||||
|
@ -510,14 +510,14 @@ public:
|
|||
Q_INVOKABLE float getDomainMinScale() const;
|
||||
|
||||
/**jsdoc
|
||||
* returns the maximum scale allowed for this avatar in the current domain.
|
||||
* 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 MyAvatar.getDomainMaxScale
|
||||
* @returns {number} maximum scale allowed for this avatar in the current domain.
|
||||
*/
|
||||
Q_INVOKABLE float getDomainMaxScale() const;
|
||||
|
||||
// returns eye height of avatar in meters, ignoreing avatar scale.
|
||||
// Returns eye height of avatar in meters, ignoring avatar scale.
|
||||
// if _targetScale is 1 then this will be identical to getEyeHeight;
|
||||
virtual float getUnscaledEyeHeight() const { return DEFAULT_AVATAR_EYE_HEIGHT; }
|
||||
|
||||
|
@ -775,7 +775,7 @@ public:
|
|||
* 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}.
|
||||
* returned by {@link MyAvatar.getJointNames} or {@link Avatar.getJointNames}.
|
||||
* @example <caption>Report the rotations of all your avatar's joints.</caption>
|
||||
* print(JSON.stringify(MyAvatar.getJointRotations()));
|
||||
*/
|
||||
|
@ -796,7 +796,7 @@ public:
|
|||
* 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}.
|
||||
* array returned by {@link MyAvatar.getJointNames} or {@link Avatar.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>
|
||||
|
@ -852,7 +852,7 @@ public:
|
|||
|
||||
/**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}.
|
||||
* {@link MyAvatar.getJointNames} or {@link Avatar.getJointNames}.
|
||||
* @function MyAvatar.getJointIndex
|
||||
* @param {string} name - The name of the joint.
|
||||
* @returns {number} The index of the joint.
|
||||
|
@ -952,7 +952,7 @@ public:
|
|||
/**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.
|
||||
* @returns {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++) {
|
||||
|
@ -963,10 +963,10 @@ public:
|
|||
|
||||
/**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
|
||||
* {@link MyAvatar.getAttachmentData} or {@link Avatar.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
|
||||
* @param {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";
|
||||
|
@ -989,7 +989,7 @@ public:
|
|||
* 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
|
||||
* @param {string} [jointName=""] - The name of the avatar joint (see {@link MyAvatar.getJointNames} or {@link Avatar.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.
|
||||
|
|
|
@ -30,6 +30,15 @@
|
|||
|
||||
#include "AvatarData.h"
|
||||
|
||||
/**jsdoc
|
||||
* <strong>Note:</strong> An <code>AvatarList</code> API is also provided for Interface and client entity scripts: it is a
|
||||
* synonym for the {@link AvatarManager} API.
|
||||
*
|
||||
* @namespace AvatarList
|
||||
*
|
||||
* @hifi-assignment-client
|
||||
*/
|
||||
|
||||
class AvatarHashMap : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
@ -42,20 +51,24 @@ public:
|
|||
// Currently, your own avatar will be included as the null avatar id.
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarManager.getAvatarIdentifiers
|
||||
* @function AvatarList.getAvatarIdentifiers
|
||||
* @returns {Uuid[]}
|
||||
*/
|
||||
Q_INVOKABLE QVector<QUuid> getAvatarIdentifiers();
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarManager.getAvatarsInRange
|
||||
* @function AvatarList.getAvatarsInRange
|
||||
* @param {Vec3} position
|
||||
* @param {number} range
|
||||
* @returns {Uuid[]}
|
||||
*/
|
||||
Q_INVOKABLE QVector<QUuid> getAvatarsInRange(const glm::vec3& position, float rangeMeters) const;
|
||||
|
||||
// No JSDod because it's documwented in AvatarManager.
|
||||
/**jsdoc
|
||||
* @function AvatarList.getAvatar
|
||||
* @param {Uuid} avatarID
|
||||
* @returns {AvatarData}
|
||||
*/
|
||||
// Null/Default-constructed QUuids will return MyAvatar
|
||||
Q_INVOKABLE virtual ScriptAvatarData* getAvatar(QUuid avatarID) { return new ScriptAvatarData(getAvatarBySessionID(avatarID)); }
|
||||
|
||||
|
@ -65,21 +78,21 @@ public:
|
|||
signals:
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarManager.avatarAddedEvent
|
||||
* @function AvatarList.avatarAddedEvent
|
||||
* @param {Uuid} sessionUUID
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void avatarAddedEvent(const QUuid& sessionUUID);
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarManager.avatarRemovedEvent
|
||||
* @function AvatarList.avatarRemovedEvent
|
||||
* @param {Uuid} sessionUUID
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void avatarRemovedEvent(const QUuid& sessionUUID);
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarManager.avatarSessionChangedEvent
|
||||
* @function AvatarList.avatarSessionChangedEvent
|
||||
* @param {Uuid} sessionUUID
|
||||
* @param {Uuid} oldSessionUUID
|
||||
* @returns {Signal}
|
||||
|
@ -89,7 +102,7 @@ signals:
|
|||
public slots:
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarManager.isAvatarInRange
|
||||
* @function AvatarList.isAvatarInRange
|
||||
* @param {string} position
|
||||
* @param {string} range
|
||||
* @returns {boolean}
|
||||
|
@ -99,28 +112,28 @@ public slots:
|
|||
protected slots:
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarManager.sessionUUIDChanged
|
||||
* @function AvatarList.sessionUUIDChanged
|
||||
* @param {Uuid} sessionUUID
|
||||
* @param {Uuid} oldSessionUUID
|
||||
*/
|
||||
void sessionUUIDChanged(const QUuid& sessionUUID, const QUuid& oldUUID);
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarManager.processAvatarDataPacket
|
||||
* @function AvatarList.processAvatarDataPacket
|
||||
* @param {} message
|
||||
* @param {} sendingNode
|
||||
*/
|
||||
void processAvatarDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarManager.processAvatarIdentityPacket
|
||||
* @function AvatarList.processAvatarIdentityPacket
|
||||
* @param {} message
|
||||
* @param {} sendingNode
|
||||
*/
|
||||
void processAvatarIdentityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarManager.processKillAvatar
|
||||
* @function AvatarList.processKillAvatar
|
||||
* @param {} message
|
||||
* @param {} sendingNode
|
||||
*/
|
||||
|
|
|
@ -32,6 +32,11 @@ const QString GET_PLACE = "/api/v1/places/%1";
|
|||
/**jsdoc
|
||||
* The location API provides facilities related to your current location in the metaverse.
|
||||
*
|
||||
* <h5>Getter/Setter</h5>
|
||||
* <p>You can get and set your current metaverse address by directly reading a string value from and writing a string value to
|
||||
* the <code>location</code> object. This is an alternative to using the <code>location.href</code> property or this object's
|
||||
* functions.</p>
|
||||
*
|
||||
* @namespace location
|
||||
*
|
||||
* @hifi-interface
|
||||
|
|
|
@ -86,7 +86,6 @@ private:
|
|||
/// Wrapper to expose resources to JS/QML
|
||||
class ScriptableResource : public QObject {
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* @constructor Resource
|
||||
*
|
||||
|
@ -98,12 +97,15 @@ class ScriptableResource : public QObject {
|
|||
* @property {string} url - URL of this resource.
|
||||
* @property {Resource.State} state - Current loading state.
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* @namespace Resource
|
||||
* @variation 0
|
||||
* @property {Resource.State} State
|
||||
*/
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QUrl url READ getURL)
|
||||
Q_PROPERTY(int state READ getState NOTIFY stateChanged)
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/**jsdoc
|
||||
|
@ -115,7 +117,6 @@ public:
|
|||
* @property {number} FINISHED - The resource has completely finished loading and is ready.
|
||||
* @property {number} FAILED - Downloading the resource has failed.
|
||||
*/
|
||||
|
||||
enum State {
|
||||
QUEUED,
|
||||
LOADING,
|
||||
|
|
|
@ -731,7 +731,9 @@ void EntityMotionState::measureBodyAcceleration() {
|
|||
// hence the equation for acceleration is: a = (v1 / (1 - D)^dt - v0) / dt
|
||||
glm::vec3 velocity = getBodyLinearVelocityGTSigma();
|
||||
|
||||
_measuredAcceleration = (velocity / powf(1.0f - _body->getLinearDamping(), dt) - _lastVelocity) * invDt;
|
||||
const float MIN_DAMPING_FACTOR = 0.01f;
|
||||
float invDampingAttenuationFactor = 1.0f / glm::max(powf(1.0f - _body->getLinearDamping(), dt), MIN_DAMPING_FACTOR);
|
||||
_measuredAcceleration = (velocity * invDampingAttenuationFactor - _lastVelocity) * invDt;
|
||||
_lastVelocity = velocity;
|
||||
if (numSubsteps > PHYSICS_ENGINE_MAX_NUM_SUBSTEPS) {
|
||||
// we fall in here when _lastMeasureStep is old: the body has just become active
|
||||
|
|
|
@ -135,15 +135,29 @@ public:
|
|||
PickQuery(const PickFilter& filter, const float maxDistance, const bool enabled);
|
||||
|
||||
/**jsdoc
|
||||
* @namespace
|
||||
* @augments Picks
|
||||
*
|
||||
* Enum for different types of Picks and Pointers.
|
||||
*
|
||||
* @typedef {enum} Picks.PickType
|
||||
* @namespace PickType
|
||||
* @variation 0
|
||||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
*
|
||||
* @property {number} Ray Ray Picks intersect a ray with the nearest object in front of them, along a given direction.
|
||||
* @property {number} Stylus Stylus Picks provide "tapping" functionality on/into flat surfaces.
|
||||
*/
|
||||
/**jsdoc
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Value</th><th>Description</th></tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td><code>{@link PickType(0)|PickType.Ray}</code></td><td></td></tr>
|
||||
* <tr><td><code>{@link PickType(0)|PickType.Stylus}</code></td><td></td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef {number} PickType
|
||||
*/
|
||||
enum PickType {
|
||||
Ray = 0,
|
||||
Stylus,
|
||||
|
|
|
@ -149,9 +149,7 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con
|
|||
batch.setStateScissorRect(viewport);
|
||||
|
||||
batch.setFramebuffer(fbo);
|
||||
batch.clearFramebuffer(
|
||||
gpu::Framebuffer::BUFFER_COLOR0 | gpu::Framebuffer::BUFFER_DEPTH,
|
||||
vec4(vec3(1.0, 1.0, 1.0), 0.0), 1.0, 0, true);
|
||||
batch.clearDepthFramebuffer(1.0, false);
|
||||
|
||||
glm::mat4 projMat;
|
||||
Transform viewMat;
|
||||
|
@ -232,12 +230,11 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende
|
|||
const auto queryResolution = setupOutput.getN<RenderShadowSetup::Outputs>(2);
|
||||
// Fetch and cull the items from the scene
|
||||
|
||||
// Enable models to not cast shadows (otherwise, models will always cast shadows)
|
||||
static const auto shadowCasterFilter = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(tagBits, tagMask).withShadowCaster();
|
||||
static const auto shadowCasterReceiverFilter = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(tagBits, tagMask);
|
||||
|
||||
const auto fetchInput = FetchSpatialTree::Inputs(shadowCasterFilter, queryResolution).asVarying();
|
||||
const auto fetchInput = FetchSpatialTree::Inputs(shadowCasterReceiverFilter, queryResolution).asVarying();
|
||||
const auto shadowSelection = task.addJob<FetchSpatialTree>("FetchShadowTree", fetchInput);
|
||||
const auto selectionInputs = FetchSpatialSelection::Inputs(shadowSelection, shadowCasterFilter).asVarying();
|
||||
const auto selectionInputs = FetchSpatialSelection::Inputs(shadowSelection, shadowCasterReceiverFilter).asVarying();
|
||||
const auto shadowItems = task.addJob<FetchSpatialSelection>("FetchShadowSelection", selectionInputs);
|
||||
|
||||
// Cull objects that are not visible in camera view. Hopefully the cull functor only performs LOD culling, not
|
||||
|
@ -261,21 +258,22 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende
|
|||
char jobName[64];
|
||||
sprintf(jobName, "ShadowCascadeSetup%d", i);
|
||||
const auto cascadeSetupOutput = task.addJob<RenderShadowCascadeSetup>(jobName, i, _cullFunctor, tagBits, tagMask);
|
||||
const auto shadowFilter = cascadeSetupOutput.getN<RenderShadowCascadeSetup::Outputs>(0);
|
||||
const auto shadowRenderFilter = cascadeSetupOutput.getN<RenderShadowCascadeSetup::Outputs>(0);
|
||||
const auto shadowBoundsFilter = cascadeSetupOutput.getN<RenderShadowCascadeSetup::Outputs>(1);
|
||||
auto antiFrustum = render::Varying(ViewFrustumPointer());
|
||||
cascadeFrustums[i] = cascadeSetupOutput.getN<RenderShadowCascadeSetup::Outputs>(1);
|
||||
cascadeFrustums[i] = cascadeSetupOutput.getN<RenderShadowCascadeSetup::Outputs>(2);
|
||||
if (i > 1) {
|
||||
antiFrustum = cascadeFrustums[i - 2];
|
||||
}
|
||||
|
||||
// CPU jobs: finer grained culling
|
||||
const auto cullInputs = CullShapeBounds::Inputs(sortedShapes, shadowFilter, antiFrustum).asVarying();
|
||||
const auto cullInputs = CullShapeBounds::Inputs(sortedShapes, shadowRenderFilter, shadowBoundsFilter, antiFrustum).asVarying();
|
||||
const auto culledShadowItemsAndBounds = task.addJob<CullShapeBounds>("CullShadowCascade", cullInputs, shadowCullFunctor, RenderDetails::SHADOW);
|
||||
|
||||
// GPU jobs: Render to shadow map
|
||||
sprintf(jobName, "RenderShadowMap%d", i);
|
||||
task.addJob<RenderShadowMap>(jobName, culledShadowItemsAndBounds, shapePlumber, i);
|
||||
task.addJob<RenderShadowCascadeTeardown>("ShadowCascadeTeardown", shadowFilter);
|
||||
task.addJob<RenderShadowCascadeTeardown>("ShadowCascadeTeardown", shadowRenderFilter);
|
||||
}
|
||||
|
||||
task.addJob<RenderShadowTeardown>("ShadowTeardown", setupOutput);
|
||||
|
@ -406,7 +404,11 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon
|
|||
|
||||
const auto globalShadow = lightStage->getCurrentKeyShadow();
|
||||
if (globalShadow && _cascadeIndex<globalShadow->getCascadeCount()) {
|
||||
output.edit0() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(_tagBits, _tagMask).withShadowCaster();
|
||||
auto baseFilter = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(_tagBits, _tagMask);
|
||||
// Second item filter is to filter items to keep in shadow frustum computation (here we need to keep shadow receivers)
|
||||
output.edit1() = baseFilter;
|
||||
// First item filter is to filter items to render in shadow map (so only keep casters)
|
||||
output.edit0() = baseFilter.withShadowCaster();
|
||||
|
||||
// Set the keylight render args
|
||||
auto& cascade = globalShadow->getCascade(_cascadeIndex);
|
||||
|
@ -419,10 +421,11 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon
|
|||
texelSize *= minTexelCount;
|
||||
_cullFunctor._minSquareSize = texelSize * texelSize;
|
||||
|
||||
output.edit1() = cascadeFrustum;
|
||||
output.edit2() = cascadeFrustum;
|
||||
} else {
|
||||
output.edit0() = ItemFilter::Builder::nothing();
|
||||
output.edit1() = ViewFrustumPointer();
|
||||
output.edit1() = ItemFilter::Builder::nothing();
|
||||
output.edit2() = ViewFrustumPointer();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -118,7 +118,7 @@ private:
|
|||
|
||||
class RenderShadowCascadeSetup {
|
||||
public:
|
||||
using Outputs = render::VaryingSet2<render::ItemFilter, ViewFrustumPointer>;
|
||||
using Outputs = render::VaryingSet3<render::ItemFilter, render::ItemFilter, ViewFrustumPointer>;
|
||||
using JobModel = render::Job::ModelO<RenderShadowCascadeSetup, Outputs>;
|
||||
|
||||
RenderShadowCascadeSetup(unsigned int cascadeIndex, RenderShadowTask::CullFunctor& cullFunctor, uint8_t tagBits = 0x00, uint8_t tagMask = 0x00) :
|
||||
|
|
|
@ -368,17 +368,19 @@ void CullShapeBounds::run(const RenderContextPointer& renderContext, const Input
|
|||
RenderArgs* args = renderContext->args;
|
||||
|
||||
const auto& inShapes = inputs.get0();
|
||||
const auto& filter = inputs.get1();
|
||||
const auto& antiFrustum = inputs.get2();
|
||||
const auto& cullFilter = inputs.get1();
|
||||
const auto& boundsFilter = inputs.get2();
|
||||
const auto& antiFrustum = inputs.get3();
|
||||
auto& outShapes = outputs.edit0();
|
||||
auto& outBounds = outputs.edit1();
|
||||
|
||||
outShapes.clear();
|
||||
outBounds = AABox();
|
||||
|
||||
if (!filter.selectsNothing()) {
|
||||
if (!cullFilter.selectsNothing() || !boundsFilter.selectsNothing()) {
|
||||
auto& details = args->_details.edit(_detailType);
|
||||
Test test(_cullFunctor, args, details, antiFrustum);
|
||||
auto scene = args->_scene;
|
||||
|
||||
for (auto& inItems : inShapes) {
|
||||
auto key = inItems.first;
|
||||
|
@ -393,16 +395,26 @@ void CullShapeBounds::run(const RenderContextPointer& renderContext, const Input
|
|||
if (antiFrustum == nullptr) {
|
||||
for (auto& item : inItems.second) {
|
||||
if (test.solidAngleTest(item.bound) && test.frustumTest(item.bound)) {
|
||||
outItems->second.emplace_back(item);
|
||||
outBounds += item.bound;
|
||||
const auto shapeKey = scene->getItem(item.id).getKey();
|
||||
if (cullFilter.test(shapeKey)) {
|
||||
outItems->second.emplace_back(item);
|
||||
}
|
||||
if (boundsFilter.test(shapeKey)) {
|
||||
outBounds += item.bound;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (auto& item : inItems.second) {
|
||||
if (test.solidAngleTest(item.bound) && test.frustumTest(item.bound) && test.antiFrustumTest(item.bound)) {
|
||||
outItems->second.emplace_back(item);
|
||||
outBounds += item.bound;
|
||||
}
|
||||
const auto shapeKey = scene->getItem(item.id).getKey();
|
||||
if (cullFilter.test(shapeKey)) {
|
||||
outItems->second.emplace_back(item);
|
||||
}
|
||||
if (boundsFilter.test(shapeKey)) {
|
||||
outBounds += item.bound;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
details._rendered += (int)outItems->second.size();
|
||||
|
|
|
@ -110,7 +110,7 @@ namespace render {
|
|||
|
||||
class CullShapeBounds {
|
||||
public:
|
||||
using Inputs = render::VaryingSet3<ShapeBounds, ItemFilter, ViewFrustumPointer>;
|
||||
using Inputs = render::VaryingSet4<ShapeBounds, ItemFilter, ItemFilter, ViewFrustumPointer>;
|
||||
using Outputs = render::VaryingSet2<ShapeBounds, AABox>;
|
||||
using JobModel = Job::ModelIO<CullShapeBounds, Inputs, Outputs>;
|
||||
|
||||
|
|
|
@ -852,7 +852,21 @@ AnimationDetails::AnimationDetails(QString role, QUrl url, float fps, float prio
|
|||
running(running), currentFrame(currentFrame), allowTranslation(allowTranslation) {
|
||||
}
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* @typedef {object} Avatar.AnimationDetails
|
||||
* @property {string} role
|
||||
* @property {string} url
|
||||
* @property {number} fps
|
||||
* @property {number} priority
|
||||
* @property {boolean} loop
|
||||
* @property {boolean} hold
|
||||
* @property {boolean} startAutomatically
|
||||
* @property {number} firstFrame
|
||||
* @property {number} lastFrame
|
||||
* @property {boolean} running
|
||||
* @property {number} currentFrame
|
||||
* @property {boolean} allowTranslation
|
||||
*/
|
||||
QScriptValue animationDetailsToScriptValue(QScriptEngine* engine, const AnimationDetails& details) {
|
||||
QScriptValue obj = engine->newObject();
|
||||
obj.setProperty("role", details.role);
|
||||
|
|
|
@ -44,6 +44,14 @@ class OffscreenQmlSurface;
|
|||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
*/
|
||||
/**jsdoc
|
||||
* @namespace tabletInterface
|
||||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
*
|
||||
* @deprecated This API is deprecated and will be removed. Use {@link Tablet} instead.
|
||||
*/
|
||||
class TabletScriptingInterface : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
@ -83,6 +91,13 @@ public:
|
|||
* @param {string} name - Tablet name.
|
||||
* @returns {TabletProxy} Tablet instance.
|
||||
*/
|
||||
/**jsdoc
|
||||
* Creates or returns a new TabletProxy and returns it.
|
||||
* @function tabletInterface.getTablet
|
||||
* @param {string} name - Tablet name.
|
||||
* @returns {TabletProxy} Tablet instance.
|
||||
* @deprecated This function is deprecated and will be removed. Use {@link Tablet.getTablet} instead.
|
||||
*/
|
||||
Q_INVOKABLE TabletProxy* getTablet(const QString& tabletId);
|
||||
|
||||
void preloadSounds();
|
||||
|
@ -91,6 +106,11 @@ public:
|
|||
* @function Tablet.playSound
|
||||
* @param {Tablet.AudioEvents} sound
|
||||
*/
|
||||
/**jsdoc
|
||||
* @function tabletInterface.playSound
|
||||
* @param {Tablet.AudioEvents} sound
|
||||
* @deprecated This function is deprecated and will be removed. Use {@link Tablet.playSound} instead.
|
||||
*/
|
||||
Q_INVOKABLE void playSound(TabletAudioEvents aEvent);
|
||||
|
||||
void setToolbarMode(bool toolbarMode);
|
||||
|
@ -108,6 +128,12 @@ signals:
|
|||
* @function Tablet.tabletNotification
|
||||
* @returns {Signal}
|
||||
*/
|
||||
/**jsdoc
|
||||
* Triggered when a tablet message or dialog is created.
|
||||
* @function tabletInterface.tabletNotification
|
||||
* @returns {Signal}
|
||||
* @deprecated This function is deprecated and will be removed. Use {@link Tablet.tabletNotification} instead.
|
||||
*/
|
||||
void tabletNotification();
|
||||
|
||||
private:
|
||||
|
|
|
@ -36,7 +36,6 @@
|
|||
|
||||
Q_DECLARE_LOGGING_CATEGORY(displayplugins)
|
||||
|
||||
const char* StandingHMDSensorMode { "Standing HMD Sensor Mode" }; // this probably shouldn't be hardcoded here
|
||||
const char* OpenVrThreadedSubmit { "OpenVR Threaded Submit" }; // this probably shouldn't be hardcoded here
|
||||
|
||||
PoseData _nextRenderPoseData;
|
||||
|
@ -451,7 +450,6 @@ bool OpenVrDisplayPlugin::internalActivate() {
|
|||
qDebug() << "OpenVR Threaded submit enabled: " << _threadedSubmit;
|
||||
|
||||
_openVrDisplayActive = true;
|
||||
_container->setIsOptionChecked(StandingHMDSensorMode, true);
|
||||
_system->GetRecommendedRenderTargetSize(&_renderTargetSize.x, &_renderTargetSize.y);
|
||||
// Recommended render target size is per-eye, so double the X size for
|
||||
// left + right eyes
|
||||
|
@ -507,7 +505,6 @@ void OpenVrDisplayPlugin::internalDeactivate() {
|
|||
Parent::internalDeactivate();
|
||||
|
||||
_openVrDisplayActive = false;
|
||||
_container->setIsOptionChecked(StandingHMDSensorMode, false);
|
||||
if (_system) {
|
||||
// TODO: Invalidate poses. It's fine if someone else sets these shared values, but we're about to stop updating them, and
|
||||
// we don't want ViveControllerManager to consider old values to be valid.
|
||||
|
|
|
@ -6,11 +6,11 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
|
||||
/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND,
|
||||
getControllerJointIndex, enableDispatcherModule, disableDispatcherModule,
|
||||
/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, Camera,
|
||||
getControllerJointIndex, enableDispatcherModule, disableDispatcherModule, entityIsFarGrabbedByOther,
|
||||
Messages, makeDispatcherModuleParameters, makeRunningValues, Settings, entityHasActions,
|
||||
Vec3, Overlays, flatten, Xform, getControllerWorldLocation, ensureDynamic, entityIsCloneable,
|
||||
cloneEntity, DISPATCHER_PROPERTIES, TEAR_AWAY_DISTANCE, Uuid, unhighlightTargetEntity
|
||||
cloneEntity, DISPATCHER_PROPERTIES, Uuid, unhighlightTargetEntity, isInEditMode
|
||||
*/
|
||||
|
||||
Script.include("/~/system/libraries/Xform.js");
|
||||
|
@ -781,7 +781,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var clearGrabActions = function(entityID) {
|
||||
var actionIDs = Entities.getActionIDs(entityID);
|
||||
var myGrabTag = "grab-" + MyAvatar.sessionUUID;
|
||||
|
@ -794,7 +794,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var onMousePress = function(event) {
|
||||
if (isInEditMode() || !event.isLeftButton) { // don't consider any left clicks on the entity while in edit
|
||||
return;
|
||||
|
@ -808,7 +808,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
|
|||
if (hasEquipData && entityProperties.parentID === EMPTY_PARENT_ID && !entityIsFarGrabbedByOther(entityID)) {
|
||||
entityProperties.id = entityID;
|
||||
var rightHandPosition = MyAvatar.getJointPosition("RightHand");
|
||||
var leftHandPosition = MyAvatar.getJointPosition("LeftHand");
|
||||
var leftHandPosition = MyAvatar.getJointPosition("LeftHand");
|
||||
var distanceToRightHand = Vec3.distance(entityProperties.position, rightHandPosition);
|
||||
var distanceToLeftHand = Vec3.distance(entityProperties.position, leftHandPosition);
|
||||
var leftHandAvailable = leftEquipEntity.targetEntityID === null;
|
||||
|
@ -828,7 +828,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
|
|||
};
|
||||
|
||||
var onKeyPress = function(event) {
|
||||
if (event.text === UNEQUIP_KEY) {
|
||||
if (event.text.toLowerCase() === UNEQUIP_KEY) {
|
||||
if (rightEquipEntity.targetEntityID) {
|
||||
rightEquipEntity.endEquipEntity();
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, entityIsGrabbable,
|
||||
Quat, Vec3, MSECS_PER_SEC, getControllerWorldLocation, makeDispatcherModuleParameters, makeRunningValues,
|
||||
TRIGGER_OFF_VALUE, NEAR_GRAB_RADIUS, findGroupParent, entityIsCloneable, propsAreCloneDynamic, cloneEntity,
|
||||
HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, unhighlightTargetEntity
|
||||
HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, unhighlightTargetEntity, Uuid
|
||||
*/
|
||||
|
||||
Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
||||
|
|
|
@ -7,12 +7,8 @@
|
|||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND,
|
||||
getControllerJointIndex, getGrabbableData, enableDispatcherModule, disableDispatcherModule,
|
||||
propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, entityIsGrabbable,
|
||||
Quat, Vec3, MSECS_PER_SEC, getControllerWorldLocation, makeDispatcherModuleParameters, makeRunningValues,
|
||||
TRIGGER_OFF_VALUE, NEAR_GRAB_RADIUS, findGroupParent, entityIsCloneable, propsAreCloneDynamic, cloneEntity,
|
||||
HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, AddressManager
|
||||
/* global Script, MyAvatar, RIGHT_HAND, LEFT_HAND, enableDispatcherModule, disableDispatcherModule,
|
||||
makeDispatcherModuleParameters, makeRunningValues, TRIGGER_OFF_VALUE, NEAR_GRAB_RADIUS, BUMPER_ON_VALUE, AddressManager
|
||||
*/
|
||||
|
||||
(function() {
|
||||
|
|
|
@ -11,8 +11,7 @@
|
|||
TRIGGER_OFF_VALUE, makeDispatcherModuleParameters, entityIsGrabbable, makeRunningValues, NEAR_GRAB_RADIUS,
|
||||
findGroupParent, Vec3, cloneEntity, entityIsCloneable, propsAreCloneDynamic, HAPTIC_PULSE_STRENGTH,
|
||||
HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, findHandChildEntities, TEAR_AWAY_DISTANCE, MSECS_PER_SEC, TEAR_AWAY_CHECK_TIME,
|
||||
TEAR_AWAY_COUNT, distanceBetweenPointAndEntityBoundingBox, print, Selection, DISPATCHER_HOVERING_LIST, Uuid,
|
||||
highlightTargetEntity, unhighlightTargetEntity
|
||||
TEAR_AWAY_COUNT, distanceBetweenPointAndEntityBoundingBox, print, Uuid, highlightTargetEntity, unhighlightTargetEntity
|
||||
*/
|
||||
|
||||
Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
||||
|
@ -43,11 +42,6 @@ Script.include("/~/system/libraries/cloneEntityUtils.js");
|
|||
[],
|
||||
100);
|
||||
|
||||
|
||||
// XXX does handJointIndex change if the avatar changes?
|
||||
this.handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand");
|
||||
this.controllerJointIndex = getControllerJointIndex(this.hand);
|
||||
|
||||
this.thisHandIsParent = function(props) {
|
||||
if (!props) {
|
||||
return false;
|
||||
|
@ -62,8 +56,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js");
|
|||
return true;
|
||||
}
|
||||
|
||||
var controllerJointIndex = this.controllerJointIndex;
|
||||
if (props.parentJointIndex === controllerJointIndex) {
|
||||
if (props.parentJointIndex === getControllerJointIndex(this.hand)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -102,7 +95,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js");
|
|||
// } else {
|
||||
// handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand");
|
||||
// }
|
||||
handJointIndex = this.controllerJointIndex;
|
||||
handJointIndex = getControllerJointIndex(this.hand);
|
||||
|
||||
var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID];
|
||||
Entities.callEntityMethod(targetProps.id, "startNearGrab", args);
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
/* global Script, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, getControllerJointIndex,
|
||||
enableDispatcherModule, disableDispatcherModule, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION,
|
||||
makeDispatcherModuleParameters, Overlays, makeRunningValues, Vec3, resizeTablet, getTabletWidthFromSettings,
|
||||
NEAR_GRAB_RADIUS
|
||||
NEAR_GRAB_RADIUS, HMD, Uuid
|
||||
*/
|
||||
|
||||
Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
||||
|
@ -37,7 +37,6 @@ Script.include("/~/system/libraries/utils.js");
|
|||
|
||||
// XXX does handJointIndex change if the avatar changes?
|
||||
this.handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand");
|
||||
this.controllerJointIndex = getControllerJointIndex(this.hand);
|
||||
|
||||
this.getOtherModule = function() {
|
||||
return (this.hand === RIGHT_HAND) ? leftNearParentingGrabOverlay : rightNearParentingGrabOverlay;
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
/* jslint bitwise: true */
|
||||
|
||||
/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, getControllerJointIndex,
|
||||
/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND,
|
||||
enableDispatcherModule, disableDispatcherModule, Messages, makeDispatcherModuleParameters, makeRunningValues, Vec3,
|
||||
HMD, Uuid, AvatarList, Picks, Pointers, PickType
|
||||
*/
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
||||
<script type="text/javascript" src="js/eventBridgeLoader.js"></script>
|
||||
<script type="text/javascript" src="js/spinButtons.js"></script>
|
||||
<script type="text/javascript" src="js/keyboardControl.js"></script>
|
||||
<script type="text/javascript" src="js/entityList.js"></script>
|
||||
</head>
|
||||
<body onload='loaded();'>
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
||||
<script type="text/javascript" src="js/eventBridgeLoader.js"></script>
|
||||
<script type="text/javascript" src="js/spinButtons.js"></script>
|
||||
<script type="text/javascript" src="js/keyboardControl.js"></script>
|
||||
<script type="text/javascript" src="js/entityProperties.js"></script>
|
||||
<script src="js/jsoneditor.min.js"></script>
|
||||
</head>
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
||||
<script type="text/javascript" src="js/eventBridgeLoader.js"></script>
|
||||
<script type="text/javascript" src="js/spinButtons.js"></script>
|
||||
<script type="text/javascript" src="js/keyboardControl.js"></script>
|
||||
<script type="text/javascript" src="js/gridControls.js"></script>
|
||||
</head>
|
||||
<body onload='loaded();'>
|
||||
|
|
|
@ -444,8 +444,6 @@ function loaded() {
|
|||
|
||||
augmentSpinButtons();
|
||||
|
||||
setUpKeyboardControl();
|
||||
|
||||
// Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked
|
||||
document.addEventListener("contextmenu", function (event) {
|
||||
event.preventDefault();
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
/* global alert, augmentSpinButtons, clearTimeout, console, document, Element, EventBridge,
|
||||
HifiEntityUI, JSONEditor, openEventBridge, setUpKeyboardControl, setTimeout, window, _ $ */
|
||||
HifiEntityUI, JSONEditor, openEventBridge, setTimeout, window, _ $ */
|
||||
|
||||
var PI = 3.14159265358979;
|
||||
var DEGREES_TO_RADIANS = PI / 180.0;
|
||||
|
@ -2157,8 +2157,6 @@ function loaded() {
|
|||
|
||||
augmentSpinButtons();
|
||||
|
||||
setUpKeyboardControl();
|
||||
|
||||
// Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked
|
||||
document.addEventListener("contextmenu", function(event) {
|
||||
event.preventDefault();
|
||||
|
|
|
@ -129,8 +129,6 @@ function loaded() {
|
|||
|
||||
augmentSpinButtons();
|
||||
|
||||
setUpKeyboardControl();
|
||||
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'init' }));
|
||||
});
|
||||
document.addEventListener("keydown", function (keyDown) {
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
//
|
||||
// keyboardControl.js
|
||||
//
|
||||
// Created by David Rowe on 28 Sep 2016.
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
function setUpKeyboardControl() {
|
||||
|
||||
var lowerTimer = null;
|
||||
var isRaised = false;
|
||||
var KEYBOARD_HEIGHT = 200;
|
||||
|
||||
function raiseKeyboard() {
|
||||
window.isKeyboardRaised = true;
|
||||
window.isNumericKeyboard = this.type === "number";
|
||||
|
||||
if (lowerTimer !== null) {
|
||||
clearTimeout(lowerTimer);
|
||||
lowerTimer = null;
|
||||
}
|
||||
|
||||
EventBridge.emitWebEvent("_RAISE_KEYBOARD" + (this.type === "number" ? "_NUMERIC" : ""));
|
||||
|
||||
if (!isRaised) {
|
||||
var delta = this.getBoundingClientRect().bottom + 10 - (document.body.clientHeight - KEYBOARD_HEIGHT);
|
||||
if (delta > 0) {
|
||||
setTimeout(function () {
|
||||
document.body.scrollTop += delta;
|
||||
}, 500); // Allow time for keyboard to be raised in QML.
|
||||
}
|
||||
}
|
||||
|
||||
isRaised = true;
|
||||
}
|
||||
|
||||
function doLowerKeyboard() {
|
||||
window.isKeyboardRaised = false;
|
||||
window.isNumericKeyboard = false;
|
||||
|
||||
EventBridge.emitWebEvent("_LOWER_KEYBOARD");
|
||||
lowerTimer = null;
|
||||
isRaised = false;
|
||||
}
|
||||
|
||||
function lowerKeyboard() {
|
||||
// Delay lowering keyboard a little in case immediately raise it again.
|
||||
if (lowerTimer === null) {
|
||||
lowerTimer = setTimeout(doLowerKeyboard, 20);
|
||||
}
|
||||
}
|
||||
|
||||
function documentBlur() {
|
||||
// Action any pending Lower keyboard event immediately upon leaving document window so that they don't interfere with
|
||||
// other Entities Editor tab.
|
||||
if (lowerTimer !== null) {
|
||||
clearTimeout(lowerTimer);
|
||||
doLowerKeyboard();
|
||||
}
|
||||
}
|
||||
|
||||
var inputs = document.querySelectorAll("input[type=text], input[type=password], input[type=number], textarea");
|
||||
for (var i = 0, length = inputs.length; i < length; i++) {
|
||||
inputs[i].addEventListener("focus", raiseKeyboard);
|
||||
inputs[i].addEventListener("blur", lowerKeyboard);
|
||||
}
|
||||
|
||||
window.addEventListener("blur", documentBlur);
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
|
||||
/* global module, Camera, HMD, MyAvatar, controllerDispatcherPlugins:true, Quat, Vec3, Overlays, Xform,
|
||||
Selection,
|
||||
Selection, Uuid,
|
||||
MSECS_PER_SEC:true , LEFT_HAND:true, RIGHT_HAND:true, FORBIDDEN_GRAB_TYPES:true,
|
||||
HAPTIC_PULSE_STRENGTH:true, HAPTIC_PULSE_DURATION:true, ZERO_VEC:true, ONE_VEC:true,
|
||||
DEFAULT_REGISTRATION_POINT:true, INCHES_TO_METERS:true,
|
||||
|
@ -34,11 +34,12 @@
|
|||
getGrabbableData:true,
|
||||
entityIsGrabbable:true,
|
||||
entityIsDistanceGrabbable:true,
|
||||
getControllerJointIndexCacheTime:true,
|
||||
getControllerJointIndexCache:true,
|
||||
getControllerJointIndex:true,
|
||||
propsArePhysical:true,
|
||||
controllerDispatcherPluginsNeedSort:true,
|
||||
projectOntoXYPlane:true,
|
||||
getChildrenProps:true,
|
||||
projectOntoEntityXYPlane:true,
|
||||
projectOntoOverlayXYPlane:true,
|
||||
makeLaserLockInfo:true,
|
||||
|
@ -53,6 +54,8 @@
|
|||
TEAR_AWAY_COUNT:true,
|
||||
TEAR_AWAY_CHECK_TIME:true,
|
||||
distanceBetweenPointAndEntityBoundingBox:true,
|
||||
entityIsEquipped:true,
|
||||
entityIsFarGrabbedByOther:true,
|
||||
highlightTargetEntity:true,
|
||||
clearHighlightedEntities:true,
|
||||
unhighlightTargetEntity:true
|
||||
|
@ -265,20 +268,32 @@ entityIsDistanceGrabbable = function(props) {
|
|||
return true;
|
||||
};
|
||||
|
||||
getControllerJointIndex = function (hand) {
|
||||
if (HMD.isHandControllerAvailable()) {
|
||||
var controllerJointIndex = -1;
|
||||
if (Camera.mode === "first person") {
|
||||
controllerJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ?
|
||||
"_CONTROLLER_RIGHTHAND" :
|
||||
"_CONTROLLER_LEFTHAND");
|
||||
} else if (Camera.mode === "third person") {
|
||||
controllerJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ?
|
||||
"_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" :
|
||||
"_CAMERA_RELATIVE_CONTROLLER_LEFTHAND");
|
||||
}
|
||||
getControllerJointIndexCacheTime = [0, 0];
|
||||
getControllerJointIndexCache = [-1, -1];
|
||||
|
||||
return controllerJointIndex;
|
||||
getControllerJointIndex = function (hand) {
|
||||
var GET_CONTROLLERJOINTINDEX_CACHE_REFRESH_TIME = 3000; // msecs
|
||||
|
||||
var now = Date.now();
|
||||
if (now - getControllerJointIndexCacheTime[hand] > GET_CONTROLLERJOINTINDEX_CACHE_REFRESH_TIME) {
|
||||
if (HMD.isHandControllerAvailable()) {
|
||||
var controllerJointIndex = -1;
|
||||
if (Camera.mode === "first person") {
|
||||
controllerJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ?
|
||||
"_CONTROLLER_RIGHTHAND" :
|
||||
"_CONTROLLER_LEFTHAND");
|
||||
} else if (Camera.mode === "third person") {
|
||||
controllerJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ?
|
||||
"_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" :
|
||||
"_CAMERA_RELATIVE_CONTROLLER_LEFTHAND");
|
||||
}
|
||||
|
||||
getControllerJointIndexCacheTime[hand] = now;
|
||||
getControllerJointIndexCache[hand] = controllerJointIndex;
|
||||
return controllerJointIndex;
|
||||
}
|
||||
} else {
|
||||
return getControllerJointIndexCache[hand];
|
||||
}
|
||||
|
||||
return -1;
|
||||
|
|
|
@ -19,6 +19,7 @@ var selectionDisplay = null; // for gridTool.js to ignore
|
|||
|
||||
Script.include("/~/system/libraries/WebTablet.js");
|
||||
Script.include("/~/system/libraries/gridTool.js");
|
||||
Script.include("/~/system/libraries/connectionUtils.js");
|
||||
|
||||
var METAVERSE_SERVER_URL = Account.metaverseServerURL;
|
||||
var MARKETPLACE_URL = METAVERSE_SERVER_URL + "/marketplace";
|
||||
|
|
|
@ -251,6 +251,7 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See
|
|||
});
|
||||
}
|
||||
break;
|
||||
case 'refresh': // old name for refreshNearby
|
||||
case 'refreshNearby':
|
||||
data = {};
|
||||
ExtendedOverlay.some(function (overlay) { // capture the audio data
|
||||
|
@ -743,10 +744,13 @@ function receiveMessage(channel, messageString, senderID) {
|
|||
var message = JSON.parse(messageString);
|
||||
switch (message.method) {
|
||||
case 'select':
|
||||
sendToQml(message); // Accepts objects, not just strings.
|
||||
if (!onPalScreen) {
|
||||
tablet.loadQMLSource(PAL_QML_SOURCE);
|
||||
Script.setTimeout(function () { sendToQml(message); }, 1000);
|
||||
} else {
|
||||
sendToQml(message); // Accepts objects, not just strings.
|
||||
}
|
||||
break;
|
||||
default:
|
||||
print('Unrecognized PAL message', messageString);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -175,40 +175,39 @@ void Test::appendTestResultsToFile(const QString& testResultsFolderPath, TestFai
|
|||
}
|
||||
|
||||
void Test::startTestsEvaluation(const QString& testFolder) {
|
||||
QString pathToTestResultsDirectory;
|
||||
if (testFolder.isNull()) {
|
||||
// Get list of JPEG images in folder, sorted by name
|
||||
pathToTestResultsDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test images", ".", QFileDialog::ShowDirsOnly);
|
||||
} else {
|
||||
pathToTestResultsDirectory = testFolder;
|
||||
}
|
||||
// Get list of JPEG images in folder, sorted by name
|
||||
QString previousSelection = snapshotDirectory;
|
||||
|
||||
if (pathToTestResultsDirectory == QString()) {
|
||||
snapshotDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test images",
|
||||
previousSelection, QFileDialog::ShowDirsOnly);
|
||||
|
||||
// If user cancelled then restore previous selection and return
|
||||
if (snapshotDirectory == "") {
|
||||
snapshotDirectory = previousSelection;
|
||||
return;
|
||||
}
|
||||
|
||||
// Quit if test results folder could not be created
|
||||
if (!createTestResultsFolderPath(pathToTestResultsDirectory)) {
|
||||
if (!createTestResultsFolderPath(snapshotDirectory)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Before any processing - all images are converted to PNGs, as this is the format stored on GitHub
|
||||
QStringList sortedSnapshotFilenames = createListOfAll_imagesInDirectory("jpg", pathToTestResultsDirectory);
|
||||
QStringList sortedSnapshotFilenames = createListOfAll_imagesInDirectory("jpg", snapshotDirectory);
|
||||
foreach(QString filename, sortedSnapshotFilenames) {
|
||||
QStringList stringParts = filename.split(".");
|
||||
copyJPGtoPNG(
|
||||
pathToTestResultsDirectory + "/" + stringParts[0] + ".jpg",
|
||||
pathToTestResultsDirectory + "/" + stringParts[0] + ".png"
|
||||
copyJPGtoPNG(snapshotDirectory + "/" + stringParts[0] + ".jpg",
|
||||
snapshotDirectory + "/" + stringParts[0] + ".png"
|
||||
);
|
||||
|
||||
QFile::remove(pathToTestResultsDirectory + "/" + stringParts[0] + ".jpg");
|
||||
QFile::remove(snapshotDirectory + "/" + stringParts[0] + ".jpg");
|
||||
}
|
||||
|
||||
// Create two lists. The first is the test results, the second is the expected images
|
||||
// The expected images are represented as a URL to enable download from GitHub
|
||||
// Images that are in the wrong format are ignored.
|
||||
|
||||
QStringList sortedTestResultsFilenames = createListOfAll_imagesInDirectory("png", pathToTestResultsDirectory);
|
||||
QStringList sortedTestResultsFilenames = createListOfAll_imagesInDirectory("png", snapshotDirectory);
|
||||
QStringList expectedImagesURLs;
|
||||
|
||||
resultImagesFullFilenames.clear();
|
||||
|
@ -216,7 +215,7 @@ void Test::startTestsEvaluation(const QString& testFolder) {
|
|||
expectedImagesFullFilenames.clear();
|
||||
|
||||
foreach(QString currentFilename, sortedTestResultsFilenames) {
|
||||
QString fullCurrentFilename = pathToTestResultsDirectory + "/" + currentFilename;
|
||||
QString fullCurrentFilename = snapshotDirectory + "/" + currentFilename;
|
||||
if (isInSnapshotFilenameFormat("png", currentFilename)) {
|
||||
resultImagesFullFilenames << fullCurrentFilename;
|
||||
|
||||
|
@ -236,11 +235,11 @@ void Test::startTestsEvaluation(const QString& testFolder) {
|
|||
QString expectedImageFilename = currentFilename.replace("/", "_").replace(".", "_EI.");
|
||||
|
||||
expectedImagesFilenames << expectedImageFilename;
|
||||
expectedImagesFullFilenames << pathToTestResultsDirectory + "/" + expectedImageFilename;
|
||||
expectedImagesFullFilenames << snapshotDirectory + "/" + expectedImageFilename;
|
||||
}
|
||||
}
|
||||
|
||||
autoTester->downloadImages(expectedImagesURLs, pathToTestResultsDirectory, expectedImagesFilenames);
|
||||
autoTester->downloadImages(expectedImagesURLs, snapshotDirectory, expectedImagesFilenames);
|
||||
}
|
||||
|
||||
void Test::finishTestsEvaluation(bool isRunningFromCommandline, bool interactiveMode, QProgressBar* progressBar) {
|
||||
|
@ -303,25 +302,39 @@ void Test::importTest(QTextStream& textStream, const QString& testPathname) {
|
|||
// This script will run all text.js scripts in every applicable sub-folder
|
||||
void Test::createRecursiveScript() {
|
||||
// Select folder to start recursing from
|
||||
QString topLevelDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder that will contain the top level test script", ".", QFileDialog::ShowDirsOnly);
|
||||
if (topLevelDirectory == "") {
|
||||
QString previousSelection = testDirectory;
|
||||
|
||||
testDirectory =
|
||||
QFileDialog::getExistingDirectory(nullptr, "Please select folder that will contain the top level test script",
|
||||
previousSelection, QFileDialog::ShowDirsOnly);
|
||||
|
||||
// If user cancelled then restore previous selection and return
|
||||
if (testDirectory == "") {
|
||||
testDirectory = previousSelection;
|
||||
return;
|
||||
}
|
||||
|
||||
createRecursiveScript(topLevelDirectory, true);
|
||||
createRecursiveScript(testDirectory, true);
|
||||
}
|
||||
|
||||
// This method creates a `testRecursive.js` script in every sub-folder.
|
||||
void Test::createAllRecursiveScripts() {
|
||||
// Select folder to start recursing from
|
||||
QString topLevelDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select the root folder for the recursive scripts", ".", QFileDialog::ShowDirsOnly);
|
||||
if (topLevelDirectory == "") {
|
||||
QString previousSelection = testDirectory;
|
||||
|
||||
testDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select the root folder for the recursive scripts",
|
||||
previousSelection,
|
||||
QFileDialog::ShowDirsOnly);
|
||||
|
||||
// If user cancelled then restore previous selection and return
|
||||
if (testDirectory == "") {
|
||||
testDirectory = previousSelection;
|
||||
return;
|
||||
}
|
||||
|
||||
createRecursiveScript(topLevelDirectory, false);
|
||||
createRecursiveScript(testDirectory, false);
|
||||
|
||||
QDirIterator it(topLevelDirectory.toStdString().c_str(), QDirIterator::Subdirectories);
|
||||
QDirIterator it(testDirectory.toStdString().c_str(), QDirIterator::Subdirectories);
|
||||
while (it.hasNext()) {
|
||||
QString directory = it.next();
|
||||
|
||||
|
@ -427,29 +440,42 @@ void Test::createRecursiveScript(const QString& topLevelDirectory, bool interact
|
|||
void Test::createTest() {
|
||||
// Rename files sequentially, as ExpectedResult_00000.jpeg, ExpectedResult_00001.jpg and so on
|
||||
// Any existing expected result images will be deleted
|
||||
QString imageSourceDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test images", ".", QFileDialog::ShowDirsOnly);
|
||||
if (imageSourceDirectory == "") {
|
||||
QString previousSelection = snapshotDirectory;
|
||||
|
||||
snapshotDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test images",
|
||||
previousSelection,
|
||||
QFileDialog::ShowDirsOnly);
|
||||
|
||||
// If user cancelled then restore previous selection and return
|
||||
if (snapshotDirectory == "") {
|
||||
snapshotDirectory = previousSelection;
|
||||
return;
|
||||
}
|
||||
|
||||
QString imageDestinationDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder to save the test images", ".", QFileDialog::ShowDirsOnly);
|
||||
if (imageDestinationDirectory == "") {
|
||||
previousSelection = testDirectory;
|
||||
|
||||
QString testDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder to save the test images",
|
||||
previousSelection, QFileDialog::ShowDirsOnly);
|
||||
|
||||
// If user cancelled then restore previous selection and return
|
||||
if (testDirectory == "") {
|
||||
testDirectory = previousSelection;
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList sortedImageFilenames = createListOfAll_imagesInDirectory("jpg", imageSourceDirectory);
|
||||
QStringList sortedImageFilenames = createListOfAll_imagesInDirectory("jpg", snapshotDirectory);
|
||||
|
||||
int i = 1;
|
||||
const int maxImages = pow(10, NUM_DIGITS);
|
||||
foreach (QString currentFilename, sortedImageFilenames) {
|
||||
QString fullCurrentFilename = imageSourceDirectory + "/" + currentFilename;
|
||||
QString fullCurrentFilename = snapshotDirectory + "/" + currentFilename;
|
||||
if (isInSnapshotFilenameFormat("jpg", currentFilename)) {
|
||||
if (i >= maxImages) {
|
||||
QMessageBox::critical(0, "Error", "More than " + QString::number(maxImages) + " images not supported");
|
||||
exit(-1);
|
||||
}
|
||||
QString newFilename = "ExpectedImage_" + QString::number(i - 1).rightJustified(5, '0') + ".png";
|
||||
QString fullNewFileName = imageDestinationDirectory + "/" + newFilename;
|
||||
QString fullNewFileName = testDirectory + "/" + newFilename;
|
||||
|
||||
try {
|
||||
copyJPGtoPNG(fullCurrentFilename, fullNewFileName);
|
||||
|
@ -489,31 +515,6 @@ ExtractedText Test::getTestScriptLines(QString testFileName) {
|
|||
QString regexTestTitle(ws + functionPerformName + "\\(" + quotedString + "\\," + ws + ownPath + "\\," + ws + functionParameter + ws + "{" + ".*");
|
||||
QRegularExpression lineContainingTitle = QRegularExpression(regexTestTitle);
|
||||
|
||||
// Assert platform checks that test is running on the correct OS
|
||||
const QString functionAssertPlatform(ws + "autoTester" + ws + "\\." + ws + "assertPlatform");
|
||||
const QString regexAssertPlatform(ws + functionAssertPlatform + ws + "\\(" + ws + quotedString + ".*");
|
||||
const QRegularExpression lineAssertPlatform = QRegularExpression(regexAssertPlatform);
|
||||
|
||||
// Assert display checks that test is running on the correct display
|
||||
const QString functionAssertDisplay(ws + "autoTester" + ws + "\\." + ws + "assertDisplay");
|
||||
const QString regexAssertDisplay(ws + functionAssertDisplay + ws + "\\(" + ws + quotedString + ".*");
|
||||
const QRegularExpression lineAssertDisplay = QRegularExpression(regexAssertDisplay);
|
||||
|
||||
// Assert CPU checks that test is running on the correct type of CPU
|
||||
const QString functionAssertCPU(ws + "autoTester" + ws + "\\." + ws + "assertCPU");
|
||||
const QString regexAssertCPU(ws + functionAssertCPU + ws + "\\(" + ws + quotedString + ".*");
|
||||
const QRegularExpression lineAssertCPU = QRegularExpression(regexAssertCPU);
|
||||
|
||||
// Assert GPU checks that test is running on the correct type of GPU
|
||||
const QString functionAssertGPU(ws + "autoTester" + ws + "\\." + ws + "assertGPU");
|
||||
const QString regexAssertGPU(ws + functionAssertGPU + ws + "\\(" + ws + quotedString + ".*");
|
||||
const QRegularExpression lineAssertGPU = QRegularExpression(regexAssertGPU);
|
||||
|
||||
// Assert the correct amount of memory
|
||||
const QString functionAssertPhysicalMemoryGB(ws + "autoTester" + ws + "\\." + ws + "assertPhysicalMemoryGB");
|
||||
const QString regexAssertPhysicalMemoryGB(ws + functionAssertPhysicalMemoryGB + ws + "\\(" + ws + quotedString + ".*");
|
||||
const QRegularExpression lineAssertPhysicalMemoryGB = QRegularExpression(regexAssertPhysicalMemoryGB);
|
||||
|
||||
|
||||
// Each step is either of the following forms:
|
||||
// autoTester.addStepSnapshot("Take snapshot"...
|
||||
|
@ -523,7 +524,7 @@ ExtractedText Test::getTestScriptLines(QString testFileName) {
|
|||
const QRegularExpression lineStepSnapshot = QRegularExpression(regexStepSnapshot);
|
||||
|
||||
const QString functionAddStepName(ws + "autoTester" + ws + "\\." + ws + "addStep");
|
||||
const QString regexStep(ws + functionAddStepName + ws + "\\(" + ws + quotedString + ws + "\\)" + ".*");
|
||||
const QString regexStep(ws + functionAddStepName + ws + "\\(" + ws + quotedString + ".*");
|
||||
const QRegularExpression lineStep = QRegularExpression(regexStep);
|
||||
|
||||
while (!line.isNull()) {
|
||||
|
@ -531,7 +532,6 @@ ExtractedText Test::getTestScriptLines(QString testFileName) {
|
|||
if (lineContainingTitle.match(line).hasMatch()) {
|
||||
QStringList tokens = line.split('"');
|
||||
relevantTextFromTest.title = tokens[1];
|
||||
|
||||
} else if (lineStepSnapshot.match(line).hasMatch()) {
|
||||
QStringList tokens = line.split('"');
|
||||
QString nameOfStep = tokens[1];
|
||||
|
@ -561,29 +561,43 @@ ExtractedText Test::getTestScriptLines(QString testFileName) {
|
|||
// The folder selected must contain a script named "test.js", the file produced is named "test.md"
|
||||
void Test::createMDFile() {
|
||||
// Folder selection
|
||||
QString testDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test", ".", QFileDialog::ShowDirsOnly);
|
||||
QString previousSelection = testDirectory;
|
||||
|
||||
testDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test", previousSelection,
|
||||
QFileDialog::ShowDirsOnly);
|
||||
|
||||
// If user cancelled then restore previous selection and return
|
||||
if (testDirectory == "") {
|
||||
testDirectory = previousSelection;
|
||||
return;
|
||||
}
|
||||
|
||||
createMDFile(testDirectory);
|
||||
|
||||
QMessageBox::information(0, "Success", "MD file has been created");
|
||||
}
|
||||
|
||||
void Test::createAllMDFiles() {
|
||||
// Select folder to start recursing from
|
||||
QString topLevelDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select the root folder for the MD files", ".", QFileDialog::ShowDirsOnly);
|
||||
if (topLevelDirectory == "") {
|
||||
QString previousSelection = testDirectory;
|
||||
|
||||
testDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select the root folder for the MD files",
|
||||
previousSelection, QFileDialog::ShowDirsOnly);
|
||||
|
||||
// If user cancelled then restore previous selection and return
|
||||
if (testDirectory == "") {
|
||||
testDirectory = previousSelection;
|
||||
return;
|
||||
}
|
||||
|
||||
// First test if top-level folder has a test.js file
|
||||
const QString testPathname{ topLevelDirectory + "/" + TEST_FILENAME };
|
||||
const QString testPathname { testDirectory + "/" + TEST_FILENAME };
|
||||
QFileInfo fileInfo(testPathname);
|
||||
if (fileInfo.exists()) {
|
||||
createMDFile(topLevelDirectory);
|
||||
createMDFile(testDirectory);
|
||||
}
|
||||
|
||||
QDirIterator it(topLevelDirectory.toStdString().c_str(), QDirIterator::Subdirectories);
|
||||
QDirIterator it(testDirectory.toStdString().c_str(), QDirIterator::Subdirectories);
|
||||
while (it.hasNext()) {
|
||||
QString directory = it.next();
|
||||
|
||||
|
@ -638,31 +652,33 @@ void Test::createMDFile(const QString& testDirectory) {
|
|||
stream << "## Steps\n";
|
||||
stream << "Press space bar to advance step by step\n\n";
|
||||
|
||||
// Note that snapshots of step n are taken in step n+1
|
||||
// (this implies that if the LAST step requests a snapshot then this will not work - caveat emptor)
|
||||
int snapShotIndex { 0 };
|
||||
for (size_t i = 0; i < testScriptLines.stepList.size(); ++i) {
|
||||
stream << "### Step " << QString::number(i + 1) << "\n";
|
||||
stream << "- " << testScriptLines.stepList[i]->text << "\n";
|
||||
if ((i + 1 < testScriptLines.stepList.size()) && testScriptLines.stepList[i + 1]->takeSnapshot) {
|
||||
if ((i + 1 < testScriptLines.stepList.size()) && testScriptLines.stepList[i]->takeSnapshot) {
|
||||
stream << "- .rightJustified(5, '0') << ".png)\n";
|
||||
++snapShotIndex;
|
||||
}
|
||||
}
|
||||
|
||||
mdFile.close();
|
||||
|
||||
QMessageBox::information(0, "Success", "Test MD file " + mdFilename + " has been created");
|
||||
}
|
||||
|
||||
void Test::createTestsOutline() {
|
||||
QString testsRootDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select the tests root folder", ".", QFileDialog::ShowDirsOnly);
|
||||
if (testsRootDirectory == "") {
|
||||
QString previousSelection = testDirectory;
|
||||
|
||||
testDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select the tests root folder", previousSelection,
|
||||
QFileDialog::ShowDirsOnly);
|
||||
|
||||
// If user cancelled then restore previous selection and return
|
||||
if (testDirectory == "") {
|
||||
testDirectory = previousSelection;
|
||||
return;
|
||||
}
|
||||
|
||||
const QString testsOutlineFilename { "testsOutline.md" };
|
||||
QString mdFilename(testsRootDirectory + "/" + testsOutlineFilename);
|
||||
QString mdFilename(testDirectory + "/" + testsOutlineFilename);
|
||||
QFile mdFile(mdFilename);
|
||||
if (!mdFile.open(QIODevice::WriteOnly)) {
|
||||
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to create file " + mdFilename);
|
||||
|
@ -676,10 +692,10 @@ void Test::createTestsOutline() {
|
|||
stream << "Directories with an appended (*) have an automatic test\n\n";
|
||||
|
||||
// We need to know our current depth, as this isn't given by QDirIterator
|
||||
int rootDepth { testsRootDirectory.count('/') };
|
||||
int rootDepth { testDirectory.count('/') };
|
||||
|
||||
// Each test is shown as the folder name linking to the matching GitHub URL, and the path to the associated test.md file
|
||||
QDirIterator it(testsRootDirectory.toStdString().c_str(), QDirIterator::Subdirectories);
|
||||
QDirIterator it(testDirectory.toStdString().c_str(), QDirIterator::Subdirectories);
|
||||
while (it.hasNext()) {
|
||||
QString directory = it.next();
|
||||
|
||||
|
|
|
@ -89,6 +89,12 @@ private:
|
|||
const int NUM_DIGITS { 5 };
|
||||
const QString EXPECTED_IMAGE_PREFIX { "ExpectedImage_" };
|
||||
|
||||
// We have two directories to work with.
|
||||
// The first is the directory containing the test we are working with
|
||||
// The second contains the snapshots taken for test runs that need to be evaluated
|
||||
QString testDirectory;
|
||||
QString snapshotDirectory;
|
||||
|
||||
QStringList expectedImagesFilenames;
|
||||
QStringList expectedImagesFullFilenames;
|
||||
QStringList resultImagesFullFilenames;
|
||||
|
|
|
@ -21,6 +21,7 @@ exports.handlers = {
|
|||
// directories to scan for jsdoc comments
|
||||
var dirList = [
|
||||
'../../assignment-client/src',
|
||||
'../../assignment-client/src/avatars',
|
||||
'../../assignment-client/src/entities',
|
||||
'../../assignment-client/src/octree',
|
||||
'../../interface/src',
|
||||
|
@ -31,10 +32,10 @@ exports.handlers = {
|
|||
'../../interface/src/devices',
|
||||
'../../interface/src/java',
|
||||
'../../interface/src/networking',
|
||||
'../../interface/src/ui/',
|
||||
'../../interface/src/scripting',
|
||||
'../../interface/src/ui/overlays',
|
||||
'../../interface/src/raypick',
|
||||
'../../interface/src/scripting',
|
||||
'../../interface/src/ui/',
|
||||
'../../interface/src/ui/overlays',
|
||||
'../../libraries/animation/src',
|
||||
'../../libraries/audio-client/src',
|
||||
'../../libraries/audio/src',
|
||||
|
@ -114,8 +115,8 @@ exports.handlers = {
|
|||
|
||||
// Append an Available In: table at the end of the namespace description.
|
||||
if (rows.length > 0) {
|
||||
var table = "<br><br><table><tr><th>Available in:</th><td>" + rows.join("</td><td>") + "</td></tr></table>";
|
||||
e.doclet.description = (e.doclet.description ? e.doclet.description : "") + table;
|
||||
var table = "<table><tr><th>Available in:</th><td>" + rows.join("</td><td>") + "</td></tr></table><br>";
|
||||
e.doclet.description = table + (e.doclet.description ? e.doclet.description : "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue