From 4ed37a062edb761cbbbd100752da9f9c076c5236 Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 20 Apr 2017 12:43:14 -0700 Subject: [PATCH 01/25] Protecting the case when a texture is not created at all in the ImageReader --- .../model-networking/src/model-networking/TextureCache.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 98b03eba1e..b8d3a51534 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -559,9 +559,12 @@ void ImageReader::read() { // Load the image into a gpu::Texture auto networkTexture = resource.staticCast(); texture.reset(networkTexture->getTextureLoader()(image, url)); - texture->setSource(url); if (texture) { + texture->setSource(url); texture->setFallbackTexture(networkTexture->getFallbackTexture()); + } else { + qCDebug(modelnetworking) << _url << "loading stopped; texture wasn't created"; + return; } auto textureCache = DependencyManager::get(); From 7ca4a21a91d8873cc9abae098d5ae1d71aa7a75e Mon Sep 17 00:00:00 2001 From: Daniela Fontes Date: Mon, 18 Dec 2017 21:00:29 +0000 Subject: [PATCH 02/25] Fix entity property href not being able to reset (empty) once it was set with the correct input for the first time. --- libraries/entities/src/EntityItem.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 3498d2c4b1..89fde99f2a 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -961,7 +961,10 @@ void EntityItem::setMass(float mass) { void EntityItem::setHref(QString value) { auto href = value.toLower(); - if (! (value.toLower().startsWith("hifi://")) ) { + + // If the string has something and doesn't start with with "hifi://" it shouldn't be set + // We allow the string to be empty, because that's the initial state of this property + if ( !(value.toLower().startsWith("hifi://")) && !value.isEmpty()) { return; } withWriteLock([&] { From e6c40f4aae801c09d3c6b0aec978ce50bcdde5d8 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 20 Dec 2017 10:12:04 +1300 Subject: [PATCH 03/25] Window JSDoc --- .../scripting/WindowScriptingInterface.cpp | 41 +- .../src/scripting/WindowScriptingInterface.h | 569 +++++++++++++++++- libraries/networking/src/DomainHandler.h | 40 ++ 3 files changed, 644 insertions(+), 6 deletions(-) diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 4b355653b6..b4247fd0b0 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -192,8 +192,7 @@ void WindowScriptingInterface::ensureReticleVisible() const { /// Display a "browse to directory" dialog. If `directory` is an invalid file or directory the browser will start at the current /// working directory. /// \param const QString& title title of the window -/// \param const QString& directory directory to start the file browser at -/// \param const QString& nameFilter filter to filter filenames by - see `QFileDialog` +/// \param const QString& directory directory to start the directory browser at /// \return QScriptValue file path as a string if one was selected, otherwise `QScriptValue::NullValue` QScriptValue WindowScriptingInterface::browseDir(const QString& title, const QString& directory) { ensureReticleVisible(); @@ -214,8 +213,7 @@ QScriptValue WindowScriptingInterface::browseDir(const QString& title, const QSt /// Display a "browse to directory" dialog. If `directory` is an invalid file or directory the browser will start at the current /// working directory. /// \param const QString& title title of the window -/// \param const QString& directory directory to start the file browser at -/// \param const QString& nameFilter filter to filter filenames by - see `QFileDialog` +/// \param const QString& directory directory to start the directory browser at void WindowScriptingInterface::browseDirAsync(const QString& title, const QString& directory) { ensureReticleVisible(); QString path = directory; @@ -459,6 +457,41 @@ int WindowScriptingInterface::openMessageBox(QString title, QString text, int bu return createMessageBox(title, text, buttons, defaultButton); } +/**jsdoc + *

The buttons that may be included in a message box created by {@link Window.openMessageBox|openMessageBox} are defined by + * numeric values: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
ButtonValueDescription
NoButton 0x0 An invalid button.
Ok 0x400 "OK"
Save 0x800 "Save"
SaveAll 0x1000 "Save All"
Open 0x2000 "Open"
Yes 0x4000 "Yes"
YesToAll 0x8000 "Yes to All"
No 0x10000 "No"
NoToAll 0x20000 "No to All"
Abort 0x40000 "Abort"
Retry 0x80000 "Retry"
Ignore 0x100000 "Ignore"
Close 0x200000 "Close"
Cancel 0x400000 "Cancel"
Discard 0x800000 "Discard" or "Don't Save"
Help 0x1000000 "Help"
Apply 0x2000000 "Apply"
Reset 0x4000000 "Reset"
RestoreDefaults 0x8000000 "Restore Defaults"
+ * @typedef Window.MessageBoxButton + */ int WindowScriptingInterface::createMessageBox(QString title, QString text, int buttons, int defaultButton) { auto messageBox = DependencyManager::get()->createMessageBox(OffscreenUi::ICON_INFORMATION, title, text, static_cast>(buttons), static_cast(defaultButton)); diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index d223f95af4..09938f0549 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -33,6 +33,19 @@ QScriptValue CustomPromptResultToScriptValue(QScriptEngine* engine, const Custom void CustomPromptResultFromScriptValue(const QScriptValue& object, CustomPromptResult& result); +/**jsdoc + * The Window API provides various facilities not covered elsewhere. + * + * @namespace Window + * @property {number} innerWidth - The width of the drawable area of the Interface window (i.e., without borders or other + * chrome), in pixels. Read-only. + * @property {number} innerHeight - The height of the drawable area of the Interface window (i.e., without borders or other + * chrome) plus the height of the menu bar, in pixels. Read-only. + * @property {object} location - Provides facilities for working with your current metaverse location. See {@link location}. + * @property {number} x - The x coordinate of the top left corner of the Interface window on the display. Read-only. + * @property {number} y - The y coordinate of the top left corner of the Interface window on the display. Read-only. + */ + class WindowScriptingInterface : public QObject, public Dependency { Q_OBJECT Q_PROPERTY(int innerWidth READ getInnerWidth) @@ -48,63 +61,615 @@ public: int getY(); public slots: + + /**jsdoc + * Check if the Interface window has focus. + * @function Window.hasFocus + * @returns {boolean} true if the Interface window has focus, otherwise false. + */ QScriptValue hasFocus(); + + /**jsdoc + * Make the Interface window have focus. + * @function Window.setFocus + */ void setFocus(); + + /**jsdoc + * Raise the Interface window if it is minimized, and give it focus. + * @function Window.raiseMainWindow + */ void raiseMainWindow(); + + /**jsdoc + * Display a dialog with the specified message and an "OK" button. The dialog is non-modal; the script continues without + * waiting for a user response. + * @function Window.alert + * @param {string} message="" - The message to display. + * @example Display a friendly greeting. + * Window.alert("Welcome!"); + * print("Script continues without waiting"); + */ void alert(const QString& message = ""); + + /**jsdoc + * Prompt the user to confirm something. Displays a modal dialog with a message plus "Yes" and "No" buttons. + * responds. + * @function Window.confirm + * @param {string} message="" - The question to display. + * @returns {boolean} true if the user selects "Yes", otherwise false. + * @example Ask the user a question requiring a yes/no answer. + * var answer = Window.confirm("Are you sure?"); + * print(answer); // true or false + */ QScriptValue confirm(const QString& message = ""); + + /**jsdoc + * Prompt the user to enter some text. Displays a modal dialog with a message and a text box, plus "OK" and "Cancel" + * buttons. + * @function Window.prompt + * @param {string} message - The question to display. + * @param {string} defaultText - The default answer text. + * @returns {string} The text that the user entered if they select "OK", otherwise "". + * @example Ask the user a question requiring a text answer. + * var answer = Window.prompt("Question", "answer"); + * if (answer === "") { + * print("User canceled"); + * } else { + * print("User answer: " + answer); + * } + */ QScriptValue prompt(const QString& message, const QString& defaultText); + + /**jsdoc + * Prompt the user to enter some text. Displays a non-modal dialog with a message and a text box, plus "OK" and "Cancel" + * buttons. A {@link Window.promptTextChanged|promptTextChanged} signal is emitted when the user OKs the dialog; no signal + * is emitted if the user cancels the dialog. + * @function Window.promptAsync + * @param {string} message - The question to display. + * @param {string} defaultText - The default answer text. + * @example Ask the user a question requiring a text answer without waiting for the answer. + * function onPromptTextChanged(text) { + * print("User answer: " + text); + * } + * Window.promptTextChanged.connect(onPromptTextChanged); + * + * Window.promptAsync("Question", "answer"); + * print("Script continues without waiting"); + */ void promptAsync(const QString& message = "", const QString& defaultText = ""); + + /**jsdoc + * Prompt the user for input in a custom, modal dialog. + * @deprecated This funtion is deprecated and will be removed. + * @function Window.customPrompt + * @param {object} config - Configures the modal dialog. + * @returns {object} The user's response. + */ CustomPromptResult customPrompt(const QVariant& config); + + /**jsdoc + * Prompt the user to choose a directory. Displays a modal dialog that navigates the directory tree. + * @function Window.browseDir + * @param {string} title="" - The title to display at the top of the dialog. + * @param {string} directory="" - The initial directory to start browsing at. + * @returns {string} The path of the directory if one is chosen, otherwise null. + * @example Ask the user to choose a directory. + * var directory = Window.browseDir("Select Directory", Paths.resources); + * print("Directory: " + directory); + */ QScriptValue browseDir(const QString& title = "", const QString& directory = ""); + + /**jsdoc + * Prompt the user to choose a directory. Displays a non-modal dialog that navigates the directory tree. A + * {@link Window.browseDirChanged|browseDirChanged} signal is emitted when a directory is chosen; no signal is emitted if + * the user cancels the dialog. + * @function Window.browseDirAsync + * @param {string} title="" - The title to display at the top of the dialog. + * @param {string} directory="" - The initial directory to start browsing at. + * @example Ask the user to choose a directory without waiting for the answer. + * function onBrowseDirChanged(directory) { + * print("Directory: " + directory); + * } + * Window.browseDirChanged.connect(onBrowseDirChanged); + * + * Window.browseDirAsync("Select Directory", Paths.resources); + * print("Script continues without waiting"); + */ void browseDirAsync(const QString& title = "", const QString& directory = ""); + + /**jsdoc + * Prompt the user to choose a file. Displays a modal dialog that navigates the directory tree. + * @function Window.browse + * @param {string} title="" - The title to display at the top of the dialog. + * @param {string} directory="" - The initial directory to start browsing at. + * @param {string} nameFilter="" - The types of files to display. Examples: "*.json" and + * "Images (*.png *.jpg *.svg)". All files are displayed if a filter isn't specified. + * @returns {string} The path and name of the file if one is chosen, otherwise null. + * @example Ask the user to choose an image file. + * var file = Window.browse("Select Image File", Paths.resources, "Images (*.png *.jpg *.svg)"); + * print("File: " + file); + */ QScriptValue browse(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); + + /**jsdoc + * Prompt the user to choose a file. Displays a non-modal dialog that navigates the directory tree. A + * {@link Window.openFileChanged|openFileChanged} signal is emitted when a file is chosen; no signal is emitted if the user + * cancels the dialog. + * @function Window.browseAsync + * @param {string} title="" - The title to display at the top of the dialog. + * @param {string} directory="" - The initial directory to start browsing at. + * @param {string} nameFilter="" - The types of files to display. Examples: "*.json" and + * "Images (*.png *.jpg *.svg)". All files are displayed if a filter isn't specified. + * @example Ask the user to choose an image file without waiting for the answer. + * function onOpenFileChanged(file) { + * print("File: " + file); + * } + * Window.openFileChanged.connect(onOpenFileChanged); + * + * Window.browseAsync("Select Image File", Paths.resources, "Images (*.png *.jpg *.svg)"); + * print("Script continues without waiting"); + */ void browseAsync(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); + + /**jsdoc + * Prompt the user to specify the path and name of a file to save to. Displays a model dialog that navigates the directory + * tree and allows the user to type in a file name. + * @function Window.save + * @param {string} title="" - The title to display at the top of the dialog. + * @param {string} directory="" - The initial directory to start browsing at. + * @param {string} nameFilter="" - The types of files to display. Examples: "*.json" and + * "Images (*.png *.jpg *.svg)". All files are displayed if a filter isn't specified. + * @returns {string} The path and name of the file is one is specified, otherwise null. If a single file type + * is specified in the nameFilter, that file type extension is automatically appended to the result when appropriate. + * @example Ask the user to specify a file to save to. + * var file = Window.save("Save to JSON file", Paths.resources, "*.json"); + * print("File: " + file); + */ QScriptValue save(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); + + /**jsdoc + * Prompt the user to specify the path and name of a file to save to. Displays a non-model dialog that navigates the + * directory tree and allows the user to type in a file name. A {@link Window.saveFileChanged|saveFileChanged} signal is + * emitted when a file is specified; no signal is emitted if the user cancels the dialog. + * @function Window.saveAsync + * @param {string} title="" - The title to display at the top of the dialog. + * @param {string} directory="" - The initial directory to start browsing at. + * @param {string} nameFilter="" - The types of files to display. Examples: "*.json" and + * "Images (*.png *.jpg *.svg)". All files are displayed if a filter isn't specified. + * @example Ask the user to specify a file to save to without waiting for an answer. + * function onSaveFileChanged(file) { + * print("File: " + file); + * } + * Window.saveFileChanged.connect(onSaveFileChanged); + * + * Window.saveAsync("Save to JSON file", Paths.resources, "*.json"); + * print("Script continues without waiting"); + */ void saveAsync(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); + + /**jsdoc + * Prompt the user to choose an Asset Server item. Displays a modal dialog that navigates the tree of assets on the Asset + * Server. + * @function Window.browseAssets + * @param {string} title="" - The title to display at the top of the dialog. + * @param {string} directory="" - The initial directory to start browsing at. + * @param {string} nameFilter="" - The types of files to display. Examples: "*.json" and + * "Images (*.png *.jpg *.svg)". All files are displayed if a filter isn't specified. + * @returns {string} The path and name of the asset if one is chosen, otherwise null. + * @example Ask the user to select an FBX asset. + * var asset = Window.browseAssets("Select FBX File", "/", "*.fbx"); + * print("FBX file: " + asset); + */ QScriptValue browseAssets(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); + + /**jsdoc + * Prompt the user to choose an Asset Server item. Displays a non-modal dialog that navigates the tree of assets on the + * Asset Server. A {@link Window.assetsDirChanged|assetsDirChanged} signal is emitted when an asset is chosen; no signal is + * emitted if the user cancels the dialog. + * @function Window.browseAssetsAsync + * @param {string} title="" - The title to display at the top of the dialog. + * @param {string} directory="" - The initial directory to start browsing at. + * @param {string} nameFilter="" - The types of files to display. Examples: "*.json" and + * "Images (*.png *.jpg *.svg)". All files are displayed if a filter isn't specified. + * @example + * function onAssetsDirChanged(asset) { + * print("FBX file: " + asset); + * } + * Window.assetsDirChanged.connect(onAssetsDirChanged); + * + * Window.browseAssetsAsync("Select FBX File", "/", "*.fbx"); + * print("Script continues without waiting"); + */ void browseAssetsAsync(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); + + /**jsdoc + * Open the Asset Browser dialog. If a file to upload is specified, the user is prompted to enter the folder and name to + * map the file to on the asset server. + * @function Window.showAssetServer + * @param {string} uploadFile="" - The path and name of a file to upload to the asset server. + * @example Upload a file to the asset server. + * var file = Window.browse("Select File to Add to Asset Server", Paths.resources); + * print("File: " + file); + * Window.showAssetServer(file); + */ void showAssetServer(const QString& upload = ""); + + /**jsdoc + * Get Interface's build number. + * @function Window.checkVersion + * @returns {string} - Interface's build number. + */ QString checkVersion(); + + /**jsdoc + * Copies text to the operating system's clipboard. + * @function Window.copyToClipboard + * @param {string} text - The text to copy to the operating system's clipboard. + */ void copyToClipboard(const QString& text); + + /**jsdoc + * Takes a snapshot of the current Interface view from the primary camera. When a still image only is captured, + * {@link Window.stillSnapshotTaken|stillSnapshotTaken} is emitted; when a still image plus moving images are captured, + * {@link Window.processingGifStarted|processingGifStarted} and {@link Window.processingGifCompleted|processingGifCompleted} + * are emitted. The path to store the snapshots and the length of the animated GIF to capture are specified in Settings > + * General > Snapshots. + * @function Window.takeSnapshot + * @param {boolean} notify=true - This value is passed on through the {@link Window.stillSnapshotTaken|stillSnapshotTaken} + * signal. + * @param {boolean} includeAnimated=false - If true, a moving image is captured as an animated GIF in addition + * to a still image. + * @param {number} aspectRatio=0 - The width/height ratio of the snapshot required. If the value is 0 the + * full resolution is used (window dimensions in desktop mode; HMD display dimensions in HMD mode), otherwise one or + * other dimension is adjusted in order to match the aspect ratio. + * @example Using the snapshot function and signals. + * function onStillSnapshottaken(path, notify) { + * print("Still snapshot taken: " + path); + * print("Notify: " + notify); + * } + * + * function onProcessingGifStarted(stillPath) { + * print("Still snapshot taken: " + stillPath); + * } + * + * function onProcessingGifCompleted(animatedPath) { + * print("Animated snapshot taken: " + animatedPath); + * } + * + * Window.stillSnapshotTaken.connect(onStillSnapshottaken); + * Window.processingGifStarted.connect(onProcessingGifStarted); + * Window.processingGifCompleted.connect(onProcessingGifCompleted); + * + * var notify = true; + * var animated = true; + * var aspect = 1920 / 1080; + * Window.takeSnapshot(notify, animated, aspect); + */ void takeSnapshot(bool notify = true, bool includeAnimated = false, float aspectRatio = 0.0f); + + /**jsdoc + * Takes a still snapshot of the current view from the secondary camera that can be set up through the {@link Render} API. + * @function Window.takeSecondaryCameraSnapshot + */ void takeSecondaryCameraSnapshot(); + + /**jsdoc + * Emit a {@link Window.connectionAdded|connectionAdded} or a {@link Window.connectionError|connectionError} signal that + * indicates whether or not you successfully made a user connection. + * @function Window.makeConnection + * @param {boolean} success - If true then {@link Window.connectionAdded|connectionAdded} is emitted, otherwise + * {@link Window.connectionError|connectionError} is emitted. + * @param {string} description - Descriptive text about the connection success or error. This is sent in the signal emitted. + */ void makeConnection(bool success, const QString& userNameOrError); + + /**jsdoc + * Display a notification message. Notifications are displayed in panels by the default script, nofications.js. An + * {@link Window.announcement|announcement} signal is emitted when this function is called. + * @function Window.displayAnnouncement + * @param {string} message - The announcement message. + * @example Send and capture an announcement message. + * function onAnnouncement(message) { + * // The message is also displayed as a notification by notifications.js. + * print("Announcement: " + message); + * } + * Window.announcement.connect(onAnnouncement); + * + * Window.displayAnnouncement("Hello"); + */ void displayAnnouncement(const QString& message); + + /**jsdoc + * Prepare a snapshot ready for sharing. A {@link Window.snapshotShared|snapshotShared} signal is emitted when the snapshot + * has been prepared. + * @function Window.shareSnapshot + * @param {string} path - The path and name of the image file to share. + * @param {string} href="" - The metaverse location where the snapshot was taken. + */ void shareSnapshot(const QString& path, const QUrl& href = QUrl("")); + + /**jsdoc + * Check to see if physics is active for you in the domain you're visiting - there is a delay between your arrival at a + * domain and physics becoming active for you in that domain. + * @function Window.isPhysicsEnabled + * @returns {boolean} true if physics is currently active for you, otherwise false. + * @example Wait for physics to be enabled when you change domains. + * function checkForPhysics() { + * var isPhysicsEnabled = Window.isPhysicsEnabled(); + * print("Physics enabled: " + isPhysicsEnabled); + * if (!isPhysicsEnabled) { + * Script.setTimeout(checkForPhysics, 1000); + * } + * } + * + * function onDomainChanged(domain) { + * print("Domain changed: " + domain); + * Script.setTimeout(checkForPhysics, 1000); + * } + * + * Window.domainChanged.connect(onDomainChanged); + */ bool isPhysicsEnabled(); + + /**jsdoc + * Set what to show on the PC display: normal view or entity camera view. The entity camera is configured using + * {@link Camera.setCameraEntity} and {@link Camera|Camera.mode}. + * @function Window.setDisplayTexture + * @param {Window.DisplayTexture} name - The view to display. + * @returns {boolean} true if the display texture was successfully set, otherwise false. + */ + // See spectatorCamera.js for Valid parameter values. + /**jsdoc + *

The views that may be displayed on the PC display.

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
ValueView Displayed
""Normal view.
"resource://spectatorCameraFrame"Entity camera view.
+ * @typedef Window.DisplayTexture + */ bool setDisplayTexture(const QString& name); + + /**jsdoc + * Check if a 2D point is within the drawable area of the HUD overlay. + * @function Window.isPointOnDesktopWindow + * @param {Vec2} point - The point to check in HMD display coordinates. + * @returns {boolean} true if the point is within the window or HUD, otherwise false. + */ bool isPointOnDesktopWindow(QVariant point); + + /**jsdoc + * Get the size of the drawable area of the Interface window if in desktop mode or the HMD display if in HMD mode. + * @function Window.getDeviceSize + * @returns {Vec2} The width and height of the Interface window or HMD display, in pixels. + */ glm::vec2 getDeviceSize() const; + /**jsdoc + * Open a non-modal message box that can have a variety of button combinations. See also, + * {@link Window.updateMessageBox|updateMessageBox} and {@link Window.closeMessageBox|closeMessageBox}. + * @function Window.openMessageBox + * @param {string} title - The title to display for the message box. + * @param {string} text - Text to display in the message box. + * @param {Window.MessageBoxButton} buttons - The buttons to display on the message box; one or more button values added + * together. + * @param {Window.MessageBoxButton} defaultButton - The button that has focus when the message box is opened. + * @returns {number} The ID of the message box created. + * @example Ask the user whether that want to reset something. + * var messageBox; + * var resetButton = 0x4000000; + * var cancelButton = 0x400000; + * + * function onMessageBoxClosed(id, button) { + * if (id === messageBox) { + * if (button === resetButton) { + * print("Reset"); + * } else { + * print("Don't reset"); + * } + * } + * } + * Window.messageBoxClosed.connect(onMessageBoxClosed); + * + * messageBox = Window.openMessageBox("Reset Something", + * "Do you want to reset something?", + * resetButton + cancelButton, cancelButton); + */ int openMessageBox(QString title, QString text, int buttons, int defaultButton); + + /**jsdoc + * Update the content of a message box that was opened with {@link Window.openMessageBox|openMessageBox}. + * @function Window.updateMessageBox + * @param {number} id - The ID of the message box. + * @param {string} title - The title to display for the message box. + * @param {string} text - Text to display in the message box. + * @param {Window.MessageBoxButton} buttons - The buttons to display on the message box; one or more button values added + * together. + * @param {Window.MessageBoxButton} defaultButton - The button that has focus when the message box is opened. + */ void updateMessageBox(int id, QString title, QString text, int buttons, int defaultButton); + + /**jsdoc + * Close a message box that was opened with {@link Window.openMessageBox|openMessageBox}. + * @function Window.closeMessageBox + * @param {number} id - The ID of the message box. + */ void closeMessageBox(int id); private slots: void onMessageBoxSelected(int button); signals: - void domainChanged(const QString& domainHostname); + + /**jsdoc + * Triggered when you change the domain you're visiting. Warning: Is not emitted if you go to domain that + * isn't running. + * @function Window.domainChanged + * @param {string} domain - The domain's IP address. + * @returns {Signal} + */ + void domainChanged(const QString& domain); + + /**jsdoc + * Triggered when you try to navigate to a *.svo or *.svo.json URL in a Web browser within Interface. + * @function Window.svoImportRequested + * @param {string} url - The URL of the SVO file to import/ + * @returns {Signal} + */ void svoImportRequested(const QString& url); + + /**jsdoc + * Triggered when you try to visit a domain but are refused connection. + * @function Window.domainConnectionRefused + * @param {string} reasonMessage - A description of the refusal. + * @param {Window.ConnectionRefusedReason} reasonCode - Integer number that enumerates the reason for the refusal. + * @param {string} extraInfo - Extra information about the refusal. + * @returns {Signal} + */ void domainConnectionRefused(const QString& reasonMessage, int reasonCode, const QString& extraInfo); + + /**jsdoc + * Triggered when a still snapshot has been taken by calling {@link Window.takeSnapshot|takeSnapshot}} with + * includeAnimated = false. + * @function Window.stillSnapshotTaken + * @param {string} pathStillSnapshot - The path and name of the snapshot image file. + * @param {boolean} notify - The value of the notify parameter that {@link Window.takeSnapshot|takeSnapshot} + * was called with. + * @returns {Signal} + */ void stillSnapshotTaken(const QString& pathStillSnapshot, bool notify); + + /**jsdoc + * Triggered when a snapshot submitted via {@link Window.shareSnapshot|shareSnapshot} is ready for sharing. The snapshot + * may then be shared via the {@link Account.metaverseServerURL} Web API. + * @function Window.snapshotShared + * @param {boolean} isError - true if an error was encountered preparing the snapshot for sharing, otherwise + * false. + * @param {string} reply - JSON-formatted information about the snapshot. + * @returns {Signal} + */ void snapshotShared(bool isError, const QString& reply); + + /**jsdoc + * Triggered when the snapshot images have been captured by {@link Window.takeSnapshot|takeSnapshot} and the GIF is + * starting to be processed. + * @function Window.processingGifStarted + * @param {string} pathStillSnapshot - The path and name of the still snapshot image file. + * @returns {Signal} + */ void processingGifStarted(const QString& pathStillSnapshot); + + /**jsdoc + * Triggered when a GIF has been prepared of the snapshot images captured by {@link Window.takeSnapshot|takeSnapshot}. + * @function Window.processingGifCompleted + * @param {string} pathAnimatedSnapshot - The path and name of the moving snapshot GIF file. + * @returns {Signal} + */ void processingGifCompleted(const QString& pathAnimatedSnapshot); + + /**jsdoc + * Triggered when you've successfully made a user connection. + * @function Window.connectionAdded + * @param {string} message - A description of the success. + * @returns {Signal} + */ void connectionAdded(const QString& connectionName); + + /**jsdoc + * Triggered when you failed to make a user connection. + * @function Window.connectionError + * @param {string} message - A description of the error. + * @returns {Signal} + */ void connectionError(const QString& errorString); + + /**jsdoc + * Triggered when a message is announced by {@link Window.displayAnnouncement|displayAnnouncement}. + * @function Window.announcement + * @param {string} message - The message text. + * @returns {Signal} + */ void announcement(const QString& message); + + /**jsdoc + * Triggered when the user closes a message box that was opened with {@link Window.openMessageBox|openMessageBox}. + * @function Window.messageBoxClosed + * @param {number} id - The ID of the message box that was closed. + * @param {number} button - The button that the user clicked. If the user presses Esc the Cancel button value is returned, + * whether or not the Cancel button is displayed in the message box. + * @returns {Signal} + */ void messageBoxClosed(int id, int button); + + /**jsdoc + * Triggered when the user chooses a directory in a {@link Window.browseDirAsync|browseDirAsync} dialog. + * @function Window.browseDirChanged + * @param {string} directory - The directory the user chose in the dialog. + * @returns {Signal} + */ void browseDirChanged(QString browseDir); + + /**jsdoc + * Triggered when the user chooses an asset in a {@link Window.browseAssetsAsync|browseAssetsAsync} dialog. + * @function Window.assetsDirChanged + * @param {string} asset - The path and name of the asset the user chose in the dialog. + * @returns {Signal} + */ void assetsDirChanged(QString assetsDir); + + /**jsdoc + * Triggered when the user specifies a file in a {@link Window.saveAsync|saveAsync} dialog. + * @function Window.saveFileChanged + * @param {string} filename - The path and name of the file that the user specified in the dialog. + * @returns {Signal} + */ void saveFileChanged(QString filename); + + /**jsdoc + * Triggered when the user chooses a file in a {@link Window.browseAsync|browseAsync} dialog. + * @function Window.openFileChanged + * @param {string} filename - The path and name of the file the user chose in the dialog. + * @returns {Signal} + */ void openFileChanged(QString filename); + + /**jsdoc + * Triggered when the user OKs a {@link Window.promptAsync|promptAsync} dialog. + * @function Window.promptTextChanged + * @param {string} text - The text the user entered in the dialog. + * @returns {Signal} + */ void promptTextChanged(QString text); - // triggered when window size or position changes + + /**jsdoc + * Triggered when the position or size of the Interface window changes. + * @function Window.geometryChanged + * @param {Rect} geometry - The position and size of the drawable area of the Interface window. + * @returns {Signal} + * @example Report the position of size of the Interface window when it changes. + * function onWindowGeometryChanged(rect) { + * print("Window geometry: " + JSON.stringify(rect)); + * } + * + * Window.geometryChanged.connect(onWindowGeometryChanged); + */ void geometryChanged(QRect geometry); private: diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index 7f89b47197..42ef2f627d 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -84,6 +84,46 @@ public: void softReset(); + /**jsdoc + *

The reasons that you may be refused connection to a domain are defined by numeric values:

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
ReasonValueDescription
Unknown0Some unknown reason.
ProtocolMismatch1The communications protocols of the domain and your Interface are not the same.
LoginError2You could not be logged into the domain.
NotAuthorized3You are not authorized to connect to the domain.
TooManyUsers4The domain already has its maximum number of users.
+ * @typedef Window.ConnectionRefusedReason + */ enum class ConnectionRefusedReason : uint8_t { Unknown, ProtocolMismatch, From 49bd62ca299d629c8cf133d32d19f329fdc00156 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Fri, 29 Dec 2017 14:04:36 -0700 Subject: [PATCH 04/25] Touch feeling for near grab --- interface/src/avatar/MySkeletonModel.cpp | 2 +- .../controllers/controllerDispatcher.js | 1 + scripts/system/libraries/handTouch.js | 395 ++++++++++++++++++ 3 files changed, 397 insertions(+), 1 deletion(-) create mode 100644 scripts/system/libraries/handTouch.js diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index f249be33ea..ede6a606c4 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -332,7 +332,7 @@ void MySkeletonModel::updateFingers() { } prevAbsRot = pose.getRotation(); } else { - _rig.clearJointAnimationPriority(index); + // _rig.clearJointAnimationPriority(index); } } } diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 51f927f224..24ac757f60 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -20,6 +20,7 @@ controllerDispatcherPluginsNeedSort = false; Script.include("/~/system/libraries/utils.js"); Script.include("/~/system/libraries/controllers.js"); Script.include("/~/system/libraries/controllerDispatcherUtils.js"); +Script.include("/~/system/libraries/handTouch.js"); (function() { Script.include("/~/system/libraries/pointersUtils.js"); diff --git a/scripts/system/libraries/handTouch.js b/scripts/system/libraries/handTouch.js new file mode 100644 index 0000000000..5f917f7247 --- /dev/null +++ b/scripts/system/libraries/handTouch.js @@ -0,0 +1,395 @@ +// +// scripts/system/libraries/handTouch.js +// +// Created by Luis Cuenca on 12/29/17 +// Copyright 2017 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(){ + + var animating = false; + var updateFingerWithIndex = 0; + var dataKeys = ["pinky", "ring", "middle", "index", "thumb"]; + + var grabPercent = { left: 0, + right: 0 }; + + var isGrabbing = false; + + var dataOpen = { + left: { + pinky: [{x: -0.18262, y:-2.666, z:-25.11229}, {x: 1.28845, y:0, z:1.06604}, {x: -3.967, y:0, z:-0.8351} ], + middle: [{x: -0.18262, y:0, z:-3.2809}, {x: 1.28845, y:0, z:-0.71834}, {x: -3.967, y:0, z:0.83978}], + ring: [{x: -0.18262, y:-1.11078, z:-16.24391}, {x: 1.28845, y:0, z:0.68153}, {x: -3.967, y:0, z:-0.69295}], + thumb: [{x: 5.207, y:2.595, z:38.40092}, {x: -9.869, y:11.755, z:10.50012}, {x: -9.778, y:9.647, z:15.16963}], + index: [{x: -0.18262, y:0.0099, z:2.28085}, {x: 1.28845, y:-0.01107, z:0.93037}, {x: -3.967, y:0, z:-2.64018}] + }, right: { + pinky: [{x: -0.111, y: 2.66601, z: 12.06423}, {x: 1.217, y: 0, z: -1.03973}, {x: -3.967, y: 0, z: 0.86424}], + middle: [{x: -0.111, y: 0, z: 3.26538}, {x: 1.217, y: 0, z: 0.71427}, {x: -3.967, y: 0, z: -0.85103}], + ring: [{x: -0.111, y: 1.11101, z: 3.56312}, {x: 1.217, y: 0, z: -0.64524}, {x: -3.967, y: 0, z: 0.69807}], + thumb: [{x: 5.207, y: -2.595, z: -38.26131}, {x: -9.869, y: -11.755, z: -10.51778}, {x: -9.77799, y: -9.647, z: -15.10783}], + index: [{x: -0.111, y: 0, z: -2.2816}, {x: 1.217, y: 0, z: -0.90168}, {x: -3.967, y: 0, z: 2.62649}] + } + } + + var dataClose = { + left: { + pinky:[{x: 75.45709, y:-8.01347, z:-22.54823}, {x: 69.562, y:0, z:1.06604}, {x: 74.73801, y:0, z:-0.8351}], + middle: [{x: 66.0237, y:-2.42536, z:-6.13193}, {x: 72.63042, y:0, z:-0.71834}, {x: 75.19901, y:0, z:0.83978}], + ring: [{x: 71.52988, y:-2.35423, z:-16.21694}, {x: 64.44739, y:0, z:0.68153}, {x: 70.518, y:0, z:-0.69295}], + thumb: [{x: 33.83371, y:-15.19106, z:34.66116}, {x: 0, y:0, z:-43.42915}, {x: 0, y:0, z:-30.18613}], + index: [{x: 35.56082, y:-1.21056, z:-2.07362}, {x: 79.79845, y:-0.01107, z:0.93037}, {x: 68.767, y:0, z:-2.64018}] + }, right: { + pinky:[{x: 75.45702, y: 8.013, z: 22.41022}, {x: 69.562, y: 0, z: -1.03973}, {x: 74.738, y: 0, z: 0.86424}], + middle: [{x: 66.02399, y: 2.425, z: 6.11638}, {x: 72.63002, y: 0, z: 0.71427}, {x: 72.63, y: 0, z: -0.85103}], + ring: [{x: 71.53, y: 5.022, z: 16.33612}, {x: 64.447, y: 0, z: -0.64524}, {x: 70.51801, y: 0, z: 0.69807}], + thumb: [{x: 33.834, y: 15.191, z: -34.52131}, {x: 0, y: 0, z: 43.41122}, {x: 0, y: 0, z: 30.24818}], + index: [{x: 35.633, y: 1.215, z: -6.6376}, {x: 79.72701, y: 0, z: -0.90168}, {x: 68.76701, y: 0, z: 2.62649}] + } + } + + var dataCurrent = { + left:{ + pinky:[{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], + middle: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], + ring: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], + thumb: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], + index: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}] + }, + right:{ + pinky:[{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], + middle: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], + ring: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], + thumb: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], + index: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}] + } + } + + var dataDelta = { + left:{ + pinky:[{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], + middle: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], + ring: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], + thumb: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], + index: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}] + }, + right:{ + pinky:[{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], + middle: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], + ring: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], + thumb: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], + index: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}] + } + } + + var animationSteps = 5; + + var showSphere = false; + var showLines = false; + + var fingerRays = { + left:{pinky: undefined, middle: undefined, ring: undefined, thumb: undefined, index: undefined}, + right:{pinky: undefined, middle: undefined, ring: undefined, thumb: undefined, index: undefined} + }; + + var varsToDebug = { + toggleDebugSphere: function(){ + showSphere = !showSphere; + }, + toggleDebugLines: function(){ + showLines = !showLines; + }, + fingerPercent: { + left: { + pinky: 0.5, + middle: 0.5, + ring: 0.5, + thumb: 0.5, + index: 0.5 + } , + right: { + pinky: 0.5, + middle: 0.5, + ring: 0.5, + thumb: 0.5, + index: 0.5 + } + }, + triggerValues: { + leftTriggerValue: 0, + leftTriggerClicked: 0, + rightTriggerValue: 0, + rightTriggerClicked: 0, + leftSecondaryValue: 0, + rightSecondaryValue: 0 + } + } + + function addVals(val1, val2, sign) { + var val = []; + if (val1.length != val2.length) return; + for (var i = 0; i < val1.length; i++) { + val.push({x: 0, y: 0, z: 0}); + val[i].x = val1[i].x + sign*val2[i].x; + val[i].y = val1[i].y + sign*val2[i].y; + val[i].z = val1[i].z + sign*val2[i].z; + } + return val; + } + + function multiplyValsBy(val1, num) { + var val = []; + for (var i = 0; i < val1.length; i++) { + val.push({x: 0, y: 0, z: 0}); + val[i].x = val1[i].x * num; + val[i].y = val1[i].y * num; + val[i].z = val1[i].z * num; + } + return val; + } + + function getJointDistances(jointNamesArray) { + var result = {distances: [], totalDistance: 0} + for (var i = 1; i < jointNamesArray.length; i++) { + var index0 = MyAvatar.getJointIndex(jointNamesArray[i-1]); + var index1 = MyAvatar.getJointIndex(jointNamesArray[i]); + var pos0 = MyAvatar.getJointPosition(index0); + var pos1 = MyAvatar.getJointPosition(index1); + var distance = Vec3.distance(pos0, pos1); + result.distances.push(distance); + result.totalDistance += distance; + } + return result; + } + + function estimatePalmData(side) { + var data = {position: undefined, perpendicular: undefined, distance: undefined, fingers: {pinky: undefined, middle: undefined, ring: undefined, thumb: undefined, index: undefined}}; + + // Calculate the finger lengths by adding its joint lengths + + var jointOffset = { x: 0, y: 0, z: 0 }; + var upperSide = side[0].toUpperCase() + side.substring(1); + var jointIndexHand = MyAvatar.getJointIndex(upperSide + "Hand"); + var worldPosHand = MyAvatar.jointToWorldPoint(jointOffset, jointIndexHand); + var minusWorldPosHand = {x:-worldPosHand.x, y:-worldPosHand.y, z:-worldPosHand.z}; + + var directions = {pinky: undefined, middle: undefined, ring: undefined, thumb: undefined, index: undefined}; + var positions = {pinky: undefined, middle: undefined, ring: undefined, thumb: undefined, index: undefined}; + + var sumvec = {x:0, y:0, z:0}; + sumvec = Vec3.sum(worldPosHand, sumvec); + + var thumbLength = 0; + + for (var i = 0; i < dataKeys.length; i++) { + var finger = dataKeys[i]; + var jointNames = getJointNames(upperSide, finger, 4); + var fingerLength = getJointDistances(jointNames).totalDistance; + + var jointIndex = MyAvatar.getJointIndex(jointNames[0]); + positions[finger] = MyAvatar.jointToWorldPoint(jointOffset, jointIndex); + directions[finger] = Vec3.normalize(Vec3.sum(positions[finger], minusWorldPosHand)); + data.fingers[finger] = Vec3.sum(positions[finger], Vec3.multiply(fingerLength, directions[finger])); + if (finger != "thumb") { + sumvec = Vec3.sum(Vec3.multiply(2, positions[finger]), sumvec); + } else { + thumbLength = fingerLength; + } + } + + data.perpendicular = side == "right" ? Vec3.normalize(Vec3.cross(directions["ring"], directions["pinky"])) : Vec3.normalize(Vec3.cross(directions["pinky"],directions["ring"])); + + data.position = Vec3.multiply(1.0/9, sumvec); + data.distance = 1.55*Vec3.distance(data.position, positions["index"]); + + // move back thumb check up origin + + data.fingers["thumb"] = Vec3.sum(data.fingers["thumb"], Vec3.multiply( -0.2 * thumbLength, data.perpendicular)); + + return data; + } + + Script.registerValue("GlobalDebugger", varsToDebug); + + for (var i = 0; i < dataKeys.length; i++) { + fingerRays["left"][dataKeys[i]] = Overlays.addOverlay("line3d", { + color: { red: 0, green: 0, blue: 255 }, + start: { x:0, y:0, z:0 }, + end: { x:0, y:1, z:0 }, + visible: showLines + }); + fingerRays["right"][dataKeys[i]] = Overlays.addOverlay("line3d", { + color: { red: 0, green: 0, blue: 255 }, + start: { x:0, y:0, z:0 }, + end: { x:0, y:1, z:0 }, + visible: showLines + }); + } + + var palmRay = { + left: Overlays.addOverlay("line3d", { + color: { red: 255, green: 0, blue: 0 }, + start: { x:0, y:0, z:0 }, + end: { x:0, y:1, z:0 }, + visible: showLines + }), + right: Overlays.addOverlay("line3d", { + color: { red: 255, green: 0, blue: 0 }, + start: { x:0, y:0, z:0 }, + end: { x:0, y:1, z:0 }, + visible: showLines + }) + } + + var sphereHand = { + right: Overlays.addOverlay("sphere", { + position: MyAvatar.position, + color: { red: 0, green: 255, blue: 0 }, + scale: { x: 0.01, y: 0.01, z: 0.01 }, + visible: showSphere + }), + left: Overlays.addOverlay("sphere", { + position: MyAvatar.position, + color: { red: 0, green: 255, blue: 0 }, + scale: { x: 0.01, y: 0.01, z: 0.01 }, + visible: showSphere + }) + } + + function updateSphereHand(side) { + var palmData = estimatePalmData(side); + var dist = 1.5*palmData.distance; + var palmPoint = palmData.position; + + var checkOffset = { x: palmData.perpendicular.x * dist, + y: palmData.perpendicular.y * dist, + z: palmData.perpendicular.z * dist }; + + var spherePos = Vec3.sum(palmPoint, checkOffset); + var checkPoint = Vec3.sum(palmPoint, Vec3.multiply(2, checkOffset)); + + Overlays.editOverlay(palmRay[side], { + start: palmPoint, + end: checkPoint, + visible: showLines + }); + + Overlays.editOverlay(sphereHand[side], { + position: spherePos, + scale: { + x: 2*dist, + y: 2*dist, + z: 2*dist + }, + visible: showSphere + }); + var pickRays = []; + for (var i = 0; i < dataKeys.length; i++) { + Overlays.editOverlay(fingerRays[side][dataKeys[i]], { + start: palmData.fingers[dataKeys[i]], + end: checkPoint, + visible: showLines + }); + } + var finger = dataKeys[updateFingerWithIndex]; + + var grabbables = Entities.findEntities(spherePos, dist); + + if (grabbables.length > 0) { + var origin = palmData.fingers[finger]; + var direction = Vec3.normalize(Vec3.subtract(checkPoint, origin)); + var intersection = Entities.findRayIntersection({origin: origin, direction: direction}, true, grabbables, [], true, false); + var percent = 0.5; + if (intersection.intersects && intersection.distance < 2.5*dist) { + percent = intersection.distance/(2.5*dist); + var grabMultiplier = finger === "thumb" ? 0.2 : 0.05; + percent += grabMultiplier * grabPercent[side]; + } + varsToDebug.fingerPercent[side][finger] = percent; + } + var totalDistance = addVals(dataClose[side][finger], dataOpen[side][finger], -1); + var percent = varsToDebug.fingerPercent[side][finger]; + var newFingerData = addVals(dataOpen[side][finger], multiplyValsBy(totalDistance, percent), 1); + + // Assign animation interpolation steps + dataDelta[side][finger] = multiplyValsBy(addVals(newFingerData, dataCurrent[side][finger], -1), 1.0/animationSteps); + } + + function getJointNames(side, finger, count) { + var names = []; + for (var i = 1; i < count+1; i++) { + var name = side+"Hand"+finger[0].toUpperCase()+finger.substring(1)+(i); + names.push(name); + } + return names; + } + + var leftTriggerPress = function (value) { + varsToDebug.triggerValues.leftTriggerValue = value; + grabPercent["left"] = value; + }; + var leftTriggerClick = function (value) { + varsToDebug.triggerValues.leftTriggerClicked = value; + }; + var rightTriggerPress = function (value) { + varsToDebug.triggerValues.rightTriggerValue = value; + grabPercent["right"] = value; + }; + var rightTriggerClick = function (value) { + varsToDebug.triggerValues.rightTriggerClicked = value; + }; + var leftSecondaryPress = function (value) { + varsToDebug.triggerValues.leftSecondaryValue = value; + }; + var rightSecondaryPress = function (value) { + varsToDebug.triggerValues.rightSecondaryValue = value; + }; + + var MAPPING_NAME = "com.highfidelity.handTouch"; + var mapping = Controller.newMapping(MAPPING_NAME); + mapping.from([Controller.Standard.RT]).peek().to(rightTriggerPress); + mapping.from([Controller.Standard.RTClick]).peek().to(rightTriggerClick); + mapping.from([Controller.Standard.LT]).peek().to(leftTriggerPress); + mapping.from([Controller.Standard.LTClick]).peek().to(leftTriggerClick); + + mapping.from([Controller.Standard.RB]).peek().to(rightSecondaryPress); + mapping.from([Controller.Standard.LB]).peek().to(leftSecondaryPress); + mapping.from([Controller.Standard.LeftGrip]).peek().to(leftSecondaryPress); + mapping.from([Controller.Standard.RightGrip]).peek().to(rightSecondaryPress); + + Controller.enableMapping(MAPPING_NAME); + + Script.update.connect(function(){ + updateFingerWithIndex = updateFingerWithIndex < dataKeys.length-1 ? updateFingerWithIndex + 1 : 0; + + updateSphereHand("right"); + updateSphereHand("left"); + + for (var i = 0; i < dataKeys.length; i++) { + var finger = dataKeys[i]; + var names = getJointNames("Right", finger, 3); + dataCurrent["right"][finger] = addVals(dataCurrent["right"][finger], dataDelta["right"][finger], 1); + for (var j = 0; j < names.length; j++) { + var index = MyAvatar.getJointIndex(names[j]); + var quatRot = Quat.fromVec3Degrees(dataCurrent["right"][finger][j]); + MyAvatar.setJointRotation(index, quatRot); + } + } + + for (var i = 0; i < dataKeys.length; i++) { + var finger = dataKeys[i]; + var names = getJointNames("Left", finger, 3); + dataCurrent["left"][finger] = addVals(dataCurrent["left"][finger], dataDelta["left"][finger], 1); + for (var j = 0; j < names.length; j++) { + var index = MyAvatar.getJointIndex(names[j]); + var quatRot = Quat.fromVec3Degrees(dataCurrent["left"][finger][j]); + MyAvatar.setJointRotation(index, quatRot); + } + } + }); + +}()) From 47dbb0c168ad92bba5825a250ca0fee2ae4b2dbb Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Fri, 29 Dec 2017 15:35:44 -0700 Subject: [PATCH 05/25] load script manually --- scripts/system/controllers/controllerDispatcher.js | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 24ac757f60..51f927f224 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -20,7 +20,6 @@ controllerDispatcherPluginsNeedSort = false; Script.include("/~/system/libraries/utils.js"); Script.include("/~/system/libraries/controllers.js"); Script.include("/~/system/libraries/controllerDispatcherUtils.js"); -Script.include("/~/system/libraries/handTouch.js"); (function() { Script.include("/~/system/libraries/pointersUtils.js"); From 28d9409bf064d4cbee58a608da2a8af40ddbd040 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Thu, 4 Jan 2018 11:37:37 -0700 Subject: [PATCH 06/25] Fix default hand poses --- interface/resources/qml/js/Utils.jsc | Bin 6596 -> 0 bytes interface/src/avatar/MySkeletonModel.cpp | 11 +- interface/src/avatar/MySkeletonModel.h | 2 + scripts/defaultScripts.js | 3 +- .../{libraries => controllers}/handTouch.js | 198 +++++++++++++----- 5 files changed, 159 insertions(+), 55 deletions(-) delete mode 100644 interface/resources/qml/js/Utils.jsc rename scripts/system/{libraries => controllers}/handTouch.js (76%) diff --git a/interface/resources/qml/js/Utils.jsc b/interface/resources/qml/js/Utils.jsc deleted file mode 100644 index ab20e996b9469915ac6a89901da175143e6b5024..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6596 zcmcIoZD?EP6@Kh1ckah_<+cmm)~+s7*+{cEDOm|)Y*uPBiCux$Iwl#JYtNP&7t3}W zIXI&o4oU8EkM2n zXjl7O2dDtRuNCd{@)jY-)d!#zenqKY4sn&W{weu)twmlb!$4JTzv65rm+K1vDC#!F zU$|ZVIZ9zlW+S<`HL;hgR&M8~1sQiLyXg~6*Kx4qHtya=?){4Iz`gR%B^6)FqD(EX zPx0NgjXdr9YAbNP75KOnxYqy<89DFo`<=jA05hh+!CUJc)7q5K-I-U<4BwMhw#!Kold03qHB1qPh=(m57jV zQZO*TlsKpO<^4D&)ji|-UBpF<$-`KmCFTK~acFxKqv8qb`vJ}(jwnv6ynF{ogq(=F zep_Ap)wNGud)4)O>UtbUguEF{VOB&&#)x21=BI^yoeI;=cpet< z8yEavN0<0LF8)1^DU2eDE}X7EzTC=9#(VVvU%?VLXPX@E8u^tC$cdhzrY~ z5|TzSE|9{#2}}z<=It55SzSj%iNA*yHv6nZbjwP_N>*aF82-K7|F|4Z{3((gYY*l1 z1HmPNES&HK!VAmo!G#Z7%nSc2m&=jFSbJz;vopy0hR~Acw-O%(%P$^o0YVGQoxRBc zfA58N%nJ{-l0bHGCbwg^#q(qrrDi+!w<_$cdfT#JQtVa6B|0HZKTvD>SPT8{3FK*i zWU(iZU!oSteUbW{j!MrrowDh%hwMA*Wz%5~+1G_Km+UHco!!3?d2`uawWQAO`Fhz^ zFY9DKQ7^k1!8+Li%B~H>BZmbX7dAVWSkh#gI3&0r zaF0rAW;@0YRTR^~w|fH1=rq7T1!gIn>dAFt!Sy*C45#|hZ zyKMQfPE=zR8BZ12Iu&e;jZ`qZ;(XBNe0X7VdI#CX&hyK$=>L{qIw{h|R_2l4RI7AY z^-G-Fg*Pl@c)U6&I`)6~vWKqD3?b!JxCu1uN`cA?Q-i-75ra?*w4> z7DLeZEXfck*xy13exhOT)~XM$^JKYWuGBjYFUK{J-kQV9*AmCcI=9Ipz3t&zt&~o& zbp5o=j@d0M5iMDXSkX$%t~YJ^dxYvnP2X7@RT0fd+JSJgHI(cgqRD9&=_E-h7ipTL zi!Ra}N%Kz9JRJ@v&Ijf=afs?ImCUopaAUf5Bc(P@)^4QL#!&4B4=hC;lN+q^_W+Q1 zP^n|i{5|@(6kNweRp@!g!IB0$&XzP(9WOq8UKl6|MM0Czbbp-MJtdAmS4|C?43SDf)z9}dNlLtGo5xl*UM-i)V9TyEvOFWw5I(m_-&8|fx011{`>NNH;(6Df(rr#QsN>-}$CJna|wOpzoRcq-Fq`bAH8fghvwNyJ`N_RlpJYiGQZuHu}HZ5Pa zdE8c|q5`8P==g;$uId?L_>-ZdTT7Ja(9^jWIyw}4%`oqUj`vkn%QKo+>hkt%Am^R8 zT%)}GyrqJUtqMAFDsNlV@getLocalOrientation(); _rig.computeMotionAnimationState(deltaTime, position, velocity, orientation, ccState); - // evaluate AnimGraph animation and update jointStates. + //// evaluate AnimGraph animation and update jointStates. Model::updateRig(deltaTime, parentTransform); Rig::EyeParameters eyeParams; @@ -323,17 +323,22 @@ void MySkeletonModel::updateFingers() { for (auto& link : chain) { int index = _rig.indexOfJoint(link.second); if (index >= 0) { + if (_jointRotationFrameOffsetMap.find(index) == _jointRotationFrameOffsetMap.end()) { + _jointRotationFrameOffsetMap.insert(std::pair(index, 0)); + } auto pose = myAvatar->getControllerPoseInSensorFrame(link.first); if (pose.valid) { glm::quat relRot = glm::inverse(prevAbsRot) * pose.getRotation(); // only set the rotation for the finger joints, not the hands. if (link.first != controller::Action::LEFT_HAND && link.first != controller::Action::RIGHT_HAND) { _rig.setJointRotation(index, true, relRot, CONTROLLER_PRIORITY); + _jointRotationFrameOffsetMap.find(index)->second = 0; } prevAbsRot = pose.getRotation(); - } else { - // _rig.clearJointAnimationPriority(index); + } else if (_jointRotationFrameOffsetMap.find(index)->second == 1){ + _rig.clearJointAnimationPriority(index); } + _jointRotationFrameOffsetMap.find(index)->second++; } } } diff --git a/interface/src/avatar/MySkeletonModel.h b/interface/src/avatar/MySkeletonModel.h index d9f57a439a..252b6c293b 100644 --- a/interface/src/avatar/MySkeletonModel.h +++ b/interface/src/avatar/MySkeletonModel.h @@ -28,6 +28,8 @@ private: AnimPose _prevHips; // sensor frame bool _prevHipsValid { false }; + + std::map _jointRotationFrameOffsetMap; }; #endif // hifi_MySkeletonModel_h diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index 89d4c75ae4..c14cea63f2 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -32,7 +32,8 @@ var DEFAULT_SCRIPTS_COMBINED = [ "system/tablet-ui/tabletUI.js" ]; var DEFAULT_SCRIPTS_SEPARATE = [ - "system/controllers/controllerScripts.js" + "system/controllers/controllerScripts.js", + "system/controllers/handTouch.js" //"system/chat.js" ]; diff --git a/scripts/system/libraries/handTouch.js b/scripts/system/controllers/handTouch.js similarity index 76% rename from scripts/system/libraries/handTouch.js rename to scripts/system/controllers/handTouch.js index 5f917f7247..34f11aa764 100644 --- a/scripts/system/libraries/handTouch.js +++ b/scripts/system/controllers/handTouch.js @@ -11,15 +11,36 @@ (function(){ - var animating = false; var updateFingerWithIndex = 0; + + // Keys to access finger data var dataKeys = ["pinky", "ring", "middle", "index", "thumb"]; + // Additionally close the hands to achieve a grabbing effect var grabPercent = { left: 0, right: 0 }; - var isGrabbing = false; + // var isGrabbing = false; + + // Store which fingers are touching - if all false restate the default poses + var isTouching = { + left: { + pinky: false, + middle: false, + ring: false, + thumb: false, + index: false + }, right: { + pinky: false, + middle: false, + ring: false, + thumb: false, + index: false + } + } + // joint data for opened pose + var dataOpen = { left: { pinky: [{x: -0.18262, y:-2.666, z:-25.11229}, {x: 1.28845, y:0, z:1.06604}, {x: -3.967, y:0, z:-0.8351} ], @@ -35,6 +56,8 @@ index: [{x: -0.111, y: 0, z: -2.2816}, {x: 1.217, y: 0, z: -0.90168}, {x: -3.967, y: 0, z: 2.62649}] } } + + // joint data for closed hand var dataClose = { left: { @@ -52,6 +75,8 @@ } } + // joint data for the current frame + var dataCurrent = { left:{ pinky:[{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], @@ -68,6 +93,8 @@ index: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}] } } + + // interpolated values on joint data to smooth movement var dataDelta = { left:{ @@ -86,16 +113,24 @@ } } + // Acquire an updated value per hand every 5 frames + var animationSteps = 5; + // Debugging info + var showSphere = false; var showLines = false; + // store the rays for the fingers - only for debug purposes + var fingerRays = { left:{pinky: undefined, middle: undefined, ring: undefined, thumb: undefined, index: undefined}, right:{pinky: undefined, middle: undefined, ring: undefined, thumb: undefined, index: undefined} }; + // Register object with API Debugger + var varsToDebug = { toggleDebugSphere: function(){ showSphere = !showSphere; @@ -105,18 +140,18 @@ }, fingerPercent: { left: { - pinky: 0.5, - middle: 0.5, - ring: 0.5, - thumb: 0.5, - index: 0.5 + pinky: 0.75, + middle: 0.75, + ring: 0.75, + thumb: 0.75, + index: 0.75 } , right: { - pinky: 0.5, - middle: 0.5, - ring: 0.5, - thumb: 0.5, - index: 0.5 + pinky: 0.75, + middle: 0.75, + ring: 0.75, + thumb: 0.75, + index: 0.75 } }, triggerValues: { @@ -128,6 +163,8 @@ rightSecondaryValue: 0 } } + + // Add/Subtract the joint data - per finger joint function addVals(val1, val2, sign) { var val = []; @@ -141,6 +178,8 @@ return val; } + // Multiply/Divide the joint data - per finger joint + function multiplyValsBy(val1, num) { var val = []; for (var i = 0; i < val1.length; i++) { @@ -151,6 +190,8 @@ } return val; } + + // Calculate the finger lengths by adding its joint lengths function getJointDistances(jointNamesArray) { var result = {distances: [], totalDistance: 0} @@ -165,23 +206,29 @@ } return result; } + + // Calculate the sphere that look up for entities, the center of the palm, perpendicular vector from the palm plane and origin of the the finger rays function estimatePalmData(side) { - var data = {position: undefined, perpendicular: undefined, distance: undefined, fingers: {pinky: undefined, middle: undefined, ring: undefined, thumb: undefined, index: undefined}}; - - // Calculate the finger lengths by adding its joint lengths + // Return data object + var data = {position: undefined, perpendicular: undefined, distance: undefined, fingers: {pinky: undefined, middle: undefined, ring: undefined, thumb: undefined, index: undefined}}; var jointOffset = { x: 0, y: 0, z: 0 }; + var upperSide = side[0].toUpperCase() + side.substring(1); var jointIndexHand = MyAvatar.getJointIndex(upperSide + "Hand"); + + // Store position of the hand joint var worldPosHand = MyAvatar.jointToWorldPoint(jointOffset, jointIndexHand); var minusWorldPosHand = {x:-worldPosHand.x, y:-worldPosHand.y, z:-worldPosHand.z}; + // Data for finger rays var directions = {pinky: undefined, middle: undefined, ring: undefined, thumb: undefined, index: undefined}; var positions = {pinky: undefined, middle: undefined, ring: undefined, thumb: undefined, index: undefined}; - var sumvec = {x:0, y:0, z:0}; - sumvec = Vec3.sum(worldPosHand, sumvec); + // Calculate palm center + var palmCenter = {x:0, y:0, z:0}; + palmCenter = Vec3.sum(worldPosHand, palmCenter); var thumbLength = 0; @@ -195,15 +242,17 @@ directions[finger] = Vec3.normalize(Vec3.sum(positions[finger], minusWorldPosHand)); data.fingers[finger] = Vec3.sum(positions[finger], Vec3.multiply(fingerLength, directions[finger])); if (finger != "thumb") { - sumvec = Vec3.sum(Vec3.multiply(2, positions[finger]), sumvec); + palmCenter = Vec3.sum(Vec3.multiply(2, positions[finger]), palmCenter); // Hand joint + 2 * 4 fingers(no thumb) = 9 } else { thumbLength = fingerLength; } } data.perpendicular = side == "right" ? Vec3.normalize(Vec3.cross(directions["ring"], directions["pinky"])) : Vec3.normalize(Vec3.cross(directions["pinky"],directions["ring"])); - - data.position = Vec3.multiply(1.0/9, sumvec); + + + data.position = Vec3.multiply(1.0/9, palmCenter); // Hand joint + 2 * 4 fingers(no thumb) = 9 + data.distance = 1.55*Vec3.distance(data.position, positions["index"]); // move back thumb check up origin @@ -213,8 +262,11 @@ return data; } + // Register GlobalDebugger for API Debugger Script.registerValue("GlobalDebugger", varsToDebug); + // Create debug overlays - finger rays + palm rays + spheres + for (var i = 0; i < dataKeys.length; i++) { fingerRays["left"][dataKeys[i]] = Overlays.addOverlay("line3d", { color: { red: 0, green: 0, blue: 255 }, @@ -261,14 +313,18 @@ } function updateSphereHand(side) { + var palmData = estimatePalmData(side); - var dist = 1.5*palmData.distance; var palmPoint = palmData.position; - + var dist = 1.5*palmData.distance; + + // Situate the debugging overlays + var checkOffset = { x: palmData.perpendicular.x * dist, y: palmData.perpendicular.y * dist, z: palmData.perpendicular.z * dist }; - + + var spherePos = Vec3.sum(palmPoint, checkOffset); var checkPoint = Vec3.sum(palmPoint, Vec3.multiply(2, checkOffset)); @@ -287,7 +343,7 @@ }, visible: showSphere }); - var pickRays = []; + for (var i = 0; i < dataKeys.length; i++) { Overlays.editOverlay(fingerRays[side][dataKeys[i]], { start: palmData.fingers[dataKeys[i]], @@ -295,6 +351,9 @@ visible: showLines }); } + + // Update the intersection of only one finger at a time + var finger = dataKeys[updateFingerWithIndex]; var grabbables = Entities.findEntities(spherePos, dist); @@ -303,14 +362,19 @@ var origin = palmData.fingers[finger]; var direction = Vec3.normalize(Vec3.subtract(checkPoint, origin)); var intersection = Entities.findRayIntersection({origin: origin, direction: direction}, true, grabbables, [], true, false); - var percent = 0.5; - if (intersection.intersects && intersection.distance < 2.5*dist) { + var percent = 0.75; + var isAbleToGrab = intersection.intersects && intersection.distance < 2.5*dist; + // Store if this finger is touching something + isTouching[side][finger] = isAbleToGrab; + if (isAbleToGrab) { + // update the open/close percentage for this finger percent = intersection.distance/(2.5*dist); var grabMultiplier = finger === "thumb" ? 0.2 : 0.05; percent += grabMultiplier * grabPercent[side]; } - varsToDebug.fingerPercent[side][finger] = percent; + varsToDebug.fingerPercent[side][finger] = percent; // store the current open/close percentage } + // Calculate new interpolation data var totalDistance = addVals(dataClose[side][finger], dataOpen[side][finger], -1); var percent = varsToDebug.fingerPercent[side][finger]; var newFingerData = addVals(dataOpen[side][finger], multiplyValsBy(totalDistance, percent), 1); @@ -319,6 +383,7 @@ dataDelta[side][finger] = multiplyValsBy(addVals(newFingerData, dataCurrent[side][finger], -1), 1.0/animationSteps); } + // Recreate the finger joint names - and how many 0 to count function getJointNames(side, finger, count) { var names = []; for (var i = 1; i < count+1; i++) { @@ -328,8 +393,11 @@ return names; } + // Capture the controller values + var leftTriggerPress = function (value) { varsToDebug.triggerValues.leftTriggerValue = value; + // the value for the trigger increments the hand-close percentage grabPercent["left"] = value; }; var leftTriggerClick = function (value) { @@ -337,6 +405,7 @@ }; var rightTriggerPress = function (value) { varsToDebug.triggerValues.rightTriggerValue = value; + // the value for the trigger increments the hand-close percentage grabPercent["right"] = value; }; var rightTriggerClick = function (value) { @@ -362,34 +431,61 @@ mapping.from([Controller.Standard.RightGrip]).peek().to(rightSecondaryPress); Controller.enableMapping(MAPPING_NAME); - + + function getTouching(side) { + var animating = false; + for (var i = 0; i < dataKeys.length; i++) { + var finger = dataKeys[i]; + animating = animating || isTouching[side][finger]; + } + return animating; + } + Script.update.connect(function(){ - updateFingerWithIndex = updateFingerWithIndex < dataKeys.length-1 ? updateFingerWithIndex + 1 : 0; + + // iterate fingers + + updateFingerWithIndex = (updateFingerWithIndex < dataKeys.length-1) ? updateFingerWithIndex + 1 : 0; + // precalculate data + updateSphereHand("right"); updateSphereHand("left"); - - for (var i = 0; i < dataKeys.length; i++) { - var finger = dataKeys[i]; - var names = getJointNames("Right", finger, 3); - dataCurrent["right"][finger] = addVals(dataCurrent["right"][finger], dataDelta["right"][finger], 1); - for (var j = 0; j < names.length; j++) { - var index = MyAvatar.getJointIndex(names[j]); - var quatRot = Quat.fromVec3Degrees(dataCurrent["right"][finger][j]); - MyAvatar.setJointRotation(index, quatRot); - } - } - - for (var i = 0; i < dataKeys.length; i++) { - var finger = dataKeys[i]; - var names = getJointNames("Left", finger, 3); - dataCurrent["left"][finger] = addVals(dataCurrent["left"][finger], dataDelta["left"][finger], 1); - for (var j = 0; j < names.length; j++) { - var index = MyAvatar.getJointIndex(names[j]); - var quatRot = Quat.fromVec3Degrees(dataCurrent["left"][finger][j]); - MyAvatar.setJointRotation(index, quatRot); - } - } + + // Assign interpolated values + var i, j, index; + for (i = 0; i < dataKeys.length; i++) { + var finger = dataKeys[i]; + var names = getJointNames("Right", finger, 3); + dataCurrent["right"][finger] = addVals(dataCurrent["right"][finger], dataDelta["right"][finger], 1); + for (j = 0; j < names.length; j++) { + index = MyAvatar.getJointIndex(names[j]); + // if no finger is touching restate the default poses + if (getTouching("right")) { + var quatRot = Quat.fromVec3Degrees(dataCurrent["right"][finger][j]); + MyAvatar.setJointRotation(index, quatRot); + } else { + MyAvatar.clearJointData(index); + } + } + } + + for (i = 0; i < dataKeys.length; i++) { + var finger = dataKeys[i]; + var names = getJointNames("Left", finger, 3); + dataCurrent["left"][finger] = addVals(dataCurrent["left"][finger], dataDelta["left"][finger], 1); + for (j = 0; j < names.length; j++) { + index = MyAvatar.getJointIndex(names[j]); + // if no finger is touching restate the default poses + if (getTouching("left")) { + var quatRot = Quat.fromVec3Degrees(dataCurrent["left"][finger][j]); + MyAvatar.setJointRotation(index, quatRot); + } else { + MyAvatar.clearJointData(index); + } + } + } + }); }()) From ef0164c2725aeee055b38f2cd51698f44779bfa9 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Thu, 4 Jan 2018 11:43:51 -0700 Subject: [PATCH 07/25] Fix tabs --- interface/src/avatar/MySkeletonModel.cpp | 2 +- scripts/defaultScripts.js | 2 +- scripts/system/controllers/handTouch.js | 214 +++++++++++------------ 3 files changed, 109 insertions(+), 109 deletions(-) diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 0448578036..1068b371ed 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -222,7 +222,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { auto orientation = myAvatar->getLocalOrientation(); _rig.computeMotionAnimationState(deltaTime, position, velocity, orientation, ccState); - //// evaluate AnimGraph animation and update jointStates. + // evaluate AnimGraph animation and update jointStates. Model::updateRig(deltaTime, parentTransform); Rig::EyeParameters eyeParams; diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index c14cea63f2..4ab6134ab8 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -33,7 +33,7 @@ var DEFAULT_SCRIPTS_COMBINED = [ ]; var DEFAULT_SCRIPTS_SEPARATE = [ "system/controllers/controllerScripts.js", - "system/controllers/handTouch.js" + "system/controllers/handTouch.js" //"system/chat.js" ]; diff --git a/scripts/system/controllers/handTouch.js b/scripts/system/controllers/handTouch.js index 34f11aa764..2e1cf99166 100644 --- a/scripts/system/controllers/handTouch.js +++ b/scripts/system/controllers/handTouch.js @@ -12,17 +12,17 @@ (function(){ var updateFingerWithIndex = 0; - - // Keys to access finger data + + // Keys to access finger data var dataKeys = ["pinky", "ring", "middle", "index", "thumb"]; - // Additionally close the hands to achieve a grabbing effect + // Additionally close the hands to achieve a grabbing effect var grabPercent = { left: 0, right: 0 }; // var isGrabbing = false; - - // Store which fingers are touching - if all false restate the default poses + + // Store which fingers are touching - if all false restate the default poses var isTouching = { left: { pinky: false, @@ -39,8 +39,8 @@ } } - // joint data for opened pose - + // joint data for opened pose + var dataOpen = { left: { pinky: [{x: -0.18262, y:-2.666, z:-25.11229}, {x: 1.28845, y:0, z:1.06604}, {x: -3.967, y:0, z:-0.8351} ], @@ -56,8 +56,8 @@ index: [{x: -0.111, y: 0, z: -2.2816}, {x: 1.217, y: 0, z: -0.90168}, {x: -3.967, y: 0, z: 2.62649}] } } - - // joint data for closed hand + + // joint data for closed hand var dataClose = { left: { @@ -75,8 +75,8 @@ } } - // joint data for the current frame - + // joint data for the current frame + var dataCurrent = { left:{ pinky:[{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], @@ -93,8 +93,8 @@ index: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}] } } - - // interpolated values on joint data to smooth movement + + // interpolated values on joint data to smooth movement var dataDelta = { left:{ @@ -113,24 +113,24 @@ } } - // Acquire an updated value per hand every 5 frames - + // Acquire an updated value per hand every 5 frames + var animationSteps = 5; - // Debugging info - + // Debugging info + var showSphere = false; var showLines = false; - // store the rays for the fingers - only for debug purposes - + // store the rays for the fingers - only for debug purposes + var fingerRays = { left:{pinky: undefined, middle: undefined, ring: undefined, thumb: undefined, index: undefined}, right:{pinky: undefined, middle: undefined, ring: undefined, thumb: undefined, index: undefined} }; - // Register object with API Debugger - + // Register object with API Debugger + var varsToDebug = { toggleDebugSphere: function(){ showSphere = !showSphere; @@ -163,8 +163,8 @@ rightSecondaryValue: 0 } } - - // Add/Subtract the joint data - per finger joint + + // Add/Subtract the joint data - per finger joint function addVals(val1, val2, sign) { var val = []; @@ -178,8 +178,8 @@ return val; } - // Multiply/Divide the joint data - per finger joint - + // Multiply/Divide the joint data - per finger joint + function multiplyValsBy(val1, num) { var val = []; for (var i = 0; i < val1.length; i++) { @@ -190,8 +190,8 @@ } return val; } - - // Calculate the finger lengths by adding its joint lengths + + // Calculate the finger lengths by adding its joint lengths function getJointDistances(jointNamesArray) { var result = {distances: [], totalDistance: 0} @@ -206,27 +206,27 @@ } return result; } - - // Calculate the sphere that look up for entities, the center of the palm, perpendicular vector from the palm plane and origin of the the finger rays + + // Calculate the sphere that look up for entities, the center of the palm, perpendicular vector from the palm plane and origin of the the finger rays function estimatePalmData(side) { // Return data object - var data = {position: undefined, perpendicular: undefined, distance: undefined, fingers: {pinky: undefined, middle: undefined, ring: undefined, thumb: undefined, index: undefined}}; + var data = {position: undefined, perpendicular: undefined, distance: undefined, fingers: {pinky: undefined, middle: undefined, ring: undefined, thumb: undefined, index: undefined}}; var jointOffset = { x: 0, y: 0, z: 0 }; - + var upperSide = side[0].toUpperCase() + side.substring(1); var jointIndexHand = MyAvatar.getJointIndex(upperSide + "Hand"); - - // Store position of the hand joint + + // Store position of the hand joint var worldPosHand = MyAvatar.jointToWorldPoint(jointOffset, jointIndexHand); var minusWorldPosHand = {x:-worldPosHand.x, y:-worldPosHand.y, z:-worldPosHand.z}; - // Data for finger rays + // Data for finger rays var directions = {pinky: undefined, middle: undefined, ring: undefined, thumb: undefined, index: undefined}; var positions = {pinky: undefined, middle: undefined, ring: undefined, thumb: undefined, index: undefined}; - // Calculate palm center + // Calculate palm center var palmCenter = {x:0, y:0, z:0}; palmCenter = Vec3.sum(worldPosHand, palmCenter); @@ -249,10 +249,10 @@ } data.perpendicular = side == "right" ? Vec3.normalize(Vec3.cross(directions["ring"], directions["pinky"])) : Vec3.normalize(Vec3.cross(directions["pinky"],directions["ring"])); - - + + data.position = Vec3.multiply(1.0/9, palmCenter); // Hand joint + 2 * 4 fingers(no thumb) = 9 - + data.distance = 1.55*Vec3.distance(data.position, positions["index"]); // move back thumb check up origin @@ -262,11 +262,11 @@ return data; } - // Register GlobalDebugger for API Debugger + // Register GlobalDebugger for API Debugger Script.registerValue("GlobalDebugger", varsToDebug); - // Create debug overlays - finger rays + palm rays + spheres - + // Create debug overlays - finger rays + palm rays + spheres + for (var i = 0; i < dataKeys.length; i++) { fingerRays["left"][dataKeys[i]] = Overlays.addOverlay("line3d", { color: { red: 0, green: 0, blue: 255 }, @@ -313,18 +313,18 @@ } function updateSphereHand(side) { - + var palmData = estimatePalmData(side); var palmPoint = palmData.position; var dist = 1.5*palmData.distance; - - // Situate the debugging overlays - + + // Situate the debugging overlays + var checkOffset = { x: palmData.perpendicular.x * dist, y: palmData.perpendicular.y * dist, z: palmData.perpendicular.z * dist }; - + var spherePos = Vec3.sum(palmPoint, checkOffset); var checkPoint = Vec3.sum(palmPoint, Vec3.multiply(2, checkOffset)); @@ -351,9 +351,9 @@ visible: showLines }); } - - // Update the intersection of only one finger at a time - + + // Update the intersection of only one finger at a time + var finger = dataKeys[updateFingerWithIndex]; var grabbables = Entities.findEntities(spherePos, dist); @@ -363,18 +363,18 @@ var direction = Vec3.normalize(Vec3.subtract(checkPoint, origin)); var intersection = Entities.findRayIntersection({origin: origin, direction: direction}, true, grabbables, [], true, false); var percent = 0.75; - var isAbleToGrab = intersection.intersects && intersection.distance < 2.5*dist; - // Store if this finger is touching something - isTouching[side][finger] = isAbleToGrab; + var isAbleToGrab = intersection.intersects && intersection.distance < 2.5*dist; + // Store if this finger is touching something + isTouching[side][finger] = isAbleToGrab; if (isAbleToGrab) { - // update the open/close percentage for this finger + // update the open/close percentage for this finger percent = intersection.distance/(2.5*dist); var grabMultiplier = finger === "thumb" ? 0.2 : 0.05; percent += grabMultiplier * grabPercent[side]; } varsToDebug.fingerPercent[side][finger] = percent; // store the current open/close percentage } - // Calculate new interpolation data + // Calculate new interpolation data var totalDistance = addVals(dataClose[side][finger], dataOpen[side][finger], -1); var percent = varsToDebug.fingerPercent[side][finger]; var newFingerData = addVals(dataOpen[side][finger], multiplyValsBy(totalDistance, percent), 1); @@ -383,7 +383,7 @@ dataDelta[side][finger] = multiplyValsBy(addVals(newFingerData, dataCurrent[side][finger], -1), 1.0/animationSteps); } - // Recreate the finger joint names - and how many 0 to count + // Recreate the finger joint names - and how many 0 to count function getJointNames(side, finger, count) { var names = []; for (var i = 1; i < count+1; i++) { @@ -393,11 +393,11 @@ return names; } - // Capture the controller values - + // Capture the controller values + var leftTriggerPress = function (value) { varsToDebug.triggerValues.leftTriggerValue = value; - // the value for the trigger increments the hand-close percentage + // the value for the trigger increments the hand-close percentage grabPercent["left"] = value; }; var leftTriggerClick = function (value) { @@ -405,7 +405,7 @@ }; var rightTriggerPress = function (value) { varsToDebug.triggerValues.rightTriggerValue = value; - // the value for the trigger increments the hand-close percentage + // the value for the trigger increments the hand-close percentage grabPercent["right"] = value; }; var rightTriggerClick = function (value) { @@ -431,60 +431,60 @@ mapping.from([Controller.Standard.RightGrip]).peek().to(rightSecondaryPress); Controller.enableMapping(MAPPING_NAME); - - function getTouching(side) { - var animating = false; - for (var i = 0; i < dataKeys.length; i++) { - var finger = dataKeys[i]; - animating = animating || isTouching[side][finger]; - } - return animating; - } - + + function getTouching(side) { + var animating = false; + for (var i = 0; i < dataKeys.length; i++) { + var finger = dataKeys[i]; + animating = animating || isTouching[side][finger]; + } + return animating; + } + Script.update.connect(function(){ - - // iterate fingers - - updateFingerWithIndex = (updateFingerWithIndex < dataKeys.length-1) ? updateFingerWithIndex + 1 : 0; - // precalculate data - + // iterate fingers + + updateFingerWithIndex = (updateFingerWithIndex < dataKeys.length-1) ? updateFingerWithIndex + 1 : 0; + + // precalculate data + updateSphereHand("right"); updateSphereHand("left"); - // Assign interpolated values - var i, j, index; - for (i = 0; i < dataKeys.length; i++) { - var finger = dataKeys[i]; - var names = getJointNames("Right", finger, 3); - dataCurrent["right"][finger] = addVals(dataCurrent["right"][finger], dataDelta["right"][finger], 1); - for (j = 0; j < names.length; j++) { - index = MyAvatar.getJointIndex(names[j]); - // if no finger is touching restate the default poses - if (getTouching("right")) { - var quatRot = Quat.fromVec3Degrees(dataCurrent["right"][finger][j]); - MyAvatar.setJointRotation(index, quatRot); - } else { - MyAvatar.clearJointData(index); - } - } - } + // Assign interpolated values + var i, j, index; + for (i = 0; i < dataKeys.length; i++) { + var finger = dataKeys[i]; + var names = getJointNames("Right", finger, 3); + dataCurrent["right"][finger] = addVals(dataCurrent["right"][finger], dataDelta["right"][finger], 1); + for (j = 0; j < names.length; j++) { + index = MyAvatar.getJointIndex(names[j]); + // if no finger is touching restate the default poses + if (getTouching("right")) { + var quatRot = Quat.fromVec3Degrees(dataCurrent["right"][finger][j]); + MyAvatar.setJointRotation(index, quatRot); + } else { + MyAvatar.clearJointData(index); + } + } + } - for (i = 0; i < dataKeys.length; i++) { - var finger = dataKeys[i]; - var names = getJointNames("Left", finger, 3); - dataCurrent["left"][finger] = addVals(dataCurrent["left"][finger], dataDelta["left"][finger], 1); - for (j = 0; j < names.length; j++) { - index = MyAvatar.getJointIndex(names[j]); - // if no finger is touching restate the default poses - if (getTouching("left")) { - var quatRot = Quat.fromVec3Degrees(dataCurrent["left"][finger][j]); - MyAvatar.setJointRotation(index, quatRot); - } else { - MyAvatar.clearJointData(index); - } - } - } + for (i = 0; i < dataKeys.length; i++) { + var finger = dataKeys[i]; + var names = getJointNames("Left", finger, 3); + dataCurrent["left"][finger] = addVals(dataCurrent["left"][finger], dataDelta["left"][finger], 1); + for (j = 0; j < names.length; j++) { + index = MyAvatar.getJointIndex(names[j]); + // if no finger is touching restate the default poses + if (getTouching("left")) { + var quatRot = Quat.fromVec3Degrees(dataCurrent["left"][finger][j]); + MyAvatar.setJointRotation(index, quatRot); + } else { + MyAvatar.clearJointData(index); + } + } + } }); From 367e2266f76cd7fc298bcd52b2ffc67d3ac4e453 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Thu, 4 Jan 2018 12:04:52 -0700 Subject: [PATCH 08/25] Fix eslint --- scripts/system/controllers/handTouch.js | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/scripts/system/controllers/handTouch.js b/scripts/system/controllers/handTouch.js index 2e1cf99166..f3e15c96af 100644 --- a/scripts/system/controllers/handTouch.js +++ b/scripts/system/controllers/handTouch.js @@ -9,6 +9,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +/* jslint bitwise: true */ + +/* global Script, Overlays, Controller, Vec3, Quat, MyAvatar, Entities +*/ + (function(){ var updateFingerWithIndex = 0; @@ -376,7 +381,7 @@ } // Calculate new interpolation data var totalDistance = addVals(dataClose[side][finger], dataOpen[side][finger], -1); - var percent = varsToDebug.fingerPercent[side][finger]; + percent = varsToDebug.fingerPercent[side][finger]; var newFingerData = addVals(dataOpen[side][finger], multiplyValsBy(totalDistance, percent), 1); // Assign animation interpolation steps @@ -453,16 +458,17 @@ updateSphereHand("left"); // Assign interpolated values - var i, j, index; + var i, j, index, finger, names, quatRot; + for (i = 0; i < dataKeys.length; i++) { - var finger = dataKeys[i]; - var names = getJointNames("Right", finger, 3); + finger = dataKeys[i]; + names = getJointNames("Right", finger, 3); dataCurrent["right"][finger] = addVals(dataCurrent["right"][finger], dataDelta["right"][finger], 1); for (j = 0; j < names.length; j++) { index = MyAvatar.getJointIndex(names[j]); // if no finger is touching restate the default poses if (getTouching("right")) { - var quatRot = Quat.fromVec3Degrees(dataCurrent["right"][finger][j]); + quatRot = Quat.fromVec3Degrees(dataCurrent["right"][finger][j]); MyAvatar.setJointRotation(index, quatRot); } else { MyAvatar.clearJointData(index); @@ -471,14 +477,14 @@ } for (i = 0; i < dataKeys.length; i++) { - var finger = dataKeys[i]; - var names = getJointNames("Left", finger, 3); + finger = dataKeys[i]; + names = getJointNames("Left", finger, 3); dataCurrent["left"][finger] = addVals(dataCurrent["left"][finger], dataDelta["left"][finger], 1); for (j = 0; j < names.length; j++) { index = MyAvatar.getJointIndex(names[j]); // if no finger is touching restate the default poses if (getTouching("left")) { - var quatRot = Quat.fromVec3Degrees(dataCurrent["left"][finger][j]); + quatRot = Quat.fromVec3Degrees(dataCurrent["left"][finger][j]); MyAvatar.setJointRotation(index, quatRot); } else { MyAvatar.clearJointData(index); From 67ca76baa1f2abb85b3b634a4ce8cac5e306e55e Mon Sep 17 00:00:00 2001 From: vladest Date: Thu, 4 Jan 2018 23:11:42 +0100 Subject: [PATCH 09/25] Magninfication --- .../resources/qml/hifi/tablet/TabletHome.qml | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/tablet/TabletHome.qml b/interface/resources/qml/hifi/tablet/TabletHome.qml index e934f18ab6..d643574810 100644 --- a/interface/resources/qml/hifi/tablet/TabletHome.qml +++ b/interface/resources/qml/hifi/tablet/TabletHome.qml @@ -127,9 +127,15 @@ Item { GridView { id: gridView + keyNavigationEnabled: false highlightFollowsCurrentItem: false + property int previousGridIndex: -1 + + // true if any of the buttons contains mouse + property bool containsMouse: false + anchors { fill: parent topMargin: 20 @@ -162,15 +168,29 @@ Item { flow: GridView.LeftToRight model: page.proxyModel - delegate: Item { + delegate: Control { id: wrapper width: gridView.cellWidth height: gridView.cellHeight + hoverEnabled: true + + property bool containsMouse: gridView.containsMouse + onHoveredChanged: { + if (hovered && !gridView.containsMouse) { + gridView.containsMouse = true + } else { + gridView.containsMouse = false + } + } + property var proxy: modelData TabletButton { id: tabletButton + scale: wrapper.hovered ? 1.25 : wrapper.containsMouse ? 0.75 : 1.0 + Behavior on scale { NumberAnimation { duration: 200; easing.type: Easing.Linear } } + anchors.centerIn: parent gridView: wrapper.GridView.view buttonIndex: page.proxyModel.buttonIndex(uuid); From 437b78dce5ae961ed1f47c3f6e6b52d4be77604d Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Fri, 5 Jan 2018 13:35:19 +0300 Subject: [PATCH 10/25] FB10621 "HIFI-Commerce Login" page title change to "Log in to continue" --- interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml b/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml index 404d7e84cf..f6875bb06f 100644 --- a/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml +++ b/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml @@ -53,7 +53,7 @@ Item { // Title Bar text RalewaySemiBold { - text: "HIFI COMMERCE - LOGIN"; + text: "Log in to continue"; // Text size size: hifi.fontSizes.overlayTitle; // Anchors From 108748ca9670398b80e958079393b591387688a0 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Fri, 5 Jan 2018 09:55:50 -0700 Subject: [PATCH 11/25] smooth transitions and slower back to default --- scripts/system/controllers/handTouch.js | 222 +++++++++++++++--------- 1 file changed, 138 insertions(+), 84 deletions(-) diff --git a/scripts/system/controllers/handTouch.js b/scripts/system/controllers/handTouch.js index f3e15c96af..31118608eb 100644 --- a/scripts/system/controllers/handTouch.js +++ b/scripts/system/controllers/handTouch.js @@ -19,7 +19,7 @@ var updateFingerWithIndex = 0; // Keys to access finger data - var dataKeys = ["pinky", "ring", "middle", "index", "thumb"]; + var fingerKeys = ["pinky", "ring", "middle", "index", "thumb"]; // Additionally close the hands to achieve a grabbing effect var grabPercent = { left: 0, @@ -44,6 +44,13 @@ } } + // frame count for transition to default pose + + var countToDefault = { + left: 0, + right: 0 + } + // joint data for opened pose var dataOpen = { @@ -67,19 +74,40 @@ var dataClose = { left: { pinky:[{x: 75.45709, y:-8.01347, z:-22.54823}, {x: 69.562, y:0, z:1.06604}, {x: 74.73801, y:0, z:-0.8351}], - middle: [{x: 66.0237, y:-2.42536, z:-6.13193}, {x: 72.63042, y:0, z:-0.71834}, {x: 75.19901, y:0, z:0.83978}], + middle: [{x: 66.0237, y:-2.42536, z:-6.13193}, {x: 65.63042, y:0, z:-0.71834}, {x: 60.19901, y:0, z:0.83978}], ring: [{x: 71.52988, y:-2.35423, z:-16.21694}, {x: 64.44739, y:0, z:0.68153}, {x: 70.518, y:0, z:-0.69295}], thumb: [{x: 33.83371, y:-15.19106, z:34.66116}, {x: 0, y:0, z:-43.42915}, {x: 0, y:0, z:-30.18613}], index: [{x: 35.56082, y:-1.21056, z:-2.07362}, {x: 79.79845, y:-0.01107, z:0.93037}, {x: 68.767, y:0, z:-2.64018}] }, right: { pinky:[{x: 75.45702, y: 8.013, z: 22.41022}, {x: 69.562, y: 0, z: -1.03973}, {x: 74.738, y: 0, z: 0.86424}], - middle: [{x: 66.02399, y: 2.425, z: 6.11638}, {x: 72.63002, y: 0, z: 0.71427}, {x: 72.63, y: 0, z: -0.85103}], + middle: [{x: 66.02399, y: 2.425, z: 6.11638}, {x: 65.63002, y: 0, z: 0.71427}, {x: 60.63, y: 0, z: -0.85103}], ring: [{x: 71.53, y: 5.022, z: 16.33612}, {x: 64.447, y: 0, z: -0.64524}, {x: 70.51801, y: 0, z: 0.69807}], thumb: [{x: 33.834, y: 15.191, z: -34.52131}, {x: 0, y: 0, z: 43.41122}, {x: 0, y: 0, z: 30.24818}], index: [{x: 35.633, y: 1.215, z: -6.6376}, {x: 79.72701, y: 0, z: -0.90168}, {x: 68.76701, y: 0, z: 2.62649}] } } + // snapshot for the default pose + + var dataDefault = { + left:{ + pinky:[{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], + middle: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], + ring: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], + thumb: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], + index: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], + set: false + }, + right:{ + pinky:[{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], + middle: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], + ring: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], + thumb: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], + index: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], + set: false + } + } + // joint data for the current frame var dataCurrent = { @@ -118,9 +146,13 @@ } } - // Acquire an updated value per hand every 5 frames + // Acquire an updated value per hand every 5 frames when finger is touching (faster in) - var animationSteps = 5; + var touchAnimationSteps = 5; + + // Acquire an updated value per hand every 10 frames when finger is returning to default position (slower out) + + var defaultAnimationSteps = 10; // Debugging info @@ -145,18 +177,18 @@ }, fingerPercent: { left: { - pinky: 0.75, - middle: 0.75, - ring: 0.75, - thumb: 0.75, - index: 0.75 + pinky: 0.38, + middle: 0.38, + ring: 0.38, + thumb: 0.38, + index: 0.38 } , right: { - pinky: 0.75, - middle: 0.75, - ring: 0.75, - thumb: 0.75, - index: 0.75 + pinky: 0.38, + middle: 0.38, + ring: 0.38, + thumb: 0.38, + index: 0.38 } }, triggerValues: { @@ -237,9 +269,9 @@ var thumbLength = 0; - for (var i = 0; i < dataKeys.length; i++) { - var finger = dataKeys[i]; - var jointNames = getJointNames(upperSide, finger, 4); + for (var i = 0; i < fingerKeys.length; i++) { + var finger = fingerKeys[i]; + var jointNames = getJointNames(side, finger, 4); var fingerLength = getJointDistances(jointNames).totalDistance; var jointIndex = MyAvatar.getJointIndex(jointNames[0]); @@ -247,20 +279,26 @@ directions[finger] = Vec3.normalize(Vec3.sum(positions[finger], minusWorldPosHand)); data.fingers[finger] = Vec3.sum(positions[finger], Vec3.multiply(fingerLength, directions[finger])); if (finger != "thumb") { - palmCenter = Vec3.sum(Vec3.multiply(2, positions[finger]), palmCenter); // Hand joint + 2 * 4 fingers(no thumb) = 9 + // finger joints have double the weight than the hand joint + // This would better position the palm estimation + palmCenter = Vec3.sum(Vec3.multiply(2, positions[finger]), palmCenter); } else { thumbLength = fingerLength; } } - data.perpendicular = side == "right" ? Vec3.normalize(Vec3.cross(directions["ring"], directions["pinky"])) : Vec3.normalize(Vec3.cross(directions["pinky"],directions["ring"])); + // perpendicular change direction depending on the side + + data.perpendicular = (side == "right") ? + Vec3.normalize(Vec3.cross(directions["index"], directions["pinky"])): + Vec3.normalize(Vec3.cross(directions["pinky"], directions["index"])); - data.position = Vec3.multiply(1.0/9, palmCenter); // Hand joint + 2 * 4 fingers(no thumb) = 9 + data.position = Vec3.multiply(1.0/9, palmCenter); // 1(weight) * Hand joint + 2(weight) * 4 fingers(no thumb) = 9 - data.distance = 1.55*Vec3.distance(data.position, positions["index"]); + data.distance = 1.55*Vec3.distance(data.position, positions["index"]); // 1.55 based on test/error for the sphere radius that best fits the hand - // move back thumb check up origin + // move back thumb ray origin data.fingers["thumb"] = Vec3.sum(data.fingers["thumb"], Vec3.multiply( -0.2 * thumbLength, data.perpendicular)); @@ -272,14 +310,14 @@ // Create debug overlays - finger rays + palm rays + spheres - for (var i = 0; i < dataKeys.length; i++) { - fingerRays["left"][dataKeys[i]] = Overlays.addOverlay("line3d", { + for (var i = 0; i < fingerKeys.length; i++) { + fingerRays["left"][fingerKeys[i]] = Overlays.addOverlay("line3d", { color: { red: 0, green: 0, blue: 255 }, start: { x:0, y:0, z:0 }, end: { x:0, y:1, z:0 }, visible: showLines }); - fingerRays["right"][dataKeys[i]] = Overlays.addOverlay("line3d", { + fingerRays["right"][fingerKeys[i]] = Overlays.addOverlay("line3d", { color: { red: 0, green: 0, blue: 255 }, start: { x:0, y:0, z:0 }, end: { x:0, y:1, z:0 }, @@ -317,6 +355,19 @@ }) } + function acquireDefaultPose(side) { + for (var i = 0; i < fingerKeys.length; i++) { + var finger = fingerKeys[i]; + var names = getJointNames(side, finger, 3); + for (var j = 0; j < names.length; j++) { + var index = MyAvatar.getJointIndex(names[j]); + var rotation = Quat.safeEulerAngles(MyAvatar.getJointRotation(index)); + dataDefault[side][finger][j] = dataCurrent[side][finger][j] = rotation; + } + } + dataDefault[side].set = true; + } + function updateSphereHand(side) { var palmData = estimatePalmData(side); @@ -349,9 +400,9 @@ visible: showSphere }); - for (var i = 0; i < dataKeys.length; i++) { - Overlays.editOverlay(fingerRays[side][dataKeys[i]], { - start: palmData.fingers[dataKeys[i]], + for (var i = 0; i < fingerKeys.length; i++) { + Overlays.editOverlay(fingerRays[side][fingerKeys[i]], { + start: palmData.fingers[fingerKeys[i]], end: checkPoint, visible: showLines }); @@ -359,16 +410,22 @@ // Update the intersection of only one finger at a time - var finger = dataKeys[updateFingerWithIndex]; + var finger = fingerKeys[updateFingerWithIndex]; var grabbables = Entities.findEntities(spherePos, dist); - + var newFingerData = dataDefault[side][finger]; + var animationSteps = defaultAnimationSteps; + if (grabbables.length > 0) { var origin = palmData.fingers[finger]; var direction = Vec3.normalize(Vec3.subtract(checkPoint, origin)); var intersection = Entities.findRayIntersection({origin: origin, direction: direction}, true, grabbables, [], true, false); - var percent = 0.75; - var isAbleToGrab = intersection.intersects && intersection.distance < 2.5*dist; + var percent = 0.38; + var isAbleToGrab = intersection.intersects && intersection.distance < 1.5*dist; + if (isAbleToGrab && !getTouching(side)) { + acquireDefaultPose(side); // take a snapshot of the default pose before touch starts + newFingerData = dataDefault[side][finger]; // assign default pose to finger data + } // Store if this finger is touching something isTouching[side][finger] = isAbleToGrab; if (isAbleToGrab) { @@ -376,23 +433,25 @@ percent = intersection.distance/(2.5*dist); var grabMultiplier = finger === "thumb" ? 0.2 : 0.05; percent += grabMultiplier * grabPercent[side]; + + // Calculate new interpolation data + var totalDistance = addVals(dataClose[side][finger], dataOpen[side][finger], -1); + newFingerData = addVals(dataOpen[side][finger], multiplyValsBy(totalDistance, percent), 1); // assign close/open ratio to finger to simulate touch + animationSteps = touchAnimationSteps; } - varsToDebug.fingerPercent[side][finger] = percent; // store the current open/close percentage + varsToDebug.fingerPercent[side][finger] = percent; } - // Calculate new interpolation data - var totalDistance = addVals(dataClose[side][finger], dataOpen[side][finger], -1); - percent = varsToDebug.fingerPercent[side][finger]; - var newFingerData = addVals(dataOpen[side][finger], multiplyValsBy(totalDistance, percent), 1); - - // Assign animation interpolation steps + + // Calculate animation increments dataDelta[side][finger] = multiplyValsBy(addVals(newFingerData, dataCurrent[side][finger], -1), 1.0/animationSteps); } - // Recreate the finger joint names - and how many 0 to count + // Recreate the finger joint names + function getJointNames(side, finger, count) { var names = []; for (var i = 1; i < count+1; i++) { - var name = side+"Hand"+finger[0].toUpperCase()+finger.substring(1)+(i); + var name = side[0].toUpperCase()+side.substring(1)+"Hand"+finger[0].toUpperCase()+finger.substring(1)+(i); names.push(name); } return names; @@ -437,61 +496,56 @@ Controller.enableMapping(MAPPING_NAME); + function getTouching(side) { var animating = false; - for (var i = 0; i < dataKeys.length; i++) { - var finger = dataKeys[i]; + for (var i = 0; i < fingerKeys.length; i++) { + var finger = fingerKeys[i]; animating = animating || isTouching[side][finger]; } - return animating; + return animating; // return false only if none of the fingers are touching } Script.update.connect(function(){ - // iterate fingers + // index of the finger that needs to be updated this frame - updateFingerWithIndex = (updateFingerWithIndex < dataKeys.length-1) ? updateFingerWithIndex + 1 : 0; + updateFingerWithIndex = (updateFingerWithIndex < fingerKeys.length-1) ? updateFingerWithIndex + 1 : 0; - // precalculate data - updateSphereHand("right"); - updateSphereHand("left"); - - // Assign interpolated values - var i, j, index, finger, names, quatRot; - - for (i = 0; i < dataKeys.length; i++) { - finger = dataKeys[i]; - names = getJointNames("Right", finger, 3); - dataCurrent["right"][finger] = addVals(dataCurrent["right"][finger], dataDelta["right"][finger], 1); - for (j = 0; j < names.length; j++) { - index = MyAvatar.getJointIndex(names[j]); - // if no finger is touching restate the default poses - if (getTouching("right")) { - quatRot = Quat.fromVec3Degrees(dataCurrent["right"][finger][j]); - MyAvatar.setJointRotation(index, quatRot); - } else { - MyAvatar.clearJointData(index); + + ["right", "left"].forEach(function(side){ + + // recalculate the base data + updateSphereHand(side); + + // this vars manage the transition to default pose + var isHandTouching = getTouching(side); + countToDefault[side] = isHandTouching ? 0 : countToDefault[side] + 1; + + + for (var i = 0; i < fingerKeys.length; i++) { + var finger = fingerKeys[i]; + var names = getJointNames(side, finger, 3); + + // Add the animation increments + + dataCurrent[side][finger] = addVals(dataCurrent[side][finger], dataDelta[side][finger], 1); + + // update every finger joint + + for (var j = 0; j < names.length; j++) { + var index = MyAvatar.getJointIndex(names[j]); + // if no finger is touching restate the default poses + if (isHandTouching || (dataDefault[side].set && countToDefault[side] < 5*touchAnimationSteps)) { + var quatRot = Quat.fromVec3Degrees(dataCurrent[side][finger][j]); + MyAvatar.setJointRotation(index, quatRot); + } else { + MyAvatar.clearJointData(index); + } } } - } - - for (i = 0; i < dataKeys.length; i++) { - finger = dataKeys[i]; - names = getJointNames("Left", finger, 3); - dataCurrent["left"][finger] = addVals(dataCurrent["left"][finger], dataDelta["left"][finger], 1); - for (j = 0; j < names.length; j++) { - index = MyAvatar.getJointIndex(names[j]); - // if no finger is touching restate the default poses - if (getTouching("left")) { - quatRot = Quat.fromVec3Degrees(dataCurrent["left"][finger][j]); - MyAvatar.setJointRotation(index, quatRot); - } else { - MyAvatar.clearJointData(index); - } - } - } - + }); }); }()) From d0bc881bb25367127c5fcd122ff5e8360e747659 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Fri, 5 Jan 2018 12:20:41 -0700 Subject: [PATCH 12/25] Load script manually --- scripts/defaultScripts.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index 4ab6134ab8..89d4c75ae4 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -32,8 +32,7 @@ var DEFAULT_SCRIPTS_COMBINED = [ "system/tablet-ui/tabletUI.js" ]; var DEFAULT_SCRIPTS_SEPARATE = [ - "system/controllers/controllerScripts.js", - "system/controllers/handTouch.js" + "system/controllers/controllerScripts.js" //"system/chat.js" ]; From 5a5d475c6c92509e13ce31508da2d5ed1b0790eb Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 5 Jan 2018 14:03:52 -0800 Subject: [PATCH 13/25] Reinstall the packing of the qrc in the executable in DEV_build --- interface/CMakeLists.txt | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index b3400c8dd0..eebe3efc55 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -12,10 +12,8 @@ function(JOIN VALUES GLUE OUTPUT) endfunction() -if (NOT DEV_BUILD) - set(INTERFACE_QML_QRC ${CMAKE_CURRENT_BINARY_DIR}/qml.qrc) - generate_qrc(OUTPUT ${INTERFACE_QML_QRC} PATH ${CMAKE_CURRENT_SOURCE_DIR}/resources GLOBS *.qml *.qss *.js *.html *.ttf *.gif *.svg *.png *.jpg) -endif() +set(INTERFACE_QML_QRC ${CMAKE_CURRENT_BINARY_DIR}/qml.qrc) +generate_qrc(OUTPUT ${INTERFACE_QML_QRC} PATH ${CMAKE_CURRENT_SOURCE_DIR}/resources GLOBS *.qml *.qss *.js *.html *.ttf *.gif *.svg *.png *.jpg) # set a default root dir for each of our optional externals if it was not passed set(OPTIONAL_EXTERNALS "LeapMotion") @@ -82,9 +80,7 @@ qt5_wrap_ui(QT_UI_HEADERS "${QT_UI_FILES}") # add them to the interface source files set(INTERFACE_SRCS ${INTERFACE_SRCS} "${QT_UI_HEADERS}" "${QT_RESOURCES}") -if (NOT DEV_BUILD) list(APPEND INTERFACE_SRCS ${INTERFACE_QML_QRC}) -endif() if (UNIX) install( From 4f6a804fd1b80f6fef0f0a5f8fbd4e53011918f3 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Fri, 5 Jan 2018 16:27:48 -0700 Subject: [PATCH 14/25] Fix magic numbers --- interface/src/avatar/MySkeletonModel.cpp | 2 +- scripts/system/controllers/handTouch.js | 41 ++++++++++++++++-------- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 1068b371ed..61236a0ff1 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -335,7 +335,7 @@ void MySkeletonModel::updateFingers() { _jointRotationFrameOffsetMap.find(index)->second = 0; } prevAbsRot = pose.getRotation(); - } else if (_jointRotationFrameOffsetMap.find(index)->second == 1){ + } else if (_jointRotationFrameOffsetMap.find(index)->second == 1) { // if the pose is invalid and was set on previous frame we do clear ( current frame offset = 1 ) _rig.clearJointAnimationPriority(index); } _jointRotationFrameOffsetMap.find(index)->second++; diff --git a/scripts/system/controllers/handTouch.js b/scripts/system/controllers/handTouch.js index 31118608eb..709d16939c 100644 --- a/scripts/system/controllers/handTouch.js +++ b/scripts/system/controllers/handTouch.js @@ -263,15 +263,23 @@ var directions = {pinky: undefined, middle: undefined, ring: undefined, thumb: undefined, index: undefined}; var positions = {pinky: undefined, middle: undefined, ring: undefined, thumb: undefined, index: undefined}; + var thumbLength = 0; + var weightCount = 0; + // Calculate palm center + + var handJointWeight = 1; + var fingerJointWeight = 2; + var palmCenter = {x:0, y:0, z:0}; palmCenter = Vec3.sum(worldPosHand, palmCenter); - var thumbLength = 0; + weightCount += handJointWeight; for (var i = 0; i < fingerKeys.length; i++) { var finger = fingerKeys[i]; - var jointNames = getJointNames(side, finger, 4); + var jointSuffixes = 4; // Get 4 joint names with suffix numbers (0, 1, 2, 3) + var jointNames = getJointNames(side, finger, jointSuffixes); var fingerLength = getJointDistances(jointNames).totalDistance; var jointIndex = MyAvatar.getJointIndex(jointNames[0]); @@ -281,7 +289,9 @@ if (finger != "thumb") { // finger joints have double the weight than the hand joint // This would better position the palm estimation - palmCenter = Vec3.sum(Vec3.multiply(2, positions[finger]), palmCenter); + + palmCenter = Vec3.sum(Vec3.multiply(fingerJointWeight, positions[finger]), palmCenter); + weightCount += fingerJointWeight; } else { thumbLength = fingerLength; } @@ -293,14 +303,14 @@ Vec3.normalize(Vec3.cross(directions["index"], directions["pinky"])): Vec3.normalize(Vec3.cross(directions["pinky"], directions["index"])); + data.position = Vec3.multiply(1.0/weightCount, palmCenter); - data.position = Vec3.multiply(1.0/9, palmCenter); // 1(weight) * Hand joint + 2(weight) * 4 fingers(no thumb) = 9 - - data.distance = 1.55*Vec3.distance(data.position, positions["index"]); // 1.55 based on test/error for the sphere radius that best fits the hand + var palmDistanceMultiplier = 1.55 // 1.55 based on test/error for the sphere radius that best fits the hand + data.distance = palmDistanceMultiplier*Vec3.distance(data.position, positions["index"]); // move back thumb ray origin - - data.fingers["thumb"] = Vec3.sum(data.fingers["thumb"], Vec3.multiply( -0.2 * thumbLength, data.perpendicular)); + var thumbBackMultiplier = 0.2; + data.fingers["thumb"] = Vec3.sum(data.fingers["thumb"], Vec3.multiply( -thumbBackMultiplier * thumbLength, data.perpendicular)); return data; } @@ -358,7 +368,8 @@ function acquireDefaultPose(side) { for (var i = 0; i < fingerKeys.length; i++) { var finger = fingerKeys[i]; - var names = getJointNames(side, finger, 3); + var jointSuffixes = 3; // We need rotation of the 0, 1 and 2 joints + var names = getJointNames(side, finger, jointSuffixes); for (var j = 0; j < names.length; j++) { var index = MyAvatar.getJointIndex(names[j]); var rotation = Quat.safeEulerAngles(MyAvatar.getJointRotation(index)); @@ -420,7 +431,7 @@ var origin = palmData.fingers[finger]; var direction = Vec3.normalize(Vec3.subtract(checkPoint, origin)); var intersection = Entities.findRayIntersection({origin: origin, direction: direction}, true, grabbables, [], true, false); - var percent = 0.38; + var percent = 0; // Initialize var isAbleToGrab = intersection.intersects && intersection.distance < 1.5*dist; if (isAbleToGrab && !getTouching(side)) { acquireDefaultPose(side); // take a snapshot of the default pose before touch starts @@ -430,7 +441,8 @@ isTouching[side][finger] = isAbleToGrab; if (isAbleToGrab) { // update the open/close percentage for this finger - percent = intersection.distance/(2.5*dist); + var distanceMultiplier = 2.5; + percent = intersection.distance/(distanceMultiplier*dist); var grabMultiplier = finger === "thumb" ? 0.2 : 0.05; percent += grabMultiplier * grabPercent[side]; @@ -447,7 +459,7 @@ } // Recreate the finger joint names - + function getJointNames(side, finger, count) { var names = []; for (var i = 1; i < count+1; i++) { @@ -496,7 +508,7 @@ Controller.enableMapping(MAPPING_NAME); - + function getTouching(side) { var animating = false; for (var i = 0; i < fingerKeys.length; i++) { @@ -526,7 +538,8 @@ for (var i = 0; i < fingerKeys.length; i++) { var finger = fingerKeys[i]; - var names = getJointNames(side, finger, 3); + var jointSuffixes = 3; // We need to update rotation of the 0, 1 and 2 joints + var names = getJointNames(side, finger, jointSuffixes); // Add the animation increments From 243dcb88ee8bfdc59ffaa9f0237e1ae1a8d6234a Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 5 Jan 2018 16:20:44 -0800 Subject: [PATCH 15/25] Bug fix for sit script. --- interface/src/avatar/MyAvatar.cpp | 8 ++++++++ interface/src/avatar/MyAvatar.h | 4 ++-- interface/src/avatar/MySkeletonModel.cpp | 15 ++++++++++++++- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 02a1959a95..84ede46589 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -3152,6 +3152,7 @@ glm::mat4 MyAvatar::getLeftHandCalibrationMat() const { } bool MyAvatar::pinJoint(int index, const glm::vec3& position, const glm::quat& orientation) { + std::lock_guard guard(_pinnedJointsMutex); auto hipsIndex = getJointIndex("Hips"); if (index != hipsIndex) { qWarning() << "Pinning is only supported for the hips joint at the moment."; @@ -3171,7 +3172,14 @@ bool MyAvatar::pinJoint(int index, const glm::vec3& position, const glm::quat& o return true; } +bool MyAvatar::isJointPinned(int index) { + std::lock_guard guard(_pinnedJointsMutex); + auto it = std::find(_pinnedJoints.begin(), _pinnedJoints.end(), index); + return it != _pinnedJoints.end(); +} + bool MyAvatar::clearPinOnJoint(int index) { + std::lock_guard guard(_pinnedJointsMutex); auto it = std::find(_pinnedJoints.begin(), _pinnedJoints.end(), index); if (it != _pinnedJoints.end()) { _pinnedJoints.erase(it); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 7c9513cb3e..ab74460d4e 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -448,9 +448,8 @@ public: virtual void clearJointData(const QString& name) override; virtual void clearJointsData() override; - - Q_INVOKABLE bool pinJoint(int index, const glm::vec3& position, const glm::quat& orientation); + bool isJointPinned(int index); Q_INVOKABLE bool clearPinOnJoint(int index); Q_INVOKABLE float getIKErrorOnLastSolve() const; @@ -837,6 +836,7 @@ private: bool getIsAway() const { return _isAway; } void setAway(bool value); + std::mutex _pinnedJointsMutex; std::vector _pinnedJoints; // height of user in sensor space, when standing erect. diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index f249be33ea..50e0474831 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -34,12 +34,25 @@ Rig::CharacterControllerState convertCharacterControllerState(CharacterControlle } static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) { + + glm::mat4 worldToSensorMat = glm::inverse(myAvatar->getSensorToWorldMatrix()); + + // check for pinned hips. + auto hipsIndex = myAvatar->getJointIndex("Hips"); + if (myAvatar->isJointPinned(hipsIndex)) { + Transform avatarTransform = myAvatar->getTransform(); + AnimPose result = AnimPose(worldToSensorMat * avatarTransform.getMatrix() * Matrices::Y_180); + result.scale() = glm::vec3(1.0f, 1.0f, 1.0f); + return result; + } else { + DebugDraw::getInstance().removeMarker("pinnedHips"); + } + glm::mat4 hipsMat = myAvatar->deriveBodyFromHMDSensor(); glm::vec3 hipsPos = extractTranslation(hipsMat); glm::quat hipsRot = glmExtractRotation(hipsMat); glm::mat4 avatarToWorldMat = myAvatar->getTransform().getMatrix(); - glm::mat4 worldToSensorMat = glm::inverse(myAvatar->getSensorToWorldMatrix()); glm::mat4 avatarToSensorMat = worldToSensorMat * avatarToWorldMat; // dampen hips rotation, by mixing it with the avatar orientation in sensor space From f563b8ca2ec479cc335b485aafa3e4a7d686c0a4 Mon Sep 17 00:00:00 2001 From: Liv Erickson Date: Fri, 5 Jan 2018 17:49:43 -0800 Subject: [PATCH 16/25] Make zones and lights not default grabbable --- scripts/system/edit.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index e28f877d85..4ffccb3383 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -239,6 +239,7 @@ var toolBar = (function () { var dimensions = properties.dimensions ? properties.dimensions : DEFAULT_DIMENSIONS; var position = getPositionToCreateEntity(); var entityID = null; + if (position !== null && position !== undefined) { var direction; if (Camera.mode === "entity" || Camera.mode === "independent") { @@ -281,6 +282,12 @@ var toolBar = (function () { if (Menu.isOptionChecked(GRABBABLE_ENTITIES_MENU_ITEM)) { properties.userData = JSON.stringify({ grabbableKey: { grabbable: true } }); } + + if (properties.type === "Zone" || properties.type === "Light") { + properties.userData = JSON.stringify({ grabbableKey: { grabbable: false } }); + properties.dynamic = false; + } + entityID = Entities.addEntity(properties); if (properties.type === "ParticleEffect") { From 4e582975b651ed7ca90f41cc279d2b7a5cb2f7fe Mon Sep 17 00:00:00 2001 From: Liv Erickson Date: Fri, 5 Jan 2018 18:03:20 -0800 Subject: [PATCH 17/25] cleanup --- scripts/system/edit.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 4ffccb3383..813dc4ae5b 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -279,13 +279,11 @@ var toolBar = (function () { position = grid.snapToSurface(grid.snapToGrid(position, false, dimensions), dimensions); properties.position = position; - if (Menu.isOptionChecked(GRABBABLE_ENTITIES_MENU_ITEM)) { + if (Menu.isOptionChecked(GRABBABLE_ENTITIES_MENU_ITEM) && + !(properties.type === "Zone" || properties.type === "Light")) { properties.userData = JSON.stringify({ grabbableKey: { grabbable: true } }); - } - - if (properties.type === "Zone" || properties.type === "Light") { + } else { properties.userData = JSON.stringify({ grabbableKey: { grabbable: false } }); - properties.dynamic = false; } entityID = Entities.addEntity(properties); From 673bc7de38d15a83e6a224190b95de60b032f194 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 6 Jan 2018 16:05:55 +1300 Subject: [PATCH 18/25] Update Window JSDoc --- .../src/scripting/WindowScriptingInterface.h | 59 +++++++++++-------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 09938f0549..c2fab67951 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -34,7 +34,9 @@ void CustomPromptResultFromScriptValue(const QScriptValue& object, CustomPromptR /**jsdoc - * The Window API provides various facilities not covered elsewhere. + * The Window API provides various facilities not covered elsewhere: window dimensions, window focus, normal or entity camera + * view, clipboard, announcements, user connections, common dialog boxes, snapshots, file import, domain changes, domain + * physics. * * @namespace Window * @property {number} innerWidth - The width of the drawable area of the Interface window (i.e., without borders or other @@ -187,8 +189,8 @@ public slots: * "Images (*.png *.jpg *.svg)". All files are displayed if a filter isn't specified. * @returns {string} The path and name of the file if one is chosen, otherwise null. * @example Ask the user to choose an image file. - * var file = Window.browse("Select Image File", Paths.resources, "Images (*.png *.jpg *.svg)"); - * print("File: " + file); + * var filename = Window.browse("Select Image File", Paths.resources, "Images (*.png *.jpg *.svg)"); + * print("File: " + filename); */ QScriptValue browse(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); @@ -202,8 +204,8 @@ public slots: * @param {string} nameFilter="" - The types of files to display. Examples: "*.json" and * "Images (*.png *.jpg *.svg)". All files are displayed if a filter isn't specified. * @example Ask the user to choose an image file without waiting for the answer. - * function onOpenFileChanged(file) { - * print("File: " + file); + * function onOpenFileChanged(filename) { + * print("File: " + filename); * } * Window.openFileChanged.connect(onOpenFileChanged); * @@ -220,11 +222,11 @@ public slots: * @param {string} directory="" - The initial directory to start browsing at. * @param {string} nameFilter="" - The types of files to display. Examples: "*.json" and * "Images (*.png *.jpg *.svg)". All files are displayed if a filter isn't specified. - * @returns {string} The path and name of the file is one is specified, otherwise null. If a single file type + * @returns {string} The path and name of the file if one is specified, otherwise null. If a single file type * is specified in the nameFilter, that file type extension is automatically appended to the result when appropriate. * @example Ask the user to specify a file to save to. - * var file = Window.save("Save to JSON file", Paths.resources, "*.json"); - * print("File: " + file); + * var filename = Window.save("Save to JSON file", Paths.resources, "*.json"); + * print("File: " + filename); */ QScriptValue save(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); @@ -238,8 +240,8 @@ public slots: * @param {string} nameFilter="" - The types of files to display. Examples: "*.json" and * "Images (*.png *.jpg *.svg)". All files are displayed if a filter isn't specified. * @example Ask the user to specify a file to save to without waiting for an answer. - * function onSaveFileChanged(file) { - * print("File: " + file); + * function onSaveFileChanged(filename) { + * print("File: " + filename); * } * Window.saveFileChanged.connect(onSaveFileChanged); * @@ -289,9 +291,9 @@ public slots: * @function Window.showAssetServer * @param {string} uploadFile="" - The path and name of a file to upload to the asset server. * @example Upload a file to the asset server. - * var file = Window.browse("Select File to Add to Asset Server", Paths.resources); - * print("File: " + file); - * Window.showAssetServer(file); + * var filename = Window.browse("Select File to Add to Asset Server", Paths.resources); + * print("File: " + filename); + * Window.showAssetServer(filename); */ void showAssetServer(const QString& upload = ""); @@ -321,8 +323,8 @@ public slots: * @param {boolean} includeAnimated=false - If true, a moving image is captured as an animated GIF in addition * to a still image. * @param {number} aspectRatio=0 - The width/height ratio of the snapshot required. If the value is 0 the - * full resolution is used (window dimensions in desktop mode; HMD display dimensions in HMD mode), otherwise one or - * other dimension is adjusted in order to match the aspect ratio. + * full resolution is used (window dimensions in desktop mode; HMD display dimensions in HMD mode), otherwise one of the + * dimensions is adjusted in order to match the aspect ratio. * @example Using the snapshot function and signals. * function onStillSnapshottaken(path, notify) { * print("Still snapshot taken: " + path); @@ -356,7 +358,7 @@ public slots: /**jsdoc * Emit a {@link Window.connectionAdded|connectionAdded} or a {@link Window.connectionError|connectionError} signal that - * indicates whether or not you successfully made a user connection. + * indicates whether or a user connection was successfully made using the Web API. * @function Window.makeConnection * @param {boolean} success - If true then {@link Window.connectionAdded|connectionAdded} is emitted, otherwise * {@link Window.connectionError|connectionError} is emitted. @@ -416,7 +418,7 @@ public slots: * Set what to show on the PC display: normal view or entity camera view. The entity camera is configured using * {@link Camera.setCameraEntity} and {@link Camera|Camera.mode}. * @function Window.setDisplayTexture - * @param {Window.DisplayTexture} name - The view to display. + * @param {Window.DisplayTexture} texture - The view to display. * @returns {boolean} true if the display texture was successfully set, otherwise false. */ // See spectatorCamera.js for Valid parameter values. @@ -445,17 +447,18 @@ public slots: bool setDisplayTexture(const QString& name); /**jsdoc - * Check if a 2D point is within the drawable area of the HUD overlay. + * Check if a 2D point is within the desktop window if in desktop mode, or the drawable area of the HUD overlay if in HMD + * mode. * @function Window.isPointOnDesktopWindow - * @param {Vec2} point - The point to check in HMD display coordinates. + * @param {Vec2} point - The point to check. * @returns {boolean} true if the point is within the window or HUD, otherwise false. */ bool isPointOnDesktopWindow(QVariant point); /**jsdoc - * Get the size of the drawable area of the Interface window if in desktop mode or the HMD display if in HMD mode. + * Get the size of the drawable area of the Interface window if in desktop mode or the HMD rendering surface if in HMD mode. * @function Window.getDeviceSize - * @returns {Vec2} The width and height of the Interface window or HMD display, in pixels. + * @returns {Vec2} The width and height of the Interface window or HMD rendering surface, in pixels. */ glm::vec2 getDeviceSize() const; @@ -521,13 +524,19 @@ signals: * @function Window.domainChanged * @param {string} domain - The domain's IP address. * @returns {Signal} + * @example Report when you change domains. + * function onDomainChanged(domain) { + * print("Domain changed: " + domain); + * } + * + * Window.domainChanged.connect(onDomainChanged); */ void domainChanged(const QString& domain); /**jsdoc - * Triggered when you try to navigate to a *.svo or *.svo.json URL in a Web browser within Interface. + * Triggered when you try to navigate to a *.json, *.svo, or *.svo.json URL in a Web browser within Interface. * @function Window.svoImportRequested - * @param {string} url - The URL of the SVO file to import/ + * @param {string} url - The URL of the file to import. * @returns {Signal} */ void svoImportRequested(const QString& url); @@ -543,7 +552,7 @@ signals: void domainConnectionRefused(const QString& reasonMessage, int reasonCode, const QString& extraInfo); /**jsdoc - * Triggered when a still snapshot has been taken by calling {@link Window.takeSnapshot|takeSnapshot}} with + * Triggered when a still snapshot has been taken by calling {@link Window.takeSnapshot|takeSnapshot} with * includeAnimated = false. * @function Window.stillSnapshotTaken * @param {string} pathStillSnapshot - The path and name of the snapshot image file. @@ -611,7 +620,7 @@ signals: * Triggered when the user closes a message box that was opened with {@link Window.openMessageBox|openMessageBox}. * @function Window.messageBoxClosed * @param {number} id - The ID of the message box that was closed. - * @param {number} button - The button that the user clicked. If the user presses Esc the Cancel button value is returned, + * @param {number} button - The button that the user clicked. If the user presses Esc, the Cancel button value is returned, * whether or not the Cancel button is displayed in the message box. * @returns {Signal} */ From 8a7c68829d9db8bf059cdf94c643e55277b041fb Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Sun, 7 Jan 2018 11:56:30 -0700 Subject: [PATCH 19/25] Optimizations, cleanings and globals --- interface/src/avatar/MySkeletonModel.cpp | 11 +- scripts/system/controllers/handTouch.js | 493 +++++++++++++++-------- 2 files changed, 341 insertions(+), 163 deletions(-) diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 61236a0ff1..e64547dacc 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -323,22 +323,25 @@ void MySkeletonModel::updateFingers() { for (auto& link : chain) { int index = _rig.indexOfJoint(link.second); if (index >= 0) { - if (_jointRotationFrameOffsetMap.find(index) == _jointRotationFrameOffsetMap.end()) { + auto rotationFrameOffset = _jointRotationFrameOffsetMap.find(index); + if (rotationFrameOffset == _jointRotationFrameOffsetMap.end()) { _jointRotationFrameOffsetMap.insert(std::pair(index, 0)); + rotationFrameOffset = _jointRotationFrameOffsetMap.find(index); } auto pose = myAvatar->getControllerPoseInSensorFrame(link.first); + if (pose.valid) { glm::quat relRot = glm::inverse(prevAbsRot) * pose.getRotation(); // only set the rotation for the finger joints, not the hands. if (link.first != controller::Action::LEFT_HAND && link.first != controller::Action::RIGHT_HAND) { _rig.setJointRotation(index, true, relRot, CONTROLLER_PRIORITY); - _jointRotationFrameOffsetMap.find(index)->second = 0; + rotationFrameOffset->second = 0; } prevAbsRot = pose.getRotation(); - } else if (_jointRotationFrameOffsetMap.find(index)->second == 1) { // if the pose is invalid and was set on previous frame we do clear ( current frame offset = 1 ) + } else if (rotationFrameOffset->second == 1) { // if the pose is invalid and was set on previous frame we do clear ( current frame offset = 1 ) _rig.clearJointAnimationPriority(index); } - _jointRotationFrameOffsetMap.find(index)->second++; + rotationFrameOffset->second++; } } } diff --git a/scripts/system/controllers/handTouch.js b/scripts/system/controllers/handTouch.js index 709d16939c..345ba7a1d7 100644 --- a/scripts/system/controllers/handTouch.js +++ b/scripts/system/controllers/handTouch.js @@ -11,7 +11,7 @@ /* jslint bitwise: true */ -/* global Script, Overlays, Controller, Vec3, Quat, MyAvatar, Entities +/* global Script, Overlays, Controller, Vec3, MyAvatar, Entities */ (function(){ @@ -27,6 +27,27 @@ // var isGrabbing = false; + var Palm = function() { + this.position = {x:0, y:0, z:0}; + this.perpendicular = {x:0, y:0, z:0}; + this.distance = 0; + this.fingers = { + pinky: {x:0, y:0, z:0}, + middle: {x:0, y:0, z:0}, + ring: {x:0, y:0, z:0}, + thumb: {x:0, y:0, z:0}, + index: {x:0, y:0, z:0} + }; + this.set = false; + }; + + var palmData = { + left: new Palm(), + right: new Palm() + }; + + var handJointNames = {left: "LeftHand", right: "RightHand"}; + // Store which fingers are touching - if all false restate the default poses var isTouching = { left: { @@ -42,109 +63,106 @@ thumb: false, index: false } - } - + }; + // frame count for transition to default pose var countToDefault = { left: 0, right: 0 - } + }; // joint data for opened pose var dataOpen = { left: { - pinky: [{x: -0.18262, y:-2.666, z:-25.11229}, {x: 1.28845, y:0, z:1.06604}, {x: -3.967, y:0, z:-0.8351} ], - middle: [{x: -0.18262, y:0, z:-3.2809}, {x: 1.28845, y:0, z:-0.71834}, {x: -3.967, y:0, z:0.83978}], - ring: [{x: -0.18262, y:-1.11078, z:-16.24391}, {x: 1.28845, y:0, z:0.68153}, {x: -3.967, y:0, z:-0.69295}], - thumb: [{x: 5.207, y:2.595, z:38.40092}, {x: -9.869, y:11.755, z:10.50012}, {x: -9.778, y:9.647, z:15.16963}], - index: [{x: -0.18262, y:0.0099, z:2.28085}, {x: 1.28845, y:-0.01107, z:0.93037}, {x: -3.967, y:0, z:-2.64018}] + pinky:[{x: -0.0066, y:-0.0224, z:-0.2174, w:0.9758},{x: 0.0112, y:0.0001, z:0.0093, w:0.9999},{x: -0.0346, y:0.0003, z:-0.0073, w:0.9994}], + ring:[{x: -0.0029, y:-0.0094, z:-0.1413, w:0.9899},{x: 0.0112, y:0.0001, z:0.0059, w:0.9999},{x: -0.0346, y:0.0002, z:-0.006, w:0.9994}], + middle:[{x: -0.0016, y:0, z:-0.0286, w:0.9996},{x: 0.0112, y:-0.0001, z:-0.0063, w:0.9999},{x: -0.0346, y:-0.0003, z:0.0073, w:0.9994}], + index:[{x: -0.0016, y:0.0001, z:0.0199, w:0.9998},{x: 0.0112, y:0, z:0.0081, w:0.9999},{x: -0.0346, y:0.0008, z:-0.023, w:0.9991}], + thumb:[{x: 0.0354, y:0.0363, z:0.3275, w:0.9435},{x: -0.0945, y:0.0938, z:0.0995, w:0.9861},{x: -0.0952, y:0.0718, z:0.1382, w:0.9832}] }, right: { - pinky: [{x: -0.111, y: 2.66601, z: 12.06423}, {x: 1.217, y: 0, z: -1.03973}, {x: -3.967, y: 0, z: 0.86424}], - middle: [{x: -0.111, y: 0, z: 3.26538}, {x: 1.217, y: 0, z: 0.71427}, {x: -3.967, y: 0, z: -0.85103}], - ring: [{x: -0.111, y: 1.11101, z: 3.56312}, {x: 1.217, y: 0, z: -0.64524}, {x: -3.967, y: 0, z: 0.69807}], - thumb: [{x: 5.207, y: -2.595, z: -38.26131}, {x: -9.869, y: -11.755, z: -10.51778}, {x: -9.77799, y: -9.647, z: -15.10783}], - index: [{x: -0.111, y: 0, z: -2.2816}, {x: 1.217, y: 0, z: -0.90168}, {x: -3.967, y: 0, z: 2.62649}] + pinky:[{x: -0.0034, y:0.023, z:0.1051, w:0.9942},{x: 0.0106, y:-0.0001, z:-0.0091, w:0.9999},{x: -0.0346, y:-0.0003, z:0.0075, w:0.9994}], + ring:[{x: -0.0013, y:0.0097, z:0.0311, w:0.9995},{x: 0.0106, y:-0.0001, z:-0.0056, w:0.9999},{x: -0.0346, y:-0.0002, z:0.0061, w:0.9994}], + middle:[{x: -0.001, y:0, z:0.0285, w:0.9996},{x: 0.0106, y:0.0001, z:0.0062, w:0.9999},{x: -0.0346, y:0.0003, z:-0.0074, w:0.9994}], + index:[{x: -0.001, y:0, z:-0.0199, w:0.9998},{x: 0.0106, y:-0.0001, z:-0.0079, w:0.9999},{x: -0.0346, y:-0.0008, z:0.0229, w:0.9991}], + thumb:[{x: 0.0355, y:-0.0363, z:-0.3263, w:0.9439},{x: -0.0946, y:-0.0938, z:-0.0996, w:0.9861},{x: -0.0952, y:-0.0719, z:-0.1376, w:0.9833}] } - } - - // joint data for closed hand - + }; var dataClose = { left: { - pinky:[{x: 75.45709, y:-8.01347, z:-22.54823}, {x: 69.562, y:0, z:1.06604}, {x: 74.73801, y:0, z:-0.8351}], - middle: [{x: 66.0237, y:-2.42536, z:-6.13193}, {x: 65.63042, y:0, z:-0.71834}, {x: 60.19901, y:0, z:0.83978}], - ring: [{x: 71.52988, y:-2.35423, z:-16.21694}, {x: 64.44739, y:0, z:0.68153}, {x: 70.518, y:0, z:-0.69295}], - thumb: [{x: 33.83371, y:-15.19106, z:34.66116}, {x: 0, y:0, z:-43.42915}, {x: 0, y:0, z:-30.18613}], - index: [{x: 35.56082, y:-1.21056, z:-2.07362}, {x: 79.79845, y:-0.01107, z:0.93037}, {x: 68.767, y:0, z:-2.64018}] + pinky:[{x: 0.5878, y:-0.1735, z:-0.1123, w:0.7821},{x: 0.5704, y:0.0053, z:0.0076, w:0.8213},{x: 0.6069, y:-0.0044, z:-0.0058, w:0.7947}], + ring:[{x: 0.5761, y:-0.0989, z:-0.1025, w:0.8048},{x: 0.5332, y:0.0032, z:0.005, w:0.846},{x: 0.5773, y:-0.0035, z:-0.0049, w:0.8165}], + middle:[{x: 0.543, y:-0.0469, z:-0.0333, w:0.8378},{x: 0.5419, y:-0.0034, z:-0.0053, w:0.8404},{x: 0.5015, y:0.0037, z:0.0063, w:0.8651}], + index:[{x: 0.3051, y:-0.0156, z:-0.014, w:0.9521},{x: 0.6414, y:0.0051, z:0.0063, w:0.7671},{x: 0.5646, y:-0.013, z:-0.019, w:0.8251}], + thumb:[{x: 0.313, y:-0.0348, z:0.3192, w:0.8938},{x: 0, y:0, z:-0.37, w:0.929},{x: 0, y:0, z:-0.2604, w:0.9655}] }, right: { - pinky:[{x: 75.45702, y: 8.013, z: 22.41022}, {x: 69.562, y: 0, z: -1.03973}, {x: 74.738, y: 0, z: 0.86424}], - middle: [{x: 66.02399, y: 2.425, z: 6.11638}, {x: 65.63002, y: 0, z: 0.71427}, {x: 60.63, y: 0, z: -0.85103}], - ring: [{x: 71.53, y: 5.022, z: 16.33612}, {x: 64.447, y: 0, z: -0.64524}, {x: 70.51801, y: 0, z: 0.69807}], - thumb: [{x: 33.834, y: 15.191, z: -34.52131}, {x: 0, y: 0, z: 43.41122}, {x: 0, y: 0, z: 30.24818}], - index: [{x: 35.633, y: 1.215, z: -6.6376}, {x: 79.72701, y: 0, z: -0.90168}, {x: 68.76701, y: 0, z: 2.62649}] + pinky:[{x: 0.5881, y:0.1728, z:0.1114, w:0.7823},{x: 0.5704, y:-0.0052, z:-0.0075, w:0.8213},{x: 0.6069, y:0.0046, z:0.006, w:0.7947}], + ring:[{x: 0.5729, y:0.1181, z:0.0898, w:0.8061},{x: 0.5332, y:-0.003, z:-0.0048, w:0.846},{x: 0.5773, y:0.0035, z:0.005, w:0.8165}], + middle:[{x: 0.543, y:0.0468, z:0.0332, w:0.8378},{x: 0.5419, y:0.0034, z:0.0052, w:0.8404},{x: 0.5047, y:-0.0037, z:-0.0064, w:0.8632}], + index:[{x: 0.306, y:-0.0076, z:-0.0584, w:0.9502},{x: 0.6409, y:-0.005, z:-0.006, w:0.7675},{x: 0.5646, y:0.0129, z:0.0189, w:0.8251}], + thumb:[{x: 0.313, y:0.0352, z:-0.3181, w:0.8942},{x: 0, y:0, z:0.3698, w:0.9291},{x: 0, y:0, z:0.2609, w:0.9654}] } - } + }; // snapshot for the default pose var dataDefault = { left:{ - pinky:[{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], - middle: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], - ring: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], - thumb: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], - index: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], + pinky:[{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + index: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], set: false }, right:{ - pinky:[{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], - middle: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], - ring: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], - thumb: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], - index: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], + pinky:[{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + index: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], set: false } - } + }; // joint data for the current frame var dataCurrent = { left:{ - pinky:[{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], - middle: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], - ring: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], - thumb: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], - index: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}] + pinky:[{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + index: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}] }, right:{ - pinky:[{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], - middle: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], - ring: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], - thumb: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], - index: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}] + pinky:[{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + index: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}] } - } + }; // interpolated values on joint data to smooth movement var dataDelta = { left:{ - pinky:[{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], - middle: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], - ring: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], - thumb: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], - index: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}] + pinky:[{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + index: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}] }, right:{ - pinky:[{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], - middle: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], - ring: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], - thumb: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}], - index: [{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0},{x: 0, y: 0, z: 0}] + pinky:[{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + index: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}] } - } + }; // Acquire an updated value per hand every 5 frames when finger is touching (faster in) @@ -159,21 +177,28 @@ var showSphere = false; var showLines = false; - // store the rays for the fingers - only for debug purposes + // This get setup on creation - var fingerRays = { - left:{pinky: undefined, middle: undefined, ring: undefined, thumb: undefined, index: undefined}, - right:{pinky: undefined, middle: undefined, ring: undefined, thumb: undefined, index: undefined} - }; + var linesCreated = false; + var sphereCreated = false; // Register object with API Debugger var varsToDebug = { + scriptLoaded: false, toggleDebugSphere: function(){ showSphere = !showSphere; + if (showSphere && !sphereCreated) { + createDebugSphere(); + sphereCreated = true; + } }, toggleDebugLines: function(){ showLines = !showLines; + if (showLines && !linesCreated) { + createDebugLines(); + linesCreated = true; + } }, fingerPercent: { left: { @@ -198,8 +223,15 @@ rightTriggerClicked: 0, leftSecondaryValue: 0, rightSecondaryValue: 0 - } - } + }, + palmData: { + left: new Palm(), + right: new Palm() + }, + offset: {x:0, y:0, z:0}, + avatarLoaded: false + }; + // Add/Subtract the joint data - per finger joint @@ -207,10 +239,11 @@ var val = []; if (val1.length != val2.length) return; for (var i = 0; i < val1.length; i++) { - val.push({x: 0, y: 0, z: 0}); + val.push({x: 0, y: 0, z: 0, w: 0}); val[i].x = val1[i].x + sign*val2[i].x; val[i].y = val1[i].y + sign*val2[i].y; val[i].z = val1[i].z + sign*val2[i].z; + val[i].w = val1[i].w + sign*val2[i].w; } return val; } @@ -220,10 +253,11 @@ function multiplyValsBy(val1, num) { var val = []; for (var i = 0; i < val1.length; i++) { - val.push({x: 0, y: 0, z: 0}); + val.push({x: 0, y: 0, z: 0, w: 0}); val[i].x = val1[i].x * num; val[i].y = val1[i].y * num; val[i].z = val1[i].z * num; + val[i].w = val1[i].w * num; } return val; } @@ -231,7 +265,7 @@ // Calculate the finger lengths by adding its joint lengths function getJointDistances(jointNamesArray) { - var result = {distances: [], totalDistance: 0} + var result = {distances: [], totalDistance: 0}; for (var i = 1; i < jointNamesArray.length; i++) { var index0 = MyAvatar.getJointIndex(jointNamesArray[i-1]); var index1 = MyAvatar.getJointIndex(jointNamesArray[i]); @@ -244,11 +278,43 @@ return result; } + function dataRelativeToWorld(side, dataIn, dataOut) { + + var handJoint = handJointNames[side]; + var jointIndex = MyAvatar.getJointIndex(handJoint); + var worldPosHand = MyAvatar.jointToWorldPoint({x:0, y:0, z:0}, jointIndex); + + dataOut.position = MyAvatar.jointToWorldPoint(dataIn.position, jointIndex); + // dataOut.perpendicular = Vec3.subtract(MyAvatar.jointToWorldPoint(dataIn.perpendicular, jointIndex), worldPosHand); + var localPerpendicular = side == "right" ? {x:0.2, y:0, z:1} : {x:-0.2, y:0, z:1}; + dataOut.perpendicular = Vec3.normalize(Vec3.subtract(MyAvatar.jointToWorldPoint(localPerpendicular, jointIndex), worldPosHand)); + dataOut.distance = dataIn.distance; + for (var i = 0; i < fingerKeys.length; i++) { + var finger = fingerKeys[i]; + dataOut.fingers[finger] = MyAvatar.jointToWorldPoint(dataIn.fingers[finger], jointIndex); + } + } + + function dataRelativeToHandJoint(side, dataIn, dataOut) { + + var handJoint = handJointNames[side]; + var jointIndex = MyAvatar.getJointIndex(handJoint); + var worldPosHand = MyAvatar.jointToWorldPoint({x:0, y:0, z:0}, jointIndex); + + dataOut.position = MyAvatar.worldToJointPoint(dataIn.position, jointIndex); + dataOut.perpendicular = MyAvatar.worldToJointPoint(Vec3.sum(worldPosHand, dataIn.perpendicular), jointIndex); + dataOut.distance = dataIn.distance; + for (var i = 0; i < fingerKeys.length; i++) { + var finger = fingerKeys[i]; + dataOut.fingers[finger] = MyAvatar.worldToJointPoint(dataIn.fingers[finger], jointIndex); + } + } + // Calculate the sphere that look up for entities, the center of the palm, perpendicular vector from the palm plane and origin of the the finger rays function estimatePalmData(side) { // Return data object - var data = {position: undefined, perpendicular: undefined, distance: undefined, fingers: {pinky: undefined, middle: undefined, ring: undefined, thumb: undefined, index: undefined}}; + var data = new Palm(); var jointOffset = { x: 0, y: 0, z: 0 }; @@ -300,69 +366,105 @@ // perpendicular change direction depending on the side data.perpendicular = (side == "right") ? - Vec3.normalize(Vec3.cross(directions["index"], directions["pinky"])): - Vec3.normalize(Vec3.cross(directions["pinky"], directions["index"])); + Vec3.normalize(Vec3.cross(directions.index, directions.pinky)): + Vec3.normalize(Vec3.cross(directions.pinky, directions.index)); data.position = Vec3.multiply(1.0/weightCount, palmCenter); - var palmDistanceMultiplier = 1.55 // 1.55 based on test/error for the sphere radius that best fits the hand - data.distance = palmDistanceMultiplier*Vec3.distance(data.position, positions["index"]); + if (side == "right") varsToDebug.offset = MyAvatar.worldToJointPoint(worldPosHand, jointIndexHand); + + var palmDistanceMultiplier = 1.55; // 1.55 based on test/error for the sphere radius that best fits the hand + data.distance = palmDistanceMultiplier*Vec3.distance(data.position, positions.index); // move back thumb ray origin var thumbBackMultiplier = 0.2; - data.fingers["thumb"] = Vec3.sum(data.fingers["thumb"], Vec3.multiply( -thumbBackMultiplier * thumbLength, data.perpendicular)); + data.fingers.thumb = Vec3.sum(data.fingers.thumb, Vec3.multiply( -thumbBackMultiplier * thumbLength, data.perpendicular)); - return data; + //return getDataRelativeToHandJoint(side, data); + dataRelativeToHandJoint(side, data, palmData[side]); + palmData[side].set = true; + // return palmData[side]; } // Register GlobalDebugger for API Debugger Script.registerValue("GlobalDebugger", varsToDebug); + + + // store the rays for the fingers - only for debug purposes + + var fingerRays = { + left:{ + pinky: undefined, + middle: undefined, + ring: undefined, + thumb: undefined, + index: undefined + }, + right:{ + pinky: undefined, + middle: undefined, + ring: undefined, + thumb: undefined, + index: undefined + } + }; + // Create debug overlays - finger rays + palm rays + spheres - for (var i = 0; i < fingerKeys.length; i++) { - fingerRays["left"][fingerKeys[i]] = Overlays.addOverlay("line3d", { - color: { red: 0, green: 0, blue: 255 }, - start: { x:0, y:0, z:0 }, - end: { x:0, y:1, z:0 }, - visible: showLines - }); - fingerRays["right"][fingerKeys[i]] = Overlays.addOverlay("line3d", { - color: { red: 0, green: 0, blue: 255 }, - start: { x:0, y:0, z:0 }, - end: { x:0, y:1, z:0 }, - visible: showLines - }); + var palmRay, sphereHand; + + function createDebugLines() { + + for (var i = 0; i < fingerKeys.length; i++) { + fingerRays.left[fingerKeys[i]] = Overlays.addOverlay("line3d", { + color: { red: 0, green: 0, blue: 255 }, + start: { x:0, y:0, z:0 }, + end: { x:0, y:1, z:0 }, + visible: showLines + }); + fingerRays.right[fingerKeys[i]] = Overlays.addOverlay("line3d", { + color: { red: 0, green: 0, blue: 255 }, + start: { x:0, y:0, z:0 }, + end: { x:0, y:1, z:0 }, + visible: showLines + }); + } + + palmRay = { + left: Overlays.addOverlay("line3d", { + color: { red: 255, green: 0, blue: 0 }, + start: { x:0, y:0, z:0 }, + end: { x:0, y:1, z:0 }, + visible: showLines + }), + right: Overlays.addOverlay("line3d", { + color: { red: 255, green: 0, blue: 0 }, + start: { x:0, y:0, z:0 }, + end: { x:0, y:1, z:0 }, + visible: showLines + }) + }; + linesCreated = true; } - var palmRay = { - left: Overlays.addOverlay("line3d", { - color: { red: 255, green: 0, blue: 0 }, - start: { x:0, y:0, z:0 }, - end: { x:0, y:1, z:0 }, - visible: showLines - }), - right: Overlays.addOverlay("line3d", { - color: { red: 255, green: 0, blue: 0 }, - start: { x:0, y:0, z:0 }, - end: { x:0, y:1, z:0 }, - visible: showLines - }) - } - - var sphereHand = { - right: Overlays.addOverlay("sphere", { - position: MyAvatar.position, - color: { red: 0, green: 255, blue: 0 }, - scale: { x: 0.01, y: 0.01, z: 0.01 }, - visible: showSphere - }), - left: Overlays.addOverlay("sphere", { - position: MyAvatar.position, - color: { red: 0, green: 255, blue: 0 }, - scale: { x: 0.01, y: 0.01, z: 0.01 }, - visible: showSphere - }) + function createDebugSphere() { + + sphereHand = { + right: Overlays.addOverlay("sphere", { + position: MyAvatar.position, + color: { red: 0, green: 255, blue: 0 }, + scale: { x: 0.01, y: 0.01, z: 0.01 }, + visible: showSphere + }), + left: Overlays.addOverlay("sphere", { + position: MyAvatar.position, + color: { red: 0, green: 255, blue: 0 }, + scale: { x: 0.01, y: 0.01, z: 0.01 }, + visible: showSphere + }) + }; + sphereCreated = true; } function acquireDefaultPose(side) { @@ -372,7 +474,7 @@ var names = getJointNames(side, finger, jointSuffixes); for (var j = 0; j < names.length; j++) { var index = MyAvatar.getJointIndex(names[j]); - var rotation = Quat.safeEulerAngles(MyAvatar.getJointRotation(index)); + var rotation = MyAvatar.getJointRotation(index); dataDefault[side][finger][j] = dataCurrent[side][finger][j] = rotation; } } @@ -380,44 +482,51 @@ } function updateSphereHand(side) { + + var data = new Palm(); + dataRelativeToWorld(side, palmData[side], data); + varsToDebug.palmData[side] = palmData[side]; - var palmData = estimatePalmData(side); - var palmPoint = palmData.position; - var dist = 1.5*palmData.distance; + var palmPoint = data.position; + var LOOKUP_DISTANCE_MULTIPLIER = 1.5; + var dist = LOOKUP_DISTANCE_MULTIPLIER*data.distance; // Situate the debugging overlays - var checkOffset = { x: palmData.perpendicular.x * dist, - y: palmData.perpendicular.y * dist, - z: palmData.perpendicular.z * dist }; + var checkOffset = { x: data.perpendicular.x * dist, + y: data.perpendicular.y * dist, + z: data.perpendicular.z * dist }; var spherePos = Vec3.sum(palmPoint, checkOffset); var checkPoint = Vec3.sum(palmPoint, Vec3.multiply(2, checkOffset)); - Overlays.editOverlay(palmRay[side], { - start: palmPoint, - end: checkPoint, - visible: showLines - }); - - Overlays.editOverlay(sphereHand[side], { - position: spherePos, - scale: { - x: 2*dist, - y: 2*dist, - z: 2*dist - }, - visible: showSphere - }); - - for (var i = 0; i < fingerKeys.length; i++) { - Overlays.editOverlay(fingerRays[side][fingerKeys[i]], { - start: palmData.fingers[fingerKeys[i]], + if (showLines) { + Overlays.editOverlay(palmRay[side], { + start: palmPoint, end: checkPoint, visible: showLines - }); - } + }); + for (var i = 0; i < fingerKeys.length; i++) { + Overlays.editOverlay(fingerRays[side][fingerKeys[i]], { + start: data.fingers[fingerKeys[i]], + end: checkPoint, + visible: showLines + }); + } + } + + if (showSphere) { + Overlays.editOverlay(sphereHand[side], { + position: spherePos, + scale: { + x: 2*dist, + y: 2*dist, + z: 2*dist + }, + visible: showSphere + }); + } // Update the intersection of only one finger at a time @@ -428,11 +537,11 @@ var animationSteps = defaultAnimationSteps; if (grabbables.length > 0) { - var origin = palmData.fingers[finger]; + var origin = data.fingers[finger]; var direction = Vec3.normalize(Vec3.subtract(checkPoint, origin)); var intersection = Entities.findRayIntersection({origin: origin, direction: direction}, true, grabbables, [], true, false); var percent = 0; // Initialize - var isAbleToGrab = intersection.intersects && intersection.distance < 1.5*dist; + var isAbleToGrab = intersection.intersects && intersection.distance < LOOKUP_DISTANCE_MULTIPLIER*dist; if (isAbleToGrab && !getTouching(side)) { acquireDefaultPose(side); // take a snapshot of the default pose before touch starts newFingerData = dataDefault[side][finger]; // assign default pose to finger data @@ -441,9 +550,15 @@ isTouching[side][finger] = isAbleToGrab; if (isAbleToGrab) { // update the open/close percentage for this finger - var distanceMultiplier = 2.5; - percent = intersection.distance/(distanceMultiplier*dist); - var grabMultiplier = finger === "thumb" ? 0.2 : 0.05; + + var FINGER_REACT_MULTIPLIER = 2.8; + + percent = intersection.distance/(FINGER_REACT_MULTIPLIER*dist); + + var THUMB_FACTOR = 0.2; + var FINGER_FACTOR = 0.05; + + var grabMultiplier = finger === "thumb" ? THUMB_FACTOR : FINGER_FACTOR; // Amount of grab coefficient added to the fingers - thumb is higher percent += grabMultiplier * grabPercent[side]; // Calculate new interpolation data @@ -456,6 +571,7 @@ // Calculate animation increments dataDelta[side][finger] = multiplyValsBy(addVals(newFingerData, dataCurrent[side][finger], -1), 1.0/animationSteps); + } // Recreate the finger joint names @@ -474,7 +590,7 @@ var leftTriggerPress = function (value) { varsToDebug.triggerValues.leftTriggerValue = value; // the value for the trigger increments the hand-close percentage - grabPercent["left"] = value; + grabPercent.left = value; }; var leftTriggerClick = function (value) { varsToDebug.triggerValues.leftTriggerClicked = value; @@ -482,7 +598,7 @@ var rightTriggerPress = function (value) { varsToDebug.triggerValues.rightTriggerValue = value; // the value for the trigger increments the hand-close percentage - grabPercent["right"] = value; + grabPercent.right = value; }; var rightTriggerClick = function (value) { varsToDebug.triggerValues.rightTriggerClicked = value; @@ -508,6 +624,14 @@ Controller.enableMapping(MAPPING_NAME); + if (showLines && !linesCreated) { + createDebugLines(); + linesCreated = true; + } + if (showSphere && !sphereCreated) { + createDebugSphere(); + sphereCreated = true; + } function getTouching(side) { var animating = false; @@ -518,16 +642,67 @@ return animating; // return false only if none of the fingers are touching } + function reEstimatePalmData() { + ["right", "left"].forEach(function(side){ + estimatePalmData(side); + }); + } + + MyAvatar.onLoadComplete.connect(function () { + // Sometimes the rig is not ready when this signal is trigger + console.log("avatar loaded"); + Script.setInterval(function(){ + reEstimatePalmData(); + }, 2000); + }); + + MyAvatar.sensorToWorldScaleChanged.connect(function(){ + reEstimatePalmData(); + }); + + Script.scriptEnding.connect(function () { + ["right", "left"].forEach(function(side){ + if (linesCreated) { + Overlays.deleteOverlay(palmRay[side]); + } + if (sphereCreated) { + Overlays.deleteOverlay(sphereHand[side]); + } + for (var i = 0; i < fingerKeys.length; i++) { + + var finger = fingerKeys[i]; + var jointSuffixes = 3; // We need to clear the joints 0, 1 and 2 joints + var names = getJointNames(side, finger, jointSuffixes); + + for (var j = 0; j < names.length; j++) { + var index = MyAvatar.getJointIndex(names[j]); + MyAvatar.clearJointData(index); + } + + if (linesCreated) { + Overlays.deleteOverlay(fingerRays[side][finger]); + } + } + }); + + + + }); + Script.update.connect(function(){ // index of the finger that needs to be updated this frame + + updateFingerWithIndex = (updateFingerWithIndex < fingerKeys.length-1) ? updateFingerWithIndex + 1 : 0; - ["right", "left"].forEach(function(side){ - + + if (!palmData[side].set) { + reEstimatePalmData(); + } // recalculate the base data updateSphereHand(side); @@ -551,7 +726,7 @@ var index = MyAvatar.getJointIndex(names[j]); // if no finger is touching restate the default poses if (isHandTouching || (dataDefault[side].set && countToDefault[side] < 5*touchAnimationSteps)) { - var quatRot = Quat.fromVec3Degrees(dataCurrent[side][finger][j]); + var quatRot = dataCurrent[side][finger][j]; MyAvatar.setJointRotation(index, quatRot); } else { MyAvatar.clearJointData(index); @@ -561,4 +736,4 @@ }); }); -}()) +}()); From 50f89182d72b7db93a4d6bf9e54a4ea9bb1121c1 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Sun, 7 Jan 2018 18:29:13 -0700 Subject: [PATCH 20/25] Add Utils.jsc --- interface/resources/qml/js/Utils.jsc | Bin 0 -> 6596 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 interface/resources/qml/js/Utils.jsc diff --git a/interface/resources/qml/js/Utils.jsc b/interface/resources/qml/js/Utils.jsc new file mode 100644 index 0000000000000000000000000000000000000000..ab20e996b9469915ac6a89901da175143e6b5024 GIT binary patch literal 6596 zcmcIoZD?EP6@Kh1ckah_<+cmm)~+s7*+{cEDOm|)Y*uPBiCux$Iwl#JYtNP&7t3}W zIXI&o4oU8EkM2n zXjl7O2dDtRuNCd{@)jY-)d!#zenqKY4sn&W{weu)twmlb!$4JTzv65rm+K1vDC#!F zU$|ZVIZ9zlW+S<`HL;hgR&M8~1sQiLyXg~6*Kx4qHtya=?){4Iz`gR%B^6)FqD(EX zPx0NgjXdr9YAbNP75KOnxYqy<89DFo`<=jA05hh+!CUJc)7q5K-I-U<4BwMhw#!Kold03qHB1qPh=(m57jV zQZO*TlsKpO<^4D&)ji|-UBpF<$-`KmCFTK~acFxKqv8qb`vJ}(jwnv6ynF{ogq(=F zep_Ap)wNGud)4)O>UtbUguEF{VOB&&#)x21=BI^yoeI;=cpet< z8yEavN0<0LF8)1^DU2eDE}X7EzTC=9#(VVvU%?VLXPX@E8u^tC$cdhzrY~ z5|TzSE|9{#2}}z<=It55SzSj%iNA*yHv6nZbjwP_N>*aF82-K7|F|4Z{3((gYY*l1 z1HmPNES&HK!VAmo!G#Z7%nSc2m&=jFSbJz;vopy0hR~Acw-O%(%P$^o0YVGQoxRBc zfA58N%nJ{-l0bHGCbwg^#q(qrrDi+!w<_$cdfT#JQtVa6B|0HZKTvD>SPT8{3FK*i zWU(iZU!oSteUbW{j!MrrowDh%hwMA*Wz%5~+1G_Km+UHco!!3?d2`uawWQAO`Fhz^ zFY9DKQ7^k1!8+Li%B~H>BZmbX7dAVWSkh#gI3&0r zaF0rAW;@0YRTR^~w|fH1=rq7T1!gIn>dAFt!Sy*C45#|hZ zyKMQfPE=zR8BZ12Iu&e;jZ`qZ;(XBNe0X7VdI#CX&hyK$=>L{qIw{h|R_2l4RI7AY z^-G-Fg*Pl@c)U6&I`)6~vWKqD3?b!JxCu1uN`cA?Q-i-75ra?*w4> z7DLeZEXfck*xy13exhOT)~XM$^JKYWuGBjYFUK{J-kQV9*AmCcI=9Ipz3t&zt&~o& zbp5o=j@d0M5iMDXSkX$%t~YJ^dxYvnP2X7@RT0fd+JSJgHI(cgqRD9&=_E-h7ipTL zi!Ra}N%Kz9JRJ@v&Ijf=afs?ImCUopaAUf5Bc(P@)^4QL#!&4B4=hC;lN+q^_W+Q1 zP^n|i{5|@(6kNweRp@!g!IB0$&XzP(9WOq8UKl6|MM0Czbbp-MJtdAmS4|C?43SDf)z9}dNlLtGo5xl*UM-i)V9TyEvOFWw5I(m_-&8|fx011{`>NNH;(6Df(rr#QsN>-}$CJna|wOpzoRcq-Fq`bAH8fghvwNyJ`N_RlpJYiGQZuHu}HZ5Pa zdE8c|q5`8P==g;$uId?L_>-ZdTT7Ja(9^jWIyw}4%`oqUj`vkn%QKo+>hkt%Am^R8 zT%)}GyrqJUtqMAFDsNlV@ Date: Tue, 9 Jan 2018 08:17:15 +1300 Subject: [PATCH 21/25] Fix typo --- interface/src/scripting/WindowScriptingInterface.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index c2fab67951..5cd75ee941 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -358,7 +358,7 @@ public slots: /**jsdoc * Emit a {@link Window.connectionAdded|connectionAdded} or a {@link Window.connectionError|connectionError} signal that - * indicates whether or a user connection was successfully made using the Web API. + * indicates whether or not a user connection was successfully made using the Web API. * @function Window.makeConnection * @param {boolean} success - If true then {@link Window.connectionAdded|connectionAdded} is emitted, otherwise * {@link Window.connectionError|connectionError} is emitted. From fd83e00df467c90ec3f891ab4c9178f47127c22b Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Mon, 8 Jan 2018 11:30:21 -0800 Subject: [PATCH 22/25] fix tablet interaction and far grab functionallity --- scripts/system/controllers/controllerDispatcher.js | 8 ++++---- .../controllers/controllerModules/farActionGrabEntity.js | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 16f1d086b7..7f2cbe0aee 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -148,8 +148,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); this.setIgnorePointerItems = function() { if (HMD.tabletID !== this.tabletID) { this.tabletID = HMD.tabletID; - Pointers.setIgnoreItems(_this.leftPointer, _this.blacklist); - Pointers.setIgnoreItems(_this.rightPointer, _this.blacklist); + Pointers.setIgnoreItems(_this.leftPointer, _this.blacklist.concat([HMD.tabletID])); + Pointers.setIgnoreItems(_this.rightPointer, _this.blacklist.concat([HMD.tabletID])); } }; @@ -378,8 +378,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); }; this.setBlacklist = function() { - RayPick.setIgnoreItems(_this.leftControllerRayPick, this.blacklist); - RayPick.setIgnoreItems(_this.rightControllerRayPick, this.blacklist); + RayPick.setIgnoreItems(_this.leftControllerRayPick, this.blacklist.concat([HMD.tabletID])); + RayPick.setIgnoreItems(_this.rightControllerRayPick, this.blacklist.concat([HMD.tabletID])); }; var MAPPING_NAME = "com.highfidelity.controllerDispatcher"; diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index 0e04f3b985..88195d7024 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -447,7 +447,9 @@ Script.include("/~/system/libraries/Xform.js"); this.targetObject = new TargetObject(entityID, targetProps); this.targetObject.parentProps = getEntityParents(targetProps); - Script.clearTimeout(this.contextOverlayTimer); + if (this.contextOverlayTimer) { + Script.clearTimeout(this.contextOverlayTimer); + } this.contextOverlayTimer = false; if (entityID !== this.entityWithContextOverlay) { this.destroyContextOverlay(); From d1e176ee9d236350a13288505f009572601f9508 Mon Sep 17 00:00:00 2001 From: Liv Erickson Date: Mon, 8 Jan 2018 11:31:09 -0800 Subject: [PATCH 23/25] Update menu to reflect non-grabbable entity types, include particles --- scripts/system/edit.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 813dc4ae5b..5fb75748d5 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -225,7 +225,7 @@ function adjustPositionPerBoundingBox(position, direction, registration, dimensi var TOOLS_PATH = Script.resolvePath("assets/images/tools/"); var GRABBABLE_ENTITIES_MENU_CATEGORY = "Edit"; -var GRABBABLE_ENTITIES_MENU_ITEM = "Create Entities As Grabbable"; +var GRABBABLE_ENTITIES_MENU_ITEM = "Create Entities As Grabbable (except Zones, Particles, and Lights)"; var toolBar = (function () { var EDIT_SETTING = "io.highfidelity.isEditing"; // for communication with other scripts @@ -280,7 +280,7 @@ var toolBar = (function () { position = grid.snapToSurface(grid.snapToGrid(position, false, dimensions), dimensions); properties.position = position; if (Menu.isOptionChecked(GRABBABLE_ENTITIES_MENU_ITEM) && - !(properties.type === "Zone" || properties.type === "Light")) { + !(properties.type === "Zone" || properties.type === "Light" || properties.type === "ParticleEffect")) { properties.userData = JSON.stringify({ grabbableKey: { grabbable: true } }); } else { properties.userData = JSON.stringify({ grabbableKey: { grabbable: false } }); From 97c0b33975698800d3c5c66a2aa567c6931f9e51 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 8 Jan 2018 12:50:05 -0800 Subject: [PATCH 24/25] fix avatar texture loading --- .../src/avatars-renderer/SkeletonModel.cpp | 17 ++++++++++++++ .../src/avatars-renderer/SkeletonModel.h | 8 +++++-- .../src/CauterizedMeshPartPayload.cpp | 5 +--- .../render-utils/src/MeshPartPayload.cpp | 23 ++++++------------- libraries/render-utils/src/Model.h | 4 ++-- 5 files changed, 33 insertions(+), 24 deletions(-) diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index 04b865fa03..a13ef07cd4 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -37,6 +37,16 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) : SkeletonModel::~SkeletonModel() { } +void SkeletonModel::setURL(const QUrl& url) { + _texturesLoaded = false; + Model::setURL(url); +} + +void SkeletonModel::setTextures(const QVariantMap& textures) { + _texturesLoaded = false; + Model::setTextures(textures); +} + void SkeletonModel::initJointStates() { const FBXGeometry& geometry = getFBXGeometry(); glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset); @@ -142,6 +152,13 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { Parent::simulate(deltaTime, fullUpdate); } + // FIXME: This texture loading logic should probably live in Avatar, to mirror RenderableModelEntityItem and ModelOverlay, + // but Avatars don't get updates in the same way + if (!_texturesLoaded && getGeometry() && getGeometry()->areTexturesLoaded()) { + _texturesLoaded = true; + updateRenderItems(); + } + if (!isActive() || !_owningAvatar->isMyAvatar()) { return; // only simulate for own avatar } diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h index 919e82825c..f911ad0c5a 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h @@ -31,6 +31,9 @@ public: SkeletonModel(Avatar* owningAvatar, QObject* parent = nullptr); ~SkeletonModel(); + Q_INVOKABLE void setURL(const QUrl& url) override; + Q_INVOKABLE void setTextures(const QVariantMap& textures) override; + void initJointStates() override; void simulate(float deltaTime, bool fullUpdate = true) override; @@ -115,8 +118,6 @@ protected: void computeBoundingShape(); -protected: - bool getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const; Avatar* _owningAvatar; @@ -128,6 +129,9 @@ protected: glm::vec3 _defaultEyeModelPosition; float _headClipDistance; // Near clip distance to use if no separate head model + +private: + bool _texturesLoaded { false }; }; #endif // hifi_SkeletonModel_h diff --git a/libraries/render-utils/src/CauterizedMeshPartPayload.cpp b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp index 8a5b0d6bc3..9de973480a 100644 --- a/libraries/render-utils/src/CauterizedMeshPartPayload.cpp +++ b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp @@ -46,10 +46,7 @@ void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const render::S } batch.setModelTransform(_cauterizedTransform); } else { - if (_clusterBuffer) { - batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, _clusterBuffer); - } - batch.setModelTransform(_transform); + ModelMeshPartPayload::bindTransform(batch, locations, renderMode); } } diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 9d63bd78cb..c16f7f952e 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -480,23 +480,14 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const { } void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) { - if (!_isBlendShaped) { - batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0); - batch.setInputFormat((_drawMesh->getVertexFormat())); - batch.setInputStream(0, _drawMesh->getVertexStream()); + batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0); + batch.setInputFormat((_drawMesh->getVertexFormat())); + if (_isBlendShaped && _blendedVertexBuffer) { + batch.setInputBuffer(0, _blendedVertexBuffer, 0, sizeof(glm::vec3)); + batch.setInputBuffer(1, _blendedVertexBuffer, _drawMesh->getNumVertices() * sizeof(glm::vec3), sizeof(glm::vec3)); + batch.setInputStream(2, _drawMesh->getVertexStream().makeRangedStream(2)); } else { - batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0); - batch.setInputFormat((_drawMesh->getVertexFormat())); - - if (_blendedVertexBuffer) { - batch.setInputBuffer(0, _blendedVertexBuffer, 0, sizeof(glm::vec3)); - batch.setInputBuffer(1, _blendedVertexBuffer, _drawMesh->getNumVertices() * sizeof(glm::vec3), sizeof(glm::vec3)); - batch.setInputStream(2, _drawMesh->getVertexStream().makeRangedStream(2)); - } else { - batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0); - batch.setInputFormat((_drawMesh->getVertexFormat())); - batch.setInputStream(0, _drawMesh->getVertexStream()); - } + batch.setInputStream(0, _drawMesh->getVertexStream()); } } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 4df7faac84..9c5b980ed1 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -78,7 +78,7 @@ public: /// Sets the URL of the model to render. // Should only be called from the model's rendering thread to avoid access violations of changed geometry. - Q_INVOKABLE void setURL(const QUrl& url); + Q_INVOKABLE virtual void setURL(const QUrl& url); const QUrl& getURL() const { return _url; } // new Scene/Engine rendering support @@ -136,7 +136,7 @@ public: const Geometry::Pointer& getCollisionGeometry() const { return _collisionGeometry; } const QVariantMap getTextures() const { assert(isLoaded()); return _renderGeometry->getTextures(); } - Q_INVOKABLE void setTextures(const QVariantMap& textures); + Q_INVOKABLE virtual void setTextures(const QVariantMap& textures); /// Provided as a convenience, will crash if !isLoaded() // And so that getGeometry() isn't chained everywhere From dd0e57026e123779784102658a264f6aaeb70f67 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 8 Jan 2018 15:28:59 -0800 Subject: [PATCH 25/25] Oculus Touch: More accurate spin on thrown objects The internal computation of angular velocity was incorrect. Apparently, the ovrPoseStatef.AngularRotation is not in sensor frame but local to the controller rotation. --- plugins/oculus/src/OculusHelpers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/oculus/src/OculusHelpers.cpp b/plugins/oculus/src/OculusHelpers.cpp index 3d06a4b223..4632c8ab76 100644 --- a/plugins/oculus/src/OculusHelpers.cpp +++ b/plugins/oculus/src/OculusHelpers.cpp @@ -262,7 +262,7 @@ controller::Pose ovrControllerPoseToHandPose( pose.translation = toGlm(handPose.ThePose.Position); pose.translation += rotation * translationOffset; pose.rotation = rotation * rotationOffset; - pose.angularVelocity = toGlm(handPose.AngularVelocity); + pose.angularVelocity = rotation * toGlm(handPose.AngularVelocity); pose.velocity = toGlm(handPose.LinearVelocity); pose.valid = true; return pose;